diff --git a/.bazelignore b/.bazelignore deleted file mode 100644 index 9ca3992b8666..000000000000 --- a/.bazelignore +++ /dev/null @@ -1,19 +0,0 @@ -.git -dist -node_modules -packages/angular/cli/node_modules -packages/angular/create/node_modules -packages/angular/pwa/node_modules -packages/angular/build/node_modules -packages/angular/ssr/node_modules -packages/angular_devkit/architect/node_modules -packages/angular_devkit/architect_cli/node_modules -packages/angular_devkit/build_angular/node_modules -packages/angular_devkit/build_webpack/node_modules -packages/angular_devkit/core/node_modules -packages/angular_devkit/schematics/node_modules -packages/angular_devkit/schematics_cli/node_modules -packages/ngtools/webpack/node_modules -packages/schematics/angular/node_modules -modules/testing/builder/node_modules -tests/node_modules diff --git a/.bazelrc b/.bazelrc index 816134dca1ef..92e60820829a 100644 --- a/.bazelrc +++ b/.bazelrc @@ -53,7 +53,7 @@ test --incompatible_strict_action_env build --experimental_remote_merkle_tree_cache # Ensure that tags applied in BUILDs propagate to actions -common --experimental_allow_tags_propagation +common --incompatible_allow_tags_propagation # Ensure sandboxing is enabled even for exclusive tests test --incompatible_exclusive_test_sandboxed diff --git a/.bazelversion b/.bazelversion index 5942a0d3a0e7..6d2890793d47 100644 --- a/.bazelversion +++ b/.bazelversion @@ -1 +1 @@ -7.7.1 +8.5.0 diff --git a/.github/shared-actions/windows-bazel-test/action.yml b/.github/shared-actions/windows-bazel-test/action.yml index 5e2ec1777892..7a5cf7c0297b 100644 --- a/.github/shared-actions/windows-bazel-test/action.yml +++ b/.github/shared-actions/windows-bazel-test/action.yml @@ -24,7 +24,7 @@ runs: - name: Convert symlinks for Windows host shell: pwsh run: | - $runfiles_dir = "./dist/bin/tests/legacy-cli/${{inputs.test_target_name}}_/${{inputs.test_target_name}}.bat.runfiles" + $runfiles_dir = "./dist/bin/tests/${{inputs.test_target_name}}_/${{inputs.test_target_name}}.bat.runfiles" # Needed for resolution because Aspect/Bazel looks for repositories at `/external`. # TODO(devversion): consult with Aspect on why this is needed. @@ -38,5 +38,5 @@ runs: E2E_TEMP: ${{ inputs.e2e_temp_dir }} run: | node ./scripts/windows-testing/parallel-executor.mjs \ - "./dist/bin/tests/legacy-cli/${{ inputs.test_target_name }}_/${{ inputs.test_target_name }}.bat.runfiles" \ + "./dist/bin/tests/${{ inputs.test_target_name }}_/${{ inputs.test_target_name }}.bat.runfiles" \ ${{ inputs.test_target_name }} diff --git a/.github/workflows/assistant-to-the-branch-manager.yml b/.github/workflows/assistant-to-the-branch-manager.yml index 6bab3ffca674..a30031ce31fd 100644 --- a/.github/workflows/assistant-to-the-branch-manager.yml +++ b/.github/workflows/assistant-to-the-branch-manager.yml @@ -12,10 +12,11 @@ permissions: jobs: assistant_to_the_branch_manager: runs-on: ubuntu-latest + if: github.event.repository.fork == false steps: - - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 with: persist-credentials: false - - uses: angular/dev-infra/github-actions/branch-manager@c855fffb4b01bc06e743eb3bdfd54c866af09ad8 + - uses: angular/dev-infra/github-actions/branch-manager@942d738d8f4d65b161d06e6c399fefec318cdbfe with: angular-robot-key: ${{ secrets.ANGULAR_ROBOT_PRIVATE_KEY }} diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 60e3cd99ac57..000b239cd7a0 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@c855fffb4b01bc06e743eb3bdfd54c866af09ad8 + uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@942d738d8f4d65b161d06e6c399fefec318cdbfe - name: Setup Bazel - uses: angular/dev-infra/github-actions/bazel/setup@c855fffb4b01bc06e743eb3bdfd54c866af09ad8 + uses: angular/dev-infra/github-actions/bazel/setup@942d738d8f4d65b161d06e6c399fefec318cdbfe - 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@c855fffb4b01bc06e743eb3bdfd54c866af09ad8 + uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@942d738d8f4d65b161d06e6c399fefec318cdbfe - name: Setup Bazel - uses: angular/dev-infra/github-actions/bazel/setup@c855fffb4b01bc06e743eb3bdfd54c866af09ad8 + uses: angular/dev-infra/github-actions/bazel/setup@942d738d8f4d65b161d06e6c399fefec318cdbfe - name: Setup Bazel RBE - uses: angular/dev-infra/github-actions/bazel/configure-remote@c855fffb4b01bc06e743eb3bdfd54c866af09ad8 + uses: angular/dev-infra/github-actions/bazel/configure-remote@942d738d8f4d65b161d06e6c399fefec318cdbfe with: google_credential: ${{ secrets.RBE_TRUSTED_BUILDS_USER }} - name: Install node modules @@ -61,51 +61,50 @@ jobs: runs-on: ubuntu-latest steps: - name: Initialize environment - uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@c855fffb4b01bc06e743eb3bdfd54c866af09ad8 + uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@942d738d8f4d65b161d06e6c399fefec318cdbfe - name: Setup Bazel - uses: angular/dev-infra/github-actions/bazel/setup@c855fffb4b01bc06e743eb3bdfd54c866af09ad8 + uses: angular/dev-infra/github-actions/bazel/setup@942d738d8f4d65b161d06e6c399fefec318cdbfe - name: Setup Bazel RBE - uses: angular/dev-infra/github-actions/bazel/configure-remote@c855fffb4b01bc06e743eb3bdfd54c866af09ad8 + uses: angular/dev-infra/github-actions/bazel/configure-remote@942d738d8f4d65b161d06e6c399fefec318cdbfe with: google_credential: ${{ secrets.RBE_TRUSTED_BUILDS_USER }} - name: Install node modules run: pnpm install --frozen-lockfile - name: Run module and package tests - run: pnpm bazel test -- //... -//tests/legacy-cli/... + run: pnpm bazel test -- //... -//tests/... e2e: needs: test strategy: fail-fast: false matrix: - os: [ubuntu-latest] node: [20, 22, 24] - subset: [npm, esbuild] + subset: [esbuild, webpack] shard: [0, 1, 2, 3, 4, 5] - runs-on: ${{ matrix.os }} + runs-on: ubuntu-latest steps: - name: Initialize environment - uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@c855fffb4b01bc06e743eb3bdfd54c866af09ad8 + uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@942d738d8f4d65b161d06e6c399fefec318cdbfe - name: Install node modules run: pnpm install --frozen-lockfile - name: Setup Bazel - uses: angular/dev-infra/github-actions/bazel/setup@c855fffb4b01bc06e743eb3bdfd54c866af09ad8 + uses: angular/dev-infra/github-actions/bazel/setup@942d738d8f4d65b161d06e6c399fefec318cdbfe - name: Setup Bazel RBE - uses: angular/dev-infra/github-actions/bazel/configure-remote@c855fffb4b01bc06e743eb3bdfd54c866af09ad8 + uses: angular/dev-infra/github-actions/bazel/configure-remote@942d738d8f4d65b161d06e6c399fefec318cdbfe with: google_credential: ${{ secrets.RBE_TRUSTED_BUILDS_USER }} - 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 }} + run: pnpm bazel test --test_env=E2E_SHARD_TOTAL=6 --test_env=E2E_SHARD_INDEX=${{ matrix.shard }} --config=e2e //tests:e2e.${{ matrix.subset }}_node${{ matrix.node }} build-e2e-windows: runs-on: ubuntu-latest steps: - name: Initialize environment - uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@c855fffb4b01bc06e743eb3bdfd54c866af09ad8 + uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@942d738d8f4d65b161d06e6c399fefec318cdbfe - name: Setup Bazel - uses: angular/dev-infra/github-actions/bazel/setup@c855fffb4b01bc06e743eb3bdfd54c866af09ad8 + uses: angular/dev-infra/github-actions/bazel/setup@942d738d8f4d65b161d06e6c399fefec318cdbfe - name: Setup Bazel RBE - uses: angular/dev-infra/github-actions/bazel/configure-remote@c855fffb4b01bc06e743eb3bdfd54c866af09ad8 + uses: angular/dev-infra/github-actions/bazel/configure-remote@942d738d8f4d65b161d06e6c399fefec318cdbfe with: google_credential: ${{ secrets.RBE_TRUSTED_BUILDS_USER }} - name: Install node modules @@ -114,15 +113,15 @@ jobs: run: | pnpm bazel build \ --config=e2e \ - //tests/legacy-cli:e2e.npm_node22 \ - //tests/legacy-cli:e2e.esbuild_node22 \ + //tests:e2e.webpack_node22 \ + //tests:e2e.esbuild_node22 \ --platforms=tools:windows_x64 - name: Store built Windows E2E tests - uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 with: name: win-e2e-build-artifacts path: | - dist/bin/tests/legacy-cli/** + dist/bin/tests/** !**/node_modules/** retention-days: 1 if-no-files-found: 'error' @@ -132,21 +131,20 @@ jobs: strategy: fail-fast: false matrix: - os: [windows-2025] node: [22] - subset: [npm, esbuild] + subset: [esbuild, webpack] shard: [0, 1, 2, 3, 4, 5] - runs-on: ${{ matrix.os }} + runs-on: windows-2025 steps: - name: Initialize environment - uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@c855fffb4b01bc06e743eb3bdfd54c866af09ad8 + uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@942d738d8f4d65b161d06e6c399fefec318cdbfe - name: Install node modules run: pnpm install --frozen-lockfile - name: Download built Windows E2E tests - uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.0 + uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0 with: name: win-e2e-build-artifacts - path: dist/bin/tests/legacy-cli/ + path: dist/bin/tests/ - name: Run CLI E2E tests uses: ./.github/shared-actions/windows-bazel-test with: @@ -160,26 +158,23 @@ jobs: strategy: fail-fast: false matrix: - # These tests can generate a significant amount of temp files, especially when - # flaky targets are retried. The larger machine type comes with 2x more SSD space. - os: [ubuntu-latest-4core] node: [22] - subset: [yarn, pnpm] + subset: [yarn, pnpm, bun] shard: [0, 1, 2] - runs-on: ${{ matrix.os }} + runs-on: ubuntu-latest steps: - name: Initialize environment - uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@c855fffb4b01bc06e743eb3bdfd54c866af09ad8 + uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@942d738d8f4d65b161d06e6c399fefec318cdbfe - name: Install node modules run: pnpm install --frozen-lockfile - name: Setup Bazel - uses: angular/dev-infra/github-actions/bazel/setup@c855fffb4b01bc06e743eb3bdfd54c866af09ad8 + uses: angular/dev-infra/github-actions/bazel/setup@942d738d8f4d65b161d06e6c399fefec318cdbfe - name: Setup Bazel RBE - uses: angular/dev-infra/github-actions/bazel/configure-remote@c855fffb4b01bc06e743eb3bdfd54c866af09ad8 + uses: angular/dev-infra/github-actions/bazel/configure-remote@942d738d8f4d65b161d06e6c399fefec318cdbfe with: google_credential: ${{ secrets.RBE_TRUSTED_BUILDS_USER }} - 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 }} + run: pnpm bazel test --test_env=E2E_SHARD_TOTAL=3 --test_env=E2E_SHARD_INDEX=${{ matrix.shard }} --config=e2e //tests:e2e.${{ matrix.subset }}_node${{ matrix.node }} e2e-snapshots: needs: test @@ -187,24 +182,23 @@ jobs: strategy: fail-fast: false matrix: - os: [ubuntu-latest] node: [22] - subset: [npm, esbuild] + subset: [esbuild, webpack] shard: [0, 1, 2, 3, 4, 5] - runs-on: ${{ matrix.os }} + runs-on: ubuntu-latest steps: - name: Initialize environment - uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@c855fffb4b01bc06e743eb3bdfd54c866af09ad8 + uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@942d738d8f4d65b161d06e6c399fefec318cdbfe - name: Install node modules run: pnpm install --frozen-lockfile - name: Setup Bazel - uses: angular/dev-infra/github-actions/bazel/setup@c855fffb4b01bc06e743eb3bdfd54c866af09ad8 + uses: angular/dev-infra/github-actions/bazel/setup@942d738d8f4d65b161d06e6c399fefec318cdbfe - name: Setup Bazel RBE - uses: angular/dev-infra/github-actions/bazel/configure-remote@c855fffb4b01bc06e743eb3bdfd54c866af09ad8 + uses: angular/dev-infra/github-actions/bazel/configure-remote@942d738d8f4d65b161d06e6c399fefec318cdbfe with: google_credential: ${{ secrets.RBE_TRUSTED_BUILDS_USER }} - 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 }} + run: pnpm bazel test --test_env=E2E_SHARD_TOTAL=6 --test_env=E2E_SHARD_INDEX=${{ matrix.shard }} --config=e2e //tests:e2e.snapshots.${{ matrix.subset }}_node${{ matrix.node }} browsers: needs: build @@ -214,13 +208,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@c855fffb4b01bc06e743eb3bdfd54c866af09ad8 + uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@942d738d8f4d65b161d06e6c399fefec318cdbfe - name: Install node modules run: pnpm install --frozen-lockfile - name: Setup Bazel - uses: angular/dev-infra/github-actions/bazel/setup@c855fffb4b01bc06e743eb3bdfd54c866af09ad8 + uses: angular/dev-infra/github-actions/bazel/setup@942d738d8f4d65b161d06e6c399fefec318cdbfe - name: Setup Bazel RBE - uses: angular/dev-infra/github-actions/bazel/configure-remote@c855fffb4b01bc06e743eb3bdfd54c866af09ad8 + uses: angular/dev-infra/github-actions/bazel/configure-remote@942d738d8f4d65b161d06e6c399fefec318cdbfe with: google_credential: ${{ secrets.RBE_TRUSTED_BUILDS_USER }} - name: Run E2E Browser tests @@ -235,9 +229,9 @@ jobs: run: | ./scripts/saucelabs/start-tunnel.sh & ./scripts/saucelabs/wait-for-tunnel.sh - pnpm bazel test --config=saucelabs //tests/legacy-cli:e2e.saucelabs + pnpm bazel test --config=saucelabs //tests:e2e.saucelabs ./scripts/saucelabs/stop-tunnel.sh - - uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 + - uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 if: ${{ failure() }} with: name: sauce-connect-log @@ -250,11 +244,11 @@ jobs: CIRCLE_BRANCH: ${{ github.ref_name }} steps: - name: Initialize environment - uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@c855fffb4b01bc06e743eb3bdfd54c866af09ad8 + uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@942d738d8f4d65b161d06e6c399fefec318cdbfe - name: Install node modules run: pnpm install --frozen-lockfile - name: Setup Bazel - uses: angular/dev-infra/github-actions/bazel/setup@c855fffb4b01bc06e743eb3bdfd54c866af09ad8 + uses: angular/dev-infra/github-actions/bazel/setup@942d738d8f4d65b161d06e6c399fefec318cdbfe - 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 fb4789033a1a..3932ccf6a3b8 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@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 with: persist-credentials: false - name: Initialize CodeQL - uses: github/codeql-action/init@0499de31b99561a6d14a36a5f662c2a54f91beee # v4.31.2 + uses: github/codeql-action/init@5d4e8d1aca955e8d8589aabd499c5cae939e33c7 # v4.31.9 with: languages: javascript-typescript build-mode: none config-file: .github/codeql/config.yml - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@0499de31b99561a6d14a36a5f662c2a54f91beee # v4.31.2 + uses: github/codeql-action/analyze@5d4e8d1aca955e8d8589aabd499c5cae939e33c7 # v4.31.9 with: category: '/language:javascript-typescript' diff --git a/.github/workflows/dev-infra.yml b/.github/workflows/dev-infra.yml index 4768ebd16a36..23811dc9f573 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@c855fffb4b01bc06e743eb3bdfd54c866af09ad8 + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + - uses: angular/dev-infra/github-actions/pull-request-labeling@942d738d8f4d65b161d06e6c399fefec318cdbfe 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@c855fffb4b01bc06e743eb3bdfd54c866af09ad8 + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + - uses: angular/dev-infra/github-actions/post-approval-changes@942d738d8f4d65b161d06e6c399fefec318cdbfe with: angular-robot-key: ${{ secrets.ANGULAR_ROBOT_PRIVATE_KEY }} diff --git a/.github/workflows/feature-requests.yml b/.github/workflows/feature-requests.yml index 0c61f5b0ded6..fae7787a6dff 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@c855fffb4b01bc06e743eb3bdfd54c866af09ad8 + - uses: angular/dev-infra/github-actions/feature-request@942d738d8f4d65b161d06e6c399fefec318cdbfe with: angular-robot-key: ${{ secrets.ANGULAR_ROBOT_PRIVATE_KEY }} diff --git a/.github/workflows/perf.yml b/.github/workflows/perf.yml index 508c7f109e44..abe20d3f3921 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@c855fffb4b01bc06e743eb3bdfd54c866af09ad8 + uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@942d738d8f4d65b161d06e6c399fefec318cdbfe - 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@c855fffb4b01bc06e743eb3bdfd54c866af09ad8 + uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@942d738d8f4d65b161d06e6c399fefec318cdbfe - name: Setup Bazel - uses: angular/dev-infra/github-actions/bazel/setup@c855fffb4b01bc06e743eb3bdfd54c866af09ad8 + uses: angular/dev-infra/github-actions/bazel/setup@942d738d8f4d65b161d06e6c399fefec318cdbfe - 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 aab59a7bc029..d103d3f2edcd 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@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 with: persist-credentials: false - uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2 @@ -28,17 +28,17 @@ jobs: with: filters: | snapshots: - - 'tests/legacy-cli/e2e/ng-snapshot/package.json' + - 'tests/e2e/ng-snapshot/package.json' lint: runs-on: ubuntu-latest steps: - name: Initialize environment - uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@c855fffb4b01bc06e743eb3bdfd54c866af09ad8 + uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@942d738d8f4d65b161d06e6c399fefec318cdbfe - name: Setup Bazel - uses: angular/dev-infra/github-actions/bazel/setup@c855fffb4b01bc06e743eb3bdfd54c866af09ad8 + uses: angular/dev-infra/github-actions/bazel/setup@942d738d8f4d65b161d06e6c399fefec318cdbfe - name: Setup ESLint Caching - uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 + uses: actions/cache@9255dc7a253b0ccc959486e2bca901246202afeb # v5.0.1 with: path: .eslintcache key: ${{ runner.os }}-${{ hashFiles('.eslintrc.json') }} @@ -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@c855fffb4b01bc06e743eb3bdfd54c866af09ad8 - name: Check tooling setup run: pnpm check-tooling-setup - name: Check commit message @@ -67,22 +65,24 @@ 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@942d738d8f4d65b161d06e6c399fefec318cdbfe build: runs-on: ubuntu-latest steps: - name: Initialize environment - uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@c855fffb4b01bc06e743eb3bdfd54c866af09ad8 + uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@942d738d8f4d65b161d06e6c399fefec318cdbfe - name: Setup Bazel - uses: angular/dev-infra/github-actions/bazel/setup@c855fffb4b01bc06e743eb3bdfd54c866af09ad8 + uses: angular/dev-infra/github-actions/bazel/setup@942d738d8f4d65b161d06e6c399fefec318cdbfe - name: Setup Bazel RBE - uses: angular/dev-infra/github-actions/bazel/configure-remote@c855fffb4b01bc06e743eb3bdfd54c866af09ad8 + uses: angular/dev-infra/github-actions/bazel/configure-remote@942d738d8f4d65b161d06e6c399fefec318cdbfe - name: Install node modules run: pnpm install --frozen-lockfile - name: Build release targets run: pnpm ng-dev release build - name: Store PR release packages - uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 with: name: packages path: dist/releases/*.tgz @@ -93,61 +93,60 @@ jobs: runs-on: ubuntu-latest steps: - name: Initialize environment - uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@c855fffb4b01bc06e743eb3bdfd54c866af09ad8 + uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@942d738d8f4d65b161d06e6c399fefec318cdbfe - name: Setup Bazel - uses: angular/dev-infra/github-actions/bazel/setup@c855fffb4b01bc06e743eb3bdfd54c866af09ad8 + uses: angular/dev-infra/github-actions/bazel/setup@942d738d8f4d65b161d06e6c399fefec318cdbfe - name: Setup Bazel RBE - uses: angular/dev-infra/github-actions/bazel/configure-remote@c855fffb4b01bc06e743eb3bdfd54c866af09ad8 + uses: angular/dev-infra/github-actions/bazel/configure-remote@942d738d8f4d65b161d06e6c399fefec318cdbfe - name: Install node modules run: pnpm install --frozen-lockfile - name: Run module and package tests - run: pnpm bazel test -- //... -//tests/legacy-cli/... + run: pnpm bazel test -- //... -//tests/... e2e: needs: build strategy: fail-fast: false matrix: - os: [ubuntu-latest] node: [22] - subset: [npm, esbuild] + subset: [esbuild, webpack] shard: [0, 1, 2, 3, 4, 5] - runs-on: ${{ matrix.os }} + runs-on: ubuntu-latest steps: - name: Initialize environment - uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@c855fffb4b01bc06e743eb3bdfd54c866af09ad8 + uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@942d738d8f4d65b161d06e6c399fefec318cdbfe - name: Install node modules run: pnpm install --frozen-lockfile - name: Setup Bazel - uses: angular/dev-infra/github-actions/bazel/setup@c855fffb4b01bc06e743eb3bdfd54c866af09ad8 + uses: angular/dev-infra/github-actions/bazel/setup@942d738d8f4d65b161d06e6c399fefec318cdbfe - name: Setup Bazel RBE - uses: angular/dev-infra/github-actions/bazel/configure-remote@c855fffb4b01bc06e743eb3bdfd54c866af09ad8 + uses: angular/dev-infra/github-actions/bazel/configure-remote@942d738d8f4d65b161d06e6c399fefec318cdbfe - 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 }} + run: pnpm bazel test --test_env=E2E_SHARD_TOTAL=6 --test_env=E2E_SHARD_INDEX=${{ matrix.shard }} --config=e2e //tests:e2e.${{ matrix.subset }}_node${{ matrix.node }} build-e2e-windows-subset: runs-on: ubuntu-latest steps: - name: Initialize environment - uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@c855fffb4b01bc06e743eb3bdfd54c866af09ad8 + uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@942d738d8f4d65b161d06e6c399fefec318cdbfe - name: Setup Bazel - uses: angular/dev-infra/github-actions/bazel/setup@c855fffb4b01bc06e743eb3bdfd54c866af09ad8 + uses: angular/dev-infra/github-actions/bazel/setup@942d738d8f4d65b161d06e6c399fefec318cdbfe - name: Setup Bazel RBE - uses: angular/dev-infra/github-actions/bazel/configure-remote@c855fffb4b01bc06e743eb3bdfd54c866af09ad8 + uses: angular/dev-infra/github-actions/bazel/configure-remote@942d738d8f4d65b161d06e6c399fefec318cdbfe - name: Install node modules run: pnpm install --frozen-lockfile - name: Build E2E tests for Windows on Linux run: | pnpm bazel build \ --config=e2e \ - //tests/legacy-cli:e2e.esbuild_node22 \ + //tests:e2e.esbuild_node22 \ --platforms=tools:windows_x64 - name: Store built Windows E2E tests - uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 with: name: win-e2e-build-artifacts path: | - dist/bin/tests/legacy-cli/** + dist/bin/tests/** !**/node_modules/** retention-days: 1 if-no-files-found: 'error' @@ -157,45 +156,42 @@ jobs: runs-on: windows-2025 steps: - name: Initialize environment - uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@c855fffb4b01bc06e743eb3bdfd54c866af09ad8 + uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@942d738d8f4d65b161d06e6c399fefec318cdbfe - name: Install node modules run: pnpm install --frozen-lockfile - name: Download built Windows E2E tests - uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.0 + uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0 with: name: win-e2e-build-artifacts - path: dist/bin/tests/legacy-cli/ + path: dist/bin/tests/ - name: Run CLI E2E tests uses: ./.github/shared-actions/windows-bazel-test with: test_target_name: e2e.esbuild_node22 env: E2E_SHARD_TOTAL: 1 - TESTBRIDGE_TEST_ONLY: tests/basic/{build,rebuild}.ts + TESTBRIDGE_TEST_ONLY: tests/basic/{build,rebuild,serve}.ts e2e-package-managers: needs: build strategy: fail-fast: false matrix: - # These tests can generate a significant amount of temp files, especially when - # flaky targets are retried. The larger machine type comes with 2x more SSD space. - os: [ubuntu-latest-4core] node: [22] - subset: [yarn, pnpm] + subset: [yarn, pnpm, bun] shard: [0, 1, 2] - runs-on: ${{ matrix.os }} + runs-on: ubuntu-latest steps: - name: Initialize environment - uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@c855fffb4b01bc06e743eb3bdfd54c866af09ad8 + uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@942d738d8f4d65b161d06e6c399fefec318cdbfe - name: Install node modules run: pnpm install --frozen-lockfile - name: Setup Bazel - uses: angular/dev-infra/github-actions/bazel/setup@c855fffb4b01bc06e743eb3bdfd54c866af09ad8 + uses: angular/dev-infra/github-actions/bazel/setup@942d738d8f4d65b161d06e6c399fefec318cdbfe - name: Setup Bazel RBE - uses: angular/dev-infra/github-actions/bazel/configure-remote@c855fffb4b01bc06e743eb3bdfd54c866af09ad8 + uses: angular/dev-infra/github-actions/bazel/configure-remote@942d738d8f4d65b161d06e6c399fefec318cdbfe - 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 }} + run: pnpm bazel test --test_env=E2E_SHARD_TOTAL=3 --test_env=E2E_SHARD_INDEX=${{ matrix.shard }} --config=e2e //tests:e2e.${{ matrix.subset }}_node${{ matrix.node }} e2e-snapshots: needs: [analyze, build] @@ -203,19 +199,18 @@ jobs: strategy: fail-fast: false matrix: - os: [ubuntu-latest] node: [22] - subset: [npm, esbuild] + subset: [esbuild, webpack] shard: [0, 1, 2, 3, 4, 5] - runs-on: ${{ matrix.os }} + runs-on: ubuntu-latest steps: - name: Initialize environment - uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@c855fffb4b01bc06e743eb3bdfd54c866af09ad8 + uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@942d738d8f4d65b161d06e6c399fefec318cdbfe - name: Install node modules run: pnpm install --frozen-lockfile - name: Setup Bazel - uses: angular/dev-infra/github-actions/bazel/setup@c855fffb4b01bc06e743eb3bdfd54c866af09ad8 + uses: angular/dev-infra/github-actions/bazel/setup@942d738d8f4d65b161d06e6c399fefec318cdbfe - name: Setup Bazel RBE - uses: angular/dev-infra/github-actions/bazel/configure-remote@c855fffb4b01bc06e743eb3bdfd54c866af09ad8 + uses: angular/dev-infra/github-actions/bazel/configure-remote@942d738d8f4d65b161d06e6c399fefec318cdbfe - 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 }} + run: pnpm bazel test --test_env=E2E_SHARD_TOTAL=6 --test_env=E2E_SHARD_INDEX=${{ matrix.shard }} --config=e2e //tests:e2e.snapshots.${{ matrix.subset }}_node${{ matrix.node }} diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml index 5b82ca3bd6e6..63ad55057037 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@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 with: persist-credentials: false @@ -38,7 +38,7 @@ jobs: # Upload the results as artifacts. - name: 'Upload artifact' - uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 with: name: SARIF file path: results.sarif @@ -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@0499de31b99561a6d14a36a5f662c2a54f91beee # v4.31.2 + uses: github/codeql-action/upload-sarif@5d4e8d1aca955e8d8589aabd499c5cae939e33c7 # v4.31.9 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 4a3b270ce0a7..d30060674355 100644 --- a/.ng-dev/commit-message.mjs +++ b/.ng-dev/commit-message.mjs @@ -1,4 +1,4 @@ -import { packages } from '../scripts/packages.mjs'; +import { packages } from '../scripts/packages.mts'; /** * The configuration for `ng-dev commit-message` commands. diff --git a/.ng-dev/format.mjs b/.ng-dev/format.mjs index 98115c626abd..c750eeea395c 100644 --- a/.ng-dev/format.mjs +++ b/.ng-dev/format.mjs @@ -4,8 +4,6 @@ * @type { import("@angular/ng-dev").FormatConfig } */ export const format = { - 'prettier': { - matchers: ['**/*.{mts,cts,ts,mjs,cjs,js,json,yml,yaml,md}'], - }, + 'prettier': true, 'buildifier': true, }; diff --git a/.ng-dev/github.mjs b/.ng-dev/github.mjs index 15e228fb5ae3..467741f70f83 100644 --- a/.ng-dev/github.mjs +++ b/.ng-dev/github.mjs @@ -1,7 +1,7 @@ /** * Github configuration for the ng-dev command. This repository is * used as remote for the merge script. - * + * * @type { import("@angular/ng-dev").GithubConfig } */ export const github = { @@ -9,4 +9,5 @@ export const github = { name: 'angular-cli', mainBranchName: 'main', useNgDevAuthService: true, + mergeMode: 'team-only', }; diff --git a/.ng-dev/release.mjs b/.ng-dev/release.mjs index 3d097ea80b11..2aadf9db122c 100644 --- a/.ng-dev/release.mjs +++ b/.ng-dev/release.mjs @@ -1,5 +1,5 @@ import semver from 'semver'; -import { releasePackages } from '../scripts/packages.mjs'; +import { releasePackages } from '../scripts/packages.mts'; /** * Configuration for the `ng-dev release` command. @@ -12,14 +12,13 @@ export const release = { buildPackages: async () => { // The `performNpmReleaseBuild` function is loaded at runtime to avoid loading additional // files and dependencies unless a build is required. - const { performNpmReleaseBuild } = await import('../scripts/build-packages-dist.mjs'); + const { performNpmReleaseBuild } = await import('../scripts/build-packages-dist.mts'); return performNpmReleaseBuild(); }, prereleaseCheck: async (newVersionStr) => { const newVersion = new semver.SemVer(newVersionStr); - const { assertValidDependencyRanges } = await import( - '../scripts/release-checks/dependency-ranges/index.mjs' - ); + const { assertValidDependencyRanges } = + await import('../scripts/release-checks/dependency-ranges/index.mts'); await assertValidDependencyRanges(newVersion, releasePackages); }, diff --git a/.ng-dev/tsconfig.json b/.ng-dev/tsconfig.json index 9f0a0f84be18..a250849eb673 100644 --- a/.ng-dev/tsconfig.json +++ b/.ng-dev/tsconfig.json @@ -3,6 +3,9 @@ "compilerOptions": { "resolveJsonModule": true, "allowJs": true, + "rewriteRelativeImportExtensions": true, + "erasableSyntaxOnly": true, + "verbatimModuleSyntax": true, "module": "Node16", "moduleResolution": "Node16", "checkJs": true, diff --git a/.prettierignore b/.prettierignore index b0b71acaf241..1adbf7759080 100644 --- a/.prettierignore +++ b/.prettierignore @@ -11,8 +11,7 @@ /packages/schematics/angular/third_party/ /README.md /CONTRIBUTING.md -.yarn/ dist/ -/tests/legacy-cli/e2e/assets/ +/tests/e2e/assets/ /tools/test/*.json pnpm-lock.yaml diff --git a/BUILD.bazel b/BUILD.bazel index 57090fe9772a..99bc6eb0355f 100644 --- a/BUILD.bazel +++ b/BUILD.bazel @@ -108,15 +108,3 @@ validate_ts_version_matching( module_lock_file = "MODULE.bazel.lock", package_json = "package.json", ) - -# This is needed following https://github.com/bazel-contrib/rules_nodejs/pull/3859 -toolchain( - name = "node22_windows_no_exec_config_toolchain", - exec_compatible_with = [], - target_compatible_with = [ - "@platforms//os:windows", - "@platforms//cpu:x86_64", - ], - toolchain = "@node22_windows_amd64//:toolchain", - toolchain_type = "@rules_nodejs//nodejs:toolchain_type", -) diff --git a/CHANGELOG.md b/CHANGELOG.md index 68b2de6c6cc7..c9760339d143 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,438 +1,569 @@ - + -# 21.0.0-rc.5 (2025-11-17) +# 21.1.0-next.3 (2025-12-18) -### @schematics/angular +### @angular/cli -| Commit | Type | Description | -| --------------------------------------------------------------------------------------------------- | ---- | ----------------------------------------------------------------- | -| [e8feba9ee](https://github.com/angular/angular-cli/commit/e8feba9ee163f688c51d6463336474591e886647) | fix | add missing typeSeparator to main.ts.template file | -| [6615fcf03](https://github.com/angular/angular-cli/commit/6615fcf037686cd96e97b469937b7f0736afaa77) | fix | issues in apps generated with '--file-name-style-guide=2016' flag | +| Commit | Type | Description | +| --------------------------------------------------------------------------------------------------- | ---- | ------------------------------------------------------------------------- | +| [348096623](https://github.com/angular/angular-cli/commit/348096623326857a5d8cf77d56712776e1190180) | fix | enhance list_projects MCP tool file system traversal and symlink handling | +| [032257a6d](https://github.com/angular/angular-cli/commit/032257a6d00360d2c4e6d5406409dcfa5b27d1d5) | fix | improve signal forms lesson examples in AI tutor | +| [18d74dde8](https://github.com/angular/angular-cli/commit/18d74dde8938dbe566df80753d5c148c19040179) | fix | rename mcp devserver tools to comply with naming spec | +| [a15db28b2](https://github.com/angular/angular-cli/commit/a15db28b29f6f43bef1ed1ca7c6a963d9943f801) | perf | cache resolved specific version in package manager abstraction | -### @angular/build +### @schematics/angular | Commit | Type | Description | | --------------------------------------------------------------------------------------------------- | ---- | ---------------------------------------------------------- | -| [a11dd31f0](https://github.com/angular/angular-cli/commit/a11dd31f0c80a189e7dcb3172c89a731cfc34702) | fix | configure Vitest cache to use Angular cache | -| [f05ffd104](https://github.com/angular/angular-cli/commit/f05ffd104255e86fe93f3736e1430f940cb83007) | fix | correct Vitest coverage include handling for virtual files | -| [49b65aba8](https://github.com/angular/angular-cli/commit/49b65aba8d7cd2839776e987366b981d7762760c) | fix | disable Vitest test isolation by default | -| [fa5c92346](https://github.com/angular/angular-cli/commit/fa5c92346d14a6ad03aa30ad6392fc649038605e) | fix | prioritize string type for runnerConfig schema | +| [52ace04a7](https://github.com/angular/angular-cli/commit/52ace04a7ca1c102fdf1addf5ab6fe400c0eab0e) | fix | improve VS Code background compilation start/end detection | +| [288a9225c](https://github.com/angular/angular-cli/commit/288a9225c83edec9560e2b39901740e792c54d27) | fix | remove `inlineSources` from library tsconfig template | - - - +### @angular/build -# 21.0.0-rc.4 (2025-11-14) +| Commit | Type | Description | +| --------------------------------------------------------------------------------------------------- | ---- | -------------------------------------------------------- | +| [98c207bc0](https://github.com/angular/angular-cli/commit/98c207bc0e44caccd6fffa5b8d3a013a2a3a871a) | fix | add browser condition to resolver for vitest | +| [f39f7ee95](https://github.com/angular/angular-cli/commit/f39f7ee9529113f7c75d0e0e3ffa628fed9ce92f) | fix | allow non-prefixed requests when using SSR and base href | +| [7c7e6a614](https://github.com/angular/angular-cli/commit/7c7e6a6142a9d294e04c612463449d2a4360e692) | fix | conditionally manage Vitest UI option | +| [edeb41c0e](https://github.com/angular/angular-cli/commit/edeb41c0e01881c21dec4d7f63fe8d302ce0521d) | fix | ensure tests run when compilation error is resolved | +| [9744af1f8](https://github.com/angular/angular-cli/commit/9744af1f82a8e9c2816adf636e4e8a1a8be06c60) | fix | remove LmdbCacheStore export from private API | -### @angular/build +### @angular/ssr -| Commit | Type | Description | -| --------------------------------------------------------------------------------------------------- | ---- | ----------------------------------------------------- | -| [f7c4a4c1d](https://github.com/angular/angular-cli/commit/f7c4a4c1dcd575dec82cc06597e3d6620b1be729) | fix | enhance Vitest resolution for optimal package loading | -| [0830f4fb5](https://github.com/angular/angular-cli/commit/0830f4fb549e2c45b1ef752dd42f002a1347d7c8) | fix | ensure TestBed cleanup hooks are always registered | +| Commit | Type | Description | +| --------------------------------------------------------------------------------------------------- | ---- | -------------------------------------------------------------------- | +| [e5651224b](https://github.com/angular/angular-cli/commit/e5651224b5086335d48b133e1d0b9c8536c22e5f) | fix | add leading slash to well-known non-Angular URLs | +| [081e31337](https://github.com/angular/angular-cli/commit/081e3133764c9a23f70969bfd182259be34a411e) | fix | propagate status code to redirect | +| [2d56a319d](https://github.com/angular/angular-cli/commit/2d56a319d8d45f36d9e5d958cbbd96e195c2c15e) | fix | skip SSR processing for well-known non-Angular URLs like favicon.ico | - + -# 21.0.0-rc.3 (2025-11-13) +# 21.0.4 (2025-12-18) -### @angular/cli +### @schematics/angular -| Commit | Type | Description | -| --------------------------------------------------------------------------------------------------- | ---- | ------------------------------------------------------- | -| [4deac3ec7](https://github.com/angular/angular-cli/commit/4deac3ec785b1a53156aac90441d0ed129df71ef) | fix | support multi-database search in find_examples MCP tool | +| Commit | Type | Description | +| --------------------------------------------------------------------------------------------------- | ---- | ---------------------------------------------------------- | +| [b671245b9](https://github.com/angular/angular-cli/commit/b671245b9d3ba98ac0f66dbd34f272539113be61) | fix | improve VS Code background compilation start/end detection | +| [85a28dec7](https://github.com/angular/angular-cli/commit/85a28dec771cce77a3ffee35f419b5fedca807b8) | fix | remove `inlineSources` from library tsconfig template | ### @angular/build +| Commit | Type | Description | +| --------------------------------------------------------------------------------------------------- | ---- | -------------------------------------------------------- | +| [deb4fff61](https://github.com/angular/angular-cli/commit/deb4fff6196d2eb147e358a7143e2ada2b6114c9) | fix | add browser condition to resolver for vitest | +| [570ce8d3e](https://github.com/angular/angular-cli/commit/570ce8d3eeb280eeb6dca6ba54593c9325674741) | fix | allow non-prefixed requests when using SSR and base href | +| [4dd3c1a32](https://github.com/angular/angular-cli/commit/4dd3c1a324c8e90808cc1c5febf65c8fa49dd3b9) | fix | conditionally manage Vitest UI option | +| [4b8b7caec](https://github.com/angular/angular-cli/commit/4b8b7caece41f86746321a98786dfdff499582b6) | fix | ensure tests run when compilation error is resolved | +| [bef4fcecb](https://github.com/angular/angular-cli/commit/bef4fcecb6d116f9f022da845f06708cf29be02a) | fix | remove LmdbCacheStore export from private API | + +### @angular/ssr + | Commit | Type | Description | | --------------------------------------------------------------------------------------------------- | ---- | -------------------------------------------------------------------- | -| [fcdbf6c19](https://github.com/angular/angular-cli/commit/fcdbf6c19b5a8549011aebec9e517feb12a99895) | fix | allow `globals` to be set to false | -| [931c62d20](https://github.com/angular/angular-cli/commit/931c62d20915c6c772b61d76ab88657c0858f6bd) | fix | allow unit-test runner config with absolute path | -| [fec106b60](https://github.com/angular/angular-cli/commit/fec106b60553394aab51d713e5437a713085089b) | fix | enhance Vitest dependency externalization and pre-bundling | -| [ee5e127d5](https://github.com/angular/angular-cli/commit/ee5e127d551269fa9a3e39b9b28e38d7ab35806f) | fix | ensure `ɵgetOrCreateAngularServerApp` is always defined after errors | -| [55145f582](https://github.com/angular/angular-cli/commit/55145f582253b4ecb47add7ff2ef459b7535dfdb) | fix | ensure Vitest setup files are executed in order | -| [6576bb598](https://github.com/angular/angular-cli/commit/6576bb5985c18dca7cecd9509939c2a78bf9758a) | fix | remove explicit test isolation configuration | +| [bb54747da](https://github.com/angular/angular-cli/commit/bb54747da69fb15b6c2ebb52b45a83cbff3231c8) | fix | add leading slash to well-known non-Angular URLs | +| [0cfe2e749](https://github.com/angular/angular-cli/commit/0cfe2e749f50b832c64bbba322eb0cef7ad40365) | fix | propagate status code to redirect | +| [eadadb848](https://github.com/angular/angular-cli/commit/eadadb848ca7fa45c4dda835af39400e017bbe1c) | fix | skip SSR processing for well-known non-Angular URLs like favicon.ico | - + -# 21.0.0-rc.2 (2025-11-12) +# 21.1.0-next.2 (2025-12-10) ### @angular/cli -| Commit | Type | Description | -| --------------------------------------------------------------------------------------------------- | ---- | ------------------------------------------------------ | -| [c17d7a929](https://github.com/angular/angular-cli/commit/c17d7a929adccb77f3c2c33e70005f50032d8cae) | fix | add schema versioning and metadata to example database | -| [1be35b343](https://github.com/angular/angular-cli/commit/1be35b3433179481be85ea1cb892d66170e0aebe) | fix | promote zoneless migration MCP tool to stable | +| Commit | Type | Description | +| --------------------------------------------------------------------------------------------------- | ---- | -------------------------------------------------------------- | +| [d8b76e93d](https://github.com/angular/angular-cli/commit/d8b76e93d3e9e4e7bd7ad6e12fdf59cd663cbb8e) | fix | correctly handle yarn classic tag manifest fetching | +| [7ab5c0b0a](https://github.com/angular/angular-cli/commit/7ab5c0b0a1c637f3e0adb71486e5e7e8716561e4) | fix | correctly spawn package managers on Windows in new abstraction | +| [240588b7e](https://github.com/angular/angular-cli/commit/240588b7e3c8698c83110793ab98d20caee4e1a4) | perf | optimize `ng add` version discovery | -### @schematics/angular +### @angular-devkit/build-angular -| Commit | Type | Description | -| --------------------------------------------------------------------------------------------------- | ---- | --------------------------------------------------- | -| [02f579f6e](https://github.com/angular/angular-cli/commit/02f579f6e7e490744deea5a193b0751ce31262db) | fix | correct `tsconfig.spec.json` include for spec files | +| Commit | Type | Description | +| --------------------------------------------------------------------------------------------------- | ---- | ----------------------------------------------------------------------------- | +| [985aa18d0](https://github.com/angular/angular-cli/commit/985aa18d0b6cf728c498c0873793e131a4c416c1) | fix | conditionally provide Zone.js change detection in the built-in test main file | ### @angular/build -| Commit | Type | Description | -| --------------------------------------------------------------------------------------------------- | ---- | ----------------------------------------------------------------------------------------------------------------------------- | -| [63c98741a](https://github.com/angular/angular-cli/commit/63c98741adcd21701b8bc572e9410cc1cf4f91c3) | fix | add webcontainer support for Vitest browser provider | -| [07f712253](https://github.com/angular/angular-cli/commit/07f712253bb6c37d27ae7be9845f497002b0780c) | fix | correctly handle absolute paths and casing in test discovery | -| [f82b77e47](https://github.com/angular/angular-cli/commit/f82b77e475e270dd0d23ce5d6b445c0a8aa8599a) | fix | do not remove `@angular/localize` when having external packages ([#31721](https://github.com/angular/angular-cli/pull/31721)) | -| [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 | -| [41b12509a](https://github.com/angular/angular-cli/commit/41b12509a9db8bca637e0c67d21301a75774129c) | fix | ensure TestBed setup is robust in non-isolated Vitest | -| [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)) | +| Commit | Type | Description | +| --------------------------------------------------------------------------------------------------- | ---- | ----------------------------------------------------------------------------------------------------------- | +| [30b5d81b4](https://github.com/angular/angular-cli/commit/30b5d81b4adafca32c94672a39574daa2e3fc8b7) | fix | Add custom middleware for to present an Angular-tailored message | +| [2e7227b8d](https://github.com/angular/angular-cli/commit/2e7227b8dc04d4b2ca20e18baaeebaa65d3c2aac) | fix | Ensure disposal of close-javascript-transformer | +| [38b16ea01](https://github.com/angular/angular-cli/commit/38b16ea0108c48835dc0d81863eca84f7b8cea6e) | fix | ensure locale base href retains leading slash ([#32040](https://github.com/angular/angular-cli/pull/32040)) | +| [385165cbc](https://github.com/angular/angular-cli/commit/385165cbc6ff087e6bc1fb6f686d4929e83a075a) | fix | inject testing polyfills in Karma unit-test executor | +| [6d212206f](https://github.com/angular/angular-cli/commit/6d212206fdfc94e661a25bed1287c0bc15219b63) | fix | support NODE_EXTRA_CA_CERTS in SSR SSL plugin | - + -# 20.3.10 (2025-11-12) +# 21.0.3 (2025-12-10) -### @schematics/angular +### @angular-devkit/build-angular -| Commit | Type | Description | -| --------------------------------------------------------------------------------------------------- | ---- | --------------------------------------------------- | -| [c854a719b](https://github.com/angular/angular-cli/commit/c854a719bb3a8d92fe42c8fba4b0b1789081b21c) | fix | correct `tsconfig.spec.json` include for spec files | +| Commit | Type | Description | +| --------------------------------------------------------------------------------------------------- | ---- | ----------------------------------------------------------------------------- | +| [5d85f416f](https://github.com/angular/angular-cli/commit/5d85f416f43b6bcd07b28ab920cb40c61a83ebdd) | fix | conditionally provide Zone.js change detection in the built-in test main file | ### @angular/build -| Commit | Type | Description | -| --------------------------------------------------------------------------------------------------- | ---- | ----------------------------------------------------------------------------------------------------------------------------- | -| [b3908f68e](https://github.com/angular/angular-cli/commit/b3908f68ed2f05cee56cbf0d9895dcfc3dc0ac25) | fix | do not remove `@angular/localize` when having external packages ([#31721](https://github.com/angular/angular-cli/pull/31721)) | +| Commit | Type | Description | +| --------------------------------------------------------------------------------------------------- | ---- | ----------------------------------------------------------------------------------------------------------- | +| [778b4cffc](https://github.com/angular/angular-cli/commit/778b4cffc03e7c137940e3b8c89f290fd226cf17) | fix | Add custom middleware for to present an Angular-tailored message | +| [9b02ab2ee](https://github.com/angular/angular-cli/commit/9b02ab2ee0a36aa6aafd94ea8059b48679845860) | fix | Ensure disposal of close-javascript-transformer | +| [0fc7d576e](https://github.com/angular/angular-cli/commit/0fc7d576e53f45601fdbeb95f4a853ebceae4fad) | fix | ensure locale base href retains leading slash ([#32040](https://github.com/angular/angular-cli/pull/32040)) | +| [b141670a2](https://github.com/angular/angular-cli/commit/b141670a2453dd0ea5fe6aa22ddae7175893d813) | fix | inject testing polyfills in Karma unit-test executor | +| [88c18ce68](https://github.com/angular/angular-cli/commit/88c18ce68585726652b88b10ce090039fbe1829f) | fix | support NODE_EXTRA_CA_CERTS in SSR SSL plugin | - + -# 21.0.0-rc.1 (2025-11-05) +# 21.1.0-next.1 (2025-12-03) ### @angular/cli -| Commit | Type | Description | -| --------------------------------------------------------------------------------------------------- | ---- | ------------------------------------ | -| [dfb4242b3](https://github.com/angular/angular-cli/commit/dfb4242b347365f3a2c6d006f07a16c982ff4dbe) | fix | add vitest to version command output | +| Commit | Type | Description | +| --------------------------------------------------------------------------------------------------- | ---- | ------------------------ | +| [d635a6c63](https://github.com/angular/angular-cli/commit/d635a6c6335d0838fc0977f6742f6aa9f769c527) | feat | add signal forms lessons | ### @schematics/angular -| Commit | Type | Description | -| --------------------------------------------------------------------------------------------------- | ---- | ------------------------------------------------------------- | -| [f89750b27](https://github.com/angular/angular-cli/commit/f89750b27866c307da546fe4f33da980693ca5c1) | fix | add `addImports` option to jasmine-vitest schematic | -| [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 | +| Commit | Type | Description | +| --------------------------------------------------------------------------------------------------- | ---- | ---------------------------------------------------------------- | +| [e71a72ffd](https://github.com/angular/angular-cli/commit/e71a72ffdc426e26bfb4f0bb92e8f5795a621c18) | feat | generate detailed migration report for `refactor-jasmine-vitest` | -### @angular/build +### @angular-devkit/schematics -| Commit | Type | Description | -| --------------------------------------------------------------------------------------------------- | ---- | ----------------------------------------------------- | -| [62938e799](https://github.com/angular/angular-cli/commit/62938e79977d14045b7883d459d786dbb8d4d7ee) | fix | update vitest to 4.0.6 and remove coverage workaround | +| Commit | Type | Description | +| --------------------------------------------------------------------------------------------------- | ---- | --------------------------------- | +| [98e10fa0f](https://github.com/angular/angular-cli/commit/98e10fa0f29cc8f6cf6a25c45c6888a79465eaf7) | fix | remove lazy imports in node tasks | -### @angular/ssr +### @angular/build -| Commit | Type | Description | -| --------------------------------------------------------------------------------------------------- | ---- | ------------------------------------------ | -| [5d76d84e6](https://github.com/angular/angular-cli/commit/5d76d84e64db717e177b77f7a07ee8819b258877) | fix | improve locale handling in app-engine | -| [4a3cfdfce](https://github.com/angular/angular-cli/commit/4a3cfdfce75b7cedf64e26d85b0a92ec92ddd12d) | fix | improve route matching for wildcard routes | +| Commit | Type | Description | +| --------------------------------------------------------------------------------------------------- | ---- | ---------------------------------------------------------- | +| [63c3e3f64](https://github.com/angular/angular-cli/commit/63c3e3f6406d345e777ca18bfad7d6d701e5c4ea) | fix | add filename truncation to test discovery | +| [8d8ba4f61](https://github.com/angular/angular-cli/commit/8d8ba4f61fc07dd670b705c48e82cf63424b3cce) | fix | allow overriding Vitest coverage `reportsDirectory` option | - - -# 20.3.9 (2025-11-05) - -### @angular/ssr - -| Commit | Type | Description | -| --------------------------------------------------------------------------------------------------- | ---- | ------------------------------------------ | -| [08e07e338](https://github.com/angular/angular-cli/commit/08e07e338edd799400e18cd62cd131b6d408f4cf) | fix | improve locale handling in app-engine | -| [683697ebc](https://github.com/angular/angular-cli/commit/683697ebc5e129d2b17bded9e4ff318d598e0bd3) | fix | improve route matching for wildcard routes | + - +# 21.0.2 (2025-12-03) - +### @angular/cli -# 21.0.0-rc.0 (2025-10-30) +| Commit | Type | Description | +| --------------------------------------------------------------------------------------------------- | ---- | --------------------------------------------- | +| [f1a7116cd](https://github.com/angular/angular-cli/commit/f1a7116cdff1bd83b26b0d64cea14ec4e8084583) | fix | update `@modelcontextprotocol/sdk` to v1.24.0 | - +### @angular-devkit/schematics - +| Commit | Type | Description | +| --------------------------------------------------------------------------------------------------- | ---- | --------------------------------- | +| [dc6d9469e](https://github.com/angular/angular-cli/commit/dc6d9469ea494bbfee7da191774e9fa3c0baf30a) | fix | remove lazy imports in node tasks | -# 21.0.0-next.10 (2025-10-29) +### @angular/build -### @schematics/angular +| Commit | Type | Description | +| --------------------------------------------------------------------------------------------------- | ---- | ---------------------------------------------------------- | +| [f8a1939fd](https://github.com/angular/angular-cli/commit/f8a1939fdf5abbc1de439d288d9357d4f92a72a9) | fix | add filename truncation to test discovery | +| [86dd3297f](https://github.com/angular/angular-cli/commit/86dd3297f7ad81788713cfd8dae48c0fad4b89ab) | fix | allow overriding Vitest coverage `reportsDirectory` option | -| Commit | Type | Description | -| --------------------------------------------------------------------------------------------------- | ---- | --------------------------------------------------- | -| [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 | + -### @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 | +# 20.3.13 (2025-12-03) -### @angular/build +### @angular/cli -| Commit | Type | Description | -| --------------------------------------------------------------------------------------------------- | ---- | -------------------------------------------------------- | -| [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 | -| [091d1c03e](https://github.com/angular/angular-cli/commit/091d1c03e9665371f52c4cb8ec6cd9f1f862a407) | fix | add adapters to new reporter | -| [542d52868](https://github.com/angular/angular-cli/commit/542d528683cc0e51fd5a55ed6dbf43168f7a51a5) | fix | allow custom runner configuration file for unit-test | -| [9975c7249](https://github.com/angular/angular-cli/commit/9975c72494bc2a71359dc4c5a31e43eed040716e) | fix | ensure locale data plugin runs before other plugins | -| [7c529c1bc](https://github.com/angular/angular-cli/commit/7c529c1bc606101ab8c506e0b7845d2e9f509db4) | fix | externalize Angular dependencies in Vitest runner | -| [a41f4e860](https://github.com/angular/angular-cli/commit/a41f4e860dd28bee28af47c1513f55450ca74b5f) | fix | handle redirects from guards during prerendering | -| [bab5806c2](https://github.com/angular/angular-cli/commit/bab5806c281fd4cdd63b7969e691d703ed1e7680) | fix | introduce vitest-base.config for test configuration | -| [9132e6af9](https://github.com/angular/angular-cli/commit/9132e6af9fd573d8b39c69a50b4b93e256145fd4) | fix | resolve browser provider packages using project resolver | +| Commit | Type | Description | +| --------------------------------------------------------------------------------------------------- | ---- | --------------------------------------------- | +| [cfbb61602](https://github.com/angular/angular-cli/commit/cfbb61602daf32c5b942ea84702fc3638aa111e7) | fix | update `@modelcontextprotocol/sdk` to v1.24.0 | - - -# 20.3.8 (2025-10-29) - -### @angular-devkit/build-angular - -| Commit | Type | Description | -| --------------------------------------------------------------------------------------------------- | ---- | ---------------------------------------------------------- | -| [813cba9b9](https://github.com/angular/angular-cli/commit/813cba9b9bfe60e874595ce25608ca85a890f6bf) | fix | expand jest and jest-environment-jsdom to allow version 30 | + -### @angular/build +# 21.1.0-next.0 (2025-11-26) -| Commit | Type | Description | -| --------------------------------------------------------------------------------------------------- | ---- | --------------------------------------------------- | -| [542973ab0](https://github.com/angular/angular-cli/commit/542973ab074ccd9a5f09f73ee7f2706a21db45fc) | fix | add adapters to new reporter | -| [f0885691d](https://github.com/angular/angular-cli/commit/f0885691d18b6575351774fcc50d746d981285f6) | fix | ensure locale data plugin runs before other plugins | -| [45e498f95](https://github.com/angular/angular-cli/commit/45e498f9576ff83eebe02deb235d36498ce06bde) | fix | handle redirects from guards during prerendering | +### @angular/cli - +| Commit | Type | Description | +| --------------------------------------------------------------------------------------------------- | ---- | ------------------------------------------------- | +| [c3c9ac506](https://github.com/angular/angular-cli/commit/c3c9ac5067275461e2d8caefba81ac9701949776) | feat | Add MCP tools for building and running devservers | - +### @schematics/angular -# 19.2.19 (2025-10-29) +| Commit | Type | Description | +| --------------------------------------------------------------------------------------------------- | ---- | -------------------------------------------------- | +| [36cf3afb4](https://github.com/angular/angular-cli/commit/36cf3afb485a01f86c4c90f136b38a3cf338e313) | feat | add browserMode option to jasmine-vitest schematic | +| [18cf6c51b](https://github.com/angular/angular-cli/commit/18cf6c51b72ce5c7f23012585ed992cf91cef5ed) | fix | add MCP configuration file to new workspaces | ### @angular/build -| Commit | Type | Description | -| --------------------------------------------------------------------------------------------------- | ---- | --------------------- | -| [4d8ea27a1](https://github.com/angular/angular-cli/commit/4d8ea27a1726709b8398a26915395e7611571dae) | fix | update vite to v6.4.1 | +| Commit | Type | Description | +| --------------------------------------------------------------------------------------------------- | ---- | ----------------------------------------------------------------------------------------------------------- | +| [ad99e00ad](https://github.com/angular/angular-cli/commit/ad99e00ad7edd17e369777c8d38b4137ea736121) | fix | simplify SSL handling for `ng serve` with SSR ([#31722](https://github.com/angular/angular-cli/pull/31722)) | - + -# 21.0.0-next.9 (2025-10-23) +# 21.0.1 (2025-11-26) ### @angular/cli -| Commit | Type | Description | -| --------------------------------------------------------------------------------------------------- | ---- | ------------------------------------------------------- | -| [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 | -| [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 | -| [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 | +| 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 | -| --------------------------------------------------------------------------------------------------- | ---- | ----------------------------------------------------------------- | -| [2ffc527b1](https://github.com/angular/angular-cli/commit/2ffc527b1bbe237e9f732d3506ce3a75ca1ca9d0) | feat | configure Vitest for new projects and allow runner choice | -| [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 | -| [54c4eae2a](https://github.com/angular/angular-cli/commit/54c4eae2aa49c6b45c41f0718a5915a10d426cb4) | fix | transform Jasmine type annotations in jasmine-to-vitest schematic | +| 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-devkit/schematics +### @angular/build | Commit | Type | Description | | --------------------------------------------------------------------------------------------------- | ---- | -------------------------------------------------------------- | -| [18bf8e7b3](https://github.com/angular/angular-cli/commit/18bf8e7b3b36b968d064299e5c557942c0ed7ec0) | fix | respect `--force` option when schematic contains `host.create` | - -### @angular/build - -| Commit | Type | Description | -| --------------------------------------------------------------------------------------------------- | ---- | ------------------------------------------------------- | -| [bf468e1eb](https://github.com/angular/angular-cli/commit/bf468e1eb1050c60359b6f52692ce0db286982c2) | fix | direct check include file exists in unit-test discovery | -| [b1d6d2f17](https://github.com/angular/angular-cli/commit/b1d6d2f174027a1f7800bd70ebd35143f92dc38c) | fix | resolve Angular locale data namespace in esbuild | +| [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 | -| --------------------------------------------------------------------------------------------------- | ---- | ------------------------------------------------- | -| [85c18b4ea](https://github.com/angular/angular-cli/commit/85c18b4ead77c6c090b9f5c63e9e034e58369152) | fix | correctly handle routes with matrix parameters | -| [58dcfd109](https://github.com/angular/angular-cli/commit/58dcfd1094fec52102f339e8fd3b5dd2925229b9) | 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 | - + -# 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` | +# 20.3.12 (2025-11-25) ### @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 | +| --------------------------------------------------------------------------------------------------- | ---- | ----------------------------------------------- | +| [25bb7e65c](https://github.com/angular/angular-cli/commit/25bb7e65c4fc7e401c658126c53b0b7a13d62965) | fix | ensure correct URL joining for prerender routes | ### @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 | +| --------------------------------------------------------------------------------------------------- | ---- | ------------------------------------------------------------ | +| [cceb86296](https://github.com/angular/angular-cli/commit/cceb862969e541a5f54b689a6439e32773eafe65) | fix | handle `X-Forwarded-Prefix` and `APP_BASE_HREF` in redirects | +| [1abe68ad8](https://github.com/angular/angular-cli/commit/1abe68ad87f9b892734117a087b5775068bd232b) | fix | prevent redirect loop with encoded query parameters | - + + +# 21.0.0 (2025-11-19) -# 21.0.0-next.8 (2025-10-15) +## 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 + +- - 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.11 (2025-11-19) -### @angular/ssr +### @angular/build -| Commit | Type | Description | -| --------------------------------------------------------------------------------------------------- | ---- | ------------------------------------------ | -| [7be6c8f0e](https://github.com/angular/angular-cli/commit/7be6c8f0e2883c85546eb1691c91fa7d4aefc0d3) | fix | prevent malicious URL from overriding host | +| Commit | Type | Description | +| --------------------------------------------------------------------------------------------------- | ---- | -------------------------------------------------------------------- | +| [8053f2d92](https://github.com/angular/angular-cli/commit/8053f2d92a68a8bd01854eb81aa472249f5a83b2) | fix | ensure `ɵgetOrCreateAngularServerApp` is always defined after errors | - + -# 20.3.6 (2025-10-15) +# 20.3.10 (2025-11-12) -### @angular/ssr +### @schematics/angular -| Commit | Type | Description | -| --------------------------------------------------------------------------------------------------- | ---- | ------------------------------------------ | -| [5271547c8](https://github.com/angular/angular-cli/commit/5271547c80662de10cb3bcb648779a83f6efedfb) | fix | prevent malicious URL from overriding host | +| Commit | Type | Description | +| --------------------------------------------------------------------------------------------------- | ---- | --------------------------------------------------- | +| [c854a719b](https://github.com/angular/angular-cli/commit/c854a719bb3a8d92fe42c8fba4b0b1789081b21c) | fix | correct `tsconfig.spec.json` include for spec files | + +### @angular/build + +| Commit | Type | Description | +| --------------------------------------------------------------------------------------------------- | ---- | ----------------------------------------------------------------------------------------------------------------------------- | +| [b3908f68e](https://github.com/angular/angular-cli/commit/b3908f68ed2f05cee56cbf0d9895dcfc3dc0ac25) | fix | do not remove `@angular/localize` when having external packages ([#31721](https://github.com/angular/angular-cli/pull/31721)) | - + -# 19.2.18 (2025-10-15) +# 20.3.9 (2025-11-05) ### @angular/ssr | Commit | Type | Description | | --------------------------------------------------------------------------------------------------- | ---- | ------------------------------------------ | -| [9136a5d13](https://github.com/angular/angular-cli/commit/9136a5d1302bb224ea245460ae29474bd2a3a10b) | fix | prevent malicious URL from overriding host | +| [08e07e338](https://github.com/angular/angular-cli/commit/08e07e338edd799400e18cd62cd131b6d408f4cf) | fix | improve locale handling in app-engine | +| [683697ebc](https://github.com/angular/angular-cli/commit/683697ebc5e129d2b17bded9e4ff318d598e0bd3) | fix | improve route matching for wildcard routes | - + -# 21.0.0-next.7 (2025-10-08) +# 20.3.8 (2025-10-29) -### @angular/cli +### @angular-devkit/build-angular -| 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 | +| Commit | Type | Description | +| --------------------------------------------------------------------------------------------------- | ---- | ---------------------------------------------------------- | +| [813cba9b9](https://github.com/angular/angular-cli/commit/813cba9b9bfe60e874595ce25608ca85a890f6bf) | fix | expand jest and jest-environment-jsdom to allow version 30 | -### @schematics/angular +### @angular/build -| Commit | Type | Description | -| --------------------------------------------------------------------------------------------------- | ---- | ------------------------------------------------- | -| [afb4d3e37](https://github.com/angular/angular-cli/commit/afb4d3e377b11315a03563cb8c143c35d37f113a) | fix | remove extra space before async in spec templates | +| Commit | Type | Description | +| --------------------------------------------------------------------------------------------------- | ---- | --------------------------------------------------- | +| [542973ab0](https://github.com/angular/angular-cli/commit/542973ab074ccd9a5f09f73ee7f2706a21db45fc) | fix | add adapters to new reporter | +| [f0885691d](https://github.com/angular/angular-cli/commit/f0885691d18b6575351774fcc50d746d981285f6) | fix | ensure locale data plugin runs before other plugins | +| [45e498f95](https://github.com/angular/angular-cli/commit/45e498f9576ff83eebe02deb235d36498ce06bde) | fix | handle redirects from guards during prerendering | + + + + + +# 19.2.19 (2025-10-29) ### @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 | +| Commit | Type | Description | +| --------------------------------------------------------------------------------------------------- | ---- | --------------------- | +| [4d8ea27a1](https://github.com/angular/angular-cli/commit/4d8ea27a1726709b8398a26915395e7611571dae) | fix | update vite to v6.4.1 | - + -# 20.3.5 (2025-10-08) +# 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 | -| --------------------------------------------------------------------------------------------------- | ---- | ---------------------------------------------------- | -| [7f7140680](https://github.com/angular/angular-cli/commit/7f7140680b75ff6b41f7f04349fe10cd928f1a23) | fix | cleanup karma temporary directory after process exit | +| 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 | +| --------------------------------------------------------------------------------------------------- | ---- | ------------------------------------------------- | +| [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 | - + -# 21.0.0-next.6 (2025-10-03) +# 20.3.6 (2025-10-15) -### @angular/cli +### @angular/ssr -| 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 | +| Commit | Type | Description | +| --------------------------------------------------------------------------------------------------- | ---- | ------------------------------------------ | +| [5271547c8](https://github.com/angular/angular-cli/commit/5271547c80662de10cb3bcb648779a83f6efedfb) | fix | prevent malicious URL from overriding host | -### @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 +# 19.2.18 (2025-10-15) -| Commit | Type | Description | -| --------------------------------------------------------------------------------------------------- | ---- | ------------------------------------------------------- | -| [acd785afc](https://github.com/angular/angular-cli/commit/acd785afc956efad56b03402085ff94855b9fcc6) | fix | mark `InjectionToken` as pure for improved tree-shaking | +### @angular/ssr + +| Commit | Type | Description | +| --------------------------------------------------------------------------------------------------- | ---- | ------------------------------------------ | +| [9136a5d13](https://github.com/angular/angular-cli/commit/9136a5d1302bb224ea245460ae29474bd2a3a10b) | fix | prevent malicious URL from overriding host | + + + + + +# 20.3.5 (2025-10-08) ### @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 | +| Commit | Type | Description | +| --------------------------------------------------------------------------------------------------- | ---- | ---------------------------------------------------- | +| [7f7140680](https://github.com/angular/angular-cli/commit/7f7140680b75ff6b41f7f04349fe10cd928f1a23) | fix | cleanup karma temporary directory after process exit | @@ -461,55 +592,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) @@ -528,24 +610,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) @@ -663,75 +727,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) @@ -787,36 +782,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) @@ -837,52 +802,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) @@ -915,18 +834,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 a82a4e90c6f0..b4bf8a27a1aa 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -4,36 +4,28 @@ module( name = "angular_cli", ) -bazel_dep(name = "yq.bzl", version = "0.3.1") -bazel_dep(name = "rules_nodejs", version = "6.6.1") -bazel_dep(name = "aspect_rules_js", version = "2.8.1") -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. -multiple_version_override( - module_name = "rules_pkg", - versions = [ - "0.8.1", - "1.1.0", - ], -) - -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.24.0") -bazel_dep(name = "aspect_rules_jasmine", version = "2.0.0") +bazel_dep(name = "platforms", version = "1.0.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.3") +bazel_dep(name = "aspect_rules_ts", version = "3.8.1") +bazel_dep(name = "rules_pkg", version = "1.1.0") +bazel_dep(name = "rules_cc", version = "0.2.15") +bazel_dep(name = "aspect_bazel_lib", version = "2.22.0") +bazel_dep(name = "bazel_skylib", version = "1.9.0") +bazel_dep(name = "aspect_rules_esbuild", version = "0.25.0") +bazel_dep(name = "aspect_rules_jasmine", version = "2.0.2") bazel_dep(name = "rules_angular") git_override( module_name = "rules_angular", - commit = "74d8baec22a5ffd3a1a36bac252399fa125a6c4b", + commit = "7133b97252508f8528e5c5818a9a73cacc2e2a0e", remote = "https://github.com/devversion/rules_angular.git", ) bazel_dep(name = "devinfra") git_override( module_name = "devinfra", - commit = "c855fffb4b01bc06e743eb3bdfd54c866af09ad8", + commit = "942d738d8f4d65b161d06e6c399fefec318cdbfe", remote = "https://github.com/angular/dev-infra.git", ) @@ -47,12 +39,12 @@ git_override( bazel_dep(name = "rules_browsers") git_override( module_name = "rules_browsers", - commit = "6a699bf3e896690e2923cf3ade29fbd4e492e366", + commit = "8ef3e996d5fc040a35770f860987d8b9cad8ef3d", remote = "https://github.com/devversion/rules_browsers.git", ) node = use_extension("@rules_nodejs//nodejs:extensions.bzl", "node") -node.toolchain(node_version = "24.0.0") +node.toolchain(node_version_from_nvmrc = "//:.nvmrc") use_repo( node, "nodejs_darwin_amd64", @@ -104,8 +96,13 @@ use_repo( "node24_windows_amd64", ) -# This is needed following https://github.com/bazel-contrib/rules_nodejs/pull/3859 -register_toolchains("//:node22_windows_no_exec_config_toolchain") +pnpm = use_extension("@aspect_rules_js//npm:extensions.bzl", "pnpm") +pnpm.pnpm( + name = "pnpm", + pnpm_version = "10.26.0", + pnpm_version_integrity = "sha512-Oz9scl6+cSUGwKsa1BM8+GsfS2h+/85iqbOLTXLjlUJC5kMZD8UfoWQpScc19APevUT1yw7dZXq+Y6i2p+HkAg==", +) +use_repo(pnpm, "pnpm") npm = use_extension("@aspect_rules_js//npm:extensions.bzl", "npm") npm.npm_translate_lock( @@ -146,7 +143,6 @@ npm.npm_translate_lock( }, npmrc = "//:.npmrc", pnpm_lock = "//:pnpm-lock.yaml", - verify_node_modules_ignored = "//:.bazelignore", ) use_repo(npm, "npm") @@ -172,4 +168,6 @@ register_toolchains( "@devinfra//bazel/git-toolchain:git_macos_x86_toolchain", "@devinfra//bazel/git-toolchain:git_macos_arm64_toolchain", "@devinfra//bazel/git-toolchain:git_windows_toolchain", + "//tools/toolchains:dummy_cc_windows_no_exec_toolchain", + "//tools/toolchains:node22_windows_no_exec_toolchain", ) diff --git a/MODULE.bazel.lock b/MODULE.bazel.lock index 4dc7a24f2c2f..1f9bb1615d4e 100644 --- a/MODULE.bazel.lock +++ b/MODULE.bazel.lock @@ -1,5 +1,5 @@ { - "lockFileVersion": 13, + "lockFileVersion": 24, "registryFileHashes": { "https://bcr.bazel.build/bazel_registry.json": "8a28e4aff06ee60aed2a8c281907fb8bcbf3b753c91fb5a5c57da3215d5b3497", "https://bcr.bazel.build/modules/abseil-cpp/20210324.2/MODULE.bazel": "7cd0312e064fde87c8d1cd79ba06c876bd23630c83466e9500321be55c96ace2", @@ -10,34 +10,33 @@ "https://bcr.bazel.build/modules/abseil-cpp/20230802.1/MODULE.bazel": "fa92e2eb41a04df73cdabeec37107316f7e5272650f81d6cc096418fe647b915", "https://bcr.bazel.build/modules/abseil-cpp/20240116.1/MODULE.bazel": "37bcdb4440fbb61df6a1c296ae01b327f19e9bb521f9b8e26ec854b6f97309ed", "https://bcr.bazel.build/modules/abseil-cpp/20240116.1/source.json": "9be551b8d4e3ef76875c0d744b5d6a504a27e3ae67bc6b28f46415fd2d2957da", - "https://bcr.bazel.build/modules/apple_support/1.23.1/MODULE.bazel": "53763fed456a968cf919b3240427cf3a9d5481ec5466abc9d5dc51bc70087442", - "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.22.0/MODULE.bazel": "7fe0191f047d4fe4a4a46c1107e2350cbb58a8fc2e10913aa4322d3190dec0bf", + "https://bcr.bazel.build/modules/aspect_bazel_lib/2.22.0/source.json": "369df5b7f2eae82f200fff95cf1425f90dee90a0d0948122060b48150ff0e224", "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.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_esbuild/0.25.0/MODULE.bazel": "5fef5ec709c837312823f9bcf0f276661e2cb48ad52f17c4e01176bbf1e9bf58", + "https://bcr.bazel.build/modules/aspect_rules_esbuild/0.25.0/source.json": "5e42968c6d23ab8bd95c02634b16864d866334347827cb6a8425b86c11cc4363", + "https://bcr.bazel.build/modules/aspect_rules_jasmine/2.0.2/MODULE.bazel": "45f054400ff242c4433f6d7f20f6123a9a72739cb7a1f44247d738db1644f46c", + "https://bcr.bazel.build/modules/aspect_rules_jasmine/2.0.2/source.json": "3ed399a5654259a822448f9cdbf21f6c738f16ccd7f89249c7507e374cbdd1e3", "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.8.1/MODULE.bazel": "edcde75a1357952d3acedb6f3622614c87f730927d753af77b36c604ff407f0d", - "https://bcr.bazel.build/modules/aspect_rules_js/2.8.1/source.json": "6da210e9e76eda699f9ca998a6f6c48980f78589f0f6d240842de6cef96543ce", + "https://bcr.bazel.build/modules/aspect_rules_js/2.8.3/MODULE.bazel": "807ce5f624124a8bc586c743394d174e85f0f9c6c4e0e2410b4088aebe790ac8", + "https://bcr.bazel.build/modules/aspect_rules_js/2.8.3/source.json": "c35cb4e04f61a09c17f8c569894b80de884b1e3dee2d33829704786e3f778037", "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.1/MODULE.bazel": "cbed416847e2c46c4c0fe275e3a3c8e302d236d0fb04a094e9af82d14e7c5040", - "https://bcr.bazel.build/modules/aspect_rules_ts/3.7.1/source.json": "7914a860fdf6ac399a3142fee2579184f0810fe0b7dee2eb9216ab72e6d8864e", + "https://bcr.bazel.build/modules/aspect_rules_ts/3.8.1/MODULE.bazel": "796622c65ae3008374fc2d77c32ddb4ef6da9fe891826ce648f70033a48b3667", + "https://bcr.bazel.build/modules/aspect_rules_ts/3.8.1/source.json": "a7c4f332f5c21f4e63d073f8dda40bf278d5307499fb307b35058dba558f417a", "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", - "https://bcr.bazel.build/modules/aspect_tools_telemetry/0.2.8/source.json": "786cbc49377fb6bf4859aec5b1c61f8fc26b08e9fdb929e2dde2e1e2a406bd24", + "https://bcr.bazel.build/modules/aspect_tools_telemetry/0.3.3/MODULE.bazel": "37c764292861c2f70314efa9846bb6dbb44fc0308903b3285da6528305450183", + "https://bcr.bazel.build/modules/aspect_tools_telemetry/0.3.3/source.json": "605086bbc197743a0d360f7ddc550a1d4dfa0441bc807236e17170f636153348", "https://bcr.bazel.build/modules/bazel_features/1.1.1/MODULE.bazel": "27b8c79ef57efe08efccbd9dd6ef70d61b4798320b8d3c134fd571f78963dbcd", "https://bcr.bazel.build/modules/bazel_features/1.11.0/MODULE.bazel": "f9382337dd5a474c3b7d334c2f83e50b6eaedc284253334cf823044a26de03e8", "https://bcr.bazel.build/modules/bazel_features/1.15.0/MODULE.bazel": "d38ff6e517149dc509406aca0db3ad1efdd890a85e049585b7234d04238e2a4d", @@ -45,11 +44,13 @@ "https://bcr.bazel.build/modules/bazel_features/1.18.0/MODULE.bazel": "1be0ae2557ab3a72a57aeb31b29be347bcdc5d2b1eb1e70f39e3851a7e97041a", "https://bcr.bazel.build/modules/bazel_features/1.19.0/MODULE.bazel": "59adcdf28230d220f0067b1f435b8537dd033bfff8db21335ef9217919c7fb58", "https://bcr.bazel.build/modules/bazel_features/1.21.0/MODULE.bazel": "675642261665d8eea09989aa3b8afb5c37627f1be178382c320d1b46afba5e3b", - "https://bcr.bazel.build/modules/bazel_features/1.27.0/MODULE.bazel": "621eeee06c4458a9121d1f104efb80f39d34deff4984e778359c60eaf1a8cb65", + "https://bcr.bazel.build/modules/bazel_features/1.28.0/MODULE.bazel": "4b4200e6cbf8fa335b2c3f43e1d6ef3e240319c33d43d60cc0fbd4b87ece299d", + "https://bcr.bazel.build/modules/bazel_features/1.30.0/MODULE.bazel": "a14b62d05969a293b80257e72e597c2da7f717e1e69fa8b339703ed6731bec87", "https://bcr.bazel.build/modules/bazel_features/1.34.0/MODULE.bazel": "e8475ad7c8965542e0c7aac8af68eb48c4af904be3d614b6aa6274c092c2ea1e", "https://bcr.bazel.build/modules/bazel_features/1.34.0/source.json": "dfa5c4b01110313153b484a735764d247fee5624bbab63d25289e43b151a657a", "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_features/1.9.1/MODULE.bazel": "8f679097876a9b609ad1f60249c49d68bfab783dd9be012faf9d82547b14815a", "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/MODULE.bazel": "22b70b80ac89ad3f3772526cd9feee2fa412c2b01933fea7ed13238a448d370d", "https://bcr.bazel.build/modules/bazel_lib/3.0.0/source.json": "895f21909c6fba01d7c17914bb6c8e135982275a1b18cdaa4e62272217ef1751", @@ -67,7 +68,8 @@ "https://bcr.bazel.build/modules/bazel_skylib/1.7.1/MODULE.bazel": "3120d80c5861aa616222ec015332e5f8d3171e062e3e804a2a0253e1be26e59b", "https://bcr.bazel.build/modules/bazel_skylib/1.8.1/MODULE.bazel": "88ade7293becda963e0e3ea33e7d54d3425127e0a326e0d17da085a5f1f03ff6", "https://bcr.bazel.build/modules/bazel_skylib/1.8.2/MODULE.bazel": "69ad6927098316848b34a9142bcc975e018ba27f08c4ff403f50c1b6e646ca67", - "https://bcr.bazel.build/modules/bazel_skylib/1.8.2/source.json": "34a3c8bcf233b835eb74be9d628899bb32999d3e0eadef1947a0a562a2b16ffb", + "https://bcr.bazel.build/modules/bazel_skylib/1.9.0/MODULE.bazel": "72997b29dfd95c3fa0d0c48322d05590418edef451f8db8db5509c57875fb4b7", + "https://bcr.bazel.build/modules/bazel_skylib/1.9.0/source.json": "7ad77c1e8c1b84222d9b3f3cae016a76639435744c19330b0b37c0a3c9da7dc0", "https://bcr.bazel.build/modules/buildozer/7.1.2/MODULE.bazel": "2e8dd40ede9c454042645fd8d8d0cd1527966aa5c919de86661e62953cd73d84", "https://bcr.bazel.build/modules/buildozer/7.1.2/source.json": "c9028a501d2db85793a6996205c8de120944f50a0d570438fcae0457a5f9d1f8", "https://bcr.bazel.build/modules/gawk/5.3.2.bcr.1/MODULE.bazel": "cdf8cbe5ee750db04b78878c9633cc76e80dcf4416cbe982ac3a9222f80713c8", @@ -91,7 +93,6 @@ "https://bcr.bazel.build/modules/platforms/0.0.6/MODULE.bazel": "ad6eeef431dc52aefd2d77ed20a4b353f8ebf0f4ecdd26a807d2da5aa8cd0615", "https://bcr.bazel.build/modules/platforms/0.0.7/MODULE.bazel": "72fd4a0ede9ee5c021f6a8dd92b503e089f46c227ba2813ff183b71616034814", "https://bcr.bazel.build/modules/platforms/0.0.8/MODULE.bazel": "9f142c03e348f6d263719f5074b21ef3adf0b139ee4c5133e2aa35664da9eb2d", - "https://bcr.bazel.build/modules/platforms/0.0.9/MODULE.bazel": "4a87a60c927b56ddd67db50c89acaa62f4ce2a1d2149ccb63ffd871d5ce29ebc", "https://bcr.bazel.build/modules/platforms/1.0.0/MODULE.bazel": "f05feb42b48f1b3c225e4ccf351f367be0371411a803198ec34a389fb22aa580", "https://bcr.bazel.build/modules/platforms/1.0.0/source.json": "f4ff1fd412e0246fd38c82328eb209130ead81d62dcd5a9e40910f867f733d96", "https://bcr.bazel.build/modules/protobuf/21.7/MODULE.bazel": "a5a29bb89544f9b97edce05642fac225a808b5b7be74038ea3640fae2f8e66a7", @@ -99,9 +100,9 @@ "https://bcr.bazel.build/modules/protobuf/27.1/MODULE.bazel": "703a7b614728bb06647f965264967a8ef1c39e09e8f167b3ca0bb1fd80449c0d", "https://bcr.bazel.build/modules/protobuf/29.0-rc2/MODULE.bazel": "6241d35983510143049943fc0d57937937122baf1b287862f9dc8590fc4c37df", "https://bcr.bazel.build/modules/protobuf/29.0-rc3/MODULE.bazel": "33c2dfa286578573afc55a7acaea3cada4122b9631007c594bf0729f41c8de92", - "https://bcr.bazel.build/modules/protobuf/29.0-rc3/source.json": "c16a6488fb279ef578da7098e605082d72ed85fc8d843eaae81e7d27d0f4625d", + "https://bcr.bazel.build/modules/protobuf/29.0/MODULE.bazel": "319dc8bf4c679ff87e71b1ccfb5a6e90a6dbc4693501d471f48662ac46d04e4e", + "https://bcr.bazel.build/modules/protobuf/29.0/source.json": "b857f93c796750eef95f0d61ee378f3420d00ee1dd38627b27193aa482f4f981", "https://bcr.bazel.build/modules/protobuf/3.19.0/MODULE.bazel": "6b5fbb433f760a99a22b18b6850ed5784ef0e9928a72668b66e4d7ccd47db9b0", - "https://bcr.bazel.build/modules/protobuf/3.19.6/MODULE.bazel": "9233edc5e1f2ee276a60de3eaa47ac4132302ef9643238f23128fea53ea12858", "https://bcr.bazel.build/modules/pybind11_bazel/2.11.1/MODULE.bazel": "88af1c246226d87e65be78ed49ecd1e6f5e98648558c14ce99176da041dc378e", "https://bcr.bazel.build/modules/pybind11_bazel/2.11.1/source.json": "be4789e951dd5301282729fe3d4938995dc4c1a81c2ff150afc9f1b0504c6022", "https://bcr.bazel.build/modules/re2/2023-09-01/MODULE.bazel": "cb3d511531b16cfc78a225a9e2136007a48cf8a677e4264baeab57fe78a80206", @@ -110,8 +111,8 @@ "https://bcr.bazel.build/modules/rules_android/0.1.1/source.json": "e6986b41626ee10bdc864937ffb6d6bf275bb5b9c65120e6137d56e6331f089e", "https://bcr.bazel.build/modules/rules_cc/0.0.1/MODULE.bazel": "cb2aa0747f84c6c3a78dad4e2049c154f08ab9d166b1273835a8174940365647", "https://bcr.bazel.build/modules/rules_cc/0.0.10/MODULE.bazel": "ec1705118f7eaedd6e118508d3d26deba2a4e76476ada7e0e3965211be012002", - "https://bcr.bazel.build/modules/rules_cc/0.0.11/MODULE.bazel": "9f249c5624a4788067b96b8b896be10c7e8b4375dc46f6d8e1e51100113e0992", "https://bcr.bazel.build/modules/rules_cc/0.0.13/MODULE.bazel": "0e8529ed7b323dad0775ff924d2ae5af7640b23553dfcd4d34344c7e7a867191", + "https://bcr.bazel.build/modules/rules_cc/0.0.14/MODULE.bazel": "5e343a3aac88b8d7af3b1b6d2093b55c347b8eefc2e7d1442f7a02dc8fea48ac", "https://bcr.bazel.build/modules/rules_cc/0.0.15/MODULE.bazel": "6704c35f7b4a72502ee81f61bf88706b54f06b3cbe5558ac17e2e14666cd5dcc", "https://bcr.bazel.build/modules/rules_cc/0.0.16/MODULE.bazel": "7661303b8fc1b4d7f532e54e9d6565771fea666fbdf839e0a86affcd02defe87", "https://bcr.bazel.build/modules/rules_cc/0.0.2/MODULE.bazel": "6915987c90970493ab97393024c156ea8fb9f3bea953b2f3ec05c34f19b5695c", @@ -119,7 +120,8 @@ "https://bcr.bazel.build/modules/rules_cc/0.0.8/MODULE.bazel": "964c85c82cfeb6f3855e6a07054fdb159aced38e99a5eecf7bce9d53990afa3e", "https://bcr.bazel.build/modules/rules_cc/0.0.9/MODULE.bazel": "836e76439f354b89afe6a911a7adf59a6b2518fafb174483ad78a2a2fde7b1c5", "https://bcr.bazel.build/modules/rules_cc/0.1.1/MODULE.bazel": "2f0222a6f229f0bf44cd711dc13c858dad98c62d52bd51d8fc3a764a83125513", - "https://bcr.bazel.build/modules/rules_cc/0.1.1/source.json": "d61627377bd7dd1da4652063e368d9366fc9a73920bfa396798ad92172cf645c", + "https://bcr.bazel.build/modules/rules_cc/0.2.15/MODULE.bazel": "6a0a4a75a57aa6dc888300d848053a58c6b12a29f89d4304e1c41448514ec6e8", + "https://bcr.bazel.build/modules/rules_cc/0.2.15/source.json": "197965c6dcca5c98a9288f93849e2e1c69d622e71b0be8deb524e22d48c88e32", "https://bcr.bazel.build/modules/rules_foreign_cc/0.9.0/MODULE.bazel": "c9e8c682bf75b0e7c704166d79b599f93b72cfca5ad7477df596947891feeef6", "https://bcr.bazel.build/modules/rules_fuzzing/0.5.2/MODULE.bazel": "40c97d1144356f52905566c55811f13b299453a14ac7769dfba2ac38192337a8", "https://bcr.bazel.build/modules/rules_fuzzing/0.5.2/source.json": "c8b1e2c717646f1702290959a3302a178fb639d987ab61d548105019f11e527e", @@ -134,10 +136,10 @@ "https://bcr.bazel.build/modules/rules_java/7.2.0/MODULE.bazel": "06c0334c9be61e6cef2c8c84a7800cef502063269a5af25ceb100b192453d4ab", "https://bcr.bazel.build/modules/rules_java/7.3.2/MODULE.bazel": "50dece891cfdf1741ea230d001aa9c14398062f2b7c066470accace78e412bc2", "https://bcr.bazel.build/modules/rules_java/7.6.1/MODULE.bazel": "2f14b7e8a1aa2f67ae92bc69d1ec0fa8d9f827c4e17ff5e5f02e91caa3b2d0fe", - "https://bcr.bazel.build/modules/rules_java/7.6.5/MODULE.bazel": "481164be5e02e4cab6e77a36927683263be56b7e36fef918b458d7a8a1ebadb1", + "https://bcr.bazel.build/modules/rules_java/8.14.0/MODULE.bazel": "717717ed40cc69994596a45aec6ea78135ea434b8402fb91b009b9151dd65615", + "https://bcr.bazel.build/modules/rules_java/8.14.0/source.json": "8a88c4ca9e8759da53cddc88123880565c520503321e2566b4e33d0287a3d4bc", "https://bcr.bazel.build/modules/rules_java/8.3.2/MODULE.bazel": "7336d5511ad5af0b8615fdc7477535a2e4e723a357b6713af439fe8cf0195017", "https://bcr.bazel.build/modules/rules_java/8.5.1/MODULE.bazel": "d8a9e38cc5228881f7055a6079f6f7821a073df3744d441978e7a43e20226939", - "https://bcr.bazel.build/modules/rules_java/8.5.1/source.json": "db1a77d81b059e0f84985db67a22f3f579a529a86b7997605be3d214a0abe38e", "https://bcr.bazel.build/modules/rules_jvm_external/4.4.2/MODULE.bazel": "a56b85e418c83eb1839819f0b515c431010160383306d13ec21959ac412d2fe7", "https://bcr.bazel.build/modules/rules_jvm_external/5.1/MODULE.bazel": "33f6f999e03183f7d088c9be518a63467dfd0be94a11d0055fe2d210f89aa909", "https://bcr.bazel.build/modules/rules_jvm_external/5.2/MODULE.bazel": "d9351ba35217ad0de03816ef3ed63f89d411349353077348a45348b096615036", @@ -156,11 +158,9 @@ "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.1/MODULE.bazel": "e499aabe7cb796b784e9421be909c702eb79f664c20d21cbf120332a2a8dc378", - "https://bcr.bazel.build/modules/rules_nodejs/6.6.1/source.json": "c5eb6ed233d9ddadad77342e4aeefb836a4616f875acf2dbb181f98057c8c020", + "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", "https://bcr.bazel.build/modules/rules_pkg/1.0.1/MODULE.bazel": "5b1df97dbc29623bccdf2b0dcd0f5cb08e2f2c9050aab1092fd39a41e82686ff", "https://bcr.bazel.build/modules/rules_pkg/1.1.0/MODULE.bazel": "9db8031e71b6ef32d1846106e10dd0ee2deac042bd9a2de22b4761b0c3036453", "https://bcr.bazel.build/modules/rules_pkg/1.1.0/source.json": "fef768df13a92ce6067e1cd0cdc47560dace01354f1d921cfb1d632511f7d608", @@ -171,12 +171,12 @@ "https://bcr.bazel.build/modules/rules_proto/7.0.2/MODULE.bazel": "bf81793bd6d2ad89a37a40693e56c61b0ee30f7a7fdbaf3eabbf5f39de47dea2", "https://bcr.bazel.build/modules/rules_proto/7.0.2/source.json": "1e5e7260ae32ef4f2b52fd1d0de8d03b606a44c91b694d2f1afb1d3b28a48ce1", "https://bcr.bazel.build/modules/rules_python/0.10.2/MODULE.bazel": "cc82bc96f2997baa545ab3ce73f196d040ffb8756fd2d66125a530031cd90e5f", - "https://bcr.bazel.build/modules/rules_python/0.22.1/MODULE.bazel": "26114f0c0b5e93018c0c066d6673f1a2c3737c7e90af95eff30cfee38d0bbac7", "https://bcr.bazel.build/modules/rules_python/0.23.1/MODULE.bazel": "49ffccf0511cb8414de28321f5fcf2a31312b47c40cc21577144b7447f2bf300", "https://bcr.bazel.build/modules/rules_python/0.25.0/MODULE.bazel": "72f1506841c920a1afec76975b35312410eea3aa7b63267436bfb1dd91d2d382", "https://bcr.bazel.build/modules/rules_python/0.28.0/MODULE.bazel": "cba2573d870babc976664a912539b320cbaa7114cd3e8f053c720171cde331ed", "https://bcr.bazel.build/modules/rules_python/0.31.0/MODULE.bazel": "93a43dc47ee570e6ec9f5779b2e64c1476a6ce921c48cc9a1678a91dd5f8fd58", "https://bcr.bazel.build/modules/rules_python/0.4.0/MODULE.bazel": "9208ee05fd48bf09ac60ed269791cf17fb343db56c8226a720fbb1cdf467166c", + "https://bcr.bazel.build/modules/rules_python/0.40.0/MODULE.bazel": "9d1a3cd88ed7d8e39583d9ffe56ae8a244f67783ae89b60caafc9f5cf318ada7", "https://bcr.bazel.build/modules/rules_python/1.0.0/MODULE.bazel": "898a3d999c22caa585eb062b600f88654bf92efb204fa346fb55f6f8edffca43", "https://bcr.bazel.build/modules/rules_python/1.0.0/source.json": "b0162a65c6312e45e7912e39abd1a7f8856c2c7e41ecc9b6dc688a6f6400a917", "https://bcr.bazel.build/modules/rules_shell/0.2.0/MODULE.bazel": "fda8a652ab3c7d8fee214de05e7a9916d8b28082234e8d2c0094505c5268ed3c", @@ -193,32 +193,30 @@ "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", - "https://bcr.bazel.build/modules/zlib/1.3.1.bcr.3/source.json": "2be409ac3c7601245958cd4fcdff4288be79ed23bd690b4b951f500d54ee6e7d", + "https://bcr.bazel.build/modules/zlib/1.3.1.bcr.5/MODULE.bazel": "eec517b5bbe5492629466e11dae908d043364302283de25581e3eb944326c4ca", + "https://bcr.bazel.build/modules/zlib/1.3.1.bcr.5/source.json": "22bc55c47af97246cfc093d0acf683a7869377de362b5d1c552c2c2e16b7a806", "https://bcr.bazel.build/modules/zlib/1.3.1/MODULE.bazel": "751c9940dcfe869f5f7274e1295422a34623555916eb98c174c1e945594bf198" }, "selectedYankedVersions": {}, "moduleExtensions": { - "@@aspect_rules_esbuild~//esbuild:extensions.bzl%esbuild": { + "@@aspect_rules_esbuild+//esbuild:extensions.bzl%esbuild": { "general": { - "bzlTransitiveDigest": "PXvA3NOvgV+GXd72C+Zd7J5oEOKpEXHsGxKvRiqCb9U=", - "usagesDigest": "w3kRc6iou9hC6M+vwECvdfk0P8nsVNjHSl4Ftru44zU=", + "bzlTransitiveDigest": "0aod3RK04ALA/OKTExzd7QqyeqcC4O2GWuDwUxhQHp4=", + "usagesDigest": "ToTaCONCN/E05krnHXLM1kpV1zrHNxHrGpUip973II4=", "recordedFileInputs": {}, "recordedDirentsInputs": {}, "envVariables": {}, "generatedRepoSpecs": { "esbuild_darwin-x64": { - "bzlFile": "@@aspect_rules_esbuild~//esbuild:repositories.bzl", - "ruleClassName": "esbuild_repositories", + "repoRuleId": "@@aspect_rules_esbuild+//esbuild:repositories.bzl%esbuild_repositories", "attributes": { "esbuild_version": "0.19.9", "integrity": "", @@ -226,8 +224,7 @@ } }, "esbuild_darwin-arm64": { - "bzlFile": "@@aspect_rules_esbuild~//esbuild:repositories.bzl", - "ruleClassName": "esbuild_repositories", + "repoRuleId": "@@aspect_rules_esbuild+//esbuild:repositories.bzl%esbuild_repositories", "attributes": { "esbuild_version": "0.19.9", "integrity": "", @@ -235,8 +232,7 @@ } }, "esbuild_linux-x64": { - "bzlFile": "@@aspect_rules_esbuild~//esbuild:repositories.bzl", - "ruleClassName": "esbuild_repositories", + "repoRuleId": "@@aspect_rules_esbuild+//esbuild:repositories.bzl%esbuild_repositories", "attributes": { "esbuild_version": "0.19.9", "integrity": "", @@ -244,8 +240,7 @@ } }, "esbuild_linux-arm64": { - "bzlFile": "@@aspect_rules_esbuild~//esbuild:repositories.bzl", - "ruleClassName": "esbuild_repositories", + "repoRuleId": "@@aspect_rules_esbuild+//esbuild:repositories.bzl%esbuild_repositories", "attributes": { "esbuild_version": "0.19.9", "integrity": "", @@ -253,8 +248,7 @@ } }, "esbuild_win32-x64": { - "bzlFile": "@@aspect_rules_esbuild~//esbuild:repositories.bzl", - "ruleClassName": "esbuild_repositories", + "repoRuleId": "@@aspect_rules_esbuild+//esbuild:repositories.bzl%esbuild_repositories", "attributes": { "esbuild_version": "0.19.9", "integrity": "", @@ -262,16 +256,14 @@ } }, "esbuild_toolchains": { - "bzlFile": "@@aspect_rules_esbuild~//esbuild/private:toolchains_repo.bzl", - "ruleClassName": "toolchains_repo", + "repoRuleId": "@@aspect_rules_esbuild+//esbuild/private:toolchains_repo.bzl%toolchains_repo", "attributes": { "esbuild_version": "0.19.9", "user_repository_name": "esbuild" } }, "npm__esbuild_0.19.9": { - "bzlFile": "@@aspect_rules_js~//npm/private:npm_import.bzl", - "ruleClassName": "npm_import_rule", + "repoRuleId": "@@aspect_rules_js+//npm/private:npm_import.bzl%npm_import_rule", "attributes": { "package": "esbuild", "version": "0.19.9", @@ -298,8 +290,7 @@ } }, "npm__esbuild_0.19.9__links": { - "bzlFile": "@@aspect_rules_js~//npm/private:npm_import.bzl", - "ruleClassName": "npm_import_links", + "repoRuleId": "@@aspect_rules_js+//npm/private:npm_import.bzl%npm_import_links", "attributes": { "package": "esbuild", "version": "0.19.9", @@ -325,106 +316,110 @@ }, "recordedRepoMappingEntries": [ [ - "aspect_bazel_lib~", + "aspect_bazel_lib+", "bazel_skylib", - "bazel_skylib~" + "bazel_skylib+" ], [ - "aspect_bazel_lib~", + "aspect_bazel_lib+", "bazel_tools", "bazel_tools" ], [ - "aspect_bazel_lib~", + "aspect_bazel_lib+", "tar.bzl", - "tar.bzl~" + "tar.bzl+" ], [ - "aspect_rules_esbuild~", + "aspect_rules_esbuild+", "aspect_rules_js", - "aspect_rules_js~" + "aspect_rules_js+" ], [ - "aspect_rules_esbuild~", + "aspect_rules_esbuild+", "aspect_tools_telemetry_report", - "aspect_tools_telemetry~~telemetry~aspect_tools_telemetry_report" + "aspect_tools_telemetry++telemetry+aspect_tools_telemetry_report" ], [ - "aspect_rules_esbuild~", + "aspect_rules_esbuild+", "bazel_skylib", - "bazel_skylib~" + "bazel_skylib+" ], [ - "aspect_rules_js~", + "aspect_rules_js+", "aspect_bazel_lib", - "aspect_bazel_lib~" + "aspect_bazel_lib+" ], [ - "aspect_rules_js~", + "aspect_rules_js+", "aspect_rules_js", - "aspect_rules_js~" + "aspect_rules_js+" ], [ - "aspect_rules_js~", + "aspect_rules_js+", "aspect_tools_telemetry_report", - "aspect_tools_telemetry~~telemetry~aspect_tools_telemetry_report" + "aspect_tools_telemetry++telemetry+aspect_tools_telemetry_report" ], [ - "aspect_rules_js~", + "aspect_rules_js+", "bazel_lib", - "bazel_lib~" + "bazel_lib+" ], [ - "aspect_rules_js~", + "aspect_rules_js+", "bazel_skylib", - "bazel_skylib~" + "bazel_skylib+" ], [ - "aspect_rules_js~", + "aspect_rules_js+", "bazel_tools", "bazel_tools" ], [ - "bazel_lib~", + "bazel_lib+", + "bazel_skylib", + "bazel_skylib+" + ], + [ + "bazel_lib+", "bazel_tools", "bazel_tools" ], [ - "tar.bzl~", - "aspect_bazel_lib", - "aspect_bazel_lib~" + "tar.bzl+", + "bazel_lib", + "bazel_lib+" ], [ - "tar.bzl~", + "tar.bzl+", "bazel_skylib", - "bazel_skylib~" + "bazel_skylib+" ], [ - "tar.bzl~", + "tar.bzl+", "tar.bzl", - "tar.bzl~" + "tar.bzl+" ] ] } }, - "@@aspect_rules_js~//npm:extensions.bzl%pnpm": { + "@@aspect_rules_js+//npm:extensions.bzl%pnpm": { "general": { - "bzlTransitiveDigest": "MDL1U5KxASXhKcoXRgOqr7qxCgpKkNaQ2HSyFvw6u1U=", - "usagesDigest": "J526kdgX5AQ2bYrNGwe6lTrakPUSZPfyHG24PGMUG0s=", + "bzlTransitiveDigest": "tQ+7EwLfQwqi/T4v5/N3NNHTmP6Wu/FqXxRDndEB2OU=", + "usagesDigest": "fkR8y929BQ1GFezNYBR/HXJUcMa3NtJvhzsZrG8I9vI=", "recordedFileInputs": {}, "recordedDirentsInputs": {}, "envVariables": {}, "generatedRepoSpecs": { "pnpm": { - "bzlFile": "@@aspect_rules_js~//npm/private:npm_import.bzl", - "ruleClassName": "npm_import_rule", + "repoRuleId": "@@aspect_rules_js+//npm/private:npm_import.bzl%npm_import_rule", "attributes": { "package": "pnpm", - "version": "8.15.9", + "version": "10.26.0", "root_package": "", "link_workspace": "", "link_packages": {}, - "integrity": "sha512-SZQ0ydj90aJ5Tr9FUrOyXApjOrzuW7Fee13pDzL0e1E6ypjNXP0AHDHw20VLw4BO3M1XhQHkyik6aBYWa72fgQ==", + "integrity": "sha512-Oz9scl6+cSUGwKsa1BM8+GsfS2h+/85iqbOLTXLjlUJC5kMZD8UfoWQpScc19APevUT1yw7dZXq+Y6i2p+HkAg==", "url": "", "commit": "", "patch_args": [ @@ -444,11 +439,10 @@ } }, "pnpm__links": { - "bzlFile": "@@aspect_rules_js~//npm/private:npm_import.bzl", - "ruleClassName": "npm_import_links", + "repoRuleId": "@@aspect_rules_js+//npm/private:npm_import.bzl%npm_import_links", "attributes": { "package": "pnpm", - "version": "8.15.9", + "version": "10.26.0", "dev": false, "root_package": "", "link_packages": {}, @@ -471,101 +465,105 @@ }, "recordedRepoMappingEntries": [ [ - "aspect_bazel_lib~", + "aspect_bazel_lib+", "bazel_skylib", - "bazel_skylib~" + "bazel_skylib+" ], [ - "aspect_bazel_lib~", + "aspect_bazel_lib+", "bazel_tools", "bazel_tools" ], [ - "aspect_bazel_lib~", + "aspect_bazel_lib+", "tar.bzl", - "tar.bzl~" + "tar.bzl+" ], [ - "aspect_rules_js~", + "aspect_rules_js+", "aspect_bazel_lib", - "aspect_bazel_lib~" + "aspect_bazel_lib+" ], [ - "aspect_rules_js~", + "aspect_rules_js+", "aspect_rules_js", - "aspect_rules_js~" + "aspect_rules_js+" ], [ - "aspect_rules_js~", + "aspect_rules_js+", "aspect_tools_telemetry_report", - "aspect_tools_telemetry~~telemetry~aspect_tools_telemetry_report" + "aspect_tools_telemetry++telemetry+aspect_tools_telemetry_report" ], [ - "aspect_rules_js~", + "aspect_rules_js+", "bazel_features", - "bazel_features~" + "bazel_features+" ], [ - "aspect_rules_js~", + "aspect_rules_js+", "bazel_lib", - "bazel_lib~" + "bazel_lib+" ], [ - "aspect_rules_js~", + "aspect_rules_js+", "bazel_skylib", - "bazel_skylib~" + "bazel_skylib+" ], [ - "aspect_rules_js~", + "aspect_rules_js+", "bazel_tools", "bazel_tools" ], [ - "bazel_features~", + "bazel_features+", "bazel_features_globals", - "bazel_features~~version_extension~bazel_features_globals" + "bazel_features++version_extension+bazel_features_globals" ], [ - "bazel_features~", + "bazel_features+", "bazel_features_version", - "bazel_features~~version_extension~bazel_features_version" + "bazel_features++version_extension+bazel_features_version" ], [ - "bazel_lib~", + "bazel_lib+", + "bazel_skylib", + "bazel_skylib+" + ], + [ + "bazel_lib+", "bazel_tools", "bazel_tools" ], [ - "tar.bzl~", - "aspect_bazel_lib", - "aspect_bazel_lib~" + "tar.bzl+", + "bazel_lib", + "bazel_lib+" ], [ - "tar.bzl~", + "tar.bzl+", "bazel_skylib", - "bazel_skylib~" + "bazel_skylib+" ], [ - "tar.bzl~", + "tar.bzl+", "tar.bzl", - "tar.bzl~" + "tar.bzl+" ] ] } }, - "@@aspect_rules_ts~//ts:extensions.bzl%ext": { + "@@aspect_rules_ts+//ts:extensions.bzl%ext": { "general": { - "bzlTransitiveDigest": "pEu5+6q07zdUqscbVkhWTNLGT9OGRpnFCF4OGHv1n1k=", - "usagesDigest": "PB65rYTG/QbLoSQfRhLDeERG+0m7bjTPzYXBqieQ5/4=", + "bzlTransitiveDigest": "7k3bewVApw4Kc6Rpho1Rrs1nrW/5jphUA5Mh1iHE2U4=", + "usagesDigest": "aaqqxEFKCRGFkeAf0pKmXvZZTLGYIk3pQsDFG28ZbNg=", "recordedFileInputs": { - "@@rules_browsers~//package.json": "84dc1ba9b1c667a25894e97218bd8f247d54f24bb694efb397a881be3c06a4c5" + "@@rules_browsers+//package.json": "84dc1ba9b1c667a25894e97218bd8f247d54f24bb694efb397a881be3c06a4c5" }, "recordedDirentsInputs": {}, "envVariables": {}, "generatedRepoSpecs": { "angular_cli_npm_typescript": { - "bzlFile": "@@aspect_rules_ts~//ts/private:npm_repositories.bzl", - "ruleClassName": "http_archive_version", + "repoRuleId": "@@aspect_rules_ts+//ts/private:npm_repositories.bzl%http_archive_version", "attributes": { "version": "5.9.3", "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", @@ -575,8 +573,7 @@ } }, "rules_angular_npm_typescript": { - "bzlFile": "@@aspect_rules_ts~//ts/private:npm_repositories.bzl", - "ruleClassName": "http_archive_version", + "repoRuleId": "@@aspect_rules_ts+//ts/private:npm_repositories.bzl%http_archive_version", "attributes": { "version": "5.9.2", "integrity": "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==", @@ -586,8 +583,7 @@ } }, "npm_typescript": { - "bzlFile": "@@aspect_rules_ts~//ts/private:npm_repositories.bzl", - "ruleClassName": "http_archive_version", + "repoRuleId": "@@aspect_rules_ts+//ts/private:npm_repositories.bzl%http_archive_version", "attributes": { "version": "5.9.3", "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", @@ -597,11 +593,10 @@ } }, "npm_rules_browsers_typescript": { - "bzlFile": "@@aspect_rules_ts~//ts/private:npm_repositories.bzl", - "ruleClassName": "http_archive_version", + "repoRuleId": "@@aspect_rules_ts+//ts/private:npm_repositories.bzl%http_archive_version", "attributes": { "version": "", - "version_from": "@@rules_browsers~//:package.json", + "version_from": "@@rules_browsers+//:package.json", "integrity": "", "urls": [ "https://registry.npmjs.org/typescript/-/typescript-{}.tgz" @@ -611,73 +606,71 @@ }, "recordedRepoMappingEntries": [ [ - "aspect_rules_ts~", + "aspect_rules_ts+", "aspect_rules_ts", - "aspect_rules_ts~" + "aspect_rules_ts+" ], [ - "aspect_rules_ts~", + "aspect_rules_ts+", "bazel_tools", "bazel_tools" ] ] } }, - "@@aspect_tools_telemetry~//:extension.bzl%telemetry": { + "@@aspect_tools_telemetry+//:extension.bzl%telemetry": { "general": { - "bzlTransitiveDigest": "gA7tPEdJXhskzPIEUxjX9IdDrM6+WjfbgXJ8Ez47umk=", - "usagesDigest": "SMpx40jPXDQGAklWybhmgSAy5HdV4ZEIYFMuHxifgT0=", + "bzlTransitiveDigest": "cl5A2O84vDL6Tt+Qga8FCj1DUDGqn+e7ly5rZ+4xvcc=", + "usagesDigest": "jHASCmhI+ziv94KZ5hlx6t1ixFDdVXFm2VnOVVbAqww=", "recordedFileInputs": {}, "recordedDirentsInputs": {}, "envVariables": {}, "generatedRepoSpecs": { "aspect_tools_telemetry_report": { - "bzlFile": "@@aspect_tools_telemetry~//:extension.bzl", - "ruleClassName": "tel_repository", + "repoRuleId": "@@aspect_tools_telemetry+//:extension.bzl%tel_repository", "attributes": { "deps": { - "aspect_rules_js": "2.8.1", - "aspect_rules_ts": "3.7.1", - "aspect_rules_esbuild": "0.24.0", - "aspect_tools_telemetry": "0.2.8" + "aspect_rules_js": "2.8.3", + "aspect_rules_ts": "3.8.1", + "aspect_rules_esbuild": "0.25.0", + "aspect_rules_jasmine": "2.0.2", + "aspect_tools_telemetry": "0.3.3" } } } }, "recordedRepoMappingEntries": [ [ - "aspect_tools_telemetry~", - "aspect_bazel_lib", - "aspect_bazel_lib~" + "aspect_tools_telemetry+", + "bazel_lib", + "bazel_lib+" ], [ - "aspect_tools_telemetry~", + "aspect_tools_telemetry+", "bazel_skylib", - "bazel_skylib~" + "bazel_skylib+" ] ] } }, - "@@pybind11_bazel~//:python_configure.bzl%extension": { + "@@pybind11_bazel+//:python_configure.bzl%extension": { "general": { - "bzlTransitiveDigest": "dFd3A3f+jPCss+EDKMp/jxjcUhfMku130eT1KGxSCwA=", - "usagesDigest": "gNvOHVcAlwgDsNXD0amkv2CC96mnaCThPQoE44y8K+w=", + "bzlTransitiveDigest": "c9ZWWeXeu6bctL4/SsY2otFWyeFN0JJ20+ymGyJZtWk=", + "usagesDigest": "fycyB39YnXIJkfWCIXLUKJMZzANcuLy9ZE73hRucjFk=", "recordedFileInputs": { - "@@pybind11_bazel~//MODULE.bazel": "88af1c246226d87e65be78ed49ecd1e6f5e98648558c14ce99176da041dc378e" + "@@pybind11_bazel+//MODULE.bazel": "88af1c246226d87e65be78ed49ecd1e6f5e98648558c14ce99176da041dc378e" }, "recordedDirentsInputs": {}, "envVariables": {}, "generatedRepoSpecs": { "local_config_python": { - "bzlFile": "@@pybind11_bazel~//:python_configure.bzl", - "ruleClassName": "python_configure", + "repoRuleId": "@@pybind11_bazel+//:python_configure.bzl%python_configure", "attributes": {} }, "pybind11": { - "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", - "ruleClassName": "http_archive", + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", "attributes": { - "build_file": "@@pybind11_bazel~//:pybind11.BUILD", + "build_file": "@@pybind11_bazel+//:pybind11.BUILD", "strip_prefix": "pybind11-2.11.1", "urls": [ "https://github.com/pybind/pybind11/archive/v2.11.1.zip" @@ -687,64 +680,60 @@ }, "recordedRepoMappingEntries": [ [ - "pybind11_bazel~", + "pybind11_bazel+", "bazel_tools", "bazel_tools" ] ] } }, - "@@rules_angular~//setup:extensions.bzl%rules_angular": { + "@@rules_angular+//setup:extensions.bzl%rules_angular": { "general": { "bzlTransitiveDigest": "fkaH7HMicL3g7/NDaFzlq39kcLopMyQ3KdbDn+5CRzA=", - "usagesDigest": "mthsJSuRvcThgmaeFEDgFmVR6HwM1CSMSOyLlJaCSI0=", + "usagesDigest": "ZinuLP7QHxaW5achD0Vz19qElMu4r2LvGvh96Z5zYlA=", "recordedFileInputs": {}, "recordedDirentsInputs": {}, "envVariables": {}, "generatedRepoSpecs": { "components_rules_angular_configurable_deps": { - "bzlFile": "@@rules_angular~//setup:repositories.bzl", - "ruleClassName": "configurable_deps_repo", + "repoRuleId": "@@rules_angular+//setup:repositories.bzl%configurable_deps_repo", "attributes": { - "angular_compiler_cli": "@@rules_angular~//:node_modules/@angular/compiler-cli", - "typescript": "@@rules_angular~//:node_modules/typescript" + "angular_compiler_cli": "@@rules_angular+//:node_modules/@angular/compiler-cli", + "typescript": "@@rules_angular+//:node_modules/typescript" } }, "rules_angular_configurable_deps": { - "bzlFile": "@@rules_angular~//setup:repositories.bzl", - "ruleClassName": "configurable_deps_repo", + "repoRuleId": "@@rules_angular+//setup:repositories.bzl%configurable_deps_repo", "attributes": { - "angular_compiler_cli": "@@rules_angular~//:node_modules/@angular/compiler-cli", - "typescript": "@@rules_angular~//:node_modules/typescript-local" + "angular_compiler_cli": "@@rules_angular+//:node_modules/@angular/compiler-cli", + "typescript": "@@rules_angular+//:node_modules/typescript-local" } }, "dev_infra_rules_angular_configurable_deps": { - "bzlFile": "@@rules_angular~//setup:repositories.bzl", - "ruleClassName": "configurable_deps_repo", + "repoRuleId": "@@rules_angular+//setup:repositories.bzl%configurable_deps_repo", "attributes": { - "angular_compiler_cli": "@@rules_angular~//:node_modules/@angular/compiler-cli", - "typescript": "@@rules_angular~//:node_modules/typescript" + "angular_compiler_cli": "@@rules_angular+//:node_modules/@angular/compiler-cli", + "typescript": "@@rules_angular+//:node_modules/typescript" } } }, "recordedRepoMappingEntries": [] } }, - "@@rules_browsers~//browsers:extensions.bzl%browsers": { + "@@rules_browsers+//browsers:extensions.bzl%browsers": { "general": { - "bzlTransitiveDigest": "6QMCx97Hwh2hyQPqZEA9AKAxbpygF41+K8xJfeqJYm8=", - "usagesDigest": "1PlExi+b77pSr2tAxFCVbpCtFoA7oixHabaL3dmas4Y=", + "bzlTransitiveDigest": "ljZlVgWkQJnI6EvlHVfYit2EttUE52gDTbvmota5YO8=", + "usagesDigest": "FS7q5WaIwg3KirS3njhuPFkTIBYvDaTInVGrlzu0XL8=", "recordedFileInputs": {}, "recordedDirentsInputs": {}, "envVariables": {}, "generatedRepoSpecs": { "rules_browsers_chrome_linux": { - "bzlFile": "@@rules_browsers~//browsers/private:browser_repo.bzl", - "ruleClassName": "browser_repo", + "repoRuleId": "@@rules_browsers+//browsers/private:browser_repo.bzl%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" @@ -758,12 +747,11 @@ } }, "rules_browsers_chrome_mac": { - "bzlFile": "@@rules_browsers~//browsers/private:browser_repo.bzl", - "ruleClassName": "browser_repo", + "repoRuleId": "@@rules_browsers+//browsers/private:browser_repo.bzl%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" @@ -777,12 +765,11 @@ } }, "rules_browsers_chrome_mac_arm": { - "bzlFile": "@@rules_browsers~//browsers/private:browser_repo.bzl", - "ruleClassName": "browser_repo", + "repoRuleId": "@@rules_browsers+//browsers/private:browser_repo.bzl%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" @@ -796,12 +783,11 @@ } }, "rules_browsers_chrome_win64": { - "bzlFile": "@@rules_browsers~//browsers/private:browser_repo.bzl", - "ruleClassName": "browser_repo", + "repoRuleId": "@@rules_browsers+//browsers/private:browser_repo.bzl%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" @@ -815,12 +801,11 @@ } }, "rules_browsers_chromedriver_linux": { - "bzlFile": "@@rules_browsers~//browsers/private:browser_repo.bzl", - "ruleClassName": "browser_repo", + "repoRuleId": "@@rules_browsers+//browsers/private:browser_repo.bzl%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" @@ -832,12 +817,11 @@ } }, "rules_browsers_chromedriver_mac": { - "bzlFile": "@@rules_browsers~//browsers/private:browser_repo.bzl", - "ruleClassName": "browser_repo", + "repoRuleId": "@@rules_browsers+//browsers/private:browser_repo.bzl%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" @@ -849,12 +833,11 @@ } }, "rules_browsers_chromedriver_mac_arm": { - "bzlFile": "@@rules_browsers~//browsers/private:browser_repo.bzl", - "ruleClassName": "browser_repo", + "repoRuleId": "@@rules_browsers+//browsers/private:browser_repo.bzl%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" @@ -866,12 +849,11 @@ } }, "rules_browsers_chromedriver_win64": { - "bzlFile": "@@rules_browsers~//browsers/private:browser_repo.bzl", - "ruleClassName": "browser_repo", + "repoRuleId": "@@rules_browsers+//browsers/private:browser_repo.bzl%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" @@ -883,12 +865,11 @@ } }, "rules_browsers_firefox_linux": { - "bzlFile": "@@rules_browsers~//browsers/private:browser_repo.bzl", - "ruleClassName": "browser_repo", + "repoRuleId": "@@rules_browsers+//browsers/private:browser_repo.bzl%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" @@ -900,12 +881,11 @@ } }, "rules_browsers_firefox_mac": { - "bzlFile": "@@rules_browsers~//browsers/private:browser_repo.bzl", - "ruleClassName": "browser_repo", + "repoRuleId": "@@rules_browsers+//browsers/private:browser_repo.bzl%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" @@ -917,12 +897,11 @@ } }, "rules_browsers_firefox_mac_arm": { - "bzlFile": "@@rules_browsers~//browsers/private:browser_repo.bzl", - "ruleClassName": "browser_repo", + "repoRuleId": "@@rules_browsers+//browsers/private:browser_repo.bzl%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" @@ -934,12 +913,11 @@ } }, "rules_browsers_firefox_win64": { - "bzlFile": "@@rules_browsers~//browsers/private:browser_repo.bzl", - "ruleClassName": "browser_repo", + "repoRuleId": "@@rules_browsers+//browsers/private:browser_repo.bzl%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" @@ -954,17 +932,16 @@ "recordedRepoMappingEntries": [] } }, - "@@rules_fuzzing~//fuzzing/private:extensions.bzl%non_module_dependencies": { + "@@rules_fuzzing+//fuzzing/private:extensions.bzl%non_module_dependencies": { "general": { - "bzlTransitiveDigest": "VMhyxXtdJvrNlLts7afAymA+pOatXuh5kLdxzVAZ/04=", - "usagesDigest": "YnIrdgwnf3iCLfChsltBdZ7yOJh706lpa2vww/i2pDI=", + "bzlTransitiveDigest": "WHRlQQnxW7e7XMRBhq7SARkDarLDOAbg6iLaJpk5QYM=", + "usagesDigest": "wy6ISK6UOcBEjj/mvJ/S3WeXoO67X+1llb9yPyFtPgc=", "recordedFileInputs": {}, "recordedDirentsInputs": {}, "envVariables": {}, "generatedRepoSpecs": { "platforms": { - "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", - "ruleClassName": "http_archive", + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", "attributes": { "urls": [ "https://mirror.bazel.build/github.com/bazelbuild/platforms/releases/download/0.0.8/platforms-0.0.8.tar.gz", @@ -974,8 +951,7 @@ } }, "rules_python": { - "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", - "ruleClassName": "http_archive", + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", "attributes": { "sha256": "d70cd72a7a4880f0000a6346253414825c19cdd40a28289bdf67b8e6480edff8", "strip_prefix": "rules_python-0.28.0", @@ -983,8 +959,7 @@ } }, "bazel_skylib": { - "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", - "ruleClassName": "http_archive", + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", "attributes": { "sha256": "cd55a062e763b9349921f0f5db8c3933288dc8ba4f76dd9416aac68acee3cb94", "urls": [ @@ -994,8 +969,7 @@ } }, "com_google_absl": { - "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", - "ruleClassName": "http_archive", + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", "attributes": { "urls": [ "https://github.com/abseil/abseil-cpp/archive/refs/tags/20240116.1.zip" @@ -1005,31 +979,27 @@ } }, "rules_fuzzing_oss_fuzz": { - "bzlFile": "@@rules_fuzzing~//fuzzing/private/oss_fuzz:repository.bzl", - "ruleClassName": "oss_fuzz_repository", + "repoRuleId": "@@rules_fuzzing+//fuzzing/private/oss_fuzz:repository.bzl%oss_fuzz_repository", "attributes": {} }, "honggfuzz": { - "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", - "ruleClassName": "http_archive", + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", "attributes": { - "build_file": "@@rules_fuzzing~//:honggfuzz.BUILD", + "build_file": "@@rules_fuzzing+//:honggfuzz.BUILD", "sha256": "6b18ba13bc1f36b7b950c72d80f19ea67fbadc0ac0bb297ec89ad91f2eaa423e", "url": "https://github.com/google/honggfuzz/archive/2.5.zip", "strip_prefix": "honggfuzz-2.5" } }, "rules_fuzzing_jazzer": { - "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", - "ruleClassName": "http_jar", + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_jar", "attributes": { "sha256": "ee6feb569d88962d59cb59e8a31eb9d007c82683f3ebc64955fd5b96f277eec2", "url": "https://repo1.maven.org/maven2/com/code-intelligence/jazzer/0.20.1/jazzer-0.20.1.jar" } }, "rules_fuzzing_jazzer_api": { - "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", - "ruleClassName": "http_jar", + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_jar", "attributes": { "sha256": "f5a60242bc408f7fa20fccf10d6c5c5ea1fcb3c6f44642fec5af88373ae7aa1b", "url": "https://repo1.maven.org/maven2/com/code-intelligence/jazzer-api/0.20.1/jazzer-api-0.20.1.jar" @@ -1038,47 +1008,23 @@ }, "recordedRepoMappingEntries": [ [ - "rules_fuzzing~", - "bazel_tools", - "bazel_tools" - ] - ] - } - }, - "@@rules_java~//java:rules_java_deps.bzl%compatibility_proxy": { - "general": { - "bzlTransitiveDigest": "C4xqrMy1wN4iuTN6Z2eCm94S5XingHhD6uwrIXvCxVI=", - "usagesDigest": "pwHZ+26iLgQdwvdZeA5wnAjKnNI3y6XO2VbhOTeo5h8=", - "recordedFileInputs": {}, - "recordedDirentsInputs": {}, - "envVariables": {}, - "generatedRepoSpecs": { - "compatibility_proxy": { - "bzlFile": "@@rules_java~//java:rules_java_deps.bzl", - "ruleClassName": "_compatibility_proxy_repo_rule", - "attributes": {} - } - }, - "recordedRepoMappingEntries": [ - [ - "rules_java~", + "rules_fuzzing+", "bazel_tools", "bazel_tools" ] ] } }, - "@@rules_kotlin~//src/main/starlark/core/repositories:bzlmod_setup.bzl%rules_kotlin_extensions": { + "@@rules_kotlin+//src/main/starlark/core/repositories:bzlmod_setup.bzl%rules_kotlin_extensions": { "general": { - "bzlTransitiveDigest": "eecmTsmdIQveoA97hPtH3/Ej/kugbdCI24bhXIXaly8=", - "usagesDigest": "aJF6fLy82rR95Ff5CZPAqxNoFgOMLMN5ImfBS0nhnkg=", + "bzlTransitiveDigest": "rL/34P1aFDq2GqVC2zCFgQ8nTuOC6ziogocpvG50Qz8=", + "usagesDigest": "QI2z8ZUR+mqtbwsf2fLqYdJAkPOHdOV+tF2yVAUgRzw=", "recordedFileInputs": {}, "recordedDirentsInputs": {}, "envVariables": {}, "generatedRepoSpecs": { "com_github_jetbrains_kotlin_git": { - "bzlFile": "@@rules_kotlin~//src/main/starlark/core/repositories:compiler.bzl", - "ruleClassName": "kotlin_compiler_git_repository", + "repoRuleId": "@@rules_kotlin+//src/main/starlark/core/repositories:compiler.bzl%kotlin_compiler_git_repository", "attributes": { "urls": [ "https://github.com/JetBrains/kotlin/releases/download/v1.9.23/kotlin-compiler-1.9.23.zip" @@ -1087,16 +1033,14 @@ } }, "com_github_jetbrains_kotlin": { - "bzlFile": "@@rules_kotlin~//src/main/starlark/core/repositories:compiler.bzl", - "ruleClassName": "kotlin_capabilities_repository", + "repoRuleId": "@@rules_kotlin+//src/main/starlark/core/repositories:compiler.bzl%kotlin_capabilities_repository", "attributes": { "git_repository_name": "com_github_jetbrains_kotlin_git", "compiler_version": "1.9.23" } }, "com_github_google_ksp": { - "bzlFile": "@@rules_kotlin~//src/main/starlark/core/repositories:ksp.bzl", - "ruleClassName": "ksp_compiler_plugin_repository", + "repoRuleId": "@@rules_kotlin+//src/main/starlark/core/repositories:ksp.bzl%ksp_compiler_plugin_repository", "attributes": { "urls": [ "https://github.com/google/ksp/releases/download/1.9.23-1.0.20/artifacts.zip" @@ -1106,8 +1050,7 @@ } }, "com_github_pinterest_ktlint": { - "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", - "ruleClassName": "http_file", + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_file", "attributes": { "sha256": "01b2e0ef893383a50dbeb13970fe7fa3be36ca3e83259e01649945b09d736985", "urls": [ @@ -1117,8 +1060,7 @@ } }, "rules_android": { - "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", - "ruleClassName": "http_archive", + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", "attributes": { "sha256": "cd06d15dd8bb59926e4d65f9003bfc20f9da4b2519985c27e190cddc8b7a7806", "strip_prefix": "rules_android-0.1.1", @@ -1130,157 +1072,153 @@ }, "recordedRepoMappingEntries": [ [ - "rules_kotlin~", + "rules_kotlin+", "bazel_tools", "bazel_tools" ] ] } }, - "@@rules_nodejs~//nodejs:extensions.bzl%node": { + "@@rules_nodejs+//nodejs:extensions.bzl%node": { "general": { - "bzlTransitiveDigest": "Oex83HlYG4+sPr0oUbvldjyP3JnYazq0jAVgUXDx7yU=", - "usagesDigest": "3BeF7a1MDbYZesa8EOSh4Ihre1HMpOUWna9iTCBluY0=", + "bzlTransitiveDigest": "NwcLXHrbh2hoorA/Ybmcpjxsn/6avQmewDglodkDrgo=", + "usagesDigest": "S8pbOD3W4rSYjK/dNi5FSVLmT25mLbwVs9g/9fC2SN8=", "recordedFileInputs": {}, "recordedDirentsInputs": {}, "envVariables": {}, "generatedRepoSpecs": { "nodejs_linux_amd64": { - "bzlFile": "@@rules_nodejs~//nodejs:repositories.bzl", - "ruleClassName": "_nodejs_repositories", + "repoRuleId": "@@rules_nodejs+//nodejs:repositories.bzl%_nodejs_repositories", "attributes": { "node_download_auth": {}, "node_repositories": {}, "node_urls": [ "https://nodejs.org/dist/v{version}/{filename}" ], - "node_version": "24.0.0", + "node_version": "20.19.5", + "node_version_from_nvmrc": "@@//:.nvmrc", "include_headers": false, "platform": "linux_amd64" } }, "nodejs_linux_arm64": { - "bzlFile": "@@rules_nodejs~//nodejs:repositories.bzl", - "ruleClassName": "_nodejs_repositories", + "repoRuleId": "@@rules_nodejs+//nodejs:repositories.bzl%_nodejs_repositories", "attributes": { "node_download_auth": {}, "node_repositories": {}, "node_urls": [ "https://nodejs.org/dist/v{version}/{filename}" ], - "node_version": "24.0.0", + "node_version": "20.19.5", + "node_version_from_nvmrc": "@@//:.nvmrc", "include_headers": false, "platform": "linux_arm64" } }, "nodejs_linux_s390x": { - "bzlFile": "@@rules_nodejs~//nodejs:repositories.bzl", - "ruleClassName": "_nodejs_repositories", + "repoRuleId": "@@rules_nodejs+//nodejs:repositories.bzl%_nodejs_repositories", "attributes": { "node_download_auth": {}, "node_repositories": {}, "node_urls": [ "https://nodejs.org/dist/v{version}/{filename}" ], - "node_version": "24.0.0", + "node_version": "20.19.5", + "node_version_from_nvmrc": "@@//:.nvmrc", "include_headers": false, "platform": "linux_s390x" } }, "nodejs_linux_ppc64le": { - "bzlFile": "@@rules_nodejs~//nodejs:repositories.bzl", - "ruleClassName": "_nodejs_repositories", + "repoRuleId": "@@rules_nodejs+//nodejs:repositories.bzl%_nodejs_repositories", "attributes": { "node_download_auth": {}, "node_repositories": {}, "node_urls": [ "https://nodejs.org/dist/v{version}/{filename}" ], - "node_version": "24.0.0", + "node_version": "20.19.5", + "node_version_from_nvmrc": "@@//:.nvmrc", "include_headers": false, "platform": "linux_ppc64le" } }, "nodejs_darwin_amd64": { - "bzlFile": "@@rules_nodejs~//nodejs:repositories.bzl", - "ruleClassName": "_nodejs_repositories", + "repoRuleId": "@@rules_nodejs+//nodejs:repositories.bzl%_nodejs_repositories", "attributes": { "node_download_auth": {}, "node_repositories": {}, "node_urls": [ "https://nodejs.org/dist/v{version}/{filename}" ], - "node_version": "24.0.0", + "node_version": "20.19.5", + "node_version_from_nvmrc": "@@//:.nvmrc", "include_headers": false, "platform": "darwin_amd64" } }, "nodejs_darwin_arm64": { - "bzlFile": "@@rules_nodejs~//nodejs:repositories.bzl", - "ruleClassName": "_nodejs_repositories", + "repoRuleId": "@@rules_nodejs+//nodejs:repositories.bzl%_nodejs_repositories", "attributes": { "node_download_auth": {}, "node_repositories": {}, "node_urls": [ "https://nodejs.org/dist/v{version}/{filename}" ], - "node_version": "24.0.0", + "node_version": "20.19.5", + "node_version_from_nvmrc": "@@//:.nvmrc", "include_headers": false, "platform": "darwin_arm64" } }, "nodejs_windows_amd64": { - "bzlFile": "@@rules_nodejs~//nodejs:repositories.bzl", - "ruleClassName": "_nodejs_repositories", + "repoRuleId": "@@rules_nodejs+//nodejs:repositories.bzl%_nodejs_repositories", "attributes": { "node_download_auth": {}, "node_repositories": {}, "node_urls": [ "https://nodejs.org/dist/v{version}/{filename}" ], - "node_version": "24.0.0", + "node_version": "20.19.5", + "node_version_from_nvmrc": "@@//:.nvmrc", "include_headers": false, "platform": "windows_amd64" } }, "nodejs_windows_arm64": { - "bzlFile": "@@rules_nodejs~//nodejs:repositories.bzl", - "ruleClassName": "_nodejs_repositories", + "repoRuleId": "@@rules_nodejs+//nodejs:repositories.bzl%_nodejs_repositories", "attributes": { "node_download_auth": {}, "node_repositories": {}, "node_urls": [ "https://nodejs.org/dist/v{version}/{filename}" ], - "node_version": "24.0.0", + "node_version": "20.19.5", + "node_version_from_nvmrc": "@@//:.nvmrc", "include_headers": false, "platform": "windows_arm64" } }, "nodejs": { - "bzlFile": "@@rules_nodejs~//nodejs/private:nodejs_repo_host_os_alias.bzl", - "ruleClassName": "nodejs_repo_host_os_alias", + "repoRuleId": "@@rules_nodejs+//nodejs/private:nodejs_repo_host_os_alias.bzl%nodejs_repo_host_os_alias", "attributes": { "user_node_repository_name": "nodejs" } }, "nodejs_host": { - "bzlFile": "@@rules_nodejs~//nodejs/private:nodejs_repo_host_os_alias.bzl", - "ruleClassName": "nodejs_repo_host_os_alias", + "repoRuleId": "@@rules_nodejs+//nodejs/private:nodejs_repo_host_os_alias.bzl%nodejs_repo_host_os_alias", "attributes": { "user_node_repository_name": "nodejs" } }, "nodejs_toolchains": { - "bzlFile": "@@rules_nodejs~//nodejs/private:nodejs_toolchains_repo.bzl", - "ruleClassName": "nodejs_toolchains_repo", + "repoRuleId": "@@rules_nodejs+//nodejs/private:nodejs_toolchains_repo.bzl%nodejs_toolchains_repo", "attributes": { "user_node_repository_name": "nodejs" } }, "node20_linux_amd64": { - "bzlFile": "@@rules_nodejs~//nodejs:repositories.bzl", - "ruleClassName": "_nodejs_repositories", + "repoRuleId": "@@rules_nodejs+//nodejs:repositories.bzl%_nodejs_repositories", "attributes": { "node_download_auth": {}, "node_repositories": {}, @@ -1293,8 +1231,7 @@ } }, "node20_linux_arm64": { - "bzlFile": "@@rules_nodejs~//nodejs:repositories.bzl", - "ruleClassName": "_nodejs_repositories", + "repoRuleId": "@@rules_nodejs+//nodejs:repositories.bzl%_nodejs_repositories", "attributes": { "node_download_auth": {}, "node_repositories": {}, @@ -1307,8 +1244,7 @@ } }, "node20_linux_s390x": { - "bzlFile": "@@rules_nodejs~//nodejs:repositories.bzl", - "ruleClassName": "_nodejs_repositories", + "repoRuleId": "@@rules_nodejs+//nodejs:repositories.bzl%_nodejs_repositories", "attributes": { "node_download_auth": {}, "node_repositories": {}, @@ -1321,8 +1257,7 @@ } }, "node20_linux_ppc64le": { - "bzlFile": "@@rules_nodejs~//nodejs:repositories.bzl", - "ruleClassName": "_nodejs_repositories", + "repoRuleId": "@@rules_nodejs+//nodejs:repositories.bzl%_nodejs_repositories", "attributes": { "node_download_auth": {}, "node_repositories": {}, @@ -1335,8 +1270,7 @@ } }, "node20_darwin_amd64": { - "bzlFile": "@@rules_nodejs~//nodejs:repositories.bzl", - "ruleClassName": "_nodejs_repositories", + "repoRuleId": "@@rules_nodejs+//nodejs:repositories.bzl%_nodejs_repositories", "attributes": { "node_download_auth": {}, "node_repositories": {}, @@ -1349,8 +1283,7 @@ } }, "node20_darwin_arm64": { - "bzlFile": "@@rules_nodejs~//nodejs:repositories.bzl", - "ruleClassName": "_nodejs_repositories", + "repoRuleId": "@@rules_nodejs+//nodejs:repositories.bzl%_nodejs_repositories", "attributes": { "node_download_auth": {}, "node_repositories": {}, @@ -1363,8 +1296,7 @@ } }, "node20_windows_amd64": { - "bzlFile": "@@rules_nodejs~//nodejs:repositories.bzl", - "ruleClassName": "_nodejs_repositories", + "repoRuleId": "@@rules_nodejs+//nodejs:repositories.bzl%_nodejs_repositories", "attributes": { "node_download_auth": {}, "node_repositories": {}, @@ -1377,8 +1309,7 @@ } }, "node20_windows_arm64": { - "bzlFile": "@@rules_nodejs~//nodejs:repositories.bzl", - "ruleClassName": "_nodejs_repositories", + "repoRuleId": "@@rules_nodejs+//nodejs:repositories.bzl%_nodejs_repositories", "attributes": { "node_download_auth": {}, "node_repositories": {}, @@ -1391,29 +1322,25 @@ } }, "node20": { - "bzlFile": "@@rules_nodejs~//nodejs/private:nodejs_repo_host_os_alias.bzl", - "ruleClassName": "nodejs_repo_host_os_alias", + "repoRuleId": "@@rules_nodejs+//nodejs/private:nodejs_repo_host_os_alias.bzl%nodejs_repo_host_os_alias", "attributes": { "user_node_repository_name": "node20" } }, "node20_host": { - "bzlFile": "@@rules_nodejs~//nodejs/private:nodejs_repo_host_os_alias.bzl", - "ruleClassName": "nodejs_repo_host_os_alias", + "repoRuleId": "@@rules_nodejs+//nodejs/private:nodejs_repo_host_os_alias.bzl%nodejs_repo_host_os_alias", "attributes": { "user_node_repository_name": "node20" } }, "node20_toolchains": { - "bzlFile": "@@rules_nodejs~//nodejs/private:nodejs_toolchains_repo.bzl", - "ruleClassName": "nodejs_toolchains_repo", + "repoRuleId": "@@rules_nodejs+//nodejs/private:nodejs_toolchains_repo.bzl%nodejs_toolchains_repo", "attributes": { "user_node_repository_name": "node20" } }, "node22_linux_amd64": { - "bzlFile": "@@rules_nodejs~//nodejs:repositories.bzl", - "ruleClassName": "_nodejs_repositories", + "repoRuleId": "@@rules_nodejs+//nodejs:repositories.bzl%_nodejs_repositories", "attributes": { "node_download_auth": {}, "node_repositories": {}, @@ -1426,8 +1353,7 @@ } }, "node22_linux_arm64": { - "bzlFile": "@@rules_nodejs~//nodejs:repositories.bzl", - "ruleClassName": "_nodejs_repositories", + "repoRuleId": "@@rules_nodejs+//nodejs:repositories.bzl%_nodejs_repositories", "attributes": { "node_download_auth": {}, "node_repositories": {}, @@ -1440,8 +1366,7 @@ } }, "node22_linux_s390x": { - "bzlFile": "@@rules_nodejs~//nodejs:repositories.bzl", - "ruleClassName": "_nodejs_repositories", + "repoRuleId": "@@rules_nodejs+//nodejs:repositories.bzl%_nodejs_repositories", "attributes": { "node_download_auth": {}, "node_repositories": {}, @@ -1454,8 +1379,7 @@ } }, "node22_linux_ppc64le": { - "bzlFile": "@@rules_nodejs~//nodejs:repositories.bzl", - "ruleClassName": "_nodejs_repositories", + "repoRuleId": "@@rules_nodejs+//nodejs:repositories.bzl%_nodejs_repositories", "attributes": { "node_download_auth": {}, "node_repositories": {}, @@ -1468,8 +1392,7 @@ } }, "node22_darwin_amd64": { - "bzlFile": "@@rules_nodejs~//nodejs:repositories.bzl", - "ruleClassName": "_nodejs_repositories", + "repoRuleId": "@@rules_nodejs+//nodejs:repositories.bzl%_nodejs_repositories", "attributes": { "node_download_auth": {}, "node_repositories": {}, @@ -1482,8 +1405,7 @@ } }, "node22_darwin_arm64": { - "bzlFile": "@@rules_nodejs~//nodejs:repositories.bzl", - "ruleClassName": "_nodejs_repositories", + "repoRuleId": "@@rules_nodejs+//nodejs:repositories.bzl%_nodejs_repositories", "attributes": { "node_download_auth": {}, "node_repositories": {}, @@ -1496,8 +1418,7 @@ } }, "node22_windows_amd64": { - "bzlFile": "@@rules_nodejs~//nodejs:repositories.bzl", - "ruleClassName": "_nodejs_repositories", + "repoRuleId": "@@rules_nodejs+//nodejs:repositories.bzl%_nodejs_repositories", "attributes": { "node_download_auth": {}, "node_repositories": {}, @@ -1510,8 +1431,7 @@ } }, "node22_windows_arm64": { - "bzlFile": "@@rules_nodejs~//nodejs:repositories.bzl", - "ruleClassName": "_nodejs_repositories", + "repoRuleId": "@@rules_nodejs+//nodejs:repositories.bzl%_nodejs_repositories", "attributes": { "node_download_auth": {}, "node_repositories": {}, @@ -1524,29 +1444,25 @@ } }, "node22": { - "bzlFile": "@@rules_nodejs~//nodejs/private:nodejs_repo_host_os_alias.bzl", - "ruleClassName": "nodejs_repo_host_os_alias", + "repoRuleId": "@@rules_nodejs+//nodejs/private:nodejs_repo_host_os_alias.bzl%nodejs_repo_host_os_alias", "attributes": { "user_node_repository_name": "node22" } }, "node22_host": { - "bzlFile": "@@rules_nodejs~//nodejs/private:nodejs_repo_host_os_alias.bzl", - "ruleClassName": "nodejs_repo_host_os_alias", + "repoRuleId": "@@rules_nodejs+//nodejs/private:nodejs_repo_host_os_alias.bzl%nodejs_repo_host_os_alias", "attributes": { "user_node_repository_name": "node22" } }, "node22_toolchains": { - "bzlFile": "@@rules_nodejs~//nodejs/private:nodejs_toolchains_repo.bzl", - "ruleClassName": "nodejs_toolchains_repo", + "repoRuleId": "@@rules_nodejs+//nodejs/private:nodejs_toolchains_repo.bzl%nodejs_toolchains_repo", "attributes": { "user_node_repository_name": "node22" } }, "node24_linux_amd64": { - "bzlFile": "@@rules_nodejs~//nodejs:repositories.bzl", - "ruleClassName": "_nodejs_repositories", + "repoRuleId": "@@rules_nodejs+//nodejs:repositories.bzl%_nodejs_repositories", "attributes": { "node_download_auth": {}, "node_repositories": {}, @@ -1559,8 +1475,7 @@ } }, "node24_linux_arm64": { - "bzlFile": "@@rules_nodejs~//nodejs:repositories.bzl", - "ruleClassName": "_nodejs_repositories", + "repoRuleId": "@@rules_nodejs+//nodejs:repositories.bzl%_nodejs_repositories", "attributes": { "node_download_auth": {}, "node_repositories": {}, @@ -1573,8 +1488,7 @@ } }, "node24_linux_s390x": { - "bzlFile": "@@rules_nodejs~//nodejs:repositories.bzl", - "ruleClassName": "_nodejs_repositories", + "repoRuleId": "@@rules_nodejs+//nodejs:repositories.bzl%_nodejs_repositories", "attributes": { "node_download_auth": {}, "node_repositories": {}, @@ -1587,8 +1501,7 @@ } }, "node24_linux_ppc64le": { - "bzlFile": "@@rules_nodejs~//nodejs:repositories.bzl", - "ruleClassName": "_nodejs_repositories", + "repoRuleId": "@@rules_nodejs+//nodejs:repositories.bzl%_nodejs_repositories", "attributes": { "node_download_auth": {}, "node_repositories": {}, @@ -1601,8 +1514,7 @@ } }, "node24_darwin_amd64": { - "bzlFile": "@@rules_nodejs~//nodejs:repositories.bzl", - "ruleClassName": "_nodejs_repositories", + "repoRuleId": "@@rules_nodejs+//nodejs:repositories.bzl%_nodejs_repositories", "attributes": { "node_download_auth": {}, "node_repositories": {}, @@ -1615,8 +1527,7 @@ } }, "node24_darwin_arm64": { - "bzlFile": "@@rules_nodejs~//nodejs:repositories.bzl", - "ruleClassName": "_nodejs_repositories", + "repoRuleId": "@@rules_nodejs+//nodejs:repositories.bzl%_nodejs_repositories", "attributes": { "node_download_auth": {}, "node_repositories": {}, @@ -1629,8 +1540,7 @@ } }, "node24_windows_amd64": { - "bzlFile": "@@rules_nodejs~//nodejs:repositories.bzl", - "ruleClassName": "_nodejs_repositories", + "repoRuleId": "@@rules_nodejs+//nodejs:repositories.bzl%_nodejs_repositories", "attributes": { "node_download_auth": {}, "node_repositories": {}, @@ -1643,8 +1553,7 @@ } }, "node24_windows_arm64": { - "bzlFile": "@@rules_nodejs~//nodejs:repositories.bzl", - "ruleClassName": "_nodejs_repositories", + "repoRuleId": "@@rules_nodejs+//nodejs:repositories.bzl%_nodejs_repositories", "attributes": { "node_download_auth": {}, "node_repositories": {}, @@ -1657,22 +1566,19 @@ } }, "node24": { - "bzlFile": "@@rules_nodejs~//nodejs/private:nodejs_repo_host_os_alias.bzl", - "ruleClassName": "nodejs_repo_host_os_alias", + "repoRuleId": "@@rules_nodejs+//nodejs/private:nodejs_repo_host_os_alias.bzl%nodejs_repo_host_os_alias", "attributes": { "user_node_repository_name": "node24" } }, "node24_host": { - "bzlFile": "@@rules_nodejs~//nodejs/private:nodejs_repo_host_os_alias.bzl", - "ruleClassName": "nodejs_repo_host_os_alias", + "repoRuleId": "@@rules_nodejs+//nodejs/private:nodejs_repo_host_os_alias.bzl%nodejs_repo_host_os_alias", "attributes": { "user_node_repository_name": "node24" } }, "node24_toolchains": { - "bzlFile": "@@rules_nodejs~//nodejs/private:nodejs_toolchains_repo.bzl", - "ruleClassName": "nodejs_toolchains_repo", + "repoRuleId": "@@rules_nodejs+//nodejs/private:nodejs_toolchains_repo.bzl%nodejs_toolchains_repo", "attributes": { "user_node_repository_name": "node24" } @@ -1681,16 +1587,16 @@ "recordedRepoMappingEntries": [] } }, - "@@rules_python~//python/extensions:pip.bzl%pip": { + "@@rules_python+//python/extensions:pip.bzl%pip": { "general": { - "bzlTransitiveDigest": "VKf3JZIRvp7gyc5Q9pSqri7bmB3079s5o6Yg7IaUHZI=", - "usagesDigest": "K3E4RGDnEgGXkrLOS8/ma4NTUiLvGkMrRIhPiFxv8u0=", + "bzlTransitiveDigest": "39J6fxZx6VyebAMZs6LDsQSGw91SROMECqQx77bSJqE=", + "usagesDigest": "AK1R124YPWwAs8z1CQYyjYuci8RO5Ofot+EP5ZCNQDc=", "recordedFileInputs": { - "@@rules_python~//tools/publish/requirements_linux.txt": "8175b4c8df50ae2f22d1706961884beeb54e7da27bd2447018314a175981997d", - "@@rules_fuzzing~//fuzzing/requirements.txt": "ab04664be026b632a0d2a2446c4f65982b7654f5b6851d2f9d399a19b7242a5b", - "@@rules_python~//tools/publish/requirements_windows.txt": "7673adc71dc1a81d3661b90924d7a7c0fc998cd508b3cb4174337cef3f2de556", - "@@protobuf~//python/requirements.txt": "983be60d3cec4b319dcab6d48aeb3f5b2f7c3350f26b3a9e97486c37967c73c5", - "@@rules_python~//tools/publish/requirements_darwin.txt": "2994136eab7e57b083c3de76faf46f70fad130bc8e7360a7fed2b288b69e79dc" + "@@protobuf+//python/requirements.txt": "983be60d3cec4b319dcab6d48aeb3f5b2f7c3350f26b3a9e97486c37967c73c5", + "@@rules_fuzzing+//fuzzing/requirements.txt": "ab04664be026b632a0d2a2446c4f65982b7654f5b6851d2f9d399a19b7242a5b", + "@@rules_python+//tools/publish/requirements_darwin.txt": "2994136eab7e57b083c3de76faf46f70fad130bc8e7360a7fed2b288b69e79dc", + "@@rules_python+//tools/publish/requirements_linux.txt": "8175b4c8df50ae2f22d1706961884beeb54e7da27bd2447018314a175981997d", + "@@rules_python+//tools/publish/requirements_windows.txt": "7673adc71dc1a81d3661b90924d7a7c0fc998cd508b3cb4174337cef3f2de556" }, "recordedDirentsInputs": {}, "envVariables": { @@ -1699,238 +1605,217 @@ }, "generatedRepoSpecs": { "pip_deps_310_numpy": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@pip_deps//{name}:{target}", - "python_interpreter_target": "@@rules_python~~python~python_3_10_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_10_host//:python", "repo": "pip_deps_310", "requirement": "numpy<=1.26.1" } }, "pip_deps_310_setuptools": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@pip_deps//{name}:{target}", - "python_interpreter_target": "@@rules_python~~python~python_3_10_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_10_host//:python", "repo": "pip_deps_310", "requirement": "setuptools<=70.3.0" } }, "pip_deps_311_numpy": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@pip_deps//{name}:{target}", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "pip_deps_311", "requirement": "numpy<=1.26.1" } }, "pip_deps_311_setuptools": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@pip_deps//{name}:{target}", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "pip_deps_311", "requirement": "setuptools<=70.3.0" } }, "pip_deps_312_numpy": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@pip_deps//{name}:{target}", - "python_interpreter_target": "@@rules_python~~python~python_3_12_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_12_host//:python", "repo": "pip_deps_312", "requirement": "numpy<=1.26.1" } }, "pip_deps_312_setuptools": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@pip_deps//{name}:{target}", - "python_interpreter_target": "@@rules_python~~python~python_3_12_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_12_host//:python", "repo": "pip_deps_312", "requirement": "setuptools<=70.3.0" } }, "pip_deps_38_numpy": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@pip_deps//{name}:{target}", - "python_interpreter_target": "@@rules_python~~python~python_3_8_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_8_host//:python", "repo": "pip_deps_38", "requirement": "numpy<=1.26.1" } }, "pip_deps_38_setuptools": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@pip_deps//{name}:{target}", - "python_interpreter_target": "@@rules_python~~python~python_3_8_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_8_host//:python", "repo": "pip_deps_38", "requirement": "setuptools<=70.3.0" } }, "pip_deps_39_numpy": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@pip_deps//{name}:{target}", - "python_interpreter_target": "@@rules_python~~python~python_3_9_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_9_host//:python", "repo": "pip_deps_39", "requirement": "numpy<=1.26.1" } }, "pip_deps_39_setuptools": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@pip_deps//{name}:{target}", - "python_interpreter_target": "@@rules_python~~python~python_3_9_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_9_host//:python", "repo": "pip_deps_39", "requirement": "setuptools<=70.3.0" } }, "rules_fuzzing_py_deps_310_absl_py": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_fuzzing_py_deps//{name}:{target}", "extra_pip_args": [ "--require-hashes" ], - "python_interpreter_target": "@@rules_python~~python~python_3_10_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_10_host//:python", "repo": "rules_fuzzing_py_deps_310", "requirement": "absl-py==2.0.0 --hash=sha256:9a28abb62774ae4e8edbe2dd4c49ffcd45a6a848952a5eccc6a49f3f0fc1e2f3" } }, "rules_fuzzing_py_deps_310_six": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_fuzzing_py_deps//{name}:{target}", "extra_pip_args": [ "--require-hashes" ], - "python_interpreter_target": "@@rules_python~~python~python_3_10_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_10_host//:python", "repo": "rules_fuzzing_py_deps_310", "requirement": "six==1.16.0 --hash=sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254" } }, "rules_fuzzing_py_deps_311_absl_py": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_fuzzing_py_deps//{name}:{target}", "extra_pip_args": [ "--require-hashes" ], - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_fuzzing_py_deps_311", "requirement": "absl-py==2.0.0 --hash=sha256:9a28abb62774ae4e8edbe2dd4c49ffcd45a6a848952a5eccc6a49f3f0fc1e2f3" } }, "rules_fuzzing_py_deps_311_six": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_fuzzing_py_deps//{name}:{target}", "extra_pip_args": [ "--require-hashes" ], - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_fuzzing_py_deps_311", "requirement": "six==1.16.0 --hash=sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254" } }, "rules_fuzzing_py_deps_312_absl_py": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_fuzzing_py_deps//{name}:{target}", "extra_pip_args": [ "--require-hashes" ], - "python_interpreter_target": "@@rules_python~~python~python_3_12_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_12_host//:python", "repo": "rules_fuzzing_py_deps_312", "requirement": "absl-py==2.0.0 --hash=sha256:9a28abb62774ae4e8edbe2dd4c49ffcd45a6a848952a5eccc6a49f3f0fc1e2f3" } }, "rules_fuzzing_py_deps_312_six": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_fuzzing_py_deps//{name}:{target}", "extra_pip_args": [ "--require-hashes" ], - "python_interpreter_target": "@@rules_python~~python~python_3_12_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_12_host//:python", "repo": "rules_fuzzing_py_deps_312", "requirement": "six==1.16.0 --hash=sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254" } }, "rules_fuzzing_py_deps_38_absl_py": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_fuzzing_py_deps//{name}:{target}", "extra_pip_args": [ "--require-hashes" ], - "python_interpreter_target": "@@rules_python~~python~python_3_8_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_8_host//:python", "repo": "rules_fuzzing_py_deps_38", "requirement": "absl-py==2.0.0 --hash=sha256:9a28abb62774ae4e8edbe2dd4c49ffcd45a6a848952a5eccc6a49f3f0fc1e2f3" } }, "rules_fuzzing_py_deps_38_six": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_fuzzing_py_deps//{name}:{target}", "extra_pip_args": [ "--require-hashes" ], - "python_interpreter_target": "@@rules_python~~python~python_3_8_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_8_host//:python", "repo": "rules_fuzzing_py_deps_38", "requirement": "six==1.16.0 --hash=sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254" } }, "rules_fuzzing_py_deps_39_absl_py": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_fuzzing_py_deps//{name}:{target}", "extra_pip_args": [ "--require-hashes" ], - "python_interpreter_target": "@@rules_python~~python~python_3_9_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_9_host//:python", "repo": "rules_fuzzing_py_deps_39", "requirement": "absl-py==2.0.0 --hash=sha256:9a28abb62774ae4e8edbe2dd4c49ffcd45a6a848952a5eccc6a49f3f0fc1e2f3" } }, "rules_fuzzing_py_deps_39_six": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_fuzzing_py_deps//{name}:{target}", "extra_pip_args": [ "--require-hashes" ], - "python_interpreter_target": "@@rules_python~~python~python_3_9_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_9_host//:python", "repo": "rules_fuzzing_py_deps_39", "requirement": "six==1.16.0 --hash=sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254" } }, "rules_python_publish_deps_311_backports_tarfile_py3_none_any_77e284d7": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -1944,7 +1829,7 @@ "cp311_windows_x86_64" ], "filename": "backports.tarfile-1.2.0-py3-none-any.whl", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "backports-tarfile==1.2.0", "sha256": "77e284d754527b01fb1e6fa8a1afe577858ebe4e9dad8919e34c862cb399bc34", @@ -1954,8 +1839,7 @@ } }, "rules_python_publish_deps_311_backports_tarfile_sdist_d75e02c2": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -1973,7 +1857,7 @@ "https://pypi.org/simple" ], "filename": "backports_tarfile-1.2.0.tar.gz", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "backports-tarfile==1.2.0", "sha256": "d75e02c268746e1b8144c278978b6e98e85de6ad16f8e4b0844a154557eca991", @@ -1983,8 +1867,7 @@ } }, "rules_python_publish_deps_311_certifi_py3_none_any_922820b5": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -1998,7 +1881,7 @@ "cp311_windows_x86_64" ], "filename": "certifi-2024.8.30-py3-none-any.whl", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "certifi==2024.8.30", "sha256": "922820b53db7a7257ffbda3f597266d435245903d80737e34f8a45ff3e3230d8", @@ -2008,8 +1891,7 @@ } }, "rules_python_publish_deps_311_certifi_sdist_bec941d2": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -2027,7 +1909,7 @@ "https://pypi.org/simple" ], "filename": "certifi-2024.8.30.tar.gz", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "certifi==2024.8.30", "sha256": "bec941d2aa8195e248a60b31ff9f0558284cf01a52591ceda73ea9afffd69fd9", @@ -2037,8 +1919,7 @@ } }, "rules_python_publish_deps_311_cffi_cp311_cp311_manylinux_2_17_aarch64_a1ed2dd2": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -2049,7 +1930,7 @@ "cp311_linux_x86_64" ], "filename": "cffi-1.17.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "cffi==1.17.1", "sha256": "a1ed2dd2972641495a3ec98445e09766f077aee98a1c896dcb4ad0d303628e41", @@ -2059,8 +1940,7 @@ } }, "rules_python_publish_deps_311_cffi_cp311_cp311_manylinux_2_17_ppc64le_46bf4316": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -2071,7 +1951,7 @@ "cp311_linux_x86_64" ], "filename": "cffi-1.17.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "cffi==1.17.1", "sha256": "46bf43160c1a35f7ec506d254e5c890f3c03648a4dbac12d624e4490a7046cd1", @@ -2081,8 +1961,7 @@ } }, "rules_python_publish_deps_311_cffi_cp311_cp311_manylinux_2_17_s390x_a24ed04c": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -2093,7 +1972,7 @@ "cp311_linux_x86_64" ], "filename": "cffi-1.17.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "cffi==1.17.1", "sha256": "a24ed04c8ffd54b0729c07cee15a81d964e6fee0e3d4d342a27b020d22959dc6", @@ -2103,8 +1982,7 @@ } }, "rules_python_publish_deps_311_cffi_cp311_cp311_manylinux_2_17_x86_64_610faea7": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -2115,7 +1993,7 @@ "cp311_linux_x86_64" ], "filename": "cffi-1.17.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "cffi==1.17.1", "sha256": "610faea79c43e44c71e1ec53a554553fa22321b65fae24889706c0a84d4ad86d", @@ -2125,8 +2003,7 @@ } }, "rules_python_publish_deps_311_cffi_cp311_cp311_musllinux_1_1_aarch64_a9b15d49": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -2137,7 +2014,7 @@ "cp311_linux_x86_64" ], "filename": "cffi-1.17.1-cp311-cp311-musllinux_1_1_aarch64.whl", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "cffi==1.17.1", "sha256": "a9b15d491f3ad5d692e11f6b71f7857e7835eb677955c00cc0aefcd0669adaf6", @@ -2147,8 +2024,7 @@ } }, "rules_python_publish_deps_311_cffi_cp311_cp311_musllinux_1_1_x86_64_fc48c783": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -2159,7 +2035,7 @@ "cp311_linux_x86_64" ], "filename": "cffi-1.17.1-cp311-cp311-musllinux_1_1_x86_64.whl", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "cffi==1.17.1", "sha256": "fc48c783f9c87e60831201f2cce7f3b2e4846bf4d8728eabe54d60700b318a0b", @@ -2169,8 +2045,7 @@ } }, "rules_python_publish_deps_311_cffi_sdist_1c39c601": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -2185,7 +2060,7 @@ "https://pypi.org/simple" ], "filename": "cffi-1.17.1.tar.gz", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "cffi==1.17.1", "sha256": "1c39c6016c32bc48dd54561950ebd6836e1670f2ae46128f67cf49e789c52824", @@ -2195,8 +2070,7 @@ } }, "rules_python_publish_deps_311_charset_normalizer_cp311_cp311_macosx_10_9_universal2_0d99dd8f": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -2210,7 +2084,7 @@ "cp311_windows_x86_64" ], "filename": "charset_normalizer-3.4.0-cp311-cp311-macosx_10_9_universal2.whl", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "charset-normalizer==3.4.0", "sha256": "0d99dd8ff461990f12d6e42c7347fd9ab2532fb70e9621ba520f9e8637161d7c", @@ -2220,8 +2094,7 @@ } }, "rules_python_publish_deps_311_charset_normalizer_cp311_cp311_macosx_10_9_x86_64_c57516e5": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -2235,7 +2108,7 @@ "cp311_windows_x86_64" ], "filename": "charset_normalizer-3.4.0-cp311-cp311-macosx_10_9_x86_64.whl", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "charset-normalizer==3.4.0", "sha256": "c57516e58fd17d03ebe67e181a4e4e2ccab1168f8c2976c6a334d4f819fe5944", @@ -2245,8 +2118,7 @@ } }, "rules_python_publish_deps_311_charset_normalizer_cp311_cp311_macosx_11_0_arm64_6dba5d19": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -2260,7 +2132,7 @@ "cp311_windows_x86_64" ], "filename": "charset_normalizer-3.4.0-cp311-cp311-macosx_11_0_arm64.whl", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "charset-normalizer==3.4.0", "sha256": "6dba5d19c4dfab08e58d5b36304b3f92f3bd5d42c1a3fa37b5ba5cdf6dfcbcee", @@ -2270,8 +2142,7 @@ } }, "rules_python_publish_deps_311_charset_normalizer_cp311_cp311_manylinux_2_17_aarch64_bf4475b8": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -2285,7 +2156,7 @@ "cp311_windows_x86_64" ], "filename": "charset_normalizer-3.4.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "charset-normalizer==3.4.0", "sha256": "bf4475b82be41b07cc5e5ff94810e6a01f276e37c2d55571e3fe175e467a1a1c", @@ -2295,8 +2166,7 @@ } }, "rules_python_publish_deps_311_charset_normalizer_cp311_cp311_manylinux_2_17_ppc64le_ce031db0": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -2310,7 +2180,7 @@ "cp311_windows_x86_64" ], "filename": "charset_normalizer-3.4.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "charset-normalizer==3.4.0", "sha256": "ce031db0408e487fd2775d745ce30a7cd2923667cf3b69d48d219f1d8f5ddeb6", @@ -2320,8 +2190,7 @@ } }, "rules_python_publish_deps_311_charset_normalizer_cp311_cp311_manylinux_2_17_s390x_8ff4e7cd": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -2335,7 +2204,7 @@ "cp311_windows_x86_64" ], "filename": "charset_normalizer-3.4.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "charset-normalizer==3.4.0", "sha256": "8ff4e7cdfdb1ab5698e675ca622e72d58a6fa2a8aa58195de0c0061288e6e3ea", @@ -2345,8 +2214,7 @@ } }, "rules_python_publish_deps_311_charset_normalizer_cp311_cp311_manylinux_2_17_x86_64_3710a975": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -2360,7 +2228,7 @@ "cp311_windows_x86_64" ], "filename": "charset_normalizer-3.4.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "charset-normalizer==3.4.0", "sha256": "3710a9751938947e6327ea9f3ea6332a09bf0ba0c09cae9cb1f250bd1f1549bc", @@ -2370,8 +2238,7 @@ } }, "rules_python_publish_deps_311_charset_normalizer_cp311_cp311_musllinux_1_2_aarch64_47334db7": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -2385,7 +2252,7 @@ "cp311_windows_x86_64" ], "filename": "charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_aarch64.whl", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "charset-normalizer==3.4.0", "sha256": "47334db71978b23ebcf3c0f9f5ee98b8d65992b65c9c4f2d34c2eaf5bcaf0594", @@ -2395,8 +2262,7 @@ } }, "rules_python_publish_deps_311_charset_normalizer_cp311_cp311_musllinux_1_2_ppc64le_f1a2f519": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -2410,7 +2276,7 @@ "cp311_windows_x86_64" ], "filename": "charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_ppc64le.whl", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "charset-normalizer==3.4.0", "sha256": "f1a2f519ae173b5b6a2c9d5fa3116ce16e48b3462c8b96dfdded11055e3d6365", @@ -2420,8 +2286,7 @@ } }, "rules_python_publish_deps_311_charset_normalizer_cp311_cp311_musllinux_1_2_s390x_63bc5c4a": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -2435,7 +2300,7 @@ "cp311_windows_x86_64" ], "filename": "charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_s390x.whl", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "charset-normalizer==3.4.0", "sha256": "63bc5c4ae26e4bc6be6469943b8253c0fd4e4186c43ad46e713ea61a0ba49129", @@ -2445,8 +2310,7 @@ } }, "rules_python_publish_deps_311_charset_normalizer_cp311_cp311_musllinux_1_2_x86_64_bcb4f8ea": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -2460,7 +2324,7 @@ "cp311_windows_x86_64" ], "filename": "charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_x86_64.whl", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "charset-normalizer==3.4.0", "sha256": "bcb4f8ea87d03bc51ad04add8ceaf9b0f085ac045ab4d74e73bbc2dc033f0236", @@ -2470,8 +2334,7 @@ } }, "rules_python_publish_deps_311_charset_normalizer_cp311_cp311_win_amd64_cee4373f": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -2485,7 +2348,7 @@ "cp311_windows_x86_64" ], "filename": "charset_normalizer-3.4.0-cp311-cp311-win_amd64.whl", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "charset-normalizer==3.4.0", "sha256": "cee4373f4d3ad28f1ab6290684d8e2ebdb9e7a1b74fdc39e4c211995f77bec27", @@ -2495,8 +2358,7 @@ } }, "rules_python_publish_deps_311_charset_normalizer_py3_none_any_fe9f97fe": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -2510,7 +2372,7 @@ "cp311_windows_x86_64" ], "filename": "charset_normalizer-3.4.0-py3-none-any.whl", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "charset-normalizer==3.4.0", "sha256": "fe9f97feb71aa9896b81973a7bbada8c49501dc73e58a10fcef6663af95e5079", @@ -2520,8 +2382,7 @@ } }, "rules_python_publish_deps_311_charset_normalizer_sdist_223217c3": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -2539,7 +2400,7 @@ "https://pypi.org/simple" ], "filename": "charset_normalizer-3.4.0.tar.gz", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "charset-normalizer==3.4.0", "sha256": "223217c3d4f82c3ac5e29032b3f1c2eb0fb591b72161f86d93f5719079dae93e", @@ -2549,8 +2410,7 @@ } }, "rules_python_publish_deps_311_cryptography_cp39_abi3_manylinux_2_17_aarch64_846da004": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -2561,7 +2421,7 @@ "cp311_linux_x86_64" ], "filename": "cryptography-43.0.3-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "cryptography==43.0.3", "sha256": "846da004a5804145a5f441b8530b4bf35afbf7da70f82409f151695b127213d5", @@ -2571,8 +2431,7 @@ } }, "rules_python_publish_deps_311_cryptography_cp39_abi3_manylinux_2_17_x86_64_0f996e72": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -2583,7 +2442,7 @@ "cp311_linux_x86_64" ], "filename": "cryptography-43.0.3-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "cryptography==43.0.3", "sha256": "0f996e7268af62598f2fc1204afa98a3b5712313a55c4c9d434aef49cadc91d4", @@ -2593,8 +2452,7 @@ } }, "rules_python_publish_deps_311_cryptography_cp39_abi3_manylinux_2_28_aarch64_f7b178f1": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -2605,7 +2463,7 @@ "cp311_linux_x86_64" ], "filename": "cryptography-43.0.3-cp39-abi3-manylinux_2_28_aarch64.whl", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "cryptography==43.0.3", "sha256": "f7b178f11ed3664fd0e995a47ed2b5ff0a12d893e41dd0494f406d1cf555cab7", @@ -2615,8 +2473,7 @@ } }, "rules_python_publish_deps_311_cryptography_cp39_abi3_manylinux_2_28_x86_64_c2e6fc39": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -2627,7 +2484,7 @@ "cp311_linux_x86_64" ], "filename": "cryptography-43.0.3-cp39-abi3-manylinux_2_28_x86_64.whl", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "cryptography==43.0.3", "sha256": "c2e6fc39c4ab499049df3bdf567f768a723a5e8464816e8f009f121a5a9f4405", @@ -2637,8 +2494,7 @@ } }, "rules_python_publish_deps_311_cryptography_cp39_abi3_musllinux_1_2_aarch64_e1be4655": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -2649,7 +2505,7 @@ "cp311_linux_x86_64" ], "filename": "cryptography-43.0.3-cp39-abi3-musllinux_1_2_aarch64.whl", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "cryptography==43.0.3", "sha256": "e1be4655c7ef6e1bbe6b5d0403526601323420bcf414598955968c9ef3eb7d16", @@ -2659,8 +2515,7 @@ } }, "rules_python_publish_deps_311_cryptography_cp39_abi3_musllinux_1_2_x86_64_df6b6c6d": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -2671,7 +2526,7 @@ "cp311_linux_x86_64" ], "filename": "cryptography-43.0.3-cp39-abi3-musllinux_1_2_x86_64.whl", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "cryptography==43.0.3", "sha256": "df6b6c6d742395dd77a23ea3728ab62f98379eff8fb61be2744d4679ab678f73", @@ -2681,8 +2536,7 @@ } }, "rules_python_publish_deps_311_cryptography_sdist_315b9001": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -2697,7 +2551,7 @@ "https://pypi.org/simple" ], "filename": "cryptography-43.0.3.tar.gz", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "cryptography==43.0.3", "sha256": "315b9001266a492a6ff443b61238f956b214dbec9910a081ba5b6646a055a805", @@ -2707,8 +2561,7 @@ } }, "rules_python_publish_deps_311_docutils_py3_none_any_dafca5b9": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -2722,7 +2575,7 @@ "cp311_windows_x86_64" ], "filename": "docutils-0.21.2-py3-none-any.whl", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "docutils==0.21.2", "sha256": "dafca5b9e384f0e419294eb4d2ff9fa826435bf15f15b7bd45723e8ad76811b2", @@ -2732,8 +2585,7 @@ } }, "rules_python_publish_deps_311_docutils_sdist_3a6b1873": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -2751,7 +2603,7 @@ "https://pypi.org/simple" ], "filename": "docutils-0.21.2.tar.gz", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "docutils==0.21.2", "sha256": "3a6b18732edf182daa3cd12775bbb338cf5691468f91eeeb109deff6ebfa986f", @@ -2761,8 +2613,7 @@ } }, "rules_python_publish_deps_311_idna_py3_none_any_946d195a": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -2776,7 +2627,7 @@ "cp311_windows_x86_64" ], "filename": "idna-3.10-py3-none-any.whl", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "idna==3.10", "sha256": "946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", @@ -2786,8 +2637,7 @@ } }, "rules_python_publish_deps_311_idna_sdist_12f65c9b": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -2805,7 +2655,7 @@ "https://pypi.org/simple" ], "filename": "idna-3.10.tar.gz", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "idna==3.10", "sha256": "12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", @@ -2815,8 +2665,7 @@ } }, "rules_python_publish_deps_311_importlib_metadata_py3_none_any_45e54197": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -2830,7 +2679,7 @@ "cp311_windows_x86_64" ], "filename": "importlib_metadata-8.5.0-py3-none-any.whl", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "importlib-metadata==8.5.0", "sha256": "45e54197d28b7a7f1559e60b95e7c567032b602131fbd588f1497f47880aa68b", @@ -2840,8 +2689,7 @@ } }, "rules_python_publish_deps_311_importlib_metadata_sdist_71522656": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -2859,7 +2707,7 @@ "https://pypi.org/simple" ], "filename": "importlib_metadata-8.5.0.tar.gz", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "importlib-metadata==8.5.0", "sha256": "71522656f0abace1d072b9e5481a48f07c138e00f079c38c8f883823f9c26bd7", @@ -2869,8 +2717,7 @@ } }, "rules_python_publish_deps_311_jaraco_classes_py3_none_any_f662826b": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -2884,7 +2731,7 @@ "cp311_windows_x86_64" ], "filename": "jaraco.classes-3.4.0-py3-none-any.whl", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "jaraco-classes==3.4.0", "sha256": "f662826b6bed8cace05e7ff873ce0f9283b5c924470fe664fff1c2f00f581790", @@ -2894,8 +2741,7 @@ } }, "rules_python_publish_deps_311_jaraco_classes_sdist_47a024b5": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -2913,7 +2759,7 @@ "https://pypi.org/simple" ], "filename": "jaraco.classes-3.4.0.tar.gz", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "jaraco-classes==3.4.0", "sha256": "47a024b51d0239c0dd8c8540c6c7f484be3b8fcf0b2d85c13825780d3b3f3acd", @@ -2923,8 +2769,7 @@ } }, "rules_python_publish_deps_311_jaraco_context_py3_none_any_f797fc48": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -2938,7 +2783,7 @@ "cp311_windows_x86_64" ], "filename": "jaraco.context-6.0.1-py3-none-any.whl", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "jaraco-context==6.0.1", "sha256": "f797fc481b490edb305122c9181830a3a5b76d84ef6d1aef2fb9b47ab956f9e4", @@ -2948,8 +2793,7 @@ } }, "rules_python_publish_deps_311_jaraco_context_sdist_9bae4ea5": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -2967,7 +2811,7 @@ "https://pypi.org/simple" ], "filename": "jaraco_context-6.0.1.tar.gz", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "jaraco-context==6.0.1", "sha256": "9bae4ea555cf0b14938dc0aee7c9f32ed303aa20a3b73e7dc80111628792d1b3", @@ -2977,8 +2821,7 @@ } }, "rules_python_publish_deps_311_jaraco_functools_py3_none_any_ad159f13": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -2992,7 +2835,7 @@ "cp311_windows_x86_64" ], "filename": "jaraco.functools-4.1.0-py3-none-any.whl", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "jaraco-functools==4.1.0", "sha256": "ad159f13428bc4acbf5541ad6dec511f91573b90fba04df61dafa2a1231cf649", @@ -3002,8 +2845,7 @@ } }, "rules_python_publish_deps_311_jaraco_functools_sdist_70f7e0e2": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -3021,7 +2863,7 @@ "https://pypi.org/simple" ], "filename": "jaraco_functools-4.1.0.tar.gz", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "jaraco-functools==4.1.0", "sha256": "70f7e0e2ae076498e212562325e805204fc092d7b4c17e0e86c959e249701a9d", @@ -3031,8 +2873,7 @@ } }, "rules_python_publish_deps_311_jeepney_py3_none_any_c0a454ad": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -3043,7 +2884,7 @@ "cp311_linux_x86_64" ], "filename": "jeepney-0.8.0-py3-none-any.whl", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "jeepney==0.8.0", "sha256": "c0a454ad016ca575060802ee4d590dd912e35c122fa04e70306de3d076cce755", @@ -3053,8 +2894,7 @@ } }, "rules_python_publish_deps_311_jeepney_sdist_5efe48d2": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -3069,7 +2909,7 @@ "https://pypi.org/simple" ], "filename": "jeepney-0.8.0.tar.gz", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "jeepney==0.8.0", "sha256": "5efe48d255973902f6badc3ce55e2aa6c5c3b3bc642059ef3a91247bcfcc5806", @@ -3079,8 +2919,7 @@ } }, "rules_python_publish_deps_311_keyring_py3_none_any_5426f817": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -3094,7 +2933,7 @@ "cp311_windows_x86_64" ], "filename": "keyring-25.4.1-py3-none-any.whl", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "keyring==25.4.1", "sha256": "5426f817cf7f6f007ba5ec722b1bcad95a75b27d780343772ad76b17cb47b0bf", @@ -3104,8 +2943,7 @@ } }, "rules_python_publish_deps_311_keyring_sdist_b07ebc55": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -3123,7 +2961,7 @@ "https://pypi.org/simple" ], "filename": "keyring-25.4.1.tar.gz", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "keyring==25.4.1", "sha256": "b07ebc55f3e8ed86ac81dd31ef14e81ace9dd9c3d4b5d77a6e9a2016d0d71a1b", @@ -3133,8 +2971,7 @@ } }, "rules_python_publish_deps_311_markdown_it_py_py3_none_any_35521684": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -3148,7 +2985,7 @@ "cp311_windows_x86_64" ], "filename": "markdown_it_py-3.0.0-py3-none-any.whl", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "markdown-it-py==3.0.0", "sha256": "355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1", @@ -3158,8 +2995,7 @@ } }, "rules_python_publish_deps_311_markdown_it_py_sdist_e3f60a94": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -3177,7 +3013,7 @@ "https://pypi.org/simple" ], "filename": "markdown-it-py-3.0.0.tar.gz", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "markdown-it-py==3.0.0", "sha256": "e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb", @@ -3187,8 +3023,7 @@ } }, "rules_python_publish_deps_311_mdurl_py3_none_any_84008a41": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -3202,7 +3037,7 @@ "cp311_windows_x86_64" ], "filename": "mdurl-0.1.2-py3-none-any.whl", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "mdurl==0.1.2", "sha256": "84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", @@ -3212,8 +3047,7 @@ } }, "rules_python_publish_deps_311_mdurl_sdist_bb413d29": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -3231,7 +3065,7 @@ "https://pypi.org/simple" ], "filename": "mdurl-0.1.2.tar.gz", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "mdurl==0.1.2", "sha256": "bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba", @@ -3241,8 +3075,7 @@ } }, "rules_python_publish_deps_311_more_itertools_py3_none_any_037b0d32": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -3256,7 +3089,7 @@ "cp311_windows_x86_64" ], "filename": "more_itertools-10.5.0-py3-none-any.whl", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "more-itertools==10.5.0", "sha256": "037b0d3203ce90cca8ab1defbbdac29d5f993fc20131f3664dc8d6acfa872aef", @@ -3266,8 +3099,7 @@ } }, "rules_python_publish_deps_311_more_itertools_sdist_5482bfef": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -3285,7 +3117,7 @@ "https://pypi.org/simple" ], "filename": "more-itertools-10.5.0.tar.gz", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "more-itertools==10.5.0", "sha256": "5482bfef7849c25dc3c6dd53a6173ae4795da2a41a80faea6700d9f5846c5da6", @@ -3295,8 +3127,7 @@ } }, "rules_python_publish_deps_311_nh3_cp37_abi3_macosx_10_12_x86_64_14c5a72e": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -3310,7 +3141,7 @@ "cp311_windows_x86_64" ], "filename": "nh3-0.2.18-cp37-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "nh3==0.2.18", "sha256": "14c5a72e9fe82aea5fe3072116ad4661af5cf8e8ff8fc5ad3450f123e4925e86", @@ -3320,8 +3151,7 @@ } }, "rules_python_publish_deps_311_nh3_cp37_abi3_macosx_10_12_x86_64_7b7c2a3c": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -3335,7 +3165,7 @@ "cp311_windows_x86_64" ], "filename": "nh3-0.2.18-cp37-abi3-macosx_10_12_x86_64.whl", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "nh3==0.2.18", "sha256": "7b7c2a3c9eb1a827d42539aa64091640bd275b81e097cd1d8d82ef91ffa2e811", @@ -3345,8 +3175,7 @@ } }, "rules_python_publish_deps_311_nh3_cp37_abi3_manylinux_2_17_aarch64_42c64511": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -3360,7 +3189,7 @@ "cp311_windows_x86_64" ], "filename": "nh3-0.2.18-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "nh3==0.2.18", "sha256": "42c64511469005058cd17cc1537578eac40ae9f7200bedcfd1fc1a05f4f8c200", @@ -3370,8 +3199,7 @@ } }, "rules_python_publish_deps_311_nh3_cp37_abi3_manylinux_2_17_armv7l_0411beb0": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -3385,7 +3213,7 @@ "cp311_windows_x86_64" ], "filename": "nh3-0.2.18-cp37-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "nh3==0.2.18", "sha256": "0411beb0589eacb6734f28d5497ca2ed379eafab8ad8c84b31bb5c34072b7164", @@ -3395,8 +3223,7 @@ } }, "rules_python_publish_deps_311_nh3_cp37_abi3_manylinux_2_17_ppc64_5f36b271": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -3410,7 +3237,7 @@ "cp311_windows_x86_64" ], "filename": "nh3-0.2.18-cp37-abi3-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "nh3==0.2.18", "sha256": "5f36b271dae35c465ef5e9090e1fdaba4a60a56f0bb0ba03e0932a66f28b9189", @@ -3420,8 +3247,7 @@ } }, "rules_python_publish_deps_311_nh3_cp37_abi3_manylinux_2_17_ppc64le_34c03fa7": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -3435,7 +3261,7 @@ "cp311_windows_x86_64" ], "filename": "nh3-0.2.18-cp37-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "nh3==0.2.18", "sha256": "34c03fa78e328c691f982b7c03d4423bdfd7da69cd707fe572f544cf74ac23ad", @@ -3445,8 +3271,7 @@ } }, "rules_python_publish_deps_311_nh3_cp37_abi3_manylinux_2_17_s390x_19aaba96": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -3460,7 +3285,7 @@ "cp311_windows_x86_64" ], "filename": "nh3-0.2.18-cp37-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "nh3==0.2.18", "sha256": "19aaba96e0f795bd0a6c56291495ff59364f4300d4a39b29a0abc9cb3774a84b", @@ -3470,8 +3295,7 @@ } }, "rules_python_publish_deps_311_nh3_cp37_abi3_manylinux_2_17_x86_64_de3ceed6": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -3485,7 +3309,7 @@ "cp311_windows_x86_64" ], "filename": "nh3-0.2.18-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "nh3==0.2.18", "sha256": "de3ceed6e661954871d6cd78b410213bdcb136f79aafe22aa7182e028b8c7307", @@ -3495,8 +3319,7 @@ } }, "rules_python_publish_deps_311_nh3_cp37_abi3_musllinux_1_2_aarch64_f0eca9ca": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -3510,7 +3333,7 @@ "cp311_windows_x86_64" ], "filename": "nh3-0.2.18-cp37-abi3-musllinux_1_2_aarch64.whl", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "nh3==0.2.18", "sha256": "f0eca9ca8628dbb4e916ae2491d72957fdd35f7a5d326b7032a345f111ac07fe", @@ -3520,8 +3343,7 @@ } }, "rules_python_publish_deps_311_nh3_cp37_abi3_musllinux_1_2_armv7l_3a157ab1": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -3535,7 +3357,7 @@ "cp311_windows_x86_64" ], "filename": "nh3-0.2.18-cp37-abi3-musllinux_1_2_armv7l.whl", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "nh3==0.2.18", "sha256": "3a157ab149e591bb638a55c8c6bcb8cdb559c8b12c13a8affaba6cedfe51713a", @@ -3545,8 +3367,7 @@ } }, "rules_python_publish_deps_311_nh3_cp37_abi3_musllinux_1_2_x86_64_36c95d4b": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -3560,7 +3381,7 @@ "cp311_windows_x86_64" ], "filename": "nh3-0.2.18-cp37-abi3-musllinux_1_2_x86_64.whl", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "nh3==0.2.18", "sha256": "36c95d4b70530b320b365659bb5034341316e6a9b30f0b25fa9c9eff4c27a204", @@ -3570,8 +3391,7 @@ } }, "rules_python_publish_deps_311_nh3_cp37_abi3_win_amd64_8ce0f819": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -3585,7 +3405,7 @@ "cp311_windows_x86_64" ], "filename": "nh3-0.2.18-cp37-abi3-win_amd64.whl", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "nh3==0.2.18", "sha256": "8ce0f819d2f1933953fca255db2471ad58184a60508f03e6285e5114b6254844", @@ -3595,8 +3415,7 @@ } }, "rules_python_publish_deps_311_nh3_sdist_94a16692": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -3614,7 +3433,7 @@ "https://pypi.org/simple" ], "filename": "nh3-0.2.18.tar.gz", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "nh3==0.2.18", "sha256": "94a166927e53972a9698af9542ace4e38b9de50c34352b962f4d9a7d4c927af4", @@ -3624,8 +3443,7 @@ } }, "rules_python_publish_deps_311_pkginfo_py3_none_any_889a6da2": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -3639,7 +3457,7 @@ "cp311_windows_x86_64" ], "filename": "pkginfo-1.10.0-py3-none-any.whl", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "pkginfo==1.10.0", "sha256": "889a6da2ed7ffc58ab5b900d888ddce90bce912f2d2de1dc1c26f4cb9fe65097", @@ -3649,8 +3467,7 @@ } }, "rules_python_publish_deps_311_pkginfo_sdist_5df73835": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -3668,7 +3485,7 @@ "https://pypi.org/simple" ], "filename": "pkginfo-1.10.0.tar.gz", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "pkginfo==1.10.0", "sha256": "5df73835398d10db79f8eecd5cd86b1f6d29317589ea70796994d49399af6297", @@ -3678,8 +3495,7 @@ } }, "rules_python_publish_deps_311_pycparser_py3_none_any_c3702b6d": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -3690,7 +3506,7 @@ "cp311_linux_x86_64" ], "filename": "pycparser-2.22-py3-none-any.whl", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "pycparser==2.22", "sha256": "c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc", @@ -3700,8 +3516,7 @@ } }, "rules_python_publish_deps_311_pycparser_sdist_491c8be9": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -3716,7 +3531,7 @@ "https://pypi.org/simple" ], "filename": "pycparser-2.22.tar.gz", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "pycparser==2.22", "sha256": "491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6", @@ -3726,8 +3541,7 @@ } }, "rules_python_publish_deps_311_pygments_py3_none_any_b8e6aca0": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -3741,7 +3555,7 @@ "cp311_windows_x86_64" ], "filename": "pygments-2.18.0-py3-none-any.whl", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "pygments==2.18.0", "sha256": "b8e6aca0523f3ab76fee51799c488e38782ac06eafcf95e7ba832985c8e7b13a", @@ -3751,8 +3565,7 @@ } }, "rules_python_publish_deps_311_pygments_sdist_786ff802": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -3770,7 +3583,7 @@ "https://pypi.org/simple" ], "filename": "pygments-2.18.0.tar.gz", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "pygments==2.18.0", "sha256": "786ff802f32e91311bff3889f6e9a86e81505fe99f2735bb6d60ae0c5004f199", @@ -3780,15 +3593,14 @@ } }, "rules_python_publish_deps_311_pywin32_ctypes_py3_none_any_8a151337": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ "cp311_windows_x86_64" ], "filename": "pywin32_ctypes-0.2.3-py3-none-any.whl", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "pywin32-ctypes==0.2.3", "sha256": "8a1513379d709975552d202d942d9837758905c8d01eb82b8bcc30918929e7b8", @@ -3798,8 +3610,7 @@ } }, "rules_python_publish_deps_311_pywin32_ctypes_sdist_d162dc04": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -3810,7 +3621,7 @@ "https://pypi.org/simple" ], "filename": "pywin32-ctypes-0.2.3.tar.gz", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "pywin32-ctypes==0.2.3", "sha256": "d162dc04946d704503b2edc4d55f3dba5c1d539ead017afa00142c38b9885755", @@ -3820,8 +3631,7 @@ } }, "rules_python_publish_deps_311_readme_renderer_py3_none_any_2fbca89b": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -3835,7 +3645,7 @@ "cp311_windows_x86_64" ], "filename": "readme_renderer-44.0-py3-none-any.whl", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "readme-renderer==44.0", "sha256": "2fbca89b81a08526aadf1357a8c2ae889ec05fb03f5da67f9769c9a592166151", @@ -3845,8 +3655,7 @@ } }, "rules_python_publish_deps_311_readme_renderer_sdist_8712034e": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -3864,7 +3673,7 @@ "https://pypi.org/simple" ], "filename": "readme_renderer-44.0.tar.gz", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "readme-renderer==44.0", "sha256": "8712034eabbfa6805cacf1402b4eeb2a73028f72d1166d6f5cb7f9c047c5d1e1", @@ -3874,8 +3683,7 @@ } }, "rules_python_publish_deps_311_requests_py3_none_any_70761cfe": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -3889,7 +3697,7 @@ "cp311_windows_x86_64" ], "filename": "requests-2.32.3-py3-none-any.whl", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "requests==2.32.3", "sha256": "70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6", @@ -3899,8 +3707,7 @@ } }, "rules_python_publish_deps_311_requests_sdist_55365417": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -3918,7 +3725,7 @@ "https://pypi.org/simple" ], "filename": "requests-2.32.3.tar.gz", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "requests==2.32.3", "sha256": "55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760", @@ -3928,8 +3735,7 @@ } }, "rules_python_publish_deps_311_requests_toolbelt_py2_none_any_cccfdd66": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -3943,7 +3749,7 @@ "cp311_windows_x86_64" ], "filename": "requests_toolbelt-1.0.0-py2.py3-none-any.whl", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "requests-toolbelt==1.0.0", "sha256": "cccfdd665f0a24fcf4726e690f65639d272bb0637b9b92dfd91a5568ccf6bd06", @@ -3953,8 +3759,7 @@ } }, "rules_python_publish_deps_311_requests_toolbelt_sdist_7681a0a3": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -3972,7 +3777,7 @@ "https://pypi.org/simple" ], "filename": "requests-toolbelt-1.0.0.tar.gz", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "requests-toolbelt==1.0.0", "sha256": "7681a0a3d047012b5bdc0ee37d7f8f07ebe76ab08caeccfc3921ce23c88d5bc6", @@ -3982,8 +3787,7 @@ } }, "rules_python_publish_deps_311_rfc3986_py2_none_any_50b1502b": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -3997,7 +3801,7 @@ "cp311_windows_x86_64" ], "filename": "rfc3986-2.0.0-py2.py3-none-any.whl", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "rfc3986==2.0.0", "sha256": "50b1502b60e289cb37883f3dfd34532b8873c7de9f49bb546641ce9cbd256ebd", @@ -4007,8 +3811,7 @@ } }, "rules_python_publish_deps_311_rfc3986_sdist_97aacf9d": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -4026,7 +3829,7 @@ "https://pypi.org/simple" ], "filename": "rfc3986-2.0.0.tar.gz", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "rfc3986==2.0.0", "sha256": "97aacf9dbd4bfd829baad6e6309fa6573aaf1be3f6fa735c8ab05e46cecb261c", @@ -4036,8 +3839,7 @@ } }, "rules_python_publish_deps_311_rich_py3_none_any_9836f509": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -4051,7 +3853,7 @@ "cp311_windows_x86_64" ], "filename": "rich-13.9.3-py3-none-any.whl", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "rich==13.9.3", "sha256": "9836f5096eb2172c9e77df411c1b009bace4193d6a481d534fea75ebba758283", @@ -4061,8 +3863,7 @@ } }, "rules_python_publish_deps_311_rich_sdist_bc1e01b8": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -4080,7 +3881,7 @@ "https://pypi.org/simple" ], "filename": "rich-13.9.3.tar.gz", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "rich==13.9.3", "sha256": "bc1e01b899537598cf02579d2b9f4a415104d3fc439313a7a2c165d76557a08e", @@ -4090,8 +3891,7 @@ } }, "rules_python_publish_deps_311_secretstorage_py3_none_any_f356e662": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -4102,7 +3902,7 @@ "cp311_linux_x86_64" ], "filename": "SecretStorage-3.3.3-py3-none-any.whl", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "secretstorage==3.3.3", "sha256": "f356e6628222568e3af06f2eba8df495efa13b3b63081dafd4f7d9a7b7bc9f99", @@ -4112,8 +3912,7 @@ } }, "rules_python_publish_deps_311_secretstorage_sdist_2403533e": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -4128,7 +3927,7 @@ "https://pypi.org/simple" ], "filename": "SecretStorage-3.3.3.tar.gz", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "secretstorage==3.3.3", "sha256": "2403533ef369eca6d2ba81718576c5e0f564d5cca1b58f73a8b23e7d4eeebd77", @@ -4138,8 +3937,7 @@ } }, "rules_python_publish_deps_311_twine_py3_none_any_215dbe7b": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -4153,7 +3951,7 @@ "cp311_windows_x86_64" ], "filename": "twine-5.1.1-py3-none-any.whl", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "twine==5.1.1", "sha256": "215dbe7b4b94c2c50a7315c0275d2258399280fbb7d04182c7e55e24b5f93997", @@ -4163,8 +3961,7 @@ } }, "rules_python_publish_deps_311_twine_sdist_9aa08251": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -4182,7 +3979,7 @@ "https://pypi.org/simple" ], "filename": "twine-5.1.1.tar.gz", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "twine==5.1.1", "sha256": "9aa0825139c02b3434d913545c7b847a21c835e11597f5255842d457da2322db", @@ -4192,8 +3989,7 @@ } }, "rules_python_publish_deps_311_urllib3_py3_none_any_ca899ca0": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -4207,7 +4003,7 @@ "cp311_windows_x86_64" ], "filename": "urllib3-2.2.3-py3-none-any.whl", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "urllib3==2.2.3", "sha256": "ca899ca043dcb1bafa3e262d73aa25c465bfb49e0bd9dd5d59f1d0acba2f8fac", @@ -4217,8 +4013,7 @@ } }, "rules_python_publish_deps_311_urllib3_sdist_e7d814a8": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -4236,7 +4031,7 @@ "https://pypi.org/simple" ], "filename": "urllib3-2.2.3.tar.gz", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "urllib3==2.2.3", "sha256": "e7d814a81dad81e6caf2ec9fdedb284ecc9c73076b62654547cc64ccdcae26e9", @@ -4246,8 +4041,7 @@ } }, "rules_python_publish_deps_311_zipp_py3_none_any_a817ac80": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -4261,7 +4055,7 @@ "cp311_windows_x86_64" ], "filename": "zipp-3.20.2-py3-none-any.whl", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "zipp==3.20.2", "sha256": "a817ac80d6cf4b23bf7f2828b7cabf326f15a001bea8b1f9b49631780ba28350", @@ -4271,8 +4065,7 @@ } }, "rules_python_publish_deps_311_zipp_sdist_bc9eb26f": { - "bzlFile": "@@rules_python~//python/private/pypi:whl_library.bzl", - "ruleClassName": "whl_library", + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", "attributes": { "dep_template": "@rules_python_publish_deps//{name}:{target}", "experimental_target_platforms": [ @@ -4290,7 +4083,7 @@ "https://pypi.org/simple" ], "filename": "zipp-3.20.2.tar.gz", - "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", "repo": "rules_python_publish_deps_311", "requirement": "zipp==3.20.2", "sha256": "bc9eb26f4506fda01b81bcde0ca78103b6e62f991b381fec825435c836edbc29", @@ -4300,8 +4093,7 @@ } }, "pip_deps": { - "bzlFile": "@@rules_python~//python/private/pypi:hub_repository.bzl", - "ruleClassName": "hub_repository", + "repoRuleId": "@@rules_python+//python/private/pypi:hub_repository.bzl%hub_repository", "attributes": { "repo_name": "pip_deps", "extra_hub_aliases": {}, @@ -4317,8 +4109,7 @@ } }, "rules_fuzzing_py_deps": { - "bzlFile": "@@rules_python~//python/private/pypi:hub_repository.bzl", - "ruleClassName": "hub_repository", + "repoRuleId": "@@rules_python+//python/private/pypi:hub_repository.bzl%hub_repository", "attributes": { "repo_name": "rules_fuzzing_py_deps", "extra_hub_aliases": {}, @@ -4334,8 +4125,7 @@ } }, "rules_python_publish_deps": { - "bzlFile": "@@rules_python~//python/private/pypi:hub_repository.bzl", - "ruleClassName": "hub_repository", + "repoRuleId": "@@rules_python+//python/private/pypi:hub_repository.bzl%hub_repository", "attributes": { "repo_name": "rules_python_publish_deps", "extra_hub_aliases": {}, @@ -4401,155 +4191,150 @@ } } }, - "moduleExtensionMetadata": { - "useAllRepos": "NO", - "reproducible": false - }, "recordedRepoMappingEntries": [ [ - "bazel_features~", + "bazel_features+", "bazel_features_globals", - "bazel_features~~version_extension~bazel_features_globals" + "bazel_features++version_extension+bazel_features_globals" ], [ - "bazel_features~", + "bazel_features+", "bazel_features_version", - "bazel_features~~version_extension~bazel_features_version" + "bazel_features++version_extension+bazel_features_version" ], [ - "rules_python~", + "rules_python+", "bazel_features", - "bazel_features~" + "bazel_features+" ], [ - "rules_python~", + "rules_python+", "bazel_skylib", - "bazel_skylib~" + "bazel_skylib+" ], [ - "rules_python~", + "rules_python+", "bazel_tools", "bazel_tools" ], [ - "rules_python~", + "rules_python+", "pypi__build", - "rules_python~~internal_deps~pypi__build" + "rules_python++internal_deps+pypi__build" ], [ - "rules_python~", + "rules_python+", "pypi__click", - "rules_python~~internal_deps~pypi__click" + "rules_python++internal_deps+pypi__click" ], [ - "rules_python~", + "rules_python+", "pypi__colorama", - "rules_python~~internal_deps~pypi__colorama" + "rules_python++internal_deps+pypi__colorama" ], [ - "rules_python~", + "rules_python+", "pypi__importlib_metadata", - "rules_python~~internal_deps~pypi__importlib_metadata" + "rules_python++internal_deps+pypi__importlib_metadata" ], [ - "rules_python~", + "rules_python+", "pypi__installer", - "rules_python~~internal_deps~pypi__installer" + "rules_python++internal_deps+pypi__installer" ], [ - "rules_python~", + "rules_python+", "pypi__more_itertools", - "rules_python~~internal_deps~pypi__more_itertools" + "rules_python++internal_deps+pypi__more_itertools" ], [ - "rules_python~", + "rules_python+", "pypi__packaging", - "rules_python~~internal_deps~pypi__packaging" + "rules_python++internal_deps+pypi__packaging" ], [ - "rules_python~", + "rules_python+", "pypi__pep517", - "rules_python~~internal_deps~pypi__pep517" + "rules_python++internal_deps+pypi__pep517" ], [ - "rules_python~", + "rules_python+", "pypi__pip", - "rules_python~~internal_deps~pypi__pip" + "rules_python++internal_deps+pypi__pip" ], [ - "rules_python~", + "rules_python+", "pypi__pip_tools", - "rules_python~~internal_deps~pypi__pip_tools" + "rules_python++internal_deps+pypi__pip_tools" ], [ - "rules_python~", + "rules_python+", "pypi__pyproject_hooks", - "rules_python~~internal_deps~pypi__pyproject_hooks" + "rules_python++internal_deps+pypi__pyproject_hooks" ], [ - "rules_python~", + "rules_python+", "pypi__setuptools", - "rules_python~~internal_deps~pypi__setuptools" + "rules_python++internal_deps+pypi__setuptools" ], [ - "rules_python~", + "rules_python+", "pypi__tomli", - "rules_python~~internal_deps~pypi__tomli" + "rules_python++internal_deps+pypi__tomli" ], [ - "rules_python~", + "rules_python+", "pypi__wheel", - "rules_python~~internal_deps~pypi__wheel" + "rules_python++internal_deps+pypi__wheel" ], [ - "rules_python~", + "rules_python+", "pypi__zipp", - "rules_python~~internal_deps~pypi__zipp" + "rules_python++internal_deps+pypi__zipp" ], [ - "rules_python~", + "rules_python+", "pythons_hub", - "rules_python~~python~pythons_hub" + "rules_python++python+pythons_hub" ], [ - "rules_python~~python~pythons_hub", + "rules_python++python+pythons_hub", "python_3_10_host", - "rules_python~~python~python_3_10_host" + "rules_python++python+python_3_10_host" ], [ - "rules_python~~python~pythons_hub", + "rules_python++python+pythons_hub", "python_3_11_host", - "rules_python~~python~python_3_11_host" + "rules_python++python+python_3_11_host" ], [ - "rules_python~~python~pythons_hub", + "rules_python++python+pythons_hub", "python_3_12_host", - "rules_python~~python~python_3_12_host" + "rules_python++python+python_3_12_host" ], [ - "rules_python~~python~pythons_hub", + "rules_python++python+pythons_hub", "python_3_8_host", - "rules_python~~python~python_3_8_host" + "rules_python++python+python_3_8_host" ], [ - "rules_python~~python~pythons_hub", + "rules_python++python+pythons_hub", "python_3_9_host", - "rules_python~~python~python_3_9_host" + "rules_python++python+python_3_9_host" ] ] } }, - "@@rules_sass~//src/toolchain:extensions.bzl%sass": { + "@@rules_sass+//src/toolchain:extensions.bzl%sass": { "general": { "bzlTransitiveDigest": "RA58Nyrsn03Z5YmQnpmBw3mqlVck++XIrx34amsqU/E=", - "usagesDigest": "FPXQ5+6+DFGdSdCMXLwFaruzstMFlLH6N0TRxi0sSH8=", + "usagesDigest": "R0KshhzIouLWuexMUCrl4HY+FUDwlVVgF9Z7UnwyUWA=", "recordedFileInputs": {}, "recordedDirentsInputs": {}, "envVariables": {}, "generatedRepoSpecs": { "linux_amd64_sass": { - "bzlFile": "@@rules_sass~//src/toolchain:configure_sass.bzl", - "ruleClassName": "configure_sass", + "repoRuleId": "@@rules_sass+//src/toolchain:configure_sass.bzl%configure_sass", "attributes": { "file": "@rules_sass//src/compiler/built:sass_linux_x64", "sha256": "", @@ -4560,8 +4345,7 @@ } }, "linux_arm64_sass": { - "bzlFile": "@@rules_sass~//src/toolchain:configure_sass.bzl", - "ruleClassName": "configure_sass", + "repoRuleId": "@@rules_sass+//src/toolchain:configure_sass.bzl%configure_sass", "attributes": { "file": "@rules_sass//src/compiler/built:sass_linux_arm", "sha256": "", @@ -4572,8 +4356,7 @@ } }, "darwin_amd64_sass": { - "bzlFile": "@@rules_sass~//src/toolchain:configure_sass.bzl", - "ruleClassName": "configure_sass", + "repoRuleId": "@@rules_sass+//src/toolchain:configure_sass.bzl%configure_sass", "attributes": { "file": "@rules_sass//src/compiler/built:sass_mac_x64", "sha256": "", @@ -4584,8 +4367,7 @@ } }, "darwin_arm64_sass": { - "bzlFile": "@@rules_sass~//src/toolchain:configure_sass.bzl", - "ruleClassName": "configure_sass", + "repoRuleId": "@@rules_sass+//src/toolchain:configure_sass.bzl%configure_sass", "attributes": { "file": "@rules_sass//src/compiler/built:sass_mac_arm", "sha256": "", @@ -4599,81 +4381,72 @@ "recordedRepoMappingEntries": [] } }, - "@@yq.bzl~//yq:extensions.bzl%yq": { + "@@yq.bzl+//yq:extensions.bzl%yq": { "general": { "bzlTransitiveDigest": "61Uz+o5PnlY0jJfPZEUNqsKxnM/UCLeWsn5VVCc8u5Y=", - "usagesDigest": "X+3wxc/+KjF0tyJJ5qca2U/BgoeAEmAD0kuz8iBcX+0=", + "usagesDigest": "iZPDKVC6SAQMLLGWulzoS8hhxbx9Eh5DsJBUjn69u2Q=", "recordedFileInputs": {}, "recordedDirentsInputs": {}, "envVariables": {}, "generatedRepoSpecs": { "yq_darwin_amd64": { - "bzlFile": "@@yq.bzl~//yq/toolchain:platforms.bzl", - "ruleClassName": "yq_platform_repo", + "repoRuleId": "@@yq.bzl+//yq/toolchain:platforms.bzl%yq_platform_repo", "attributes": { "platform": "darwin_amd64", "version": "4.45.1" } }, "yq_darwin_arm64": { - "bzlFile": "@@yq.bzl~//yq/toolchain:platforms.bzl", - "ruleClassName": "yq_platform_repo", + "repoRuleId": "@@yq.bzl+//yq/toolchain:platforms.bzl%yq_platform_repo", "attributes": { "platform": "darwin_arm64", "version": "4.45.1" } }, "yq_linux_amd64": { - "bzlFile": "@@yq.bzl~//yq/toolchain:platforms.bzl", - "ruleClassName": "yq_platform_repo", + "repoRuleId": "@@yq.bzl+//yq/toolchain:platforms.bzl%yq_platform_repo", "attributes": { "platform": "linux_amd64", "version": "4.45.1" } }, "yq_linux_arm64": { - "bzlFile": "@@yq.bzl~//yq/toolchain:platforms.bzl", - "ruleClassName": "yq_platform_repo", + "repoRuleId": "@@yq.bzl+//yq/toolchain:platforms.bzl%yq_platform_repo", "attributes": { "platform": "linux_arm64", "version": "4.45.1" } }, "yq_linux_s390x": { - "bzlFile": "@@yq.bzl~//yq/toolchain:platforms.bzl", - "ruleClassName": "yq_platform_repo", + "repoRuleId": "@@yq.bzl+//yq/toolchain:platforms.bzl%yq_platform_repo", "attributes": { "platform": "linux_s390x", "version": "4.45.1" } }, "yq_linux_riscv64": { - "bzlFile": "@@yq.bzl~//yq/toolchain:platforms.bzl", - "ruleClassName": "yq_platform_repo", + "repoRuleId": "@@yq.bzl+//yq/toolchain:platforms.bzl%yq_platform_repo", "attributes": { "platform": "linux_riscv64", "version": "4.45.1" } }, "yq_linux_ppc64le": { - "bzlFile": "@@yq.bzl~//yq/toolchain:platforms.bzl", - "ruleClassName": "yq_platform_repo", + "repoRuleId": "@@yq.bzl+//yq/toolchain:platforms.bzl%yq_platform_repo", "attributes": { "platform": "linux_ppc64le", "version": "4.45.1" } }, "yq_windows_amd64": { - "bzlFile": "@@yq.bzl~//yq/toolchain:platforms.bzl", - "ruleClassName": "yq_platform_repo", + "repoRuleId": "@@yq.bzl+//yq/toolchain:platforms.bzl%yq_platform_repo", "attributes": { "platform": "windows_amd64", "version": "4.45.1" } }, "yq_toolchains": { - "bzlFile": "@@yq.bzl~//yq/toolchain:toolchain.bzl", - "ruleClassName": "yq_toolchains_repo", + "repoRuleId": "@@yq.bzl+//yq/toolchain:toolchain.bzl%yq_toolchains_repo", "attributes": { "user_repository_name": "yq" } @@ -4682,5 +4455,6 @@ "recordedRepoMappingEntries": [] } } - } + }, + "facts": {} } diff --git a/REPO.bazel b/REPO.bazel new file mode 100644 index 000000000000..9eb8128879bd --- /dev/null +++ b/REPO.bazel @@ -0,0 +1,5 @@ +ignore_directories([ + ".git", + "dist", + "**/node_modules/**", +]) diff --git a/constants.bzl b/constants.bzl index 04e088577dea..e2224aa24c39 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.1.0-next.0" +ANGULAR_FW_VERSION = "^21.1.0-next.0" +ANGULAR_FW_PEER_DEP = "^21.0.0 || ^21.1.0-next.0" +NG_PACKAGR_PEER_DEP = "^21.0.0 || ^21.1.0-next.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/DEVELOPER.md b/docs/DEVELOPER.md index bd0c9bdf1ee3..8204bd7dcbce 100644 --- a/docs/DEVELOPER.md +++ b/docs/DEVELOPER.md @@ -89,8 +89,8 @@ You can find more info about debugging [tests with Bazel in the docs.](https://g ### End to end tests - For a complete list of test targets use the following Bazel query: `pnpm bazel query "tests(//tests/...)"` -- Run a subset of the tests: `pnpm bazel test //tests/legacy-cli:e2e_node22 --config=e2e --test_filter="tests/i18n/ivy-localize-*"` -- Use `bazel run` to debug failing tests debugging: `pnpm bazel run //tests/legacy-cli:e2e_node22 --config=e2e --test_arg="--glob=tests/basic/aot.ts"` +- Run a subset of the tests: `pnpm bazel test //tests:e2e_node22 --config=e2e --test_filter="tests/i18n/ivy-localize-*"` +- Use `bazel run` to debug failing tests debugging: `pnpm bazel run //tests:e2e_node22 --config=e2e --test_arg="--glob=tests/basic/aot.ts"` - Provide additional `e2e_runner` options using `--test_arg`: `--test_arg="--package-manager=yarn"` When running the debug commands, Node will stop and wait for a debugger to attach. 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/eslint.config.mjs b/eslint.config.mjs index 318b5dec3bee..192ba19f2007 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -190,9 +190,7 @@ export default [ ], '@typescript-eslint/await-thenable': 'off', - '@typescript-eslint/ban-types': 'off', '@typescript-eslint/no-implied-eval': 'off', - '@typescript-eslint/no-var-requires': 'off', '@typescript-eslint/no-unsafe-argument': 'off', '@typescript-eslint/no-unsafe-assignment': 'off', '@typescript-eslint/no-unsafe-call': 'off', diff --git a/goldens/public-api/angular_devkit/schematics/tools/index.api.md b/goldens/public-api/angular_devkit/schematics/tools/index.api.md index 6a32d88e9ec0..c93e3ec27762 100644 --- a/goldens/public-api/angular_devkit/schematics/tools/index.api.md +++ b/goldens/public-api/angular_devkit/schematics/tools/index.api.md @@ -203,7 +203,7 @@ export class NodeModulesTestEngineHost extends NodeModulesEngineHost { // (undocumented) protected _resolveCollectionPath(name: string, requester?: string): string; // (undocumented) - get tasks(): TaskConfiguration<{}>[]; + get tasks(): TaskConfiguration[]; // (undocumented) transformContext(context: FileSystemSchematicContext): FileSystemSchematicContext; } diff --git a/modules/testing/builder/package.json b/modules/testing/builder/package.json index fa7810f68892..fe92f53c0725 100644 --- a/modules/testing/builder/package.json +++ b/modules/testing/builder/package.json @@ -5,9 +5,9 @@ "@angular/ssr": "workspace:*", "@angular-devkit/build-angular": "workspace:*", "browser-sync": "3.0.4", - "@vitest/coverage-v8": "4.0.8", - "jsdom": "27.2.0", + "@vitest/coverage-v8": "4.0.15", + "jsdom": "27.3.0", "rxjs": "7.8.2", - "vitest": "4.0.8" + "vitest": "4.0.15" } } diff --git a/package.json b/package.json index 19b580831ba2..5f862d813789 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@angular/devkit-repo", - "version": "21.1.0-next.0", + "version": "21.1.0-next.3", "private": true, "description": "Software Development Kit for Angular", "keywords": [ @@ -11,31 +11,29 @@ "Angular DevKit" ], "scripts": { - "admin": "node --no-warnings=ExperimentalWarning --loader ts-node/esm/transpile-only ./scripts/devkit-admin.mjs", + "admin": "node --no-warnings=ExperimentalWarning --experimental-transform-types ./scripts/devkit-admin.mts", "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", - "postinstall": "pnpm -s webdriver-update && husky", - "//webdriver-update-README": "ChromeDriver version must match Puppeteer Chromium version, see https://github.com/GoogleChrome/puppeteer/releases http://chromedriver.chromium.org/downloads", - "webdriver-update": "webdriver-manager update --standalone false --gecko false --versions.chrome 106.0.5249.21", - "ng-dev": "node --no-warnings=ExperimentalWarning --loader ts-node/esm/transpile-only node_modules/@angular/ng-dev/bundles/cli.mjs", - "ts-circular-deps": "pnpm -s ng-dev ts-circular-deps --config ./scripts/circular-deps-test.conf.mjs", + "postinstall": "husky", + "ts-circular-deps": "ng-dev ts-circular-deps --config ./scripts/circular-deps-test.conf.mjs", "check-tooling-setup": "tsc --project .ng-dev/tsconfig.json", - "diff-release-package": "node --no-warnings=ExperimentalWarning --loader ts-node/esm/transpile-only scripts/diff-release-package.mts" + "diff-release-package": "node scripts/diff-release-package.mts" }, "repository": { "type": "git", "url": "https://github.com/angular/angular-cli.git" }, - "packageManager": "pnpm@10.22.0", + "packageManager": "pnpm@10.26.1", "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.22.0" + "pnpm": "10.26.1" }, "author": "Angular Authors", "license": "MIT", @@ -44,26 +42,26 @@ }, "homepage": "https://github.com/angular/angular-cli", "devDependencies": { - "@angular/animations": "21.0.0-rc.2", - "@angular/cdk": "21.0.0-rc.2", - "@angular/common": "21.0.0-rc.2", - "@angular/compiler": "21.0.0-rc.2", - "@angular/compiler-cli": "21.0.0-rc.2", - "@angular/core": "21.0.0-rc.2", - "@angular/forms": "21.0.0-rc.2", - "@angular/localize": "21.0.0-rc.2", - "@angular/material": "21.0.0-rc.2", - "@angular/ng-dev": "https://github.com/angular/dev-infra-private-ng-dev-builds.git#49d7316da246484759b94f87e6eda47fde86b992", - "@angular/platform-browser": "21.0.0-rc.2", - "@angular/platform-server": "21.0.0-rc.2", - "@angular/router": "21.0.0-rc.2", - "@angular/service-worker": "21.0.0-rc.2", + "@angular/animations": "21.1.0-next.4", + "@angular/cdk": "21.1.0-next.3", + "@angular/common": "21.1.0-next.4", + "@angular/compiler": "21.1.0-next.4", + "@angular/compiler-cli": "21.1.0-next.4", + "@angular/core": "21.1.0-next.4", + "@angular/forms": "21.1.0-next.4", + "@angular/localize": "21.1.0-next.4", + "@angular/material": "21.1.0-next.3", + "@angular/ng-dev": "https://github.com/angular/dev-infra-private-ng-dev-builds.git#ddc3809c1993612732eaae62d28e828b2ed789e5", + "@angular/platform-browser": "21.1.0-next.4", + "@angular/platform-server": "21.1.0-next.4", + "@angular/router": "21.1.0-next.4", + "@angular/service-worker": "21.1.0-next.4", "@babel/core": "7.28.5", "@bazel/bazelisk": "1.26.0", "@bazel/buildifier": "8.2.1", - "@eslint/compat": "1.4.1", - "@eslint/eslintrc": "3.3.1", - "@eslint/js": "9.39.1", + "@eslint/compat": "2.0.0", + "@eslint/eslintrc": "3.3.3", + "@eslint/js": "9.39.2", "@rollup/plugin-alias": "^6.0.0", "@rollup/plugin-commonjs": "^29.0.0", "@rollup/plugin-json": "^6.1.0", @@ -92,25 +90,24 @@ "@types/yargs": "^17.0.20", "@types/yargs-parser": "^21.0.0", "@types/yarnpkg__lockfile": "^1.1.5", - "@typescript-eslint/eslint-plugin": "8.46.4", - "@typescript-eslint/parser": "8.46.4", + "@typescript-eslint/eslint-plugin": "8.50.0", + "@typescript-eslint/parser": "8.50.0", "ajv": "8.17.1", - "ansi-colors": "4.1.3", "buffer": "6.0.3", - "esbuild": "0.27.0", - "esbuild-wasm": "0.27.0", - "eslint": "9.39.1", + "esbuild": "0.27.1", + "esbuild-wasm": "0.27.1", + "eslint": "9.39.2", "eslint-config-prettier": "10.1.8", "eslint-plugin-header": "3.1.1", "eslint-plugin-import": "2.32.0", - "express": "5.1.0", + "express": "5.2.1", "fast-glob": "3.3.3", "globals": "16.5.0", "http-proxy": "^1.18.1", "http-proxy-middleware": "3.0.5", "husky": "9.1.7", - "jasmine": "~5.12.0", - "jasmine-core": "~5.12.0", + "jasmine": "~5.13.0", + "jasmine-core": "~5.13.0", "jasmine-reporters": "^2.5.2", "jasmine-spec-reporter": "~7.0.0", "karma": "~6.4.0", @@ -119,30 +116,26 @@ "karma-jasmine": "~5.1.0", "karma-jasmine-html-reporter": "~2.1.0", "karma-source-map-support": "1.4.0", - "listr2": "9.0.5", "lodash": "^4.17.21", "magic-string": "0.30.21", "prettier": "^3.0.0", "protractor": "~7.0.0", "puppeteer": "18.2.1", "quicktype-core": "23.2.6", - "rollup": "4.53.2", + "rollup": "4.53.5", "rollup-license-plugin": "~3.1.0", - "rollup-plugin-dts": "6.2.3", + "rollup-plugin-dts": "6.3.0", "rollup-plugin-sourcemaps2": "0.5.4", "semver": "7.7.3", "source-map-support": "0.5.21", - "tar": "^7.0.0", "ts-node": "^10.9.1", "tslib": "2.8.1", "typescript": "5.9.3", "undici": "7.16.0", "unenv": "^1.10.0", - "verdaccio": "6.2.1", + "verdaccio": "6.2.4", "verdaccio-auth-memory": "^10.0.0", - "yargs-parser": "22.0.0", - "zod": "4.1.12", - "zone.js": "^0.15.0" + "zone.js": "^0.16.0" }, "dependenciesMeta": { "esbuild": { diff --git a/packages/angular/build/BUILD.bazel b/packages/angular/build/BUILD.bazel index d8f89be1d5cd..2f74117ff850 100644 --- a/packages/angular/build/BUILD.bazel +++ b/packages/angular/build/BUILD.bazel @@ -292,6 +292,10 @@ jasmine_test( name = "dev-server_integration_tests", size = "medium", data = [":dev-server_integration_test_lib"], + env = { + # Force IPv4 to resolve RBE resolution issues + "NODE_OPTIONS": "--dns-result-order=ipv4first", + }, flaky = True, shard_count = 10, ) diff --git a/packages/angular/build/package.json b/packages/angular/build/package.json index 4b24b0cabe3b..22e5e6b4b1be 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.27.0", + "esbuild": "0.27.1", "https-proxy-agent": "7.0.6", "istanbul-lib-instrument": "6.0.3", "jsonc-parser": "3.3.1", @@ -37,13 +37,13 @@ "parse5-html-rewriting-stream": "8.0.0", "picomatch": "4.0.3", "piscina": "5.1.4", - "rolldown": "1.0.0-beta.50", - "sass": "1.94.0", + "rolldown": "1.0.0-beta.54", + "sass": "1.97.0", "semver": "7.7.3", "source-map-support": "0.5.21", "tinyglobby": "0.2.15", "undici": "7.16.0", - "vite": "7.2.2", + "vite": "7.3.0", "watchpack": "2.4.4" }, "optionalDependencies": { @@ -52,12 +52,12 @@ "devDependencies": { "@angular-devkit/core": "workspace:*", "@angular/ssr": "workspace:*", - "jsdom": "27.2.0", + "jsdom": "27.3.0", "less": "4.4.2", - "ng-packagr": "21.0.0-rc.1", + "ng-packagr": "21.1.0-next.0", "postcss": "8.5.6", "rxjs": "7.8.2", - "vitest": "4.0.8" + "vitest": "4.0.15" }, "peerDependencies": { "@angular/compiler": "0.0.0-ANGULAR-FW-PEER-DEP", diff --git a/packages/angular/build/src/builders/application/options.ts b/packages/angular/build/src/builders/application/options.ts index 1b3a15b8cd56..83b7ea428f35 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, stripLeadingSlash } from '../../utils/url'; import { Schema as ApplicationBuilderOptions, ExperimentalPlatform, @@ -681,7 +681,16 @@ export function getLocaleBaseHref( const baseHrefSuffix = localeData.baseHref ?? localeData.subPath + '/'; - return baseHrefSuffix !== '' ? urlJoin(baseHref, baseHrefSuffix) : undefined; + let joinedBaseHref: string | undefined; + if (baseHrefSuffix !== '') { + joinedBaseHref = addTrailingSlash(joinUrlParts(baseHref, baseHrefSuffix)); + + if (baseHref && baseHref[0] !== '/') { + joinedBaseHref = stripLeadingSlash(joinedBaseHref); + } + } + + return joinedBaseHref; } /** diff --git a/packages/angular/build/src/builders/application/options_spec.ts b/packages/angular/build/src/builders/application/options_spec.ts new file mode 100644 index 000000000000..ac6320905019 --- /dev/null +++ b/packages/angular/build/src/builders/application/options_spec.ts @@ -0,0 +1,106 @@ +/** + * @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 { NormalizedApplicationBuildOptions, getLocaleBaseHref } from './options'; + +describe('getLocaleBaseHref', () => { + const baseI18nOptions: NormalizedApplicationBuildOptions['i18nOptions'] = { + inlineLocales: new Set(), + sourceLocale: 'en-US', + locales: {}, + flatOutput: false, + shouldInline: false, + hasDefinedSourceLocale: false, + }; + + it('should return undefined if flatOutput is true', () => { + const result = getLocaleBaseHref(undefined, { ...baseI18nOptions, flatOutput: true }, 'fr'); + expect(result).toBeUndefined(); + }); + + it('should return undefined if locale is not found', () => { + const result = getLocaleBaseHref(undefined, baseI18nOptions, 'fr'); + expect(result).toBeUndefined(); + }); + + it('should return baseHref from locale data if present', () => { + const i18nOptions = { + ...baseI18nOptions, + locales: { + fr: { + files: [], + translation: {}, + subPath: 'fr', + baseHref: '/fr/', + }, + }, + }; + const result = getLocaleBaseHref(undefined, i18nOptions, 'fr'); + expect(result).toBe('/fr/'); + }); + + it('should join baseHref and locale subPath if baseHref is provided', () => { + const i18nOptions = { + ...baseI18nOptions, + locales: { + fr: { + files: [], + translation: {}, + subPath: 'fr', + }, + }, + }; + const result = getLocaleBaseHref('/app/', i18nOptions, 'fr'); + expect(result).toBe('/app/fr/'); + }); + + it('should handle missing baseHref (undefined) correctly', () => { + const i18nOptions = { + ...baseI18nOptions, + locales: { + fr: { + files: [], + translation: {}, + subPath: 'fr', + }, + }, + }; + const result = getLocaleBaseHref(undefined, i18nOptions, 'fr'); + expect(result).toBe('/fr/'); + }); + + it('should handle empty baseHref correctly', () => { + const i18nOptions = { + ...baseI18nOptions, + locales: { + fr: { + files: [], + translation: {}, + subPath: 'fr', + }, + }, + }; + const result = getLocaleBaseHref('', i18nOptions, 'fr'); + expect(result).toBe('/fr/'); + }); + + it('should strip leading slash if baseHref does not start with slash', () => { + const i18nOptions = { + ...baseI18nOptions, + locales: { + fr: { + files: [], + translation: {}, + subPath: 'fr', + }, + }, + }; + const result = getLocaleBaseHref('app/', i18nOptions, 'fr'); + expect(result).toBe('app/fr/'); + }); +}); 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/allowed-hosts_spec.ts b/packages/angular/build/src/builders/dev-server/tests/options/allowed-hosts_spec.ts index 8e96c7b4b4b0..775e057bece6 100644 --- a/packages/angular/build/src/builders/dev-server/tests/options/allowed-hosts_spec.ts +++ b/packages/angular/build/src/builders/dev-server/tests/options/allowed-hosts_spec.ts @@ -10,6 +10,7 @@ import { executeDevServer } from '../../index'; import { executeOnceAndGet } from '../execute-fetch'; import { describeServeBuilder } from '../jasmine-helpers'; import { BASE_OPTIONS, DEV_SERVER_BUILDER_INFO } from '../setup'; +import { text } from 'node:stream/consumers'; const FETCH_HEADERS = Object.freeze({ Host: 'example.com' }); @@ -33,6 +34,7 @@ describeServeBuilder(executeDevServer, DEV_SERVER_BUILDER_INFO, (harness, setupT expect(result?.success).toBeTrue(); expect(response?.statusCode).toBe(403); + expect(response && (await text(response))).toContain('angular.json'); }); it('does not allow an invalid host when option is an empty array', async () => { @@ -47,6 +49,7 @@ describeServeBuilder(executeDevServer, DEV_SERVER_BUILDER_INFO, (harness, setupT expect(result?.success).toBeTrue(); expect(response?.statusCode).toBe(403); + expect(response && (await text(response))).toContain('angular.json'); }); it('allows a host when specified in the option', async () => { 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 75987b2c2cc2..8129daac1ba1 100644 --- a/packages/angular/build/src/builders/dev-server/vite/index.ts +++ b/packages/angular/build/src/builders/dev-server/vite/index.ts @@ -243,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(); 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 9cabeabccfec..73f58ad5c348 100644 --- a/packages/angular/build/src/builders/dev-server/vite/server.ts +++ b/packages/angular/build/src/builders/dev-server/vite/server.ts @@ -60,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. diff --git a/packages/angular/build/src/builders/unit-test/options.ts b/packages/angular/build/src/builders/unit-test/options.ts index ac3d1b4e9652..7f8f8db182fe 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>; @@ -56,7 +56,7 @@ export async function normalizeOptions( const { runner, browsers, progress, filter, browserViewport, ui, runnerConfig } = options; - if (ui && runner !== 'vitest') { + if (ui && runner !== Runner.Vitest) { throw new Error('The "ui" option is only available for the "vitest" runner.'); } @@ -95,7 +95,7 @@ export async function normalizeOptions( include: options.include ?? ['**/*.spec.ts'], exclude: options.exclude, filter, - runnerName: runner ?? 'vitest', + runnerName: runner ?? Runner.Vitest, coverage: { enabled: options.coverage, exclude: options.coverageExclude, @@ -119,7 +119,7 @@ export async function normalizeOptions( browserViewport: width && height ? { width, height } : undefined, watch, debug: options.debug ?? false, - ui: options.ui ?? false, + ui: process.env['CI'] ? false : ui, providersFile: options.providersFile && path.join(workspaceRoot, options.providersFile), setupFiles: options.setupFiles ? options.setupFiles.map((setupFile) => path.join(workspaceRoot, setupFile)) 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 4b30ff0405bf..0cee5e11cd6f 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 @@ -11,7 +11,7 @@ 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'; +import { type NormalizedUnitTestBuilderOptions, injectTestingPolyfills } from '../../options'; import type { TestExecutor } from '../api'; export class KarmaExecutor implements TestExecutor { @@ -70,7 +70,7 @@ export class KarmaExecutor implements TestExecutor { const karmaOptions: KarmaBuilderOptions = { karmaConfig, tsConfig: unitTestOptions.tsConfig ?? buildTargetOptions.tsConfig, - polyfills: buildTargetOptions.polyfills, + polyfills: injectTestingPolyfills(buildTargetOptions.polyfills), assets: buildTargetOptions.assets, scripts: buildTargetOptions.scripts, styles: buildTargetOptions.styles, 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 4f7469ebf28b..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 @@ -36,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( @@ -120,21 +124,23 @@ export async function setupBrowserConfiguration( } const isCI = !!process.env['CI']; - let headless = isCI || browsers.some((name) => name.toLowerCase().includes('headless')); + const instances = browsers.map(normalizeBrowserName); if (providerName === 'preview') { - // `preview` provider does not support headless mode - headless = false; + 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/executor.ts b/packages/angular/build/src/builders/unit-test/runners/vitest/executor.ts index bac8e389c4c5..00782d1704cf 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 @@ -90,7 +90,7 @@ export class VitestExecutor implements TestExecutor { if (buildResult.kind === ResultKind.Incremental) { // To rerun tests, Vitest needs the original test file paths, not the output paths. const modifiedSourceFiles = new Set(); - for (const modifiedFile of buildResult.modified) { + for (const modifiedFile of [...buildResult.modified, ...buildResult.added]) { // The `modified` files in the build result are the output paths. // We need to find the original source file path to pass to Vitest. const source = this.entryPointToTestFile.get(modifiedFile); @@ -220,7 +220,7 @@ export class VitestExecutor implements TestExecutor { cache: cacheOptions.enabled ? undefined : false, testNamePattern: this.options.filter, watch, - ui, + ...(typeof ui === 'boolean' ? { ui } : {}), ...debugOptions, }, { 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 96d3337e0149..b92c6e7f872d 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 @@ -9,6 +9,7 @@ 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 { BrowserConfigOptions, @@ -127,7 +128,7 @@ export async function createVitestConfigPlugin( }, resolve: { mainFields: ['es2020', 'module', 'main'], - conditions: ['es2015', 'es2020', 'module'], + conditions: ['es2015', 'es2020', 'module', ...(browser ? ['browser'] : [])], }, }; @@ -153,7 +154,11 @@ export async function createVitestConfigPlugin( return { test: { - coverage: await generateCoverageOption(options.coverage, projectName), + coverage: await generateCoverageOption( + options.coverage, + testConfig?.coverage, + projectName, + ), // eslint-disable-next-line @typescript-eslint/no-explicit-any ...(reporters ? ({ reporters } as any) : {}), projects: [projectConfig], @@ -163,14 +168,39 @@ export async function createVitestConfigPlugin( }; } +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: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)) { @@ -185,15 +215,28 @@ export function createVitestPlugins(pluginOptions: PluginOptions): VitestPlugins } } - if (testFileToEntryPoint.has(id)) { - return id; + // 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; } - assert(buildResultFiles.size > 0, 'buildResult must be available for resolving.'); - const relativePath = path.relative(workspaceRoot, id); + // 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 id; + 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.'); @@ -217,17 +260,10 @@ export function createVitestPlugins(pluginOptions: PluginOptions): VitestPlugins const outputFile = buildResultFiles.get(outputPath); if (outputFile) { + const code = await loadResultFile(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 sourceMapText = sourceMapFile - ? sourceMapFile.origin === 'memory' - ? Buffer.from(sourceMapFile.contents).toString('utf-8') - : await readFile(sourceMapFile.inputPath, 'utf-8') - : undefined; + 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, @@ -271,11 +307,12 @@ export function createVitestPlugins(pluginOptions: PluginOptions): VitestPlugins } async function generateCoverageOption( - coverage: NormalizedUnitTestBuilderOptions['coverage'], + optionsCoverage: NormalizedUnitTestBuilderOptions['coverage'], + configCoverage: VitestCoverageOption | undefined, projectName: string, ): Promise { let defaultExcludes: string[] = []; - if (coverage.exclude) { + if (optionsCoverage.exclude) { try { const vitestConfig = await import('vitest/config'); defaultExcludes = vitestConfig.coverageConfigDefaults.exclude; @@ -283,28 +320,31 @@ async function generateCoverageOption( } return { - enabled: coverage.enabled, excludeAfterRemap: true, + reportsDirectory: + configCoverage?.reportsDirectory ?? toPosixPath(path.join('coverage', projectName)), + ...(optionsCoverage.enabled !== undefined ? { enabled: optionsCoverage.enabled } : {}), // 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, + ...(optionsCoverage.include + ? { include: ['spec-*.js', 'chunk-*.js', ...optionsCoverage.include] } + : {}), + thresholds: optionsCoverage.thresholds, + watermarks: optionsCoverage.watermarks, // Special handling for `exclude`/`reporters` due to an undefined value causing upstream failures - ...(coverage.exclude + ...(optionsCoverage.exclude ? { exclude: [ // Augment the default exclude https://vitest.dev/config/#coverage-exclude // with the user defined exclusions - ...coverage.exclude, + ...optionsCoverage.exclude, ...defaultExcludes, ], } : {}), - ...(coverage.reporters - ? ({ reporter: coverage.reporters } satisfies VitestCoverageOption) + ...(optionsCoverage.reporters + ? ({ reporter: optionsCoverage.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 3b92d31a4cde..2a3cf27719e8 100644 --- a/packages/angular/build/src/builders/unit-test/schema.json +++ b/packages/angular/build/src/builders/unit-test/schema.json @@ -67,8 +67,7 @@ }, "ui": { "type": "boolean", - "description": "Enables the Vitest UI for interactive test execution. This option is only available for the Vitest runner.", - "default": false + "description": "Enables the Vitest UI for interactive test execution. This option is only available for the Vitest runner." }, "coverage": { "type": "boolean", 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 89d38dbbe787..d4f097b388f7 100644 --- a/packages/angular/build/src/builders/unit-test/test-discovery.ts +++ b/packages/angular/build/src/builders/unit-test/test-discovery.ts @@ -6,6 +6,7 @@ * found in the LICENSE file at https://angular.dev/license */ +import { createHash } from 'node:crypto'; 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'; @@ -18,6 +19,9 @@ import { toPosixPath } from '../../utils/path'; */ const TEST_FILE_INFIXES = ['.spec', '.test']; +/** Maximum length for a generated test entrypoint name. */ +const MAX_FILENAME_LENGTH = 128; + /** * Finds all test files in the project. This function implements a special handling * for static paths (non-globs) to improve developer experience. For example, if a @@ -65,7 +69,7 @@ export async function findTests( }); for (const match of globMatches) { - resolvedTestFiles.add(match); + resolvedTestFiles.add(toPosixPath(match)); } } @@ -120,7 +124,7 @@ export function getTestEntrypoints( * @param removeTestExtension Whether to remove the test file infix and extension from the result. * @returns A dash-cased name derived from the relative path of the test file. */ -function generateNameFromPath( +export function generateNameFromPath( testFile: string, roots: string[], removeTestExtension: boolean, @@ -155,7 +159,29 @@ function generateNameFromPath( result += char === '/' || char === '\\' ? '-' : char; } - return result; + return truncateName(result, relativePath); +} + +/** + * Truncates a generated name if it exceeds the maximum allowed filename length. + * If truncation occurs, the name will be shortened by replacing a middle segment + * with an 8-character SHA256 hash of the original full path to maintain uniqueness. + * + * @param name The generated name to potentially truncate. + * @param originalPath The original full path from which the name was derived. Used for hashing. + * @returns The original name if within limits, or a truncated name with a hash. + */ +function truncateName(name: string, originalPath: string): string { + if (name.length <= MAX_FILENAME_LENGTH) { + return name; + } + + const hash = createHash('sha256').update(originalPath).digest('hex').substring(0, 8); + const availableLength = MAX_FILENAME_LENGTH - hash.length - 2; // 2 for '-' separators + const prefixLength = Math.floor(availableLength / 2); + const suffixLength = availableLength - prefixLength; + + return `${name.substring(0, prefixLength)}-${hash}-${name.substring(name.length - suffixLength)}`; } /** @@ -261,15 +287,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/test-discovery_spec.ts b/packages/angular/build/src/builders/unit-test/test-discovery_spec.ts new file mode 100644 index 000000000000..617839868d50 --- /dev/null +++ b/packages/angular/build/src/builders/unit-test/test-discovery_spec.ts @@ -0,0 +1,91 @@ +/** + * @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 { generateNameFromPath } from './test-discovery'; + +describe('generateNameFromPath', () => { + const roots = ['/project/src/', '/project/']; + + it('should generate a dash-cased name from a simple path', () => { + const testFile = '/project/src/app/components/my-component.spec.ts'; + const result = generateNameFromPath(testFile, roots, true); + expect(result).toBe('app-components-my-component'); + }); + + it('should handle Windows-style paths', () => { + const testFile = 'C:\\project\\src\\app\\components\\my-component.spec.ts'; + const result = generateNameFromPath(testFile, ['C:\\project\\src\\'], true); + expect(result).toBe('app-components-my-component'); + }); + + it('should remove test extensions when removeTestExtension is true', () => { + const testFile = '/project/src/app/utils/helpers.test.ts'; + const result = generateNameFromPath(testFile, roots, true); + expect(result).toBe('app-utils-helpers'); + }); + + it('should not remove test extensions when removeTestExtension is false', () => { + const testFile = '/project/src/app/utils/helpers.test.ts'; + const result = generateNameFromPath(testFile, roots, false); + expect(result).toBe('app-utils-helpers.test'); + }); + + it('should handle paths with leading dots and slashes', () => { + const testFile = '/project/src/./app/services/api.service.spec.ts'; + const result = generateNameFromPath(testFile, roots, true); + expect(result).toBe('app-services-api.service'); + }); + + it('should return the basename if no root matches', () => { + const testFile = '/unrelated/path/to/some/file.spec.ts'; + const result = generateNameFromPath(testFile, roots, true); + expect(result).toBe('file'); + }); + + it('should truncate a long file name', () => { + const longPath = + 'a/very/long/path/that/definitely/exceeds/the/maximum/allowed/length/for/a/filename/in/order/to/trigger/the/truncation/logic/in/the/function.spec.ts'; // eslint-disable-line max-len + const testFile = `/project/src/${longPath}`; + const result = generateNameFromPath(testFile, roots, true); + + expect(result.length).toBeLessThanOrEqual(128); + expect(result).toBe( + 'a-very-long-path-that-definitely-exceeds-the-maximum-allowe-9cf40291-me-in-order-to-trigger-the-truncation-logic-in-the-function', + ); // eslint-disable-line max-len + }); + + it('should generate different hashes for different paths with similar truncated names', () => { + const longPath1 = + 'a/very/long/path/that/definitely/exceeds/the/maximum/allowed/length/for/a/filename/in/order/to/trigger/the/truncation/logic/variant-a.spec.ts'; // eslint-disable-line max-len + const longPath2 = + 'a/very/long/path/that/definitely/exceeds/the/maximum/allowed/length/for/a/filename/in/order/to/trigger/the/truncation/logic/variant-b.spec.ts'; // eslint-disable-line max-len + + const testFile1 = `/project/src/${longPath1}`; + const testFile2 = `/project/src/${longPath2}`; + + const result1 = generateNameFromPath(testFile1, roots, true); + const result2 = generateNameFromPath(testFile2, roots, true); + + expect(result1).not.toBe(result2); + // The hash is always 8 characters long and is surrounded by hyphens. + const hashRegex = /-[a-f0-9]{8}-/; + const hash1 = result1.match(hashRegex)?.[0]; + const hash2 = result2.match(hashRegex)?.[0]; + + expect(hash1).toBeDefined(); + expect(hash2).toBeDefined(); + expect(hash1).not.toBe(hash2); + }); + + it('should not truncate a filename that is exactly the max length', () => { + const name = 'a'.repeat(128); + const testFile = `/project/src/${name}.spec.ts`; + const result = generateNameFromPath(testFile, roots, true); + expect(result).toBe(name); + }); +}); 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 index f68dd8daa171..603c69f533ea 100644 --- 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 @@ -42,6 +42,56 @@ describeBuilder(execute, UNIT_TEST_BUILDER_INFO, (harness) => { harness.expectFile('vitest-results.xml').toExist(); }); + it('should use custom reportsDirectory defined in runnerConfig file', async () => { + harness.useTarget('test', { + ...BASE_OPTIONS, + runnerConfig: 'vitest.config.ts', + coverage: true, + }); + + harness.writeFile( + 'vitest.config.ts', + ` + import { defineConfig } from 'vitest/config'; + export default defineConfig({ + test: { + coverage: { + reportsDirectory: './custom-coverage-reports', + }, + }, + }); + `, + ); + + const { result } = await harness.executeOnce(); + expect(result?.success).toBeTrue(); + harness.expectFile('custom-coverage-reports/coverage-final.json').toExist(); + }); + + it('should use default reportsDirectory when not defined in runnerConfig file', async () => { + harness.useTarget('test', { + ...BASE_OPTIONS, + coverage: true, + runnerConfig: 'vitest.config.ts', + }); + + harness.writeFile( + 'vitest.config.ts', + ` + import { defineConfig } from 'vitest/config'; + export default defineConfig({ + test: { + coverage: {}, + }, + }); + `, + ); + + const { result } = await harness.executeOnce(); + expect(result?.success).toBeTrue(); + harness.expectFile('coverage/test/coverage-final.json').toExist(); + }); + it('should exclude test files based on runnerConfig file', async () => { harness.useTarget('test', { ...BASE_OPTIONS, diff --git a/packages/angular/build/src/builders/unit-test/tests/behavior/watch_rebuild_spec.ts b/packages/angular/build/src/builders/unit-test/tests/behavior/watch_rebuild_spec.ts new file mode 100644 index 000000000000..6ff753c7eac7 --- /dev/null +++ b/packages/angular/build/src/builders/unit-test/tests/behavior/watch_rebuild_spec.ts @@ -0,0 +1,68 @@ +/** + * @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('Watch Mode Behavior', () => { + beforeEach(async () => { + setupApplicationTarget(harness); + }); + + it('should run tests when a compilation error is fixed and a test failure is introduced simultaneously', async () => { + harness.useTarget('test', { + ...BASE_OPTIONS, + watch: true, + }); + + await harness.executeWithCases([ + // 1. Initial success + ({ result }) => { + expect(result?.success).toBeTrue(); + + // 2. Introduce compilation error + harness.writeFiles({ + 'src/app/app.component.spec.ts': ` + import { describe, expect, test } from 'vitest' + describe('AppComponent', () => { + test('should create the app', () => { + expect(true).toBe(true); // Syntax error incoming + const x: string = 1; // Type error + }); + });`, + }); + }, + // 3. Expect compilation error + ({ result }) => { + expect(result?.success).toBeFalse(); + + // 4. Fix compilation error BUT introduce test failure + harness.writeFiles({ + 'src/app/app.component.spec.ts': ` + import { describe, expect, test } from 'vitest' + describe('AppComponent', () => { + test('should create the app', () => { + expect(true).toBe(false); // Logic failure + }); + });`, + }); + }, + // 5. Expect test failure (NOT success, which would happen if the test was skipped) + ({ result }) => { + expect(result?.success).toBeFalse(); + }, + ]); + }); + }); +}); 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 8012ad7f567a..c55d7482bb2c 100644 --- a/packages/angular/build/src/private.ts +++ b/packages/angular/build/src/private.ts @@ -36,7 +36,6 @@ 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'; @@ -61,6 +60,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 ca4e869aecd5..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'; 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 32bd7c586ed2..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'; 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/esbuild/angular/compiler-plugin.ts b/packages/angular/build/src/tools/esbuild/angular/compiler-plugin.ts index 95e6694b728b..af4dcaea01fb 100644 --- a/packages/angular/build/src/tools/esbuild/angular/compiler-plugin.ts +++ b/packages/angular/build/src/tools/esbuild/angular/compiler-plugin.ts @@ -591,6 +591,7 @@ export function createCompilerPlugin( build.onDispose(() => { sharedTSCompilationState?.dispose(); void compilation.close?.(); + void javascriptTransformer.close(); void cacheStore?.close(); }); 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/base-middleware.ts b/packages/angular/build/src/tools/vite/middlewares/base-middleware.ts new file mode 100644 index 000000000000..00198e03061a --- /dev/null +++ b/packages/angular/build/src/tools/vite/middlewares/base-middleware.ts @@ -0,0 +1,56 @@ +/** + * @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 type { IncomingMessage, ServerResponse } from 'node:http'; +import type { Connect } from 'vite'; +import { addLeadingSlash } from '../../../utils/url'; + +/** + * Patches the Vite base middleware to correctly handle the Angular application's base href. + * This is necessary because Vite's default base middleware might not align with Angular's + * expected path handling when using SSR, especially when a base href is configured. + * + * @param middlewares The Connect server instance containing the middleware stack. + * @param base The base URL path to be handled by the middleware. + */ +export function patchBaseMiddleware(middlewares: Connect.Server, base: string): void { + const entry = middlewares.stack.find( + ({ handle }) => typeof handle === 'function' && handle.name.startsWith('viteBaseMiddleware'), + ); + + if (typeof entry?.handle !== 'function') { + return; + } + + entry.handle = function angularBaseMiddleware( + req: IncomingMessage, + res: ServerResponse, + next: (err?: unknown) => void, + ) { + const url = req.url || '/'; + if (url.startsWith(base)) { + // Rewrite the URL to remove the base prefix before passing it to the next middleware. + // If the URL is exactly the base, it becomes '/'. + // Otherwise, we slice off the base and ensure there's a leading slash. + // See: https://github.com/vitejs/vite/blob/e81c183f8c8ccaf7774ef0d0ee125bf63dbf30b4/packages/vite/src/node/server/middlewares/base.ts#L12 + req.url = url === base ? '/' : addLeadingSlash(url.slice(base.length - 1)); + + return next(); + } + + const { pathname, hash, search } = new URL(url, 'http://localhost'); + if (pathname === '/' || pathname === '/index.html') { + res.writeHead(302, { Location: `${base}${search}${hash}` }); + res.end(); + + return; + } + + next(); + }; +} diff --git a/packages/angular/build/src/tools/vite/middlewares/host-check-middleware.ts b/packages/angular/build/src/tools/vite/middlewares/host-check-middleware.ts new file mode 100644 index 000000000000..8561354812b3 --- /dev/null +++ b/packages/angular/build/src/tools/vite/middlewares/host-check-middleware.ts @@ -0,0 +1,76 @@ +/** + * @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 type { IncomingMessage, ServerResponse } from 'node:http'; +import type { Connect } from 'vite'; + +export function patchHostValidationMiddleware(middlewares: Connect.Server): void { + const entry = middlewares.stack.find( + ({ handle }) => + typeof handle === 'function' && handle.name.startsWith('hostValidationMiddleware'), + ); + + if (typeof entry?.handle !== 'function') { + return; + } + + const originalHandle = entry.handle as Connect.NextHandleFunction; + + entry.handle = function angularHostValidationMiddleware( + req: IncomingMessage, + res: ServerResponse, + next: (err?: unknown) => void, + ) { + originalHandle( + req, + { + writeHead: (code) => { + res.writeHead(code, { 'content-type': 'text/html' }); + }, + end: () => { + const hostname = req.headers.host?.toLowerCase().split(':')[0] ?? ''; + res.end(html403(hostname)); + }, + } as ServerResponse, + next, + ); + }; +} + +function html403(hostname: string): string { + return ` + + + + + Blocked request + + + +
+

Blocked request. This host ("${hostname}") is not allowed.

+

To allow this host, add it to allowedHosts under the serve target in angular.json.

+
{
+  "serve": {
+    "options": {
+      "allowedHosts": ["${hostname}"]
+    }
+  }
+}
+
+ + `; +} diff --git a/packages/angular/build/src/tools/vite/middlewares/index.ts b/packages/angular/build/src/tools/vite/middlewares/index.ts index ef2db01f3aaf..807e739eed59 100644 --- a/packages/angular/build/src/tools/vite/middlewares/index.ts +++ b/packages/angular/build/src/tools/vite/middlewares/index.ts @@ -16,3 +16,5 @@ export { export { createAngularHeadersMiddleware } from './headers-middleware'; export { createAngularComponentMiddleware } from './component-middleware'; export { createChromeDevtoolsMiddleware } from './chrome-devtools-middleware'; +export { patchHostValidationMiddleware } from './host-check-middleware'; +export { patchBaseMiddleware } from './base-middleware'; 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 9719fc6b7482..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,8 +37,7 @@ 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 { @@ -88,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/setup-middlewares-plugin.ts b/packages/angular/build/src/tools/vite/plugins/setup-middlewares-plugin.ts index b82cc2d3acd6..5d20d5c705ac 100644 --- a/packages/angular/build/src/tools/vite/plugins/setup-middlewares-plugin.ts +++ b/packages/angular/build/src/tools/vite/plugins/setup-middlewares-plugin.ts @@ -17,6 +17,8 @@ import { createAngularSsrExternalMiddleware, createAngularSsrInternalMiddleware, createChromeDevtoolsMiddleware, + patchBaseMiddleware, + patchHostValidationMiddleware, } from '../middlewares'; import { AngularMemoryOutputFiles, AngularOutputAssets } from '../utils'; @@ -86,10 +88,12 @@ export function createAngularSetupMiddlewaresPlugin( resetComponentUpdates, } = options; + const middlewares = server.middlewares; + // Headers, assets and resources get handled first - server.middlewares.use(createAngularHeadersMiddleware(server)); - server.middlewares.use(createAngularComponentMiddleware(server, templateUpdates)); - server.middlewares.use( + middlewares.use(createAngularHeadersMiddleware(server)); + middlewares.use(createAngularComponentMiddleware(server, templateUpdates)); + middlewares.use( createAngularAssetsMiddleware( server, assets, @@ -99,30 +103,29 @@ export function createAngularSetupMiddlewaresPlugin( ), ); - server.middlewares.use( - createChromeDevtoolsMiddleware(server.config.cacheDir, options.projectRoot), - ); + middlewares.use(createChromeDevtoolsMiddleware(server.config.cacheDir, options.projectRoot)); - extensionMiddleware?.forEach((middleware) => server.middlewares.use(middleware)); + extensionMiddleware?.forEach((middleware) => middlewares.use(middleware)); // Returning a function, installs middleware after the main transform middleware but // before the built-in HTML middleware // eslint-disable-next-line @typescript-eslint/no-misused-promises return async () => { + patchHostValidationMiddleware(server.middlewares); + if (ssrMode === ServerSsrMode.ExternalSsrMiddleware) { - server.middlewares.use( - await createAngularSsrExternalMiddleware(server, indexHtmlTransformer), - ); + patchBaseMiddleware(server.middlewares, server.config.base); + middlewares.use(await createAngularSsrExternalMiddleware(server, indexHtmlTransformer)); return; } if (ssrMode === ServerSsrMode.InternalSsrMiddleware) { - server.middlewares.use(createAngularSsrInternalMiddleware(server, indexHtmlTransformer)); + middlewares.use(createAngularSsrInternalMiddleware(server, indexHtmlTransformer)); } - server.middlewares.use(angularHtmlFallbackMiddleware); - server.middlewares.use( + middlewares.use(angularHtmlFallbackMiddleware); + middlewares.use( createAngularIndexHtmlMiddleware( server, outputFiles, 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 index c87e0bd112a0..80ddf56e739a 100644 --- a/packages/angular/build/src/tools/vite/plugins/ssr-ssl-plugin.ts +++ b/packages/angular/build/src/tools/vite/plugins/ssr-ssl-plugin.ts @@ -6,7 +6,8 @@ * found in the LICENSE file at https://angular.dev/license */ -import { rootCertificates } from 'node:tls'; +import { readFile } from 'node:fs/promises'; +import { getCACertificates, rootCertificates, setDefaultCACertificates } from 'node:tls'; import type { Plugin } from 'vite'; export function createAngularServerSideSSLPlugin(): Plugin { @@ -23,17 +24,42 @@ export function createAngularServerSideSSLPlugin(): Plugin { return; } - // 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 + 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']; + } + + const { cert } = https; + const additionalCerts = Array.isArray(cert) ? cert : [cert]; + + // TODO(alanagius): Remove the `if` check once we only support Node.js 22.18.0+ and 24.5.0+. + if (getCACertificates && setDefaultCACertificates) { + const currentCerts = getCACertificates('default'); + setDefaultCACertificates([...currentCerts, ...additionalCerts]); + + return; + } + + // TODO(alanagius): Remove the below and `undici` dependency once we only support Node.js 22.18.0+ and 24.5.0+. const { getGlobalDispatcher, setGlobalDispatcher, Agent } = await import('undici'); const originalDispatcher = getGlobalDispatcher(); - const { cert } = https; - const certificates = Array.isArray(cert) ? cert : [cert]; + const ca = [...rootCertificates, ...additionalCerts]; + const extraNodeCerts = process.env['NODE_EXTRA_CA_CERTS']; + if (extraNodeCerts) { + ca.push(await readFile(extraNodeCerts)); + } setGlobalDispatcher( new Agent({ connect: { - ca: [...rootCertificates, ...certificates], + ca, }, }), ); 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 5ece379ec9c0..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'; @@ -240,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 }; @@ -298,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, @@ -311,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()), }); } } @@ -369,15 +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; -} 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 e173d31e5413..abed616cd810 100644 --- a/packages/angular/cli/BUILD.bazel +++ b/packages/angular/cli/BUILD.bazel @@ -4,8 +4,7 @@ # found in the LICENSE file at https://angular.dev/license load("@npm//:defs.bzl", "npm_link_all_packages") -load("//tools:defaults.bzl", "jasmine_test", "npm_package", "ts_project") -load("//tools:example_db_generator.bzl", "cli_example_db") +load("//tools:defaults.bzl", "jasmine_test", "ng_examples_db", "npm_package", "ts_project") load("//tools:ng_cli_schema_generator.bzl", "cli_json_schema") load("//tools:ts_json_schema.bzl", "ts_json_schema") @@ -15,6 +14,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 +37,7 @@ RUNTIME_ASSETS = glob( ) + [ "//packages/angular/cli:lib/config/schema.json", "//packages/angular/cli:lib/code-examples.db", + ":angular_best_practices", ] ts_project( @@ -57,6 +68,7 @@ ts_project( ":node_modules/algoliasearch", ":node_modules/ini", ":node_modules/jsonc-parser", + ":node_modules/listr2", ":node_modules/npm-package-arg", ":node_modules/pacote", ":node_modules/parse5-html-rewriting-stream", @@ -72,13 +84,12 @@ ts_project( "//:node_modules/@types/semver", "//:node_modules/@types/yargs", "//:node_modules/@types/yarnpkg__lockfile", - "//:node_modules/listr2", "//:node_modules/semver", "//:node_modules/typescript", ], ) -cli_example_db( +ng_examples_db( name = "cli_example_database", srcs = glob( include = [ diff --git a/packages/angular/cli/package.json b/packages/angular/cli/package.json index db18df264765..1fb8b671f261 100644 --- a/packages/angular/cli/package.json +++ b/packages/angular/cli/package.json @@ -27,20 +27,20 @@ "@angular-devkit/schematics": "workspace:0.0.0-PLACEHOLDER", "@inquirer/prompts": "7.10.1", "@listr2/prompt-adapter-inquirer": "3.0.5", - "@modelcontextprotocol/sdk": "1.21.1", + "@modelcontextprotocol/sdk": "1.25.0", "@schematics/angular": "workspace:0.0.0-PLACEHOLDER", "@yarnpkg/lockfile": "1.1.0", - "algoliasearch": "5.43.0", + "algoliasearch": "5.46.0", "ini": "6.0.0", "jsonc-parser": "3.3.1", "listr2": "9.0.5", - "npm-package-arg": "13.0.1", - "pacote": "21.0.3", + "npm-package-arg": "13.0.2", + "pacote": "21.0.4", "parse5-html-rewriting-stream": "8.0.0", "resolve": "1.22.11", "semver": "7.7.3", "yargs": "18.0.0", - "zod": "3.25.76" + "zod": "4.2.1" }, "ng-update": { "migrations": "@schematics/angular/migrations/migration-collection.json", 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/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/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..0dae016fba12 100644 --- a/packages/angular/cli/src/commands/add/cli.ts +++ b/packages/angular/cli/src/commands/add/cli.ts @@ -8,12 +8,12 @@ import { Listr, ListrRenderer, ListrTaskWrapper, color, figures } from 'listr2'; import assert from 'node:assert'; +import fs from 'node:fs/promises'; import { createRequire } from 'node:module'; import { dirname, join } from 'node:path'; import npa from 'npm-package-arg'; -import { Range, compare, intersects, prerelease, satisfies, valid } from 'semver'; +import semver, { Range, compare, intersects, prerelease, satisfies, valid } from 'semver'; import { Argv } from 'yargs'; -import { PackageManager } from '../../../lib/config/workspace-schema'; import { CommandModuleImplementation, Options, @@ -23,14 +23,14 @@ import { SchematicsCommandArgs, SchematicsCommandModule, } from '../../command-builder/schematics-command-module'; -import { assertIsError } from '../../utilities/error'; import { NgAddSaveDependency, + PackageManager, PackageManifest, PackageMetadata, - fetchPackageManifest, - fetchPackageMetadata, -} from '../../utilities/package-metadata'; + createPackageManager, +} from '../../package-managers'; +import { assertIsError } from '../../utilities/error'; import { isTTY } from '../../utilities/tty'; import { VERSION } from '../../utilities/version'; @@ -44,8 +44,8 @@ interface AddCommandArgs extends SchematicsCommandArgs { } interface AddCommandTaskContext { + packageManager: PackageManager; packageIdentifier: npa.Result; - usingYarn?: boolean; savePackage?: NgAddSaveDependency; collectionName?: string; executeSchematic: AddCommandModule['executeSchematic']; @@ -173,12 +173,12 @@ export default class AddCommandModule } } - const taskContext: AddCommandTaskContext = { + const taskContext = { packageIdentifier, executeSchematic: this.executeSchematic.bind(this), getPeerDependencyConflicts: this.getPeerDependencyConflicts.bind(this), dryRun: options.dryRun, - }; + } as AddCommandTaskContext; const tasks = new Listr( [ @@ -194,7 +194,7 @@ export default class AddCommandModule rendererOptions: { persistentOutput: true }, }, { - title: 'Loading package information from registry', + title: 'Loading package information', task: (context, task) => this.loadPackageInfoTask(context, task, options), rendererOptions: { persistentOutput: true }, }, @@ -294,13 +294,16 @@ export default class AddCommandModule } } - private determinePackageManagerTask( + private async determinePackageManagerTask( context: AddCommandTaskContext, task: AddCommandTaskWrapper, - ): void { - const { packageManager } = this.context; - context.usingYarn = packageManager.name === PackageManager.Yarn; - task.output = `Using package manager: ${color.dim(packageManager.name)}`; + ): Promise { + context.packageManager = await createPackageManager({ + cwd: this.context.root, + logger: this.context.logger, + dryRun: context.dryRun, + }); + task.output = `Using package manager: ${color.dim(context.packageManager.name)}`; } private async findCompatiblePackageVersionTask( @@ -308,77 +311,84 @@ export default class AddCommandModule task: AddCommandTaskWrapper, options: Options, ): Promise { - const { logger } = this.context; - const { verbose, registry } = options; + const { registry, verbose } = options; + const { packageManager, packageIdentifier } = context; + const packageName = packageIdentifier.name; - assert( - context.packageIdentifier.name, - 'Registry package identifiers should always have a name.', - ); + assert(packageName, 'Registry package identifiers should always have a name.'); - // only package name provided; search for viable version - // plus special cases for packages that did not have peer deps setup - let packageMetadata; + const rejectionReasons: string[] = []; + + // Attempt to use the 'latest' tag from the registry. try { - packageMetadata = await fetchPackageMetadata(context.packageIdentifier.name, logger, { + const latestManifest = await packageManager.getManifest(`${packageName}@latest`, { registry, - usingYarn: context.usingYarn, - verbose, }); + + if (latestManifest) { + const conflicts = await this.getPeerDependencyConflicts(latestManifest); + if (!conflicts) { + context.packageIdentifier = npa.resolve(latestManifest.name, latestManifest.version); + task.output = `Found compatible package version: ${color.blue(latestManifest.version)}.`; + + return; + } + rejectionReasons.push(...conflicts); + } } catch (e) { assertIsError(e); throw new CommandError(`Unable to load package information from registry: ${e.message}`); } - const rejectionReasons: string[] = []; - - // Start with the version tagged as `latest` if it exists - const latestManifest = packageMetadata.tags['latest']; - if (latestManifest) { - const latestConflicts = await this.getPeerDependencyConflicts(latestManifest); - if (latestConflicts) { - // 'latest' is invalid so search for most recent matching package - rejectionReasons.push(...latestConflicts); - } else { - context.packageIdentifier = npa.resolve(latestManifest.name, latestManifest.version); - task.output = `Found compatible package version: ${color.blue(latestManifest.version)}.`; + // 'latest' is invalid or not found, search for most recent matching package. + task.output = + 'Could not find a compatible version with `latest`. Searching for a compatible version.'; - return; - } + let packageMetadata; + try { + packageMetadata = await packageManager.getRegistryMetadata(packageName, { + registry, + }); + } catch (e) { + assertIsError(e); + throw new CommandError(`Unable to load package information from registry: ${e.message}`); } - // Allow prelease versions if the CLI itself is a prerelease - const allowPrereleases = !!prerelease(VERSION.full); - const versionManifests = this.#getPotentialVersionManifests(packageMetadata, allowPrereleases); + if (!packageMetadata) { + throw new CommandError('Unable to load package information from registry.'); + } - let found = false; - for (const versionManifest of versionManifests) { - // Already checked the 'latest' version - if (latestManifest?.version === versionManifest.version) { - continue; - } + // Allow prelease versions if the CLI itself is a prerelease or locally built. + const allowPrereleases = !!prerelease(VERSION.full) || VERSION.full === '0.0.0'; + const potentialVersions = this.#getPotentialVersions(packageMetadata, allowPrereleases); - const conflicts = await this.getPeerDependencyConflicts(versionManifest); - if (conflicts) { - if (options.verbose || rejectionReasons.length < DEFAULT_CONFLICT_DISPLAY_LIMIT) { - rejectionReasons.push(...conflicts); - } - continue; - } + // Heuristic-based search: Check the latest release of each major version first. + const majorVersions = this.#getMajorVersions(potentialVersions); + let found = await this.#findCompatibleVersion(context, majorVersions, { + registry, + verbose, + rejectionReasons, + }); - context.packageIdentifier = npa.resolve(versionManifest.name, versionManifest.version); - found = true; - break; + // Exhaustive search: If no compatible major version is found, fall back to checking all versions. + if (!found) { + const checkedVersions = new Set(majorVersions); + const remainingVersions = potentialVersions.filter((v) => !checkedVersions.has(v)); + found = await this.#findCompatibleVersion(context, remainingVersions, { + registry, + verbose, + rejectionReasons, + }); } if (!found) { - let message = `Unable to find compatible package. Using 'latest' tag.`; + let message = `Unable to find compatible package.`; if (rejectionReasons.length > 0) { message += '\nThis is often because of incompatible peer dependencies.\n' + 'These versions were rejected due to the following conflicts:\n' + rejectionReasons - .slice(0, options.verbose ? undefined : DEFAULT_CONFLICT_DISPLAY_LIMIT) + .slice(0, verbose ? undefined : DEFAULT_CONFLICT_DISPLAY_LIMIT) .map((r) => ` - ${r}`) .join('\n'); } @@ -390,35 +400,82 @@ export default class AddCommandModule } } - #getPotentialVersionManifests( - packageMetadata: PackageMetadata, - allowPrereleases: boolean, - ): PackageManifest[] { - const versionExclusions = packageVersionExclusions[packageMetadata.name]; - const versionManifests = Object.values(packageMetadata.versions).filter( - (value: PackageManifest) => { - // Prerelease versions are not stable and should not be considered by default - if (!allowPrereleases && prerelease(value.version)) { - return false; - } - // Deprecated versions should not be used or considered - if (value.deprecated) { - return false; - } - // Excluded package versions should not be considered - if ( - versionExclusions && - satisfies(value.version, versionExclusions, { includePrerelease: true }) - ) { - return false; + async #findCompatibleVersion( + context: AddCommandTaskContext, + versions: string[], + options: { + registry?: string; + verbose?: boolean; + rejectionReasons: string[]; + }, + ): Promise { + const { packageManager, packageIdentifier } = context; + const { registry, verbose, rejectionReasons } = options; + const packageName = packageIdentifier.name; + assert(packageName, 'Package name must be defined.'); + + for (const version of versions) { + const manifest = await packageManager.getManifest(`${packageName}@${version}`, { + registry, + }); + if (!manifest) { + continue; + } + + const conflicts = await this.getPeerDependencyConflicts(manifest); + if (conflicts) { + if (verbose || rejectionReasons.length < DEFAULT_CONFLICT_DISPLAY_LIMIT) { + rejectionReasons.push(...conflicts); } + continue; + } - return true; - }, - ); + context.packageIdentifier = npa.resolve(manifest.name, manifest.version); + + return manifest; + } + + return null; + } + + #getPotentialVersions(packageMetadata: PackageMetadata, allowPrereleases: boolean): string[] { + const versionExclusions = packageVersionExclusions[packageMetadata.name]; + const latestVersion = packageMetadata['dist-tags']['latest']; + + const versions = Object.values(packageMetadata.versions).filter((version) => { + // Latest tag has already been checked + if (latestVersion && version === latestVersion) { + return false; + } + + // Prerelease versions are not stable and should not be considered by default + if (!allowPrereleases && prerelease(version)) { + return false; + } + + // Excluded package versions should not be considered + if (versionExclusions && satisfies(version, versionExclusions, { includePrerelease: true })) { + return false; + } + + return true; + }); // Sort in reverse SemVer order so that the newest compatible version is chosen - return versionManifests.sort((a, b) => compare(b.version, a.version, true)); + return versions.sort((a, b) => compare(b, a, true)); + } + + #getMajorVersions(versions: string[]): string[] { + const majorVersions = new Map(); + for (const version of versions) { + const major = semver.major(version); + const existing = majorVersions.get(major); + if (!existing || semver.gt(version, existing)) { + majorVersions.set(major, version); + } + } + + return [...majorVersions.values()].sort((a, b) => compare(b, a, true)); } private async loadPackageInfoTask( @@ -426,15 +483,12 @@ export default class AddCommandModule task: AddCommandTaskWrapper, options: Options, ): Promise { - const { logger } = this.context; - const { verbose, registry } = options; + const { registry } = options; let manifest; try { - manifest = await fetchPackageManifest(context.packageIdentifier.toString(), logger, { + manifest = await context.packageManager.getManifest(context.packageIdentifier.toString(), { registry, - verbose, - usingYarn: context.usingYarn, }); } catch (e) { assertIsError(e); @@ -443,6 +497,12 @@ export default class AddCommandModule ); } + if (!manifest) { + throw new CommandError( + `Unable to fetch package information for '${context.packageIdentifier}'.`, + ); + } + context.hasSchematics = !!manifest.schematics; context.savePackage = manifest['ng-add']?.save; context.collectionName = manifest.name; @@ -487,8 +547,8 @@ export default class AddCommandModule task: AddCommandTaskWrapper, options: Options, ): Promise { - const { packageManager } = this.context; const { registry } = options; + const { packageManager, packageIdentifier, savePackage } = context; // Only show if installation will actually occur task.title = 'Installing package'; @@ -498,32 +558,31 @@ export default class AddCommandModule // Temporary packages are located in a different directory // Hence we need to resolve them using the temp path - const { success, tempNodeModules } = await packageManager.installTemp( - context.packageIdentifier.toString(), - registry ? [`--registry="${registry}"`] : undefined, + const { workingDirectory } = await packageManager.acquireTempPackage( + packageIdentifier.toString(), + { + registry, + }, ); - const tempRequire = createRequire(tempNodeModules + '/'); + + const tempRequire = createRequire(workingDirectory + '/'); assert(context.collectionName, 'Collection name should always be available'); const resolvedCollectionPath = tempRequire.resolve( join(context.collectionName, 'package.json'), ); - if (!success) { - throw new CommandError('Unable to install package'); - } - context.collectionName = dirname(resolvedCollectionPath); } else { - const success = await packageManager.install( - context.packageIdentifier.toString(), - context.savePackage, - registry ? [`--registry="${registry}"`] : undefined, - undefined, + await packageManager.add( + packageIdentifier.toString(), + 'none', + savePackage !== 'dependencies', + false, + true, + { + registry, + }, ); - - if (!success) { - throw new CommandError('Unable to install package'); - } } } @@ -631,24 +690,28 @@ export default class AddCommandModule return cachedVersion; } - const { logger, root } = this.context; - let installedPackage; + const { root } = this.context; + let installedPackagePath; try { - installedPackage = this.rootRequire.resolve(join(name, 'package.json')); + installedPackagePath = this.rootRequire.resolve(join(name, 'package.json')); } catch {} - if (installedPackage) { + if (installedPackagePath) { try { - const installed = await fetchPackageManifest(dirname(installedPackage), logger); - this.#projectVersionCache.set(name, installed.version); + const installedPackage = JSON.parse( + await fs.readFile(installedPackagePath, 'utf-8'), + ) as PackageManifest; + this.#projectVersionCache.set(name, installedPackage.version); - return installed.version; + return installedPackage.version; } catch {} } let projectManifest; try { - projectManifest = await fetchPackageManifest(root, logger); + projectManifest = JSON.parse( + await fs.readFile(join(root, 'package.json'), 'utf-8'), + ) as PackageManifest; } catch {} if (projectManifest) { diff --git a/packages/angular/cli/src/commands/mcp/cli.ts b/packages/angular/cli/src/commands/mcp/cli.ts index 0076752ac6f3..091a9064ca7f 100644 --- a/packages/angular/cli/src/commands/mcp/cli.ts +++ b/packages/angular/cli/src/commands/mcp/cli.ts @@ -13,7 +13,7 @@ import { type CommandModuleImplementation, } from '../../command-builder/command-module'; import { isTTY } from '../../utilities/tty'; -import { EXPERIMENTAL_TOOLS, createMcpServer } from './mcp-server'; +import { EXPERIMENTAL_TOOLS, EXPERIMENTAL_TOOL_GROUPS, createMcpServer } from './mcp-server'; const INTERACTIVE_MESSAGE = ` To start using the Angular CLI MCP Server, add this configuration to your host: @@ -54,7 +54,10 @@ export default class McpCommandModule extends CommandModule implements CommandMo alias: 'E', array: true, describe: 'Enable an experimental tool.', - choices: EXPERIMENTAL_TOOLS.map(({ name }) => name), + choices: [ + ...EXPERIMENTAL_TOOLS.map(({ name }) => name), + ...Object.keys(EXPERIMENTAL_TOOL_GROUPS), + ], hidden: true, }); } diff --git a/packages/angular/cli/src/commands/mcp/dev-server.ts b/packages/angular/cli/src/commands/mcp/devserver.ts similarity index 86% rename from packages/angular/cli/src/commands/mcp/dev-server.ts rename to packages/angular/cli/src/commands/mcp/devserver.ts index e6da33aa7bd3..cf8378294edd 100644 --- a/packages/angular/cli/src/commands/mcp/dev-server.ts +++ b/packages/angular/cli/src/commands/mcp/devserver.ts @@ -30,7 +30,7 @@ export type BuildStatus = 'success' | 'failure' | 'unknown'; /** * An Angular development server managed by the MCP server. */ -export interface DevServer { +export interface Devserver { /** * Launches the dev server and returns immediately. * @@ -64,19 +64,19 @@ export interface DevServer { port: number; } -export function devServerKey(project?: string) { +export function devserverKey(project?: string) { return project ?? ''; } /** * A local Angular development server managed by the MCP server. */ -export class LocalDevServer implements DevServer { +export class LocalDevserver implements Devserver { readonly host: Host; readonly port: number; readonly project?: string; - private devServerProcess: ChildProcess | null = null; + private devserverProcess: ChildProcess | null = null; private serverLogs: string[] = []; private buildInProgress = false; private latestBuildLogStartIndex?: number = undefined; @@ -89,7 +89,7 @@ export class LocalDevServer implements DevServer { } start() { - if (this.devServerProcess) { + if (this.devserverProcess) { throw Error('Dev server already started.'); } @@ -100,14 +100,14 @@ export class LocalDevServer implements DevServer { args.push(`--port=${this.port}`); - this.devServerProcess = this.host.spawn('ng', args, { stdio: 'pipe' }); - this.devServerProcess.stdout?.on('data', (data) => { + this.devserverProcess = this.host.spawn('ng', args, { stdio: 'pipe' }); + this.devserverProcess.stdout?.on('data', (data) => { this.addLog(data.toString()); }); - this.devServerProcess.stderr?.on('data', (data) => { + this.devserverProcess.stderr?.on('data', (data) => { this.addLog(data.toString()); }); - this.devServerProcess.stderr?.on('close', () => { + this.devserverProcess.stderr?.on('close', () => { this.stop(); }); this.buildInProgress = true; @@ -127,8 +127,8 @@ export class LocalDevServer implements DevServer { } stop() { - this.devServerProcess?.kill(); - this.devServerProcess = null; + this.devserverProcess?.kill(); + this.devserverProcess = null; } getServerLogs(): string[] { diff --git a/packages/angular/cli/src/commands/mcp/host.ts b/packages/angular/cli/src/commands/mcp/host.ts index 0be6ff67a7fc..1ff0bb9724b3 100644 --- a/packages/angular/cli/src/commands/mcp/host.ts +++ b/packages/angular/cli/src/commands/mcp/host.ts @@ -16,7 +16,8 @@ import { existsSync as nodeExistsSync } from 'fs'; import { ChildProcess, spawn } from 'node:child_process'; import { Stats } from 'node:fs'; -import { stat } from 'node:fs/promises'; +import { glob as nodeGlob, readFile as nodeReadFile, stat } from 'node:fs/promises'; +import { createRequire } from 'node:module'; import { createServer } from 'node:net'; /** @@ -50,6 +51,33 @@ export interface Host { */ existsSync(path: string): boolean; + /** + * Reads a file and returns its content. + * @param path The path to the file. + * @param encoding The encoding to use. + * @returns A promise that resolves to the file content. + */ + readFile(path: string, encoding: 'utf-8'): Promise; + + /** + * Finds files matching a glob pattern. + * @param pattern The glob pattern. + * @param options Options for the glob search. + * @returns An async iterable of file entries. + */ + glob( + pattern: string, + options: { cwd: string }, + ): AsyncIterable<{ name: string; parentPath: string; isFile(): boolean }>; + + /** + * Resolves a module request from a given path. + * @param request The module request to resolve. + * @param from The path from which to resolve the request. + * @returns The resolved module path. + */ + resolveModule(request: string, from: string): string; + /** * Spawns a child process and returns a promise that resolves with the process's * output or rejects with a structured error. @@ -100,6 +128,19 @@ export const LocalWorkspaceHost: Host = { existsSync: nodeExistsSync, + readFile: nodeReadFile, + + glob: function ( + pattern: string, + options: { cwd: string }, + ): AsyncIterable<{ name: string; parentPath: string; isFile(): boolean }> { + return nodeGlob(pattern, { ...options, withFileTypes: true }); + }, + + resolveModule(request: string, from: string): string { + return createRequire(from).resolve(request); + }, + runCommand: async ( command: string, args: readonly string[], diff --git a/packages/angular/cli/src/commands/mcp/mcp-server.ts b/packages/angular/cli/src/commands/mcp/mcp-server.ts index dfcd162a44f7..512398876513 100644 --- a/packages/angular/cli/src/commands/mcp/mcp-server.ts +++ b/packages/angular/cli/src/commands/mcp/mcp-server.ts @@ -10,16 +10,17 @@ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; import { join } from 'node:path'; import type { AngularWorkspace } from '../../utilities/config'; import { VERSION } from '../../utilities/version'; -import type { DevServer } from './dev-server'; +import type { Devserver } from './devserver'; +import { LocalWorkspaceHost } from './host'; import { registerInstructionsResource } from './resources/instructions'; import { AI_TUTOR_TOOL } from './tools/ai-tutor'; import { BEST_PRACTICES_TOOL } from './tools/best-practices'; import { BUILD_TOOL } from './tools/build'; -import { START_DEVSERVER_TOOL } from './tools/devserver/start-devserver'; -import { STOP_DEVSERVER_TOOL } from './tools/devserver/stop-devserver'; -import { WAIT_FOR_DEVSERVER_BUILD_TOOL } from './tools/devserver/wait-for-devserver-build'; +import { DEVSERVER_START_TOOL } from './tools/devserver/devserver-start'; +import { DEVSERVER_STOP_TOOL } from './tools/devserver/devserver-stop'; +import { DEVSERVER_WAIT_FOR_BUILD_TOOL } from './tools/devserver/devserver-wait-for-build'; import { DOC_SEARCH_TOOL } from './tools/doc-search'; -import { FIND_EXAMPLE_TOOL } from './tools/examples'; +import { FIND_EXAMPLE_TOOL } from './tools/examples/index'; import { MODERNIZE_TOOL } from './tools/modernize'; import { ZONELESS_MIGRATION_TOOL } from './tools/onpush-zoneless-migration/zoneless-migration'; import { LIST_PROJECTS_TOOL } from './tools/projects'; @@ -28,7 +29,16 @@ import { type AnyMcpToolDeclaration, registerTools } from './tools/tool-registry /** * Tools to manage devservers. Should be bundled together, then added to experimental or stable as a group. */ -const SERVE_TOOLS = [START_DEVSERVER_TOOL, STOP_DEVSERVER_TOOL, WAIT_FOR_DEVSERVER_BUILD_TOOL]; +const DEVSERVER_TOOLS = [DEVSERVER_START_TOOL, DEVSERVER_STOP_TOOL, DEVSERVER_WAIT_FOR_BUILD_TOOL]; + +/** + * Experimental tools that are grouped together under a single name. + * + * Used for enabling them as a group. + */ +export const EXPERIMENTAL_TOOL_GROUPS = { + 'devserver': DEVSERVER_TOOLS, +}; /** * The set of tools that are enabled by default for the MCP server. @@ -47,7 +57,7 @@ const STABLE_TOOLS = [ * 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 = [BUILD_TOOL, MODERNIZE_TOOL, ...SERVE_TOOLS] as const; +export const EXPERIMENTAL_TOOLS = [BUILD_TOOL, MODERNIZE_TOOL, ...DEVSERVER_TOOLS] as const; export async function createMcpServer( options: { @@ -114,7 +124,8 @@ equivalent actions. workspace: options.workspace, logger, exampleDatabasePath: join(__dirname, '../../../lib/code-examples.db'), - devServers: new Map(), + devservers: new Map(), + host: LocalWorkspaceHost, }, toolDeclarations, ); @@ -146,6 +157,13 @@ export function assembleToolDeclarations( if (process.env['NG_MCP_CODE_EXAMPLES'] === '1') { enabledExperimentalTools.add('find_examples'); } + for (const [toolGroupName, toolGroup] of Object.entries(EXPERIMENTAL_TOOL_GROUPS)) { + if (enabledExperimentalTools.delete(toolGroupName)) { + for (const tool of toolGroup) { + enabledExperimentalTools.add(tool.name); + } + } + } if (enabledExperimentalTools.size > 0) { const experimentalToolsMap = new Map(experimentalDeclarations.map((tool) => [tool.name, tool])); diff --git a/packages/angular/cli/src/commands/mcp/resources/ai-tutor.md b/packages/angular/cli/src/commands/mcp/resources/ai-tutor.md index 7dacf2f525ee..cbe6437e44ac 100644 --- a/packages/angular/cli/src/commands/mcp/resources/ai-tutor.md +++ b/packages/angular/cli/src/commands/mcp/resources/ai-tutor.md @@ -19,6 +19,7 @@ This is your most important principle. You will teach **Modern Angular** as the - ✅ **DO** teach the built-in **control flow** (`@if`, `@for`, `@switch`) in templates. - ✅ **DO** teach the new v20 file naming conventions (e.g., `app.ts` for a component file). - ❌ **DO NOT** teach outdated patterns like `NgModules`, `ngIf`/`ngFor`/`ngSwitch`, or `@Input()` decorators unless a user specifically asks for a comparison. Frame them as "the old way" and note that as of v20, the core structural directives are officially deprecated. +- **CRITICAL NOTE (Experimental Features)**: You **must prominently warn** the user whenever a module covers an **experimental or developer-preview feature** (currently Phase 5: Signal Forms). Emphasize that the API is subject to change. ### 2. The Concept-Example-Exercise-Support Cycle @@ -26,7 +27,7 @@ Your primary teaching method involves guiding the user to solve problems themsel 1. **Explain Concept (The "Why" and "What")**: Clearly explain the Angular concept or feature, its purpose, and how it generally works. The depth of this explanation depends on the user's experience level. -2. **Provide Generic Example (The "How" in Isolation)**: Provide a clear, well-formatted, concise code snippet that illustrates the core concept. **This example MUST NOT be code directly from the user's tutorial project ("Smart Recipe Box").** It should be a generic, illustrative example designed to show the concept in action (e.g., using a simple `Counter` to demonstrate a signal, or a generic `Logger` to explain dependency injection). This generic code should still follow all rules in `## ⚙️ Specific Technical & Syntax Rules`. +2.  **Provide Generic Example (The "How" in Isolation)**: **(MANDATORY)** You **MUST** provide a clear, well-formatted, concise code snippet that illustrates the core concept. **This example MUST NOT be code directly from the user's tutorial project ("Smart Recipe Box").** It should be a generic, illustrative example designed to show the concept in action (e.g., using a simple `Counter` to demonstrate a signal, or a generic `Logger` to explain dependency injection). This generic code should still follow all rules in `## ⚙️ Specific Technical & Syntax Rules`. 3. **Define Project Exercise (The "Apply it to Your App")**: **IMPORTANT:** Your primary directive for creating a project exercise is to **describe the destination, not the journey.** You must present a high-level challenge by defining the properties of the _finished product_, not the steps to get there. @@ -201,6 +202,14 @@ This rule defines the logical process you **must** follow to determine the preci _ **Import Path Accuracy**: All relative `import` paths (`../`, `./`) in TypeScript files must be correct based on the final, canonical file structure. _ **Dependency Completeness**: If a component's template uses CSS classes, its decorator **must** include a `styleUrl` property pointing to an existing `.css` file. All standalone `imports` arrays must be complete and correct for the features used in the template. \* **Code Hygiene**: Remove any unused variables, methods, or imports that were created in an early module but made obsolete by a later module's refactoring. +### 17. Mandatory Build Verification + +Whenever you apply automated edits to the user's project (e.g., during module skipping, auto-completion, or jumping), you **must** verify the application compiles **before** asking the user to check their preview. + +- **Action**: Immediately after writing file changes, run `ng build`. +- **Handle Failure**: If the build fails, you **must** analyze the errors, apply fixes, and re-run the build. Do not return control to the user until the build passes. +- **Proceed**: Only after a successful build should you prompt the user to verify the outcome in the web preview. + --- ## ⚙️ Specific Technical & Syntax Rules @@ -289,6 +298,10 @@ ng generate service _ **`RouterModule`**: Instruct users that they **should NEVER** need to import `RouterModule` into their standalone components. Router directives are globally available via `provideRouter`. - **`RouterLink` and `RouterOutlet` Import**: When a component template uses router directives like `routerLink`, `routerLinkActive`, or ``, you **must** instruct the user to `import` the specific directive class (e.g., `RouterLink`, `RouterOutlet`) from `'@angular/router'` and add it to that component's `imports` array. +### **Application Configuration (app.config.ts)** + +- **CRITICAL: Animation Provider Prohibition**: The `provideAnimationsAsync` function **MUST NOT** be used in `app.config.ts` or any other configuration file. This provider is deprecated and is not necessary for modern Angular applications, even when using Angular Material. **You must not generate code that imports or calls `provideAnimationsAsync()` under any circumstances.** + ### Styling, Layout, and Accessibility - **Layout Guidance (Flexbox vs. Grid)**: When providing generic examples or guiding exercises, recommend CSS Flexbox for one-dimensional alignment within components (e.g., aligning items in a header). @@ -299,6 +312,100 @@ ng generate service - When an exercise involves Material, guide the user to import the specific `Mat...Module` needed for the UI components they are using. - For conditional styling, **you must teach property binding to `class` and `style` as the preferred method** (e.g., `[class.is-active]="isActive()"` or `[style.color]="'red'"`). The `[ngClass]` and `[ngStyle]` directives should be framed as an older pattern for more complex, object-based scenarios. +### Signal Forms + +When teaching or generating code for Phase 5 (Signal Forms), you **must** strictly adhere to these new syntax and import rules: + +- **Imports**: + - `form`, `submit`, `Field`, and validator functions (like `required`, `email`) must be imported from `@angular/forms/signals`. + - **Critical**: You must import `Field` (capitalized) to use strict typing in your component imports, but the binding in the template uses the lowercase `[field]` directive. +- **Definition**: + - Use `protected readonly myForm = form(...)` to create the form group. + - The first argument is the initial model state (e.g., `this.initialData` or a signal). + - The second argument is the validation callback (optional). +- **Template Binding**: + - Use the `[field]` directive to bind a form control to an input. + - **Correct Syntax**: `` (Note: `field` is lowercase here). +- **Submission Logic**: + - Use the `submit()` utility function inside the form's `(submit)` event handler. + - **Syntax**: Add `(submit)="save($event)"` to the `
` element and use `submit(this.myForm, async () => { /* logic */ })` in the handler. + - The handler must call `event.preventDefault()` to prevent the default form submission behavior. +- **Resetting Logic**: + - To reset the form, you must perform two actions: + 1. **Clear Interaction State**: Call `.reset()` on the form signal's value: `this.myForm().reset()`. + 2. **Clear Values**: Update the underlying model signal: `this.myModel.set({ ... })`. +- **Validation Syntax**: + - Import validator functions (`required`, `email`, etc.) directly from `@angular/forms/signals`. + - Apply them inside the definition callback. +- **Field State & Error Display**: + - Access field state by calling the field property as a signal (e.g., `myForm.email()`). + - Check validity using the `.invalid()` signal. + - Retrieve errors using the `.errors()` signal, which returns an array of error objects. + - **Pattern**: + ```html + @if (myForm.email().invalid()) { +
    + @for (error of myForm.email().errors(); track error.kind) { +
  • {{ error.message }}
  • + } +
+ } + ``` +- **Code Example (Standard Pattern)**: + + ```typescript + // src/app/example/example.ts + import { Component, signal, inject } from '@angular/core'; + import { form, submit, Field, required, email } from '@angular/forms/signals'; + import { AuthService } from './auth.service'; + + @Component({ + selector: 'app-example', + standalone: true, + imports: [Field], + template: ` + + + @if (loginForm.email().touched() && loginForm.email().invalid()) { +

+ @for (error of loginForm.email().errors(); track error.kind) { + {{ error.message }} + } +

+ } + + + + +
+ `, + }) + export class Example { + private readonly authService = inject(AuthService); + protected readonly loginModel = signal({ email: '', password: '' }); + + protected readonly loginForm = form(this.loginModel, (s) => { + required(s.email, { message: 'Required' }); + email(s.email, { message: 'Invalid email' }); + }); + + protected async save(event: Event): Promise { + event.preventDefault(); + await submit(this.loginForm, async () => { + await this.authService.login(this.loginForm().value()); + this.loginForm().reset(); + this.loginModel.set({ email: '', password: '' }); + }); + } + } + ``` + +````` + + --- ## 🚀 Onboarding: Project Analysis & Confirmation @@ -429,9 +536,29 @@ _(The LLM will need to interpret "project-specific" or "app-themed" below based _ **16a**: A new component exists with a `ReactiveForm` (using `FormBuilder`, `FormGroup`, `FormControl`). `description`: "building a reactive form to add new items." _ **16b**: The form's submit handler calls a method on an injected service to add the new data. `description`: "adding the new item to the service on form submission." - **Module 17 (Intro to Angular Material)** - _ **17a**: `package.json` contains `@angular/material`. `description`: "installing Angular Material." + _ **17a**: `package.json` contains `@angular/material`. `description`: "installing Angular Material." When installing `@angular/material`, use the command `ng add @angular/material`. Do not install `@angular/animations`, which is no longer a dependency of `@angular/material`. _ **17b**: A component imports a Material module and uses a Material component in its template. `description`: "using an Angular Material component." +### Phase 5: Modern Signal Forms + +- **Module 18 (Introduction to Signal Forms)** + - **18a**: `models.ts` includes `authorEmail` in the `RecipeModel` interface. `description`: "updating the model for new form fields." + - **18b**: A component imports `form` and `Field` from `@angular/forms/signals`. `description`: "importing the Signal Forms API." + - **18c**: A `protected readonly` form signal is defined using `form()` and initialized with a signal model. `description`: "creating the form signal." + - **18d**: The template uses the `[field]` directive on inputs to bind to the form. `description`: "binding inputs to the signal form." +- **Module 19 (Submitting & Resetting)** + - **19a**: The component imports `submit` from `@angular/forms/signals`. `description`: "importing the submit utility." + - **19b**: A save method uses `submit(this.form, ...)` to wrap the submission logic. `description`: "using the submit utility function." + - **19c**: The save method calls the service to add data. `description`: "integrating the service call." + - **19d**: The save method resets the form state using `.reset()` and clears the model values using `.set()`. `description`: "implementing form reset logic." +- **Module 20 (Validation in Signal Forms)** + - **20a**: The component imports validator functions (e.g., `required`, `email`) from `@angular/forms/signals`. `description`: "importing functional validators." + - **20b**: The `form()` definition uses a validation callback. `description`: "defining the validation schema." +- **Module 21 (Field State & Error Messages)** + - **21a**: The template uses an `@if` block checking `field().invalid()` (e.g., `myForm.name().invalid()`). `description`: "checking field invalidity." + - **21b**: Inside the check, an `@for` loop iterates over `field().errors()`. `description`: "iterating over validation errors." + - **21c**: The loop displays the `error.message`. `description`: "displaying specific error messages." + --- ## 🗺️ The Phased Learning Journey @@ -501,7 +628,7 @@ touch src/app/mock-recipes.ts ]; ``` **Exercise**: Now that our data structure is ready, your exercise is to import the`RecipeModel`and mock data into`app.ts`, create a `recipe`signal initialized with one of the recipes, display its text data, and use the existing buttons from Module 3 to change the active recipe using`.set()`. - ```` +````` - **Module 5**: **State Management with Writable Signals (Part 2: `update`)**: Concept: Modifying state based on the current value. Exercise: Create a new `servings` signal of type `number`. Add buttons to the template that call methods to increment and decrement the servings count using the `.update()` method. - **Module 6**: **Computed Signals**: Concept: Deriving state with `computed()`. Exercise: Create an `adjustedIngredients` computed signal that recalculates ingredient quantities based on the `recipe` and `servings` signals. Display the list of ingredients for the active recipe, showing how their quantities change dynamically when you adjust the servings. @@ -625,3 +752,73 @@ touch src/app/mock-recipes.ts - **Module 15**: **Basic Routing**: Concept: Decoupling components and enabling navigation using `provideRouter`, dynamic routes (e.g., `path: 'recipes/:id'`), and the `routerLink` directive. **Exercise**: A major refactoring lesson. Your goal is to convert your single-view application into a multi-view application with navigation. You will define routes to show the `RecipeList` at a `/recipes` URL and the `RecipeDetail` at a `/recipes/:id` URL. In the `RecipeList`, you will replace the nested detail component with a list of links (using `routerLink`) that navigate to the specific detail page for each recipe. Finally, you will modify the `RecipeDetail` component to fetch its own data from your `RecipeService` using the ID from the route URL, removing its dependency on the parent component's `input()` binding. - **Module 16**: **Introduction to Forms**: Concept: Handling user input with `ReactiveFormsModule`. Exercise: Create a new component with a reactive form to add a new recipe. Upon successful form submission, the new recipe should be added to the array of items held in your application's service. - **Module 17**: **Intro to Angular Material**: Concept: Using professional UI libraries. Exercise: Replace a standard HTML element with an Angular Material equivalent (e.g., `MatButton`). + +### Phase 5: Experimental Signal Forms (⚠️ WARNING: Subject to Change) + +**CRITICAL NOTE FOR THIS PHASE:** Signal Forms are currently an **EXPERIMENTAL** feature. The API may change significantly in future Angular releases. Please proceed with the understanding that this section demonstrates a cutting-edge feature. + +- **Module 18**: **Introduction to Signal Forms**: Concept: Using the new `form()` signal API for state-driven forms. **Setup**: **Prerequisite: Angular v21+**. Signal Forms are a feature available starting in Angular v21. Before proceeding, please check your `package.json` or run `ng version`. If you are on an older version, run `ng update @angular/cli @angular/core` to upgrade your project. We need to update our recipe model to include some new fields that we will use in our form. Please update `models.ts` and `mock-recipes.ts` with the code below. + **File: `src/app/models.ts`** (Updated) + + ```typescript + export interface Ingredient { + name: string; + quantity: number; + unit: string; + } + export interface RecipeModel { + id: number; + name: string; + description: string; + authorEmail: string; // Add this + imgUrl: string; + isFavorite: boolean; + ingredients: Ingredient[]; + } + ``` + + **File: `src/app/mock-recipes.ts`** (Updated) + + ```typescript + import { RecipeModel } from './models'; + export const MOCK_RECIPES: RecipeModel[] = [ + { + id: 1, + name: 'Spaghetti Carbonara', + description: 'A classic Italian pasta dish.', + authorEmail: 'mario@italy.com', // Add this + imgUrl: + '[https://via.placeholder.com/300x200.png?text=Spaghetti+Carbonara](https://via.placeholder.com/300x200.png?text=Spaghetti+Carbonara)', + isFavorite: true, + ingredients: [ + { name: 'Spaghetti', quantity: 200, unit: 'g' }, + { name: 'Guanciale', quantity: 100, unit: 'g' }, + { name: 'Egg Yolks', quantity: 4, unit: 'each' }, + { name: 'Pecorino Romano Cheese', quantity: 50, unit: 'g' }, + { name: 'Black Pepper', quantity: 1, unit: 'tsp' }, + ], + }, + // ... (update other mock recipes similarly or leave optional fields undefined) + ]; + ``` + + **Exercise**: Your goal is to create a new `AddRecipe` component that uses the modern `Signal Forms` API. Import `form` and `Field` from `@angular/forms/signals`. Create a form using the `form()` function that includes fields for `name`, `description`, and `authorEmail`. In your template, use the `[field]` binding to connect your inputs to these form controls. + +- **Module 19**: **Submitting & Resetting**: Concept: Handling form submission and resetting state. **Exercise**: Inject the service into your `AddRecipe` component. Create a protected `save()` method triggered by the form's `(submit)` event. Inside this method: + 1. Call `event.preventDefault()` to prevent the default form submission. + 2. Use the `submit(this.myForm, ...)` utility. + 3. Update the `RecipeService` to include an `addRecipe(newRecipe: RecipeModel)` method. + 4. Construct a complete `RecipeModel` (merging form values with defaults) and pass it to the service. + 5. **Reset the form**: Call `this.myForm().reset()` to clear interaction flags. + 6. **Clear the values**: Call `this.myModel.set(...)` to reset the inputs. + +- **Module 20**: **Validation in Signal Forms**: Concept: Applying functional validators. **Exercise**: Import `required` and `email` from `@angular/forms/signals`. Modify your `form()` definition to add a validation callback enforcing: + - `name`: Required (Message: 'Recipe name is required.'). + - `description`: Required (Message: 'Description is required.'). + - `authorEmail`: Required (Message: 'Author email is required.') AND Email format (Message: 'Please enter a valid email address.'). + **Finally, ensure your submit button has `type="submit"`. Note: the `submit()` utility automatically handles validation by marking all fields as touched when submission is attempted.** + +- **Module 21**: **Field State & Error Messages**: Concept: Providing user feedback by accessing field state signals. **Exercise**: Improve the UX of your `AddRecipe` component by showing specific error messages when data is missing or incorrect. In your template, for the `name`, `description`, and `authorEmail` inputs: + 1. Create an `@if` block that checks if the field is `invalid()` (e.g., `myForm.name().invalid()`). + 2. Inside the block, use `@for` to iterate over the field's `.errors()` (use `track error.kind` to identify each error by its type). + 3. Display the `error.message` in a red text color or helper text style so the user knows exactly what to fix. 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/testing/mock-host.ts b/packages/angular/cli/src/commands/mcp/testing/mock-host.ts index 1720b1377792..29f41c24e101 100644 --- a/packages/angular/cli/src/commands/mcp/testing/mock-host.ts +++ b/packages/angular/cli/src/commands/mcp/testing/mock-host.ts @@ -13,9 +13,12 @@ import type { Host } from '../host'; * This class allows spying on host methods and controlling their return values. */ export class MockHost implements Host { - runCommand = jasmine.createSpy('runCommand').and.resolveTo({ stdout: '', stderr: '' }); + runCommand = jasmine.createSpy('runCommand').and.resolveTo({ logs: [] }); stat = jasmine.createSpy('stat'); existsSync = jasmine.createSpy('existsSync'); + readFile = jasmine.createSpy('readFile').and.resolveTo(''); + glob = jasmine.createSpy('glob').and.returnValue((async function* () {})()); + resolveModule = jasmine.createSpy('resolveRequest').and.returnValue('/dev/null'); spawn = jasmine.createSpy('spawn'); getAvailablePort = jasmine.createSpy('getAvailablePort'); } diff --git a/packages/angular/cli/src/commands/mcp/tools/best-practices.ts b/packages/angular/cli/src/commands/mcp/tools/best-practices.ts index 2c72d2175bac..52bf71a8048a 100644 --- a/packages/angular/cli/src/commands/mcp/tools/best-practices.ts +++ b/packages/angular/cli/src/commands/mcp/tools/best-practices.ts @@ -211,7 +211,7 @@ function createBestPracticesHandler({ logger }: McpToolContext) { type: 'text' as const, text: content, annotations: { - audience: ['assistant'], + audience: ['assistant' as const], priority: 0.9, source, }, diff --git a/packages/angular/cli/src/commands/mcp/tools/build.ts b/packages/angular/cli/src/commands/mcp/tools/build.ts index f2618f8e6309..7984fc864dc6 100644 --- a/packages/angular/cli/src/commands/mcp/tools/build.ts +++ b/packages/angular/cli/src/commands/mcp/tools/build.ts @@ -7,7 +7,7 @@ */ import { z } from 'zod'; -import { CommandError, type Host, LocalWorkspaceHost } from '../host'; +import { CommandError, type Host } from '../host'; import { createStructuredContentOutput } from '../utils'; import { type McpToolDeclaration, declareTool } from './tool-registry'; @@ -97,7 +97,7 @@ Perform a one-off, non-watched build using "ng build". Use this tool whenever th * This tool runs "ng build" so it expects to run within an Angular workspace. -* If you want a watched build which updates as files are changed, use "start_devserver" instead, which also serves the app. +* If you want a watched build which updates as files are changed, use "devserver.start" instead, which also serves the app. * You can provide a project instead of building the root one. The "list_projects" MCP tool could be used to obtain the list of projects. * This tool defaults to a development environment while a regular "ng build" defaults to a production environment. An unexpected build failure might suggest the project is not configured for the requested environment. @@ -107,5 +107,5 @@ Perform a one-off, non-watched build using "ng build". Use this tool whenever th isLocalOnly: true, inputSchema: buildToolInputSchema.shape, outputSchema: buildToolOutputSchema.shape, - factory: () => (input) => runBuild(input, LocalWorkspaceHost), + factory: (context) => (input) => runBuild(input, context.host), }); diff --git a/packages/angular/cli/src/commands/mcp/tools/devserver/start-devserver.ts b/packages/angular/cli/src/commands/mcp/tools/devserver/devserver-start.ts similarity index 58% rename from packages/angular/cli/src/commands/mcp/tools/devserver/start-devserver.ts rename to packages/angular/cli/src/commands/mcp/tools/devserver/devserver-start.ts index dbbc4dbd3cfd..574a937fe073 100644 --- a/packages/angular/cli/src/commands/mcp/tools/devserver/start-devserver.ts +++ b/packages/angular/cli/src/commands/mcp/tools/devserver/devserver-start.ts @@ -7,12 +7,11 @@ */ import { z } from 'zod'; -import { LocalDevServer, devServerKey } from '../../dev-server'; -import { type Host, LocalWorkspaceHost } from '../../host'; +import { LocalDevserver, devserverKey } from '../../devserver'; import { createStructuredContentOutput } from '../../utils'; import { type McpToolContext, type McpToolDeclaration, declareTool } from '../tool-registry'; -const startDevServerToolInputSchema = z.object({ +const devserverStartToolInputSchema = z.object({ project: z .string() .optional() @@ -21,9 +20,9 @@ const startDevServerToolInputSchema = z.object({ ), }); -export type StartDevserverToolInput = z.infer; +export type DevserverStartToolInput = z.infer; -const startDevServerToolOutputSchema = z.object({ +const devserverStartToolOutputSchema = z.object({ message: z.string().describe('A message indicating the result of the operation.'), address: z .string() @@ -33,33 +32,29 @@ const startDevServerToolOutputSchema = z.object({ ), }); -export type StartDevserverToolOutput = z.infer; +export type DevserverStartToolOutput = z.infer; function localhostAddress(port: number) { return `http://localhost:${port}/`; } -export async function startDevServer( - input: StartDevserverToolInput, - context: McpToolContext, - host: Host, -) { - const projectKey = devServerKey(input.project); +export async function startDevserver(input: DevserverStartToolInput, context: McpToolContext) { + const projectKey = devserverKey(input.project); - let devServer = context.devServers.get(projectKey); - if (devServer) { + let devserver = context.devservers.get(projectKey); + if (devserver) { return createStructuredContentOutput({ message: `Development server for project '${projectKey}' is already running.`, - address: localhostAddress(devServer.port), + address: localhostAddress(devserver.port), }); } - const port = await host.getAvailablePort(); + const port = await context.host.getAvailablePort(); - devServer = new LocalDevServer({ host, project: input.project, port }); - devServer.start(); + devserver = new LocalDevserver({ host: context.host, project: input.project, port }); + devserver.start(); - context.devServers.set(projectKey, devServer); + context.devservers.set(projectKey, devserver); return createStructuredContentOutput({ message: `Development server for project '${projectKey}' started and watching for workspace changes.`, @@ -67,37 +62,37 @@ export async function startDevServer( }); } -export const START_DEVSERVER_TOOL: McpToolDeclaration< - typeof startDevServerToolInputSchema.shape, - typeof startDevServerToolOutputSchema.shape +export const DEVSERVER_START_TOOL: McpToolDeclaration< + typeof devserverStartToolInputSchema.shape, + typeof devserverStartToolOutputSchema.shape > = declareTool({ - name: 'start_devserver', + name: 'devserver.start', title: 'Start Development Server', description: ` -Starts the Angular development server ("ng serve") as a background process. Follow this up with "wait_for_devserver_build" to wait until +Starts the Angular development server ("ng serve") as a background process. Follow this up with "devserver.wait_for_build" to wait until the first build completes. * **Starting the Server:** Use this tool to begin serving the application. The tool will return immediately while the server runs in the background. -* **Get Initial Build Logs:** Once a dev server has started, use the "wait_for_devserver_build" tool to ensure it's alive. If there are any - build errors, "wait_for_devserver_build" would provide them back and you can give them to the user or rely on them to propose a fix. -* **Get Updated Build Logs:** Important: as long as a devserver is alive (i.e. "stop_devserver" wasn't called), after every time you make a - change to the workspace, re-run "wait_for_devserver_build" to see whether the change was successfully built and wait for the devserver to +* **Get Initial Build Logs:** Once a dev server has started, use the "devserver.wait_for_build" tool to ensure it's alive. If there are any + build errors, "devserver.wait_for_build" would provide them back and you can give them to the user or rely on them to propose a fix. +* **Get Updated Build Logs:** Important: as long as a devserver is alive (i.e. "devserver.stop" wasn't called), after every time you make a + change to the workspace, re-run "devserver.wait_for_build" to see whether the change was successfully built and wait for the devserver to be updated. * This tool manages development servers by itself. It maintains at most a single dev server instance for each project in the monorepo. * This is an asynchronous operation. Subsequent commands can be ran while the server is active. -* Use 'stop_devserver' to gracefully shut down the server and access the full log output. +* Use 'devserver.stop' to gracefully shut down the server and access the full log output. `, isReadOnly: true, isLocalOnly: true, - inputSchema: startDevServerToolInputSchema.shape, - outputSchema: startDevServerToolOutputSchema.shape, + inputSchema: devserverStartToolInputSchema.shape, + outputSchema: devserverStartToolOutputSchema.shape, factory: (context) => (input) => { - return startDevServer(input, context, LocalWorkspaceHost); + return startDevserver(input, context); }, }); diff --git a/packages/angular/cli/src/commands/mcp/tools/devserver/stop-devserver.ts b/packages/angular/cli/src/commands/mcp/tools/devserver/devserver-stop.ts similarity index 69% rename from packages/angular/cli/src/commands/mcp/tools/devserver/stop-devserver.ts rename to packages/angular/cli/src/commands/mcp/tools/devserver/devserver-stop.ts index ed33ff3f0f7d..203cd1770a7d 100644 --- a/packages/angular/cli/src/commands/mcp/tools/devserver/stop-devserver.ts +++ b/packages/angular/cli/src/commands/mcp/tools/devserver/devserver-stop.ts @@ -7,11 +7,11 @@ */ import { z } from 'zod'; -import { devServerKey } from '../../dev-server'; +import { devserverKey } from '../../devserver'; import { createStructuredContentOutput } from '../../utils'; import { type McpToolContext, type McpToolDeclaration, declareTool } from '../tool-registry'; -const stopDevserverToolInputSchema = z.object({ +const devserverStopToolInputSchema = z.object({ project: z .string() .optional() @@ -20,18 +20,18 @@ const stopDevserverToolInputSchema = z.object({ ), }); -export type StopDevserverToolInput = z.infer; +export type DevserverStopToolInput = z.infer; -const stopDevserverToolOutputSchema = z.object({ +const devserverStopToolOutputSchema = z.object({ message: z.string().describe('A message indicating the result of the operation.'), logs: z.array(z.string()).optional().describe('The full logs from the dev server.'), }); -export type StopDevserverToolOutput = z.infer; +export type DevserverStopToolOutput = z.infer; -export function stopDevserver(input: StopDevserverToolInput, context: McpToolContext) { - const projectKey = devServerKey(input.project); - const devServer = context.devServers.get(projectKey); +export function stopDevserver(input: DevserverStopToolInput, context: McpToolContext) { + const projectKey = devserverKey(input.project); + const devServer = context.devservers.get(projectKey); if (!devServer) { return createStructuredContentOutput({ @@ -41,7 +41,7 @@ export function stopDevserver(input: StopDevserverToolInput, context: McpToolCon } devServer.stop(); - context.devServers.delete(projectKey); + context.devservers.delete(projectKey); return createStructuredContentOutput({ message: `Development server for project '${projectKey}' stopped.`, @@ -49,15 +49,15 @@ export function stopDevserver(input: StopDevserverToolInput, context: McpToolCon }); } -export const STOP_DEVSERVER_TOOL: McpToolDeclaration< - typeof stopDevserverToolInputSchema.shape, - typeof stopDevserverToolOutputSchema.shape +export const DEVSERVER_STOP_TOOL: McpToolDeclaration< + typeof devserverStopToolInputSchema.shape, + typeof devserverStopToolOutputSchema.shape > = declareTool({ - name: 'stop_devserver', + name: 'devserver.stop', title: 'Stop Development Server', description: ` -Stops a running Angular development server ("ng serve") that was started with the "start_devserver" tool. +Stops a running Angular development server ("ng serve") that was started with the "devserver.start" tool. * **Stopping the Server:** Use this tool to terminate a running development server and retrieve the logs. @@ -70,8 +70,8 @@ Stops a running Angular development server ("ng serve") that was started with th `, isReadOnly: true, isLocalOnly: true, - inputSchema: stopDevserverToolInputSchema.shape, - outputSchema: stopDevserverToolOutputSchema.shape, + inputSchema: devserverStopToolInputSchema.shape, + outputSchema: devserverStopToolOutputSchema.shape, factory: (context) => (input) => { return stopDevserver(input, context); }, diff --git a/packages/angular/cli/src/commands/mcp/tools/devserver/wait-for-devserver-build.ts b/packages/angular/cli/src/commands/mcp/tools/devserver/devserver-wait-for-build.ts similarity index 67% rename from packages/angular/cli/src/commands/mcp/tools/devserver/wait-for-devserver-build.ts rename to packages/angular/cli/src/commands/mcp/tools/devserver/devserver-wait-for-build.ts index 1a002f4c5b2a..945c38f0c30e 100644 --- a/packages/angular/cli/src/commands/mcp/tools/devserver/wait-for-devserver-build.ts +++ b/packages/angular/cli/src/commands/mcp/tools/devserver/devserver-wait-for-build.ts @@ -7,7 +7,7 @@ */ import { z } from 'zod'; -import { devServerKey } from '../../dev-server'; +import { devserverKey } from '../../devserver'; import { createStructuredContentOutput } from '../../utils'; import { type McpToolContext, type McpToolDeclaration, declareTool } from '../tool-registry'; @@ -21,7 +21,7 @@ export const WATCH_DELAY = 1000; */ const DEFAULT_TIMEOUT = 180_000; // In milliseconds -const waitForDevserverBuildToolInputSchema = z.object({ +const devserverWaitForBuildToolInputSchema = z.object({ project: z .string() .optional() @@ -36,9 +36,9 @@ const waitForDevserverBuildToolInputSchema = z.object({ ), }); -export type WaitForDevserverBuildToolInput = z.infer; +export type DevserverWaitForBuildToolInput = z.infer; -const waitForDevserverBuildToolOutputSchema = z.object({ +const devserverWaitForBuildToolOutputSchema = z.object({ status: z .enum(['success', 'failure', 'unknown', 'timeout', 'no_devserver_found']) .describe( @@ -50,22 +50,22 @@ const waitForDevserverBuildToolOutputSchema = z.object({ .describe('The logs from the most recent build, if one exists.'), }); -export type WaitForDevserverBuildToolOutput = z.infer; +export type DevserverWaitForBuildToolOutput = z.infer; function wait(ms: number): Promise { return new Promise((resolve) => setTimeout(resolve, ms)); } export async function waitForDevserverBuild( - input: WaitForDevserverBuildToolInput, + input: DevserverWaitForBuildToolInput, context: McpToolContext, ) { - const projectKey = devServerKey(input.project); - const devServer = context.devServers.get(projectKey); + const projectKey = devserverKey(input.project); + const devServer = context.devservers.get(projectKey); const deadline = Date.now() + input.timeout; if (!devServer) { - return createStructuredContentOutput({ + return createStructuredContentOutput({ status: 'no_devserver_found', }); } @@ -73,49 +73,49 @@ export async function waitForDevserverBuild( await wait(WATCH_DELAY); while (devServer.isBuilding()) { if (Date.now() > deadline) { - return createStructuredContentOutput({ + return createStructuredContentOutput({ status: 'timeout', }); } await wait(WATCH_DELAY); } - return createStructuredContentOutput({ + return createStructuredContentOutput({ ...devServer.getMostRecentBuild(), }); } -export const WAIT_FOR_DEVSERVER_BUILD_TOOL: McpToolDeclaration< - typeof waitForDevserverBuildToolInputSchema.shape, - typeof waitForDevserverBuildToolOutputSchema.shape +export const DEVSERVER_WAIT_FOR_BUILD_TOOL: McpToolDeclaration< + typeof devserverWaitForBuildToolInputSchema.shape, + typeof devserverWaitForBuildToolOutputSchema.shape > = declareTool({ - name: 'wait_for_devserver_build', + name: 'devserver.wait_for_build', title: 'Wait for Devserver Build', description: ` -Waits for a dev server that was started with the "start_devserver" tool to complete its build, then reports the build logs from its most +Waits for a dev server that was started with the "devserver.start" tool to complete its build, then reports the build logs from its most recent build. -* **Waiting for a build:** As long as a devserver is alive ("start_devserver" was called for this project and "stop_devserver" wasn't +* **Waiting for a build:** As long as a devserver is alive ("devserver.start" was called for this project and "devserver.stop" wasn't called yet), then if you're making a file change and want to ensure it was successfully built, call this tool instead of any other build tool or command. When it retuns you'll get build logs back **and** you'll know the user's devserver is up-to-date with the latest changes. -* This tool expects that a dev server was launched on the same project with the "start_devserver" tool, otherwise a "no_devserver_found" +* This tool expects that a dev server was launched on the same project with the "devserver.start" tool, otherwise a "no_devserver_found" status will be returned. * This tool will block until the build is complete or the timeout is reached. If you expect a long build process, consider increasing the - timeout. Timeouts on initial run (right after "start_devserver" calls) or after a big change are not necessarily indicative of an error. + timeout. Timeouts on initial run (right after "devserver.start" calls) or after a big change are not necessarily indicative of an error. * If you encountered a timeout and it might be reasonable, just call this tool again. * If the dev server is not building, it will return quickly, with the logs from the last build. -* A 'no_devserver_found' status can indicate the underlying server was stopped for some reason. Try first to call the "start_devserver" +* A 'no_devserver_found' status can indicate the underlying server was stopped for some reason. Try first to call the "devserver.start" tool again, before giving up. `, isReadOnly: true, isLocalOnly: true, - inputSchema: waitForDevserverBuildToolInputSchema.shape, - outputSchema: waitForDevserverBuildToolOutputSchema.shape, + inputSchema: devserverWaitForBuildToolInputSchema.shape, + outputSchema: devserverWaitForBuildToolOutputSchema.shape, factory: (context) => (input) => { return waitForDevserverBuild(input, context); }, diff --git a/packages/angular/cli/src/commands/mcp/tools/devserver/serve_spec.ts b/packages/angular/cli/src/commands/mcp/tools/devserver/devserver_spec.ts similarity index 88% rename from packages/angular/cli/src/commands/mcp/tools/devserver/serve_spec.ts rename to packages/angular/cli/src/commands/mcp/tools/devserver/devserver_spec.ts index 7b9ce0f10a08..ea6449ceeffa 100644 --- a/packages/angular/cli/src/commands/mcp/tools/devserver/serve_spec.ts +++ b/packages/angular/cli/src/commands/mcp/tools/devserver/devserver_spec.ts @@ -10,9 +10,9 @@ import { EventEmitter } from 'events'; import type { ChildProcess } from 'node:child_process'; import type { MockHost } from '../../testing/mock-host'; import type { McpToolContext } from '../tool-registry'; -import { startDevServer } from './start-devserver'; -import { stopDevserver } from './stop-devserver'; -import { WATCH_DELAY, waitForDevserverBuild } from './wait-for-devserver-build'; +import { startDevserver } from './devserver-start'; +import { stopDevserver } from './devserver-stop'; +import { WATCH_DELAY, waitForDevserverBuild } from './devserver-wait-for-build'; class MockChildProcess extends EventEmitter { stdout = new EventEmitter(); @@ -37,12 +37,13 @@ describe('Serve Tools', () => { } as MockHost; mockContext = { - devServers: new Map(), + devservers: new Map(), + host: mockHost, } as Partial as McpToolContext; }); it('should start and stop a dev server', async () => { - const startResult = await startDevServer({}, mockContext, mockHost); + const startResult = await startDevserver({}, mockContext); expect(startResult.structuredContent.message).toBe( `Development server for project '' started and watching for workspace changes.`, ); @@ -56,7 +57,7 @@ describe('Serve Tools', () => { }); it('should wait for a build to complete', async () => { - await startDevServer({}, mockContext, mockHost); + await startDevserver({}, mockContext); const waitPromise = waitForDevserverBuild({ timeout: 10 }, mockContext); @@ -78,7 +79,7 @@ describe('Serve Tools', () => { it('should handle multiple dev servers', async () => { // Start server for project 1. This uses the basic mockProcess created for the tests. - const startResult1 = await startDevServer({ project: 'app-one' }, mockContext, mockHost); + const startResult1 = await startDevserver({ project: 'app-one' }, mockContext); expect(startResult1.structuredContent.message).toBe( `Development server for project 'app-one' started and watching for workspace changes.`, ); @@ -87,7 +88,7 @@ describe('Serve Tools', () => { // Start server for project 2, returning a new mock process. const process2 = new MockChildProcess(); mockHost.spawn.and.returnValue(process2 as unknown as ChildProcess); - const startResult2 = await startDevServer({ project: 'app-two' }, mockContext, mockHost); + const startResult2 = await startDevserver({ project: 'app-two' }, mockContext); expect(startResult2.structuredContent.message).toBe( `Development server for project 'app-two' started and watching for workspace changes.`, ); @@ -116,7 +117,7 @@ describe('Serve Tools', () => { }); it('should handle server crash', async () => { - await startDevServer({ project: 'crash-app' }, mockContext, mockHost); + await startDevserver({ project: 'crash-app' }, mockContext); // Simulate a crash with exit code 1 mockProcess.stdout.emit('data', 'Fatal error.'); @@ -128,7 +129,7 @@ describe('Serve Tools', () => { }); it('wait should timeout if build takes too long', async () => { - await startDevServer({ project: 'timeout-app' }, mockContext, mockHost); + await startDevserver({ project: 'timeout-app' }, mockContext); const waitResult = await waitForDevserverBuild( { project: 'timeout-app', timeout: 10 }, mockContext, @@ -139,7 +140,7 @@ describe('Serve Tools', () => { it('should wait through multiple cycles for a build to complete', async () => { jasmine.clock().install(); try { - await startDevServer({}, mockContext, mockHost); + await startDevserver({}, mockContext); // Immediately simulate a build starting so isBuilding() is true. mockProcess.stdout.emit('data', '❯ Changes detected. Rebuilding...'); diff --git a/packages/angular/cli/src/commands/mcp/tools/doc-search.ts b/packages/angular/cli/src/commands/mcp/tools/doc-search.ts index 7d0f1bd92ab5..385f0d00d6a4 100644 --- a/packages/angular/cli/src/commands/mcp/tools/doc-search.ts +++ b/packages/angular/cli/src/commands/mcp/tools/doc-search.ts @@ -176,7 +176,7 @@ function createDocSearchHandler({ logger }: McpToolContext) { const textContent: { type: 'text'; text: string; - annotations?: { audience: string[]; priority: number }; + annotations?: { audience: Array<'user' | 'assistant'>; priority: number }; }[] = [ { type: 'text' as const, diff --git a/packages/angular/cli/src/commands/mcp/tools/examples.ts b/packages/angular/cli/src/commands/mcp/tools/examples.ts deleted file mode 100644 index b05b2b4edf97..000000000000 --- a/packages/angular/cli/src/commands/mcp/tools/examples.ts +++ /dev/null @@ -1,755 +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 { glob, readFile, stat } from 'node:fs/promises'; -import { createRequire } from 'node:module'; -import { dirname, isAbsolute, join, relative, resolve } from 'node:path'; -import type { DatabaseSync, SQLInputValue } from 'node:sqlite'; -import { z } from 'zod'; -import { type McpToolContext, declareTool } from './tool-registry'; - -const findExampleInputSchema = z.object({ - workspacePath: z - .string() - .optional() - .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.', - ), - query: z - .string() - .describe( - `The primary, conceptual search query. This should capture the user's main goal or question ` + - `(e.g., 'lazy loading a route' or 'how to use signal inputs'). The query will be processed ` + - 'by a powerful full-text search engine.\n\n' + - 'Key Syntax Features (see https://www.sqlite.org/fts5.html for full documentation):\n' + - ' - AND (default): Space-separated terms are combined with AND.\n' + - ' - Example: \'standalone component\' (finds results with both "standalone" and "component")\n' + - ' - OR: Use the OR operator to find results with either term.\n' + - " - Example: 'validation OR validator'\n" + - ' - NOT: Use the NOT operator to exclude terms.\n' + - " - Example: 'forms NOT reactive'\n" + - ' - Grouping: Use parentheses () to group expressions.\n' + - " - Example: '(validation OR validator) AND forms'\n" + - ' - Phrase Search: Use double quotes "" for exact phrases.\n' + - ' - Example: \'"template-driven forms"\'\n' + - ' - Prefix Search: Use an asterisk * for prefix matching.\n' + - ' - Example: \'rout*\' (matches "route", "router", "routing")', - ), - keywords: z - .array(z.string()) - .optional() - .describe( - 'A list of specific, exact keywords to narrow the search. Use this for precise terms like ' + - 'API names, function names, or decorators (e.g., `ngFor`, `trackBy`, `inject`).', - ), - required_packages: z - .array(z.string()) - .optional() - .describe( - "A list of NPM packages that an example must use. Use this when the user's request is " + - 'specific to a feature within a certain package (e.g., if the user asks about `ngModel`, ' + - 'you should filter by `@angular/forms`).', - ), - related_concepts: z - .array(z.string()) - .optional() - .describe( - 'A list of high-level concepts to filter by. Use this to find examples related to broader ' + - 'architectural ideas or patterns (e.g., `signals`, `dependency injection`, `routing`).', - ), - includeExperimental: z - .boolean() - .optional() - .default(false) - .describe( - 'By default, this tool returns only production-safe examples. Set this to `true` **only if** ' + - 'the user explicitly asks for a bleeding-edge feature or if a stable solution to their ' + - 'problem cannot be found. If you set this to `true`, you **MUST** preface your answer by ' + - 'warning the user that the example uses experimental APIs that are not suitable for production.', - ), -}); - -type FindExampleInput = z.infer; - -const findExampleOutputSchema = z.object({ - examples: z.array( - z.object({ - title: z - .string() - .describe( - 'The title of the example. Use this as a heading when presenting the example to the user.', - ), - summary: z - .string() - .describe( - "A one-sentence summary of the example's purpose. Use this to help the user decide " + - 'if the example is relevant to them.', - ), - keywords: z - .array(z.string()) - .optional() - .describe( - 'A list of keywords for the example. You can use these to explain why this example ' + - "was a good match for the user's query.", - ), - required_packages: z - .array(z.string()) - .optional() - .describe( - 'A list of NPM packages required for the example to work. Before presenting the code, ' + - 'you should inform the user if any of these packages need to be installed.', - ), - related_concepts: z - .array(z.string()) - .optional() - .describe( - 'A list of related concepts. You can suggest these to the user as topics for ' + - 'follow-up questions.', - ), - related_tools: z - .array(z.string()) - .optional() - .describe( - 'A list of related MCP tools. You can suggest these as potential next steps for the user.', - ), - content: z - .string() - .describe( - 'A complete, self-contained Angular code example in Markdown format. This should be ' + - 'presented to the user inside a markdown code block.', - ), - snippet: z - .string() - .optional() - .describe( - 'A contextual snippet from the content showing the matched search term. This field is ' + - 'critical for efficiently evaluating a result`s relevance. It enables two primary ' + - 'workflows:\n\n' + - '1. For direct questions: You can internally review snippets to select the single best ' + - 'result before generating a comprehensive answer from its full `content`.\n' + - '2. For ambiguous or exploratory questions: You can present a summary of titles and ' + - 'snippets to the user, allowing them to guide the next step.', - ), - }), - ), -}); - -export const FIND_EXAMPLE_TOOL = declareTool({ - name: 'find_examples', - title: 'Find Angular Code Examples', - description: ` - -Augments your knowledge base with a curated database of official, best-practice code examples, -focusing on **modern, new, and recently updated** Angular features. This tool acts as a RAG -(Retrieval-Augmented Generation) source, providing ground-truth information on the latest Angular -APIs and patterns. You **MUST** use it to understand and apply current standards when working with -new or evolving features. - - -* **Knowledge Augmentation:** Learning about new or updated Angular features (e.g., query: 'signal input' or 'deferrable views'). -* **Modern Implementation:** Finding the correct modern syntax for features - (e.g., query: 'functional route guard' or 'http client with fetch'). -* **Refactoring to Modern Patterns:** Upgrading older code by finding examples of new syntax - (e.g., query: 'built-in control flow' to replace "*ngIf"). -* **Advanced Filtering:** Combining a full-text search with filters to narrow results. - (e.g., query: 'forms', required_packages: ['@angular/forms'], keywords: ['validation']) - - -* **Project-Specific Use (Recommended):** For tasks inside a user's project, you **MUST** provide the - \`workspacePath\` argument to get examples that match the project's Angular version. Get this - path from \`list_projects\`. -* **General Use:** If no project context is available (e.g., for general questions or learning), - you can call the tool without the \`workspacePath\` argument. It will return the latest - generic examples. -* **Tool Selection:** This database primarily contains examples for new and recently updated Angular - features. For established, core features, the main documentation (via the - \`search_documentation\` tool) may be a better source of information. -* The examples in this database are the single source of truth for modern Angular coding patterns. -* The search query uses a powerful full-text search syntax (FTS5). Refer to the 'query' - parameter description for detailed syntax rules and examples. -* You can combine the main 'query' with optional filters like 'keywords', 'required_packages', - and 'related_concepts' to create highly specific searches. -`, - inputSchema: findExampleInputSchema.shape, - outputSchema: findExampleOutputSchema.shape, - isReadOnly: true, - isLocalOnly: true, - shouldRegister: ({ logger }) => { - // sqlite database support requires Node.js 22.16+ - const [nodeMajor, nodeMinor] = process.versions.node.split('.', 2).map(Number); - if (nodeMajor < 22 || (nodeMajor === 22 && nodeMinor < 16)) { - logger.warn( - `MCP tool 'find_examples' requires Node.js 22.16 (or higher). ` + - ' Registration of this tool has been skipped.', - ); - - return false; - } - - return true; - }, - factory: createFindExampleHandler, -}); - -/** - * 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 - * { - * "angular": { - * "examples": { - * "format": "sqlite", - * "path": "./resources/code-examples.db" - * } - * } - * } - * ``` - * - * @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 array of objects, each containing a database path and source. - */ -async function getVersionSpecificExampleDatabases( - workspacePath: string, - logger: McpToolContext['logger'], -): 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 = dirname(pkgJsonPath); - const dbPath = resolve(packageDirectory, examplesInfo.path); - - // Ensure the resolved database path is within the package boundary. - const relativePath = relative(packageDirectory, dbPath); - if (relativePath.startsWith('..') || 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.', - ); - continue; - } - - const source = `package ${packageName}@${pkgJson.version}`; - databases.push({ dbPath, source }); - } - } catch (e) { - logger.warn( - `Failed to read or parse version-specific examples metadata referenced in '${pkgJsonPath}': ${ - e instanceof Error ? e.message : e - }.`, - ); - } - } - - return databases; -} - -async function createFindExampleHandler({ logger, exampleDatabasePath }: McpToolContext) { - const runtimeDb = process.env['NG_MCP_EXAMPLES_DIR'] - ? await setupRuntimeExamples(process.env['NG_MCP_EXAMPLES_DIR']) - : undefined; - - suppressSqliteWarning(); - - return async (input: FindExampleInput) => { - // If the dev-time override is present, use it and bypass all other logic. - if (runtimeDb) { - return queryDatabase([runtimeDb], input); - } - - const resolvedDbs: { path: string; source: string }[] = []; - - // First, try to get all available version-specific guides. - if (input.workspacePath) { - const versionSpecificDbs = await getVersionSpecificExampleDatabases( - input.workspacePath, - logger, - ); - for (const db of versionSpecificDbs) { - resolvedDbs.push({ path: db.dbPath, source: db.source }); - } - } - - // 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 (resolvedDbs.length === 0) { - // This should be prevented by the registration logic in mcp-server.ts - throw new Error('No example databases are available.'); - } - - const { DatabaseSync } = await import('node:sqlite'); - 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; - } - } - - if (dbConnections.length === 0) { - throw new Error('All available example databases were invalid. Cannot perform query.'); - } - - return queryDatabase(dbConnections, input); - }; -} - -function queryDatabase(dbs: DatabaseSync[], input: FindExampleInput) { - const { query, keywords, required_packages, related_concepts, includeExperimental } = input; - - // Build the query dynamically - const params: SQLInputValue[] = []; - let sql = - `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, " + - // 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 = []; - - // FTS query - if (query) { - whereClauses.push('examples_fts MATCH ?'); - params.push(escapeSearchQuery(query)); - } - - // JSON array filters - const addJsonFilter = (column: string, values: string[] | undefined) => { - if (values?.length) { - for (const value of values) { - whereClauses.push(`e.${column} LIKE ?`); - params.push(`%"${value}"%`); - } - } - }; - - addJsonFilter('keywords', keywords); - addJsonFilter('required_packages', required_packages); - addJsonFilter('related_concepts', related_concepts); - - if (!includeExperimental) { - whereClauses.push('e.experimental = 0'); - } - - if (whereClauses.length > 0) { - sql += ` WHERE ${whereClauses.join(' AND ')}`; - } - - // Query database and return results - const examples = []; - const textContent = []; - - 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) { - text += `\n**Snippet:** ${example.snippet}`; - } - text += `\n\n---\n\n${example.content}`; - textContent.push({ type: 'text' as const, text }); - } - - return { - content: textContent, - structuredContent: { examples: finalExamples }, - }; -} - -/** - * Escapes a search query for FTS5 by tokenizing and quoting terms. - * - * This function processes a raw search string and prepares it for an FTS5 full-text search. - * It correctly handles quoted phrases, logical operators (AND, OR, NOT), parentheses, - * and prefix searches (ending with an asterisk), ensuring that individual search - * terms are properly quoted to be treated as literals by the search engine. - * This is primarily intended to avoid unintentional usage of FTS5 query syntax by consumers. - * - * @param query The raw search query string. - * @returns A sanitized query string suitable for FTS5. - */ -export function escapeSearchQuery(query: string): string { - // This regex tokenizes the query string into parts: - // 1. Quoted phrases (e.g., "foo bar") - // 2. Parentheses ( and ) - // 3. FTS5 operators (AND, OR, NOT, NEAR) - // 4. Words, which can include a trailing asterisk for prefix search (e.g., foo*) - const tokenizer = /"([^"]*)"|([()])|\b(AND|OR|NOT|NEAR)\b|([^\s()]+)/g; - let match; - const result: string[] = []; - let lastIndex = 0; - - while ((match = tokenizer.exec(query)) !== null) { - // Add any whitespace or other characters between tokens - if (match.index > lastIndex) { - result.push(query.substring(lastIndex, match.index)); - } - - const [, quoted, parenthesis, operator, term] = match; - - if (quoted !== undefined) { - // It's a quoted phrase, keep it as is. - result.push(`"${quoted}"`); - } else if (parenthesis) { - // It's a parenthesis, keep it as is. - result.push(parenthesis); - } else if (operator) { - // It's an operator, keep it as is. - result.push(operator); - } else if (term) { - // It's a term that needs to be quoted. - if (term.endsWith('*')) { - result.push(`"${term.slice(0, -1)}"*`); - } else { - result.push(`"${term}"`); - } - } - lastIndex = tokenizer.lastIndex; - } - - // Add any remaining part of the string - if (lastIndex < query.length) { - result.push(query.substring(lastIndex)); - } - - return result.join(''); -} - -/** - * Suppresses the experimental warning emitted by Node.js for the `node:sqlite` module. - * - * This is a workaround to prevent the console from being cluttered with warnings - * about the experimental status of the SQLite module, which is used by this tool. - */ -function suppressSqliteWarning() { - const originalProcessEmit = process.emit; - // eslint-disable-next-line @typescript-eslint/no-explicit-any - process.emit = function (event: string, error?: unknown): any { - if ( - event === 'warning' && - error instanceof Error && - error.name === 'ExperimentalWarning' && - error.message.includes('SQLite') - ) { - return false; - } - - // eslint-disable-next-line @typescript-eslint/no-explicit-any, prefer-rest-params - return originalProcessEmit.apply(process, arguments as any); - }; -} - -/** - * A simple YAML front matter parser. - * - * This function extracts the YAML block enclosed by `---` at the beginning of a string - * and parses it into a JavaScript object. It is not a full YAML parser and only - * supports simple key-value pairs and string arrays. - * - * @param content The string content to parse. - * @returns A record containing the parsed front matter data. - */ -function parseFrontmatter(content: string): Record { - const match = content.match(/^---\r?\n(.*?)\r?\n---/s); - if (!match) { - return {}; - } - - const frontmatter = match[1]; - const data: Record = {}; - const lines = frontmatter.split(/\r?\n/); - - let currentKey = ''; - let isArray = false; - const arrayValues: string[] = []; - - for (const line of lines) { - const keyValueMatch = line.match(/^([^:]+):\s*(.*)/); - if (keyValueMatch) { - if (currentKey && isArray) { - data[currentKey] = arrayValues.slice(); - arrayValues.length = 0; - } - - const [, key, value] = keyValueMatch; - currentKey = key.trim(); - isArray = value.trim() === ''; - - if (!isArray) { - const trimmedValue = value.trim(); - if (trimmedValue === 'true') { - data[currentKey] = true; - } else if (trimmedValue === 'false') { - data[currentKey] = false; - } else { - data[currentKey] = trimmedValue; - } - } - } else { - const arrayItemMatch = line.match(/^\s*-\s*(.*)/); - if (arrayItemMatch && currentKey && isArray) { - let value = arrayItemMatch[1].trim(); - // Unquote if the value is quoted. - if ( - (value.startsWith("'") && value.endsWith("'")) || - (value.startsWith('"') && value.endsWith('"')) - ) { - value = value.slice(1, -1); - } - arrayValues.push(value); - } - } - } - - if (currentKey && isArray) { - data[currentKey] = arrayValues; - } - - return data; -} - -async function setupRuntimeExamples(examplesPath: string): Promise { - const { DatabaseSync } = await import('node:sqlite'); - 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, - title TEXT NOT NULL, - summary TEXT NOT NULL, - keywords TEXT, - required_packages TEXT, - related_concepts TEXT, - related_tools TEXT, - experimental INTEGER NOT NULL DEFAULT 0, - content TEXT NOT NULL - ); - `); - - // Create an FTS5 virtual table to provide full-text search capabilities. - db.exec(` - CREATE VIRTUAL TABLE examples_fts USING fts5( - title, - summary, - keywords, - required_packages, - related_concepts, - related_tools, - content, - content='examples', - content_rowid='id', - tokenize = 'porter ascii' - ); - `); - - // Create triggers to keep the FTS table synchronized with the examples table. - db.exec(` - CREATE TRIGGER examples_after_insert AFTER INSERT ON examples BEGIN - INSERT INTO examples_fts(rowid, title, summary, keywords, required_packages, related_concepts, related_tools, content) - VALUES ( - new.id, new.title, new.summary, new.keywords, new.required_packages, new.related_concepts, - new.related_tools, new.content - ); - END; - `); - - const insertStatement = db.prepare( - 'INSERT INTO examples(' + - 'title, summary, keywords, required_packages, related_concepts, related_tools, experimental, content' + - ') VALUES(?, ?, ?, ?, ?, ?, ?, ?);', - ); - - const frontmatterSchema = z.object({ - title: z.string(), - summary: z.string(), - keywords: z.array(z.string()).optional(), - required_packages: z.array(z.string()).optional(), - related_concepts: z.array(z.string()).optional(), - related_tools: z.array(z.string()).optional(), - experimental: z.boolean().optional(), - }); - - db.exec('BEGIN TRANSACTION'); - for await (const entry of glob('**/*.md', { cwd: examplesPath, withFileTypes: true })) { - if (!entry.isFile()) { - continue; - } - - const content = await readFile(join(entry.parentPath, entry.name), 'utf-8'); - const frontmatter = parseFrontmatter(content); - - const validation = frontmatterSchema.safeParse(frontmatter); - if (!validation.success) { - // eslint-disable-next-line no-console - console.warn(`Skipping invalid example file ${entry.name}:`, validation.error.issues); - continue; - } - - const { - title, - summary, - keywords, - required_packages, - related_concepts, - related_tools, - experimental, - } = validation.data; - - insertStatement.run( - title, - summary, - JSON.stringify(keywords ?? []), - JSON.stringify(required_packages ?? []), - JSON.stringify(related_concepts ?? []), - JSON.stringify(related_tools ?? []), - experimental ? 1 : 0, - content, - ); - } - db.exec('END TRANSACTION'); - - 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/examples/database-discovery.ts b/packages/angular/cli/src/commands/mcp/tools/examples/database-discovery.ts new file mode 100644 index 000000000000..aeab96e15871 --- /dev/null +++ b/packages/angular/cli/src/commands/mcp/tools/examples/database-discovery.ts @@ -0,0 +1,106 @@ +/** + * @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 { dirname, isAbsolute, relative, resolve } from 'node:path'; +import type { McpToolContext } from '../tool-registry'; + +/** + * 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 + * { + * "angular": { + * "examples": { + * "format": "sqlite", + * "path": "./resources/code-examples.db" + * } + * } + * } + * ``` + * + * @param workspacePath The absolute path to the user's `angular.json` file. + * @param logger The MCP tool context logger for reporting warnings. + * @param host The host interface for file system and module resolution operations. + * @returns A promise that resolves to an array of objects, each containing a database path and source. + */ +export async function getVersionSpecificExampleDatabases( + workspacePath: string, + logger: McpToolContext['logger'], + host: McpToolContext['host'], +): Promise<{ dbPath: string; source: string }[]> { + 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 = host.resolveModule(`${packageName}/package.json`, workspacePath); + } 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 host.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 = dirname(pkgJsonPath); + const dbPath = resolve(packageDirectory, examplesInfo.path); + + // Ensure the resolved database path is within the package boundary. + const relativePath = relative(packageDirectory, dbPath); + if (relativePath.startsWith('..') || 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 host.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; + } + + const source = `package ${packageName}@${pkgJson.version}`; + databases.push({ dbPath, source }); + } + } catch (e) { + logger.warn( + `Failed to read or parse version-specific examples metadata referenced in '${pkgJsonPath}': ${ + e instanceof Error ? e.message : e + }.`, + ); + } + } + + return databases; +} diff --git a/packages/angular/cli/src/commands/mcp/tools/examples/database-discovery_spec.ts b/packages/angular/cli/src/commands/mcp/tools/examples/database-discovery_spec.ts new file mode 100644 index 000000000000..0d5680d01c06 --- /dev/null +++ b/packages/angular/cli/src/commands/mcp/tools/examples/database-discovery_spec.ts @@ -0,0 +1,137 @@ +/** + * @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 type { Stats } from 'node:fs'; +import { Host } from '../../host'; +import { getVersionSpecificExampleDatabases } from './database-discovery'; + +describe('getVersionSpecificExampleDatabases', () => { + let mockHost: jasmine.SpyObj; + let mockLogger: { warn: jasmine.Spy }; + + beforeEach(() => { + mockHost = jasmine.createSpyObj('Host', ['resolveModule', 'readFile', 'stat']); + mockLogger = { + warn: jasmine.createSpy('warn'), + }; + }); + + it('should find a valid example database from a package', async () => { + mockHost.resolveModule.and.callFake((specifier) => { + if (specifier === '@angular/core/package.json') { + return '/path/to/node_modules/@angular/core/package.json'; + } + throw new Error(`Unexpected module specifier: ${specifier}`); + }); + mockHost.readFile.and.resolveTo( + JSON.stringify({ + name: '@angular/core', + version: '18.1.0', + angular: { + examples: { + format: 'sqlite', + path: './resources/code-examples.db', + }, + }, + }), + ); + mockHost.stat.and.resolveTo({ size: 1024 } as Stats); + + const databases = await getVersionSpecificExampleDatabases( + '/path/to/workspace', + mockLogger, + mockHost, + ); + + expect(databases.length).toBe(1); + expect(databases[0].dbPath).toBe( + '/path/to/node_modules/@angular/core/resources/code-examples.db', + ); + expect(databases[0].source).toBe('package @angular/core@18.1.0'); + expect(mockLogger.warn).not.toHaveBeenCalled(); + }); + + it('should skip packages without angular.examples metadata', async () => { + mockHost.resolveModule.and.returnValue('/path/to/node_modules/@angular/core/package.json'); + mockHost.readFile.and.resolveTo(JSON.stringify({ name: '@angular/core', version: '18.1.0' })); + + const databases = await getVersionSpecificExampleDatabases( + '/path/to/workspace', + mockLogger, + mockHost, + ); + + expect(databases.length).toBe(0); + }); + + it('should handle packages that are not found', async () => { + mockHost.resolveModule.and.throwError(new Error('Cannot find module')); + + const databases = await getVersionSpecificExampleDatabases( + '/path/to/workspace', + mockLogger, + mockHost, + ); + + expect(databases.length).toBe(0); + expect(mockLogger.warn).not.toHaveBeenCalled(); + }); + + it('should reject database paths that attempt path traversal', async () => { + mockHost.resolveModule.and.returnValue('/path/to/node_modules/@angular/core/package.json'); + mockHost.readFile.and.resolveTo( + JSON.stringify({ + name: '@angular/core', + version: '18.1.0', + angular: { + examples: { + format: 'sqlite', + path: '../outside-package/danger.db', + }, + }, + }), + ); + + const databases = await getVersionSpecificExampleDatabases( + '/path/to/workspace', + mockLogger, + mockHost, + ); + + expect(databases.length).toBe(0); + expect(mockLogger.warn).toHaveBeenCalledWith( + jasmine.stringMatching(/Detected a potential path traversal attempt/), + ); + }); + + it('should skip database files larger than 10MB', async () => { + mockHost.resolveModule.and.returnValue('/path/to/node_modules/@angular/core/package.json'); + mockHost.readFile.and.resolveTo( + JSON.stringify({ + name: '@angular/core', + version: '18.1.0', + angular: { + examples: { + format: 'sqlite', + path: './resources/code-examples.db', + }, + }, + }), + ); + mockHost.stat.and.resolveTo({ size: 11 * 1024 * 1024 } as Stats); // 11MB + + const databases = await getVersionSpecificExampleDatabases( + '/path/to/workspace', + mockLogger, + mockHost, + ); + + expect(databases.length).toBe(0); + expect(mockLogger.warn).toHaveBeenCalledWith(jasmine.stringMatching(/is larger than 10MB/)); + }); +}); diff --git a/packages/angular/cli/src/commands/mcp/tools/examples/database.ts b/packages/angular/cli/src/commands/mcp/tools/examples/database.ts new file mode 100644 index 000000000000..cd4f31a655c0 --- /dev/null +++ b/packages/angular/cli/src/commands/mcp/tools/examples/database.ts @@ -0,0 +1,142 @@ +/** + * @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 type { DatabaseSync, SQLInputValue } from 'node:sqlite'; +import { escapeSearchQuery } from './query-escaper'; +import type { FindExampleInput } from './schemas'; + +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. + */ +export 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}`, + ); + } +} + +export function queryDatabase(dbs: DatabaseSync[], input: FindExampleInput) { + const { query, keywords, required_packages, related_concepts, includeExperimental } = input; + + // Build the query dynamically + const params: SQLInputValue[] = []; + let sql = + `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, " + + // 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 = []; + + // FTS query + if (query) { + whereClauses.push('examples_fts MATCH ?'); + params.push(escapeSearchQuery(query)); + } + + // JSON array filters + const addJsonFilter = (column: string, values: string[] | undefined) => { + if (values?.length) { + for (const value of values) { + whereClauses.push(`e.${column} LIKE ?`); + params.push(`%"${value}"%`); + } + } + }; + + addJsonFilter('keywords', keywords); + addJsonFilter('required_packages', required_packages); + addJsonFilter('related_concepts', related_concepts); + + if (!includeExperimental) { + whereClauses.push('e.experimental = 0'); + } + + if (whereClauses.length > 0) { + sql += ` WHERE ${whereClauses.join(' AND ')}`; + } + + // Query database and return results + const examples = []; + const textContent = []; + + 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) { + text += `\n**Snippet:** ${example.snippet}`; + } + text += `\n\n---\n\n${example.content}`; + textContent.push({ type: 'text' as const, text }); + } + + return { + content: textContent, + structuredContent: { examples: finalExamples }, + }; +} diff --git a/packages/angular/cli/src/commands/mcp/tools/examples/index.ts b/packages/angular/cli/src/commands/mcp/tools/examples/index.ts new file mode 100644 index 000000000000..8ceecf2d840d --- /dev/null +++ b/packages/angular/cli/src/commands/mcp/tools/examples/index.ts @@ -0,0 +1,133 @@ +/** + * @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 type { DatabaseSync } from 'node:sqlite'; +import { type McpToolContext, declareTool } from '../tool-registry'; +import { queryDatabase, validateDatabaseSchema } from './database'; +import { getVersionSpecificExampleDatabases } from './database-discovery'; +import { setupRuntimeExamples } from './runtime-database'; +import { type FindExampleInput, findExampleInputSchema, findExampleOutputSchema } from './schemas'; +import { suppressSqliteWarning } from './utils'; + +export const FIND_EXAMPLE_TOOL = declareTool({ + name: 'find_examples', + title: 'Find Angular Code Examples', + description: ` + +Augments your knowledge base with a curated database of official, best-practice code examples, +focusing on **modern, new, and recently updated** Angular features. This tool acts as a RAG +(Retrieval-Augmented Generation) source, providing ground-truth information on the latest Angular +APIs and patterns. You **MUST** use it to understand and apply current standards when working with +new or evolving features. + + +* **Knowledge Augmentation:** Learning about new or updated Angular features (e.g., query: 'signal input' or 'deferrable views'). +* **Modern Implementation:** Finding the correct modern syntax for features + (e.g., query: 'functional route guard' or 'http client with fetch'). +* **Refactoring to Modern Patterns:** Upgrading older code by finding examples of new syntax + (e.g., query: 'built-in control flow' to replace "*ngIf"). +* **Advanced Filtering:** Combining a full-text search with filters to narrow results. + (e.g., query: 'forms', required_packages: ['@angular/forms'], keywords: ['validation']) + + +* **Project-Specific Use (Recommended):** For tasks inside a user's project, you **MUST** provide the + \`workspacePath\` argument to get examples that match the project's Angular version. Get this + path from \`list_projects\`. +* **General Use:** If no project context is available (e.g., for general questions or learning), + you can call the tool without the \`workspacePath\` argument. It will return the latest + generic examples. +* **Tool Selection:** This database primarily contains examples for new and recently updated Angular + features. For established, core features, the main documentation (via the + \`search_documentation\` tool) may be a better source of information. +* The examples in this database are the single source of truth for modern Angular coding patterns. +* The search query uses a powerful full-text search syntax (FTS5). Refer to the 'query' + parameter description for detailed syntax rules and examples. +* You can combine the main 'query' with optional filters like 'keywords', 'required_packages', + and 'related_concepts' to create highly specific searches. +`, + inputSchema: findExampleInputSchema.shape, + outputSchema: findExampleOutputSchema.shape, + isReadOnly: true, + isLocalOnly: true, + shouldRegister: ({ logger }) => { + // sqlite database support requires Node.js 22.16+ + const [nodeMajor, nodeMinor] = process.versions.node.split('.', 2).map(Number); + if (nodeMajor < 22 || (nodeMajor === 22 && nodeMinor < 16)) { + logger.warn( + `MCP tool 'find_examples' requires Node.js 22.16 (or higher). ` + + ' Registration of this tool has been skipped.', + ); + + return false; + } + + return true; + }, + factory: createFindExampleHandler, +}); + +async function createFindExampleHandler({ logger, exampleDatabasePath, host }: McpToolContext) { + const runtimeDb = process.env['NG_MCP_EXAMPLES_DIR'] + ? await setupRuntimeExamples(process.env['NG_MCP_EXAMPLES_DIR'], host) + : undefined; + + suppressSqliteWarning(); + + return async (input: FindExampleInput) => { + // If the dev-time override is present, use it and bypass all other logic. + if (runtimeDb) { + return queryDatabase([runtimeDb], input); + } + + const resolvedDbs: { path: string; source: string }[] = []; + + // First, try to get all available version-specific guides. + if (input.workspacePath) { + const versionSpecificDbs = await getVersionSpecificExampleDatabases( + input.workspacePath, + logger, + host, + ); + for (const db of versionSpecificDbs) { + resolvedDbs.push({ path: db.dbPath, source: db.source }); + } + } + + // 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 (resolvedDbs.length === 0) { + // This should be prevented by the registration logic in mcp-server.ts + throw new Error('No example databases are available.'); + } + + const { DatabaseSync } = await import('node:sqlite'); + 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; + } + } + + if (dbConnections.length === 0) { + throw new Error('All available example databases were invalid. Cannot perform query.'); + } + + return queryDatabase(dbConnections, input); + }; +} diff --git a/packages/angular/cli/src/commands/mcp/tools/examples/query-escaper.ts b/packages/angular/cli/src/commands/mcp/tools/examples/query-escaper.ts new file mode 100644 index 000000000000..bb6acd375d45 --- /dev/null +++ b/packages/angular/cli/src/commands/mcp/tools/examples/query-escaper.ts @@ -0,0 +1,66 @@ +/** + * @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 + */ + +/** + * Escapes a search query for FTS5 by tokenizing and quoting terms. + * + * This function processes a raw search string and prepares it for an FTS5 full-text search. + * It correctly handles quoted phrases, logical operators (AND, OR, NOT), parentheses, + * and prefix searches (ending with an asterisk), ensuring that individual search + * terms are properly quoted to be treated as literals by the search engine. + * This is primarily intended to avoid unintentional usage of FTS5 query syntax by consumers. + * + * @param query The raw search query string. + * @returns A sanitized query string suitable for FTS5. + */ +export function escapeSearchQuery(query: string): string { + // This regex tokenizes the query string into parts: + // 1. Quoted phrases (e.g., "foo bar") + // 2. Parentheses ( and ) + // 3. FTS5 operators (AND, OR, NOT, NEAR) + // 4. Words, which can include a trailing asterisk for prefix search (e.g., foo*) + const tokenizer = /"([^"]*)"|([()])|\b(AND|OR|NOT|NEAR)\b|([^\s()]+)/g; + let match; + const result: string[] = []; + let lastIndex = 0; + + while ((match = tokenizer.exec(query)) !== null) { + // Add any whitespace or other characters between tokens + if (match.index > lastIndex) { + result.push(query.substring(lastIndex, match.index)); + } + + const [, quoted, parenthesis, operator, term] = match; + + if (quoted !== undefined) { + // It's a quoted phrase, keep it as is. + result.push(`"${quoted}"`); + } else if (parenthesis) { + // It's a parenthesis, keep it as is. + result.push(parenthesis); + } else if (operator) { + // It's an operator, keep it as is. + result.push(operator); + } else if (term) { + // It's a term that needs to be quoted. + if (term.endsWith('*')) { + result.push(`"${term.slice(0, -1)}"*`); + } else { + result.push(`"${term}"`); + } + } + lastIndex = tokenizer.lastIndex; + } + + // Add any remaining part of the string + if (lastIndex < query.length) { + result.push(query.substring(lastIndex)); + } + + return result.join(''); +} diff --git a/packages/angular/cli/src/commands/mcp/tools/examples_spec.ts b/packages/angular/cli/src/commands/mcp/tools/examples/query-escaper_spec.ts similarity index 97% rename from packages/angular/cli/src/commands/mcp/tools/examples_spec.ts rename to packages/angular/cli/src/commands/mcp/tools/examples/query-escaper_spec.ts index 9fc75120aee5..6aa801fe0349 100644 --- a/packages/angular/cli/src/commands/mcp/tools/examples_spec.ts +++ b/packages/angular/cli/src/commands/mcp/tools/examples/query-escaper_spec.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.dev/license */ -import { escapeSearchQuery } from './examples'; +import { escapeSearchQuery } from './query-escaper'; describe('escapeSearchQuery', () => { it('should wrap single terms in double quotes', () => { diff --git a/tools/example_db_generator.js b/packages/angular/cli/src/commands/mcp/tools/examples/runtime-database.ts similarity index 72% rename from tools/example_db_generator.js rename to packages/angular/cli/src/commands/mcp/tools/examples/runtime-database.ts index 052bb1afb53d..5ca74dc60d63 100644 --- a/tools/example_db_generator.js +++ b/packages/angular/cli/src/commands/mcp/tools/examples/runtime-database.ts @@ -6,10 +6,10 @@ * found in the LICENSE file at https://angular.dev/license */ -const { globSync, readdirSync, readFileSync, mkdirSync, existsSync, rmSync } = require('node:fs'); -const { resolve, dirname, join } = require('node:path'); -const { DatabaseSync } = require('node:sqlite'); -const { z } = require('zod'); +import { join } from 'node:path'; +import type { DatabaseSync } from 'node:sqlite'; +import { z } from 'zod'; +import type { McpToolContext } from '../tool-registry'; /** * A simple YAML front matter parser. @@ -21,19 +21,19 @@ const { z } = require('zod'); * @param content The string content to parse. * @returns A record containing the parsed front matter data. */ -function parseFrontmatter(content) { +function parseFrontmatter(content: string): Record { const match = content.match(/^---\r?\n(.*?)\r?\n---/s); if (!match) { return {}; } const frontmatter = match[1]; - const data = {}; + const data: Record = {}; const lines = frontmatter.split(/\r?\n/); let currentKey = ''; let isArray = false; - const arrayValues = []; + const arrayValues: string[] = []; for (const line of lines) { const keyValueMatch = line.match(/^([^:]+):\s*(.*)/); @@ -80,16 +80,14 @@ function parseFrontmatter(content) { return data; } -function generate(inPath, outPath) { - const dbPath = outPath; - mkdirSync(dirname(outPath), { recursive: true }); +export async function setupRuntimeExamples( + examplesPath: string, + host: McpToolContext['host'], +): Promise { + const { DatabaseSync } = await import('node:sqlite'); + const db = new DatabaseSync(':memory:'); - if (existsSync(dbPath)) { - rmSync(dbPath); - } - const db = new DatabaseSync(dbPath); - - // Create a table to store metadata. + // Create a relational table to store the structured example data. db.exec(` CREATE TABLE metadata ( key TEXT PRIMARY KEY NOT NULL, @@ -103,7 +101,6 @@ function generate(inPath, outPath) { ('created_at', '${new Date().toISOString()}'); `); - // Create a relational table to store the structured example data. db.exec(` CREATE TABLE examples ( id INTEGER PRIMARY KEY, @@ -137,13 +134,10 @@ function generate(inPath, outPath) { // Create triggers to keep the FTS table synchronized with the examples table. db.exec(` CREATE TRIGGER examples_after_insert AFTER INSERT ON examples BEGIN - INSERT INTO examples_fts( - rowid, title, summary, keywords, required_packages, related_concepts, related_tools, - content - ) + INSERT INTO examples_fts(rowid, title, summary, keywords, required_packages, related_concepts, related_tools, content) VALUES ( - new.id, new.title, new.summary, new.keywords, new.required_packages, - new.related_concepts, new.related_tools, new.content + new.id, new.title, new.summary, new.keywords, new.required_packages, new.related_concepts, + new.related_tools, new.content ); END; `); @@ -165,22 +159,19 @@ function generate(inPath, outPath) { }); db.exec('BEGIN TRANSACTION'); - const entries = globSync - ? globSync('**/*.md', { cwd: resolve(inPath), withFileTypes: true }) - : readdirSync(resolve(inPath), { withFileTypes: true }); - for (const entry of entries) { - if (!entry.isFile() || !entry.name.endsWith('.md')) { + for await (const entry of host.glob('**/*.md', { cwd: examplesPath })) { + if (!entry.isFile()) { continue; } - const content = readFileSync(join(entry.parentPath, entry.name), 'utf-8'); + const content = await host.readFile(join(entry.parentPath, entry.name), 'utf-8'); const frontmatter = parseFrontmatter(content); const validation = frontmatterSchema.safeParse(frontmatter); if (!validation.success) { - console.error(`Validation failed for example file: ${entry.name}`); - console.error('Issues:', validation.error.issues); - throw new Error(`Invalid front matter in ${entry.name}`); + // eslint-disable-next-line no-console + console.warn(`Skipping invalid example file ${entry.name}:`, validation.error.issues); + continue; } const { @@ -192,6 +183,7 @@ function generate(inPath, outPath) { related_tools, experimental, } = validation.data; + insertStatement.run( title, summary, @@ -205,25 +197,5 @@ function generate(inPath, outPath) { } db.exec('END TRANSACTION'); - db.close(); + return db; } - -if (require.main === module) { - const argv = process.argv.slice(2); - if (argv.length !== 2) { - console.error('Must include 2 arguments.'); - process.exit(1); - } - - const [inPath, outPath] = argv; - - try { - generate(inPath, outPath); - } catch (error) { - console.error('An error happened:'); - console.error(error); - process.exit(127); - } -} - -exports.generate = generate; diff --git a/packages/angular/cli/src/commands/mcp/tools/examples/schemas.ts b/packages/angular/cli/src/commands/mcp/tools/examples/schemas.ts new file mode 100644 index 000000000000..2122f6775bc8 --- /dev/null +++ b/packages/angular/cli/src/commands/mcp/tools/examples/schemas.ts @@ -0,0 +1,132 @@ +/** + * @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 { z } from 'zod'; + +export const findExampleInputSchema = z.object({ + workspacePath: z + .string() + .optional() + .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.', + ), + query: z + .string() + .describe( + "The primary, conceptual search query. This should capture the user's main goal or question " + + "(e.g., 'lazy loading a route' or 'how to use signal inputs'). The query will be processed " + + 'by a powerful full-text search engine.\n\n' + + 'Key Syntax Features (see https://www.sqlite.org/fts5.html for full documentation):\n' + + ' - AND (default): Space-separated terms are combined with AND.\n' + + ' - Example: \'standalone component\' (finds results with both "standalone" and "component")\n' + + ' - OR: Use the OR operator to find results with either term.\n' + + " - Example: 'validation OR validator'\n" + + ' - NOT: Use the NOT operator to exclude terms.\n' + + " - Example: 'forms NOT reactive'\n" + + ' - Grouping: Use parentheses () to group expressions.\n' + + " - Example: '(validation OR validator) AND forms'\n" + + ' - Phrase Search: Use double quotes "" for exact phrases.\n' + + ' - Example: \'"template-driven forms"\'\n' + + ' - Prefix Search: Use an asterisk * for prefix matching.\n' + + ' - Example: \'rout*\' (matches "route", "router", "routing")', + ), + keywords: z + .array(z.string()) + .optional() + .describe( + 'A list of specific, exact keywords to narrow the search. Use this for precise terms like ', + ), + required_packages: z + .array(z.string()) + .optional() + .describe( + "A list of NPM packages that an example must use. Use this when the user's request is " + + 'specific to a feature within a certain package (e.g., if the user asks about `ngModel`, ' + + 'you should filter by `@angular/forms`).', + ), + related_concepts: z + .array(z.string()) + .optional() + .describe( + 'A list of high-level concepts to filter by. Use this to find examples related to broader ' + + 'architectural ideas or patterns (e.g., `signals`, `dependency injection`, `routing`).', + ), + includeExperimental: z + .boolean() + .optional() + .default(false) + .describe( + 'By default, this tool returns only production-safe examples. Set this to `true` **only if** ' + + 'the user explicitly asks for a bleeding-edge feature or if a stable solution to their ' + + 'problem cannot be found. If you set this to `true`, you **MUST** preface your answer by ' + + 'warning the user that the example uses experimental APIs that are not suitable for production.', + ), +}); + +export type FindExampleInput = z.infer; + +export const findExampleOutputSchema = z.object({ + examples: z.array( + z.object({ + title: z + .string() + .describe( + 'The title of the example. Use this as a heading when presenting the example to the user.', + ), + summary: z + .string() + .describe( + "A one-sentence summary of the example's purpose. Use this to help the user decide " + + 'if the example is relevant to them.', + ), + keywords: z + .array(z.string()) + .optional() + .describe( + 'A list of keywords for the example. You can use these to explain why this example ' + + "was a good match for the user's query.", + ), + required_packages: z + .array(z.string()) + .optional() + .describe( + 'A list of NPM packages required for the example to work. Before presenting the code, ' + + 'you should inform the user if any of these packages need to be installed.', + ), + related_concepts: z + .array(z.string()) + .optional() + .describe( + 'A list of related concepts. You can suggest these to the user as topics for ' + + 'follow-up questions.', + ), + related_tools: z + .array(z.string()) + .optional() + .describe( + 'A list of related MCP tools. You can suggest these as potential next steps for the user.', + ), + content: z + .string() + .describe( + 'A complete, self-contained Angular code example in Markdown format. This should be ' + + 'presented to the user inside a markdown code block.', + ), + snippet: z + .string() + .optional() + .describe( + 'A contextual snippet from the content showing the matched search term. This field is ' + + 'critical for efficiently evaluating a result`s relevance. It enables two primary ', + ), + }), + ), +}); diff --git a/packages/angular/cli/src/commands/mcp/tools/examples/utils.ts b/packages/angular/cli/src/commands/mcp/tools/examples/utils.ts new file mode 100644 index 000000000000..312994d7b3fd --- /dev/null +++ b/packages/angular/cli/src/commands/mcp/tools/examples/utils.ts @@ -0,0 +1,31 @@ +/** + * @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 + */ + +/** + * Suppresses the experimental warning emitted by Node.js for the `node:sqlite` module. + * + * This is a workaround to prevent the console from being cluttered with warnings + * about the experimental status of the SQLite module, which is used by this tool. + */ +export function suppressSqliteWarning(): void { + const originalProcessEmit = process.emit; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + process.emit = function (event: string, error?: unknown): any { + if ( + event === 'warning' && + error instanceof Error && + error.name === 'ExperimentalWarning' && + error.message.includes('SQLite') + ) { + return false; + } + + // eslint-disable-next-line @typescript-eslint/no-explicit-any, prefer-rest-params + return originalProcessEmit.apply(process, arguments as any); + }; +} diff --git a/packages/angular/cli/src/commands/mcp/tools/modernize.ts b/packages/angular/cli/src/commands/mcp/tools/modernize.ts index 754bb683bdc0..9e85c6f7246b 100644 --- a/packages/angular/cli/src/commands/mcp/tools/modernize.ts +++ b/packages/angular/cli/src/commands/mcp/tools/modernize.ts @@ -8,7 +8,7 @@ import { dirname, join, relative } from 'path'; import { z } from 'zod'; -import { CommandError, type Host, LocalWorkspaceHost } from '../host'; +import { CommandError, type Host } from '../host'; import { createStructuredContentOutput, findAngularJsonDir } from '../utils'; import { type McpToolDeclaration, declareTool } from './tool-registry'; @@ -205,5 +205,5 @@ ${TRANSFORMATIONS.map((t) => ` * ${t.name}: ${t.description}`).join('\n')} outputSchema: modernizeOutputSchema.shape, isLocalOnly: true, isReadOnly: false, - factory: () => (input) => runModernization(input, LocalWorkspaceHost), + factory: (context) => (input) => runModernization(input, context.host), }); 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 bb2b1574d294..f85a6f322c60 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: ` @@ -70,12 +70,10 @@ export async function registerZonelessMigrationTool( fileOrDirPath: string, extras: RequestHandlerExtra, ) { - let filesWithComponents, componentTestFiles, zoneFiles; + let filesWithComponents, componentTestFiles, zoneFiles, categorizationErrors; try { - ({ filesWithComponents, componentTestFiles, zoneFiles } = await discoverAndCategorizeFiles( - fileOrDirPath, - extras, - )); + ({ filesWithComponents, componentTestFiles, zoneFiles, categorizationErrors } = + await discoverAndCategorizeFiles(fileOrDirPath, extras)); } catch (e) { return createResponse( `Error: Could not access the specified path. Please ensure the following path is correct ` + @@ -113,6 +111,14 @@ export async function registerZonelessMigrationTool( } } + if (categorizationErrors.length > 0) { + let errorMessage = + 'Migration analysis is complete for all actionable files. However, the following files could not be analyzed due to errors:\n'; + errorMessage += categorizationErrors.map((e) => `- ${e.filePath}: ${e.message}`).join('\n'); + + return createResponse(errorMessage); + } + return createTestDebuggingGuideForNonActionableInput(fileOrDirPath); } @@ -120,10 +126,11 @@ async function discoverAndCategorizeFiles( fileOrDirPath: string, extras: RequestHandlerExtra, ) { - let files: SourceFile[] = []; + const filePaths: string[] = []; const componentTestFiles = new Set(); const filesWithComponents = new Set(); const zoneFiles = new Set(); + const categorizationErrors: { filePath: string; message: string }[] = []; let isDirectory: boolean; try { @@ -134,52 +141,87 @@ async function discoverAndCategorizeFiles( } if (isDirectory) { - const allFiles = glob(`${fileOrDirPath}/**/*.ts`); - for await (const file of allFiles) { - files.push(await createSourceFile(file)); + for await (const file of glob(`${fileOrDirPath}/**/*.ts`)) { + filePaths.push(file); } } else { - files = [await createSourceFile(fileOrDirPath)]; + filePaths.push(fileOrDirPath); const maybeTestFile = await getTestFilePath(fileOrDirPath); if (maybeTestFile) { - componentTestFiles.add(await createSourceFile(maybeTestFile)); + // Eagerly add the test file path for categorization. + filePaths.push(maybeTestFile); } } - for (const sourceFile of files) { - const content = sourceFile.getFullText(); - const componentSpecifier = await getImportSpecifier(sourceFile, '@angular/core', 'Component'); - const zoneSpecifier = await getImportSpecifier(sourceFile, '@angular/core', 'NgZone'); - const testBedSpecifier = await getImportSpecifier( - sourceFile, - /(@angular\/core)?\/testing/, - 'TestBed', + const CONCURRENCY_LIMIT = 50; + const filesToProcess = [...filePaths]; + while (filesToProcess.length > 0) { + const batch = filesToProcess.splice(0, CONCURRENCY_LIMIT); + const results = await Promise.allSettled( + batch.map(async (filePath) => { + const sourceFile = await createSourceFile(filePath); + await categorizeFile(sourceFile, extras, { + filesWithComponents, + componentTestFiles, + zoneFiles, + }); + }), ); - if (testBedSpecifier) { - componentTestFiles.add(sourceFile); - } else if (componentSpecifier) { - if ( - !content.includes('changeDetectionStrategy: ChangeDetectionStrategy.OnPush') && - !content.includes('changeDetectionStrategy: ChangeDetectionStrategy.Default') - ) { - filesWithComponents.add(sourceFile); - } else { - sendDebugMessage( - `Component file already has change detection strategy: ${sourceFile.fileName}. Skipping migration.`, - extras, - ); - } - const testFilePath = await getTestFilePath(sourceFile.fileName); - if (testFilePath) { - componentTestFiles.add(await createSourceFile(testFilePath)); + for (let i = 0; i < results.length; i++) { + const result = results[i]; + if (result.status === 'rejected') { + const failedFile = batch[i]; + const reason = result.reason instanceof Error ? result.reason.message : `${result.reason}`; + categorizationErrors.push({ filePath: failedFile, message: reason }); } - } else if (zoneSpecifier) { - zoneFiles.add(sourceFile); } } - return { filesWithComponents, componentTestFiles, zoneFiles }; + return { filesWithComponents, componentTestFiles, zoneFiles, categorizationErrors }; +} + +async function categorizeFile( + sourceFile: SourceFile, + extras: RequestHandlerExtra, + categorizedFiles: { + filesWithComponents: Set; + componentTestFiles: Set; + zoneFiles: Set; + }, +) { + const { filesWithComponents, componentTestFiles, zoneFiles } = categorizedFiles; + const content = sourceFile.getFullText(); + const componentSpecifier = await getImportSpecifier(sourceFile, '@angular/core', 'Component'); + const zoneSpecifier = await getImportSpecifier(sourceFile, '@angular/core', 'NgZone'); + const testBedSpecifier = await getImportSpecifier( + sourceFile, + /(@angular\/core)?\/testing/, + 'TestBed', + ); + + if (testBedSpecifier) { + componentTestFiles.add(sourceFile); + } else if (componentSpecifier) { + if ( + !content.includes('changeDetectionStrategy: ChangeDetectionStrategy.OnPush') && + !content.includes('changeDetectionStrategy: ChangeDetectionStrategy.Default') + ) { + filesWithComponents.add(sourceFile); + } else { + sendDebugMessage( + `Component file already has change detection strategy: ${sourceFile.fileName}. Skipping migration.`, + extras, + ); + } + + const testFilePath = await getTestFilePath(sourceFile.fileName); + if (testFilePath) { + componentTestFiles.add(await createSourceFile(testFilePath)); + } + } else if (zoneSpecifier) { + zoneFiles.add(sourceFile); + } } async function rankComponentFilesForMigration( diff --git a/packages/angular/cli/src/commands/mcp/tools/projects.ts b/packages/angular/cli/src/commands/mcp/tools/projects.ts index e6abff0ca418..8c6eb5d332f6 100644 --- a/packages/angular/cli/src/commands/mcp/tools/projects.ts +++ b/packages/angular/cli/src/commands/mcp/tools/projects.ts @@ -6,8 +6,9 @@ * found in the LICENSE file at https://angular.dev/license */ +import { realpathSync } from 'node:fs'; import { readFile, readdir, stat } from 'node:fs/promises'; -import { dirname, extname, join, normalize, posix, resolve } from 'node:path'; +import { dirname, extname, isAbsolute, join, normalize, posix, relative, resolve } from 'node:path'; import { fileURLToPath } from 'node:url'; import semver from 'semver'; import { z } from 'zod'; @@ -148,15 +149,24 @@ their types, and their locations. }); const EXCLUDED_DIRS = new Set(['node_modules', 'dist', 'out', 'coverage']); +const IGNORED_FILE_SYSTEM_ERRORS = new Set(['EACCES', 'EPERM', 'ENOENT', 'EBUSY']); + +function isIgnorableFileError(error: Error & { code?: string }): boolean { + return !!error.code && IGNORED_FILE_SYSTEM_ERRORS.has(error.code); +} /** * Iteratively finds all 'angular.json' files with controlled concurrency and directory exclusions. * This non-recursive implementation is suitable for very large directory trees, * prevents file descriptor exhaustion (`EMFILE` errors), and handles symbolic link loops. * @param rootDir The directory to start the search from. + * @param allowedRealRoots A list of allowed real root directories (resolved paths) to restrict symbolic link traversal. * @returns An async generator that yields the full path of each found 'angular.json' file. */ -async function* findAngularJsonFiles(rootDir: string): AsyncGenerator { +async function* findAngularJsonFiles( + rootDir: string, + allowedRealRoots: ReadonlyArray, +): AsyncGenerator { const CONCURRENCY_LIMIT = 50; const queue: string[] = [rootDir]; const seenInodes = new Set(); @@ -166,7 +176,7 @@ async function* findAngularJsonFiles(rootDir: string): AsyncGenerator { seenInodes.add(rootStats.ino); } catch (error) { assertIsError(error); - if (error.code === 'EACCES' || error.code === 'EPERM' || error.code === 'ENOENT') { + if (isIgnorableFileError(error)) { return; // Cannot access root, so there's nothing to do. } throw error; @@ -182,24 +192,47 @@ async function* findAngularJsonFiles(rootDir: string): AsyncGenerator { const subdirectories: string[] = []; for (const entry of entries) { const fullPath = join(dir, entry.name); - if (entry.isDirectory()) { + if (entry.isDirectory() || entry.isSymbolicLink()) { // Exclude dot-directories, build/cache directories, and node_modules if (entry.name.startsWith('.') || EXCLUDED_DIRS.has(entry.name)) { continue; } - // Check for symbolic link loops + let entryStats; try { - const entryStats = await stat(fullPath); + entryStats = await stat(fullPath); if (seenInodes.has(entryStats.ino)) { continue; // Already visited this directory (symlink loop), skip. } - seenInodes.add(entryStats.ino); + // Only process actual directories or symlinks to directories. + if (!entryStats.isDirectory()) { + continue; + } } catch { // Ignore errors from stat (e.g., broken symlinks) continue; } + if (entry.isSymbolicLink()) { + try { + const targetPath = realpathSync(fullPath); + // Ensure the link target is within one of the allowed roots. + const isAllowed = allowedRealRoots.some((root) => { + const rel = relative(root, targetPath); + + return !rel.startsWith('..') && !isAbsolute(rel); + }); + + if (!isAllowed) { + continue; + } + } catch { + // Ignore broken links. + continue; + } + } + + seenInodes.add(entryStats.ino); subdirectories.push(fullPath); } else if (entry.name === 'angular.json') { foundFilesInBatch.push(fullPath); @@ -209,7 +242,7 @@ async function* findAngularJsonFiles(rootDir: string): AsyncGenerator { return subdirectories; } catch (error) { assertIsError(error); - if (error.code === 'EACCES' || error.code === 'EPERM') { + if (isIgnorableFileError(error)) { return []; // Silently ignore permission errors. } throw error; @@ -529,8 +562,20 @@ async function createListProjectsHandler({ server }: McpToolContext) { searchRoots = [process.cwd()]; } + // Pre-resolve allowed roots to handle their own symlinks or normalizations. + // We ignore failures here; if a root is broken, we simply won't match against it. + const realAllowedRoots = searchRoots + .map((r) => { + try { + return realpathSync(r); + } catch { + return null; + } + }) + .filter((r): r is string => r !== null); + for (const root of searchRoots) { - for await (const configFile of findAngularJsonFiles(root)) { + for await (const configFile of findAngularJsonFiles(root, realAllowedRoots)) { const { workspace, parsingError, versioningError } = await processConfigFile( configFile, root, diff --git a/packages/angular/cli/src/commands/mcp/tools/tool-registry.ts b/packages/angular/cli/src/commands/mcp/tools/tool-registry.ts index 9bbce768000b..5af40a4e98b5 100644 --- a/packages/angular/cli/src/commands/mcp/tools/tool-registry.ts +++ b/packages/angular/cli/src/commands/mcp/tools/tool-registry.ts @@ -7,9 +7,11 @@ */ import type { McpServer, ToolCallback } from '@modelcontextprotocol/sdk/server/mcp.js'; +import type { ToolAnnotations } from '@modelcontextprotocol/sdk/types'; import type { ZodRawShape } from 'zod'; import type { AngularWorkspace } from '../../../utilities/config'; -import type { DevServer } from '../dev-server'; +import type { Devserver } from '../devserver'; +import type { Host } from '../host'; type ToolConfig = Parameters[1]; @@ -18,7 +20,8 @@ export interface McpToolContext { workspace?: AngularWorkspace; logger: { warn(text: string): void }; exampleDatabasePath?: string; - devServers: Map; + devservers: Map; + host: Host; } export type McpToolFactory = ( @@ -29,7 +32,7 @@ export interface McpToolDeclaration; diff --git a/packages/angular/cli/src/commands/update/cli.ts b/packages/angular/cli/src/commands/update/cli.ts index fdf8e850e026..3de979b481e6 100644 --- a/packages/angular/cli/src/commands/update/cli.ts +++ b/packages/angular/cli/src/commands/update/cli.ts @@ -6,22 +6,13 @@ * found in the LICENSE file at https://angular.dev/license */ -import { SchematicDescription, UnsuccessfulWorkflowExecution } from '@angular-devkit/schematics'; -import { - FileSystemCollectionDescription, - FileSystemSchematicDescription, - NodeWorkflow, -} from '@angular-devkit/schematics/tools'; +import { NodeWorkflow } from '@angular-devkit/schematics/tools'; import { Listr } from 'listr2'; -import { SpawnSyncReturns, execSync, spawnSync } from 'node:child_process'; import { existsSync, promises as fs } from 'node:fs'; import { createRequire } from 'node:module'; import * as path from 'node:path'; -import { join, resolve } from 'node:path'; import npa from 'npm-package-arg'; -import * as semver from 'semver'; import { Argv } from 'yargs'; -import { PackageManager } from '../../../lib/config/workspace-schema'; import { CommandModule, CommandModuleError, @@ -29,26 +20,30 @@ import { Options, } from '../../command-builder/command-module'; import { SchematicEngineHost } from '../../command-builder/utilities/schematic-engine-host'; -import { subscribeToWorkflow } from '../../command-builder/utilities/schematic-workflow'; -import { colors, figures } from '../../utilities/color'; +import { PackageManager, PackageManifest, createPackageManager } from '../../package-managers'; +import { colors } from '../../utilities/color'; import { disableVersionCheck } from '../../utilities/environment-options'; import { assertIsError } from '../../utilities/error'; -import { writeErrorToLogFile } from '../../utilities/log-file'; -import { - PackageIdentifier, - PackageManifest, - fetchPackageManifest, - fetchPackageMetadata, -} from '../../utilities/package-metadata'; import { PackageTreeNode, findPackageJson, getProjectDependencies, readPackageJson, } from '../../utilities/package-tree'; -import { askChoices } from '../../utilities/prompt'; -import { isTTY } from '../../utilities/tty'; -import { VERSION } from '../../utilities/version'; +import { + checkCLIVersion, + coerceVersionNumber, + runTempBinary, + shouldForcePackageManager, +} from './utilities/cli-version'; +import { ANGULAR_PACKAGES_REGEXP } from './utilities/constants'; +import { checkCleanGit } from './utilities/git'; +import { + commitChanges, + executeMigration, + executeMigrations, + executeSchematic, +} from './utilities/migration'; interface UpdateCommandArgs { packages?: string[]; @@ -63,21 +58,8 @@ interface UpdateCommandArgs { 'create-commits': boolean; } -interface MigrationSchematicDescription - extends SchematicDescription { - version?: string; - optional?: boolean; - recommended?: boolean; - documentation?: string; -} - -interface MigrationSchematicDescriptionWithVersion extends MigrationSchematicDescription { - version: string; -} - class CommandError extends Error {} -const ANGULAR_PACKAGES_REGEXP = /^@(?:angular|nguniversal)\//; const UPDATE_SCHEMATIC_COLLECTION = path.join(__dirname, 'schematic/collection.json'); export default class UpdateCommandModule extends CommandModule { @@ -87,7 +69,7 @@ export default class UpdateCommandModule extends CommandModule { return localYargs @@ -161,7 +143,7 @@ export default class UpdateCommandModule extends CommandModule): Promise { - const { logger, packageManager } = this.context; + const { logger } = this.context; + // Instantiate the package manager + const packageManager = await createPackageManager({ + cwd: this.context.root, + logger, + configuredPackageManager: this.context.packageManager.name, + }); // Check if the current installed CLI version is older than the latest compatible version. // Skip when running `ng update` without a package name as this will not trigger an actual update. if (!disableVersionCheck && options.packages?.length) { - const cliVersionToInstall = await this.checkCLIVersion( + const cliVersionToInstall = await checkCLIVersion( options.packages, - options.verbose, + logger, + packageManager, options.next, ); @@ -204,11 +193,15 @@ export default class UpdateCommandModule extends CommandModule favor @schematics/update from this package // Otherwise, use packages from the active workspace (migrations) resolvePaths: this.resolvePaths, @@ -264,8 +257,9 @@ export default class UpdateCommandModule extends CommandModule = {}, - ): Promise<{ success: boolean; files: Set }> { - const { logger } = this.context; - const workflowSubscription = subscribeToWorkflow(workflow, logger); - - // TODO: Allow passing a schematic instance directly - try { - await workflow - .execute({ - collection, - schematic, + : this.updatePackagesAndMigrate( + workflow, + rootDependencies, options, - logger, - }) - .toPromise(); - - return { success: !workflowSubscription.error, files: workflowSubscription.files }; - } catch (e) { - if (e instanceof UnsuccessfulWorkflowExecution) { - logger.error(`${figures.cross} Migration failed. See above for further details.\n`); - } else { - assertIsError(e); - const logPath = writeErrorToLogFile(e); - logger.fatal( - `${figures.cross} Migration failed: ${e.message}\n` + - ` See "${logPath}" for further details.\n`, + packages, + packageManager, ); - } - - return { success: false, files: workflowSubscription.files }; - } finally { - workflowSubscription.unsubscribe(); - } - } - - /** - * @return Whether or not the migration was performed successfully. - */ - private async executeMigration( - workflow: NodeWorkflow, - packageName: string, - collectionPath: string, - migrationName: string, - commit?: boolean, - ): Promise { - const { logger } = this.context; - const collection = workflow.engine.createCollection(collectionPath); - const name = collection.listSchematicNames().find((name) => name === migrationName); - if (!name) { - logger.error(`Cannot find migration '${migrationName}' in '${packageName}'.`); - - return 1; - } - - logger.info(colors.cyan(`** Executing '${migrationName}' of package '${packageName}' **\n`)); - const schematic = workflow.engine.createSchematic(name, collection); - - return this.executePackageMigrations(workflow, [schematic.description], packageName, commit); - } - - /** - * @return Whether or not the migrations were performed successfully. - */ - private async executeMigrations( - workflow: NodeWorkflow, - packageName: string, - collectionPath: string, - from: string, - to: string, - commit?: boolean, - ): Promise { - const collection = workflow.engine.createCollection(collectionPath); - const migrationRange = new semver.Range( - '>' + (semver.prerelease(from) ? from.split('-')[0] + '-0' : from) + ' <=' + to.split('-')[0], - ); - - const requiredMigrations: MigrationSchematicDescriptionWithVersion[] = []; - const optionalMigrations: MigrationSchematicDescriptionWithVersion[] = []; - - for (const name of collection.listSchematicNames()) { - const schematic = workflow.engine.createSchematic(name, collection); - const description = schematic.description as MigrationSchematicDescription; - - description.version = coerceVersionNumber(description.version); - if (!description.version) { - continue; - } - - if (semver.satisfies(description.version, migrationRange, { includePrerelease: true })) { - (description.optional ? optionalMigrations : requiredMigrations).push( - description as MigrationSchematicDescriptionWithVersion, - ); - } - } - - if (requiredMigrations.length === 0 && optionalMigrations.length === 0) { - return 0; - } - - // Required migrations - if (requiredMigrations.length) { - this.context.logger.info( - colors.cyan(`** Executing migrations of package '${packageName}' **\n`), - ); - - requiredMigrations.sort( - (a, b) => semver.compare(a.version, b.version) || a.name.localeCompare(b.name), - ); - - const result = await this.executePackageMigrations( - workflow, - requiredMigrations, - packageName, - commit, - ); - - if (result === 1) { - return 1; - } - } - - // Optional migrations - if (optionalMigrations.length) { - this.context.logger.info( - colors.magenta(`** Optional migrations of package '${packageName}' **\n`), - ); - - optionalMigrations.sort( - (a, b) => semver.compare(a.version, b.version) || a.name.localeCompare(b.name), - ); - - const migrationsToRun = await this.getOptionalMigrationsToRun( - optionalMigrations, - packageName, - ); - - if (migrationsToRun?.length) { - return this.executePackageMigrations(workflow, migrationsToRun, packageName, commit); - } - } - - return 0; - } - - private async executePackageMigrations( - workflow: NodeWorkflow, - migrations: MigrationSchematicDescription[], - packageName: string, - commit = false, - ): Promise<1 | 0> { - const { logger } = this.context; - for (const migration of migrations) { - const { title, description } = getMigrationTitleAndDescription(migration); - - logger.info(colors.cyan(figures.pointer) + ' ' + colors.bold(title)); - - if (description) { - logger.info(' ' + description); - } - - const { success, files } = await this.executeSchematic( - workflow, - migration.collection.name, - migration.name, - ); - if (!success) { - return 1; - } - - let modifiedFilesText: string; - switch (files.size) { - case 0: - modifiedFilesText = 'No changes made'; - break; - case 1: - modifiedFilesText = '1 file modified'; - break; - default: - modifiedFilesText = `${files.size} files modified`; - break; - } - - logger.info(` Migration completed (${modifiedFilesText}).`); - - // Commit migration - if (commit) { - const commitPrefix = `${packageName} migration - ${migration.name}`; - const commitMessage = migration.description - ? `${commitPrefix}\n\n${migration.description}` - : commitPrefix; - const committed = this.commit(commitMessage); - if (!committed) { - // Failed to commit, something went wrong. Abort the update. - return 1; - } - } - - logger.info(''); // Extra trailing newline. - } - - return 0; } private async migrateOnly( @@ -575,8 +368,9 @@ export default class UpdateCommandModule extends CommandModule, options: Options, - packages: PackageIdentifier[], + packages: npa.Result[], + packageManager: PackageManager, ): Promise { const { logger } = this.context; @@ -617,13 +413,14 @@ export default class UpdateCommandModule extends CommandModule).stderr}`); - - return false; - } - - if (!commitNeeded) { - logger.info(' No changes to commit after migration.'); - - return true; - } - - // Commit changes and abort on error. - try { - createCommit(message); - } catch (err) { - logger.error( - `Failed to commit update (${message}):\n${(err as SpawnSyncReturns).stderr}`, - ); - - return false; - } - - // Notify user of the commit. - const hash = findCurrentGitSha(); - const shortMessage = message.split('\n')[0]; - if (hash) { - logger.info(` Committed migration step (${getShortHash(hash)}): ${shortMessage}.`); - } else { - // Commit was successful, but reading the hash was not. Something weird happened, - // but nothing that would stop the update. Just log the weirdness and continue. - logger.info(` Committed migration step: ${shortMessage}.`); - logger.warn(' Failed to look up hash of most recent commit, continuing anyways.'); - } - - return true; - } - - private checkCleanGit(): boolean { - try { - const topLevel = execSync('git rev-parse --show-toplevel', { - encoding: 'utf8', - stdio: 'pipe', - }); - const result = execSync('git status --porcelain', { encoding: 'utf8', stdio: 'pipe' }); - if (result.trim().length === 0) { - return true; - } - - // Only files inside the workspace root are relevant - for (const entry of result.split('\n')) { - const relativeEntry = path.relative( - path.resolve(this.context.root), - path.resolve(topLevel.trim(), entry.slice(3).trim()), - ); - - if (!relativeEntry.startsWith('..') && !path.isAbsolute(relativeEntry)) { - return false; - } - } - } catch {} - - return true; - } - - /** - * Checks if the current installed CLI version is older or newer than a compatible version. - * @returns the version to install or null when there is no update to install. - */ - private async checkCLIVersion( - packagesToUpdate: string[], - verbose = false, - next = false, - ): Promise { - const { version } = await fetchPackageManifest( - `@angular/cli@${this.getCLIUpdateRunnerVersion(packagesToUpdate, next)}`, - this.context.logger, - { - verbose, - usingYarn: this.context.packageManager.name === PackageManager.Yarn, - }, - ); - - return VERSION.full === version ? null : version; - } - - private getCLIUpdateRunnerVersion( - packagesToUpdate: string[] | undefined, - next: boolean, - ): string | number { - if (next) { - return 'next'; - } - - const updatingAngularPackage = packagesToUpdate?.find((r) => ANGULAR_PACKAGES_REGEXP.test(r)); - if (updatingAngularPackage) { - // If we are updating any Angular package we can update the CLI to the target version because - // migrations for @angular/core@13 can be executed using Angular/cli@13. - // This is same behaviour as `npx @angular/cli@13 update @angular/core@13`. - - // `@angular/cli@13` -> ['', 'angular/cli', '13'] - // `@angular/cli` -> ['', 'angular/cli'] - const tempVersion = coerceVersionNumber(updatingAngularPackage.split('@')[2]); - - return semver.parse(tempVersion)?.major ?? 'latest'; - } - - // When not updating an Angular package we cannot determine which schematic runtime the migration should to be executed in. - // Typically, we can assume that the `@angular/cli` was updated previously. - // Example: Angular official packages are typically updated prior to NGRX etc... - // Therefore, we only update to the latest patch version of the installed major version of the Angular CLI. - - // This is important because we might end up in a scenario where locally Angular v12 is installed, updating NGRX from 11 to 12. - // We end up using Angular ClI v13 to run the migrations if we run the migrations using the CLI installed major version + 1 logic. - return VERSION.major; - } - - private async runTempBinary(packageName: string, args: string[] = []): Promise { - const { success, tempNodeModules } = await this.context.packageManager.installTemp(packageName); - if (!success) { - return 1; - } - - // Remove version/tag etc... from package name - // Ex: @angular/cli@latest -> @angular/cli - const packageNameNoVersion = packageName.substring(0, packageName.lastIndexOf('@')); - const pkgLocation = join(tempNodeModules, packageNameNoVersion); - const packageJsonPath = join(pkgLocation, 'package.json'); - - // Get a binary location for this package - let binPath: string | undefined; - if (existsSync(packageJsonPath)) { - const content = await fs.readFile(packageJsonPath, 'utf-8'); - if (content) { - const { bin = {} } = JSON.parse(content) as { bin: Record }; - const binKeys = Object.keys(bin); - - if (binKeys.length) { - binPath = resolve(pkgLocation, bin[binKeys[0]]); - } - } - } - - if (!binPath) { - throw new Error(`Cannot locate bin for temporary package: ${packageNameNoVersion}.`); - } - - const { status, error } = spawnSync(process.execPath, [binPath, ...args], { - stdio: 'inherit', - env: { - ...process.env, - NG_DISABLE_VERSION_CHECK: 'true', - NG_CLI_ANALYTICS: 'false', - }, - }); - - if (status === null && error) { - throw error; - } - - return status ?? 0; - } - - private packageManagerForce(verbose: boolean): boolean { - // npm 7+ can fail due to it incorrectly resolving peer dependencies that have valid SemVer - // ranges during an update. Update will set correct versions of dependencies within the - // package.json file. The force option is set to workaround these errors. - // Example error: - // npm ERR! Conflicting peer dependency: @angular/compiler-cli@14.0.0-rc.0 - // npm ERR! node_modules/@angular/compiler-cli - // npm ERR! peer @angular/compiler-cli@"^14.0.0 || ^14.0.0-rc" from @angular-devkit/build-angular@14.0.0-rc.0 - // npm ERR! node_modules/@angular-devkit/build-angular - // npm ERR! dev @angular-devkit/build-angular@"~14.0.0-rc.0" from the root project - if ( - this.context.packageManager.name === PackageManager.Npm && - this.context.packageManager.version && - semver.gte(this.context.packageManager.version, '7.0.0') - ) { - if (verbose) { - this.context.logger.info( - 'NPM 7+ detected -- enabling force option for package installation', - ); - } - - return true; - } - - return false; - } - - private async getOptionalMigrationsToRun( - optionalMigrations: MigrationSchematicDescription[], - packageName: string, - ): Promise { - const { logger } = this.context; - const numberOfMigrations = optionalMigrations.length; - logger.info( - `This package has ${numberOfMigrations} optional migration${ - numberOfMigrations > 1 ? 's' : '' - } that can be executed.`, - ); - - if (!isTTY()) { - for (const migration of optionalMigrations) { - const { title } = getMigrationTitleAndDescription(migration); - logger.info(colors.cyan(figures.pointer) + ' ' + colors.bold(title)); - logger.info(colors.gray(` ng update ${packageName} --name ${migration.name}`)); - logger.info(''); // Extra trailing newline. - } - - return undefined; - } - - logger.info( - 'Optional migrations may be skipped and executed after the update process, if preferred.', - ); - logger.info(''); // Extra trailing newline. - - const answer = await askChoices( - `Select the migrations that you'd like to run`, - optionalMigrations.map((migration) => { - const { title, documentation } = getMigrationTitleAndDescription(migration); - - return { - name: `[${colors.white(migration.name)}] ${title}${documentation ? ` (${documentation})` : ''}`, - value: migration.name, - checked: migration.recommended, - }; - }), - null, - ); - - logger.info(''); // Extra trailing newline. - - return optionalMigrations.filter(({ name }) => answer?.includes(name)); - } -} - -/** - * @return Whether or not the working directory has Git changes to commit. - */ -function hasChangesToCommit(): boolean { - // List all modified files not covered by .gitignore. - // If any files are returned, then there must be something to commit. - - return execSync('git ls-files -m -d -o --exclude-standard').toString() !== ''; -} - -/** - * Precondition: Must have pending changes to commit, they do not need to be staged. - * Postcondition: The Git working tree is committed and the repo is clean. - * @param message The commit message to use. - */ -function createCommit(message: string) { - // Stage entire working tree for commit. - execSync('git add -A', { encoding: 'utf8', stdio: 'pipe' }); - - // Commit with the message passed via stdin to avoid bash escaping issues. - execSync('git commit --no-verify -F -', { encoding: 'utf8', stdio: 'pipe', input: message }); -} - -/** - * @return The Git SHA hash of the HEAD commit. Returns null if unable to retrieve the hash. - */ -function findCurrentGitSha(): string | null { - try { - return execSync('git rev-parse HEAD', { encoding: 'utf8', stdio: 'pipe' }).trim(); - } catch { - return null; - } -} - -function getShortHash(commitHash: string): string { - return commitHash.slice(0, 9); -} - -function coerceVersionNumber(version: string | undefined): string | undefined { - if (!version) { - return undefined; - } - - if (!/^\d{1,30}\.\d{1,30}\.\d{1,30}/.test(version)) { - const match = version.match(/^\d{1,30}(\.\d{1,30})*/); - - if (!match) { - return undefined; - } - - if (!match[1]) { - version = version.substring(0, match[0].length) + '.0.0' + version.substring(match[0].length); - } else if (!match[2]) { - version = version.substring(0, match[0].length) + '.0' + version.substring(match[0].length); - } else { - return undefined; - } - } - - return semver.valid(version) ?? undefined; -} - -function getMigrationTitleAndDescription(migration: MigrationSchematicDescription): { - title: string; - description: string; - documentation?: string; -} { - const [title, ...description] = migration.description.split('. '); - - return { - title: title.endsWith('.') ? title : title + '.', - description: description.join('.\n '), - documentation: migration.documentation - ? new URL(migration.documentation, 'https://angular.dev').href - : undefined, - }; } diff --git a/packages/angular/cli/src/commands/update/schematic/index.ts b/packages/angular/cli/src/commands/update/schematic/index.ts index 26d2d06836b4..86bfe92deca1 100644 --- a/packages/angular/cli/src/commands/update/schematic/index.ts +++ b/packages/angular/cli/src/commands/update/schematic/index.ts @@ -389,20 +389,6 @@ function _getUpdateMetadata( result.packageGroupName = metadata['packageGroupName']; } - if (metadata['requirements']) { - const requirements = metadata['requirements']; - // Verify that requirements are - if ( - typeof requirements != 'object' || - Array.isArray(requirements) || - Object.keys(requirements).some((name) => typeof requirements[name] != 'string') - ) { - logger.warn(`requirements metadata of package ${packageJson.name} is malformed. Ignoring.`); - } else { - result.requirements = requirements; - } - } - if (metadata['migrations']) { const migrations = metadata['migrations']; if (typeof migrations != 'string') { diff --git a/packages/angular/cli/src/commands/update/utilities/cli-version.ts b/packages/angular/cli/src/commands/update/utilities/cli-version.ts new file mode 100644 index 000000000000..15e9a0ef32a8 --- /dev/null +++ b/packages/angular/cli/src/commands/update/utilities/cli-version.ts @@ -0,0 +1,198 @@ +/** + * @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 { logging } from '@angular-devkit/core'; +import { spawnSync } from 'node:child_process'; +import { existsSync, promises as fs } from 'node:fs'; +import { join, resolve } from 'node:path'; +import * as semver from 'semver'; +import { PackageManager } from '../../../package-managers'; +import { VERSION } from '../../../utilities/version'; +import { ANGULAR_PACKAGES_REGEXP } from './constants'; + +/** + * Coerces a string into a valid semantic version number. + * @param version The version string to coerce. + * @returns A valid semantic version string, or undefined if coercion fails. + */ +export function coerceVersionNumber(version: string | undefined): string | undefined { + if (!version) { + return undefined; + } + + if (!/^\d{1,30}\.\d{1,30}\.\d{1,30}/.test(version)) { + const match = version.match(/^\d{1,30}(\.\d{1,30})*/); + + if (!match) { + return undefined; + } + + if (!match[1]) { + version = version.substring(0, match[0].length) + '.0.0' + version.substring(match[0].length); + } else if (!match[2]) { + version = version.substring(0, match[0].length) + '.0' + version.substring(match[0].length); + } else { + return undefined; + } + } + + return semver.valid(version) ?? undefined; +} + +/** + * Checks if the installed CLI version is compatible with the packages being updated. + * @param packagesToUpdate The list of packages being updated. + * @param logger The logger instance. + * @param packageManager The package manager instance. + * @param verbose Whether to log verbose output. + * @param next Whether to check for the next version. + * @returns The version of the CLI to install, or null if the current version is compatible. + */ +export async function checkCLIVersion( + packagesToUpdate: string[], + logger: logging.LoggerApi, + packageManager: PackageManager, + next = false, +): Promise { + const runnerVersion = getCLIUpdateRunnerVersion(packagesToUpdate, next); + const manifest = await packageManager.getManifest(`@angular/cli@${runnerVersion}`); + + if (!manifest) { + logger.warn(`Could not find @angular/cli version '${runnerVersion}'.`); + + return null; + } + + const version = manifest.version; + + return VERSION.full === version ? null : version; +} + +/** + * Determines the version of the CLI to use for the update process. + * @param packagesToUpdate The list of packages being updated. + * @param next Whether to use the next version. + * @returns The version or tag to use for the CLI update runner. + */ +export function getCLIUpdateRunnerVersion( + packagesToUpdate: string[] | undefined, + next: boolean, +): string | number { + if (next) { + return 'next'; + } + + const updatingAngularPackage = packagesToUpdate?.find((r) => ANGULAR_PACKAGES_REGEXP.test(r)); + if (updatingAngularPackage) { + // If we are updating any Angular package we can update the CLI to the target version because + // migrations for @angular/core@13 can be executed using Angular/cli@13. + // This is same behaviour as `npx @angular/cli@13 update @angular/core@13`. + + // `@angular/cli@13` -> ['', 'angular/cli', '13'] + // `@angular/cli` -> ['', 'angular/cli'] + const tempVersion = coerceVersionNumber(updatingAngularPackage.split('@')[2]); + + return semver.parse(tempVersion)?.major ?? 'latest'; + } + + // When not updating an Angular package we cannot determine which schematic runtime the migration should to be executed in. + // Typically, we can assume that the `@angular/cli` was updated previously. + // Example: Angular official packages are typically updated prior to NGRX etc... + // Therefore, we only update to the latest patch version of the installed major version of the Angular CLI. + + // This is important because we might end up in a scenario where locally Angular v12 is installed, updating NGRX from 11 to 12. + // We end up using Angular ClI v13 to run the migrations if we run the migrations using the CLI installed major version + 1 logic. + return VERSION.major; +} + +/** + * Runs a binary from a temporary package installation. + * @param packageName The name of the package to install and run. + * @param packageManager The package manager instance. + * @param args The arguments to pass to the binary. + * @returns The exit code of the binary. + */ +export async function runTempBinary( + packageName: string, + packageManager: PackageManager, + args: string[] = [], +): Promise { + const { workingDirectory, cleanup } = await packageManager.acquireTempPackage(packageName); + + try { + // Remove version/tag etc... from package name + // Ex: @angular/cli@latest -> @angular/cli + const packageNameNoVersion = packageName.substring(0, packageName.lastIndexOf('@')); + const pkgLocation = join(workingDirectory, 'node_modules', packageNameNoVersion); + const packageJsonPath = join(pkgLocation, 'package.json'); + + // Get a binary location for this package + let binPath: string | undefined; + if (existsSync(packageJsonPath)) { + const content = await fs.readFile(packageJsonPath, 'utf-8'); + if (content) { + const { bin = {} } = JSON.parse(content) as { bin: Record }; + const binKeys = Object.keys(bin); + + if (binKeys.length) { + binPath = resolve(pkgLocation, bin[binKeys[0]]); + } + } + } + + if (!binPath) { + throw new Error(`Cannot locate bin for temporary package: ${packageNameNoVersion}.`); + } + + const { status, error } = spawnSync(process.execPath, [binPath, ...args], { + stdio: 'inherit', + env: { + ...process.env, + NG_DISABLE_VERSION_CHECK: 'true', + NG_CLI_ANALYTICS: 'false', + }, + }); + + if (status === null && error) { + throw error; + } + + return status ?? 0; + } finally { + await cleanup(); + } +} + +/** + * Determines whether to force the package manager to ignore peer dependency warnings. + * @param packageManager The package manager instance. + * @param logger The logger instance. + * @param verbose Whether to log verbose output. + * @returns True if the package manager should be forced, false otherwise. + */ +export async function shouldForcePackageManager( + packageManager: PackageManager, + logger: logging.LoggerApi, + verbose: boolean, +): Promise { + // npm 7+ can fail due to it incorrectly resolving peer dependencies that have valid SemVer + // ranges during an update. Update will set correct versions of dependencies within the + // package.json file. The force option is set to workaround these errors. + if (packageManager.name === 'npm') { + const version = await packageManager.getVersion(); + if (semver.gte(version, '7.0.0')) { + if (verbose) { + logger.info('NPM 7+ detected -- enabling force option for package installation'); + } + + return true; + } + } + + return false; +} diff --git a/packages/angular/cli/src/commands/update/utilities/constants.ts b/packages/angular/cli/src/commands/update/utilities/constants.ts new file mode 100644 index 000000000000..dfeef5ff96d1 --- /dev/null +++ b/packages/angular/cli/src/commands/update/utilities/constants.ts @@ -0,0 +1,13 @@ +/** + * @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 + */ + +/** + * Regular expression to match Angular packages. + * Checks for packages starting with `@angular/` or `@nguniversal/`. + */ +export const ANGULAR_PACKAGES_REGEXP = /^@(?:angular|nguniversal)\//; diff --git a/packages/angular/cli/src/commands/update/utilities/git.ts b/packages/angular/cli/src/commands/update/utilities/git.ts new file mode 100644 index 000000000000..0998c9c61fff --- /dev/null +++ b/packages/angular/cli/src/commands/update/utilities/git.ts @@ -0,0 +1,119 @@ +/** + * @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 { execFileSync } from 'node:child_process'; +import * as path from 'node:path'; + +/** + * Execute a git command. + * @param args Arguments to pass to the git command. + * @param input Optional input to pass to the command via stdin. + * @returns The output of the command. + */ +function execGit(args: string[], input?: string): string { + return execFileSync('git', args, { encoding: 'utf8', stdio: 'pipe', input }); +} + +/** + * Checks if the git repository is clean. + * This function only checks for changes that are within the specified root directory. + * Changes outside the root directory are ignored. + * @param root The root directory of the project to check. + * @returns True if the repository is clean within the root, false otherwise. + */ +export function checkCleanGit(root: string): boolean { + try { + const topLevel = execGit(['rev-parse', '--show-toplevel']); + const result = execGit(['status', '--porcelain', '-z']); + if (result.length === 0) { + return true; + } + + const entries = result.split('\0'); + for (let i = 0; i < entries.length; i++) { + const line = entries[i]; + if (!line) { + continue; + } + + // Status is the first 2 characters. + // If the status is a rename ('R'), the next entry in the split array is the target path. + let filePath = line.slice(3); + const status = line.slice(0, 2); + if (status[0] === 'R') { + // Check the source path (filePath) + if (isPathInsideRoot(filePath, root, topLevel.trim())) { + return false; + } + + // The next entry is the target path of the rename. + i++; + filePath = entries[i]; + } + + if (isPathInsideRoot(filePath, root, topLevel.trim())) { + return false; + } + } + } catch {} // eslint-disable-line no-empty + + return true; +} + +function isPathInsideRoot(filePath: string, root: string, topLevel: string): boolean { + const relativeEntry = path.relative(path.resolve(root), path.resolve(topLevel, filePath)); + + return !relativeEntry.startsWith('..') && !path.isAbsolute(relativeEntry); +} + +/** + * Checks if the working directory has pending changes to commit. + * @returns Whether or not the working directory has Git changes to commit. Returns false if not in a Git repository. + */ +export function hasChangesToCommit(): boolean { + try { + // List all modified files not covered by .gitignore. + // If any files are returned, then there must be something to commit. + return execGit(['ls-files', '-m', '-d', '-o', '--exclude-standard']).trim() !== ''; + } catch { + return false; + } +} + +/** + * Stages all changes in the Git working tree and creates a new commit. + * @param message The commit message to use. + */ +export function createCommit(message: string) { + // Stage entire working tree for commit. + execGit(['add', '-A']); + + // Commit with the message passed via stdin to avoid bash escaping issues. + execGit(['commit', '--no-verify', '-F', '-'], message); +} + +/** + * Finds the full Git SHA hash of the HEAD commit. + * @returns The full Git SHA hash of the HEAD commit. Returns null if unable to retrieve the hash. + */ +export function findCurrentGitSha(): string | null { + try { + return execGit(['rev-parse', 'HEAD']).trim(); + } catch { + return null; + } +} + +/** + * Gets the short hash of a commit. + * @param commitHash The full commit hash. + * @returns The short hash (first 9 characters). + */ +export function getShortHash(commitHash: string): string { + return commitHash.slice(0, 9); +} diff --git a/packages/angular/cli/src/commands/update/utilities/migration.ts b/packages/angular/cli/src/commands/update/utilities/migration.ts new file mode 100644 index 000000000000..3b53694d3859 --- /dev/null +++ b/packages/angular/cli/src/commands/update/utilities/migration.ts @@ -0,0 +1,361 @@ +/** + * @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 { logging } from '@angular-devkit/core'; +import { SchematicDescription, UnsuccessfulWorkflowExecution } from '@angular-devkit/schematics'; +import { + FileSystemCollectionDescription, + FileSystemSchematicDescription, + NodeWorkflow, +} from '@angular-devkit/schematics/tools'; +import { SpawnSyncReturns } from 'node:child_process'; +import * as semver from 'semver'; +import { subscribeToWorkflow } from '../../../command-builder/utilities/schematic-workflow'; +import { colors, figures } from '../../../utilities/color'; +import { assertIsError } from '../../../utilities/error'; +import { writeErrorToLogFile } from '../../../utilities/log-file'; +import { askChoices } from '../../../utilities/prompt'; +import { isTTY } from '../../../utilities/tty'; +import { coerceVersionNumber } from './cli-version'; +import { createCommit, findCurrentGitSha, getShortHash, hasChangesToCommit } from './git'; + +export interface MigrationSchematicDescription extends SchematicDescription< + FileSystemCollectionDescription, + FileSystemSchematicDescription +> { + version?: string; + optional?: boolean; + recommended?: boolean; + documentation?: string; +} + +interface MigrationSchematicDescriptionWithVersion extends MigrationSchematicDescription { + version: string; +} + +export async function executeSchematic( + workflow: NodeWorkflow, + logger: logging.Logger, + collection: string, + schematic: string, + options: Record = {}, +): Promise<{ success: boolean; files: Set }> { + const workflowSubscription = subscribeToWorkflow(workflow, logger); + + // TODO: Allow passing a schematic instance directly + try { + await workflow + .execute({ + collection, + schematic, + options, + logger, + }) + .toPromise(); + + return { success: !workflowSubscription.error, files: workflowSubscription.files }; + } catch (e) { + if (e instanceof UnsuccessfulWorkflowExecution) { + logger.error(`${figures.cross} Migration failed. See above for further details.\n`); + } else { + assertIsError(e); + const logPath = writeErrorToLogFile(e); + logger.fatal( + `${figures.cross} Migration failed: ${e.message}\n` + + ` See "${logPath}" for further details.\n`, + ); + } + + return { success: false, files: workflowSubscription.files }; + } finally { + workflowSubscription.unsubscribe(); + } +} + +/** + * @return Whether or not the migration was performed successfully. + */ +export async function executeMigration( + workflow: NodeWorkflow, + logger: logging.Logger, + packageName: string, + collectionPath: string, + migrationName: string, + commit: boolean = false, +): Promise { + const collection = workflow.engine.createCollection(collectionPath); + const name = collection.listSchematicNames().find((name) => name === migrationName); + if (!name) { + logger.error(`Cannot find migration '${migrationName}' in '${packageName}'.`); + + return 1; + } + + logger.info(colors.cyan(`** Executing '${migrationName}' of package '${packageName}' **\n`)); + const schematic = workflow.engine.createSchematic(name, collection); + + return executePackageMigrations( + workflow, + logger, + [schematic.description as MigrationSchematicDescription], + packageName, + commit, + ); +} + +/** + * @return Whether or not the migrations were performed successfully. + */ +export async function executeMigrations( + workflow: NodeWorkflow, + logger: logging.Logger, + packageName: string, + collectionPath: string, + from: string, + to: string, + commit: boolean = false, +): Promise { + const collection = workflow.engine.createCollection(collectionPath); + const migrationRange = new semver.Range( + '>' + (semver.prerelease(from) ? from.split('-')[0] + '-0' : from) + ' <=' + to.split('-')[0], + ); + + const requiredMigrations: MigrationSchematicDescriptionWithVersion[] = []; + const optionalMigrations: MigrationSchematicDescriptionWithVersion[] = []; + + for (const name of collection.listSchematicNames()) { + const schematic = workflow.engine.createSchematic(name, collection); + const description = schematic.description as MigrationSchematicDescription; + + description.version = coerceVersionNumber(description.version); + if (!description.version) { + continue; + } + + if (semver.satisfies(description.version, migrationRange, { includePrerelease: true })) { + (description.optional ? optionalMigrations : requiredMigrations).push( + description as MigrationSchematicDescriptionWithVersion, + ); + } + } + + if (requiredMigrations.length === 0 && optionalMigrations.length === 0) { + return 0; + } + + // Required migrations + if (requiredMigrations.length) { + logger.info(colors.cyan(`** Executing migrations of package '${packageName}' **\n`)); + + requiredMigrations.sort( + (a, b) => semver.compare(a.version, b.version) || a.name.localeCompare(b.name), + ); + + const result = await executePackageMigrations( + workflow, + logger, + requiredMigrations, + packageName, + commit, + ); + + if (result === 1) { + return 1; + } + } + + // Optional migrations + if (optionalMigrations.length) { + logger.info(colors.magenta(`** Optional migrations of package '${packageName}' **\n`)); + + optionalMigrations.sort( + (a, b) => semver.compare(a.version, b.version) || a.name.localeCompare(b.name), + ); + + const migrationsToRun = await getOptionalMigrationsToRun( + logger, + optionalMigrations, + packageName, + ); + + if (migrationsToRun?.length) { + return executePackageMigrations(workflow, logger, migrationsToRun, packageName, commit); + } + } + + return 0; +} + +async function executePackageMigrations( + workflow: NodeWorkflow, + logger: logging.Logger, + migrations: MigrationSchematicDescription[], + packageName: string, + commit = false, +): Promise<1 | 0> { + for (const migration of migrations) { + const { title, description } = getMigrationTitleAndDescription(migration); + + logger.info(colors.cyan(figures.pointer) + ' ' + colors.bold(title)); + + if (description) { + logger.info(' ' + description); + } + + const { success, files } = await executeSchematic( + workflow, + logger, + migration.collection.name, + migration.name, + ); + if (!success) { + return 1; + } + + let modifiedFilesText: string; + switch (files.size) { + case 0: + modifiedFilesText = 'No changes made'; + break; + case 1: + modifiedFilesText = '1 file modified'; + break; + default: + modifiedFilesText = `${files.size} files modified`; + break; + } + + logger.info(` Migration completed (${modifiedFilesText}).`); + + // Commit migration + if (commit) { + const commitPrefix = `${packageName} migration - ${migration.name}`; + const commitMessage = migration.description + ? `${commitPrefix}\n\n${migration.description}` + : commitPrefix; + const committed = commitChanges(logger, commitMessage); + if (!committed) { + // Failed to commit, something went wrong. Abort the update. + return 1; + } + } + + logger.info(''); // Extra trailing newline. + } + + return 0; +} + +/** + * @return Whether or not the commit was successful. + */ +export function commitChanges(logger: logging.Logger, message: string): boolean { + // Check if a commit is needed. + let commitNeeded: boolean; + try { + commitNeeded = hasChangesToCommit(); + } catch (err) { + logger.error(` Failed to read Git tree:\n${(err as SpawnSyncReturns).stderr}`); + + return false; + } + + if (!commitNeeded) { + logger.info(' No changes to commit after migration.'); + + return true; + } + + // Commit changes and abort on error. + try { + createCommit(message); + } catch (err) { + logger.error( + `Failed to commit update (${message}):\n${(err as SpawnSyncReturns).stderr}`, + ); + + return false; + } + + // Notify user of the commit. + const hash = findCurrentGitSha(); + const shortMessage = message.split('\n')[0]; + if (hash) { + logger.info(` Committed migration step (${getShortHash(hash)}): ${shortMessage}.`); + } else { + // Commit was successful, but reading the hash was not. Something weird happened, + // but nothing that would stop the update. Just log the weirdness and continue. + logger.info(` Committed migration step: ${shortMessage}.`); + logger.warn(' Failed to look up hash of most recent commit, continuing anyways.'); + } + + return true; +} + +async function getOptionalMigrationsToRun( + logger: logging.Logger, + optionalMigrations: MigrationSchematicDescription[], + packageName: string, +): Promise { + const numberOfMigrations = optionalMigrations.length; + logger.info( + `This package has ${numberOfMigrations} optional migration${ + numberOfMigrations > 1 ? 's' : '' + } that can be executed.`, + ); + + if (!isTTY()) { + for (const migration of optionalMigrations) { + const { title } = getMigrationTitleAndDescription(migration); + logger.info(colors.cyan(figures.pointer) + ' ' + colors.bold(title)); + logger.info(colors.gray(` ng update ${packageName} --name ${migration.name}`)); + logger.info(''); // Extra trailing newline. + } + + return undefined; + } + + logger.info( + 'Optional migrations may be skipped and executed after the update process, if preferred.', + ); + logger.info(''); // Extra trailing newline. + + const answer = await askChoices( + `Select the migrations that you'd like to run`, + optionalMigrations.map((migration) => { + const { title, documentation } = getMigrationTitleAndDescription(migration); + + return { + name: `[${colors.white(migration.name)}] ${title}${documentation ? ` (${documentation})` : ''}`, + value: migration.name, + checked: migration.recommended, + }; + }), + null, + ); + + logger.info(''); // Extra trailing newline. + + return optionalMigrations.filter(({ name }) => answer?.includes(name)); +} + +function getMigrationTitleAndDescription(migration: MigrationSchematicDescription): { + title: string; + description: string; + documentation?: string; +} { + const [title, ...description] = migration.description.split('. '); + + return { + title: title.endsWith('.') ? title : title + '.', + description: description.join('.\n '), + documentation: migration.documentation + ? new URL(migration.documentation, 'https://angular.dev').href + : undefined, + }; +} diff --git a/packages/angular/cli/src/package-managers/error.ts b/packages/angular/cli/src/package-managers/error.ts index c17af3f7cae3..1ce79f2240a7 100644 --- a/packages/angular/cli/src/package-managers/error.ts +++ b/packages/angular/cli/src/package-managers/error.ts @@ -35,3 +35,18 @@ export class PackageManagerError extends Error { super(message); } } + +/** + * Represents structured information about an error returned by a package manager command. + * This is a data interface, not an `Error` subclass. + */ +export interface ErrorInfo { + /** A specific error code (e.g. 'E404', 'EACCES'). */ + readonly code: string; + + /** A short, human-readable summary of the error. */ + readonly summary: string; + + /** An optional, detailed description of the error. */ + readonly detail?: string; +} diff --git a/packages/angular/cli/src/package-managers/host.ts b/packages/angular/cli/src/package-managers/host.ts index 1295154ceacf..82d61031d147 100644 --- a/packages/angular/cli/src/package-managers/host.ts +++ b/packages/angular/cli/src/package-managers/host.ts @@ -13,10 +13,10 @@ * enabling the injection of mock or test-specific implementations. */ -import { spawn } from 'node:child_process'; +import { type SpawnOptions, spawn } from 'node:child_process'; import { Stats } from 'node:fs'; -import { mkdtemp, readdir, rm, stat, writeFile } from 'node:fs/promises'; -import { tmpdir } from 'node:os'; +import { mkdtemp, readFile, readdir, rm, stat, writeFile } from 'node:fs/promises'; +import { platform, tmpdir } from 'node:os'; import { join } from 'node:path'; import { PackageManagerError } from './error'; @@ -38,6 +38,13 @@ export interface Host { */ readdir(path: string): Promise; + /** + * Reads the content of a file. + * @param path The path to the file. + * @returns A promise that resolves to the file content as a string. + */ + readFile(path: string): Promise; + /** * Creates a new, unique temporary directory. * @returns A promise that resolves to the absolute path of the created directory. @@ -85,6 +92,7 @@ export interface Host { export const NodeJS_HOST: Host = { stat, readdir, + readFile: (path: string) => readFile(path, { encoding: 'utf8' }), writeFile, createTempDirectory: () => mkdtemp(join(tmpdir(), 'angular-cli-')), deleteDirectory: (path: string) => rm(path, { recursive: true, force: true }), @@ -99,10 +107,11 @@ export const NodeJS_HOST: Host = { } = {}, ): Promise<{ stdout: string; stderr: string }> => { const signal = options.timeout ? AbortSignal.timeout(options.timeout) : undefined; + const isWin32 = platform() === 'win32'; return new Promise((resolve, reject) => { - const childProcess = spawn(command, args, { - shell: false, + const spawnOptions = { + shell: isWin32, stdio: options.stdio ?? 'pipe', signal, cwd: options.cwd, @@ -110,7 +119,10 @@ export const NodeJS_HOST: Host = { ...process.env, ...options.env, }, - }); + } satisfies SpawnOptions; + const childProcess = isWin32 + ? spawn(`${command} ${args.join(' ')}`, spawnOptions) + : spawn(command, args, spawnOptions); let stdout = ''; childProcess.stdout?.on('data', (data) => (stdout += data.toString())); diff --git a/packages/angular/cli/src/package-managers/package-manager-descriptor.ts b/packages/angular/cli/src/package-managers/package-manager-descriptor.ts index 62a6ae8b79b6..f48ed1e32ed7 100644 --- a/packages/angular/cli/src/package-managers/package-manager-descriptor.ts +++ b/packages/angular/cli/src/package-managers/package-manager-descriptor.ts @@ -12,15 +12,19 @@ * package-manager-specific commands, flags, and output parsing. */ +import { ErrorInfo } from './error'; import { Logger } from './logger'; import { PackageManifest, PackageMetadata } from './package-metadata'; import { InstalledPackage } from './package-tree'; import { parseNpmLikeDependencies, + parseNpmLikeError, parseNpmLikeManifest, parseNpmLikeMetadata, parseYarnClassicDependencies, - parseYarnLegacyManifest, + parseYarnClassicError, + parseYarnClassicManifest, + parseYarnClassicMetadata, parseYarnModernDependencies, } from './parsers'; @@ -73,6 +77,9 @@ export interface PackageManagerDescriptor { /** The command to fetch the registry manifest of a package. */ readonly getManifestCommand: readonly string[]; + /** Whether a specific version lookup is needed prior to fetching a registry manifest. */ + readonly requiresManifestVersionLookup?: boolean; + /** A function that formats the arguments for field-filtered registry views. */ readonly viewCommandFieldArgFormatter?: (fields: readonly string[]) => string[]; @@ -82,16 +89,34 @@ export interface PackageManagerDescriptor { listDependencies: (stdout: string, logger?: Logger) => Map; /** A function to parse the output of `getManifestCommand` for a specific version. */ - getPackageManifest: (stdout: string, logger?: Logger) => PackageManifest | null; + getRegistryManifest: (stdout: string, logger?: Logger) => PackageManifest | null; /** A function to parse the output of `getManifestCommand` for the full package metadata. */ getRegistryMetadata: (stdout: string, logger?: Logger) => PackageMetadata | null; + + /** A function to parse the output when a command fails. */ + getError?: (output: string, logger?: Logger) => ErrorInfo | null; }; + + /** A function that checks if a structured error represents a "package not found" error. */ + readonly isNotFound: (error: ErrorInfo) => boolean; } /** A type that represents the name of a supported package manager. */ export type PackageManagerName = keyof typeof SUPPORTED_PACKAGE_MANAGERS; +/** A set of error codes that are known to indicate a "package not found" error. */ +const NOT_FOUND_ERROR_CODES = new Set(['E404']); + +/** + * A shared function to check if a structured error represents a "package not found" error. + * @param error The structured error to check. + * @returns True if the error code is a known "not found" code, false otherwise. + */ +function isKnownNotFound(error: ErrorInfo): boolean { + return NOT_FOUND_ERROR_CODES.has(error.code); +} + /** * A map of supported package managers to their descriptors. * This is the single source of truth for all package-manager-specific @@ -122,9 +147,11 @@ export const SUPPORTED_PACKAGE_MANAGERS = { viewCommandFieldArgFormatter: (fields) => [...fields], outputParsers: { listDependencies: parseNpmLikeDependencies, - getPackageManifest: parseNpmLikeManifest, + getRegistryManifest: parseNpmLikeManifest, getRegistryMetadata: parseNpmLikeMetadata, + getError: parseNpmLikeError, }, + isNotFound: isKnownNotFound, }, yarn: { binary: 'yarn', @@ -144,9 +171,11 @@ export const SUPPORTED_PACKAGE_MANAGERS = { viewCommandFieldArgFormatter: (fields) => ['--fields', fields.join(',')], outputParsers: { listDependencies: parseYarnModernDependencies, - getPackageManifest: parseNpmLikeManifest, + getRegistryManifest: parseNpmLikeManifest, getRegistryMetadata: parseNpmLikeMetadata, + getError: parseNpmLikeError, }, + isNotFound: isKnownNotFound, }, 'yarn-classic': { binary: 'yarn', @@ -165,12 +194,15 @@ export const SUPPORTED_PACKAGE_MANAGERS = { getRegistryOptions: (registry: string) => ({ args: ['--registry', registry] }), versionCommand: ['--version'], listDependenciesCommand: ['list', '--depth=0', '--json'], - getManifestCommand: ['info', '--json'], + getManifestCommand: ['info', '--json', '--verbose'], + requiresManifestVersionLookup: true, outputParsers: { listDependencies: parseYarnClassicDependencies, - getPackageManifest: parseYarnLegacyManifest, - getRegistryMetadata: parseNpmLikeMetadata, + getRegistryManifest: parseYarnClassicManifest, + getRegistryMetadata: parseYarnClassicMetadata, + getError: parseYarnClassicError, }, + isNotFound: isKnownNotFound, }, pnpm: { binary: 'pnpm', @@ -190,9 +222,11 @@ export const SUPPORTED_PACKAGE_MANAGERS = { viewCommandFieldArgFormatter: (fields) => [...fields], outputParsers: { listDependencies: parseNpmLikeDependencies, - getPackageManifest: parseNpmLikeManifest, + getRegistryManifest: parseNpmLikeManifest, getRegistryMetadata: parseNpmLikeMetadata, + getError: parseNpmLikeError, }, + isNotFound: isKnownNotFound, }, bun: { binary: 'bun', @@ -209,12 +243,13 @@ export const SUPPORTED_PACKAGE_MANAGERS = { versionCommand: ['--version'], listDependenciesCommand: ['pm', 'ls', '--json'], getManifestCommand: ['pm', 'view', '--json'], - viewCommandFieldArgFormatter: (fields) => [...fields], outputParsers: { listDependencies: parseNpmLikeDependencies, - getPackageManifest: parseNpmLikeManifest, + getRegistryManifest: parseNpmLikeManifest, getRegistryMetadata: parseNpmLikeMetadata, + getError: parseNpmLikeError, }, + isNotFound: isKnownNotFound, }, } satisfies Record; diff --git a/packages/angular/cli/src/package-managers/package-manager.ts b/packages/angular/cli/src/package-managers/package-manager.ts index 4f4620994769..b76831be109c 100644 --- a/packages/angular/cli/src/package-managers/package-manager.ts +++ b/packages/angular/cli/src/package-managers/package-manager.ts @@ -13,6 +13,8 @@ */ import { join } from 'node:path'; +import npa from 'npm-package-arg'; +import { maxSatisfying } from 'semver'; import { PackageManagerError } from './error'; import { Host } from './host'; import { Logger } from './logger'; @@ -155,12 +157,14 @@ export class PackageManager { return { stdout: '', stderr: '' }; } - return this.host.runCommand(this.descriptor.binary, finalArgs, { + const commandResult = await this.host.runCommand(this.descriptor.binary, finalArgs, { ...runOptions, cwd: executionDirectory, stdio: 'pipe', env: finalEnv, }); + + return { stdout: commandResult.stdout.trim(), stderr: commandResult.stderr.trim() }; } /** @@ -190,20 +194,56 @@ export class PackageManager { let stdout; let stderr; + let exitCode; + let thrownError; try { ({ stdout, stderr } = await this.#run(args, runOptions)); + exitCode = 0; } catch (e) { - if (e instanceof PackageManagerError && typeof e.exitCode === 'number' && e.exitCode !== 0) { - // Some package managers exit with a non-zero code when the package is not found. + thrownError = e; + if (e instanceof PackageManagerError) { + stdout = e.stdout; + stderr = e.stderr; + exitCode = e.exitCode; + } else { + // Re-throw unexpected errors + throw e; + } + } + + // Yarn classic can exit with code 0 even when an error occurs. + // To ensure we capture these cases, we will always attempt to parse a + // structured error from the output, regardless of the exit code. + const getError = this.descriptor.outputParsers.getError; + const parsedError = + getError?.(stdout, this.options.logger) ?? getError?.(stderr, this.options.logger) ?? null; + + if (parsedError) { + this.options.logger?.debug( + `[${this.descriptor.binary}] Structured error (code: ${parsedError.code}): ${parsedError.summary}`, + ); + + // Special case for 'not found' errors (e.g., E404). Return null for these. + if (this.descriptor.isNotFound(parsedError)) { if (cache && cacheKey) { cache.set(cacheKey, null); } return null; + } else { + // For all other structured errors, throw a more informative error. + throw new PackageManagerError(parsedError.summary, stdout, stderr, exitCode); } - throw e; } + // If an error was originally thrown and we didn't parse a more specific + // structured error, re-throw the original error now. + if (thrownError) { + throw thrownError; + } + + // If we reach this point, the command succeeded and no structured error was found. + // We can now safely parse the successful output. try { const result = parser(stdout, this.options.logger); if (cache && cacheKey) { @@ -215,7 +255,7 @@ export class PackageManager { const message = `Failed to parse package manager output: ${ e instanceof Error ? e.message : '' }`; - throw new PackageManagerError(message, stdout, stderr, 0); + throw new PackageManagerError(message, stdout, stderr, exitCode); } } @@ -353,7 +393,7 @@ export class PackageManager { * @param options.bypassCache If true, ignores the in-memory cache and fetches fresh data. * @returns A promise that resolves to the `PackageManifest` object, or `null` if the package is not found. */ - async getPackageManifest( + async getRegistryManifest( packageName: string, version: string, options: { timeout?: number; registry?: string; bypassCache?: boolean } = {}, @@ -367,11 +407,119 @@ export class PackageManager { const cacheKey = options.registry ? `${specifier}|${options.registry}` : specifier; - return this.#fetchAndParse( + const manifest = await this.#fetchAndParse( commandArgs, - (stdout, logger) => this.descriptor.outputParsers.getPackageManifest(stdout, logger), + (stdout, logger) => this.descriptor.outputParsers.getRegistryManifest(stdout, logger), { ...options, cache: this.#manifestCache, cacheKey }, ); + + // If the provided version was not a specific version, also cache the specific fetched version + if (manifest && manifest.version !== version) { + const manifestSpecifier = `${manifest.name}@${manifest.version}`; + const manifestCacheKey = options.registry + ? `${manifestSpecifier}|${options.registry}` + : manifestSpecifier; + this.#manifestCache.set(manifestCacheKey, manifest); + } + + return manifest; + } + + /** + * Fetches the manifest for a package. + * + * This method can resolve manifests for packages from the registry, as well + * as those specified by file paths, directory paths, and remote tarballs. + * Caching is only supported for registry packages. + * + * @param specifier The package specifier to resolve the manifest for. + * @param options Options for the fetch. + * @returns A promise that resolves to the `PackageManifest` object, or `null` if the package is not found. + */ + async getManifest( + specifier: string | npa.Result, + options: { timeout?: number; registry?: string; bypassCache?: boolean } = {}, + ): Promise { + const { name, type, fetchSpec } = typeof specifier === 'string' ? npa(specifier) : specifier; + + switch (type) { + case 'range': + case 'version': + case 'tag': { + if (!name) { + throw new Error(`Could not parse package name from specifier: ${specifier}`); + } + + // `fetchSpec` is the version, range, or tag. + let versionSpec = fetchSpec ?? 'latest'; + if (this.descriptor.requiresManifestVersionLookup) { + if (type === 'tag' || !fetchSpec) { + const metadata = await this.getRegistryMetadata(name, options); + if (!metadata) { + return null; + } + versionSpec = metadata['dist-tags'][versionSpec]; + } else if (type === 'range') { + const metadata = await this.getRegistryMetadata(name, options); + if (!metadata) { + return null; + } + versionSpec = maxSatisfying(metadata.versions, fetchSpec) ?? ''; + } + if (!versionSpec) { + return null; + } + } + + return this.getRegistryManifest(name, versionSpec, options); + } + case 'directory': { + if (!fetchSpec) { + throw new Error(`Could not parse directory path from specifier: ${specifier}`); + } + + const manifestPath = join(fetchSpec, 'package.json'); + const manifest = await this.host.readFile(manifestPath); + + return JSON.parse(manifest); + } + case 'file': + case 'remote': + case 'git': { + if (!fetchSpec) { + throw new Error(`Could not parse location from specifier: ${specifier}`); + } + + // Caching is not supported for non-registry specifiers. + const { workingDirectory, cleanup } = await this.acquireTempPackage(fetchSpec, { + ...options, + ignoreScripts: true, + }); + + try { + // Discover the package name by reading the temporary `package.json` file. + // The package manager will have added the package to the `dependencies`. + const tempManifest = await this.host.readFile(join(workingDirectory, 'package.json')); + const { dependencies } = JSON.parse(tempManifest) as PackageManifest; + const packageName = dependencies && Object.keys(dependencies)[0]; + + if (!packageName) { + throw new Error(`Could not determine package name for specifier: ${specifier}`); + } + + // The package will be installed in `/node_modules/`. + const packagePath = join(workingDirectory, 'node_modules', packageName); + const manifestPath = join(packagePath, 'package.json'); + const manifest = await this.host.readFile(manifestPath); + + return JSON.parse(manifest); + } finally { + await cleanup(); + } + } + default: + throw new Error(`Unsupported package specifier type: ${type}`); + } } /** @@ -379,14 +527,14 @@ export class PackageManager { * responsible for managing the lifecycle of the temporary directory by calling * the returned `cleanup` function. * - * @param packageName The name of the package to install. + * @param specifier The specifier of the package to install. * @param options Options for the installation. * @returns A promise that resolves to an object containing the temporary path * and a cleanup function. */ async acquireTempPackage( - packageName: string, - options: { registry?: string } = {}, + specifier: string, + options: { registry?: string; ignoreScripts?: boolean } = {}, ): Promise<{ workingDirectory: string; cleanup: () => Promise }> { const workingDirectory = await this.host.createTempDirectory(); const cleanup = () => this.host.deleteDirectory(workingDirectory); @@ -396,7 +544,10 @@ export class PackageManager { // Writing an empty package.json file beforehand prevents this. await this.host.writeFile(join(workingDirectory, 'package.json'), '{}'); - const args: readonly string[] = [this.descriptor.addCommand, packageName]; + const flags = [options.ignoreScripts ? this.descriptor.ignoreScriptsFlag : ''].filter( + (flag) => flag, + ); + const args: readonly string[] = [this.descriptor.addCommand, specifier, ...flags]; try { await this.#run(args, { ...options, cwd: workingDirectory }); diff --git a/packages/angular/cli/src/package-managers/parsers.ts b/packages/angular/cli/src/package-managers/parsers.ts index e14b455a4fe6..0e12fd5f0cfb 100644 --- a/packages/angular/cli/src/package-managers/parsers.ts +++ b/packages/angular/cli/src/package-managers/parsers.ts @@ -12,6 +12,7 @@ * into their own file improves modularity and allows for focused testing. */ +import { ErrorInfo } from './error'; import { Logger } from './logger'; import { PackageManifest, PackageMetadata } from './package-metadata'; import { InstalledPackage } from './package-tree'; @@ -31,6 +32,26 @@ function logStdout(stdout: string, logger?: Logger): void { logger.debug(` stdout:\n${output}`); } +/** + * A generator function that parses a string containing JSONL (newline-delimited JSON) + * and yields each successfully parsed JSON object. + * @param output The string output to parse. + * @param logger An optional logger instance. + */ +// eslint-disable-next-line @typescript-eslint/no-explicit-any +function* parseJsonLines(output: string, logger?: Logger): Generator { + for (const line of output.split('\n')) { + if (!line.trim()) { + continue; + } + try { + yield JSON.parse(line); + } catch (e) { + logger?.debug(` Ignoring non-JSON line: ${e}`); + } + } +} + interface NpmListDependency { version: string; path?: string; @@ -106,7 +127,7 @@ export function parseNpmLikeDependencies( * Parses the output of `yarn list` (classic). * * The expected output is a JSON stream (JSONL), where each line is a JSON object. - * The relevant object has a `type` of `'tree'`. + * The relevant object has a `type` of `'tree'` with a `data` property. * Yarn classic does not provide a path, so the `path` property will be `undefined`. * * ```json @@ -131,11 +152,7 @@ export function parseYarnClassicDependencies( return dependencies; } - for (const line of stdout.split('\n')) { - if (!line) { - continue; - } - const json = JSON.parse(line); + for (const json of parseJsonLines(stdout, logger)) { if (json.type === 'tree' && json.data?.trees) { for (const info of json.data.trees) { const name = info.name.split('@')[0]; @@ -202,25 +219,16 @@ export function parseYarnModernDependencies( ` Failed to parse as single JSON object: ${e}. Falling back to line-by-line parsing.`, ); // Fallback for older versions of yarn berry that might still output json lines - for (const line of stdout.split('\n')) { - if (!line) { - continue; - } - try { - const json = JSON.parse(line); - if (json.type === 'tree' && json.data?.trees) { - for (const info of json.data.trees) { - const name = info.name.split('@')[0]; - const version = info.name.split('@').pop(); - dependencies.set(name, { - name, - version, - }); - } + for (const json of parseJsonLines(stdout, logger)) { + if (json.type === 'tree' && json.data?.trees) { + for (const info of json.data.trees) { + const name = info.name.split('@')[0]; + const version = info.name.split('@').pop(); + dependencies.set(name, { + name, + version, + }); } - } catch (innerError) { - logger?.debug(` Ignoring non-JSON line: ${innerError}`); - // Ignore lines that are not valid JSON. } } } @@ -269,12 +277,19 @@ export function parseNpmLikeMetadata(stdout: string, logger?: Logger): PackageMe } /** - * Parses the output of `yarn info` (classic). + * Parses the output of `yarn info` (classic) to get a package manifest. + * + * When `yarn info --verbose` is used, the output is a JSONL stream. This function + * iterates through the lines to find the object with `type: 'inspect'` which contains + * the package manifest. + * + * For non-verbose output, it falls back to parsing a single JSON object. + * * @param stdout The standard output of the command. * @param logger An optional logger instance. - * @returns The package manifest object. + * @returns The package manifest object, or `null` if not found. */ -export function parseYarnLegacyManifest(stdout: string, logger?: Logger): PackageManifest | null { +export function parseYarnClassicManifest(stdout: string, logger?: Logger): PackageManifest | null { logger?.debug(`Parsing yarn classic manifest...`); logStdout(stdout, logger); @@ -284,8 +299,223 @@ export function parseYarnLegacyManifest(stdout: string, logger?: Logger): Packag return null; } - const data = JSON.parse(stdout); + // Yarn classic outputs JSONL. We need to find the relevant object. + let manifest; + for (const json of parseJsonLines(stdout, logger)) { + // The manifest data is in a JSON object with type 'inspect'. + if (json.type === 'inspect' && json.data) { + manifest = json.data; + break; + } + } + + if (!manifest) { + logger?.debug(' Failed to find manifest in yarn classic output.'); + + return null; + } + + // Yarn classic removes any field with a falsy value + // https://github.com/yarnpkg/yarn/blob/7cafa512a777048ce0b666080a24e80aae3d66a9/src/cli/commands/info.js#L26-L29 + // Add a default of 'false' for the `save` field when the `ng-add` object is present but does not have any fields. + // There is a small chance this causes an incorrect value. However, the use of `ng-add` is rare and, in the cases + // it is used, save is set to either a `false` literal or a truthy value. Special cases can be added for specific + // packages if discovered. + if ( + manifest['ng-add'] && + typeof manifest['ng-add'] === 'object' && + Object.keys(manifest['ng-add']).length === 0 + ) { + manifest['ng-add'].save ??= false; + } + + return manifest; +} + +/** + * Parses the output of `yarn info` (classic) to get package metadata. + * @param stdout The standard output of the command. + * @param logger An optional logger instance. + * @returns The package metadata object. + */ +export function parseYarnClassicMetadata(stdout: string, logger?: Logger): PackageMetadata | null { + logger?.debug(`Parsing yarn classic metadata...`); + logStdout(stdout, logger); + + if (!stdout) { + logger?.debug(' stdout is empty. No metadata found.'); + + return null; + } + + // Yarn classic outputs JSONL. We need to find the relevant object. + let metadata; + for (const json of parseJsonLines(stdout, logger)) { + // The metadata data is in a JSON object with type 'inspect'. + if (json.type === 'inspect' && json.data) { + metadata = json.data; + break; + } + } + + if (!metadata) { + logger?.debug(' Failed to find metadata in yarn classic output.'); + + return null; + } + + return metadata; +} + +/** + * Parses the `stdout` or `stderr` output of npm, pnpm, modern yarn, or bun to extract structured error information. + * + * This parser uses a multi-stage approach. It first attempts to parse the entire `output` as a + * single JSON object, which is the standard for modern tools like pnpm, yarn, and bun. If JSON + * parsing fails, it falls back to a line-by-line regex-based approach to handle the plain + * text output from older versions of npm. + * + * Example JSON output (pnpm): + * ```json + * { + * "code": "E404", + * "summary": "Not Found - GET https://registry.npmjs.org/@angular%2fnon-existent - Not found", + * "detail": "The requested resource '@angular/non-existent@*' could not be found or you do not have permission to access it." + * } + * ``` + * + * Example text output (npm): + * ``` + * npm error code E404 + * npm error 404 Not Found - GET https://registry.npmjs.org/@angular%2fnon-existent - Not found + * ``` + * + * @param output The standard output or standard error of the command. + * @param logger An optional logger instance. + * @returns An `ErrorInfo` object if parsing is successful, otherwise `null`. + */ +export function parseNpmLikeError(output: string, logger?: Logger): ErrorInfo | null { + logger?.debug(`Parsing npm-like error output...`); + logStdout(output, logger); // Log output for debugging purposes + + if (!output) { + logger?.debug(' output is empty. No error found.'); + + return null; + } + + // Attempt to parse as JSON first (common for pnpm, modern yarn, bun) + try { + const jsonError = JSON.parse(output); + if ( + jsonError && + typeof jsonError.code === 'string' && + (typeof jsonError.summary === 'string' || typeof jsonError.message === 'string') + ) { + const summary = jsonError.summary || jsonError.message; + logger?.debug(` Successfully parsed JSON error with code '${jsonError.code}'.`); + + return { + code: jsonError.code, + summary, + detail: jsonError.detail, + }; + } + } catch (e) { + logger?.debug(` Failed to parse output as JSON: ${e}. Attempting regex fallback.`); + // Fallback to regex for plain text errors (common for npm) + } + + // Regex for npm-like error codes (e.g., `npm ERR! code E404` or `npm error code E404`) + const errorCodeMatch = output.match(/npm (ERR!|error) code (E\d{3}|[A-Z_]+)/); + if (errorCodeMatch) { + const code = errorCodeMatch[2]; // Capture group 2 is the actual error code + let summary: string | undefined; + + // Find the most descriptive summary line (the line after `npm ERR! code ...` or `npm error code ...`). + for (const line of output.split('\n')) { + if (line.startsWith('npm ERR!') && !line.includes(' code ')) { + summary = line.replace('npm ERR! ', '').trim(); + break; + } else if (line.startsWith('npm error') && !line.includes(' code ')) { + summary = line.replace('npm error ', '').trim(); + break; + } + } + + logger?.debug(` Successfully parsed text error with code '${code}'.`); + + return { + code, + summary: summary || `Package manager error: ${code}`, + }; + } + + logger?.debug(' Failed to parse npm-like error. No structured error found.'); + + return null; +} + +/** + * Parses the `stdout` or `stderr` output of yarn classic to extract structured error information. + * + * This parser first attempts to find an HTTP status code (e.g., 404, 401) in the verbose output. + * If found, it returns a standardized error code (`E${statusCode}`). + * If no HTTP status code is found, it falls back to parsing generic JSON error lines. + * + * Example verbose output (with HTTP status code): + * ```json + * {"type":"verbose","data":"Request \"https://registry.npmjs.org/@angular%2fnon-existent\" finished with status code 404."} + * ``` + * + * Example generic JSON error output: + * ```json + * {"type":"error","data":"Received invalid response from npm."} + * ``` + * + * @param output The standard output or standard error of the command. + * @param logger An optional logger instance. + * @returns An `ErrorInfo` object if parsing is successful, otherwise `null`. + */ +export function parseYarnClassicError(output: string, logger?: Logger): ErrorInfo | null { + logger?.debug(`Parsing yarn classic error output...`); + logStdout(output, logger); // Log output for debugging purposes + + if (!output) { + logger?.debug(' output is empty. No error found.'); + + return null; + } + + // First, check for any HTTP status code in the verbose output. + const statusCodeMatch = output.match(/finished with status code (\d{3})/); + if (statusCodeMatch) { + const statusCode = Number(statusCodeMatch[1]); + // Status codes in the 200-299 range are successful. + if (statusCode < 200 || statusCode >= 300) { + logger?.debug(` Detected HTTP error status code '${statusCode}' in verbose output.`); + + return { + code: `E${statusCode}`, + summary: `Request failed with status code ${statusCode}.`, + }; + } + } + + // Fallback to the JSON error type if no HTTP status code is present. + for (const json of parseJsonLines(output, logger)) { + if (json.type === 'error' && typeof json.data === 'string') { + const summary = json.data; + logger?.debug(` Successfully parsed generic yarn classic error.`); + + return { + code: 'UNKNOWN_ERROR', + summary, + }; + } + } + + logger?.debug(' Failed to parse yarn classic error. No structured error found.'); - // Yarn classic wraps the manifest in a `data` property. - return data.data ?? data; + return null; } diff --git a/packages/angular/cli/src/package-managers/parsers_spec.ts b/packages/angular/cli/src/package-managers/parsers_spec.ts new file mode 100644 index 000000000000..8717a6d1a5a1 --- /dev/null +++ b/packages/angular/cli/src/package-managers/parsers_spec.ts @@ -0,0 +1,112 @@ +/** + * @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 { parseNpmLikeError, parseYarnClassicError } from './parsers'; + +describe('parsers', () => { + describe('parseNpmLikeError', () => { + it('should parse a structured JSON error from modern yarn', () => { + const stdout = JSON.stringify({ + code: 'ERR_PNPM_NO_SUCH_PACKAGE', + summary: 'No such package.', + detail: 'Package not found.', + }); + const error = parseNpmLikeError(stdout); + expect(error).toEqual({ + code: 'ERR_PNPM_NO_SUCH_PACKAGE', + summary: 'No such package.', + detail: 'Package not found.', + }); + }); + + it('should parse a plain text error from npm', () => { + const stdout = + 'npm error code E404\nnpm error 404 Not Found - GET https://registry.example.com/non-existent-package'; + const error = parseNpmLikeError(stdout); + expect(error).toEqual({ + code: 'E404', + summary: '404 Not Found - GET https://registry.example.com/non-existent-package', + }); + }); + + it('should parse a plain text error from npm with ERR!', () => { + const stderr = + 'npm ERR! code E404\nnpm ERR! 404 Not Found - GET https://registry.example.com/non-existent-package'; + const error = parseNpmLikeError(stderr); + expect(error).toEqual({ + code: 'E404', + summary: '404 Not Found - GET https://registry.example.com/non-existent-package', + }); + }); + + it('should parse a structured JSON error with a message property', () => { + const stderr = JSON.stringify({ + code: 'EUNSUPPORTEDPROTOCOL', + message: 'Unsupported protocol.', + detail: 'The protocol "invalid:" is not supported.', + }); + const error = parseNpmLikeError(stderr); + expect(error).toEqual({ + code: 'EUNSUPPORTEDPROTOCOL', + summary: 'Unsupported protocol.', + detail: 'The protocol "invalid:" is not supported.', + }); + }); + + it('should return null for empty stdout', () => { + const error = parseNpmLikeError(''); + expect(error).toBeNull(); + }); + + it('should return null for unparsable stdout', () => { + const error = parseNpmLikeError('An unexpected error occurred.'); + expect(error).toBeNull(); + }); + }); + + describe('parseYarnClassicError', () => { + it('should parse a 404 from verbose logs', () => { + const stdout = + '{"type":"verbose","data":"Request "https://registry.example.com/non-existent-package" finished with status code 404."}'; + const error = parseYarnClassicError(stdout); + expect(error).toEqual({ + code: 'E404', + summary: 'Request failed with status code 404.', + }); + }); + + it('should parse a non-404 HTTP error from verbose logs', () => { + const stdout = + '{"type":"verbose","data":"Request "https://registry.example.com/private-package" finished with status code 401."}'; + const error = parseYarnClassicError(stdout); + expect(error).toEqual({ + code: 'E401', + summary: 'Request failed with status code 401.', + }); + }); + + it('should parse a generic JSON error when no HTTP status is found', () => { + const stdout = '{"type":"error","data":"An unexpected error occurred."}'; + const error = parseYarnClassicError(stdout); + expect(error).toEqual({ + code: 'UNKNOWN_ERROR', + summary: 'An unexpected error occurred.', + }); + }); + + it('should return null for empty stdout', () => { + const error = parseYarnClassicError(''); + expect(error).toBeNull(); + }); + + it('should return null for unparsable stdout', () => { + const error = parseYarnClassicError('A random error message.'); + expect(error).toBeNull(); + }); + }); +}); diff --git a/packages/angular/cli/src/package-managers/testing/mock-host.ts b/packages/angular/cli/src/package-managers/testing/mock-host.ts index 69b252501850..af518553a61d 100644 --- a/packages/angular/cli/src/package-managers/testing/mock-host.ts +++ b/packages/angular/cli/src/package-managers/testing/mock-host.ts @@ -58,4 +58,8 @@ export class MockHost implements Host { writeFile(): Promise { throw new Error('Method not implemented.'); } + + readFile(): Promise { + throw new Error('Method not implemented.'); + } } 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 64b6f29ee270..16498600f603 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,6 +61,7 @@ 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", @@ -80,7 +81,6 @@ pkg_tar( name = "npm_package_archive", srcs = [":npm_package"], extension = "tgz", - strip_prefix = "./npm_package", # should not be built unless it is a dependency of another rule tags = ["manual"], ) diff --git a/packages/angular/ssr/package.json b/packages/angular/ssr/package.json index 8037c8a075e6..b3ca917e1b08 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-rc.2", - "@angular/compiler": "21.0.0-rc.2", - "@angular/core": "21.0.0-rc.2", - "@angular/platform-browser": "21.0.0-rc.2", - "@angular/platform-server": "21.0.0-rc.2", - "@angular/router": "21.0.0-rc.2", + "@angular/common": "21.1.0-next.4", + "@angular/compiler": "21.1.0-next.4", + "@angular/core": "21.1.0-next.4", + "@angular/platform-browser": "21.1.0-next.4", + "@angular/platform-server": "21.1.0-next.4", + "@angular/router": "21.1.0-next.4", "@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 dd204a4b595f..0cb728e8535d 100644 --- a/packages/angular/ssr/src/app-engine.ts +++ b/packages/angular/ssr/src/app-engine.ts @@ -38,7 +38,7 @@ export class AngularAppEngine { * * @private */ - static ɵhooks = /* #__PURE__*/ new Hooks(); + static ɵhooks: Hooks = /* #__PURE__*/ new Hooks(); /** * The manifest for the server application. diff --git a/packages/angular/ssr/src/app.ts b/packages/angular/ssr/src/app.ts index 4895866d715b..96afaa44c8d6 100644 --- a/packages/angular/ssr/src/app.ts +++ b/packages/angular/ssr/src/app.ts @@ -25,8 +25,20 @@ import { InlineCriticalCssProcessor } from './utils/inline-critical-css'; import { LRUCache } from './utils/lru-cache'; import { AngularBootstrap, renderAngular } from './utils/ng'; import { promiseWithAbort } from './utils/promise'; +import { createRedirectResponse } from './utils/redirect'; import { buildPathWithParams, joinUrlParts, stripLeadingSlash } from './utils/url'; +/** + * A set of well-known URLs that are not handled by Angular. + * + * These URLs are typically for static assets or endpoints that should + * bypass the Angular routing and rendering process. + */ +const WELL_KNOWN_NON_ANGULAR_URLS: ReadonlySet = new Set([ + '/favicon.ico', + '/.well-known/appspecific/com.chrome.devtools.json', +]); + /** * Maximum number of critical CSS entries the cache can store. * This value determines the capacity of the LRU (Least Recently Used) cache, which stores critical CSS for pages. @@ -166,6 +178,10 @@ export class AngularServerApp { */ async handle(request: Request, requestContext?: unknown): Promise { const url = new URL(request.url); + if (WELL_KNOWN_NON_ANGULAR_URLS.has(url.pathname)) { + return null; + } + this.router ??= await ServerRouter.from(this.manifest, url); const matchedRoute = this.router.match(url); @@ -175,8 +191,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) { @@ -329,7 +352,7 @@ export class AngularServerApp { } if (result.redirectTo) { - return createRedirectResponse(result.redirectTo, status); + return createRedirectResponse(result.redirectTo, responseInit.status); } if (renderMode === RenderMode.Prerender) { @@ -524,20 +547,3 @@ function appendPreloadHintsToHtml(html: string, preload: readonly string[]): str html.slice(bodyCloseIdx), ].join('\n'); } - -/** - * Creates an HTTP redirect response with a specified location and status code. - * - * @param location - The URL to which the response should redirect. - * @param status - The HTTP status code for the redirection. Defaults to 302 (Found). - * See: https://developer.mozilla.org/en-US/docs/Web/API/Response/redirect_static#status - * @returns A `Response` object representing the HTTP redirect. - */ -function createRedirectResponse(location: string, status = 302): Response { - return new Response(null, { - status, - headers: { - 'Location': location, - }, - }); -} diff --git a/packages/angular/ssr/src/routes/ng-routes.ts b/packages/angular/ssr/src/routes/ng-routes.ts index e46dd685511a..b60e704371a4 100644 --- a/packages/angular/ssr/src/routes/ng-routes.ts +++ b/packages/angular/ssr/src/routes/ng-routes.ts @@ -28,6 +28,7 @@ import { Console } from '../console'; import { AngularAppManifest, getAngularAppManifest } from '../manifest'; import { AngularBootstrap, isNgModule } from '../utils/ng'; import { promiseWithAbort } from '../utils/promise'; +import { VALID_REDIRECT_RESPONSE_CODES, isValidRedirectResponseCode } from '../utils/redirect'; import { addTrailingSlash, joinUrlParts, stripLeadingSlash } from '../utils/url'; import { PrerenderFallback, @@ -59,11 +60,6 @@ const CATCH_ALL_REGEXP = /\/(\*\*)$/; */ const URL_PARAMETER_REGEXP = /(? = {}> * * @param node - The current node to start the traversal from. Defaults to the root node of the tree. */ - *traverse(node = this.root): Generator { + *traverse( + node: RouteTreeNode = this.root, + ): Generator { if (node.metadata) { yield node.metadata; } 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/redirect.ts b/packages/angular/ssr/src/utils/redirect.ts new file mode 100644 index 000000000000..7a65ff472a2a --- /dev/null +++ b/packages/angular/ssr/src/utils/redirect.ts @@ -0,0 +1,46 @@ +/** + * @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 + */ + +/** + * An set of HTTP status codes that are considered valid for redirect responses. + */ +export const VALID_REDIRECT_RESPONSE_CODES = new Set([301, 302, 303, 307, 308]); + +/** + * Checks if the given HTTP status code is a valid redirect response code. + * + * @param code The HTTP status code to check. + * @returns `true` if the code is a valid redirect response code, `false` otherwise. + */ +export function isValidRedirectResponseCode(code: number): boolean { + return VALID_REDIRECT_RESPONSE_CODES.has(code); +} + +/** + * Creates an HTTP redirect response with a specified location and status code. + * + * @param location - The URL to which the response should redirect. + * @param status - The HTTP status code for the redirection. Defaults to 302 (Found). + * See: https://developer.mozilla.org/en-US/docs/Web/API/Response/redirect_static#status + * @returns A `Response` object representing the HTTP redirect. + */ +export function createRedirectResponse(location: string, status = 302): Response { + if (ngDevMode && !isValidRedirectResponseCode(status)) { + throw new Error( + `Invalid redirect status code: ${status}. ` + + `Please use one of the following redirect response codes: ${[...VALID_REDIRECT_RESPONSE_CODES.values()].join(', ')}.`, + ); + } + + return new Response(null, { + status, + headers: { + 'Location': location, + }, + }); +} 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_spec.ts b/packages/angular/ssr/test/app_spec.ts index f85d4700329f..9584feafeb72 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, RESPONSE_INIT, inject } from '@angular/core'; import { CanActivateFn, Router } from '@angular/router'; import { AngularServerApp } from '../src/app'; import { RenderMode } from '../src/routes/route-config'; @@ -32,6 +33,11 @@ describe('AngularServerApp', () => { }) class RedirectComponent { constructor() { + const responseInit = inject(RESPONSE_INIT); + if (responseInit) { + responseInit.status = 308; + } + void inject(Router).navigate([], { queryParams: { filter: 'test' }, }); @@ -124,12 +130,28 @@ describe('AngularServerApp', () => { hash: 'f799132d0a09e0fef93c68a12e443527700eb59e6f67fcb7854c3a60ff082fde', }, }, + undefined, + undefined, + [ + { + provide: APP_BASE_HREF, + useFactory: () => inject(REQUEST)?.headers.get('X-Forwarded-Prefix'), + }, + ], ); app = new AngularServerApp(); }); describe('handle', () => { + it('should return null for well-known non-angular URLs', async () => { + const response = await app.handle( + new Request('http://localhost/.well-known/appspecific/com.chrome.devtools.json'), + ); + + expect(response).toBeNull(); + }); + describe('CSR and SSG pages', () => { it('should correctly render the content for the requested page', async () => { const response = await app.handle(new Request('http://localhost/home')); @@ -301,7 +323,7 @@ describe('AngularServerApp', () => { it('returns a 302 status and redirects to the correct location when `router.navigate` is used', async () => { const response = await app.handle(new Request('http://localhost/redirect-via-navigate')); expect(response?.headers.get('location')).toBe('/redirect-via-navigate?filter=test'); - expect(response?.status).toBe(302); + expect(response?.status).toBe(308); }); it('returns a 302 status and redirects to the correct location when `urlTree` is updated in a guard', async () => { @@ -309,6 +331,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_devkit/architect/BUILD.bazel b/packages/angular_devkit/architect/BUILD.bazel index 1c550d124e84..92fce0d2bfb3 100644 --- a/packages/angular_devkit/architect/BUILD.bazel +++ b/packages/angular_devkit/architect/BUILD.bazel @@ -110,6 +110,8 @@ npm_package( "README.md", ":architect", ":license", + "//packages/angular_devkit/architect/bin", + "//packages/angular_devkit/architect/bin:cli.js", "//packages/angular_devkit/architect/node", "//packages/angular_devkit/architect/testing", ], diff --git a/packages/angular_devkit/architect/bin/BUILD.bazel b/packages/angular_devkit/architect/bin/BUILD.bazel new file mode 100644 index 000000000000..cb7d761ac84d --- /dev/null +++ b/packages/angular_devkit/architect/bin/BUILD.bazel @@ -0,0 +1,28 @@ +# Copyright Google Inc. 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 + +load("//tools:defaults.bzl", "ts_project") + +licenses(["notice"]) + +package(default_visibility = ["//visibility:public"]) + +exports_files([ + "cli.js", +]) + +ts_project( + name = "bin", + srcs = glob( + include = ["**/*.ts"], + exclude = ["**/*_spec.ts"], + ), + deps = [ + "//:node_modules/@types/node", + "//packages/angular_devkit/architect", + "//packages/angular_devkit/architect:node_modules/@angular-devkit/core", + "//packages/angular_devkit/architect/node", + ], +) diff --git a/packages/angular_devkit/architect_cli/bin/architect.ts b/packages/angular_devkit/architect/bin/architect.ts similarity index 53% rename from packages/angular_devkit/architect_cli/bin/architect.ts rename to packages/angular_devkit/architect/bin/architect.ts index 8cba7b4c11a7..acfd798b89a2 100644 --- a/packages/angular_devkit/architect_cli/bin/architect.ts +++ b/packages/angular_devkit/architect/bin/architect.ts @@ -7,15 +7,13 @@ * found in the LICENSE file at https://angular.dev/license */ -import { Architect, BuilderInfo, BuilderProgressState, Target } from '@angular-devkit/architect'; -import { WorkspaceNodeModulesArchitectHost } from '@angular-devkit/architect/node'; -import { JsonValue, json, logging, schema, tags, workspaces } from '@angular-devkit/core'; +import { JsonValue, json, logging, schema, strings, tags, workspaces } from '@angular-devkit/core'; import { NodeJsSyncHost, createConsoleLogger } from '@angular-devkit/core/node'; -import * as ansiColors from 'ansi-colors'; import { existsSync } from 'node:fs'; import * as path from 'node:path'; -import yargsParser, { camelCase, decamelize } from 'yargs-parser'; -import { MultiProgressBar } from '../src/progress'; +import { parseArgs, styleText } from 'node:util'; +import { Architect } from '../index'; +import { WorkspaceNodeModulesArchitectHost } from '../node/index'; function findUp(names: string | string[], from: string) { if (!Array.isArray(names)) { @@ -59,102 +57,34 @@ function usage(logger: logging.Logger, exitCode = 0): never { return process.exit(exitCode); } -function _targetStringFromTarget({ project, target, configuration }: Target) { - return `${project}:${target}${configuration !== undefined ? ':' + configuration : ''}`; -} - -interface BarInfo { - status?: string; - builder: BuilderInfo; - target?: Target; -} - -// Create a separate instance to prevent unintended global changes to the color configuration -const colors = ansiColors.create(); - async function _executeTarget( parentLogger: logging.Logger, workspace: workspaces.WorkspaceDefinition, root: string, - argv: ReturnType, + targetStr: string, + options: json.JsonObject, registry: schema.SchemaRegistry, ) { const architectHost = new WorkspaceNodeModulesArchitectHost(workspace, root); const architect = new Architect(architectHost, registry); // Split a target into its parts. - const { - _: [targetStr = ''], - help, - ...options - } = argv; - const [project, target, configuration] = targetStr.toString().split(':'); + const [project, target, configuration] = targetStr.split(':'); const targetSpec = { project, target, configuration }; const logger = new logging.Logger('jobs'); const logs: logging.LogEntry[] = []; logger.subscribe((entry) => logs.push({ ...entry, message: `${entry.name}: ` + entry.message })); - // Camelize options as yargs will return the object in kebab-case when camel casing is disabled. - const camelCasedOptions: json.JsonObject = {}; - for (const [key, value] of Object.entries(options)) { - if (/[A-Z]/.test(key)) { - throw new Error(`Unknown argument ${key}. Did you mean ${decamelize(key)}?`); - } - - camelCasedOptions[camelCase(key)] = value as JsonValue; - } - - const run = await architect.scheduleTarget(targetSpec, camelCasedOptions, { logger }); - const bars = new MultiProgressBar(':name :bar (:current/:total) :status'); - - run.progress.subscribe((update) => { - const data = bars.get(update.id) || { - id: update.id, - builder: update.builder, - target: update.target, - status: update.status || '', - name: ( - (update.target ? _targetStringFromTarget(update.target) : update.builder.name) + - ' '.repeat(80) - ).substring(0, 40), - }; - - if (update.status !== undefined) { - data.status = update.status; - } - - switch (update.state) { - case BuilderProgressState.Error: - data.status = 'Error: ' + update.error; - bars.update(update.id, data); - break; - - case BuilderProgressState.Stopped: - data.status = 'Done.'; - bars.complete(update.id); - bars.update(update.id, data, update.total, update.total); - break; - - case BuilderProgressState.Waiting: - bars.update(update.id, data); - break; - - case BuilderProgressState.Running: - bars.update(update.id, data, update.current, update.total); - break; - } - - bars.render(); - }); + const run = await architect.scheduleTarget(targetSpec, options, { logger }); // Wait for full completion of the builder. try { const result = await run.lastOutput; if (result.success) { - parentLogger.info(colors.green('SUCCESS')); + parentLogger.info(styleText(['green'], 'SUCCESS')); } else { - parentLogger.info(colors.red('FAILURE')); + parentLogger.info(styleText(['red'], 'FAILURE')); } parentLogger.info('Result: ' + JSON.stringify({ ...result, info: undefined }, null, 4)); @@ -163,11 +93,10 @@ async function _executeTarget( logs.splice(0); await run.stop(); - bars.terminate(); return result.success ? 0 : 1; } catch (err) { - parentLogger.info(colors.red('ERROR')); + parentLogger.info(styleText(['red'], 'ERROR')); parentLogger.info('\nLogs:'); logs.forEach((l) => parentLogger.next(l)); @@ -178,30 +107,118 @@ async function _executeTarget( } } +const CLI_OPTION_DEFINITIONS = { + 'help': { type: 'boolean' }, + 'verbose': { type: 'boolean' }, +} as const; + +interface Options { + positionals: string[]; + builderOptions: json.JsonObject; + cliOptions: Partial>; +} + +/** Parse the command line. */ +function parseOptions(args: string[]): Options { + const { values, tokens } = parseArgs({ + args, + strict: false, + tokens: true, + allowPositionals: true, + allowNegative: true, + options: CLI_OPTION_DEFINITIONS, + }); + + const builderOptions: json.JsonObject = {}; + const positionals: string[] = []; + + for (let i = 0; i < tokens.length; i++) { + const token = tokens[i]; + + if (token.kind === 'positional') { + positionals.push(token.value); + continue; + } + + if (token.kind !== 'option') { + continue; + } + + const name = token.name; + let value: JsonValue = token.value ?? true; + + // `parseArgs` already handled known boolean args and their --no- forms. + // Only process options not in CLI_OPTION_DEFINITIONS here. + if (name in CLI_OPTION_DEFINITIONS) { + continue; + } + + if (/[A-Z]/.test(name)) { + throw new Error( + `Unknown argument ${name}. Did you mean ${strings.decamelize(name).replaceAll('_', '-')}?`, + ); + } + + // Handle --no-flag for unknown options, treating it as false + if (name.startsWith('no-')) { + const realName = name.slice(3); + builderOptions[strings.camelize(realName)] = false; + continue; + } + + // Handle value for unknown options + if (token.inlineValue === undefined) { + // Look ahead + const nextToken = tokens[i + 1]; + if (nextToken?.kind === 'positional') { + value = nextToken.value; + i++; // Consume next token + } else { + value = true; // Treat as boolean if no value follows + } + } + + // Type inference for numbers + if (typeof value === 'string' && !isNaN(Number(value))) { + value = Number(value); + } + + const camelName = strings.camelize(name); + if (Object.prototype.hasOwnProperty.call(builderOptions, camelName)) { + const existing = builderOptions[camelName]; + if (Array.isArray(existing)) { + existing.push(value); + } else { + builderOptions[camelName] = [existing, value] as JsonValue; + } + } else { + builderOptions[camelName] = value; + } + } + + return { + positionals, + builderOptions, + cliOptions: values as Options['cliOptions'], + }; +} + async function main(args: string[]): Promise { /** Parse the command line. */ - const argv = yargsParser(args, { - boolean: ['help'], - configuration: { - 'dot-notation': false, - 'boolean-negation': true, - 'strip-aliased': true, - 'camel-case-expansion': false, - }, - }); + const { positionals, cliOptions, builderOptions } = parseOptions(args); /** Create the DevKit Logger used through the CLI. */ - const logger = createConsoleLogger(argv['verbose'], process.stdout, process.stderr, { + const logger = createConsoleLogger(!!cliOptions['verbose'], process.stdout, process.stderr, { info: (s) => s, debug: (s) => s, - warn: (s) => colors.bold.yellow(s), - error: (s) => colors.bold.red(s), - fatal: (s) => colors.bold.red(s), + warn: (s) => styleText(['yellow', 'bold'], s), + error: (s) => styleText(['red', 'bold'], s), + fatal: (s) => styleText(['red', 'bold'], s), }); // Check the target. - const targetStr = argv._[0] || ''; - if (!targetStr || argv.help) { + const targetStr = positionals[0]; + if (!targetStr || cliOptions.help) { // Show architect usage if there's no target. usage(logger); } @@ -237,7 +254,7 @@ async function main(args: string[]): Promise { // Clear the console. process.stdout.write('\u001Bc'); - return await _executeTarget(logger, workspace, root, argv, registry); + return await _executeTarget(logger, workspace, root, targetStr, builderOptions, registry); } main(process.argv.slice(2)).then( diff --git a/packages/angular_devkit/architect/bin/cli.js b/packages/angular_devkit/architect/bin/cli.js new file mode 100755 index 000000000000..be5ba36279df --- /dev/null +++ b/packages/angular_devkit/architect/bin/cli.js @@ -0,0 +1,10 @@ +#!/usr/bin/env node +/** + * @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 + */ + +require('./architect'); diff --git a/packages/angular_devkit/architect/package.json b/packages/angular_devkit/architect/package.json index 0080d4654372..c3f51752bc0c 100644 --- a/packages/angular_devkit/architect/package.json +++ b/packages/angular_devkit/architect/package.json @@ -3,6 +3,9 @@ "version": "0.0.0-EXPERIMENTAL-PLACEHOLDER", "description": "Angular Build Facade", "experimental": true, + "bin": { + "architect": "./bin/cli.js" + }, "main": "src/index.js", "typings": "src/index.d.ts", "dependencies": { 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_cli/BUILD.bazel b/packages/angular_devkit/architect_cli/BUILD.bazel index 62bf288cbf7a..291238b8d897 100644 --- a/packages/angular_devkit/architect_cli/BUILD.bazel +++ b/packages/angular_devkit/architect_cli/BUILD.bazel @@ -1,5 +1,5 @@ load("@npm//:defs.bzl", "npm_link_all_packages") -load("//tools:defaults.bzl", "npm_package", "ts_project") +load("//tools:defaults.bzl", "npm_package") # Copyright Google Inc. All Rights Reserved. # @@ -11,23 +11,6 @@ package(default_visibility = ["//visibility:public"]) npm_link_all_packages() -ts_project( - name = "architect_cli", - srcs = [ - "bin/architect.ts", - ] + glob(["src/**/*.ts"]), - deps = [ - ":node_modules/@angular-devkit/architect", - ":node_modules/@angular-devkit/core", - ":node_modules/progress", - "//:node_modules/@types/node", - "//:node_modules/@types/progress", - "//:node_modules/@types/yargs-parser", - "//:node_modules/ansi-colors", - "//:node_modules/yargs-parser", - ], -) - genrule( name = "license", srcs = ["//:LICENSE"], @@ -39,12 +22,11 @@ npm_package( name = "pkg", pkg_deps = [ "//packages/angular_devkit/architect:package.json", - "//packages/angular_devkit/core:package.json", ], tags = ["release-package"], deps = [ ":README.md", - ":architect_cli", + ":bin/cli.js", ":license", ], ) diff --git a/packages/angular_devkit/architect_cli/bin/cli.js b/packages/angular_devkit/architect_cli/bin/cli.js new file mode 100644 index 000000000000..c1988e048dec --- /dev/null +++ b/packages/angular_devkit/architect_cli/bin/cli.js @@ -0,0 +1,10 @@ +#!/usr/bin/env node +/** + * @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 '@angular-devkit/architect/bin/architect.js'; diff --git a/packages/angular_devkit/architect_cli/package.json b/packages/angular_devkit/architect_cli/package.json index 4d9174dd8716..bf1ba1d2debb 100644 --- a/packages/angular_devkit/architect_cli/package.json +++ b/packages/angular_devkit/architect_cli/package.json @@ -1,11 +1,12 @@ { "name": "@angular-devkit/architect-cli", "version": "0.0.0-EXPERIMENTAL-PLACEHOLDER", + "type": "module", "description": "Angular Architect CLI", "homepage": "https://github.com/angular/angular-cli", "experimental": true, "bin": { - "architect": "./bin/architect.js" + "architect": "./bin/cli.js" }, "keywords": [ "build system", @@ -14,13 +15,6 @@ "tooling" ], "dependencies": { - "@angular-devkit/architect": "workspace:0.0.0-EXPERIMENTAL-PLACEHOLDER", - "@angular-devkit/core": "workspace:0.0.0-PLACEHOLDER", - "ansi-colors": "4.1.3", - "progress": "2.0.3", - "yargs-parser": "22.0.0" - }, - "devDependencies": { - "@types/progress": "2.0.7" + "@angular-devkit/architect": "workspace:0.0.0-EXPERIMENTAL-PLACEHOLDER" } } diff --git a/packages/angular_devkit/architect_cli/src/progress.ts b/packages/angular_devkit/architect_cli/src/progress.ts deleted file mode 100644 index e8c74353eb4b..000000000000 --- a/packages/angular_devkit/architect_cli/src/progress.ts +++ /dev/null @@ -1,102 +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 * as readline from 'node:readline'; -import ProgressBar from 'progress'; - -export class MultiProgressBar { - private _bars = new Map(); - - constructor( - private _status: string, - private _stream = process.stderr, - ) {} - private _add(id: Key, data: T): { data: T; bar: ProgressBar } { - const width = Math.min(80, this._stream.columns || 80); - const value = { - data, - bar: new ProgressBar(this._status, { - renderThrottle: 0, - clear: true, - total: 1, - width: width, - complete: '#', - incomplete: '.', - stream: this._stream, - }), - }; - this._bars.set(id, value); - readline.moveCursor(this._stream, 0, 1); - - return value; - } - - complete(id: Key) { - const maybeBar = this._bars.get(id); - if (maybeBar) { - maybeBar.bar.complete = true; - } - } - - add(id: Key, data: T) { - this._add(id, data); - } - - get(key: Key): T | undefined { - const maybeValue = this._bars.get(key); - - return maybeValue && maybeValue.data; - } - has(key: Key) { - return this._bars.has(key); - } - update(key: Key, data: T, current?: number, total?: number) { - let maybeBar = this._bars.get(key); - - if (!maybeBar) { - maybeBar = this._add(key, data); - } - - maybeBar.data = data; - if (total !== undefined) { - maybeBar.bar.total = total; - } - if (current !== undefined) { - maybeBar.bar.curr = Math.max(0, Math.min(current, maybeBar.bar.total)); - } - } - - render(max = Infinity, sort?: (a: T, b: T) => number) { - const stream = this._stream; - - readline.moveCursor(stream, 0, -this._bars.size); - readline.cursorTo(stream, 0); - - let values: Iterable<{ data: T; bar: ProgressBar }> = this._bars.values(); - if (sort) { - values = [...values].sort((a, b) => sort(a.data, b.data)); - } - - for (const { data, bar } of values) { - if (max-- == 0) { - return; - } - - bar.render(data); - readline.moveCursor(stream, 0, 1); - readline.cursorTo(stream, 0); - } - } - - terminate() { - for (const { bar } of this._bars.values()) { - bar.terminate(); - } - this._bars.clear(); - } -} diff --git a/packages/angular_devkit/build_angular/BUILD.bazel b/packages/angular_devkit/build_angular/BUILD.bazel index 2a505e1fd8ca..9d145d03682c 100644 --- a/packages/angular_devkit/build_angular/BUILD.bazel +++ b/packages/angular_devkit/build_angular/BUILD.bazel @@ -422,6 +422,9 @@ LARGE_SPECS = { "//modules/testing/builder:node_modules/@angular-devkit/build-angular", ], env = { + # Force IPv4 to resolve RBE resolution issues + "NODE_OPTIONS": "--dns-result-order=ipv4first", + # TODO: Replace Puppeteer downloaded browsers with Bazel-managed browsers, # or standardize to avoid complex configuration like this! "PUPPETEER_DOWNLOAD_PATH": "../../../node_modules/puppeteer/downloads", diff --git a/packages/angular_devkit/build_angular/package.json b/packages/angular_devkit/build_angular/package.json index 9cbd58bdddf6..35dff92b9d8b 100644 --- a/packages/angular_devkit/build_angular/package.json +++ b/packages/angular_devkit/build_angular/package.json @@ -23,12 +23,12 @@ "@discoveryjs/json-ext": "0.6.3", "@ngtools/webpack": "workspace:0.0.0-PLACEHOLDER", "ansi-colors": "4.1.3", - "autoprefixer": "10.4.22", + "autoprefixer": "10.4.23", "babel-loader": "10.0.0", "browserslist": "^4.26.0", "copy-webpack-plugin": "13.0.1", "css-loader": "7.1.2", - "esbuild-wasm": "0.27.0", + "esbuild-wasm": "0.27.1", "http-proxy-middleware": "3.0.5", "istanbul-lib-instrument": "6.0.3", "jsonc-parser": "3.3.1", @@ -38,7 +38,7 @@ "license-webpack-plugin": "4.0.2", "loader-utils": "3.3.1", "mini-css-extract-plugin": "2.9.4", - "open": "10.2.0", + "open": "11.0.0", "ora": "9.0.0", "picomatch": "4.0.3", "piscina": "5.1.4", @@ -46,7 +46,7 @@ "postcss-loader": "8.2.0", "resolve-url-loader": "5.0.0", "rxjs": "7.8.2", - "sass": "1.94.0", + "sass": "1.97.0", "sass-loader": "16.0.6", "semver": "7.7.3", "source-map-loader": "5.0.0", @@ -55,20 +55,20 @@ "tinyglobby": "0.2.15", "tree-kill": "1.2.2", "tslib": "2.8.1", - "webpack": "5.102.1", + "webpack": "5.104.0", "webpack-dev-middleware": "7.4.5", "webpack-dev-server": "5.2.2", "webpack-merge": "6.0.1", "webpack-subresource-integrity": "5.1.0" }, "optionalDependencies": { - "esbuild": "0.27.0" + "esbuild": "0.27.1" }, "devDependencies": { "@angular/ssr": "workspace:*", "@web/test-runner": "0.20.2", "browser-sync": "3.0.4", - "ng-packagr": "21.0.0-rc.1", + "ng-packagr": "21.1.0-next.0", "undici": "7.16.0" }, "peerDependencies": { diff --git a/packages/angular_devkit/build_angular/src/builders/browser/index.ts b/packages/angular_devkit/build_angular/src/builders/browser/index.ts index 1052e2165587..9c1576251d74 100644 --- a/packages/angular_devkit/build_angular/src/builders/browser/index.ts +++ b/packages/angular_devkit/build_angular/src/builders/browser/index.ts @@ -43,7 +43,6 @@ import { normalizeOptimization, urlJoin, } from '../../utils'; -import { colors } from '../../utils/color'; import { copyAssets } from '../../utils/copy-assets'; import { assertIsError } from '../../utils/error'; import { i18nInlineEmittedFiles } from '../../utils/i18n-inlining'; @@ -280,7 +279,7 @@ export function buildWebpackBrowser( ); spinner.succeed('Copying assets complete.'); } catch (err) { - spinner.fail(colors.redBright('Copying of assets failed.')); + spinner.fail('Copying of assets failed.'); assertIsError(err); return { 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/karma/browser_builder.ts b/packages/angular_devkit/build_angular/src/builders/karma/browser_builder.ts index 775a29fdf549..dc45da558527 100644 --- a/packages/angular_devkit/build_angular/src/builders/karma/browser_builder.ts +++ b/packages/angular_devkit/build_angular/src/builders/karma/browser_builder.ts @@ -149,15 +149,20 @@ async function initializeBrowser( return [karma, (await webpackConfigurationTransformer?.(config)) ?? config]; } -function getBuiltInMainFile(includeZoneProvider = false): string { +function getBuiltInMainFile(): string { const content = Buffer.from( ` import { provideZoneChangeDetection, ɵcompileNgModuleDefs as compileNgModuleDefs } from '@angular/core'; import { getTestBed } from '@angular/core/testing'; import { BrowserTestingModule, platformBrowserTesting } from '@angular/platform-browser/testing'; + const providers = []; + if (typeof window.Zone !== 'undefined') { + providers.push(provideZoneChangeDetection()); + } + export class TestModule {} - compileNgModuleDefs(TestModule, {providers: [${includeZoneProvider ? 'provideZoneChangeDetection()' : ''}]}); + compileNgModuleDefs(TestModule, {providers}); // Initialize the Angular testing environment. getTestBed().initTestEnvironment([BrowserTestingModule, TestModule], platformBrowserTesting(), { diff --git a/packages/angular_devkit/build_angular/src/builders/server/index.ts b/packages/angular_devkit/build_angular/src/builders/server/index.ts index 5a246178d10c..4903fe8e2403 100644 --- a/packages/angular_devkit/build_angular/src/builders/server/index.ts +++ b/packages/angular_devkit/build_angular/src/builders/server/index.ts @@ -28,7 +28,6 @@ import { deleteOutputDir, normalizeAssetPatterns, } from '../../utils'; -import { colors } from '../../utils/color'; import { copyAssets } from '../../utils/copy-assets'; import { assertIsError } from '../../utils/error'; import { i18nInlineEmittedFiles } from '../../utils/i18n-inlining'; @@ -121,7 +120,7 @@ export function execute( ); spinner.succeed('Copying assets complete.'); } catch (err) { - spinner.fail(colors.redBright('Copying of assets failed.')); + spinner.fail('Copying of assets failed.'); assertIsError(err); return { diff --git a/packages/angular_devkit/build_angular/src/tools/babel/webpack-loader.ts b/packages/angular_devkit/build_angular/src/tools/babel/webpack-loader.ts index 5be925d2c6b6..8cee4edf4d90 100644 --- a/packages/angular_devkit/build_angular/src/tools/babel/webpack-loader.ts +++ b/packages/angular_devkit/build_angular/src/tools/babel/webpack-loader.ts @@ -36,7 +36,6 @@ let linkerPluginCreator: */ let i18nPluginCreators: I18nPluginCreators | undefined; -// eslint-disable-next-line max-lines-per-function export default custom(() => { const baseOptions = Object.freeze({ babelrc: false, diff --git a/packages/angular_devkit/build_angular/test/hello-world-lib/projects/lib/package.json b/packages/angular_devkit/build_angular/test/hello-world-lib/projects/lib/package.json index a60d893b89a3..f6f00dcb4dd5 100644 --- a/packages/angular_devkit/build_angular/test/hello-world-lib/projects/lib/package.json +++ b/packages/angular_devkit/build_angular/test/hello-world-lib/projects/lib/package.json @@ -2,7 +2,7 @@ "name": "lib", "version": "0.0.1", "peerDependencies": { - "@angular/common": "^21.0.0-next", - "@angular/core": "^21.0.0-next" + "@angular/common": "^21.1.0-next", + "@angular/core": "^21.1.0-next" } } \ No newline at end of file diff --git a/packages/angular_devkit/build_webpack/BUILD.bazel b/packages/angular_devkit/build_webpack/BUILD.bazel index f66ea94f1919..3a104c243a66 100644 --- a/packages/angular_devkit/build_webpack/BUILD.bazel +++ b/packages/angular_devkit/build_webpack/BUILD.bazel @@ -90,6 +90,10 @@ jasmine_test( "//:node_modules/typescript", "//:node_modules/zone.js", ], + env = { + # Force IPv4 to resolve RBE resolution issues + "NODE_OPTIONS": "--dns-result-order=ipv4first", + }, ) genrule( diff --git a/packages/angular_devkit/build_webpack/package.json b/packages/angular_devkit/build_webpack/package.json index 80780f3f1638..5b1d83e5fa20 100644 --- a/packages/angular_devkit/build_webpack/package.json +++ b/packages/angular_devkit/build_webpack/package.json @@ -22,7 +22,7 @@ "devDependencies": { "@angular-devkit/core": "workspace:0.0.0-PLACEHOLDER", "@ngtools/webpack": "workspace:0.0.0-PLACEHOLDER", - "webpack": "5.102.1", + "webpack": "5.104.0", "webpack-dev-server": "5.2.2" }, "peerDependencies": { diff --git a/packages/angular_devkit/core/package.json b/packages/angular_devkit/core/package.json index 2fa6f3eb24a4..abdeffbe7116 100644 --- a/packages/angular_devkit/core/package.json +++ b/packages/angular_devkit/core/package.json @@ -33,10 +33,10 @@ "source-map": "0.7.6" }, "devDependencies": { - "chokidar": "4.0.3" + "chokidar": "5.0.0" }, "peerDependencies": { - "chokidar": "^4.0.0" + "chokidar": "^5.0.0" }, "peerDependenciesMeta": { "chokidar": { 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/record_spec.ts b/packages/angular_devkit/core/src/virtual-fs/host/record_spec.ts index 12debf5d94e4..3927e2a872a1 100644 --- a/packages/angular_devkit/core/src/virtual-fs/host/record_spec.ts +++ b/packages/angular_devkit/core/src/virtual-fs/host/record_spec.ts @@ -6,7 +6,6 @@ * found in the LICENSE file at https://angular.dev/license */ -/* eslint-disable @typescript-eslint/no-explicit-any */ import { path } from '../path'; import { stringToFileBuffer } from './buffer'; import { CordHost } from './record'; 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/rules/base_spec.ts b/packages/angular_devkit/schematics/src/rules/base_spec.ts index 09963fd48b1f..9470a55fcdb1 100644 --- a/packages/angular_devkit/schematics/src/rules/base_spec.ts +++ b/packages/angular_devkit/schematics/src/rules/base_spec.ts @@ -6,7 +6,6 @@ * found in the LICENSE file at https://angular.dev/license */ -/* eslint-disable @typescript-eslint/no-non-null-assertion */ import { Path, virtualFs } from '@angular-devkit/core'; import { lastValueFrom, of as observableOf } from 'rxjs'; import { Rule, SchematicContext, Source } from '../engine/interface'; diff --git a/packages/angular_devkit/schematics/src/rules/call_spec.ts b/packages/angular_devkit/schematics/src/rules/call_spec.ts index ef71747fb2df..f088f03a60b1 100644 --- a/packages/angular_devkit/schematics/src/rules/call_spec.ts +++ b/packages/angular_devkit/schematics/src/rules/call_spec.ts @@ -6,7 +6,6 @@ * found in the LICENSE file at https://angular.dev/license */ -/* eslint-disable @typescript-eslint/no-non-null-assertion */ /* eslint-disable @typescript-eslint/no-explicit-any */ import { of as observableOf } from 'rxjs'; import { Rule, SchematicContext, Source } from '../engine/interface'; 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/angular_devkit/schematics/tasks/node/index.ts b/packages/angular_devkit/schematics/tasks/node/index.ts index 300a9ea243d4..419b534cde4b 100644 --- a/packages/angular_devkit/schematics/tasks/node/index.ts +++ b/packages/angular_devkit/schematics/tasks/node/index.ts @@ -7,29 +7,28 @@ */ import { TaskExecutor, TaskExecutorFactory } from '../../src'; +import NodePackageExecutor from '../package-manager/executor'; import { NodePackageName, NodePackageTaskFactoryOptions } from '../package-manager/options'; +import RepositoryInitializerExecutor from '../repo-init/executor'; import { RepositoryInitializerName, RepositoryInitializerTaskFactoryOptions, } from '../repo-init/options'; +import RunSchematicExecutor from '../run-schematic/executor'; import { RunSchematicName } from '../run-schematic/options'; export class BuiltinTaskExecutor { static readonly NodePackage: TaskExecutorFactory = { name: NodePackageName, - create: (options) => - import('../package-manager/executor').then((mod) => mod.default(options)) as Promise< - TaskExecutor<{}> - >, + create: async (options) => NodePackageExecutor(options) as TaskExecutor<{}>, }; static readonly RepositoryInitializer: TaskExecutorFactory = { name: RepositoryInitializerName, - create: (options) => import('../repo-init/executor').then((mod) => mod.default(options)), + create: async (options) => RepositoryInitializerExecutor(options) as TaskExecutor<{}>, }; static readonly RunSchematic: TaskExecutorFactory<{}> = { name: RunSchematicName, - create: () => - import('../run-schematic/executor').then((mod) => mod.default()) as Promise>, + create: async () => RunSchematicExecutor() as TaskExecutor<{}>, }; } diff --git a/packages/angular_devkit/schematics/tasks/repo-init/init-task.ts b/packages/angular_devkit/schematics/tasks/repo-init/init-task.ts index ed875df2bf69..8e8e8498704b 100644 --- a/packages/angular_devkit/schematics/tasks/repo-init/init-task.ts +++ b/packages/angular_devkit/schematics/tasks/repo-init/init-task.ts @@ -15,12 +15,10 @@ export interface CommitOptions { email?: string; } -export class RepositoryInitializerTask - implements TaskConfigurationGenerator -{ +export class RepositoryInitializerTask implements TaskConfigurationGenerator { constructor( - public workingDirectory?: string, - public commitOptions?: CommitOptions, + public workingDirectory?: string | undefined, + public commitOptions?: CommitOptions | undefined, ) {} toConfiguration(): TaskConfiguration { diff --git a/packages/angular_devkit/schematics/testing/schematic-test-runner.ts b/packages/angular_devkit/schematics/testing/schematic-test-runner.ts index a37a2ed6921b..5ebff7108253 100644 --- a/packages/angular_devkit/schematics/testing/schematic-test-runner.ts +++ b/packages/angular_devkit/schematics/testing/schematic-test-runner.ts @@ -25,7 +25,7 @@ import { BuiltinTaskExecutor } from '../tasks/node'; import { NodeModulesTestEngineHost, validateOptionsWithSchema } from '../tools'; export class UnitTestTree extends DelegateTree { - get files() { + get files(): string[] { const result: string[] = []; this.visit((path) => result.push(path)); @@ -74,7 +74,7 @@ export class SchematicTestRunner { this._collection = this._engine.createCollection(this._collectionName); } - get engine() { + get engine(): SchematicEngine<{}, {}> { return this._engine; } get logger(): logging.Logger { @@ -84,7 +84,7 @@ export class SchematicTestRunner { return [...this._engineHost.tasks]; } - registerCollection(collectionName: string, collectionPath: string) { + registerCollection(collectionName: string, collectionPath: string): void { this._engineHost.registerCollection(collectionName, collectionPath); } diff --git a/packages/angular_devkit/schematics/tools/export-ref.ts b/packages/angular_devkit/schematics/tools/export-ref.ts index aa5a71f10af7..8c9c459382f2 100644 --- a/packages/angular_devkit/schematics/tools/export-ref.ts +++ b/packages/angular_devkit/schematics/tools/export-ref.ts @@ -26,13 +26,13 @@ export class ExportStringRef { } } - get ref() { + get ref(): T | undefined { return this._ref; } - get module() { + get module(): string { return this._module; } - get path() { + get path(): string { return this._path; } } diff --git a/packages/angular_devkit/schematics/tools/fallback-engine-host.ts b/packages/angular_devkit/schematics/tools/fallback-engine-host.ts index 97bfbba5c26d..f8790f00784d 100644 --- a/packages/angular_devkit/schematics/tools/fallback-engine-host.ts +++ b/packages/angular_devkit/schematics/tools/fallback-engine-host.ts @@ -41,7 +41,7 @@ export class FallbackEngineHost implements EngineHost<{}, {}> { addHost( host: EngineHost, - ) { + ): void { this._hosts.push(host); } diff --git a/packages/angular_devkit/schematics/tools/file-system-engine-host-base.ts b/packages/angular_devkit/schematics/tools/file-system-engine-host-base.ts index 7ce607a50431..cf788851b14c 100644 --- a/packages/angular_devkit/schematics/tools/file-system-engine-host-base.ts +++ b/packages/angular_devkit/schematics/tools/file-system-engine-host-base.ts @@ -119,7 +119,7 @@ export abstract class FileSystemEngineHostBase implements FileSystemEngineHost { private _contextTransforms: ContextTransform[] = []; private _taskFactories = new Map Observable>(); - listSchematicNames(collection: FileSystemCollectionDesc, includeHidden?: boolean) { + listSchematicNames(collection: FileSystemCollectionDesc, includeHidden?: boolean): string[] { const schematics: string[] = []; for (const key of Object.keys(collection.schematics)) { const schematic = collection.schematics[key]; @@ -140,11 +140,13 @@ export abstract class FileSystemEngineHostBase implements FileSystemEngineHost { return schematics; } - registerOptionsTransform(t: OptionTransform) { + registerOptionsTransform( + t: OptionTransform, + ): void { this._transforms.push(t); } - registerContextTransform(t: ContextTransform) { + registerContextTransform(t: ContextTransform): void { this._contextTransforms.push(t); } diff --git a/packages/angular_devkit/schematics/tools/file-system-engine-host.ts b/packages/angular_devkit/schematics/tools/file-system-engine-host.ts index bd24f8808d77..67b98276528a 100644 --- a/packages/angular_devkit/schematics/tools/file-system-engine-host.ts +++ b/packages/angular_devkit/schematics/tools/file-system-engine-host.ts @@ -48,7 +48,10 @@ export class FileSystemEngineHost extends FileSystemEngineHostBase { throw new CollectionCannotBeResolvedException(name); } - protected _resolveReferenceString(refString: string, parentPath: string) { + protected _resolveReferenceString( + refString: string, + parentPath: string, + ): { ref: RuleFactory<{}>; path: string } | null { // Use the same kind of export strings as NodeModule. const ref = new ExportStringRef>(refString, parentPath); if (!ref.ref) { diff --git a/packages/angular_devkit/schematics/tools/node-module-engine-host.ts b/packages/angular_devkit/schematics/tools/node-module-engine-host.ts index 0bc269840c18..c7f72aa68650 100644 --- a/packages/angular_devkit/schematics/tools/node-module-engine-host.ts +++ b/packages/angular_devkit/schematics/tools/node-module-engine-host.ts @@ -106,7 +106,7 @@ export class NodeModulesEngineHost extends FileSystemEngineHostBase { refString: string, parentPath: string, collectionDescription?: FileSystemCollectionDesc, - ) { + ): { ref: RuleFactory<{}>; path: string } | null { const ref = new ExportStringRef>(refString, parentPath); if (!ref.ref) { return null; diff --git a/packages/angular_devkit/schematics/tools/node-modules-test-engine-host.ts b/packages/angular_devkit/schematics/tools/node-modules-test-engine-host.ts index c15fd18d12a9..701e99af781f 100644 --- a/packages/angular_devkit/schematics/tools/node-modules-test-engine-host.ts +++ b/packages/angular_devkit/schematics/tools/node-modules-test-engine-host.ts @@ -18,15 +18,15 @@ export class NodeModulesTestEngineHost extends NodeModulesEngineHost { #collections = new Map(); #tasks: TaskConfiguration[] = []; - get tasks() { + get tasks(): TaskConfiguration[] { return this.#tasks; } - clearTasks() { + clearTasks(): void { this.#tasks = []; } - registerCollection(name: string, path: string) { + registerCollection(name: string, path: string): void { this.#collections.set(name, path); } diff --git a/packages/angular_devkit/schematics_cli/BUILD.bazel b/packages/angular_devkit/schematics_cli/BUILD.bazel index f67abd6a3eb6..9952e8cf0857 100644 --- a/packages/angular_devkit/schematics_cli/BUILD.bazel +++ b/packages/angular_devkit/schematics_cli/BUILD.bazel @@ -49,9 +49,6 @@ ts_project( ":node_modules/@angular-devkit/schematics", ":node_modules/@inquirer/prompts", "//:node_modules/@types/node", - "//:node_modules/@types/yargs-parser", - "//:node_modules/ansi-colors", - "//:node_modules/yargs-parser", ], ) diff --git a/packages/angular_devkit/schematics_cli/bin/schematics.ts b/packages/angular_devkit/schematics_cli/bin/schematics.ts index 8e9779728a77..8dc64ff5eae0 100644 --- a/packages/angular_devkit/schematics_cli/bin/schematics.ts +++ b/packages/angular_devkit/schematics_cli/bin/schematics.ts @@ -9,12 +9,11 @@ import { JsonValue, logging, schema } from '@angular-devkit/core'; import { ProcessOutput, createConsoleLogger } from '@angular-devkit/core/node'; -import { UnsuccessfulWorkflowExecution } from '@angular-devkit/schematics'; +import { UnsuccessfulWorkflowExecution, strings } from '@angular-devkit/schematics'; import { NodeWorkflow } from '@angular-devkit/schematics/tools'; -import ansiColors from 'ansi-colors'; import { existsSync } from 'node:fs'; import * as path from 'node:path'; -import yargsParser, { camelCase, decamelize } from 'yargs-parser'; +import { parseArgs, styleText } from 'node:util'; /** * Parse the name of schematic passed in argument, and return a {collection, schematic} named @@ -216,24 +215,20 @@ function getPackageManagerName() { return 'npm'; } -// eslint-disable-next-line max-lines-per-function export async function main({ args, stdout = process.stdout, stderr = process.stderr, }: MainOptions): Promise<0 | 1> { - const { cliOptions, schematicOptions, _ } = parseArgs(args); - - // Create a separate instance to prevent unintended global changes to the color configuration - const colors = ansiColors.create(); + const { cliOptions, schematicOptions, _ } = parseOptions(args); /** Create the DevKit Logger used through the CLI. */ const logger = createConsoleLogger(!!cliOptions.verbose, stdout, stderr, { info: (s) => s, debug: (s) => s, - warn: (s) => colors.bold.yellow(s), - error: (s) => colors.bold.red(s), - fatal: (s) => colors.bold.red(s), + warn: (s) => styleText(['bold', 'yellow'], s), + error: (s) => styleText(['bold', 'red'], s), + fatal: (s) => styleText(['bold', 'red'], s), }); if (cliOptions.help) { @@ -250,10 +245,9 @@ export async function main({ const isLocalCollection = collectionName.startsWith('.') || collectionName.startsWith('/'); /** Gather the arguments for later use. */ - const debugPresent = cliOptions.debug !== null; - const debug = debugPresent ? !!cliOptions.debug : isLocalCollection; - const dryRunPresent = cliOptions['dry-run'] !== null; - const dryRun = dryRunPresent ? !!cliOptions['dry-run'] : debug; + const debug = cliOptions.debug ?? isLocalCollection; + const dryRunPresent = cliOptions['dry-run'] != null; + const dryRun = cliOptions['dry-run'] ?? debug; const force = !!cliOptions.force; const allowPrivate = !!cliOptions['allow-private']; @@ -317,21 +311,21 @@ export async function main({ case 'update': loggingQueue.push( // TODO: `as unknown` was necessary during TS 5.9 update. Figure out a long-term solution. - `${colors.cyan('UPDATE')} ${eventPath} (${(event.content as unknown as Buffer).length} bytes)`, + `${styleText(['cyan'], 'UPDATE')} ${eventPath} (${(event.content as unknown as Buffer).length} bytes)`, ); break; case 'create': loggingQueue.push( // TODO: `as unknown` was necessary during TS 5.9 update. Figure out a long-term solution. - `${colors.green('CREATE')} ${eventPath} (${(event.content as unknown as Buffer).length} bytes)`, + `${styleText(['green'], 'CREATE')} ${eventPath} (${(event.content as unknown as Buffer).length} bytes)`, ); break; case 'delete': - loggingQueue.push(`${colors.yellow('DELETE')} ${eventPath}`); + loggingQueue.push(`${styleText(['yellow'], 'DELETE')} ${eventPath}`); break; case 'rename': loggingQueue.push( - `${colors.blue('RENAME')} ${eventPath} => ${removeLeadingSlash(event.to)}`, + `${styleText(['blue'], 'RENAME')} ${eventPath} => ${removeLeadingSlash(event.to)}`, ); break; } @@ -447,69 +441,105 @@ Any additional option is passed to the Schematics depending on its schema. `; } -/** Parse the command line. */ -const booleanArgs = [ - 'allow-private', - 'debug', - 'dry-run', - 'force', - 'help', - 'list-schematics', - 'verbose', - 'interactive', -] as const; - -type ElementType> = - T extends ReadonlyArray ? ElementType : never; +const CLI_OPTION_DEFINITIONS = { + 'allow-private': { type: 'boolean' }, + 'debug': { type: 'boolean' }, + 'dry-run': { type: 'boolean' }, + 'force': { type: 'boolean' }, + 'help': { type: 'boolean' }, + 'list-schematics': { type: 'boolean' }, + 'verbose': { type: 'boolean' }, + 'interactive': { type: 'boolean', default: true }, +} as const; interface Options { _: string[]; schematicOptions: Record; - cliOptions: Partial, boolean | null>>; + cliOptions: Partial>; } /** Parse the command line. */ -function parseArgs(args: string[]): Options { - const { _, ...options } = yargsParser(args, { - boolean: booleanArgs as unknown as string[], - default: { - 'interactive': true, - 'debug': null, - 'dry-run': null, - }, - configuration: { - 'dot-notation': false, - 'boolean-negation': true, - 'strip-aliased': true, - 'camel-case-expansion': false, - }, +function parseOptions(args: string[]): Options { + const { values, tokens } = parseArgs({ + args, + strict: false, + tokens: true, + allowPositionals: true, + allowNegative: true, + options: CLI_OPTION_DEFINITIONS, }); - // Camelize options as yargs will return the object in kebab-case when camel casing is disabled. const schematicOptions: Options['schematicOptions'] = {}; - const cliOptions: Options['cliOptions'] = {}; + const positionals: string[] = []; + + for (let i = 0; i < tokens.length; i++) { + const token = tokens[i]; + + if (token.kind === 'positional') { + positionals.push(token.value); + continue; + } + + if (token.kind !== 'option') { + continue; + } + + const name = token.name; + let value: string | number | boolean = token.value ?? true; + + // `parseArgs` already handled known boolean args and their --no- forms. + // Only process options not in CLI_OPTION_DEFINITIONS here. + if (name in CLI_OPTION_DEFINITIONS) { + continue; + } - const isCliOptions = ( - key: ElementType | string, - ): key is ElementType => - booleanArgs.includes(key as ElementType); + if (/[A-Z]/.test(name)) { + throw new Error( + `Unknown argument ${name}. Did you mean ${strings.decamelize(name).replaceAll('_', '-')}?`, + ); + } - for (const [key, value] of Object.entries(options)) { - if (/[A-Z]/.test(key)) { - throw new Error(`Unknown argument ${key}. Did you mean ${decamelize(key)}?`); + // Handle --no-flag for unknown options, treating it as false + if (name.startsWith('no-')) { + const realName = name.slice(3); + schematicOptions[strings.camelize(realName)] = false; + continue; } - if (isCliOptions(key)) { - cliOptions[key] = value; + // Handle value for unknown options + if (token.inlineValue === undefined) { + // Look ahead + const nextToken = tokens[i + 1]; + if (nextToken?.kind === 'positional') { + value = nextToken.value; + i++; // Consume next token + } else { + value = true; // Treat as boolean if no value follows + } + } + + // Type inference for numbers + if (typeof value === 'string' && !isNaN(Number(value))) { + value = Number(value); + } + + const camelName = strings.camelize(name); + if (Object.prototype.hasOwnProperty.call(schematicOptions, camelName)) { + const existing = schematicOptions[camelName]; + if (Array.isArray(existing)) { + existing.push(value); + } else { + schematicOptions[camelName] = [existing, value]; + } } else { - schematicOptions[camelCase(key)] = value; + schematicOptions[camelName] = value; } } return { - _: _.map((v) => v.toString()), + _: positionals, schematicOptions, - cliOptions, + cliOptions: values as Options['cliOptions'], }; } diff --git a/packages/angular_devkit/schematics_cli/package.json b/packages/angular_devkit/schematics_cli/package.json index c48815b3cb52..0b49edc35c55 100644 --- a/packages/angular_devkit/schematics_cli/package.json +++ b/packages/angular_devkit/schematics_cli/package.json @@ -18,8 +18,6 @@ "dependencies": { "@angular-devkit/core": "workspace:0.0.0-PLACEHOLDER", "@angular-devkit/schematics": "workspace:0.0.0-PLACEHOLDER", - "@inquirer/prompts": "7.10.1", - "ansi-colors": "4.1.3", - "yargs-parser": "22.0.0" + "@inquirer/prompts": "7.10.1" } } diff --git a/packages/angular_devkit/schematics_cli/schematic/files/package.json b/packages/angular_devkit/schematics_cli/schematic/files/package.json index f1067a247bec..ddbc5d48e920 100644 --- a/packages/angular_devkit/schematics_cli/schematic/files/package.json +++ b/packages/angular_devkit/schematics_cli/schematic/files/package.json @@ -19,7 +19,7 @@ "devDependencies": { "@types/node": "^20.17.19", "@types/jasmine": "~5.1.0", - "jasmine": "~5.12.0", + "jasmine": "~5.13.0", "typescript": "~5.9.2" } } diff --git a/packages/ngtools/webpack/package.json b/packages/ngtools/webpack/package.json index 618d45ce52d9..daeacb77baba 100644 --- a/packages/ngtools/webpack/package.json +++ b/packages/ngtools/webpack/package.json @@ -27,9 +27,9 @@ }, "devDependencies": { "@angular-devkit/core": "workspace:0.0.0-PLACEHOLDER", - "@angular/compiler": "21.0.0-rc.2", - "@angular/compiler-cli": "21.0.0-rc.2", + "@angular/compiler": "21.1.0-next.4", + "@angular/compiler-cli": "21.1.0-next.4", "typescript": "5.9.3", - "webpack": "5.102.1" + "webpack": "5.104.0" } } diff --git a/packages/ngtools/webpack/src/transformers/replace_resources_spec.ts b/packages/ngtools/webpack/src/transformers/replace_resources_spec.ts index 102d6e1e0879..8b15f5d55b09 100644 --- a/packages/ngtools/webpack/src/transformers/replace_resources_spec.ts +++ b/packages/ngtools/webpack/src/transformers/replace_resources_spec.ts @@ -28,7 +28,6 @@ function transform( } describe('@ngtools/webpack transformers', () => { - /* eslint-disable max-len */ describe('find_resources', () => { it('should replace resources', () => { const input = tags.stripIndent` 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/app-shell/index.ts b/packages/schematics/angular/app-shell/index.ts index 13905359d16c..2bae5e4bef14 100644 --- a/packages/schematics/angular/app-shell/index.ts +++ b/packages/schematics/angular/app-shell/index.ts @@ -6,7 +6,14 @@ * found in the LICENSE file at https://angular.dev/license */ -import { Rule, SchematicsException, Tree, chain, schematic } from '@angular-devkit/schematics'; +import { + Rule, + RuleFactory, + SchematicsException, + Tree, + chain, + schematic, +} from '@angular-devkit/schematics'; import { dirname, join } from 'node:path/posix'; import ts from '../third_party/github.com/Microsoft/TypeScript/lib/typescript'; import { @@ -190,19 +197,23 @@ function addServerRoutingConfig(options: AppShellOptions, isStandalone: boolean) }; } -export default createProjectSchematic(async (options, { tree }) => { - const browserEntryPoint = await getMainFilePath(tree, options.project); - const isStandalone = isStandaloneApp(tree, browserEntryPoint); - - return chain([ - validateProject(browserEntryPoint), - schematic('server', options), - addServerRoutingConfig(options, isStandalone), - schematic('component', { - name: 'app-shell', - module: 'app.module.server.ts', - project: options.project, - standalone: isStandalone, - }), - ]); -}); +const appShellSchematic: RuleFactory = createProjectSchematic( + async (options, { tree }) => { + const browserEntryPoint = await getMainFilePath(tree, options.project); + const isStandalone = isStandaloneApp(tree, browserEntryPoint); + + return chain([ + validateProject(browserEntryPoint), + schematic('server', options), + addServerRoutingConfig(options, isStandalone), + schematic('component', { + name: 'app-shell', + module: 'app.module.server.ts', + project: options.project, + standalone: isStandalone, + }), + ]); + }, +); + +export default appShellSchematic; 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" > ',<% if(inlineTemplate) { %> template: ` -

Welcome to {{ title() }}!

+

Hello, {{ title() }}

<% if (routing) { %><% 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 0e6ebd5930a7..ce8010ecfbce 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 @@ -5,7 +5,7 @@ import { RouterOutlet } from '@angular/router';<% } %> selector: '<%= selector %>', imports: [<% if(routing) { %>RouterOutlet<% } %>],<% if(inlineTemplate) { %> template: ` -

Welcome to {{ title() }}!

+

Hello, {{ title() }}

<% if (routing) { %><% 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 53ddd53acad0..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 }, @@ -187,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); @@ -392,17 +338,15 @@ function addAppToWorkspaceFile(options: ApplicationOptions, appDir: string): Rul test: options.skipTests || options.minimal ? undefined - : options.testRunner === 'vitest' - ? { - builder: Builders.BuildUnitTest, - options: {}, - } - : { - builder: Builders.BuildUnitTest, - options: { - runner: 'karma', - }, - }, + : { + 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 737af0578b0d..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,7 +442,7 @@ 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')); @@ -876,8 +876,10 @@ describe('Application Schematic', () => { 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 () => { diff --git a/packages/schematics/angular/component/index.ts b/packages/schematics/angular/component/index.ts index c3c4fe1c3448..1a07f69d1a15 100644 --- a/packages/schematics/angular/component/index.ts +++ b/packages/schematics/angular/component/index.ts @@ -8,7 +8,7 @@ import { FileOperator, - Rule, + RuleFactory, apply, applyTemplates, chain, @@ -40,60 +40,65 @@ function buildSelector(options: ComponentOptions, projectPrefix: string) { return selector; } -export default createProjectSchematic((options, { project, tree }) => { - if (options.path === undefined) { - options.path = buildDefaultPath(project); - } +const componentSchematic: RuleFactory = createProjectSchematic( + (options, { project, tree }) => { + if (options.path === undefined) { + options.path = buildDefaultPath(project); + } + + options.module = findModuleFromOptions(tree, options); + // Schematic templates require a defined type value + options.type ??= ''; - options.module = findModuleFromOptions(tree, options); - // Schematic templates require a defined type value - options.type ??= ''; + const parsedPath = parseName(options.path, options.name); + options.name = parsedPath.name; + options.path = parsedPath.path; + options.selector = + options.selector || buildSelector(options, (project && project.prefix) || ''); - const parsedPath = parseName(options.path, options.name); - options.name = parsedPath.name; - options.path = parsedPath.path; - options.selector = options.selector || buildSelector(options, (project && project.prefix) || ''); + validateHtmlSelector(options.selector); - validateHtmlSelector(options.selector); + const classifiedName = + strings.classify(options.name) + + (options.addTypeToClassName && options.type ? strings.classify(options.type) : ''); + validateClassName(classifiedName); + const zoneless = isZonelessApp(project); - const classifiedName = - strings.classify(options.name) + - (options.addTypeToClassName && options.type ? strings.classify(options.type) : ''); - validateClassName(classifiedName); - const zoneless = isZonelessApp(project); + const skipStyleFile = options.inlineStyle || options.style === Style.None; + const templateSource = apply(url('./files'), [ + options.skipTests ? filter((path) => !path.endsWith('.spec.ts.template')) : noop(), + skipStyleFile ? filter((path) => !path.endsWith('.__style__.template')) : noop(), + options.inlineTemplate ? filter((path) => !path.endsWith('.html.template')) : noop(), + applyTemplates({ + ...strings, + 'if-flat': (s: string) => (options.flat ? '' : s), + 'ngext': options.ngHtml ? '.ng' : '', + ...options, + // Add a new variable for the classified name, conditionally including the type + classifiedName, + zoneless, + }), + !options.type + ? forEach(((file) => { + return file.path.includes('..') + ? { + content: file.content, + path: file.path.replace('..', '.'), + } + : file; + }) as FileOperator) + : noop(), + move(parsedPath.path), + ]); - const skipStyleFile = options.inlineStyle || options.style === Style.None; - const templateSource = apply(url('./files'), [ - options.skipTests ? filter((path) => !path.endsWith('.spec.ts.template')) : noop(), - skipStyleFile ? filter((path) => !path.endsWith('.__style__.template')) : noop(), - options.inlineTemplate ? filter((path) => !path.endsWith('.html.template')) : noop(), - applyTemplates({ - ...strings, - 'if-flat': (s: string) => (options.flat ? '' : s), - 'ngext': options.ngHtml ? '.ng' : '', - ...options, - // Add a new variable for the classified name, conditionally including the type - classifiedName, - zoneless, - }), - !options.type - ? forEach(((file) => { - return file.path.includes('..') - ? { - content: file.content, - path: file.path.replace('..', '.'), - } - : file; - }) as FileOperator) - : noop(), - move(parsedPath.path), - ]); + return chain([ + addDeclarationToNgModule({ + type: 'component', + ...options, + }), + mergeWith(templateSource), + ]); + }, +); - return chain([ - addDeclarationToNgModule({ - type: 'component', - ...options, - }), - mergeWith(templateSource), - ]); -}); +export default componentSchematic; diff --git a/packages/schematics/angular/config/index.ts b/packages/schematics/angular/config/index.ts index c4bafc39657e..73818699a134 100644 --- a/packages/schematics/angular/config/index.ts +++ b/packages/schematics/angular/config/index.ts @@ -8,6 +8,7 @@ import { Rule, + RuleFactory, SchematicsException, apply, applyTemplates, @@ -24,18 +25,22 @@ import { updateWorkspace } from '../utility/workspace'; import { Builders as AngularBuilder } from '../utility/workspace-models'; import { Schema as ConfigOptions, Type as ConfigType } from './schema'; -export default createProjectSchematic((options, { project }) => { - switch (options.type) { - case ConfigType.Karma: - 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.`); - } -}); +const configSchematic: RuleFactory = createProjectSchematic( + (options, { project }) => { + switch (options.type) { + case ConfigType.Karma: + 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.`); + } + }, +); + +export default configSchematic; function addVitestConfig(options: ConfigOptions): Rule { return (tree, context) => diff --git a/packages/schematics/angular/directive/index.ts b/packages/schematics/angular/directive/index.ts index bfe87129bb79..72aa73b6a219 100644 --- a/packages/schematics/angular/directive/index.ts +++ b/packages/schematics/angular/directive/index.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.dev/license */ -import { Rule, chain, strings } from '@angular-devkit/schematics'; +import { RuleFactory, chain, strings } from '@angular-devkit/schematics'; import { addDeclarationToNgModule } from '../utility/add-declaration-to-ng-module'; import { findModuleFromOptions } from '../utility/find-module'; import { generateFromFiles } from '../utility/generate-from-files'; @@ -27,32 +27,36 @@ function buildSelector(options: DirectiveOptions, projectPrefix: string) { return strings.camelize(selector); } -export default createProjectSchematic((options, { project, tree }) => { - if (options.path === undefined) { - options.path = buildDefaultPath(project); - } - - options.module = findModuleFromOptions(tree, options); - const parsedPath = parseName(options.path, options.name); - options.name = parsedPath.name; - options.path = parsedPath.path; - options.selector = options.selector || buildSelector(options, project.prefix || ''); - - validateHtmlSelector(options.selector); - const classifiedName = - strings.classify(options.name) + - (options.addTypeToClassName && options.type ? strings.classify(options.type) : ''); - validateClassName(classifiedName); - - return chain([ - addDeclarationToNgModule({ - type: 'directive', - - ...options, - }), - generateFromFiles({ - ...options, - classifiedName, - }), - ]); -}); +const directiveSchematic: RuleFactory = createProjectSchematic( + (options, { project, tree }) => { + if (options.path === undefined) { + options.path = buildDefaultPath(project); + } + + options.module = findModuleFromOptions(tree, options); + const parsedPath = parseName(options.path, options.name); + options.name = parsedPath.name; + options.path = parsedPath.path; + options.selector = options.selector || buildSelector(options, project.prefix || ''); + + validateHtmlSelector(options.selector); + const classifiedName = + strings.classify(options.name) + + (options.addTypeToClassName && options.type ? strings.classify(options.type) : ''); + validateClassName(classifiedName); + + return chain([ + addDeclarationToNgModule({ + type: 'directive', + + ...options, + }), + generateFromFiles({ + ...options, + classifiedName, + }), + ]); + }, +); + +export default directiveSchematic; diff --git a/packages/schematics/angular/library/files/tsconfig.lib.json.template b/packages/schematics/angular/library/files/tsconfig.lib.json.template index 79a2168d3492..779ab5549814 100644 --- a/packages/schematics/angular/library/files/tsconfig.lib.json.template +++ b/packages/schematics/angular/library/files/tsconfig.lib.json.template @@ -6,7 +6,6 @@ "outDir": "<%= relativePathToWorkspaceRoot %>/out-tsc/lib", "declaration": true, "declarationMap": true, - "inlineSources": true, "types": [] }, "include": [ 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/module/index.ts b/packages/schematics/angular/module/index.ts index 6a40ddf89eb2..6811ab55f3f1 100644 --- a/packages/schematics/angular/module/index.ts +++ b/packages/schematics/angular/module/index.ts @@ -8,6 +8,7 @@ import { Rule, + RuleFactory, Tree, apply, applyTemplates, @@ -134,59 +135,63 @@ function buildRoute(options: ModuleOptions, modulePath: string) { return `{ path: '${options.route}', loadChildren: ${loadChildren} }`; } -export default createProjectSchematic(async (options, { tree }) => { - if (options.path === undefined) { - options.path = await createDefaultPath(tree, options.project); - } - - if (options.module) { - options.module = findModuleFromOptions(tree, options); - } - - let routingModulePath; - const isLazyLoadedModuleGen = !!(options.route && options.module); - if (isLazyLoadedModuleGen) { - options.routingScope = RoutingScope.Child; - routingModulePath = getRoutingModulePath(tree, options.module as string); - } - - const parsedPath = parseName(options.path, options.name); - options.name = parsedPath.name; - options.path = parsedPath.path; - validateClassName(strings.classify(options.name)); - - const templateSource = apply(url('./files'), [ - options.routing || (isLazyLoadedModuleGen && routingModulePath) - ? noop() - : filter((path) => !path.includes('-routing')), - applyTemplates({ - ...strings, - 'if-flat': (s: string) => (options.flat ? '' : s), - lazyRoute: isLazyLoadedModuleGen, - lazyRouteWithoutRouteModule: isLazyLoadedModuleGen && !routingModulePath, - lazyRouteWithRouteModule: isLazyLoadedModuleGen && !!routingModulePath, - ...options, - }), - move(parsedPath.path), - ]); - const moduleDasherized = strings.dasherize(options.name); - const modulePath = `${ - !options.flat ? moduleDasherized + '/' : '' - }${moduleDasherized}${options.typeSeparator}module.ts`; - - const componentOptions: ComponentOptions = { - module: modulePath, - flat: options.flat, - name: options.name, - path: options.path, - project: options.project, - standalone: false, - }; +const moduleSchematic: RuleFactory = createProjectSchematic( + async (options, { tree }) => { + if (options.path === undefined) { + options.path = await createDefaultPath(tree, options.project); + } + + if (options.module) { + options.module = findModuleFromOptions(tree, options); + } + + let routingModulePath; + const isLazyLoadedModuleGen = !!(options.route && options.module); + if (isLazyLoadedModuleGen) { + options.routingScope = RoutingScope.Child; + routingModulePath = getRoutingModulePath(tree, options.module as string); + } - return chain([ - !isLazyLoadedModuleGen ? addImportToNgModule(options) : noop(), - addRouteDeclarationToNgModule(options, routingModulePath), - mergeWith(templateSource), - isLazyLoadedModuleGen ? schematic('component', componentOptions) : noop(), - ]); -}); + const parsedPath = parseName(options.path, options.name); + options.name = parsedPath.name; + options.path = parsedPath.path; + validateClassName(strings.classify(options.name)); + + const templateSource = apply(url('./files'), [ + options.routing || (isLazyLoadedModuleGen && routingModulePath) + ? noop() + : filter((path) => !path.includes('-routing')), + applyTemplates({ + ...strings, + 'if-flat': (s: string) => (options.flat ? '' : s), + lazyRoute: isLazyLoadedModuleGen, + lazyRouteWithoutRouteModule: isLazyLoadedModuleGen && !routingModulePath, + lazyRouteWithRouteModule: isLazyLoadedModuleGen && !!routingModulePath, + ...options, + }), + move(parsedPath.path), + ]); + const moduleDasherized = strings.dasherize(options.name); + const modulePath = `${ + !options.flat ? moduleDasherized + '/' : '' + }${moduleDasherized}${options.typeSeparator}module.ts`; + + const componentOptions: ComponentOptions = { + module: modulePath, + flat: options.flat, + name: options.name, + path: options.path, + project: options.project, + standalone: false, + }; + + return chain([ + !isLazyLoadedModuleGen ? addImportToNgModule(options) : noop(), + addRouteDeclarationToNgModule(options, routingModulePath), + mergeWith(templateSource), + isLazyLoadedModuleGen ? schematic('component', componentOptions) : noop(), + ]); + }, +); + +export default moduleSchematic; 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 7f136908c747..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 { @@ -178,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/index.ts b/packages/schematics/angular/pipe/index.ts index 8333d0c4b1ee..c74307b1c6df 100644 --- a/packages/schematics/angular/pipe/index.ts +++ b/packages/schematics/angular/pipe/index.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.dev/license */ -import { chain, strings } from '@angular-devkit/schematics'; +import { RuleFactory, chain, strings } from '@angular-devkit/schematics'; import { addDeclarationToNgModule } from '../utility/add-declaration-to-ng-module'; import { findModuleFromOptions } from '../utility/find-module'; import { generateFromFiles } from '../utility/generate-from-files'; @@ -16,19 +16,23 @@ import { validateClassName } from '../utility/validation'; import { createDefaultPath } from '../utility/workspace'; import { Schema as PipeOptions } from './schema'; -export default createProjectSchematic(async (options, { tree }) => { - options.path ??= await createDefaultPath(tree, options.project); - options.module = findModuleFromOptions(tree, options); - const parsedPath = parseName(options.path, options.name); - options.name = parsedPath.name; - options.path = parsedPath.path; - validateClassName(strings.classify(options.name)); +const pipeSchematic: RuleFactory = createProjectSchematic( + async (options, { tree }) => { + options.path ??= await createDefaultPath(tree, options.project); + options.module = findModuleFromOptions(tree, options); + const parsedPath = parseName(options.path, options.name); + options.name = parsedPath.name; + options.path = parsedPath.path; + validateClassName(strings.classify(options.name)); - return chain([ - addDeclarationToNgModule({ - type: 'pipe', - ...options, - }), - generateFromFiles(options), - ]); -}); + return chain([ + addDeclarationToNgModule({ + type: 'pipe', + ...options, + }), + generateFromFiles(options), + ]); + }, +); + +export default pipeSchematic; diff --git a/packages/schematics/angular/refactor/jasmine-vitest/index.ts b/packages/schematics/angular/refactor/jasmine-vitest/index.ts index a7e34ad26c02..0e2f2e35b9e7 100644 --- a/packages/schematics/angular/refactor/jasmine-vitest/index.ts +++ b/packages/schematics/angular/refactor/jasmine-vitest/index.ts @@ -121,6 +121,7 @@ export default function (options: Schema): Rule { const content = tree.readText(file); const newContent = transformJasmineToVitest(file, content, reporter, { addImports: !!options.addImports, + browserMode: !!options.browerMode, }); if (content !== newContent) { @@ -129,6 +130,11 @@ export default function (options: Schema): Rule { } } + if (options.report) { + const reportContent = reporter.generateReportContent(); + tree.create(`jasmine-vitest-${new Date().toISOString()}.md`, reportContent); + } + reporter.printSummary(options.verbose); }; } diff --git a/packages/schematics/angular/refactor/jasmine-vitest/schema.json b/packages/schematics/angular/refactor/jasmine-vitest/schema.json index 99f34057ffb5..4192a27367fd 100644 --- a/packages/schematics/angular/refactor/jasmine-vitest/schema.json +++ b/packages/schematics/angular/refactor/jasmine-vitest/schema.json @@ -30,6 +30,16 @@ "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 + }, + "browserMode": { + "type": "boolean", + "description": "Whether the tests are intended to run in browser mode. If true, the `toHaveClass` assertions are left as is because Vitest browser mode has such an assertion. Otherwise they're migrated to an equivalent assertion.", + "default": false + }, + "report": { + "type": "boolean", + "description": "Whether to generate a summary report file (jasmine-vitest-.md) in the project root.", + "default": true } } } 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 ebc7e1631907..d037ed5c7f08 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 @@ -11,10 +11,17 @@ import { format } from 'prettier'; import { transformJasmineToVitest } from './test-file-transformer'; import { RefactorReporter } from './utils/refactor-reporter'; -async function expectTransformation(input: string, expected: string): Promise { +async function expectTransformation( + input: string, + expected: string, + options: { addImports: boolean; browserMode: boolean } = { + addImports: false, + browserMode: false, + }, +): Promise { const logger = new logging.NullLogger(); const reporter = new RefactorReporter(logger); - const transformed = transformJasmineToVitest('spec.ts', input, reporter, { addImports: false }); + const transformed = transformJasmineToVitest('spec.ts', input, reporter, options); const formattedTransformed = await format(transformed, { parser: 'typescript' }); const formattedExpected = await format(expected, { parser: 'typescript' }); @@ -255,14 +262,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 +282,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 +308,7 @@ describe('Jasmine to Vitest Transformer - Integration Tests', () => { }); }); `; + /* eslint-enable max-len */ await expectTransformation(jasmineCode, vitestCode); }); @@ -386,6 +396,44 @@ describe('Jasmine to Vitest Transformer - Integration Tests', () => { await expectTransformation(jasmineCode, vitestCode); }); + it('should not transform toHaveClass in browser mode', async () => { + const jasmineCode = ` + describe('toHaveClass in browser mode', () => { + let el: HTMLElement; + + beforeEach(() => { + el = document.createElement('div'); + }); + + it('should handle DOM matchers like toHaveClass', () => { + el.classList.add('my-class'); + expect(el).withContext('element should have my-class').toHaveClass('my-class'); + el.classList.remove('my-class'); + expect(el).not.toHaveClass('my-class'); + }); + }); + `; + + const vitestCode = ` + describe('toHaveClass in browser mode', () => { + let el: HTMLElement; + + beforeEach(() => { + el = document.createElement('div'); + }); + + it('should handle DOM matchers like toHaveClass', () => { + el.classList.add('my-class'); + expect(el, 'element should have my-class').toHaveClass('my-class'); + el.classList.remove('my-class'); + expect(el).not.toHaveClass('my-class'); + }); + }); + `; + + await expectTransformation(jasmineCode, vitestCode, { addImports: false, browserMode: true }); + }); + it('should add TODOs for unsupported Jasmine features', async () => { const jasmineCode = ` describe('Unsupported Features', () => { @@ -416,7 +464,6 @@ describe('Jasmine to Vitest Transformer - Integration Tests', () => { }); `; - /* eslint-disable max-len */ const vitestCode = ` describe('Unsupported Features', () => { beforeAll(() => { @@ -448,7 +495,6 @@ 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 eb8afdfe0449..db225a0a4473 100644 --- a/packages/schematics/angular/refactor/jasmine-vitest/test-file-transformer.ts +++ b/packages/schematics/angular/refactor/jasmine-vitest/test-file-transformer.ts @@ -56,6 +56,19 @@ import { RefactorReporter } from './utils/refactor-reporter'; */ 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. @@ -152,7 +165,7 @@ export function transformJasmineToVitest( filePath: string, content: string, reporter: RefactorReporter, - options: { addImports: boolean }, + options: { addImports: boolean; browserMode: boolean }, ): string { const contentWithPlaceholders = preserveBlankLines(content); @@ -183,13 +196,15 @@ export function transformJasmineToVitest( if (ts.isCallExpression(transformedNode)) { if (options.addImports && ts.isIdentifier(transformedNode.expression)) { const name = transformedNode.expression.text; - if (name === 'describe' || name === 'it' || name === 'expect') { + if (VITEST_FUNCTION_NAMES.has(name)) { addVitestValueImport(pendingVitestValueImports, name); } } for (const transformer of callExpressionTransformers) { - transformedNode = transformer(transformedNode, refactorCtx); + if (!(options.browserMode && transformer === transformToHaveClass)) { + transformedNode = transformer(transformedNode, refactorCtx); + } } } else if (ts.isPropertyAccessExpression(transformedNode)) { for (const transformer of propertyAccessExpressionTransformers) { 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 index fac88bb9ed02..1fd4beb6546e 100644 --- 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 @@ -81,5 +81,41 @@ describe('Jasmine to Vitest Transformer', () => { `; 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 a93875b912e9..9aa6532206da 100644 --- a/packages/schematics/angular/refactor/jasmine-vitest/test-helpers.ts +++ b/packages/schematics/angular/refactor/jasmine-vitest/test-helpers.ts @@ -30,7 +30,10 @@ export async function expectTransformation( ): Promise { const logger = new logging.NullLogger(); const reporter = new RefactorReporter(logger); - const transformed = transformJasmineToVitest('spec.ts', input, reporter, { addImports }); + const transformed = transformJasmineToVitest('spec.ts', input, reporter, { + addImports, + browserMode: false, + }); 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..9b0c61b6dca9 100644 --- a/packages/schematics/angular/refactor/jasmine-vitest/transformers/jasmine-lifecycle.ts +++ b/packages/schematics/angular/refactor/jasmine-vitest/transformers/jasmine-lifecycle.ts @@ -85,7 +85,7 @@ export function transformPending( 'Converted `pending()` to a skipped test (`it.skip`).', ); const category = 'pending'; - reporter.recordTodo(category); + reporter.recordTodo(category, sourceFile, bodyNode); addTodoComment(replacement, category); ts.addSyntheticLeadingComment( replacement, @@ -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, sourceFile, node); + 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 b1b746aad504..05c137100271 100644 --- a/packages/schematics/angular/refactor/jasmine-vitest/transformers/jasmine-matcher.ts +++ b/packages/schematics/angular/refactor/jasmine-vitest/transformers/jasmine-matcher.ts @@ -56,7 +56,7 @@ export function transformSyntacticSugarMatchers( if (matcherName === 'toHaveSpyInteractions') { const category = 'toHaveSpyInteractions'; - reporter.recordTodo(category); + reporter.recordTodo(category, sourceFile, node); addTodoComment(node, category); return node; @@ -64,7 +64,7 @@ export function transformSyntacticSugarMatchers( if (matcherName === 'toThrowMatching') { const category = 'toThrowMatching'; - reporter.recordTodo(category); + reporter.recordTodo(category, sourceFile, node); addTodoComment(node, category, { name: matcherName }); return node; @@ -304,11 +304,11 @@ export function transformExpectAsync( if (matcherName) { if (matcherName === 'toBePending') { const category = 'toBePending'; - reporter.recordTodo(category); + reporter.recordTodo(category, sourceFile, node); addTodoComment(node, category); } else { const category = 'unsupported-expect-async-matcher'; - reporter.recordTodo(category); + reporter.recordTodo(category, sourceFile, node); addTodoComment(node, category, { name: matcherName }); } } @@ -418,7 +418,7 @@ export function transformArrayWithExactContents( if (!ts.isArrayLiteralExpression(argument.arguments[0])) { const category = 'arrayWithExactContents-dynamic-variable'; - reporter.recordTodo(category); + reporter.recordTodo(category, sourceFile, node); addTodoComment(node, category); return node; @@ -451,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, sourceFile, node); + addTodoComment(lengthStmt, category); + + return [lengthStmt, containingStmt]; } export function transformCalledOnceWith( @@ -611,7 +615,7 @@ export function transformExpectNothing( reporter.reportTransformation(sourceFile, node, 'Removed `expect().nothing()` statement.'); const category = 'expect-nothing'; - reporter.recordTodo(category); + reporter.recordTodo(category, sourceFile, node); addTodoComment(replacement, category); ts.addSyntheticLeadingComment( replacement, 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 f9667701e376..2872a3f7503e 100644 --- a/packages/schematics/angular/refactor/jasmine-vitest/transformers/jasmine-misc.ts +++ b/packages/schematics/angular/refactor/jasmine-vitest/transformers/jasmine-misc.ts @@ -61,7 +61,15 @@ export function transformTimerMocks( 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); } @@ -146,7 +154,7 @@ export function transformGlobalFunctions( `Found unsupported global function \`${functionName}\`.`, ); const category = 'unsupported-global-function'; - reporter.recordTodo(category); + reporter.recordTodo(category, sourceFile, node); addTodoComment(node, category, { name: functionName }); } @@ -179,7 +187,7 @@ export function transformUnsupportedJasmineCalls( node, `Found unsupported call \`jasmine.${methodName}\`.`, ); - reporter.recordTodo(methodName); + reporter.recordTodo(methodName, sourceFile, node); addTodoComment(node, methodName); } @@ -230,7 +238,7 @@ export function transformUnknownJasmineProperties( `Found unknown jasmine property \`jasmine.${propName}\`.`, ); const category = 'unknown-jasmine-property'; - reporter.recordTodo(category); + reporter.recordTodo(category, sourceFile, node); addTodoComment(node, category, { name: propName }); } } 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..0ce25ccacfd5 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 }) => { @@ -127,7 +132,6 @@ describe('Jasmine to Vitest Transformer', () => { return a.toString() === b.toString(); }); `, - // eslint-disable-next-line max-len expected: `// TODO: vitest-migration: jasmine.addCustomEqualityTester is not supported. Please manually migrate to expect.addEqualityTesters(). See: https://vitest.dev/api/expect.html#expect-addequalitytesters jasmine.addCustomEqualityTester((a, b) => { return a.toString() === b.toString(); 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 d2d9af5f01a6..f019dbb53099 100644 --- a/packages/schematics/angular/refactor/jasmine-vitest/transformers/jasmine-spy.ts +++ b/packages/schematics/angular/refactor/jasmine-vitest/transformers/jasmine-spy.ts @@ -153,7 +153,7 @@ export function transformSpies(node: ts.Node, refactorCtx: RefactorContext): ts. } default: { const category = 'unsupported-spy-strategy'; - reporter.recordTodo(category); + reporter.recordTodo(category, sourceFile, node); addTodoComment(node, category, { name: strategyName }); break; } @@ -202,7 +202,7 @@ export function transformSpies(node: ts.Node, refactorCtx: RefactorContext): ts. 'Found unsupported `jasmine.spyOnAllFunctions()`.', ); const category = 'spyOnAllFunctions'; - reporter.recordTodo(category); + reporter.recordTodo(category, sourceFile, node); addTodoComment(node, category); return node; @@ -227,25 +227,28 @@ export function transformCreateSpyObj( '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); + reporter.recordTodo(category, sourceFile, node); addTodoComment(node, category); 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); + reporter.recordTodo(category, sourceFile, node); addTodoComment(node, category); return node; @@ -256,7 +259,7 @@ export function transformCreateSpyObj( properties.push(...(propertiesArg.properties as unknown as ts.PropertyAssignment[])); } else { const category = 'createSpyObj-dynamic-property-map'; - reporter.recordTodo(category); + reporter.recordTodo(category, sourceFile, node); addTodoComment(node, category); } } @@ -264,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, ); } @@ -279,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( @@ -456,7 +486,7 @@ export function transformSpyCallInspection(node: ts.Node, refactorCtx: RefactorC node.parent.name.text !== 'args' ) { const category = 'mostRecent-without-args'; - reporter.recordTodo(category); + reporter.recordTodo(category, sourceFile, node); addTodoComment(node, category); } 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..6c11f776bd3b 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 @@ -59,7 +59,6 @@ describe('Jasmine to Vitest Transformer', () => { { description: 'should add a TODO for jasmine.spyOnAllFunctions(object)', input: `jasmine.spyOnAllFunctions(myObject);`, - // eslint-disable-next-line max-len expected: `// TODO: vitest-migration: Vitest does not have a direct equivalent for jasmine.spyOnAllFunctions(). Please spy on individual methods manually using vi.spyOn(). See: https://vitest.dev/api/vi.html#vi-spyon jasmine.spyOnAllFunctions(myObject); `, @@ -99,7 +98,6 @@ describe('Jasmine to Vitest Transformer', () => { { description: 'should add a TODO for an unsupported spy strategy', input: `spyOn(service, 'myMethod').and.unknownStrategy();`, - // eslint-disable-next-line max-len expected: `// TODO: vitest-migration: Unsupported spy strategy ".and.unknownStrategy()" found. Please migrate this manually. See: https://vitest.dev/api/mocked.html#mock vi.spyOn(service, 'myMethod').and.unknownStrategy();`, }, @@ -117,9 +115,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 +140,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 +159,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 +174,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"), };`, }, ]; @@ -242,7 +267,6 @@ vi.spyOn(service, 'myMethod').and.unknownStrategy();`, { description: 'should add a TODO for spy.calls.mostRecent() without .args', input: `const mostRecent = mySpy.calls.mostRecent();`, - // eslint-disable-next-line max-len expected: `// TODO: vitest-migration: Direct usage of mostRecent() is not supported. Please refactor to access .args directly or use vi.mocked(spy).mock.lastCall. See: https://vitest.dev/api/mocked.html#mock-lastcall const mostRecent = mySpy.calls.mostRecent();`, }, diff --git a/packages/schematics/angular/refactor/jasmine-vitest/utils/refactor-reporter.ts b/packages/schematics/angular/refactor/jasmine-vitest/utils/refactor-reporter.ts index 737abdc0ef94..63b924d12139 100644 --- a/packages/schematics/angular/refactor/jasmine-vitest/utils/refactor-reporter.ts +++ b/packages/schematics/angular/refactor/jasmine-vitest/utils/refactor-reporter.ts @@ -6,7 +6,6 @@ * found in the LICENSE file at https://angular.dev/license */ -import { logging } from '@angular-devkit/core'; import ts from '../../../third_party/github.com/Microsoft/TypeScript/lib/typescript'; import { TodoCategory } from './todo-notes'; @@ -15,8 +14,9 @@ export class RefactorReporter { private filesTransformed = 0; private readonly todos = new Map(); private readonly verboseLogs = new Map(); + private readonly fileTodos = new Map(); - constructor(private logger: logging.LoggerApi) {} + constructor(private logger: { info(message: string): void; warn(message: string): void }) {} get hasTodos(): boolean { return this.todos.size > 0; @@ -30,14 +30,27 @@ export class RefactorReporter { this.filesTransformed++; } - recordTodo(category: TodoCategory): void { + recordTodo(category: TodoCategory, sourceFile: ts.SourceFile, node: ts.Node): void { this.todos.set(category, (this.todos.get(category) ?? 0) + 1); + + const { line } = ts.getLineAndCharacterOfPosition( + sourceFile, + ts.getOriginalNode(node).getStart(sourceFile), + ); + const filePath = sourceFile.fileName; + + let fileTodos = this.fileTodos.get(filePath); + if (!fileTodos) { + fileTodos = []; + this.fileTodos.set(filePath, fileTodos); + } + fileTodos.push({ category, line: line + 1 }); } reportTransformation(sourceFile: ts.SourceFile, node: ts.Node, message: string): void { const { line } = ts.getLineAndCharacterOfPosition( sourceFile, - ts.getOriginalNode(node).getStart(), + ts.getOriginalNode(node).getStart(sourceFile), ); const filePath = sourceFile.fileName; @@ -49,6 +62,79 @@ export class RefactorReporter { logs.push(`L${line + 1}: ${message}`); } + generateReportContent(): string { + const lines: string[] = []; + lines.push('# Jasmine to Vitest Refactoring Report'); + lines.push(''); + lines.push(`Date: ${new Date().toISOString()}`); + lines.push(''); + + const summaryEntries = [ + { label: 'Files Scanned', value: this.filesScanned }, + { label: 'Files Transformed', value: this.filesTransformed }, + { label: 'Files Skipped', value: this.filesScanned - this.filesTransformed }, + { label: 'Total TODOs', value: [...this.todos.values()].reduce((a, b) => a + b, 0) }, + ]; + + const firstColPad = Math.max(...summaryEntries.map(({ label }) => label.length)); + const secondColPad = 5; + + lines.push('## Summary'); + lines.push(''); + lines.push(`| ${' '.padEnd(firstColPad)} | ${'Count'.padStart(secondColPad)} |`); + lines.push(`|:${'-'.repeat(firstColPad + 1)}|${'-'.repeat(secondColPad + 1)}:|`); + for (const { label, value } of summaryEntries) { + lines.push(`| ${label.padEnd(firstColPad)} | ${String(value).padStart(secondColPad)} |`); + } + lines.push(''); + + if (this.todos.size > 0) { + lines.push('## TODO Overview'); + lines.push(''); + const todoEntries = [...this.todos.entries()]; + const firstColPad = Math.max( + 'Category'.length, + ...todoEntries.map(([category]) => category.length), + ); + const secondColPad = 5; + + lines.push(`| ${'Category'.padEnd(firstColPad)} | ${'Count'.padStart(secondColPad)} |`); + lines.push(`|:${'-'.repeat(firstColPad + 1)}|${'-'.repeat(secondColPad + 1)}:|`); + for (const [category, count] of todoEntries) { + lines.push(`| ${category.padEnd(firstColPad)} | ${String(count).padStart(secondColPad)} |`); + } + lines.push(''); + } + + if (this.fileTodos.size > 0) { + lines.push('## Files Requiring Manual Attention'); + lines.push(''); + // Sort files alphabetically + const sortedFiles = [...this.fileTodos.keys()].sort(); + + for (const filePath of sortedFiles) { + const relativePath = filePath.startsWith('/') ? filePath.substring(1) : filePath; + lines.push(`### [\`${relativePath}\`](./${relativePath})`); + const todos = this.fileTodos.get(filePath); + if (todos) { + // Sort todos by line number + todos.sort((a, b) => a.line - b.line); + + for (const todo of todos) { + lines.push(`- [L${todo.line}](./${relativePath}#L${todo.line}): ${todo.category}`); + } + } + lines.push(''); + } + } else { + lines.push('## No Manual Changes Required'); + lines.push(''); + lines.push('All identified patterns were successfully transformed.'); + } + + return lines.join('\n'); + } + printSummary(verbose = false): void { if (verbose && this.verboseLogs.size > 0) { this.logger.info('Detailed Transformation Log:'); diff --git a/packages/schematics/angular/refactor/jasmine-vitest/utils/refactor-reporter_spec.ts b/packages/schematics/angular/refactor/jasmine-vitest/utils/refactor-reporter_spec.ts index 10053135a10e..184dca31f9c7 100644 --- a/packages/schematics/angular/refactor/jasmine-vitest/utils/refactor-reporter_spec.ts +++ b/packages/schematics/angular/refactor/jasmine-vitest/utils/refactor-reporter_spec.ts @@ -7,11 +7,14 @@ */ import { logging } from '@angular-devkit/core'; +import ts from '../../../third_party/github.com/Microsoft/TypeScript/lib/typescript'; import { RefactorReporter } from './refactor-reporter'; describe('RefactorReporter', () => { let logger: logging.LoggerApi; let reporter: RefactorReporter; + let sourceFile: ts.SourceFile; + let node: ts.Node; beforeEach(() => { logger = { @@ -19,6 +22,8 @@ describe('RefactorReporter', () => { warn: jasmine.createSpy('warn'), } as unknown as logging.LoggerApi; reporter = new RefactorReporter(logger); + sourceFile = ts.createSourceFile('/test.spec.ts', 'statement;', ts.ScriptTarget.Latest); + node = sourceFile.statements[0]; }); it('should correctly increment scanned and transformed files', () => { @@ -34,9 +39,9 @@ describe('RefactorReporter', () => { }); it('should record and count todos by category', () => { - reporter.recordTodo('pending'); - reporter.recordTodo('spyOnAllFunctions'); - reporter.recordTodo('pending'); + reporter.recordTodo('pending', sourceFile, node); + reporter.recordTodo('spyOnAllFunctions', sourceFile, node); + reporter.recordTodo('pending', sourceFile, node); reporter.printSummary(); expect(logger.warn).toHaveBeenCalledWith('- 3 TODO(s) added for manual review:'); @@ -48,4 +53,25 @@ describe('RefactorReporter', () => { reporter.printSummary(); expect(logger.warn).not.toHaveBeenCalled(); }); + + it('should generate a markdown report with TODOs', () => { + reporter.incrementScannedFiles(); + reporter.recordTodo('pending', sourceFile, node); + + const report = reporter.generateReportContent(); + + expect(report).toContain('# Jasmine to Vitest Refactoring Report'); + expect(report).toContain('## Summary'); + expect(report).toContain('| | Count |'); + expect(report).toContain('|:------------------|------:|'); + expect(report).toContain('| Files Scanned | 1 |'); + expect(report).toContain('| Total TODOs | 1 |'); + expect(report).toContain('## TODO Overview'); + expect(report).toContain('| Category | Count |'); + expect(report).toContain('|:---------|------:|'); + expect(report).toContain('| pending | 1 |'); + expect(report).toContain('## Files Requiring Manual Attention'); + expect(report).toContain('### [`test.spec.ts`](./test.spec.ts)'); + expect(report).toContain('- [L1](./test.spec.ts#L1): pending'); + }); }); 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..8a9d888a0298 100644 --- a/packages/schematics/angular/refactor/jasmine-vitest/utils/todo-notes.ts +++ b/packages/schematics/angular/refactor/jasmine-vitest/utils/todo-notes.ts @@ -39,7 +39,7 @@ export const TODO_NOTES = { 'Please migrate this manually by checking the `mock.calls.length` of the individual spies.', }, 'toThrowMatching': { - message: (context: { name: string }) => + message: (context: { name: string }): string => `Unsupported matcher ".${context.name}()" found. Please migrate this manually.`, url: 'https://vitest.dev/api/expect.html#tothrowerror', }, @@ -49,19 +49,23 @@ export const TODO_NOTES = { 'Please migrate this manually, for example by using `Promise.race` to check if the promise settles within a short timeout.', }, 'unsupported-expect-async-matcher': { - message: (context: { name: string }) => + message: (context: { name: string }): string => `Unsupported expectAsync matcher ".${context.name}()" found. Please migrate this manually.`, }, 'arrayWithExactContents-dynamic-variable': { 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.', }, 'unsupported-global-function': { - message: (context: { name: string }) => + message: (context: { name: string }): string => `Unsupported global function \`${context.name}\` found. This function is used for custom reporters in Jasmine ` + 'and has no direct equivalent in Vitest.', }, @@ -85,7 +89,7 @@ export const TODO_NOTES = { ' Please manually assert the contents of the Set.', }, 'unknown-jasmine-property': { - message: (context: { name: string }) => + message: (context: { name: string }): string => `Unsupported jasmine property "${context.name}" found. Please migrate this manually.`, }, 'spyOnAllFunctions': { @@ -110,7 +114,7 @@ export const TODO_NOTES = { url: 'https://vitest.dev/api/vi.html#vi-fn', }, 'unsupported-spy-strategy': { - message: (context: { name: string }) => + message: (context: { name: string }): string => `Unsupported spy strategy ".and.${context.name}()" found. Please migrate this manually.`, url: 'https://vitest.dev/api/mocked.html#mock', }, @@ -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/server/index.ts b/packages/schematics/angular/server/index.ts index 00448bfbfb1e..077114e9027d 100644 --- a/packages/schematics/angular/server/index.ts +++ b/packages/schematics/angular/server/index.ts @@ -9,6 +9,7 @@ import { JsonValue, Path, basename, dirname, join, normalize } from '@angular-devkit/core'; import { Rule, + RuleFactory, SchematicsException, Tree, apply, @@ -163,91 +164,97 @@ function addDependencies(skipInstall: boolean | undefined): Rule { }; } -export default createProjectSchematic(async (options, { project, tree }) => { - if (project?.extensions.projectType !== 'application') { - throw new SchematicsException(`Server schematic requires a project type of "application".`); - } - - const clientBuildTarget = project.targets.get('build'); - if (!clientBuildTarget) { - throw targetBuildNotFoundError(); - } - - const usingApplicationBuilder = isUsingApplicationBuilder(project); - - if ( - project.targets.has('server') || - (usingApplicationBuilder && clientBuildTarget.options?.server !== undefined) - ) { - // Server has already been added. - return noop(); - } - - const clientBuildOptions = clientBuildTarget.options as Record; - const browserEntryPoint = await getMainFilePath(tree, options.project); - const isStandalone = isStandaloneApp(tree, browserEntryPoint); - const sourceRoot = project.sourceRoot ?? join(normalize(project.root), 'src'); - - let filesUrl = `./files/${usingApplicationBuilder ? 'application-builder/' : 'server-builder/'}`; - filesUrl += isStandalone ? 'standalone-src' : 'ngmodule-src'; - - const { componentName, componentImportPathInSameFile, moduleName, moduleImportPathInSameFile } = - resolveBootstrappedComponentData(tree, browserEntryPoint) || { - componentName: 'App', - componentImportPathInSameFile: './app/app', - moduleName: 'AppModule', - moduleImportPathInSameFile: './app/app.module', - }; - const templateSource = apply(url(filesUrl), [ - applyTemplates({ - ...strings, - ...options, - appComponentName: componentName, - appComponentPath: componentImportPathInSameFile, - appModuleName: moduleName, - appModulePath: - moduleImportPathInSameFile === null - ? null - : `./${posix.basename(moduleImportPathInSameFile)}`, - }), - move(sourceRoot), - ]); - - const clientTsConfig = normalize(clientBuildOptions.tsConfig); - const tsConfigExtends = basename(clientTsConfig); - const tsConfigDirectory = dirname(clientTsConfig); - - return chain([ - mergeWith(templateSource), - ...(usingApplicationBuilder - ? [ - updateConfigFileApplicationBuilder(options), - updateTsConfigFile(clientBuildOptions.tsConfig), - ] - : [ - mergeWith( - apply(url('./files/server-builder/root'), [ - applyTemplates({ - ...strings, - ...options, - stripTsExtension: (s: string) => s.replace(/\.ts$/, ''), - tsConfigExtends, - hasLocalizePackage: !!getPackageJsonDependency(tree, '@angular/localize'), - relativePathToWorkspaceRoot: relativePathToWorkspaceRoot(tsConfigDirectory), - }), - move(tsConfigDirectory), - ]), - ), - updateConfigFileBrowserBuilder(options, tsConfigDirectory), - ]), - addDependencies(options.skipInstall), - addRootProvider( - options.project, - ({ code, external }) => - code`${external('provideClientHydration', '@angular/platform-browser')}(${external( - 'withEventReplay', - '@angular/platform-browser', - )}())`, - ), - ]); -}); +const serverSchematic: RuleFactory = createProjectSchematic( + async (options, { project, tree }) => { + if (project?.extensions.projectType !== 'application') { + throw new SchematicsException(`Server schematic requires a project type of "application".`); + } + + const clientBuildTarget = project.targets.get('build'); + if (!clientBuildTarget) { + throw targetBuildNotFoundError(); + } + + const usingApplicationBuilder = isUsingApplicationBuilder(project); + + if ( + project.targets.has('server') || + (usingApplicationBuilder && clientBuildTarget.options?.server !== undefined) + ) { + // Server has already been added. + return noop(); + } + + const clientBuildOptions = clientBuildTarget.options as Record; + const browserEntryPoint = await getMainFilePath(tree, options.project); + const isStandalone = isStandaloneApp(tree, browserEntryPoint); + const sourceRoot = project.sourceRoot ?? join(normalize(project.root), 'src'); + + let filesUrl = `./files/${ + usingApplicationBuilder ? 'application-builder/' : 'server-builder/' + }`; + filesUrl += isStandalone ? 'standalone-src' : 'ngmodule-src'; + + const { componentName, componentImportPathInSameFile, moduleName, moduleImportPathInSameFile } = + resolveBootstrappedComponentData(tree, browserEntryPoint) || { + componentName: 'App', + componentImportPathInSameFile: './app/app', + moduleName: 'AppModule', + moduleImportPathInSameFile: './app/app.module', + }; + const templateSource = apply(url(filesUrl), [ + applyTemplates({ + ...strings, + ...options, + appComponentName: componentName, + appComponentPath: componentImportPathInSameFile, + appModuleName: moduleName, + appModulePath: + moduleImportPathInSameFile === null + ? null + : `./${posix.basename(moduleImportPathInSameFile)}`, + }), + move(sourceRoot), + ]); + + const clientTsConfig = normalize(clientBuildOptions.tsConfig); + const tsConfigExtends = basename(clientTsConfig); + const tsConfigDirectory = dirname(clientTsConfig); + + return chain([ + mergeWith(templateSource), + ...(usingApplicationBuilder + ? [ + updateConfigFileApplicationBuilder(options), + updateTsConfigFile(clientBuildOptions.tsConfig), + ] + : [ + mergeWith( + apply(url('./files/server-builder/root'), [ + applyTemplates({ + ...strings, + ...options, + stripTsExtension: (s: string) => s.replace(/\.ts$/, ''), + tsConfigExtends, + hasLocalizePackage: !!getPackageJsonDependency(tree, '@angular/localize'), + relativePathToWorkspaceRoot: relativePathToWorkspaceRoot(tsConfigDirectory), + }), + move(tsConfigDirectory), + ]), + ), + updateConfigFileBrowserBuilder(options, tsConfigDirectory), + ]), + addDependencies(options.skipInstall), + addRootProvider( + options.project, + ({ code, external }) => + code`${external('provideClientHydration', '@angular/platform-browser')}(${external( + 'withEventReplay', + '@angular/platform-browser', + )}())`, + ), + ]); + }, +); + +export default serverSchematic; diff --git a/packages/schematics/angular/service-worker/index.ts b/packages/schematics/angular/service-worker/index.ts index c357e5b9187d..4ef2c9839def 100644 --- a/packages/schematics/angular/service-worker/index.ts +++ b/packages/schematics/angular/service-worker/index.ts @@ -8,6 +8,7 @@ import { Rule, + RuleFactory, SchematicContext, SchematicsException, Tree, @@ -104,7 +105,7 @@ function getTsSourceFile(host: Tree, path: string): ts.SourceFile { return source; } -export default createProjectSchematic( +const serviceWorkerSchematic: RuleFactory = createProjectSchematic( async (options, { project, workspace, tree }) => { if (project.extensions.projectType !== 'application') { throw new SchematicsException(`Service worker requires a project type of "application".`); @@ -151,6 +152,8 @@ export default createProjectSchematic( }, ); +export default serviceWorkerSchematic; + function addImport(host: Tree, filePath: string, symbolName: string, moduleName: string): void { const moduleSource = getTsSourceFile(host, filePath); const change = insertImport(moduleSource, filePath, symbolName, moduleName); diff --git a/packages/schematics/angular/service/index.ts b/packages/schematics/angular/service/index.ts index 48558dcc0d3a..7805e78cc823 100644 --- a/packages/schematics/angular/service/index.ts +++ b/packages/schematics/angular/service/index.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.dev/license */ -import { Rule, strings } from '@angular-devkit/schematics'; +import { RuleFactory, strings } from '@angular-devkit/schematics'; import { generateFromFiles } from '../utility/generate-from-files'; import { parseName } from '../utility/parse-name'; import { createProjectSchematic } from '../utility/project'; @@ -14,22 +14,26 @@ import { validateClassName } from '../utility/validation'; import { buildDefaultPath } from '../utility/workspace'; import { Schema as ServiceOptions } from './schema'; -export default createProjectSchematic((options, { project, tree }) => { - if (options.path === undefined) { - options.path = buildDefaultPath(project); - } +const serviceSchematic: RuleFactory = createProjectSchematic( + (options, { project, tree }) => { + if (options.path === undefined) { + options.path = buildDefaultPath(project); + } - const parsedPath = parseName(options.path, options.name); - options.name = parsedPath.name; - options.path = parsedPath.path; + const parsedPath = parseName(options.path, options.name); + options.name = parsedPath.name; + options.path = parsedPath.path; - const classifiedName = - strings.classify(options.name) + - (options.addTypeToClassName && options.type ? strings.classify(options.type) : ''); - validateClassName(classifiedName); + const classifiedName = + strings.classify(options.name) + + (options.addTypeToClassName && options.type ? strings.classify(options.type) : ''); + validateClassName(classifiedName); - return generateFromFiles({ - ...options, - classifiedName, - }); -}); + return generateFromFiles({ + ...options, + classifiedName, + }); + }, +); + +export default serviceSchematic; diff --git a/packages/schematics/angular/ssr/index.ts b/packages/schematics/angular/ssr/index.ts index 8d3132228f24..6e27eab47cd5 100644 --- a/packages/schematics/angular/ssr/index.ts +++ b/packages/schematics/angular/ssr/index.ts @@ -9,6 +9,7 @@ import { isJsonObject } from '@angular-devkit/core'; import { Rule, + RuleFactory, SchematicContext, SchematicsException, Tree, @@ -360,29 +361,33 @@ function addServerFile( }; } -export default createProjectSchematic(async (options, { project, tree, context }) => { - const browserEntryPoint = await getMainFilePath(tree, options.project); - const isStandalone = isStandaloneApp(tree, browserEntryPoint); +const ssrSchematic: RuleFactory = createProjectSchematic( + async (options, { project, tree, context }) => { + const browserEntryPoint = await getMainFilePath(tree, options.project); + const isStandalone = isStandaloneApp(tree, browserEntryPoint); - const usingApplicationBuilder = isUsingApplicationBuilder(project); - const sourceRoot = project.sourceRoot ?? join(project.root, 'src'); + const usingApplicationBuilder = isUsingApplicationBuilder(project); + const sourceRoot = project.sourceRoot ?? join(project.root, 'src'); - return chain([ - schematic('server', { - ...options, - skipInstall: true, - }), - ...(usingApplicationBuilder - ? [ - updateApplicationBuilderWorkspaceConfigRule(sourceRoot, options, context), - updateApplicationBuilderTsConfigRule(options), - ] - : [ - updateWebpackBuilderServerTsConfigRule(options), - updateWebpackBuilderWorkspaceConfigRule(sourceRoot, options), - ]), - addServerFile(sourceRoot, options, isStandalone), - addScriptsRule(options, usingApplicationBuilder), - addDependencies(options, usingApplicationBuilder), - ]); -}); + return chain([ + schematic('server', { + ...options, + skipInstall: true, + }), + ...(usingApplicationBuilder + ? [ + updateApplicationBuilderWorkspaceConfigRule(sourceRoot, options, context), + updateApplicationBuilderTsConfigRule(options), + ] + : [ + updateWebpackBuilderServerTsConfigRule(options), + updateWebpackBuilderWorkspaceConfigRule(sourceRoot, options), + ]), + addServerFile(sourceRoot, options, isStandalone), + addScriptsRule(options, usingApplicationBuilder), + addDependencies(options, usingApplicationBuilder), + ]); + }, +); + +export default ssrSchematic; diff --git a/packages/schematics/angular/tailwind/index.ts b/packages/schematics/angular/tailwind/index.ts index 152399deee5b..e246e5f55bfe 100644 --- a/packages/schematics/angular/tailwind/index.ts +++ b/packages/schematics/angular/tailwind/index.ts @@ -8,6 +8,7 @@ import { type Rule, + RuleFactory, SchematicsException, apply, applyTemplates, @@ -122,16 +123,20 @@ function managePostCssConfiguration(project: ProjectDefinition): Rule { }; } -export default createProjectSchematic((options, { project }) => { - return chain([ - addTailwindStyles(options, project), - managePostCssConfiguration(project), - ...TAILWIND_DEPENDENCIES.map((name) => - addDependency(name, latestVersions[name], { - type: DependencyType.Dev, - existing: ExistingBehavior.Skip, - install: options.skipInstall ? InstallBehavior.None : InstallBehavior.Auto, - }), - ), - ]); -}); +const tailwindSchematic: RuleFactory = createProjectSchematic( + (options, { project }) => { + return chain([ + addTailwindStyles(options, project), + managePostCssConfiguration(project), + ...TAILWIND_DEPENDENCIES.map((name) => + addDependency(name, latestVersions[name], { + type: DependencyType.Dev, + existing: ExistingBehavior.Skip, + install: options.skipInstall ? InstallBehavior.None : InstallBehavior.Auto, + }), + ), + ]); + }, +); + +export default tailwindSchematic; diff --git a/packages/schematics/angular/utility/change.ts b/packages/schematics/angular/utility/change.ts index e4055620258c..6736b29e594b 100644 --- a/packages/schematics/angular/utility/change.ts +++ b/packages/schematics/angular/utility/change.ts @@ -33,9 +33,9 @@ export interface Change { */ export class NoopChange implements Change { description = 'No operation.'; - order = Infinity; + order: number = Infinity; path = null; - apply() { + apply(): Promise { return Promise.resolve(); } } @@ -62,7 +62,7 @@ export class InsertChange implements Change { /** * This method does not insert spaces if there is none in the original string. */ - apply(host: Host) { + apply(host: Host): Promise { return host.read(this.path).then((content) => { const prefix = content.substring(0, this.pos); const suffix = content.substring(this.pos); diff --git a/packages/schematics/angular/utility/dependencies.ts b/packages/schematics/angular/utility/dependencies.ts index 06c4f38653bd..b90ba8796975 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 { @@ -34,7 +37,7 @@ const ALL_DEPENDENCY_TYPE = [ export function addPackageJsonDependency( tree: Tree, dependency: NodeDependency, - pkgJsonPath = PKG_JSON_PATH, + pkgJsonPath: string = PKG_JSON_PATH, ): void { const json = new JSONFile(tree, pkgJsonPath); @@ -48,7 +51,7 @@ export function addPackageJsonDependency( export function removePackageJsonDependency( tree: Tree, name: string, - pkgJsonPath = PKG_JSON_PATH, + pkgJsonPath: string = PKG_JSON_PATH, ): void { const json = new JSONFile(tree, pkgJsonPath); @@ -60,7 +63,7 @@ export function removePackageJsonDependency( export function getPackageJsonDependency( tree: Tree, name: string, - pkgJsonPath = PKG_JSON_PATH, + pkgJsonPath: string = PKG_JSON_PATH, ): NodeDependency | null { const json = new JSONFile(tree, pkgJsonPath); @@ -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 10f2cf8eb6bd..29ef3658f23b 100644 --- a/packages/schematics/angular/utility/latest-versions/package.json +++ b/packages/schematics/angular/utility/latest-versions/package.json @@ -8,7 +8,7 @@ "@types/node": "^20.17.19", "browser-sync": "^3.0.0", "express": "^5.1.0", - "jasmine-core": "~5.12.0", + "jasmine-core": "~5.13.0", "jasmine-spec-reporter": "~7.0.0", "karma-chrome-launcher": "~3.2.0", "karma-coverage": "~2.2.0", @@ -26,6 +26,6 @@ "ts-node": "~10.9.0", "typescript": "~5.9.2", "vitest": "^4.0.8", - "zone.js": "~0.15.0" + "zone.js": "~0.16.0" } } diff --git a/packages/schematics/angular/utility/standalone/code_block.ts b/packages/schematics/angular/utility/standalone/code_block.ts index 128c6076a41b..439699718cb9 100644 --- a/packages/schematics/angular/utility/standalone/code_block.ts +++ b/packages/schematics/angular/utility/standalone/code_block.ts @@ -75,7 +75,10 @@ export class CodeBlock { * @param initialCode Code pending transformed. * @param filePath Path of the file in which the code will be inserted. */ - static transformPendingCode(initialCode: PendingCode, filePath: string) { + static transformPendingCode( + initialCode: PendingCode, + filePath: string, + ): { code: PendingCode; rules: Rule[] } { const code = { ...initialCode }; const rules: Rule[] = []; diff --git a/packages/schematics/angular/utility/standalone/rules_spec.ts b/packages/schematics/angular/utility/standalone/rules_spec.ts index da11e0527c11..3208ed0d6f04 100644 --- a/packages/schematics/angular/utility/standalone/rules_spec.ts +++ b/packages/schematics/angular/utility/standalone/rules_spec.ts @@ -354,7 +354,6 @@ describe('standalone utilities', () => { host, ); - // eslint-disable-next-line @typescript-eslint/no-floating-promises await expectAsync(promise).toBeRejectedWithError( `Cannot add provider to invalid bootstrapApplication call in ${mainPath}`, ); @@ -384,7 +383,6 @@ describe('standalone utilities', () => { host, ); - // eslint-disable-next-line @typescript-eslint/no-floating-promises await expectAsync(promise).toBeRejectedWithError( `Cannot statically analyze bootstrapApplication call in ${mainPath}`, ); @@ -402,7 +400,6 @@ describe('standalone utilities', () => { host, ); - // eslint-disable-next-line @typescript-eslint/no-floating-promises await expectAsync(promise).toBeRejectedWithError('Bootstrap call not found'); }); }); diff --git a/packages/schematics/angular/utility/standalone/util.ts b/packages/schematics/angular/utility/standalone/util.ts index 4c64e68ad559..433967bce172 100644 --- a/packages/schematics/angular/utility/standalone/util.ts +++ b/packages/schematics/angular/utility/standalone/util.ts @@ -137,7 +137,7 @@ function findImportLocalName( * @param path Path to the file that is being changed. * @param changes Changes that should be applied to the file. */ -export function applyChangesToFile(tree: Tree, path: string, changes: Change[]) { +export function applyChangesToFile(tree: Tree, path: string, changes: Change[]): void { if (changes.length > 0) { const recorder = tree.beginUpdate(path); applyToUpdateRecorder(recorder, changes); diff --git a/packages/schematics/angular/utility/validation.ts b/packages/schematics/angular/utility/validation.ts index 8b380d1b8262..54c3fddb815d 100644 --- a/packages/schematics/angular/utility/validation.ts +++ b/packages/schematics/angular/utility/validation.ts @@ -10,7 +10,7 @@ import { SchematicsException } from '@angular-devkit/schematics'; // Must start with a letter, and must contain only alphanumeric characters or dashes. // When adding a dash the segment after the dash must also start with a letter. -export const htmlSelectorRe = +export const htmlSelectorRe: RegExp = /^[a-zA-Z][.0-9a-zA-Z]*((:?-[0-9]+)*|(:?-[a-zA-Z][.0-9a-zA-Z]*(:?-[0-9]+)*)*)$/; // See: https://github.com/tc39/proposal-regexp-unicode-property-escapes/blob/fe6d07fad74cd0192d154966baa1e95e7cda78a1/README.md#other-examples diff --git a/packages/schematics/angular/utility/workspace.ts b/packages/schematics/angular/utility/workspace.ts index b831458edf40..f8567fc534a3 100644 --- a/packages/schematics/angular/utility/workspace.ts +++ b/packages/schematics/angular/utility/workspace.ts @@ -81,7 +81,7 @@ export function updateWorkspace( */ export async function getWorkspace( tree: Tree, - path = DEFAULT_WORKSPACE_PATH, + path: string = DEFAULT_WORKSPACE_PATH, ): Promise { const host = new TreeWorkspaceHost(tree); diff --git a/packages/schematics/angular/web-worker/index.ts b/packages/schematics/angular/web-worker/index.ts index c9b5da381cbb..a19e2b714174 100644 --- a/packages/schematics/angular/web-worker/index.ts +++ b/packages/schematics/angular/web-worker/index.ts @@ -8,6 +8,7 @@ import { Rule, + RuleFactory, SchematicContext, SchematicsException, Tree, @@ -73,54 +74,58 @@ if (typeof Worker !== 'undefined') { }; } -export default createProjectSchematic((options, { project }) => { - const projectType = project.extensions['projectType']; - if (projectType !== 'application') { - throw new SchematicsException(`Web Worker requires a project type of "application".`); - } +const webWorkerSchematic: RuleFactory = createProjectSchematic( + (options, { project }) => { + const projectType = project.extensions['projectType']; + if (projectType !== 'application') { + throw new SchematicsException(`Web Worker requires a project type of "application".`); + } + + if (options.path === undefined) { + options.path = buildDefaultPath(project); + } + const parsedPath = parseName(options.path, options.name); + options.name = parsedPath.name; + options.path = parsedPath.path; - if (options.path === undefined) { - options.path = buildDefaultPath(project); - } - const parsedPath = parseName(options.path, options.name); - options.name = parsedPath.name; - options.path = parsedPath.path; + const templateSourceWorkerCode = apply(url('./files/worker'), [ + applyTemplates({ ...options, ...strings }), + move(parsedPath.path), + ]); - const templateSourceWorkerCode = apply(url('./files/worker'), [ - applyTemplates({ ...options, ...strings }), - move(parsedPath.path), - ]); + const root = project.root || ''; + const templateSourceWorkerConfig = apply(url('./files/worker-tsconfig'), [ + applyTemplates({ + ...options, + relativePathToWorkspaceRoot: relativePathToWorkspaceRoot(root), + }), + move(root), + ]); - const root = project.root || ''; - const templateSourceWorkerConfig = apply(url('./files/worker-tsconfig'), [ - applyTemplates({ - ...options, - relativePathToWorkspaceRoot: relativePathToWorkspaceRoot(root), - }), - move(root), - ]); + return chain([ + // Add project configuration. + updateWorkspace((workspace) => { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const project = workspace.projects.get(options.project)!; + const buildTarget = project.targets.get('build'); + const testTarget = project.targets.get('test'); + if (!buildTarget) { + throw new Error(`Build target is not defined for this project.`); + } - return chain([ - // Add project configuration. - updateWorkspace((workspace) => { - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - const project = workspace.projects.get(options.project)!; - const buildTarget = project.targets.get('build'); - const testTarget = project.targets.get('test'); - if (!buildTarget) { - throw new Error(`Build target is not defined for this project.`); - } + const workerConfigPath = join(root, 'tsconfig.worker.json'); + (buildTarget.options ??= {}).webWorkerTsConfig ??= workerConfigPath; + if (testTarget) { + (testTarget.options ??= {}).webWorkerTsConfig ??= workerConfigPath; + } + }), + // Create the worker in a sibling module. + options.snippet ? addSnippet(options) : noop(), + // Add the worker. + mergeWith(templateSourceWorkerCode), + mergeWith(templateSourceWorkerConfig), + ]); + }, +); - const workerConfigPath = join(root, 'tsconfig.worker.json'); - (buildTarget.options ??= {}).webWorkerTsConfig ??= workerConfigPath; - if (testTarget) { - (testTarget.options ??= {}).webWorkerTsConfig ??= workerConfigPath; - } - }), - // Create the worker in a sibling module. - options.snippet ? addSnippet(options) : noop(), - // Add the worker. - mergeWith(templateSourceWorkerCode), - mergeWith(templateSourceWorkerConfig), - ]); -}); +export default webWorkerSchematic; 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/packages/schematics/angular/workspace/files/__dot__gitignore.template b/packages/schematics/angular/workspace/files/__dot__gitignore.template index b1d225e26e57..854acd5fc039 100644 --- a/packages/schematics/angular/workspace/files/__dot__gitignore.template +++ b/packages/schematics/angular/workspace/files/__dot__gitignore.template @@ -26,6 +26,7 @@ yarn-error.log !.vscode/tasks.json !.vscode/launch.json !.vscode/extensions.json +!.vscode/mcp.json .history/* # Miscellaneous diff --git a/packages/schematics/angular/workspace/files/__dot__vscode/mcp.json.template b/packages/schematics/angular/workspace/files/__dot__vscode/mcp.json.template new file mode 100644 index 000000000000..bf4004da9477 --- /dev/null +++ b/packages/schematics/angular/workspace/files/__dot__vscode/mcp.json.template @@ -0,0 +1,9 @@ +{ + // For more information, visit: https://angular.dev/ai/mcp + "mcpServers": { + "angular-cli": { + "command": "npx", + "args": ["-y", "@angular/cli", "mcp"] + } + } +} diff --git a/packages/schematics/angular/workspace/files/__dot__vscode/tasks.json.template b/packages/schematics/angular/workspace/files/__dot__vscode/tasks.json.template index f3125a95b776..96e9e1c26833 100644 --- a/packages/schematics/angular/workspace/files/__dot__vscode/tasks.json.template +++ b/packages/schematics/angular/workspace/files/__dot__vscode/tasks.json.template @@ -12,10 +12,10 @@ "background": { "activeOnStart": true, "beginsPattern": { - "regexp": "(.*?)" + "regexp": "Changes detected" }, "endsPattern": { - "regexp": "bundle generation complete" + "regexp": "bundle generation (complete|failed)" } } } @@ -30,10 +30,10 @@ "background": { "activeOnStart": true, "beginsPattern": { - "regexp": "(.*?)" + "regexp": "Changes detected" }, "endsPattern": { - "regexp": "bundle generation complete" + "regexp": "bundle generation (complete|failed)" } } } diff --git a/packages/schematics/angular/workspace/index_spec.ts b/packages/schematics/angular/workspace/index_spec.ts index 22be6e797e14..65dd7987aa41 100644 --- a/packages/schematics/angular/workspace/index_spec.ts +++ b/packages/schematics/angular/workspace/index_spec.ts @@ -29,6 +29,7 @@ describe('Workspace Schematic', () => { jasmine.arrayContaining([ '/.vscode/extensions.json', '/.vscode/launch.json', + '/.vscode/mcp.json', '/.vscode/tasks.json', '/.editorconfig', '/angular.json', @@ -70,6 +71,7 @@ describe('Workspace Schematic', () => { jasmine.arrayContaining([ '/.vscode/extensions.json', '/.vscode/launch.json', + '/.vscode/mcp.json', '/.vscode/tasks.json', '/angular.json', '/.gitignore', diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c3ca74ef6ef1..836c840b056a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -20,47 +20,47 @@ importers: built: true devDependencies: '@angular/animations': - specifier: 21.0.0-rc.2 - version: 21.0.0-rc.2(@angular/core@21.0.0-rc.2(@angular/compiler@21.0.0-rc.2)(rxjs@7.8.2)(zone.js@0.15.1)) + specifier: 21.1.0-next.4 + version: 21.1.0-next.4(@angular/core@21.1.0-next.4(@angular/compiler@21.1.0-next.4)(rxjs@7.8.2)(zone.js@0.16.0)) '@angular/cdk': - specifier: 21.0.0-rc.2 - version: 21.0.0-rc.2(@angular/common@21.0.0-rc.2(@angular/core@21.0.0-rc.2(@angular/compiler@21.0.0-rc.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@21.0.0-rc.2(@angular/compiler@21.0.0-rc.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2) + specifier: 21.1.0-next.3 + version: 21.1.0-next.3(@angular/common@21.1.0-next.4(@angular/core@21.1.0-next.4(@angular/compiler@21.1.0-next.4)(rxjs@7.8.2)(zone.js@0.16.0))(rxjs@7.8.2))(@angular/core@21.1.0-next.4(@angular/compiler@21.1.0-next.4)(rxjs@7.8.2)(zone.js@0.16.0))(@angular/platform-browser@21.1.0-next.4(@angular/animations@21.1.0-next.4(@angular/core@21.1.0-next.4(@angular/compiler@21.1.0-next.4)(rxjs@7.8.2)(zone.js@0.16.0)))(@angular/common@21.1.0-next.4(@angular/core@21.1.0-next.4(@angular/compiler@21.1.0-next.4)(rxjs@7.8.2)(zone.js@0.16.0))(rxjs@7.8.2))(@angular/core@21.1.0-next.4(@angular/compiler@21.1.0-next.4)(rxjs@7.8.2)(zone.js@0.16.0)))(rxjs@7.8.2) '@angular/common': - specifier: 21.0.0-rc.2 - version: 21.0.0-rc.2(@angular/core@21.0.0-rc.2(@angular/compiler@21.0.0-rc.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2) + specifier: 21.1.0-next.4 + version: 21.1.0-next.4(@angular/core@21.1.0-next.4(@angular/compiler@21.1.0-next.4)(rxjs@7.8.2)(zone.js@0.16.0))(rxjs@7.8.2) '@angular/compiler': - specifier: 21.0.0-rc.2 - version: 21.0.0-rc.2 + specifier: 21.1.0-next.4 + version: 21.1.0-next.4 '@angular/compiler-cli': - specifier: 21.0.0-rc.2 - version: 21.0.0-rc.2(@angular/compiler@21.0.0-rc.2)(typescript@5.9.3) + specifier: 21.1.0-next.4 + version: 21.1.0-next.4(@angular/compiler@21.1.0-next.4)(typescript@5.9.3) '@angular/core': - specifier: 21.0.0-rc.2 - version: 21.0.0-rc.2(@angular/compiler@21.0.0-rc.2)(rxjs@7.8.2)(zone.js@0.15.1) + specifier: 21.1.0-next.4 + version: 21.1.0-next.4(@angular/compiler@21.1.0-next.4)(rxjs@7.8.2)(zone.js@0.16.0) '@angular/forms': - specifier: 21.0.0-rc.2 - version: 21.0.0-rc.2(@angular/common@21.0.0-rc.2(@angular/core@21.0.0-rc.2(@angular/compiler@21.0.0-rc.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@21.0.0-rc.2(@angular/compiler@21.0.0-rc.2)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@21.0.0-rc.2(@angular/animations@21.0.0-rc.2(@angular/core@21.0.0-rc.2(@angular/compiler@21.0.0-rc.2)(rxjs@7.8.2)(zone.js@0.15.1)))(@angular/common@21.0.0-rc.2(@angular/core@21.0.0-rc.2(@angular/compiler@21.0.0-rc.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@21.0.0-rc.2(@angular/compiler@21.0.0-rc.2)(rxjs@7.8.2)(zone.js@0.15.1)))(@standard-schema/spec@1.0.0)(rxjs@7.8.2) + specifier: 21.1.0-next.4 + version: 21.1.0-next.4(@angular/common@21.1.0-next.4(@angular/core@21.1.0-next.4(@angular/compiler@21.1.0-next.4)(rxjs@7.8.2)(zone.js@0.16.0))(rxjs@7.8.2))(@angular/core@21.1.0-next.4(@angular/compiler@21.1.0-next.4)(rxjs@7.8.2)(zone.js@0.16.0))(@angular/platform-browser@21.1.0-next.4(@angular/animations@21.1.0-next.4(@angular/core@21.1.0-next.4(@angular/compiler@21.1.0-next.4)(rxjs@7.8.2)(zone.js@0.16.0)))(@angular/common@21.1.0-next.4(@angular/core@21.1.0-next.4(@angular/compiler@21.1.0-next.4)(rxjs@7.8.2)(zone.js@0.16.0))(rxjs@7.8.2))(@angular/core@21.1.0-next.4(@angular/compiler@21.1.0-next.4)(rxjs@7.8.2)(zone.js@0.16.0)))(rxjs@7.8.2) '@angular/localize': - specifier: 21.0.0-rc.2 - version: 21.0.0-rc.2(@angular/compiler-cli@21.0.0-rc.2(@angular/compiler@21.0.0-rc.2)(typescript@5.9.3))(@angular/compiler@21.0.0-rc.2) + specifier: 21.1.0-next.4 + version: 21.1.0-next.4(@angular/compiler-cli@21.1.0-next.4(@angular/compiler@21.1.0-next.4)(typescript@5.9.3))(@angular/compiler@21.1.0-next.4) '@angular/material': - specifier: 21.0.0-rc.2 - version: 21.0.0-rc.2(4c9afee98155400dc203886c5442fc76) + specifier: 21.1.0-next.3 + version: 21.1.0-next.3(5911ac44acdb5e81564606f5886cc827) '@angular/ng-dev': - specifier: https://github.com/angular/dev-infra-private-ng-dev-builds.git#49d7316da246484759b94f87e6eda47fde86b992 - version: https://codeload.github.com/angular/dev-infra-private-ng-dev-builds/tar.gz/49d7316da246484759b94f87e6eda47fde86b992(@modelcontextprotocol/sdk@1.21.1) + specifier: https://github.com/angular/dev-infra-private-ng-dev-builds.git#ddc3809c1993612732eaae62d28e828b2ed789e5 + version: https://codeload.github.com/angular/dev-infra-private-ng-dev-builds/tar.gz/ddc3809c1993612732eaae62d28e828b2ed789e5(@modelcontextprotocol/sdk@1.25.0(zod@4.2.1)) '@angular/platform-browser': - specifier: 21.0.0-rc.2 - version: 21.0.0-rc.2(@angular/animations@21.0.0-rc.2(@angular/core@21.0.0-rc.2(@angular/compiler@21.0.0-rc.2)(rxjs@7.8.2)(zone.js@0.15.1)))(@angular/common@21.0.0-rc.2(@angular/core@21.0.0-rc.2(@angular/compiler@21.0.0-rc.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@21.0.0-rc.2(@angular/compiler@21.0.0-rc.2)(rxjs@7.8.2)(zone.js@0.15.1)) + specifier: 21.1.0-next.4 + version: 21.1.0-next.4(@angular/animations@21.1.0-next.4(@angular/core@21.1.0-next.4(@angular/compiler@21.1.0-next.4)(rxjs@7.8.2)(zone.js@0.16.0)))(@angular/common@21.1.0-next.4(@angular/core@21.1.0-next.4(@angular/compiler@21.1.0-next.4)(rxjs@7.8.2)(zone.js@0.16.0))(rxjs@7.8.2))(@angular/core@21.1.0-next.4(@angular/compiler@21.1.0-next.4)(rxjs@7.8.2)(zone.js@0.16.0)) '@angular/platform-server': - specifier: 21.0.0-rc.2 - version: 21.0.0-rc.2(@angular/common@21.0.0-rc.2(@angular/core@21.0.0-rc.2(@angular/compiler@21.0.0-rc.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/compiler@21.0.0-rc.2)(@angular/core@21.0.0-rc.2(@angular/compiler@21.0.0-rc.2)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@21.0.0-rc.2(@angular/animations@21.0.0-rc.2(@angular/core@21.0.0-rc.2(@angular/compiler@21.0.0-rc.2)(rxjs@7.8.2)(zone.js@0.15.1)))(@angular/common@21.0.0-rc.2(@angular/core@21.0.0-rc.2(@angular/compiler@21.0.0-rc.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@21.0.0-rc.2(@angular/compiler@21.0.0-rc.2)(rxjs@7.8.2)(zone.js@0.15.1)))(rxjs@7.8.2) + specifier: 21.1.0-next.4 + version: 21.1.0-next.4(@angular/common@21.1.0-next.4(@angular/core@21.1.0-next.4(@angular/compiler@21.1.0-next.4)(rxjs@7.8.2)(zone.js@0.16.0))(rxjs@7.8.2))(@angular/compiler@21.1.0-next.4)(@angular/core@21.1.0-next.4(@angular/compiler@21.1.0-next.4)(rxjs@7.8.2)(zone.js@0.16.0))(@angular/platform-browser@21.1.0-next.4(@angular/animations@21.1.0-next.4(@angular/core@21.1.0-next.4(@angular/compiler@21.1.0-next.4)(rxjs@7.8.2)(zone.js@0.16.0)))(@angular/common@21.1.0-next.4(@angular/core@21.1.0-next.4(@angular/compiler@21.1.0-next.4)(rxjs@7.8.2)(zone.js@0.16.0))(rxjs@7.8.2))(@angular/core@21.1.0-next.4(@angular/compiler@21.1.0-next.4)(rxjs@7.8.2)(zone.js@0.16.0)))(rxjs@7.8.2) '@angular/router': - specifier: 21.0.0-rc.2 - version: 21.0.0-rc.2(@angular/common@21.0.0-rc.2(@angular/core@21.0.0-rc.2(@angular/compiler@21.0.0-rc.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@21.0.0-rc.2(@angular/compiler@21.0.0-rc.2)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@21.0.0-rc.2(@angular/animations@21.0.0-rc.2(@angular/core@21.0.0-rc.2(@angular/compiler@21.0.0-rc.2)(rxjs@7.8.2)(zone.js@0.15.1)))(@angular/common@21.0.0-rc.2(@angular/core@21.0.0-rc.2(@angular/compiler@21.0.0-rc.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@21.0.0-rc.2(@angular/compiler@21.0.0-rc.2)(rxjs@7.8.2)(zone.js@0.15.1)))(rxjs@7.8.2) + specifier: 21.1.0-next.4 + version: 21.1.0-next.4(@angular/common@21.1.0-next.4(@angular/core@21.1.0-next.4(@angular/compiler@21.1.0-next.4)(rxjs@7.8.2)(zone.js@0.16.0))(rxjs@7.8.2))(@angular/core@21.1.0-next.4(@angular/compiler@21.1.0-next.4)(rxjs@7.8.2)(zone.js@0.16.0))(@angular/platform-browser@21.1.0-next.4(@angular/animations@21.1.0-next.4(@angular/core@21.1.0-next.4(@angular/compiler@21.1.0-next.4)(rxjs@7.8.2)(zone.js@0.16.0)))(@angular/common@21.1.0-next.4(@angular/core@21.1.0-next.4(@angular/compiler@21.1.0-next.4)(rxjs@7.8.2)(zone.js@0.16.0))(rxjs@7.8.2))(@angular/core@21.1.0-next.4(@angular/compiler@21.1.0-next.4)(rxjs@7.8.2)(zone.js@0.16.0)))(rxjs@7.8.2) '@angular/service-worker': - specifier: 21.0.0-rc.2 - version: 21.0.0-rc.2(@angular/core@21.0.0-rc.2(@angular/compiler@21.0.0-rc.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2) + specifier: 21.1.0-next.4 + version: 21.1.0-next.4(@angular/core@21.1.0-next.4(@angular/compiler@21.1.0-next.4)(rxjs@7.8.2)(zone.js@0.16.0))(rxjs@7.8.2) '@babel/core': specifier: 7.28.5 version: 7.28.5 @@ -71,29 +71,29 @@ importers: specifier: 8.2.1 version: 8.2.1 '@eslint/compat': - specifier: 1.4.1 - version: 1.4.1(eslint@9.39.1(jiti@2.6.1)) + specifier: 2.0.0 + version: 2.0.0(eslint@9.39.2(jiti@2.6.1)) '@eslint/eslintrc': - specifier: 3.3.1 - version: 3.3.1 + specifier: 3.3.3 + version: 3.3.3 '@eslint/js': - specifier: 9.39.1 - version: 9.39.1 + specifier: 9.39.2 + version: 9.39.2 '@rollup/plugin-alias': specifier: ^6.0.0 - version: 6.0.0(rollup@4.53.2) + version: 6.0.0(rollup@4.53.5) '@rollup/plugin-commonjs': specifier: ^29.0.0 - version: 29.0.0(rollup@4.53.2) + version: 29.0.0(rollup@4.53.5) '@rollup/plugin-json': specifier: ^6.1.0 - version: 6.1.0(rollup@4.53.2) + version: 6.1.0(rollup@4.53.5) '@rollup/plugin-node-resolve': specifier: 16.0.3 - version: 16.0.3(rollup@4.53.2) + version: 16.0.3(rollup@4.53.5) '@stylistic/eslint-plugin': specifier: ^5.0.0 - version: 5.5.0(eslint@9.39.1(jiti@2.6.1)) + version: 5.6.1(eslint@9.39.2(jiti@2.6.1)) '@types/babel__core': specifier: 7.20.5 version: 7.20.5 @@ -105,7 +105,7 @@ importers: version: 2.29.1 '@types/express': specifier: ~5.0.1 - version: 5.0.5 + version: 5.0.6 '@types/http-proxy': specifier: ^1.17.4 version: 1.17.17 @@ -114,7 +114,7 @@ importers: 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 @@ -126,13 +126,13 @@ importers: version: 3.0.8 '@types/loader-utils': specifier: ^3.0.0 - version: 3.0.0(esbuild@0.27.0) + version: 3.0.0(esbuild@0.27.1) '@types/lodash': specifier: ^4.17.0 - version: 4.17.20 + version: 4.17.21 '@types/node': specifier: ^22.12.0 - version: 22.19.0 + version: 22.19.3 '@types/npm-package-arg': specifier: ^6.1.0 version: 6.1.4 @@ -153,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.34 + version: 17.0.35 '@types/yargs-parser': specifier: ^21.0.0 version: 21.0.3 @@ -164,41 +164,38 @@ importers: specifier: ^1.1.5 version: 1.1.9 '@typescript-eslint/eslint-plugin': - specifier: 8.46.4 - version: 8.46.4(@typescript-eslint/parser@8.46.4(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3) + specifier: 8.50.0 + version: 8.50.0(@typescript-eslint/parser@8.50.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) '@typescript-eslint/parser': - specifier: 8.46.4 - version: 8.46.4(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3) + specifier: 8.50.0 + version: 8.50.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) ajv: specifier: 8.17.1 version: 8.17.1 - ansi-colors: - specifier: 4.1.3 - version: 4.1.3 buffer: specifier: 6.0.3 version: 6.0.3 esbuild: - specifier: 0.27.0 - version: 0.27.0 + specifier: 0.27.1 + version: 0.27.1 esbuild-wasm: - specifier: 0.27.0 - version: 0.27.0 + specifier: 0.27.1 + version: 0.27.1 eslint: - specifier: 9.39.1 - version: 9.39.1(jiti@2.6.1) + specifier: 9.39.2 + version: 9.39.2(jiti@2.6.1) eslint-config-prettier: specifier: 10.1.8 - version: 10.1.8(eslint@9.39.1(jiti@2.6.1)) + version: 10.1.8(eslint@9.39.2(jiti@2.6.1)) eslint-plugin-header: specifier: 3.1.1 - version: 3.1.1(eslint@9.39.1(jiti@2.6.1)) + version: 3.1.1(eslint@9.39.2(jiti@2.6.1)) eslint-plugin-import: specifier: 2.32.0 - version: 2.32.0(@typescript-eslint/parser@8.46.4(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.1(jiti@2.6.1)) + version: 2.32.0(@typescript-eslint/parser@8.50.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1)) express: - specifier: 5.1.0 - version: 5.1.0 + specifier: 5.2.1 + version: 5.2.1 fast-glob: specifier: 3.3.3 version: 3.3.3 @@ -215,11 +212,11 @@ importers: specifier: 9.1.7 version: 9.1.7 jasmine: - specifier: ~5.12.0 - version: 5.12.0 + specifier: ~5.13.0 + version: 5.13.0 jasmine-core: - specifier: ~5.12.0 - version: 5.12.1 + specifier: ~5.13.0 + version: 5.13.0 jasmine-reporters: specifier: ^2.5.2 version: 2.5.2 @@ -240,13 +237,10 @@ 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.1)(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.13.0)(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 - listr2: - specifier: 9.0.5 - version: 9.0.5 lodash: specifier: ^4.17.21 version: 4.17.21 @@ -255,7 +249,7 @@ importers: version: 0.30.21 prettier: specifier: ^3.0.0 - version: 3.6.2 + version: 3.7.4 protractor: specifier: ~7.0.0 version: 7.0.0 @@ -266,29 +260,26 @@ importers: specifier: 23.2.6 version: 23.2.6(encoding@0.1.13) rollup: - specifier: 4.53.2 - version: 4.53.2 + specifier: 4.53.5 + version: 4.53.5 rollup-license-plugin: specifier: ~3.1.0 version: 3.1.0 rollup-plugin-dts: - specifier: 6.2.3 - version: 6.2.3(rollup@4.53.2)(typescript@5.9.3) + specifier: 6.3.0 + version: 6.3.0(rollup@4.53.5)(typescript@5.9.3) rollup-plugin-sourcemaps2: specifier: 0.5.4 - version: 0.5.4(@types/node@22.19.0)(rollup@4.53.2) + version: 0.5.4(@types/node@22.19.3)(rollup@4.53.5) semver: specifier: 7.7.3 version: 7.7.3 source-map-support: specifier: 0.5.21 version: 0.5.21 - tar: - specifier: ^7.0.0 - version: 7.5.2 ts-node: specifier: ^10.9.1 - version: 10.9.2(@types/node@22.19.0)(typescript@5.9.3) + version: 10.9.2(@types/node@22.19.3)(typescript@5.9.3) tslib: specifier: 2.8.1 version: 2.8.1 @@ -302,20 +293,14 @@ importers: specifier: ^1.10.0 version: 1.10.0 verdaccio: - specifier: 6.2.1 - version: 6.2.1(encoding@0.1.13) + specifier: 6.2.4 + version: 6.2.4(encoding@0.1.13) verdaccio-auth-memory: specifier: ^10.0.0 version: 10.3.1 - yargs-parser: - specifier: 22.0.0 - version: 22.0.0 - zod: - specifier: 4.1.12 - version: 4.1.12 zone.js: - specifier: ^0.15.0 - version: 0.15.1 + specifier: ^0.16.0 + version: 0.16.0 modules/testing/builder: devDependencies: @@ -332,20 +317,20 @@ importers: specifier: workspace:* version: link:../../../packages/angular/ssr '@vitest/coverage-v8': - specifier: 4.0.8 - version: 4.0.8(vitest@4.0.8(@types/node@24.10.0)(jiti@2.6.1)(jsdom@27.2.0(bufferutil@4.0.9)(utf-8-validate@6.0.5))(less@4.4.2)(sass@1.94.0)(terser@5.44.1)(tsx@4.20.6)(yaml@2.8.1)) + specifier: 4.0.15 + version: 4.0.15(vitest@4.0.15(@opentelemetry/api@1.9.0)(@types/node@24.10.4)(jiti@2.6.1)(jsdom@27.3.0(bufferutil@4.0.9)(utf-8-validate@6.0.5))(less@4.4.2)(sass@1.97.0)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)) browser-sync: specifier: 3.0.4 version: 3.0.4(bufferutil@4.0.9)(utf-8-validate@6.0.5) jsdom: - specifier: 27.2.0 - version: 27.2.0(bufferutil@4.0.9)(utf-8-validate@6.0.5) + specifier: 27.3.0 + version: 27.3.0(bufferutil@4.0.9)(utf-8-validate@6.0.5) rxjs: specifier: 7.8.2 version: 7.8.2 vitest: - specifier: 4.0.8 - version: 4.0.8(@types/node@24.10.0)(jiti@2.6.1)(jsdom@27.2.0(bufferutil@4.0.9)(utf-8-validate@6.0.5))(less@4.4.2)(sass@1.94.0)(terser@5.44.1)(tsx@4.20.6)(yaml@2.8.1) + specifier: 4.0.15 + version: 4.0.15(@opentelemetry/api@1.9.0)(@types/node@24.10.4)(jiti@2.6.1)(jsdom@27.3.0(bufferutil@4.0.9)(utf-8-validate@6.0.5))(less@4.4.2)(sass@1.97.0)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2) packages/angular/build: dependencies: @@ -366,19 +351,19 @@ importers: version: 7.24.7 '@inquirer/confirm': specifier: 5.1.21 - version: 5.1.21(@types/node@24.10.0) + version: 5.1.21(@types/node@24.10.4) '@vitejs/plugin-basic-ssl': specifier: 2.1.0 - version: 2.1.0(vite@7.2.2(@types/node@24.10.0)(jiti@2.6.1)(less@4.4.2)(sass@1.94.0)(terser@5.44.1)(tsx@4.20.6)(yaml@2.8.1)) + version: 2.1.0(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(less@4.4.2)(sass@1.97.0)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)) beasties: specifier: 0.3.5 version: 0.3.5 browserslist: specifier: ^4.26.0 - version: 4.27.0 + version: 4.28.1 esbuild: - specifier: 0.27.0 - version: 0.27.0 + specifier: 0.27.1 + version: 0.27.1 https-proxy-agent: specifier: 7.0.6 version: 7.0.6(supports-color@10.2.2) @@ -407,11 +392,11 @@ importers: specifier: 5.1.4 version: 5.1.4 rolldown: - specifier: 1.0.0-beta.50 - version: 1.0.0-beta.50 + specifier: 1.0.0-beta.54 + version: 1.0.0-beta.54 sass: - specifier: 1.94.0 - version: 1.94.0 + specifier: 1.97.0 + version: 1.97.0 semver: specifier: 7.7.3 version: 7.7.3 @@ -425,8 +410,8 @@ importers: specifier: 7.16.0 version: 7.16.0 vite: - specifier: 7.2.2 - version: 7.2.2(@types/node@24.10.0)(jiti@2.6.1)(less@4.4.2)(sass@1.94.0)(terser@5.44.1)(tsx@4.20.6)(yaml@2.8.1) + specifier: 7.3.0 + version: 7.3.0(@types/node@24.10.4)(jiti@2.6.1)(less@4.4.2)(sass@1.97.0)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2) watchpack: specifier: 2.4.4 version: 2.4.4 @@ -438,14 +423,14 @@ importers: specifier: workspace:* version: link:../ssr jsdom: - specifier: 27.2.0 - version: 27.2.0(bufferutil@4.0.9)(utf-8-validate@6.0.5) + specifier: 27.3.0 + version: 27.3.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-rc.1 - version: 21.0.0-rc.1(@angular/compiler-cli@21.0.0-rc.2(@angular/compiler@21.0.0-rc.2)(typescript@5.9.3))(tslib@2.8.1)(typescript@5.9.3) + specifier: 21.1.0-next.0 + version: 21.1.0-next.0(@angular/compiler-cli@21.1.0-next.4(@angular/compiler@21.1.0-next.4)(typescript@5.9.3))(tslib@2.8.1)(typescript@5.9.3) postcss: specifier: 8.5.6 version: 8.5.6 @@ -453,8 +438,8 @@ importers: specifier: 7.8.2 version: 7.8.2 vitest: - specifier: 4.0.8 - version: 4.0.8(@types/node@24.10.0)(jiti@2.6.1)(jsdom@27.2.0(bufferutil@4.0.9)(utf-8-validate@6.0.5))(less@4.4.2)(sass@1.94.0)(terser@5.44.1)(tsx@4.20.6)(yaml@2.8.1) + specifier: 4.0.15 + version: 4.0.15(@opentelemetry/api@1.9.0)(@types/node@24.10.4)(jiti@2.6.1)(jsdom@27.3.0(bufferutil@4.0.9)(utf-8-validate@6.0.5))(less@4.4.2)(sass@1.97.0)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2) optionalDependencies: lmdb: specifier: 3.4.4 @@ -473,13 +458,13 @@ importers: version: link:../../angular_devkit/schematics '@inquirer/prompts': specifier: 7.10.1 - version: 7.10.1(@types/node@24.10.0) + version: 7.10.1(@types/node@24.10.4) '@listr2/prompt-adapter-inquirer': specifier: 3.0.5 - version: 3.0.5(@inquirer/prompts@7.10.1(@types/node@24.10.0))(@types/node@24.10.0)(listr2@9.0.5) + version: 3.0.5(@inquirer/prompts@7.10.1(@types/node@24.10.4))(@types/node@24.10.4)(listr2@9.0.5) '@modelcontextprotocol/sdk': - specifier: 1.21.1 - version: 1.21.1 + specifier: 1.25.0 + version: 1.25.0(zod@4.2.1) '@schematics/angular': specifier: workspace:0.0.0-PLACEHOLDER version: link:../../schematics/angular @@ -487,8 +472,8 @@ importers: specifier: 1.1.0 version: 1.1.0 algoliasearch: - specifier: 5.43.0 - version: 5.43.0 + specifier: 5.46.0 + version: 5.46.0 ini: specifier: 6.0.0 version: 6.0.0 @@ -499,11 +484,11 @@ importers: specifier: 9.0.5 version: 9.0.5 npm-package-arg: - specifier: 13.0.1 - version: 13.0.1 + specifier: 13.0.2 + version: 13.0.2 pacote: - specifier: 21.0.3 - version: 21.0.3 + specifier: 21.0.4 + version: 21.0.4 parse5-html-rewriting-stream: specifier: 8.0.0 version: 8.0.0 @@ -517,8 +502,8 @@ importers: specifier: 18.0.0 version: 18.0.0 zod: - specifier: 3.25.76 - version: 3.25.76 + specifier: 4.2.1 + version: 4.2.1 packages/angular/pwa: dependencies: @@ -542,23 +527,23 @@ importers: specifier: workspace:* version: link:../../angular_devkit/schematics '@angular/common': - specifier: 21.0.0-rc.2 - version: 21.0.0-rc.2(@angular/core@21.0.0-rc.2(@angular/compiler@21.0.0-rc.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2) + specifier: 21.1.0-next.4 + version: 21.1.0-next.4(@angular/core@21.1.0-next.4(@angular/compiler@21.1.0-next.4)(rxjs@7.8.2)(zone.js@0.16.0))(rxjs@7.8.2) '@angular/compiler': - specifier: 21.0.0-rc.2 - version: 21.0.0-rc.2 + specifier: 21.1.0-next.4 + version: 21.1.0-next.4 '@angular/core': - specifier: 21.0.0-rc.2 - version: 21.0.0-rc.2(@angular/compiler@21.0.0-rc.2)(rxjs@7.8.2)(zone.js@0.15.1) + specifier: 21.1.0-next.4 + version: 21.1.0-next.4(@angular/compiler@21.1.0-next.4)(rxjs@7.8.2)(zone.js@0.16.0) '@angular/platform-browser': - specifier: 21.0.0-rc.2 - version: 21.0.0-rc.2(@angular/animations@21.0.0-rc.2(@angular/core@21.0.0-rc.2(@angular/compiler@21.0.0-rc.2)(rxjs@7.8.2)(zone.js@0.15.1)))(@angular/common@21.0.0-rc.2(@angular/core@21.0.0-rc.2(@angular/compiler@21.0.0-rc.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@21.0.0-rc.2(@angular/compiler@21.0.0-rc.2)(rxjs@7.8.2)(zone.js@0.15.1)) + specifier: 21.1.0-next.4 + version: 21.1.0-next.4(@angular/animations@21.1.0-next.4(@angular/core@21.1.0-next.4(@angular/compiler@21.1.0-next.4)(rxjs@7.8.2)(zone.js@0.16.0)))(@angular/common@21.1.0-next.4(@angular/core@21.1.0-next.4(@angular/compiler@21.1.0-next.4)(rxjs@7.8.2)(zone.js@0.16.0))(rxjs@7.8.2))(@angular/core@21.1.0-next.4(@angular/compiler@21.1.0-next.4)(rxjs@7.8.2)(zone.js@0.16.0)) '@angular/platform-server': - specifier: 21.0.0-rc.2 - version: 21.0.0-rc.2(@angular/common@21.0.0-rc.2(@angular/core@21.0.0-rc.2(@angular/compiler@21.0.0-rc.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/compiler@21.0.0-rc.2)(@angular/core@21.0.0-rc.2(@angular/compiler@21.0.0-rc.2)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@21.0.0-rc.2(@angular/animations@21.0.0-rc.2(@angular/core@21.0.0-rc.2(@angular/compiler@21.0.0-rc.2)(rxjs@7.8.2)(zone.js@0.15.1)))(@angular/common@21.0.0-rc.2(@angular/core@21.0.0-rc.2(@angular/compiler@21.0.0-rc.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@21.0.0-rc.2(@angular/compiler@21.0.0-rc.2)(rxjs@7.8.2)(zone.js@0.15.1)))(rxjs@7.8.2) + specifier: 21.1.0-next.4 + version: 21.1.0-next.4(@angular/common@21.1.0-next.4(@angular/core@21.1.0-next.4(@angular/compiler@21.1.0-next.4)(rxjs@7.8.2)(zone.js@0.16.0))(rxjs@7.8.2))(@angular/compiler@21.1.0-next.4)(@angular/core@21.1.0-next.4(@angular/compiler@21.1.0-next.4)(rxjs@7.8.2)(zone.js@0.16.0))(@angular/platform-browser@21.1.0-next.4(@angular/animations@21.1.0-next.4(@angular/core@21.1.0-next.4(@angular/compiler@21.1.0-next.4)(rxjs@7.8.2)(zone.js@0.16.0)))(@angular/common@21.1.0-next.4(@angular/core@21.1.0-next.4(@angular/compiler@21.1.0-next.4)(rxjs@7.8.2)(zone.js@0.16.0))(rxjs@7.8.2))(@angular/core@21.1.0-next.4(@angular/compiler@21.1.0-next.4)(rxjs@7.8.2)(zone.js@0.16.0)))(rxjs@7.8.2) '@angular/router': - specifier: 21.0.0-rc.2 - version: 21.0.0-rc.2(@angular/common@21.0.0-rc.2(@angular/core@21.0.0-rc.2(@angular/compiler@21.0.0-rc.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@21.0.0-rc.2(@angular/compiler@21.0.0-rc.2)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@21.0.0-rc.2(@angular/animations@21.0.0-rc.2(@angular/core@21.0.0-rc.2(@angular/compiler@21.0.0-rc.2)(rxjs@7.8.2)(zone.js@0.15.1)))(@angular/common@21.0.0-rc.2(@angular/core@21.0.0-rc.2(@angular/compiler@21.0.0-rc.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@21.0.0-rc.2(@angular/compiler@21.0.0-rc.2)(rxjs@7.8.2)(zone.js@0.15.1)))(rxjs@7.8.2) + specifier: 21.1.0-next.4 + version: 21.1.0-next.4(@angular/common@21.1.0-next.4(@angular/core@21.1.0-next.4(@angular/compiler@21.1.0-next.4)(rxjs@7.8.2)(zone.js@0.16.0))(rxjs@7.8.2))(@angular/core@21.1.0-next.4(@angular/compiler@21.1.0-next.4)(rxjs@7.8.2)(zone.js@0.16.0))(@angular/platform-browser@21.1.0-next.4(@angular/animations@21.1.0-next.4(@angular/core@21.1.0-next.4(@angular/compiler@21.1.0-next.4)(rxjs@7.8.2)(zone.js@0.16.0)))(@angular/common@21.1.0-next.4(@angular/core@21.1.0-next.4(@angular/compiler@21.1.0-next.4)(rxjs@7.8.2)(zone.js@0.16.0))(rxjs@7.8.2))(@angular/core@21.1.0-next.4(@angular/compiler@21.1.0-next.4)(rxjs@7.8.2)(zone.js@0.16.0)))(rxjs@7.8.2) '@schematics/angular': specifier: workspace:* version: link:../../schematics/angular @@ -580,22 +565,6 @@ importers: '@angular-devkit/architect': specifier: workspace:0.0.0-EXPERIMENTAL-PLACEHOLDER version: link:../architect - '@angular-devkit/core': - specifier: workspace:0.0.0-PLACEHOLDER - version: link:../core - ansi-colors: - specifier: 4.1.3 - version: 4.1.3 - progress: - specifier: 2.0.3 - version: 2.0.3 - yargs-parser: - specifier: 22.0.0 - version: 22.0.0 - devDependencies: - '@types/progress': - specifier: 2.0.7 - version: 2.0.7 packages/angular_devkit/build_angular: dependencies: @@ -651,23 +620,23 @@ importers: specifier: 4.1.3 version: 4.1.3 autoprefixer: - specifier: 10.4.22 - version: 10.4.22(postcss@8.5.6) + specifier: 10.4.23 + version: 10.4.23(postcss@8.5.6) babel-loader: specifier: 10.0.0 - version: 10.0.0(@babel/core@7.28.5)(webpack@5.102.1(esbuild@0.27.0)) + version: 10.0.0(@babel/core@7.28.5)(webpack@5.104.0(esbuild@0.27.1)) browserslist: specifier: ^4.26.0 - version: 4.27.0 + version: 4.28.1 copy-webpack-plugin: specifier: 13.0.1 - version: 13.0.1(webpack@5.102.1(esbuild@0.27.0)) + version: 13.0.1(webpack@5.104.0(esbuild@0.27.1)) css-loader: specifier: 7.1.2 - version: 7.1.2(webpack@5.102.1(esbuild@0.27.0)) + version: 7.1.2(webpack@5.104.0(esbuild@0.27.1)) esbuild-wasm: - specifier: 0.27.0 - version: 0.27.0 + specifier: 0.27.1 + version: 0.27.1 http-proxy-middleware: specifier: 3.0.5 version: 3.0.5 @@ -685,19 +654,19 @@ 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.27.0)) + version: 12.3.0(less@4.4.2)(webpack@5.104.0(esbuild@0.27.1)) license-webpack-plugin: specifier: 4.0.2 - version: 4.0.2(webpack@5.102.1(esbuild@0.27.0)) + version: 4.0.2(webpack@5.104.0(esbuild@0.27.1)) 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.27.0)) + version: 2.9.4(webpack@5.104.0(esbuild@0.27.1)) open: - specifier: 10.2.0 - version: 10.2.0 + specifier: 11.0.0 + version: 11.0.0 ora: specifier: 9.0.0 version: 9.0.0 @@ -712,7 +681,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.27.0)) + version: 8.2.0(postcss@8.5.6)(typescript@5.9.3)(webpack@5.104.0(esbuild@0.27.1)) resolve-url-loader: specifier: 5.0.0 version: 5.0.0 @@ -720,17 +689,17 @@ importers: specifier: 7.8.2 version: 7.8.2 sass: - specifier: 1.94.0 - version: 1.94.0 + specifier: 1.97.0 + version: 1.97.0 sass-loader: specifier: 16.0.6 - version: 16.0.6(sass@1.94.0)(webpack@5.102.1(esbuild@0.27.0)) + version: 16.0.6(sass@1.97.0)(webpack@5.104.0(esbuild@0.27.1)) 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.27.0)) + version: 5.0.0(webpack@5.104.0(esbuild@0.27.1)) source-map-support: specifier: 0.5.21 version: 0.5.21 @@ -747,20 +716,20 @@ importers: specifier: 2.8.1 version: 2.8.1 webpack: - specifier: 5.102.1 - version: 5.102.1(esbuild@0.27.0) + specifier: 5.104.0 + version: 5.104.0(esbuild@0.27.1) webpack-dev-middleware: specifier: 7.4.5 - version: 7.4.5(webpack@5.102.1(esbuild@0.27.0)) + version: 7.4.5(webpack@5.104.0(esbuild@0.27.1)) 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.27.0)) + version: 5.2.2(bufferutil@4.0.9)(utf-8-validate@6.0.5)(webpack@5.104.0(esbuild@0.27.1)) 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.27.0)) + version: 5.1.0(webpack@5.104.0(esbuild@0.27.1)) devDependencies: '@angular/ssr': specifier: workspace:* @@ -772,15 +741,15 @@ importers: specifier: 3.0.4 version: 3.0.4(bufferutil@4.0.9)(utf-8-validate@6.0.5) ng-packagr: - specifier: 21.0.0-rc.1 - version: 21.0.0-rc.1(@angular/compiler-cli@21.0.0-rc.2(@angular/compiler@21.0.0-rc.2)(typescript@5.9.3))(tslib@2.8.1)(typescript@5.9.3) + specifier: 21.1.0-next.0 + version: 21.1.0-next.0(@angular/compiler-cli@21.1.0-next.4(@angular/compiler@21.1.0-next.4)(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.27.0 - version: 0.27.0 + specifier: 0.27.1 + version: 0.27.1 packages/angular_devkit/build_webpack: dependencies: @@ -798,11 +767,11 @@ importers: specifier: workspace:0.0.0-PLACEHOLDER version: link:../../ngtools/webpack webpack: - specifier: 5.102.1 - version: 5.102.1(esbuild@0.27.0) + specifier: 5.104.0 + version: 5.104.0(esbuild@0.27.1) 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.27.0)) + version: 5.2.2(bufferutil@4.0.9)(utf-8-validate@6.0.5)(webpack@5.104.0(esbuild@0.27.1)) packages/angular_devkit/core: dependencies: @@ -826,8 +795,8 @@ importers: version: 0.7.6 devDependencies: chokidar: - specifier: 4.0.3 - version: 4.0.3 + specifier: 5.0.0 + version: 5.0.0 packages/angular_devkit/schematics: dependencies: @@ -857,13 +826,7 @@ importers: version: link:../schematics '@inquirer/prompts': specifier: 7.10.1 - version: 7.10.1(@types/node@24.10.0) - ansi-colors: - specifier: 4.1.3 - version: 4.1.3 - yargs-parser: - specifier: 22.0.0 - version: 22.0.0 + version: 7.10.1(@types/node@24.10.4) packages/ngtools/webpack: devDependencies: @@ -871,17 +834,17 @@ importers: specifier: workspace:0.0.0-PLACEHOLDER version: link:../../angular_devkit/core '@angular/compiler': - specifier: 21.0.0-rc.2 - version: 21.0.0-rc.2 + specifier: 21.1.0-next.4 + version: 21.1.0-next.4 '@angular/compiler-cli': - specifier: 21.0.0-rc.2 - version: 21.0.0-rc.2(@angular/compiler@21.0.0-rc.2)(typescript@5.9.3) + specifier: 21.1.0-next.4 + version: 21.1.0-next.4(@angular/compiler@21.1.0-next.4)(typescript@5.9.3) typescript: specifier: 5.9.3 version: 5.9.3 webpack: - specifier: 5.102.1 - version: 5.102.1(esbuild@0.27.0) + specifier: 5.104.0 + version: 5.104.0(esbuild@0.27.1) packages/schematics/angular: dependencies: @@ -900,214 +863,217 @@ importers: '@angular-devkit/schematics': specifier: workspace:* version: link:../packages/angular_devkit/schematics - rxjs: - specifier: 7.8.2 - version: 7.8.2 + '@types/tar-stream': + specifier: 3.1.4 + version: 3.1.4 + tar-stream: + specifier: 3.1.7 + version: 3.1.7 tree-kill: specifier: 1.2.2 version: 1.2.2 packages: - '@acemir/cssom@0.9.23': - resolution: {integrity: sha512-2kJ1HxBKzPLbmhZpxBiTZggjtgCwKg1ma5RHShxvd6zgqhDEdEkzpiwe7jLkI2p2BrZvFCXIihdoMkl1H39VnA==} + '@acemir/cssom@0.9.29': + resolution: {integrity: sha512-G90x0VW+9nW4dFajtjCoT+NM0scAfH9Mb08IcjgFHYbfiL/lU04dTF9JuVOi3/OH+DJCQdcIseSXkdCB9Ky6JA==} - '@actions/core@1.11.1': - resolution: {integrity: sha512-hXJCSrkwfA46Vd9Z3q4cpEpHB1rL5NG04+/rbqW9d3+CSvtB1tYe8UTpAlixa1vj0m/ULglfEK2UKxMGxCxv5A==} + '@actions/core@2.0.1': + resolution: {integrity: sha512-oBfqT3GwkvLlo1fjvhQLQxuwZCGTarTE5OuZ2Wg10hvhBj7LRIlF611WT4aZS6fDhO5ZKlY7lCAZTlpmyaHaeg==} - '@actions/exec@1.1.1': - resolution: {integrity: sha512-+sCcHHbVdk93a0XT19ECtO/gIXoxvdsgQLzb2fE2/5sIZmWQuluYyjPQtrtTHdU1YzTZ7bAPN4sITq2xi1679w==} + '@actions/exec@2.0.0': + resolution: {integrity: sha512-k8ngrX2voJ/RIN6r9xB82NVqKpnMRtxDoiO+g3olkIUpQNqjArXrCQceduQZCQj3P3xm32pChRLqRrtXTlqhIw==} - '@actions/http-client@2.2.3': - resolution: {integrity: sha512-mx8hyJi/hjFvbPokCg4uRd4ZX78t+YyRPtnKWwIl+RzNaVuFpQHfmlGVfsKEJN8LwTCvL+DfVgAM04XaHkm6bA==} + '@actions/http-client@3.0.0': + resolution: {integrity: sha512-1s3tXAfVMSz9a4ZEBkXXRQD4QhY3+GAsWSbaYpeknPOKEeyRiU3lH+bHiLMZdo2x/fIeQ/hscL1wCkDLVM2DZQ==} - '@actions/io@1.1.3': - resolution: {integrity: sha512-wi9JjgKLYS7U/z8PPbco+PvTb/nRWjeoFlJ1Qer83k/3C5PHQi28hiVdeE2kHXmIL99mQFawx8qt/JPjZilJ8Q==} + '@actions/io@2.0.0': + resolution: {integrity: sha512-Jv33IN09XLO+0HS79aaODsvIRyduiF7NY/F6LYeK5oeUmrsz7aFdRphQjFoESF4jS7lMauDOttKALcpapVDIAg==} - '@algolia/abtesting@1.9.0': - resolution: {integrity: sha512-4q9QCxFPiDIx1n5w41A1JMkrXI8p0ugCQnCGFtCKZPmWtwgWCqwVRncIbp++81xSELFZVQUfiB7Kbsla1tIBSw==} + '@algolia/abtesting@1.12.0': + resolution: {integrity: sha512-EfW0bfxjPs+C7ANkJDw2TATntfBKsFiy7APh+KO0pQ8A6HYa5I0NjFuCGCXWfzzzLXNZta3QUl3n5Kmm6aJo9Q==} engines: {node: '>= 14.0.0'} - '@algolia/client-abtesting@5.43.0': - resolution: {integrity: sha512-YsKYkohIMxiYEAu8nppZi5EioYDUIo9Heoor8K8vMUnkUtGCOEU/Q4p5OWaYSSBx3evo09Ga9rG4jsKViIcDzQ==} + '@algolia/client-abtesting@5.46.0': + resolution: {integrity: sha512-eG5xV8rujK4ZIHXrRshvv9O13NmU/k42Rnd3w43iKH5RaQ2zWuZO6Q7XjaoJjAFVCsJWqRbXzbYyPGrbF3wGNg==} engines: {node: '>= 14.0.0'} - '@algolia/client-analytics@5.43.0': - resolution: {integrity: sha512-kDGJWt3nzf0nu5RPFXQhNGl6Q0cn35fazxVWXhd0Fw3Vo6gcVfrcezcBenHb66laxnVJ7uwr1uKhmsu3Wy25sQ==} + '@algolia/client-analytics@5.46.0': + resolution: {integrity: sha512-AYh2uL8IUW9eZrbbT+wZElyb7QkkeV3US2NEKY7doqMlyPWE8lErNfkVN1NvZdVcY4/SVic5GDbeDz2ft8YIiQ==} engines: {node: '>= 14.0.0'} - '@algolia/client-common@5.43.0': - resolution: {integrity: sha512-RAFipkAnI8xhL/Sgi/gpXgNWN5HDM6F7z4NNNOcI8ZMYysZEBsqVXojg/WdKEKkQCOHVTZ3mooIjc5BaQdyVtA==} + '@algolia/client-common@5.46.0': + resolution: {integrity: sha512-0emZTaYOeI9WzJi0TcNd2k3SxiN6DZfdWc2x2gHt855Jl9jPUOzfVTL6gTvCCrOlT4McvpDGg5nGO+9doEjjig==} engines: {node: '>= 14.0.0'} - '@algolia/client-insights@5.43.0': - resolution: {integrity: sha512-PmVs83THco8Qig3cAjU9a5eAGaSxsfgh7PdmWMQFE/MCmIcLPv0MVpgfcGGyPjZGYvPC4cg+3q7JJxcNSsEaTg==} + '@algolia/client-insights@5.46.0': + resolution: {integrity: sha512-wrBJ8fE+M0TDG1As4DDmwPn2TXajrvmvAN72Qwpuv8e2JOKNohF7+JxBoF70ZLlvP1A1EiH8DBu+JpfhBbNphQ==} engines: {node: '>= 14.0.0'} - '@algolia/client-personalization@5.43.0': - resolution: {integrity: sha512-Bs4zMLXvkAr19FSOZWNizlNUpRFxZVxtvyEJ+q3n3+hPZUcKjo0LIh15qghhRcQPEihjBN6Gr/U+AqRfOCsvnA==} + '@algolia/client-personalization@5.46.0': + resolution: {integrity: sha512-LnkeX4p0ENt0DoftDJJDzQQJig/sFQmD1eQifl/iSjhUOGUIKC/7VTeXRcKtQB78naS8njUAwpzFvxy1CDDXDQ==} engines: {node: '>= 14.0.0'} - '@algolia/client-query-suggestions@5.43.0': - resolution: {integrity: sha512-pwHv+z8TZAKbwAWt9+v2gIqlqcCFiMdteTdgdPn2yOBRx4WUQdsIWAaG9GiV3by8jO51FuFQnTohhauuI63y3A==} + '@algolia/client-query-suggestions@5.46.0': + resolution: {integrity: sha512-aF9tc4ex/smypXw+W3lBPB1jjKoaGHpZezTqofvDOI/oK1dR2sdTpFpK2Ru+7IRzYgwtRqHF3znmTlyoNs9dpA==} engines: {node: '>= 14.0.0'} - '@algolia/client-search@5.43.0': - resolution: {integrity: sha512-wKy6x6fKcnB1CsfeNNdGp4dzLzz04k8II3JLt6Sp81F8s57Ks3/K9qsysmL9SJa8P486s719bBttVLE8JJYurQ==} + '@algolia/client-search@5.46.0': + resolution: {integrity: sha512-22SHEEVNjZfFWkFks3P6HilkR3rS7a6GjnCIqR22Zz4HNxdfT0FG+RE7efTcFVfLUkTTMQQybvaUcwMrHXYa7Q==} engines: {node: '>= 14.0.0'} - '@algolia/ingestion@1.43.0': - resolution: {integrity: sha512-TA21h2KwqCUyPXhSAWF3R2UES/FAnzjaVPDI6cRPXeadX+pdrGN0GWat5gSUATJVcMHECn+lGvuMMRxO86o2Pg==} + '@algolia/ingestion@1.46.0': + resolution: {integrity: sha512-2LT0/Z+/sFwEpZLH6V17WSZ81JX2uPjgvv5eNlxgU7rPyup4NXXfuMbtCJ+6uc4RO/LQpEJd3Li59ke3wtyAsA==} engines: {node: '>= 14.0.0'} - '@algolia/monitoring@1.43.0': - resolution: {integrity: sha512-rvWVEiA1iLcFmHS3oIXGIBreHIxNZqEFDjiNyRtLEffgd62kul2DjXM7H5bOouDMTo1ywMWT9OeQnzrhlTGAwA==} + '@algolia/monitoring@1.46.0': + resolution: {integrity: sha512-uivZ9wSWZ8mz2ZU0dgDvQwvVZV8XBv6lYBXf8UtkQF3u7WeTqBPeU8ZoeTyLpf0jAXCYOvc1mAVmK0xPLuEwOQ==} engines: {node: '>= 14.0.0'} - '@algolia/recommend@5.43.0': - resolution: {integrity: sha512-scCijGd38npvH2uHbYhO4f1SR8It5R2FZqOjNcMfw/7Ph7Hxvl+cd7Mo6RzIxsNRcLW5RrwjtpTK3gpDe8r/WQ==} + '@algolia/recommend@5.46.0': + resolution: {integrity: sha512-O2BB8DuySuddgOAbhyH4jsGbL+KyDGpzJRtkDZkv091OMomqIA78emhhMhX9d/nIRrzS1wNLWB/ix7Hb2eV5rg==} engines: {node: '>= 14.0.0'} - '@algolia/requester-browser-xhr@5.43.0': - resolution: {integrity: sha512-jMkRLWJYr4Hcmpl89e4vIWs69Mkf8Uwx7MG5ZKk2UxW3G3TmouGjI0Ph5mVPmg3Jf1UG3AdmVDc4XupzycT1Jw==} + '@algolia/requester-browser-xhr@5.46.0': + resolution: {integrity: sha512-eW6xyHCyYrJD0Kjk9Mz33gQ40LfWiEA51JJTVfJy3yeoRSw/NXhAL81Pljpa0qslTs6+LO/5DYPZddct6HvISQ==} engines: {node: '>= 14.0.0'} - '@algolia/requester-fetch@5.43.0': - resolution: {integrity: sha512-KyQiVz+HdYtissC0J9KIGhHhKytQyJX+82GVsbv5rSCXbETnAoojvUyCn+3KRtWUvMDYCsZ+Y7hM71STTUJUJg==} + '@algolia/requester-fetch@5.46.0': + resolution: {integrity: sha512-Vn2+TukMGHy4PIxmdvP667tN/MhS7MPT8EEvEhS6JyFLPx3weLcxSa1F9gVvrfHWCUJhLWoMVJVB2PT8YfRGcw==} engines: {node: '>= 14.0.0'} - '@algolia/requester-node-http@5.43.0': - resolution: {integrity: sha512-UnUBNY0U+oT0bkYDsEqVsCkErC2w7idk4CRiLSzicqY8tGylD9oP0j13X/fse1CuiAFCCr3jfl+cBlN6dC0OFw==} + '@algolia/requester-node-http@5.46.0': + resolution: {integrity: sha512-xaqXyna5yBZ+r1SJ9my/DM6vfTqJg9FJgVydRJ0lnO+D5NhqGW/qaRG/iBGKr/d4fho34el6WakV7BqJvrl/HQ==} engines: {node: '>= 14.0.0'} '@ampproject/remapping@2.3.0': resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} engines: {node: '>=6.0.0'} - '@angular/animations@21.0.0-rc.2': - resolution: {integrity: sha512-NibaaIiPHwp7MlLt3ncNmwiX5GdXtBFzsYnsKt19W9KRR51XGBj+Cr85fyhLF+UyntPZKWMbllia22IvxOL84g==} + '@angular/animations@21.1.0-next.4': + resolution: {integrity: sha512-GtbawUvSBiUX5/DPLJh0iQcsdqLaNhrs0X7XET/6DyKDK39dlWjOLc/etBPQc7RlbP1QzlbpsISb/Gu0rcbv5A==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} peerDependencies: - '@angular/core': 21.0.0-rc.2 + '@angular/core': 21.1.0-next.4 - '@angular/cdk@21.0.0-rc.2': - resolution: {integrity: sha512-QO+0m1OXHwbpB4hoFnx+tkWsvChcNXvyHv3I4bN+wI8iv54yBuVgT5r8w+3VOAg/QB0em0AQEU7fdXfI3mwb3w==} + '@angular/cdk@21.1.0-next.3': + resolution: {integrity: sha512-1NxzybXwBefUdOX5HzffjgZg4AwYuogDfRDgViTSzM4yZsVPup5+dDafwZAjYu90qdjriH5d/Lf6PUxhp2rLtA==} 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/platform-browser': ^21.0.0-0 || ^21.1.0-0 || ^21.2.0-0 || ^21.3.0-0 || ^22.0.0-0 rxjs: ^6.5.3 || ^7.4.0 - '@angular/common@21.0.0-rc.2': - resolution: {integrity: sha512-NAu9v3CPxkGHoZvvauywNlr0mPa7WBtyvS03WJlBD6vijruBdbmB5ey9CKTAmAtPWadN0Wld+/SKuy+aVs6VKQ==} + '@angular/common@21.1.0-next.4': + resolution: {integrity: sha512-HNM0eaZ86pXQZnmI6MlVj0FvvI3wF5mBkGyMN8Ktuswf9DUq04xBkliLiMwkb5UFmeSibxE3mUaMymw92Nn4fA==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} peerDependencies: - '@angular/core': 21.0.0-rc.2 + '@angular/core': 21.1.0-next.4 rxjs: ^6.5.3 || ^7.4.0 - '@angular/compiler-cli@21.0.0-rc.2': - resolution: {integrity: sha512-mAdWUJ2ZWqgs9fQa1NxqcYeXAgoE6jl2l4kcWNKKGP7WLjNSoE0A1MrIx9fsp+bbKXzY8Ly1WpwS9iTABFA9Xg==} + '@angular/compiler-cli@21.1.0-next.4': + resolution: {integrity: sha512-iW+8gnGSUqCv4WdN3LMv9ikh9vHfKnbfaG01Hvzxs+q4tL3xVRDezeL+EnpaIdmKsCOIfsYrWwAXNfMd48S4Lw==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} hasBin: true peerDependencies: - '@angular/compiler': 21.0.0-rc.2 + '@angular/compiler': 21.1.0-next.4 typescript: '>=5.9 <6.0' peerDependenciesMeta: typescript: optional: true - '@angular/compiler@21.0.0-rc.2': - resolution: {integrity: sha512-wJOwO9GoWhMm0c3ITgLhGN/tglN1Ntx6Mj588pQpHb5SKEdriqdUwEwu66MNrMy3o0FXdfiuahzWuqL37DDR3w==} + '@angular/compiler@21.1.0-next.4': + resolution: {integrity: sha512-uY4Yg3OJ/DL6AlqMjO8VXgKiFHJK3QspFJzslkJKys2d8I7a7YIoWxYRJ9ZUfWW++8Swig17pL9NOrRLXx+iQg==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} - '@angular/core@21.0.0-rc.2': - resolution: {integrity: sha512-ycxrRfvIcerGomRBdre28lh9N/h7rbxqoeRN0SjycsigJZ5FUBmvN9CyXdQdEXCjNi4ctzSOX3NEvytvJX7M/Q==} + '@angular/core@21.1.0-next.4': + resolution: {integrity: sha512-aJAGd+8o/8vle68hAJGah/DMQVD4/vFf/lDhnqe69sFLY7HLeq5UdBjIu00nZ1DUVeL0n/QOA97bLRICINhVrg==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} peerDependencies: - '@angular/compiler': 21.0.0-rc.2 + '@angular/compiler': 21.1.0-next.4 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-rc.2': - resolution: {integrity: sha512-OrCwXBnC2KeY2tUoBzIeQqARlXTspkBhRG6qmTZmqStKvrLRx6yDjXCPtYpl8+LwyzU6IMl/UmqGVXfr0aXVoA==} + '@angular/forms@21.1.0-next.4': + resolution: {integrity: sha512-GluP6ZCId5DSukrgx/RlJX2CsVwHsRTSO8wAdYsqk2JIQpSPDtJk14RzvdHnMGeuBHrWn2dy88hq8G6W0SlQDA==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} peerDependencies: - '@angular/common': 21.0.0-rc.2 - '@angular/core': 21.0.0-rc.2 - '@angular/platform-browser': 21.0.0-rc.2 - '@standard-schema/spec': ^1.0.0 + '@angular/common': 21.1.0-next.4 + '@angular/core': 21.1.0-next.4 + '@angular/platform-browser': 21.1.0-next.4 rxjs: ^6.5.3 || ^7.4.0 - '@angular/localize@21.0.0-rc.2': - resolution: {integrity: sha512-tSmCEYjDJAygBoQDNHT1hkZ1UK3I9QwbsC8L7hgsynJioDNqV2X4zAE748G/zvMT4PLb9LMadx4e0yvSE8TDog==} + '@angular/localize@21.1.0-next.4': + resolution: {integrity: sha512-awaQi5ib3UteQrIpxZmVrPBLnpAiPFeqVaogj0+hbn5dIvcQ4qbnjq3aTT/eR64aDGL6hByJ2e0Ac5fmVKUAEw==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} hasBin: true peerDependencies: - '@angular/compiler': 21.0.0-rc.2 - '@angular/compiler-cli': 21.0.0-rc.2 + '@angular/compiler': 21.1.0-next.4 + '@angular/compiler-cli': 21.1.0-next.4 - '@angular/material@21.0.0-rc.2': - resolution: {integrity: sha512-ousayydWUWkrWw7lKuiN9b/BeTNEio/75Z51+Hkg0n5xzgGWNV5tVT/IFw8IwssKWlsJslSCzohj/j5l9Hl7rw==} + '@angular/material@21.1.0-next.3': + resolution: {integrity: sha512-m59JnFOUpTk5yLAYpJnk+nfvhzUO7tIG/WHFFOD2VmqWuadyZ+k6M4bQPy0ereumUcLue1QN7ZM6UpJWlgRqVQ==} peerDependencies: - '@angular/cdk': 21.0.0-rc.2 + '@angular/cdk': 21.1.0-next.3 '@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 rxjs: ^6.5.3 || ^7.4.0 - '@angular/ng-dev@https://codeload.github.com/angular/dev-infra-private-ng-dev-builds/tar.gz/49d7316da246484759b94f87e6eda47fde86b992': - resolution: {tarball: https://codeload.github.com/angular/dev-infra-private-ng-dev-builds/tar.gz/49d7316da246484759b94f87e6eda47fde86b992} - version: 0.0.0-c855fffb4b01bc06e743eb3bdfd54c866af09ad8 + '@angular/ng-dev@https://codeload.github.com/angular/dev-infra-private-ng-dev-builds/tar.gz/ddc3809c1993612732eaae62d28e828b2ed789e5': + resolution: {tarball: https://codeload.github.com/angular/dev-infra-private-ng-dev-builds/tar.gz/ddc3809c1993612732eaae62d28e828b2ed789e5} + version: 0.0.0-942d738d8f4d65b161d06e6c399fefec318cdbfe hasBin: true - '@angular/platform-browser@21.0.0-rc.2': - resolution: {integrity: sha512-j1owMY2oI+AUxQUdo7Y/R+r6sEqg+u4tr4CNcLvunV8DVqvSjwr3AqHI87fcQ6o+9b1GiiM6lvJil8OCPcUOjw==} + '@angular/platform-browser@21.1.0-next.4': + resolution: {integrity: sha512-3Tntq39GTw6wWsp92FZ438mz0eILW+9aXh/r0BzRTFnr2QtDrpEOnLqTNfdxJlS/NEYyrSmP7XzkmAlt13zu2g==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} peerDependencies: - '@angular/animations': 21.0.0-rc.2 - '@angular/common': 21.0.0-rc.2 - '@angular/core': 21.0.0-rc.2 + '@angular/animations': 21.1.0-next.4 + '@angular/common': 21.1.0-next.4 + '@angular/core': 21.1.0-next.4 peerDependenciesMeta: '@angular/animations': optional: true - '@angular/platform-server@21.0.0-rc.2': - resolution: {integrity: sha512-YrdFBZXdOVyS6wkZT3vqQ/3I/a915lS98UnF6BGtXCpmjxVfR737kmJHNG2FHMz7vo3HefQMxoz7Xb/my3HPIg==} + '@angular/platform-server@21.1.0-next.4': + resolution: {integrity: sha512-R+FzXYCjNV6T7iMDPZ18FrSsTBZx200DU+ivKCMwUR9nwPYnA4oD+YwZKd+OgZrQqo4p5T/seXWScnqROopvQg==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} peerDependencies: - '@angular/common': 21.0.0-rc.2 - '@angular/compiler': 21.0.0-rc.2 - '@angular/core': 21.0.0-rc.2 - '@angular/platform-browser': 21.0.0-rc.2 + '@angular/common': 21.1.0-next.4 + '@angular/compiler': 21.1.0-next.4 + '@angular/core': 21.1.0-next.4 + '@angular/platform-browser': 21.1.0-next.4 rxjs: ^6.5.3 || ^7.4.0 - '@angular/router@21.0.0-rc.2': - resolution: {integrity: sha512-C/tWcfqU/Zx0uLEAfho3nWA9f1mfcDUZxRCLnf9zDksoJl5yJbgIFeNytU5aheHAWKqA3Laj5tnbfUNeU/Vtfg==} + '@angular/router@21.1.0-next.4': + resolution: {integrity: sha512-2ZLGbA57w9zA+yO6iXMuSaORJbD2jMoFRrKcMHamDhw81rpbJ3zcBjQ+I8GeAVgus5irWRr/6dYOmgy9kSldkg==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} peerDependencies: - '@angular/common': 21.0.0-rc.2 - '@angular/core': 21.0.0-rc.2 - '@angular/platform-browser': 21.0.0-rc.2 + '@angular/common': 21.1.0-next.4 + '@angular/core': 21.1.0-next.4 + '@angular/platform-browser': 21.1.0-next.4 rxjs: ^6.5.3 || ^7.4.0 - '@angular/service-worker@21.0.0-rc.2': - resolution: {integrity: sha512-X+FjX76ceQzE4gC5pZzCkPoQlDkcO8pDMbP1gYz745GMwO0TROUVVDrQb9h8Fu1ptknQB+tT1YprStxpVmDQXw==} + '@angular/service-worker@21.1.0-next.4': + resolution: {integrity: sha512-RadEpo+xp7hzv2qgpNRmsFsILX5ZY7AYGLHTVvEu4j5DUI9LUJllnmlEz/U5HHz/99h8eEPZPlUZ/H0OMMmUfw==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} hasBin: true peerDependencies: - '@angular/core': 21.0.0-rc.2 + '@angular/core': 21.1.0-next.4 rxjs: ^6.5.3 || ^7.4.0 - '@asamuzakjp/css-color@4.0.5': - resolution: {integrity: sha512-lMrXidNhPGsDjytDy11Vwlb6OIGrT3CmLg3VWNFyWkLWtijKl7xjvForlh8vuj0SHGjgl4qZEQzUmYTeQA2JFQ==} + '@asamuzakjp/css-color@4.1.1': + resolution: {integrity: sha512-B0Hv6G3gWGMn0xKJ0txEi/jM5iFpT3MfDxmhZFb4W047GvytCf1DHQ1D69W3zHI4yWe2aTZAA0JnbMZ7Xc8DuQ==} - '@asamuzakjp/dom-selector@6.7.4': - resolution: {integrity: sha512-buQDjkm+wDPXd6c13534URWZqbz0RP5PAhXZ+LIoa5LgwInT9HVJvGIJivg75vi8I13CxDGdTnz+aY5YUJlIAA==} + '@asamuzakjp/dom-selector@6.7.6': + resolution: {integrity: sha512-hBaJER6A9MpdG3WgdlOolHmbOYvSk46y7IQN/1+iqiCuUu6iWdQrs9DGKF8ocqsEqWujWf/V7b7vaDgiUmIvUg==} '@asamuzakjp/nwsapi@2.3.9': resolution: {integrity: sha512-n8GuYSrI9bF7FFZ/SjhwevlHc8xaVlb/7HmHelnc/PZXBD2ZR49NnN9sMMuDdEGPeeRQ5d0hqlSlEpgCX3Wl0Q==} @@ -1120,10 +1086,6 @@ packages: resolution: {integrity: sha512-6uFXyCayocRbqhZOB+6XcuZbkMNimwfVGFji8CTZnCzOHVGvDqzvitu1re2AU5LROliz7eQPhB8CpAMvnx9EjA==} engines: {node: '>=6.9.0'} - '@babel/core@7.28.4': - resolution: {integrity: sha512-2BCOP7TN8M+gVDj7/ht3hsaO/B/n5oDbiAyyvnRlNOs+u1o+JWNYTQrmpuNp1/Wq2gcFrI01JAW+paEKDMx/CA==} - engines: {node: '>=6.9.0'} - '@babel/core@7.28.5': resolution: {integrity: sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==} engines: {node: '>=6.9.0'} @@ -1677,8 +1639,8 @@ packages: peerDependencies: '@csstools/css-tokenizer': ^3.0.4 - '@csstools/css-syntax-patches-for-csstree@1.0.15': - resolution: {integrity: sha512-q0p6zkVq2lJnmzZVPR33doA51G7YOja+FBvRdp5ISIthL0MtFCgYHHhR563z9WFGxcOn0WfjSkPDJ5Qig3H3Sw==} + '@csstools/css-syntax-patches-for-csstree@1.0.22': + resolution: {integrity: sha512-qBcx6zYlhleiFfdtzkRgwNC7VVoAwfK76Vmsw5t+PbvtdknO9StgRk7ROvq9so1iqbdW4uLIDAsXRsTfUrIoOw==} engines: {node: '>=18'} '@csstools/css-tokenizer@3.0.4': @@ -1693,323 +1655,167 @@ packages: resolution: {integrity: sha512-4B4OijXeVNOPZlYA2oEwWOTkzyltLao+xbotHQeqN++Rv27Y6s818+n2Qkp8q+Fxhn0t/5lA5X1Mxktud8eayQ==} engines: {node: '>=14.17.0'} - '@emnapi/core@1.7.0': - resolution: {integrity: sha512-pJdKGq/1iquWYtv1RRSljZklxHCOCAJFJrImO5ZLKPJVJlVUcs8yFwNQlqS0Lo8xT1VAXXTCZocF9n26FWEKsw==} + '@emnapi/core@1.7.1': + resolution: {integrity: sha512-o1uhUASyo921r2XtHYOHy7gdkGLge8ghBEQHMWmyJFoXlpU58kIrhhN3w26lpQb6dspetweapMn2CSNwQ8I4wg==} - '@emnapi/runtime@1.7.0': - resolution: {integrity: sha512-oAYoQnCYaQZKVS53Fq23ceWMRxq5EhQsE0x0RdQ55jT7wagMu5k+fS39v1fiSLrtrLQlXwVINenqhLMtTrV/1Q==} + '@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.12': - resolution: {integrity: sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==} - engines: {node: '>=18'} - cpu: [ppc64] - os: [aix] - - '@esbuild/aix-ppc64@0.27.0': - resolution: {integrity: sha512-KuZrd2hRjz01y5JK9mEBSD3Vj3mbCvemhT466rSuJYeE/hjuBrHfjjcjMdTm/sz7au+++sdbJZJmuBwQLuw68A==} + '@esbuild/aix-ppc64@0.27.1': + resolution: {integrity: sha512-HHB50pdsBX6k47S4u5g/CaLjqS3qwaOVE5ILsq64jyzgMhLuCuZ8rGzM9yhsAjfjkbgUPMzZEPa7DAp7yz6vuA==} engines: {node: '>=18'} cpu: [ppc64] os: [aix] - '@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.27.0': - resolution: {integrity: sha512-CC3vt4+1xZrs97/PKDkl0yN7w8edvU2vZvAFGD16n9F0Cvniy5qvzRXjfO1l94efczkkQE6g1x0i73Qf5uthOQ==} + '@esbuild/android-arm64@0.27.1': + resolution: {integrity: sha512-45fuKmAJpxnQWixOGCrS+ro4Uvb4Re9+UTieUY2f8AEc+t7d4AaZ6eUJ3Hva7dtrxAAWHtlEFsXFMAgNnGU9uQ==} engines: {node: '>=18'} cpu: [arm64] os: [android] - '@esbuild/android-arm@0.25.12': - resolution: {integrity: sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==} - engines: {node: '>=18'} - cpu: [arm] - os: [android] - - '@esbuild/android-arm@0.27.0': - resolution: {integrity: sha512-j67aezrPNYWJEOHUNLPj9maeJte7uSMM6gMoxfPC9hOg8N02JuQi/T7ewumf4tNvJadFkvLZMlAq73b9uwdMyQ==} + '@esbuild/android-arm@0.27.1': + resolution: {integrity: sha512-kFqa6/UcaTbGm/NncN9kzVOODjhZW8e+FRdSeypWe6j33gzclHtwlANs26JrupOntlcWmB0u8+8HZo8s7thHvg==} 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/android-x64@0.27.0': - resolution: {integrity: sha512-wurMkF1nmQajBO1+0CJmcN17U4BP6GqNSROP8t0X/Jiw2ltYGLHpEksp9MpoBqkrFR3kv2/te6Sha26k3+yZ9Q==} + '@esbuild/android-x64@0.27.1': + resolution: {integrity: sha512-LBEpOz0BsgMEeHgenf5aqmn/lLNTFXVfoWMUox8CtWWYK9X4jmQzWjoGoNb8lmAYml/tQ/Ysvm8q7szu7BoxRQ==} 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.27.0': - resolution: {integrity: sha512-uJOQKYCcHhg07DL7i8MzjvS2LaP7W7Pn/7uA0B5S1EnqAirJtbyw4yC5jQ5qcFjHK9l6o/MX9QisBg12kNkdHg==} + '@esbuild/darwin-arm64@0.27.1': + resolution: {integrity: sha512-veg7fL8eMSCVKL7IW4pxb54QERtedFDfY/ASrumK/SbFsXnRazxY4YykN/THYqFnFwJ0aVjiUrVG2PwcdAEqQQ==} 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/darwin-x64@0.27.0': - resolution: {integrity: sha512-8mG6arH3yB/4ZXiEnXof5MK72dE6zM9cDvUcPtxhUZsDjESl9JipZYW60C3JGreKCEP+p8P/72r69m4AZGJd5g==} + '@esbuild/darwin-x64@0.27.1': + resolution: {integrity: sha512-+3ELd+nTzhfWb07Vol7EZ+5PTbJ/u74nC6iv4/lwIU99Ip5uuY6QoIf0Hn4m2HoV0qcnRivN3KSqc+FyCHjoVQ==} engines: {node: '>=18'} cpu: [x64] os: [darwin] - '@esbuild/freebsd-arm64@0.25.12': - resolution: {integrity: sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==} + '@esbuild/freebsd-arm64@0.27.1': + resolution: {integrity: sha512-/8Rfgns4XD9XOSXlzUDepG8PX+AVWHliYlUkFI3K3GB6tqbdjYqdhcb4BKRd7C0BhZSoaCxhv8kTcBrcZWP+xg==} 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.27.0': - resolution: {integrity: sha512-zCMeMXI4HS/tXvJz8vWGexpZj2YVtRAihHLk1imZj4efx1BQzN76YFeKqlDr3bUWI26wHwLWPd3rwh6pe4EV7g==} + '@esbuild/freebsd-x64@0.27.1': + resolution: {integrity: sha512-GITpD8dK9C+r+5yRT/UKVT36h/DQLOHdwGVwwoHidlnA168oD3uxA878XloXebK4Ul3gDBBIvEdL7go9gCUFzQ==} engines: {node: '>=18'} cpu: [x64] os: [freebsd] - '@esbuild/linux-arm64@0.25.12': - resolution: {integrity: sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==} - engines: {node: '>=18'} - cpu: [arm64] - os: [linux] - - '@esbuild/linux-arm64@0.27.0': - resolution: {integrity: sha512-AS18v0V+vZiLJyi/4LphvBE+OIX682Pu7ZYNsdUHyUKSoRwdnOsMf6FDekwoAFKej14WAkOef3zAORJgAtXnlQ==} + '@esbuild/linux-arm64@0.27.1': + resolution: {integrity: sha512-W9//kCrh/6in9rWIBdKaMtuTTzNj6jSeG/haWBADqLLa9P8O5YSRDzgD5y9QBok4AYlzS6ARHifAb75V6G670Q==} 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-arm@0.27.0': - resolution: {integrity: sha512-t76XLQDpxgmq2cNXKTVEB7O7YMb42atj2Re2Haf45HkaUpjM2J0UuJZDuaGbPbamzZ7bawyGFUkodL+zcE+jvQ==} + '@esbuild/linux-arm@0.27.1': + resolution: {integrity: sha512-ieMID0JRZY/ZeCrsFQ3Y3NlHNCqIhTprJfDgSB3/lv5jJZ8FX3hqPyXWhe+gvS5ARMBJ242PM+VNz/ctNj//eA==} 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.27.0': - resolution: {integrity: sha512-Mz1jxqm/kfgKkc/KLHC5qIujMvnnarD9ra1cEcrs7qshTUSksPihGrWHVG5+osAIQ68577Zpww7SGapmzSt4Nw==} + '@esbuild/linux-ia32@0.27.1': + resolution: {integrity: sha512-VIUV4z8GD8rtSVMfAj1aXFahsi/+tcoXXNYmXgzISL+KB381vbSTNdeZHHHIYqFyXcoEhu9n5cT+05tRv13rlw==} 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.27.0': - resolution: {integrity: sha512-QbEREjdJeIreIAbdG2hLU1yXm1uu+LTdzoq1KCo4G4pFOLlvIspBm36QrQOar9LFduavoWX2msNFAAAY9j4BDg==} + '@esbuild/linux-loong64@0.27.1': + resolution: {integrity: sha512-l4rfiiJRN7sTNI//ff65zJ9z8U+k6zcCg0LALU5iEWzY+a1mVZ8iWC1k5EsNKThZ7XCQ6YWtsZ8EWYm7r1UEsg==} 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-mips64el@0.27.0': - resolution: {integrity: sha512-sJz3zRNe4tO2wxvDpH/HYJilb6+2YJxo/ZNbVdtFiKDufzWq4JmKAiHy9iGoLjAV7r/W32VgaHGkk35cUXlNOg==} + '@esbuild/linux-mips64el@0.27.1': + resolution: {integrity: sha512-U0bEuAOLvO/DWFdygTHWY8C067FXz+UbzKgxYhXC0fDieFa0kDIra1FAhsAARRJbvEyso8aAqvPdNxzWuStBnA==} 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.27.0': - resolution: {integrity: sha512-z9N10FBD0DCS2dmSABDBb5TLAyF1/ydVb+N4pi88T45efQ/w4ohr/F/QYCkxDPnkhkp6AIpIcQKQ8F0ANoA2JA==} + '@esbuild/linux-ppc64@0.27.1': + resolution: {integrity: sha512-NzdQ/Xwu6vPSf/GkdmRNsOfIeSGnh7muundsWItmBsVpMoNPVpM61qNzAVY3pZ1glzzAxLR40UyYM23eaDDbYQ==} engines: {node: '>=18'} cpu: [ppc64] os: [linux] - '@esbuild/linux-riscv64@0.25.12': - resolution: {integrity: sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==} + '@esbuild/linux-riscv64@0.27.1': + resolution: {integrity: sha512-7zlw8p3IApcsN7mFw0O1Z1PyEk6PlKMu18roImfl3iQHTnr/yAfYv6s4hXPidbDoI2Q0pW+5xeoM4eTCC0UdrQ==} 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.12': - resolution: {integrity: sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==} - engines: {node: '>=18'} - cpu: [s390x] - os: [linux] - - '@esbuild/linux-s390x@0.27.0': - resolution: {integrity: sha512-hPlRWR4eIDDEci953RI1BLZitgi5uqcsjKMxwYfmi4LcwyWo2IcRP+lThVnKjNtk90pLS8nKdroXYOqW+QQH+w==} + '@esbuild/linux-s390x@0.27.1': + resolution: {integrity: sha512-cGj5wli+G+nkVQdZo3+7FDKC25Uh4ZVwOAK6A06Hsvgr8WqBBuOy/1s+PUEd/6Je+vjfm6stX0kmib5b/O2Ykw==} 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.27.0': - resolution: {integrity: sha512-1hBWx4OUJE2cab++aVZ7pObD6s+DK4mPGpemtnAORBvb5l/g5xFGk0vc0PjSkrDs0XaXj9yyob3d14XqvnQ4gw==} + '@esbuild/linux-x64@0.27.1': + resolution: {integrity: sha512-z3H/HYI9MM0HTv3hQZ81f+AKb+yEoCRlUby1F80vbQ5XdzEMyY/9iNlAmhqiBKw4MJXwfgsh7ERGEOhrM1niMA==} 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-arm64@0.27.0': - resolution: {integrity: sha512-6m0sfQfxfQfy1qRuecMkJlf1cIzTOgyaeXaiVaaki8/v+WB+U4hc6ik15ZW6TAllRlg/WuQXxWj1jx6C+dfy3w==} + '@esbuild/netbsd-arm64@0.27.1': + resolution: {integrity: sha512-wzC24DxAvk8Em01YmVXyjl96Mr+ecTPyOuADAvjGg+fyBpGmxmcr2E5ttf7Im8D0sXZihpxzO1isus8MdjMCXQ==} engines: {node: '>=18'} cpu: [arm64] os: [netbsd] - '@esbuild/netbsd-x64@0.25.12': - resolution: {integrity: sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==} + '@esbuild/netbsd-x64@0.27.1': + resolution: {integrity: sha512-1YQ8ybGi2yIXswu6eNzJsrYIGFpnlzEWRl6iR5gMgmsrR0FcNoV1m9k9sc3PuP5rUBLshOZylc9nqSgymI+TYg==} engines: {node: '>=18'} cpu: [x64] os: [netbsd] - '@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.27.0': - resolution: {integrity: sha512-fWgqR8uNbCQ/GGv0yhzttj6sU/9Z5/Sv/VGU3F5OuXK6J6SlriONKrQ7tNlwBrJZXRYk5jUhuWvF7GYzGguBZQ==} + '@esbuild/openbsd-arm64@0.27.1': + resolution: {integrity: sha512-5Z+DzLCrq5wmU7RDaMDe2DVXMRm2tTDvX2KU14JJVBN2CT/qov7XVix85QoJqHltpvAOZUAc3ndU56HSMWrv8g==} engines: {node: '>=18'} cpu: [arm64] os: [openbsd] - '@esbuild/openbsd-x64@0.25.12': - resolution: {integrity: sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==} + '@esbuild/openbsd-x64@0.27.1': + resolution: {integrity: sha512-Q73ENzIdPF5jap4wqLtsfh8YbYSZ8Q0wnxplOlZUOyZy7B4ZKW8DXGWgTCZmF8VWD7Tciwv5F4NsRf6vYlZtqg==} 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==} + '@esbuild/openharmony-arm64@0.27.1': + resolution: {integrity: sha512-ajbHrGM/XiK+sXM0JzEbJAen+0E+JMQZ2l4RR4VFwvV9JEERx+oxtgkpoKv1SevhjavK2z2ReHk32pjzktWbGg==} 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.12': - resolution: {integrity: sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==} - engines: {node: '>=18'} - cpu: [x64] - os: [sunos] - - '@esbuild/sunos-x64@0.27.0': - resolution: {integrity: sha512-Q1KY1iJafM+UX6CFEL+F4HRTgygmEW568YMqDA5UV97AuZSm21b7SXIrRJDwXWPzr8MGr75fUZPV67FdtMHlHA==} + '@esbuild/sunos-x64@0.27.1': + resolution: {integrity: sha512-IPUW+y4VIjuDVn+OMzHc5FV4GubIwPnsz6ubkvN8cuhEqH81NovB53IUlrlBkPMEPxvNnf79MGBoz8rZ2iW8HA==} 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.27.0': - resolution: {integrity: sha512-W1eyGNi6d+8kOmZIwi/EDjrL9nxQIQ0MiGqe/AWc6+IaHloxHSGoeRgDRKHFISThLmsewZ5nHFvGFWdBYlgKPg==} + '@esbuild/win32-arm64@0.27.1': + resolution: {integrity: sha512-RIVRWiljWA6CdVu8zkWcRmGP7iRRIIwvhDKem8UMBjPql2TXM5PkDVvvrzMtj1V+WFPB4K7zkIGM7VzRtFkjdg==} 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.27.0': - resolution: {integrity: sha512-30z1aKL9h22kQhilnYkORFYt+3wp7yZsHWus+wSKAJR8JtdfI76LJ4SBdMsCopTR3z/ORqVu5L1vtnHZWVj4cQ==} + '@esbuild/win32-ia32@0.27.1': + resolution: {integrity: sha512-2BR5M8CPbptC1AK5JbJT1fWrHLvejwZidKx3UMSF0ecHMa+smhi16drIrCEggkgviBwLYd5nwrFLSl5Kho96RQ==} engines: {node: '>=18'} cpu: [ia32] os: [win32] - '@esbuild/win32-x64@0.25.12': - resolution: {integrity: sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==} - engines: {node: '>=18'} - cpu: [x64] - os: [win32] - - '@esbuild/win32-x64@0.27.0': - resolution: {integrity: sha512-aIitBcjQeyOhMTImhLZmtxfdOcuNRpwlPNmlFKPcHQYPhEssw75Cl1TSXJXpMkzaua9FUetx/4OQKq7eJul5Cg==} + '@esbuild/win32-x64@0.27.1': + resolution: {integrity: sha512-d5X6RMYv6taIymSk8JBP+nxv8DQAMY6A51GPgusqLdK9wBz5wWIXy1KjTck6HnjE9hqJzJRdk+1p/t5soSbCtw==} engines: {node: '>=18'} cpu: [x64] os: [win32] @@ -2024,9 +1830,9 @@ packages: resolution: {integrity: sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==} engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} - '@eslint/compat@1.4.1': - resolution: {integrity: sha512-cfO82V9zxxGBxcQDr1lfaYB7wykTa0b00mGa36FrJl7iTFd0Z2cHfEYuxcBRP/iNijCsWsEkA+jzT8hGYmv33w==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@eslint/compat@2.0.0': + resolution: {integrity: sha512-T9AfE1G1uv4wwq94ozgTGio5EUQBqAVe1X9qsQtSNVEYW6j3hvtZVm8Smr4qL1qDPFg+lOB2cL5RxTRMzq4CTA==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} peerDependencies: eslint: ^8.40 || 9 peerDependenciesMeta: @@ -2045,12 +1851,16 @@ packages: 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==} + '@eslint/core@1.0.0': + resolution: {integrity: sha512-PRfWP+8FOldvbApr6xL7mNCw4cJcSTq4GA7tYbgq15mRb0kWKO/wEB2jr+uwjFH3sZvEZneZyCUGTxsv4Sahyw==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} + + '@eslint/eslintrc@3.3.3': + resolution: {integrity: sha512-Kr+LPIUVKz2qkx1HAMH8q1q6azbqBAsXJUxBl/ODDuVPX45Z9DfwB8tPjTi6nNZ8BuM3nbJxC5zCAg5elnBUTQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/js@9.39.1': - resolution: {integrity: sha512-S26Stp4zCy88tH94QbBv3XCuzRQiZ9yXofEILmglYTh/Ug/a9/umqvgFtYBAo3Lp0nsI/5/qH1CCrbdK3AP1Tw==} + '@eslint/js@9.39.2': + resolution: {integrity: sha512-q1mjIoW1VX4IvSocvM/vbTiveKC4k9eLrajNEuSsmjymSDEbpGddtpfOoN7YGAqBK3NG+uqo8ia4PDTt8buCYA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@eslint/object-schema@2.1.7': @@ -2065,8 +1875,8 @@ packages: resolution: {integrity: sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA==} engines: {node: '>=14'} - '@firebase/ai@2.5.0': - resolution: {integrity: sha512-OXv/jZLRjV9jTejWA4KOvW8gM1hNsLvQSCPwKhi2CEfe0Nap3rM6z+Ial0PGqXga0WgzhpypEvJOFvaAUFX3kg==} + '@firebase/ai@2.6.1': + resolution: {integrity: sha512-qJd9bpABqsanFnwdbjZEDbKKr1jRtuUZ+cHyNBLWsxobH4pd73QncvuO3XlMq4eKBLlg1f5jNdFpJ3G3ABu2Tg==} engines: {node: '>=20.0.0'} peerDependencies: '@firebase/app': 0.x @@ -2103,19 +1913,19 @@ packages: peerDependencies: '@firebase/app': 0.x - '@firebase/app-compat@0.5.5': - resolution: {integrity: sha512-lVG/nRnXaot0rQSZazmTNqy83ti9O3+kdwoaE0d5wahRIWNoDirbIMcGVjDDgdmf4IE6FYreWOMh0L3DV1475w==} + '@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.5': - resolution: {integrity: sha512-zyNY77xJOGwcuB+xCxF8z8lSiHvD4ox7BCsqLEHEvgqQoRjxFZ0fkROR6NV5QyXmCqRLodMM8J5d2EStOocWIw==} + '@firebase/app@0.14.6': + resolution: {integrity: sha512-4uyt8BOrBsSq6i4yiOV/gG6BnnrvTeyymlNcaN/dKvyU1GoolxAafvIvaNP1RCGPlNab3OuE4MKUQuv2lH+PLQ==} engines: {node: '>=20.0.0'} - '@firebase/auth-compat@0.6.1': - resolution: {integrity: sha512-I0o2ZiZMnMTOQfqT22ur+zcGDVSAfdNZBHo26/Tfi8EllfR1BO7aTVo2rt/ts8o/FWsK8pOALLeVBGhZt8w/vg==} + '@firebase/auth-compat@0.6.2': + resolution: {integrity: sha512-8UhCzF6pav9bw/eXA8Zy1QAKssPRYEYXaWagie1ewLTwHkXv6bKp/j6/IwzSYQP67sy/BMFXIFaCCsoXzFLr7A==} engines: {node: '>=20.0.0'} peerDependencies: '@firebase/app-compat': 0.x @@ -2129,12 +1939,12 @@ packages: '@firebase/app-types': 0.x '@firebase/util': 1.x - '@firebase/auth@1.11.1': - resolution: {integrity: sha512-Mea0G/BwC1D0voSG+60Ylu3KZchXAFilXQ/hJXWCw3gebAu+RDINZA0dJMNeym7HFxBaBaByX8jSa7ys5+F2VA==} + '@firebase/auth@1.12.0': + resolution: {integrity: sha512-zkvLpsrxynWHk07qGrUDfCSqKf4AvfZGEqJ7mVCtYGjNNDbGE71k0Yn84rg8QEZu4hQw1BC0qDEHzpNVBcSVmA==} engines: {node: '>=20.0.0'} peerDependencies: '@firebase/app': 0.x - '@react-native-async-storage/async-storage': ^1.18.1 + '@react-native-async-storage/async-storage': ^2.2.0 peerDependenciesMeta: '@react-native-async-storage/async-storage': optional: true @@ -2143,8 +1953,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 @@ -2159,8 +1969,8 @@ packages: resolution: {integrity: sha512-gM6MJFae3pTyNLoc9VcJNuaUDej0ctdjn3cVtILo3D5lpp0dmUHHLFN/pUKe7ImyeB1KAvRlEYxvIHNF04Filg==} engines: {node: '>=20.0.0'} - '@firebase/firestore-compat@0.4.2': - resolution: {integrity: sha512-cy7ov6SpFBx+PHwFdOOjbI7kH00uNKmIFurAn560WiPCZXy9EMnil1SOG7VF4hHZKdenC+AHtL4r3fNpirpm0w==} + '@firebase/firestore-compat@0.4.3': + resolution: {integrity: sha512-1ylF/njF68Pmb6p0erP0U78XQv1w77Wap4bUmqZ7ZVkmN1oMgplyu0TyirWtCBoKFRV2+SUZfWXvIij/z39LYg==} engines: {node: '>=20.0.0'} peerDependencies: '@firebase/app-compat': 0.x @@ -2171,8 +1981,8 @@ packages: '@firebase/app-types': 0.x '@firebase/util': 1.x - '@firebase/firestore@4.9.2': - resolution: {integrity: sha512-iuA5+nVr/IV/Thm0Luoqf2mERUvK9g791FZpUJV1ZGXO6RL2/i/WFJUj5ZTVXy5pRjpWYO+ZzPcReNrlilmztA==} + '@firebase/firestore@4.9.3': + resolution: {integrity: sha512-RVuvhcQzs1sD5Osr2naQS71H0bQMbSnib16uOWAKk3GaKb/WBPyCYSr2Ry7MqlxDP/YhwknUxECL07lw9Rq1nA==} engines: {node: '>=20.0.0'} peerDependencies: '@firebase/app': 0.x @@ -2306,17 +2116,17 @@ packages: resolution: {integrity: sha512-IJn+8A3QZJfe7FUtWqHVNo3xJs7KFpurCWGWCiCz3oEh+BkRymKZ1QxfAbU2yGMDzTytLGQ2IV6T2r3cuo75/w==} engines: {node: '>=18'} - '@google/genai@1.29.0': - resolution: {integrity: sha512-cQP7Ssa06W+MSAyVtL/812FBtZDoDehnFObIpK1xo5Uv4XvqBcVZ8OhXgihOIXWn7xvPQGvLclR8+yt3Ysnd9g==} + '@google/genai@1.33.0': + resolution: {integrity: sha512-ThUjFZ1N0DU88peFjnQkb8K198EWaW2RmmnDShFQ+O+xkIH9itjpRe358x3L/b4X/A7dimkvq63oz49Vbh7Cog==} engines: {node: '>=20.0.0'} peerDependencies: - '@modelcontextprotocol/sdk': ^1.20.1 + '@modelcontextprotocol/sdk': ^1.24.0 peerDependenciesMeta: '@modelcontextprotocol/sdk': optional: true - '@grpc/grpc-js@1.14.0': - resolution: {integrity: sha512-N8Jx6PaYzcTRNzirReJCtADVoq4z7+1KQ4E70jTg/koQiMoUSN1kbNjPOqpPbhMFhfU1/l7ixspPl8dNY+FoUg==} + '@grpc/grpc-js@1.14.3': + resolution: {integrity: sha512-Iq8QQQ/7X3Sac15oB6p0FmUg/klxQvXLeileoqrTRGJYLV+/9tubbr9ipz0GKHjmXVsgFPo/+W+2cA8eNcR+XA==} engines: {node: '>=12.10.0'} '@grpc/grpc-js@1.9.15': @@ -2336,6 +2146,12 @@ packages: '@hapi/bourne@3.0.0': resolution: {integrity: sha512-Waj1cwPXJDucOib4a3bAISsKJVb15MKi9IvmTI/7ssVEm6sywXGjVJDhl6/umt1pK1ZS7PacXU3A1PmFKHEZ2w==} + '@hono/node-server@1.19.7': + resolution: {integrity: sha512-vUcD0uauS7EU2caukW8z5lJKtoGMokxNbJtBiwHgpqxEXokaHCBkQUmCHhjFB1VUTWdqj25QoMkMKzgjq+uhrw==} + engines: {node: '>=18.14.1'} + peerDependencies: + hono: ^4 + '@humanfs/core@0.19.1': resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==} engines: {node: '>=18.18.0'} @@ -2356,6 +2172,10 @@ packages: resolution: {integrity: sha512-S8qNSZiYzFd0wAcyG5AXCvUHC5Sr7xpZ9wZ2py9XR88jUz8wooStVx5M6dRzczbBWjic9NP7+rY0Xi7qqK/aMQ==} engines: {node: '>=18'} + '@inquirer/ansi@2.0.2': + resolution: {integrity: sha512-SYLX05PwJVnW+WVegZt1T4Ip1qba1ik+pNJPDiqvk6zS5Y/i8PhRzLpGEtVd7sW0G8cMtkD8t4AZYhQwm8vnww==} + 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'} @@ -2365,6 +2185,15 @@ packages: '@types/node': optional: true + '@inquirer/checkbox@5.0.3': + resolution: {integrity: sha512-xtQP2eXMFlOcAhZ4ReKP2KZvDIBb1AnCfZ81wWXG3DXLVH0f0g4obE0XDPH+ukAEMRcZT0kdX2AS1jrWGXbpxw==} + 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.21': resolution: {integrity: sha512-KR8edRkIsUayMXV+o3Gv+q4jlhENF9nMYUZs9PA2HzrXeHI8M5uDag70U7RJn9yyiMZSbtF5/UexBtAVtZGSbQ==} engines: {node: '>=18'} @@ -2374,6 +2203,15 @@ packages: '@types/node': optional: true + '@inquirer/confirm@6.0.3': + resolution: {integrity: sha512-lyEvibDFL+NA5R4xl8FUmNhmu81B+LDL9L/MpKkZlQDJZXzG8InxiqYxiAlQYa9cqLLhYqKLQwZqXmSTqCLjyw==} + 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'} @@ -2383,6 +2221,15 @@ packages: '@types/node': optional: true + '@inquirer/core@11.1.0': + resolution: {integrity: sha512-+jD/34T1pK8M5QmZD/ENhOfXdl9Zr+BrQAUc5h2anWgi7gggRq15ZbiBeLoObj0TLbdgW7TAIQRU2boMc9uOKQ==} + 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'} @@ -2392,6 +2239,15 @@ packages: '@types/node': optional: true + '@inquirer/editor@5.0.3': + resolution: {integrity: sha512-wYyQo96TsAqIciP/r5D3cFeV8h4WqKQ/YOvTg5yOfP2sqEbVVpbxPpfV3LM5D0EP4zUI3EZVHyIUIllnoIa8OQ==} + 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'} @@ -2401,6 +2257,15 @@ packages: '@types/node': optional: true + '@inquirer/expand@5.0.3': + resolution: {integrity: sha512-2oINvuL27ujjxd95f6K2K909uZOU2x1WiAl7Wb1X/xOtL8CgQ1kSxzykIr7u4xTkXkXOAkCuF45T588/YKee7w==} + 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'} @@ -2410,10 +2275,23 @@ packages: '@types/node': optional: true + '@inquirer/external-editor@2.0.2': + resolution: {integrity: sha512-X/fMXK7vXomRWEex1j8mnj7s1mpnTeP4CO/h2gysJhHLT2WjBnLv4ZQEGpm/kcYI8QfLZ2fgW+9kTKD+jeopLg==} + 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/figures@2.0.2': + resolution: {integrity: sha512-qXm6EVvQx/FmnSrCWCIGtMHwqeLgxABP8XgcaAoywsL0NFga9gD5kfG0gXiv80GjK9Hsoz4pgGwF/+CjygyV9A==} + 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'} @@ -2423,6 +2301,15 @@ packages: '@types/node': optional: true + '@inquirer/input@5.0.3': + resolution: {integrity: sha512-4R0TdWl53dtp79Vs6Df2OHAtA2FVNqya1hND1f5wjHWxZJxwDMSNB1X5ADZJSsQKYAJ5JHCTO+GpJZ42mK0Otw==} + 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'} @@ -2432,6 +2319,15 @@ packages: '@types/node': optional: true + '@inquirer/number@4.0.3': + resolution: {integrity: sha512-TjQLe93GGo5snRlu83JxE38ZPqj5ZVggL+QqqAF2oBA5JOJoxx25GG3EGH/XN/Os5WOmKfO8iLVdCXQxXRZIMQ==} + 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'} @@ -2441,9 +2337,9 @@ packages: '@types/node': optional: true - '@inquirer/prompts@7.10.0': - resolution: {integrity: sha512-X2HAjY9BClfFkJ2RP3iIiFxlct5JJVdaYYXhA7RKxsbc9KL+VbId79PSoUGH/OLS011NFbHHDMDcBKUj3T89+Q==} - engines: {node: '>=18'} + '@inquirer/password@5.0.3': + resolution: {integrity: sha512-rCozGbUMAHedTeYWEN8sgZH4lRCdgG/WinFkit6ZPsp8JaNg2T0g3QslPBS5XbpORyKP/I+xyBO81kFEvhBmjA==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0'} peerDependencies: '@types/node': '>=18' peerDependenciesMeta: @@ -2459,6 +2355,15 @@ packages: '@types/node': optional: true + '@inquirer/prompts@8.1.0': + resolution: {integrity: sha512-LsZMdKcmRNF5LyTRuZE5nWeOjganzmN3zwbtNfcs6GPh3I2TsTtF1UYZlbxVfhxd+EuUqLGs/Lm3Xt4v6Az1wA==} + 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'} @@ -2468,6 +2373,15 @@ packages: '@types/node': optional: true + '@inquirer/rawlist@5.1.0': + resolution: {integrity: sha512-yUCuVh0jW026Gr2tZlG3kHignxcrLKDR3KBp+eUgNz+BAdSeZk0e18yt2gyBr+giYhj/WSIHCmPDOgp1mT2niQ==} + 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'} @@ -2477,6 +2391,15 @@ packages: '@types/node': optional: true + '@inquirer/search@4.0.3': + resolution: {integrity: sha512-lzqVw0YwuKYetk5VwJ81Ba+dyVlhseHPx9YnRKQgwXdFS0kEavCz2gngnNhnMIxg8+j1N/rUl1t5s1npwa7bqg==} + 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'} @@ -2486,6 +2409,15 @@ packages: '@types/node': optional: true + '@inquirer/select@5.0.3': + resolution: {integrity: sha512-M+ynbwS0ecQFDYMFrQrybA0qL8DV0snpc4kKevCCNaTpfghsRowRY7SlQBeIYNzHqXtiiz4RG9vTOeb/udew7w==} + 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'} @@ -2495,9 +2427,9 @@ packages: '@types/node': optional: true - '@inquirer/type@3.0.9': - resolution: {integrity: sha512-QPaNt/nmE2bLGQa9b7wwyRJoLZ7pN6rcyXvzU0YCmivmJyq1BVo94G98tStRWkoD1RgDX5C+dPlhhHzNdu/W/w==} - engines: {node: '>=18'} + '@inquirer/type@4.0.2': + resolution: {integrity: sha512-cae7mzluplsjSdgFA6ACLygb5jC8alO0UUnFPyu0E7tNRPrL+q/f8VcSXp+cjZQ7l5CMpDpi2G1+IQvkOiL1Lw==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0'} peerDependencies: '@types/node': '>=18' peerDependenciesMeta: @@ -2630,11 +2562,12 @@ packages: cpu: [x64] os: [win32] - '@modelcontextprotocol/sdk@1.21.1': - resolution: {integrity: sha512-UyLFcJLDvUuZbGnaQqXFT32CpPpGj7VS19roLut6gkQVhb439xUzYWbsUvdI3ZPL+2hnFosuugtYWE0Mcs1rmQ==} + '@modelcontextprotocol/sdk@1.25.0': + resolution: {integrity: sha512-z0Zhn/LmQ3yz91dEfd5QgS7DpSjA4pk+3z2++zKgn5L6iDFM9QapsVoAQSbKLvlrFsZk9+ru6yHHWNq2lCYJKQ==} engines: {node: '>=18'} peerDependencies: '@cfworker/json-schema': ^4.1.1 + zod: ^3.25 || ^4.0 peerDependenciesMeta: '@cfworker/json-schema': optional: true @@ -2786,8 +2719,8 @@ packages: resolution: {integrity: sha512-xJIPs+bYuc9ASBl+cvGsKbGrJmS6fAKaSZCnT0lhahT5rhA2VVy9/EcIgd2JhtEuFOJNx7UHNn/qiTPTY4nrQw==} engines: {node: '>= 10'} - '@napi-rs/wasm-runtime@1.0.7': - resolution: {integrity: sha512-SeDnOO0Tk7Okiq6DbXmmBODgOAb9dp9gjlphokTUxmt8U3liIP1ZsozBahH69j/RJv+Rfs6IwUKHTgQYJ/HBAw==} + '@napi-rs/wasm-runtime@1.1.0': + resolution: {integrity: sha512-Fq6DJW+Bb5jaWE69/qOE0D1TUN9+6uWhCeZpdnSBk14pjLcCWR7Q8n49PTSPHazM37JqrsdpEthXy2xn6jWWiA==} '@nodelib/fs.scandir@2.1.5': resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} @@ -2801,49 +2734,41 @@ 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': - resolution: {integrity: sha512-fkxoPuFGvxyrH+OQzyTkX2LUEamrF4jZSmxjAtPPHHGO0dqsQ8tTKjnIS8SAnPHdk2I03BDtSMR5K/4loKg79Q==} - engines: {node: ^18.17.0 || >=20.5.0} + '@npmcli/installed-package-contents@4.0.0': + resolution: {integrity: sha512-yNyAdkBxB72gtZ4GrwXCM0ZUedo9nIbOMKfGjt6Cu6DXf0p8y1PViZAKDC8q8kv/fufx0WTjRBdSlyrvnP7hmA==} + engines: {node: ^20.17.0 || >=22.9.0} hasBin: true '@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/promise-spawn@9.0.0': - resolution: {integrity: sha512-qxvGj3ZM6Zuk8YeVMY0gZHY19WN6g3OGxwR4MBaxHImfD/4zD0HpgBHNOSayEaisj/p3PyQjdQlO9tbl5ZBFZg==} + '@npmcli/promise-spawn@9.0.1': + resolution: {integrity: sha512-OLUaoqBuyxeTqUvjA3FZFiXUfYC1alp3Sa99gW3EUDz3tZ3CbXDdcZ7qWKBzicrJleIgucoWamWH1saAmH/l2Q==} engines: {node: ^20.17.0 || >=22.9.0} - '@npmcli/redact@3.2.2': - resolution: {integrity: sha512-7VmYAmk4csGv08QzrDKScdzn11jHPFGyqJW39FyPgPuAp3zIaUmuCo1yxw9aGs+NEJuTGQ9Gwqpt93vtJubucg==} - engines: {node: ^18.17.0 || >=20.5.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.2': - resolution: {integrity: sha512-9lCTqxaoa9c9cdkzSSx+q/qaYrCrUPEwTWzLkVYg1/T8ESH3BG9vmb1zRc6ODsBVB0+gnGRSqSr01pxTS1yX3A==} + '@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.2': @@ -2874,8 +2799,8 @@ packages: 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.3': resolution: {integrity: sha512-grAEuupr/C1rALFnXTv6ZQhFuL1D8G5y8CN04RgrO4FIPMrtm+mcZzFG7dcBm+nq+1ppNixu+Jd78aeJOYxlGA==} @@ -2910,12 +2835,12 @@ packages: peerDependencies: '@octokit/core': '>=6' - '@octokit/request-error@7.0.2': - resolution: {integrity: sha512-U8piOROoQQUyExw5c6dTkU3GKxts5/ERRThIauNL7yaRoeXW0q/5bgHWT7JfWBw1UyrbK8ERId2wVkcB32n0uQ==} + '@octokit/request-error@7.1.0': + resolution: {integrity: sha512-KMQIfq5sOPpkQYajXHwnhjCC0slzCNScLHs9JafXc4RAJI+9f+jNDlBNaIMTvazOPLgb4BnlhGJOTbnN0wIjPw==} engines: {node: '>= 20'} - '@octokit/request@10.0.6': - resolution: {integrity: sha512-FO+UgZCUu+pPnZAR+iKdUt64kPE7QW7ciqpldaMXaNzixz5Jld8dJ31LAUewk0cfSRkNSRKyqG438ba9c/qDlQ==} + '@octokit/request@10.0.7': + resolution: {integrity: sha512-v93h0i1yu4idj8qFPZwjehoJx4j3Ntn+JhXsdJrG9pYaX6j/XRz2RmasMUHtNgQD39nrv/VwTWSqK0RNXR8upA==} engines: {node: '>= 20'} '@octokit/rest@22.0.1': @@ -2950,12 +2875,12 @@ packages: 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.97.0': - resolution: {integrity: sha512-lxmZK4xFrdvU0yZiDwgVQTCvh2gHWBJCBk5ALsrtsBWhs0uDIi+FTOnXRQeQfs304imdvTdaakT/lqwQ8hkOXQ==} + '@oxc-project/types@0.102.0': + resolution: {integrity: sha512-8Skrw405g+/UJPKWJ1twIk3BIH2nXdiVlVNtYT23AXVwpsd79es4K+KYt06Fbnkc5BaTvk/COT2JuCLYdwnCdA==} '@parcel/watcher-android-arm64@2.5.1': resolution: {integrity: sha512-KF8+j9nNbUN8vzOFDpRMsaKBHZ/mcjEjMToVMJOhTozkDonQFFrRcfdLWn6yWKCmJKmdVxSgHiYvTCef4/qcBA==} @@ -3045,6 +2970,9 @@ packages: resolution: {integrity: sha512-dfUnCxiN9H4ap84DvD2ubjw+3vUNpstxa0TneY/Paat8a3R4uQZDLSvWjmznAY/DoahqTHl9V46HF/Zs3F29pg==} engines: {node: '>= 10.0.0'} + '@pinojs/redact@0.4.0': + resolution: {integrity: sha512-k2ENnmBugE/rzQfEcdWHcCY+/FM3VLzH9cYEsbdsoqrvzAKRhUZeRNhAZvB8OitQJ1TBed3yqWtdjzS6wJKBwg==} + '@pkgjs/parseargs@0.11.0': resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} engines: {node: '>=14'} @@ -3057,16 +2985,16 @@ packages: resolution: {integrity: sha512-tNe7a6U4rCpxLMBaR0SIYTdjxGdL0Vwb3G1zY8++sPtHSvy7qd54u8CIB0Z+Y6t5tc9pNYMYCMwhE/wdSY7ltg==} engines: {node: '>=18.12'} - '@pnpm/dependency-path@1001.1.4': - resolution: {integrity: sha512-AUFsnxZB13ME7JKFis3/zo+Cz4qvYl7PzpzFFrw0bL75OY2+jeXcG3dbNXoVfpjaA3/lyZBtQmxE+Q6xbjyV5Q==} + '@pnpm/dependency-path@1001.1.8': + resolution: {integrity: sha512-+/SabdOsq4ycO/s1F82mUTmYb9KTE7e74qbXE9caM6slbaJesVqQOKDxSP4RqCy5jkjDz26kpkWzxeNJLowdNQ==} engines: {node: '>=18.12'} '@pnpm/graceful-fs@1000.0.1': resolution: {integrity: sha512-JnzaAVFJIEgwTcB55eww8N3h5B6qJdZqDA2wYkSK+OcTvvMSQb9c2STMhBP6GfkWygG1fs3w8D7JRx9SPZnxJg==} engines: {node: '>=18.12'} - '@pnpm/types@1001.0.0': - resolution: {integrity: sha512-9P7I8Zv8hvAO81+D5KVmwveH4nmxhBNFEEeb77YYPV72bkyqzKTR6I8OGEs7TNZ6gPHufF+lIyBVEqO6SMFpJA==} + '@pnpm/types@1001.2.0': + resolution: {integrity: sha512-UIju+OadUVS0q5q/MbRAzMS5M9HZcZyT6evyrgPUH0DV9przkcW7/LH1Sj33Q2MpJO9Nzqw4b4w72x8mvtUAew==} engines: {node: '>=18.12'} '@protobufjs/aspromise@1.1.2': @@ -3099,100 +3027,94 @@ 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.11.0': + resolution: {integrity: sha512-n6oQX6mYkG8TRPuPXmbPidkUbsSRalhmaaVAQxvH1IkQy63cwsH+kOjB3e4cpCDHg0aSvsiX9bQ4s2VB6mGWUQ==} engines: {node: '>=18'} hasBin: true - '@rolldown/binding-android-arm64@1.0.0-beta.50': - resolution: {integrity: sha512-XlEkrOIHLyGT3avOgzfTFSjG+f+dZMw+/qd+Y3HLN86wlndrB/gSimrJCk4gOhr1XtRtEKfszpadI3Md4Z4/Ag==} + '@rolldown/binding-android-arm64@1.0.0-beta.54': + resolution: {integrity: sha512-zZRx/ur3Fai3fxiEmVp48+6GCBR48PRWJR1X3TTMn9yiq2bBHlYPgBaQtDOYWXv5H3J5dXujeTyGnuoY+kdGCg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [android] - '@rolldown/binding-darwin-arm64@1.0.0-beta.50': - resolution: {integrity: sha512-+JRqKJhoFlt5r9q+DecAGPLZ5PxeLva+wCMtAuoFMWPoZzgcYrr599KQ+Ix0jwll4B4HGP43avu9My8KtSOR+w==} + '@rolldown/binding-darwin-arm64@1.0.0-beta.54': + resolution: {integrity: sha512-zMyFEJmbIs91x22HAA/eUvmZHgjX8tGsD3TJ+WC9aY4bCdl3w84H9vMZmChSHAF1dYvGNH4KQDI2IubeZaCYtg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [darwin] - '@rolldown/binding-darwin-x64@1.0.0-beta.50': - resolution: {integrity: sha512-fFXDjXnuX7/gQZQm/1FoivVtRcyAzdjSik7Eo+9iwPQ9EgtA5/nB2+jmbzaKtMGG3q+BnZbdKHCtOacmNrkIDA==} + '@rolldown/binding-darwin-x64@1.0.0-beta.54': + resolution: {integrity: sha512-Ex7QttdaVnEpmE/zroUT5Qm10e2+Vjd9q0LX9eXm59SitxDODMpC8GI1Rct5RrLf4GLU4DzdXBj6DGzuR+6g6w==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [darwin] - '@rolldown/binding-freebsd-x64@1.0.0-beta.50': - resolution: {integrity: sha512-F1b6vARy49tjmT/hbloplzgJS7GIvwWZqt+tAHEstCh0JIh9sa8FAMVqEmYxDviqKBaAI8iVvUREm/Kh/PD26Q==} + '@rolldown/binding-freebsd-x64@1.0.0-beta.54': + resolution: {integrity: sha512-E1XO10ryM/Vxw3Q1wvs9s2mSpVBfbHtzkbJcdu26qh17ZmVwNWLiIoqEcbkXm028YwkReG4Gd2gCZ3NxgTQ28Q==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [freebsd] - '@rolldown/binding-linux-arm-gnueabihf@1.0.0-beta.50': - resolution: {integrity: sha512-U6cR76N8T8M6lHj7EZrQ3xunLPxSvYYxA8vJsBKZiFZkT8YV4kjgCO3KwMJL0NOjQCPGKyiXO07U+KmJzdPGRw==} + '@rolldown/binding-linux-arm-gnueabihf@1.0.0-beta.54': + resolution: {integrity: sha512-oS73Uks8jczQR9pg0Bj718vap/x71exyJ5yuxu4X5V4MhwRQnky7ANSPm6ARUfraxOqt49IBfcMeGnw2rTSqdA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] - '@rolldown/binding-linux-arm64-gnu@1.0.0-beta.50': - resolution: {integrity: sha512-ONgyjofCrrE3bnh5GZb8EINSFyR/hmwTzZ7oVuyUB170lboza1VMCnb8jgE6MsyyRgHYmN8Lb59i3NKGrxrYjw==} + '@rolldown/binding-linux-arm64-gnu@1.0.0-beta.54': + resolution: {integrity: sha512-pY8N2X5C+/ZQcy0eRdfOzOP//OFngP1TaIqDjFwfBPws2UNavKS8SpxhPEgUaYIaT0keVBd/TB+eVy9z+CIOtw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] libc: [glibc] - '@rolldown/binding-linux-arm64-musl@1.0.0-beta.50': - resolution: {integrity: sha512-L0zRdH2oDPkmB+wvuTl+dJbXCsx62SkqcEqdM+79LOcB+PxbAxxjzHU14BuZIQdXcAVDzfpMfaHWzZuwhhBTcw==} + '@rolldown/binding-linux-arm64-musl@1.0.0-beta.54': + resolution: {integrity: sha512-cgTooAFm2MUmFriB7IYaWBNyqrGlRPKG+yaK2rGFl2rcdOcO24urY4p3eyB0ogqsRLvJbIxwjjYiWiIP7Eo1Cw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] libc: [musl] - '@rolldown/binding-linux-x64-gnu@1.0.0-beta.50': - resolution: {integrity: sha512-gyoI8o/TGpQd3OzkJnh1M2kxy1Bisg8qJ5Gci0sXm9yLFzEXIFdtc4EAzepxGvrT2ri99ar5rdsmNG0zP0SbIg==} + '@rolldown/binding-linux-x64-gnu@1.0.0-beta.54': + resolution: {integrity: sha512-nGyLT1Qau0W+kEL44V2jhHmvfS3wyJW08E4WEu2E6NuIy+uChKN1X0aoxzFIDi2owDsYaZYez/98/f268EupIQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] libc: [glibc] - '@rolldown/binding-linux-x64-musl@1.0.0-beta.50': - resolution: {integrity: sha512-zti8A7M+xFDpKlghpcCAzyOi+e5nfUl3QhU023ce5NCgUxRG5zGP2GR9LTydQ1rnIPwZUVBWd4o7NjZDaQxaXA==} + '@rolldown/binding-linux-x64-musl@1.0.0-beta.54': + resolution: {integrity: sha512-KH374P0TUjDXssROT/orvzaWrzGOptD13PTrltgKwbDprJTMknoLiYsOD6Ttz92O2VuAcCtFuJ1xbyFM2Uo/Xg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] libc: [musl] - '@rolldown/binding-openharmony-arm64@1.0.0-beta.50': - resolution: {integrity: sha512-eZUssog7qljrrRU9Mi0eqYEPm3Ch0UwB+qlWPMKSUXHNqhm3TvDZarJQdTevGEfu3EHAXJvBIe0YFYr0TPVaMA==} + '@rolldown/binding-openharmony-arm64@1.0.0-beta.54': + resolution: {integrity: sha512-oMAVO4wbfAbhpBxPsSp8R7ntL2DchpNfO+tGhN8/sI9jsbYwOv78uIW1fTwOBslhjTVFltGJ+l23mubNQcYNaQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [openharmony] - '@rolldown/binding-wasm32-wasi@1.0.0-beta.50': - resolution: {integrity: sha512-nmCN0nIdeUnmgeDXiQ+2HU6FT162o+rxnF7WMkBm4M5Ds8qTU7Dzv2Wrf22bo4ftnlrb2hKK6FSwAJSAe2FWLg==} + '@rolldown/binding-wasm32-wasi@1.0.0-beta.54': + resolution: {integrity: sha512-MYY/FmY+HehHiQkNx04W5oLy/Fqd1hXYqZmmorSDXvAHnxMbSgmdFicKsSYOg/sVGHBMEP1tTn6kV5sWrS45rA==} engines: {node: '>=14.0.0'} cpu: [wasm32] - '@rolldown/binding-win32-arm64-msvc@1.0.0-beta.50': - resolution: {integrity: sha512-7kcNLi7Ua59JTTLvbe1dYb028QEPaJPJQHqkmSZ5q3tJueUeb6yjRtx8mw4uIqgWZcnQHAR3PrLN4XRJxvgIkA==} + '@rolldown/binding-win32-arm64-msvc@1.0.0-beta.54': + resolution: {integrity: sha512-66o3uKxUmcYskT9exskxs3OVduXf5x0ndlMkYOjSpBgqzhLtkub136yDvZkNT1OkNDET0odSwcU7aWdpnwzAyg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [win32] - '@rolldown/binding-win32-ia32-msvc@1.0.0-beta.50': - resolution: {integrity: sha512-lL70VTNvSCdSZkDPPVMwWn/M2yQiYvSoXw9hTLgdIWdUfC3g72UaruezusR6ceRuwHCY1Ayu2LtKqXkBO5LIwg==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [ia32] - os: [win32] - - '@rolldown/binding-win32-x64-msvc@1.0.0-beta.50': - resolution: {integrity: sha512-4qU4x5DXWB4JPjyTne/wBNPqkbQU8J45bl21geERBKtEittleonioACBL1R0PsBu0Aq21SwMK5a9zdBkWSlQtQ==} + '@rolldown/binding-win32-x64-msvc@1.0.0-beta.54': + resolution: {integrity: sha512-FbbbrboChLBXfeEsOfaypBGqzbdJ/CcSA2BPLCggojnIHy58Jo+AXV7HATY8opZk7194rRbokIT8AfPJtZAWtg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [win32] - '@rolldown/pluginutils@1.0.0-beta.50': - resolution: {integrity: sha512-5e76wQiQVeL1ICOZVUg4LSOVYg9jyhGCin+icYozhsUzM+fHE7kddi1bdiE0jwVqTfkjba3jUFbEkoC9WkdvyA==} + '@rolldown/pluginutils@1.0.0-beta.54': + resolution: {integrity: sha512-AHgcZ+w7RIRZ65ihSQL8YuoKcpD9Scew4sEeP1BBUT9QdTo6KjwHrZZXjID6nL10fhKessCH6OPany2QKwAwTQ==} '@rollup/plugin-alias@6.0.0': resolution: {integrity: sha512-tPCzJOtS7uuVZd+xPhoy5W4vThe6KWXNmsFCNktaAh5RTqcLiSfT4huPQIXkgJ6YCOjJHvecOAzQxLFhPxKr+g==} @@ -3257,250 +3179,129 @@ packages: rollup: optional: true - '@rollup/rollup-android-arm-eabi@4.53.1': - resolution: {integrity: sha512-bxZtughE4VNVJlL1RdoSE545kc4JxL7op57KKoi59/gwuU5rV6jLWFXXc8jwgFoT6vtj+ZjO+Z2C5nrY0Cl6wA==} + '@rollup/rollup-android-arm-eabi@4.53.5': + resolution: {integrity: sha512-iDGS/h7D8t7tvZ1t6+WPK04KD0MwzLZrG0se1hzBjSi5fyxlsiggoJHwh18PCFNn7tG43OWb6pdZ6Y+rMlmyNQ==} cpu: [arm] os: [android] - '@rollup/rollup-android-arm-eabi@4.53.2': - resolution: {integrity: sha512-yDPzwsgiFO26RJA4nZo8I+xqzh7sJTZIWQOxn+/XOdPE31lAvLIYCKqjV+lNH/vxE2L2iH3plKxDCRK6i+CwhA==} - cpu: [arm] - os: [android] - - '@rollup/rollup-android-arm64@4.53.1': - resolution: {integrity: sha512-44a1hreb02cAAfAKmZfXVercPFaDjqXCK+iKeVOlJ9ltvnO6QqsBHgKVPTu+MJHSLLeMEUbeG2qiDYgbFPU48g==} - cpu: [arm64] - os: [android] - - '@rollup/rollup-android-arm64@4.53.2': - resolution: {integrity: sha512-k8FontTxIE7b0/OGKeSN5B6j25EuppBcWM33Z19JoVT7UTXFSo3D9CdU39wGTeb29NO3XxpMNauh09B+Ibw+9g==} + '@rollup/rollup-android-arm64@4.53.5': + resolution: {integrity: sha512-wrSAViWvZHBMMlWk6EJhvg8/rjxzyEhEdgfMMjREHEq11EtJ6IP6yfcCH57YAEca2Oe3FNCE9DSTgU70EIGmVw==} cpu: [arm64] os: [android] - '@rollup/rollup-darwin-arm64@4.53.1': - resolution: {integrity: sha512-usmzIgD0rf1syoOZ2WZvy8YpXK5G1V3btm3QZddoGSa6mOgfXWkkv+642bfUUldomgrbiLQGrPryb7DXLovPWQ==} + '@rollup/rollup-darwin-arm64@4.53.5': + resolution: {integrity: sha512-S87zZPBmRO6u1YXQLwpveZm4JfPpAa6oHBX7/ghSiGH3rz/KDgAu1rKdGutV+WUI6tKDMbaBJomhnT30Y2t4VQ==} cpu: [arm64] os: [darwin] - '@rollup/rollup-darwin-arm64@4.53.2': - resolution: {integrity: sha512-A6s4gJpomNBtJ2yioj8bflM2oogDwzUiMl2yNJ2v9E7++sHrSrsQ29fOfn5DM/iCzpWcebNYEdXpaK4tr2RhfQ==} - cpu: [arm64] - os: [darwin] - - '@rollup/rollup-darwin-x64@4.53.1': - resolution: {integrity: sha512-is3r/k4vig2Gt8mKtTlzzyaSQ+hd87kDxiN3uDSDwggJLUV56Umli6OoL+/YZa/KvtdrdyNfMKHzL/P4siOOmg==} + '@rollup/rollup-darwin-x64@4.53.5': + resolution: {integrity: sha512-YTbnsAaHo6VrAczISxgpTva8EkfQus0VPEVJCEaboHtZRIb6h6j0BNxRBOwnDciFTZLDPW5r+ZBmhL/+YpTZgA==} cpu: [x64] os: [darwin] - '@rollup/rollup-darwin-x64@4.53.2': - resolution: {integrity: sha512-e6XqVmXlHrBlG56obu9gDRPW3O3hLxpwHpLsBJvuI8qqnsrtSZ9ERoWUXtPOkY8c78WghyPHZdmPhHLWNdAGEw==} - cpu: [x64] - os: [darwin] - - '@rollup/rollup-freebsd-arm64@4.53.1': - resolution: {integrity: sha512-QJ1ksgp/bDJkZB4daldVmHaEQkG4r8PUXitCOC2WRmRaSaHx5RwPoI3DHVfXKwDkB+Sk6auFI/+JHacTekPRSw==} + '@rollup/rollup-freebsd-arm64@4.53.5': + resolution: {integrity: sha512-1T8eY2J8rKJWzaznV7zedfdhD1BqVs1iqILhmHDq/bqCUZsrMt+j8VCTHhP0vdfbHK3e1IQ7VYx3jlKqwlf+vw==} cpu: [arm64] os: [freebsd] - '@rollup/rollup-freebsd-arm64@4.53.2': - resolution: {integrity: sha512-v0E9lJW8VsrwPux5Qe5CwmH/CF/2mQs6xU1MF3nmUxmZUCHazCjLgYvToOk+YuuUqLQBio1qkkREhxhc656ViA==} - cpu: [arm64] - os: [freebsd] - - '@rollup/rollup-freebsd-x64@4.53.1': - resolution: {integrity: sha512-J6ma5xgAzvqsnU6a0+jgGX/gvoGokqpkx6zY4cWizRrm0ffhHDpJKQgC8dtDb3+MqfZDIqs64REbfHDMzxLMqQ==} + '@rollup/rollup-freebsd-x64@4.53.5': + resolution: {integrity: sha512-sHTiuXyBJApxRn+VFMaw1U+Qsz4kcNlxQ742snICYPrY+DDL8/ZbaC4DVIB7vgZmp3jiDaKA0WpBdP0aqPJoBQ==} cpu: [x64] os: [freebsd] - '@rollup/rollup-freebsd-x64@4.53.2': - resolution: {integrity: sha512-ClAmAPx3ZCHtp6ysl4XEhWU69GUB1D+s7G9YjHGhIGCSrsg00nEGRRZHmINYxkdoJehde8VIsDC5t9C0gb6yqA==} - cpu: [x64] - os: [freebsd] - - '@rollup/rollup-linux-arm-gnueabihf@4.53.1': - resolution: {integrity: sha512-JzWRR41o2U3/KMNKRuZNsDUAcAVUYhsPuMlx5RUldw0E4lvSIXFUwejtYz1HJXohUmqs/M6BBJAUBzKXZVddbg==} - cpu: [arm] - os: [linux] - libc: [glibc] - - '@rollup/rollup-linux-arm-gnueabihf@4.53.2': - resolution: {integrity: sha512-EPlb95nUsz6Dd9Qy13fI5kUPXNSljaG9FiJ4YUGU1O/Q77i5DYFW5KR8g1OzTcdZUqQQ1KdDqsTohdFVwCwjqg==} + '@rollup/rollup-linux-arm-gnueabihf@4.53.5': + resolution: {integrity: sha512-dV3T9MyAf0w8zPVLVBptVlzaXxka6xg1f16VAQmjg+4KMSTWDvhimI/Y6mp8oHwNrmnmVl9XxJ/w/mO4uIQONA==} cpu: [arm] os: [linux] libc: [glibc] - '@rollup/rollup-linux-arm-musleabihf@4.53.1': - resolution: {integrity: sha512-L8kRIrnfMrEoHLHtHn+4uYA52fiLDEDyezgxZtGUTiII/yb04Krq+vk3P2Try+Vya9LeCE9ZHU8CXD6J9EhzHQ==} - cpu: [arm] - os: [linux] - libc: [musl] - - '@rollup/rollup-linux-arm-musleabihf@4.53.2': - resolution: {integrity: sha512-BOmnVW+khAUX+YZvNfa0tGTEMVVEerOxN0pDk2E6N6DsEIa2Ctj48FOMfNDdrwinocKaC7YXUZ1pHlKpnkja/Q==} + '@rollup/rollup-linux-arm-musleabihf@4.53.5': + resolution: {integrity: sha512-wIGYC1x/hyjP+KAu9+ewDI+fi5XSNiUi9Bvg6KGAh2TsNMA3tSEs+Sh6jJ/r4BV/bx/CyWu2ue9kDnIdRyafcQ==} cpu: [arm] os: [linux] libc: [musl] - '@rollup/rollup-linux-arm64-gnu@4.53.1': - resolution: {integrity: sha512-ysAc0MFRV+WtQ8li8hi3EoFi7us6d1UzaS/+Dp7FYZfg3NdDljGMoVyiIp6Ucz7uhlYDBZ/zt6XI0YEZbUO11Q==} - cpu: [arm64] - os: [linux] - libc: [glibc] - - '@rollup/rollup-linux-arm64-gnu@4.53.2': - resolution: {integrity: sha512-Xt2byDZ+6OVNuREgBXr4+CZDJtrVso5woFtpKdGPhpTPHcNG7D8YXeQzpNbFRxzTVqJf7kvPMCub/pcGUWgBjA==} + '@rollup/rollup-linux-arm64-gnu@4.53.5': + resolution: {integrity: sha512-Y+qVA0D9d0y2FRNiG9oM3Hut/DgODZbU9I8pLLPwAsU0tUKZ49cyV1tzmB/qRbSzGvY8lpgGkJuMyuhH7Ma+Vg==} cpu: [arm64] os: [linux] libc: [glibc] - '@rollup/rollup-linux-arm64-musl@4.53.1': - resolution: {integrity: sha512-UV6l9MJpDbDZZ/fJvqNcvO1PcivGEf1AvKuTcHoLjVZVFeAMygnamCTDikCVMRnA+qJe+B3pSbgX2+lBMqgBhA==} - cpu: [arm64] - os: [linux] - libc: [musl] - - '@rollup/rollup-linux-arm64-musl@4.53.2': - resolution: {integrity: sha512-+LdZSldy/I9N8+klim/Y1HsKbJ3BbInHav5qE9Iy77dtHC/pibw1SR/fXlWyAk0ThnpRKoODwnAuSjqxFRDHUQ==} + '@rollup/rollup-linux-arm64-musl@4.53.5': + resolution: {integrity: sha512-juaC4bEgJsyFVfqhtGLz8mbopaWD+WeSOYr5E16y+1of6KQjc0BpwZLuxkClqY1i8sco+MdyoXPNiCkQou09+g==} cpu: [arm64] os: [linux] libc: [musl] - '@rollup/rollup-linux-loong64-gnu@4.53.1': - resolution: {integrity: sha512-UDUtelEprkA85g95Q+nj3Xf0M4hHa4DiJ+3P3h4BuGliY4NReYYqwlc0Y8ICLjN4+uIgCEvaygYlpf0hUj90Yg==} - cpu: [loong64] - os: [linux] - libc: [glibc] - - '@rollup/rollup-linux-loong64-gnu@4.53.2': - resolution: {integrity: sha512-8ms8sjmyc1jWJS6WdNSA23rEfdjWB30LH8Wqj0Cqvv7qSHnvw6kgMMXRdop6hkmGPlyYBdRPkjJnj3KCUHV/uQ==} + '@rollup/rollup-linux-loong64-gnu@4.53.5': + resolution: {integrity: sha512-rIEC0hZ17A42iXtHX+EPJVL/CakHo+tT7W0pbzdAGuWOt2jxDFh7A/lRhsNHBcqL4T36+UiAgwO8pbmn3dE8wA==} cpu: [loong64] os: [linux] libc: [glibc] - '@rollup/rollup-linux-ppc64-gnu@4.53.1': - resolution: {integrity: sha512-vrRn+BYhEtNOte/zbc2wAUQReJXxEx2URfTol6OEfY2zFEUK92pkFBSXRylDM7aHi+YqEPJt9/ABYzmcrS4SgQ==} - cpu: [ppc64] - os: [linux] - libc: [glibc] - - '@rollup/rollup-linux-ppc64-gnu@4.53.2': - resolution: {integrity: sha512-3HRQLUQbpBDMmzoxPJYd3W6vrVHOo2cVW8RUo87Xz0JPJcBLBr5kZ1pGcQAhdZgX9VV7NbGNipah1omKKe23/g==} + '@rollup/rollup-linux-ppc64-gnu@4.53.5': + resolution: {integrity: sha512-T7l409NhUE552RcAOcmJHj3xyZ2h7vMWzcwQI0hvn5tqHh3oSoclf9WgTl+0QqffWFG8MEVZZP1/OBglKZx52Q==} cpu: [ppc64] os: [linux] libc: [glibc] - '@rollup/rollup-linux-riscv64-gnu@4.53.1': - resolution: {integrity: sha512-gto/1CxHyi4A7YqZZNznQYrVlPSaodOBPKM+6xcDSCMVZN/Fzb4K+AIkNz/1yAYz9h3Ng+e2fY9H6bgawVq17w==} - cpu: [riscv64] - os: [linux] - libc: [glibc] - - '@rollup/rollup-linux-riscv64-gnu@4.53.2': - resolution: {integrity: sha512-fMjKi+ojnmIvhk34gZP94vjogXNNUKMEYs+EDaB/5TG/wUkoeua7p7VCHnE6T2Tx+iaghAqQX8teQzcvrYpaQA==} + '@rollup/rollup-linux-riscv64-gnu@4.53.5': + resolution: {integrity: sha512-7OK5/GhxbnrMcxIFoYfhV/TkknarkYC1hqUw1wU2xUN3TVRLNT5FmBv4KkheSG2xZ6IEbRAhTooTV2+R5Tk0lQ==} cpu: [riscv64] os: [linux] libc: [glibc] - '@rollup/rollup-linux-riscv64-musl@4.53.1': - resolution: {integrity: sha512-KZ6Vx7jAw3aLNjFR8eYVcQVdFa/cvBzDNRFM3z7XhNNunWjA03eUrEwJYPk0G8V7Gs08IThFKcAPS4WY/ybIrQ==} - cpu: [riscv64] - os: [linux] - libc: [musl] - - '@rollup/rollup-linux-riscv64-musl@4.53.2': - resolution: {integrity: sha512-XuGFGU+VwUUV5kLvoAdi0Wz5Xbh2SrjIxCtZj6Wq8MDp4bflb/+ThZsVxokM7n0pcbkEr2h5/pzqzDYI7cCgLQ==} + '@rollup/rollup-linux-riscv64-musl@4.53.5': + resolution: {integrity: sha512-GwuDBE/PsXaTa76lO5eLJTyr2k8QkPipAyOrs4V/KJufHCZBJ495VCGJol35grx9xryk4V+2zd3Ri+3v7NPh+w==} cpu: [riscv64] os: [linux] libc: [musl] - '@rollup/rollup-linux-s390x-gnu@4.53.1': - resolution: {integrity: sha512-HvEixy2s/rWNgpwyKpXJcHmE7om1M89hxBTBi9Fs6zVuLU4gOrEMQNbNsN/tBVIMbLyysz/iwNiGtMOpLAOlvA==} - cpu: [s390x] - os: [linux] - libc: [glibc] - - '@rollup/rollup-linux-s390x-gnu@4.53.2': - resolution: {integrity: sha512-w6yjZF0P+NGzWR3AXWX9zc0DNEGdtvykB03uhonSHMRa+oWA6novflo2WaJr6JZakG2ucsyb+rvhrKac6NIy+w==} + '@rollup/rollup-linux-s390x-gnu@4.53.5': + resolution: {integrity: sha512-IAE1Ziyr1qNfnmiQLHBURAD+eh/zH1pIeJjeShleII7Vj8kyEm2PF77o+lf3WTHDpNJcu4IXJxNO0Zluro8bOw==} cpu: [s390x] os: [linux] libc: [glibc] - '@rollup/rollup-linux-x64-gnu@4.53.1': - resolution: {integrity: sha512-E/n8x2MSjAQgjj9IixO4UeEUeqXLtiA7pyoXCFYLuXpBA/t2hnbIdxHfA7kK9BFsYAoNU4st1rHYdldl8dTqGA==} - cpu: [x64] - os: [linux] - libc: [glibc] - - '@rollup/rollup-linux-x64-gnu@4.53.2': - resolution: {integrity: sha512-yo8d6tdfdeBArzC7T/PnHd7OypfI9cbuZzPnzLJIyKYFhAQ8SvlkKtKBMbXDxe1h03Rcr7u++nFS7tqXz87Gtw==} + '@rollup/rollup-linux-x64-gnu@4.53.5': + resolution: {integrity: sha512-Pg6E+oP7GvZ4XwgRJBuSXZjcqpIW3yCBhK4BcsANvb47qMvAbCjR6E+1a/U2WXz1JJxp9/4Dno3/iSJLcm5auw==} cpu: [x64] os: [linux] libc: [glibc] - '@rollup/rollup-linux-x64-musl@4.53.1': - resolution: {integrity: sha512-IhJ087PbLOQXCN6Ui/3FUkI9pWNZe/Z7rEIVOzMsOs1/HSAECCvSZ7PkIbkNqL/AZn6WbZvnoVZw/qwqYMo4/w==} - cpu: [x64] - os: [linux] - libc: [musl] - - '@rollup/rollup-linux-x64-musl@4.53.2': - resolution: {integrity: sha512-ah59c1YkCxKExPP8O9PwOvs+XRLKwh/mV+3YdKqQ5AMQ0r4M4ZDuOrpWkUaqO7fzAHdINzV9tEVu8vNw48z0lA==} + '@rollup/rollup-linux-x64-musl@4.53.5': + resolution: {integrity: sha512-txGtluxDKTxaMDzUduGP0wdfng24y1rygUMnmlUJ88fzCCULCLn7oE5kb2+tRB+MWq1QDZT6ObT5RrR8HFRKqg==} cpu: [x64] os: [linux] libc: [musl] - '@rollup/rollup-openharmony-arm64@4.53.1': - resolution: {integrity: sha512-0++oPNgLJHBblreu0SFM7b3mAsBJBTY0Ksrmu9N6ZVrPiTkRgda52mWR7TKhHAsUb9noCjFvAw9l6ZO1yzaVbA==} - cpu: [arm64] - os: [openharmony] - - '@rollup/rollup-openharmony-arm64@4.53.2': - resolution: {integrity: sha512-4VEd19Wmhr+Zy7hbUsFZ6YXEiP48hE//KPLCSVNY5RMGX2/7HZ+QkN55a3atM1C/BZCGIgqN+xrVgtdak2S9+A==} + '@rollup/rollup-openharmony-arm64@4.53.5': + resolution: {integrity: sha512-3DFiLPnTxiOQV993fMc+KO8zXHTcIjgaInrqlG8zDp1TlhYl6WgrOHuJkJQ6M8zHEcntSJsUp1XFZSY8C1DYbg==} cpu: [arm64] os: [openharmony] - '@rollup/rollup-win32-arm64-msvc@4.53.1': - resolution: {integrity: sha512-VJXivz61c5uVdbmitLkDlbcTk9Or43YC2QVLRkqp86QoeFSqI81bNgjhttqhKNMKnQMWnecOCm7lZz4s+WLGpQ==} + '@rollup/rollup-win32-arm64-msvc@4.53.5': + resolution: {integrity: sha512-nggc/wPpNTgjGg75hu+Q/3i32R00Lq1B6N1DO7MCU340MRKL3WZJMjA9U4K4gzy3dkZPXm9E1Nc81FItBVGRlA==} cpu: [arm64] os: [win32] - '@rollup/rollup-win32-arm64-msvc@4.53.2': - resolution: {integrity: sha512-IlbHFYc/pQCgew/d5fslcy1KEaYVCJ44G8pajugd8VoOEI8ODhtb/j8XMhLpwHCMB3yk2J07ctup10gpw2nyMA==} - cpu: [arm64] - os: [win32] - - '@rollup/rollup-win32-ia32-msvc@4.53.1': - resolution: {integrity: sha512-NmZPVTUOitCXUH6erJDzTQ/jotYw4CnkMDjCYRxNHVD9bNyfrGoIse684F9okwzKCV4AIHRbUkeTBc9F2OOH5Q==} - cpu: [ia32] - os: [win32] - - '@rollup/rollup-win32-ia32-msvc@4.53.2': - resolution: {integrity: sha512-lNlPEGgdUfSzdCWU176ku/dQRnA7W+Gp8d+cWv73jYrb8uT7HTVVxq62DUYxjbaByuf1Yk0RIIAbDzp+CnOTFg==} + '@rollup/rollup-win32-ia32-msvc@4.53.5': + resolution: {integrity: sha512-U/54pTbdQpPLBdEzCT6NBCFAfSZMvmjr0twhnD9f4EIvlm9wy3jjQ38yQj1AGznrNO65EWQMgm/QUjuIVrYF9w==} cpu: [ia32] os: [win32] - '@rollup/rollup-win32-x64-gnu@4.53.1': - resolution: {integrity: sha512-2SNj7COIdAf6yliSpLdLG8BEsp5lgzRehgfkP0Av8zKfQFKku6JcvbobvHASPJu4f3BFxej5g+HuQPvqPhHvpQ==} - cpu: [x64] - os: [win32] - - '@rollup/rollup-win32-x64-gnu@4.53.2': - resolution: {integrity: sha512-S6YojNVrHybQis2lYov1sd+uj7K0Q05NxHcGktuMMdIQ2VixGwAfbJ23NnlvvVV1bdpR2m5MsNBViHJKcA4ADw==} - cpu: [x64] - os: [win32] - - '@rollup/rollup-win32-x64-msvc@4.53.1': - resolution: {integrity: sha512-rLarc1Ofcs3DHtgSzFO31pZsCh8g05R2azN1q3fF+H423Co87My0R+tazOEvYVKXSLh8C4LerMK41/K7wlklcg==} + '@rollup/rollup-win32-x64-gnu@4.53.5': + resolution: {integrity: sha512-2NqKgZSuLH9SXBBV2dWNRCZmocgSOx8OJSdpRaEcRlIfX8YrKxUT6z0F1NpvDVhOsl190UFTRh2F2WDWWCYp3A==} cpu: [x64] os: [win32] - '@rollup/rollup-win32-x64-msvc@4.53.2': - resolution: {integrity: sha512-k+/Rkcyx//P6fetPoLMb8pBeqJBNGx81uuf7iljX9++yNBVRDQgD04L+SVXmXmh5ZP4/WOp4mWF0kmi06PW2tA==} + '@rollup/rollup-win32-x64-msvc@4.53.5': + resolution: {integrity: sha512-JRpZUhCfhZ4keB5v0fe02gQJy05GqboPOaxvjugW04RLSYYoB/9t2lx2u/tMs/Na/1NXfY8QYjgRljRpN+MjTQ==} cpu: [x64] os: [win32] - '@rollup/wasm-node@4.52.5': - resolution: {integrity: sha512-ldY4tEzSMBHNwB8TfRpi7RRRjjyfKlwjdebw5pS1lu0xaY3g4RDc6ople2wEYulVOKVeH7ZJwRx0iw4pGtjMHg==} + '@rollup/wasm-node@4.54.0': + resolution: {integrity: sha512-CeEdHzNY+ZIR6NWpIOiJuCrr6tTK7cRGeOf6GYg5f73+UwJLqn5a4d5Ovf/hOWDyHM1KcySbxHQESJ9krhe0+A==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true @@ -3511,24 +3312,24 @@ packages: resolution: {integrity: sha512-NwCl5Y0V6Di0NexvkTqdoVfmjTaQwoLM236r89KEojGmq/jMls8S+zb7yOwAPdXvbwfKDlP+lmXgAL4vKSQT+A==} engines: {node: ^20.17.0 || >=22.9.0} - '@sigstore/core@3.0.0': - resolution: {integrity: sha512-NgbJ+aW9gQl/25+GIEGYcCyi8M+ng2/5X04BMuIgoDfgvp18vDcoNHOQjQsG9418HGNYRxG3vfEXaR1ayD37gg==} + '@sigstore/core@3.1.0': + resolution: {integrity: sha512-o5cw1QYhNQ9IroioJxpzexmPjfCe7gzafd2RY3qnMpxr4ZEja+Jad/U8sgFpaue6bOaF+z7RVkyKVV44FN+N8A==} engines: {node: ^20.17.0 || >=22.9.0} '@sigstore/protobuf-specs@0.5.0': resolution: {integrity: sha512-MM8XIwUjN2bwvCg1QvrMtbBmpcSHrkhFSCu1D11NyPvDQ25HEc4oG5/OcQfd/Tlf/OxmKWERDj0zGE23jQaMwA==} engines: {node: ^18.17.0 || >=20.5.0} - '@sigstore/sign@4.0.1': - resolution: {integrity: sha512-KFNGy01gx9Y3IBPG/CergxR9RZpN43N+lt3EozEfeoyqm8vEiLxwRl3ZO5sPx3Obv1ix/p7FWOlPc2Jgwfp9PA==} + '@sigstore/sign@4.1.0': + resolution: {integrity: sha512-Vx1RmLxLGnSUqx/o5/VsCjkuN5L7y+vxEEwawvc7u+6WtX2W4GNa7b9HEjmcRWohw/d6BpATXmvOwc78m+Swdg==} engines: {node: ^20.17.0 || >=22.9.0} - '@sigstore/tuf@4.0.0': - resolution: {integrity: sha512-0QFuWDHOQmz7t66gfpfNO6aEjoFrdhkJaej/AOqb4kqWZVbPWFZifXZzkxyQBB1OwTbkhdT3LNpMFxwkTvf+2w==} + '@sigstore/tuf@4.0.1': + resolution: {integrity: sha512-OPZBg8y5Vc9yZjmWCHrlWPMBqW5yd8+wFNl+thMdtcWz3vjVSoJQutF8YkrzI0SLGnkuFof4HSsWUhXrf219Lw==} engines: {node: ^20.17.0 || >=22.9.0} - '@sigstore/verify@3.0.0': - resolution: {integrity: sha512-moXtHH33AobOhTZF8xcX1MpOFqdvfCk7v6+teJL8zymBiDXwEsQH6XG9HGx2VIxnJZNm4cNSzflTLDnQLmIdmw==} + '@sigstore/verify@3.1.0': + resolution: {integrity: sha512-mNe0Iigql08YupSOGv197YdHpPPr+EzDZmfCgMc7RPNaZTw5aLN01nBl6CHJOh3BGtnMIj83EeN4butBchc8Ag==} engines: {node: ^20.17.0 || >=22.9.0} '@sindresorhus/is@4.6.0': @@ -3538,11 +3339,11 @@ packages: '@socket.io/component-emitter@3.1.2': resolution: {integrity: sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==} - '@standard-schema/spec@1.0.0': - resolution: {integrity: sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA==} + '@standard-schema/spec@1.1.0': + resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==} - '@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' @@ -3558,8 +3359,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==} @@ -3574,8 +3375,8 @@ packages: resolution: {integrity: sha512-yVtV8zsdo8qFHe+/3kw81dSLyF7D576A5cCFCi4X7B39tWT7SekaEFUnvnWJHz+9qO7qJTah1JbrDjWKqFtdWA==} engines: {node: ^16.14.0 || >=18.0.0} - '@tufjs/models@4.0.0': - resolution: {integrity: sha512-h5x5ga/hh82COe+GoD4+gKUeV4T3iaYOxqLt41GRKApinPI7DMidhCmNVTjKfhCWFJIGXaFJee07XczdT4jdZQ==} + '@tufjs/models@4.1.0': + resolution: {integrity: sha512-Y8cK9aggNRsqJVaKUlEYs4s7CvQ1b1ta2DVPyAimb0I2qhzjNk+A+mxvll/klL0RlfuIUei8BF7YWiua4kQqww==} engines: {node: ^20.17.0 || >=22.9.0} '@tybys/wasm-util@0.10.1': @@ -3674,8 +3475,8 @@ packages: '@types/express@4.17.25': resolution: {integrity: sha512-dVd04UKsfpINUnK0yBoYHDF3xu7xVH4BuDotC/xGuycx4CgbP48X/KF/586bcObxT0HENHXEU8Nqtu6NR+eKhw==} - '@types/express@5.0.5': - resolution: {integrity: sha512-LuIQOcb6UmnF7C1PCFmEU1u2hmiHL43fgFQX67sN3H4Z+0Yk0Neo++mFsBjhOAuLzvlQeqAAkeDOZrJs9rzumQ==} + '@types/express@5.0.6': + resolution: {integrity: sha512-sKYVuV7Sv9fbPIt/442koC7+IIwK5olP1KWeD88e/idgoJqDm3JV/YUiPwkoKK92ylff2MGxSz1CSjsXelx0YA==} '@types/folder-hash@4.0.4': resolution: {integrity: sha512-c+PwHm51Dw3fXM8SDK+93PO3oXdk4XNouCCvV67lj4aijRkZz5g67myk+9wqWWnyv3go6q96hT6ywcd3XtoZiQ==} @@ -3710,8 +3511,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==} @@ -3737,8 +3538,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==} @@ -3752,11 +3553,11 @@ packages: '@types/node-forge@1.3.14': resolution: {integrity: sha512-mhVF2BnD4BO+jtOp7z1CdzaK4mbuK0LLQYAvdOLqHTavxFNq4zA1EmYkpnFjP8HOUzedfQkRnp0E2ulSAYSzAw==} - '@types/node@22.19.0': - resolution: {integrity: sha512-xpr/lmLPQEj+TUnHmR+Ab91/glhJvsqcjB+yY0Ix9GO70H6Lb4FHH5GeqdOE5btAx7eIMwuHkp4H2MSkLcqWbA==} + '@types/node@22.19.3': + resolution: {integrity: sha512-1N9SBnWYOJTrNZCdh/yJE+t910Y128BoyY+zBLWhL3r0TYzlTmFdXrPwHL9DyFZmlEXNQQolTZh3KHV31QDhyA==} - '@types/node@24.10.0': - resolution: {integrity: sha512-qzQZRBqkFsYyaSWXuEHc2WR9c0a0CXwiE5FWUvn7ZM+vdy1uZLfCunD38UzhuB7YN/J11ndbDBcTmOdxJo9Q7A==} + '@types/node@24.10.4': + resolution: {integrity: sha512-vnDVpYPMzs4wunl27jHrfmwojOGKya0xyM3sH+UE5iv5uPS6vX7UIoh6m+vQc5LGBq52HBKPIn/zcSZVzeDEZg==} '@types/npm-package-arg@6.1.4': resolution: {integrity: sha512-vDgdbMy2QXHnAruzlv68pUtXCjmqUk3WrBAsRboRovsOmxbfn/WiYCjmecyKjGztnMps5dWp4Uq2prp+Ilo17Q==} @@ -3836,8 +3637,11 @@ packages: '@types/stack-trace@0.0.33': resolution: {integrity: sha512-O7in6531Bbvlb2KEsJ0dq0CHZvc3iWSR5ZYMtvGgnHA56VgriAN/AU2LorfmcvAl2xc9N5fbCTRyMRRl8nd74g==} - '@types/watchpack@2.4.4': - resolution: {integrity: sha512-SbuSavsPxfOPZwVHBgQUVuzYBe6+8KL7dwiJLXaj5rmv3DxktOMwX5WP1J6UontwUbewjVoc7pCgZvqy6rPn+A==} + '@types/tar-stream@3.1.4': + resolution: {integrity: sha512-921gW0+g29mCJX0fRvqeHzBlE/XclDaAG0Ousy1LCghsOhvaKacDeRGEVzQP9IPfKn8Vysy7FEXAIxycpc/CMg==} + + '@types/watchpack@2.4.5': + resolution: {integrity: sha512-8CarnGOIYYRL342jwQyHrGwz4vCD3y5uwwYmzQVzT2Z24DqSd6wwBva6m0eNJX4S5pVmrx9xUEbOsOoqBVhWsg==} '@types/which@3.0.4': resolution: {integrity: sha512-liyfuo/106JdlgSchJzXEQCVArk0CvevqPote8F8HgWgJ3dRCcTHgJIsLDuee0kxk/mhbInzIZk3QWSZJ8R+2w==} @@ -3851,8 +3655,8 @@ packages: '@types/yargs-parser@21.0.3': resolution: {integrity: sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==} - '@types/yargs@17.0.34': - resolution: {integrity: sha512-KExbHVa92aJpw9WDQvzBaGVE2/Pz+pLZQloT2hjL8IqsZnV62rlPOYvNnLmf/L2dyllfVUOVBj64M0z/46eR2A==} + '@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==} @@ -3860,83 +3664,79 @@ packages: '@types/yauzl@2.10.3': resolution: {integrity: sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==} - '@typescript-eslint/eslint-plugin@8.46.4': - resolution: {integrity: sha512-R48VhmTJqplNyDxCyqqVkFSZIx1qX6PzwqgcXn1olLrzxcSBDlOsbtcnQuQhNtnNiJ4Xe5gREI1foajYaYU2Vg==} + '@typescript-eslint/eslint-plugin@8.50.0': + resolution: {integrity: sha512-O7QnmOXYKVtPrfYzMolrCTfkezCJS9+ljLdKW/+DCvRsc3UAz+sbH6Xcsv7p30+0OwUbeWfUDAQE0vpabZ3QLg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - '@typescript-eslint/parser': ^8.46.4 + '@typescript-eslint/parser': ^8.50.0 eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/parser@8.46.4': - resolution: {integrity: sha512-tK3GPFWbirvNgsNKto+UmB/cRtn6TZfyw0D6IKrW55n6Vbs7KJoZtI//kpTKzE/DUmmnAFD8/Ca46s7Obs92/w==} + '@typescript-eslint/parser@8.50.0': + resolution: {integrity: sha512-6/cmF2piao+f6wSxUsJLZjck7OQsYyRtcOZS02k7XINSNlz93v6emM8WutDQSXnroG2xwYlEVHJI+cPA7CPM3Q==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/project-service@8.46.4': - resolution: {integrity: sha512-nPiRSKuvtTN+no/2N1kt2tUh/HoFzeEgOm9fQ6XQk4/ApGqjx0zFIIaLJ6wooR1HIoozvj2j6vTi/1fgAz7UYQ==} + '@typescript-eslint/project-service@8.50.0': + resolution: {integrity: sha512-Cg/nQcL1BcoTijEWyx4mkVC56r8dj44bFDvBdygifuS20f3OZCHmFbjF34DPSi07kwlFvqfv/xOLnJ5DquxSGQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/scope-manager@8.46.4': - resolution: {integrity: sha512-tMDbLGXb1wC+McN1M6QeDx7P7c0UWO5z9CXqp7J8E+xGcJuUuevWKxuG8j41FoweS3+L41SkyKKkia16jpX7CA==} + '@typescript-eslint/scope-manager@8.50.0': + resolution: {integrity: sha512-xCwfuCZjhIqy7+HKxBLrDVT5q/iq7XBVBXLn57RTIIpelLtEIZHXAF/Upa3+gaCpeV1NNS5Z9A+ID6jn50VD4A==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/tsconfig-utils@8.46.4': - resolution: {integrity: sha512-+/XqaZPIAk6Cjg7NWgSGe27X4zMGqrFqZ8atJsX3CWxH/jACqWnrWI68h7nHQld0y+k9eTTjb9r+KU4twLoo9A==} + '@typescript-eslint/tsconfig-utils@8.50.0': + resolution: {integrity: sha512-vxd3G/ybKTSlm31MOA96gqvrRGv9RJ7LGtZCn2Vrc5htA0zCDvcMqUkifcjrWNNKXHUU3WCkYOzzVSFBd0wa2w==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/type-utils@8.46.4': - resolution: {integrity: sha512-V4QC8h3fdT5Wro6vANk6eojqfbv5bpwHuMsBcJUJkqs2z5XnYhJzyz9Y02eUmF9u3PgXEUiOt4w4KHR3P+z0PQ==} + '@typescript-eslint/type-utils@8.50.0': + resolution: {integrity: sha512-7OciHT2lKCewR0mFoBrvZJ4AXTMe/sYOe87289WAViOocEmDjjv8MvIOT2XESuKj9jp8u3SZYUSh89QA4S1kQw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.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.46.4': - resolution: {integrity: sha512-USjyxm3gQEePdUwJBFjjGNG18xY9A2grDVGuk7/9AkjIF1L+ZrVnwR5VAU5JXtUnBL/Nwt3H31KlRDaksnM7/w==} + '@typescript-eslint/types@8.50.0': + resolution: {integrity: sha512-iX1mgmGrXdANhhITbpp2QQM2fGehBse9LbTf0sidWK6yg/NE+uhV5dfU1g6EYPlcReYmkE9QLPq/2irKAmtS9w==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/typescript-estree@8.46.4': - resolution: {integrity: sha512-7oV2qEOr1d4NWNmpXLR35LvCfOkTNymY9oyW+lUHkmCno7aOmIf/hMaydnJBUTBMRCOGZh8YjkFOc8dadEoNGA==} + '@typescript-eslint/typescript-estree@8.50.0': + resolution: {integrity: sha512-W7SVAGBR/IX7zm1t70Yujpbk+zdPq/u4soeFSknWFdXIFuWsBGBOUu/Tn/I6KHSKvSh91OiMuaSnYp3mtPt5IQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/utils@8.46.4': - resolution: {integrity: sha512-AbSv11fklGXV6T28dp2Me04Uw90R2iJ30g2bgLz529Koehrmkbs1r7paFqr1vPCZi7hHwYxYtxfyQMRC8QaVSg==} + '@typescript-eslint/utils@8.50.0': + resolution: {integrity: sha512-87KgUXET09CRjGCi2Ejxy3PULXna63/bMYv72tCAlDJC3Yqwln0HiFJ3VJMst2+mEtNtZu5oFvX4qJGjKsnAgg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/visitor-keys@8.46.4': - resolution: {integrity: sha512-/++5CYLQqsO9HFGLI7APrxBJYo+5OCMpViuhV8q5/Qa3o5mMrF//eQHks+PXcsAVaLdn817fMuS7zqoXNNZGaw==} + '@typescript-eslint/visitor-keys@8.50.0': + resolution: {integrity: sha512-Xzmnb58+Db78gT/CCj/PVCvK+zxbnsw6F+O1oheYszJbBSdEjVhQi3C/Xttzxgi/GLmpvOggRs1RFpiJ8+c34Q==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@verdaccio/auth@8.0.0-next-8.24': - resolution: {integrity: sha512-stRp0DdTTx3p6dnh2cKOPJZOhu6sZOf8evV2fpYtADYW0UyhhZwELBXukpa5WGQ3H3rWzsXSaccra+D7tB1LgA==} + '@verdaccio/auth@8.0.0-next-8.28': + resolution: {integrity: sha512-tM0URyKFq1dGFaoBN0whT1SDur1lBKG+pkQwxgJIXgH04DmefLCH63pn/K1iDDfLwqyxMewpmvMTBS390SSR+A==} engines: {node: '>=18'} - '@verdaccio/config@8.0.0-next-8.24': - resolution: {integrity: sha512-TRTVY6g2bH5V/1MQOXmdwOIuiT8i/tAtRX4T7FHwCutWTMfdFLgwy4e1VvTH8d1MwGRNyVin4O8PHVu5Xh4ouA==} + '@verdaccio/config@8.0.0-next-8.28': + resolution: {integrity: sha512-yl7lcSOzT9y7EgUald4bZXIKxhZdAPRvtDZwmsPL433bc+CInyriqYCo6L4fZgFxYAdj9iDzvZIJtCtsFc+8NA==} engines: {node: '>=18'} '@verdaccio/core@8.0.0-next-8.21': resolution: {integrity: sha512-n3Y8cqf84cwXxUUdTTfEJc8fV55PONPKijCt2YaC0jilb5qp1ieB3d4brqTOdCdXuwkmnG2uLCiGpUd/RuSW0Q==} engines: {node: '>=18'} - '@verdaccio/core@8.0.0-next-8.24': - resolution: {integrity: sha512-58Et0Mj562ergUd7galslWNsTseOHBCDkCIHokmoeWGX4+P46Aoa9+G0laFHyZxxfkuGkZXhO1WHysc9LzkfiA==} + '@verdaccio/core@8.0.0-next-8.28': + resolution: {integrity: sha512-W/wBvgF53CLr7m2zDV4QnHGYh0M/D3HTm5OuTxIjeclwDZGH2UHSdiI2BFFhvW7zt6MQVjfTQdgtcH4cmLGXhQ==} engines: {node: '>=18'} '@verdaccio/file-locking@10.3.1': @@ -3947,59 +3747,59 @@ packages: resolution: {integrity: sha512-F6xQWvsZnEyGjugrYfe+D/ChSVudXmBFWi8xuTIX6PAdp7dk9x9biOGQFW8O3GSAK8UhJ6WlRisQKJeYRa6vWQ==} engines: {node: '>=18'} - '@verdaccio/hooks@8.0.0-next-8.24': - resolution: {integrity: sha512-jrBHk51rsANI47YbHCFKprBPelLDklwKhkMINEYnFOQwuB3HEcupd6hGNDaj64sRnZNoK2VuJH0cAWn0iqVErg==} + '@verdaccio/hooks@8.0.0-next-8.28': + resolution: {integrity: sha512-K+Cu9Pls8G2Wvu6pOYuelCTNBLpSOpmodJaPbUVv/kkhsTphgRiRENhOZGbiPhfVKehQeUCj9WiF8C7xzTf2Vg==} engines: {node: '>=18'} - '@verdaccio/loaders@8.0.0-next-8.14': - resolution: {integrity: sha512-cWrTTJ7HWjrzhXIVVXPHFUFTdzbRgvU5Xwte8O/JPMtrEAxtbjg18kCIdQwAcomB1S+BkffnnQJ8TPLymnuCrg==} + '@verdaccio/loaders@8.0.0-next-8.18': + resolution: {integrity: sha512-p8o/DST+TPLz1pTqnduYKMggNxYO5ET+3a4UXLArVDWbhqNnYFRmZVyIW8r0JFOuRwUKdpcJMkXefufMRm8B8g==} engines: {node: '>=18'} '@verdaccio/local-storage-legacy@11.1.1': resolution: {integrity: sha512-P6ahH2W6/KqfJFKP+Eid7P134FHDLNvHa+i8KVgRVBeo2/IXb6FEANpM1mCVNvPANu0LCAmNJBOXweoUKViaoA==} engines: {node: '>=18'} - '@verdaccio/logger-commons@8.0.0-next-8.24': - resolution: {integrity: sha512-gEBUajG1m93xG+FJ+9+Mxg3FC9tSnP0RHyrhb4gPZPqft7JpRkwjbqpjxASGzPyxdTJZwiAefr7aafXv0xMpJA==} + '@verdaccio/logger-commons@8.0.0-next-8.28': + resolution: {integrity: sha512-GlIpSCKC6uKR4D9BZFbCiiJHeAaZ/n8izYqMKqFV5RKOrdMaMmYKjVFLvjGDpd22AFnCvSUfgrKCX1FXe8ZFqw==} engines: {node: '>=18'} '@verdaccio/logger-prettify@8.0.0-next-8.4': resolution: {integrity: sha512-gjI/JW29fyalutn/X1PQ0iNuGvzeVWKXRmnLa7gXVKhdi4p37l/j7YZ7n44XVbbiLIKAK0pbavEg9Yr66QrYaA==} engines: {node: '>=18'} - '@verdaccio/logger@8.0.0-next-8.24': - resolution: {integrity: sha512-7/arkwQy2zI5W5z9zMf5wyWiZx18xbLultteNPWHkQv9CtoFtuK+TSY8rH7ITtCGoNCcB94jvvTYRylxAdaHEw==} + '@verdaccio/logger@8.0.0-next-8.28': + resolution: {integrity: sha512-Iss+5mUJSvkDIwOWv6lk9OZjHl9PKHXf4FwM/YI5a77B06r5LYvdpppKVwGxBFn8jFcyGBl97zz8uqz6uD/rhQ==} engines: {node: '>=18'} - '@verdaccio/middleware@8.0.0-next-8.24': - resolution: {integrity: sha512-LuFralbC8bxl2yQx9prKEMrNbxj8BkojcUDIa+jZZRnghaZrMm1yHqf5eLHCrBBIOM3m2thJ6Ux2UfBCQP4UKA==} + '@verdaccio/middleware@8.0.0-next-8.28': + resolution: {integrity: sha512-L2PL4JJQ6dKipNrA/weWTEw47ZUgiJrvKb7g/z3yuDR5O/C7AJ+mOxqsWww0kgq0cvQm+kOXMVY9Oq1g9a+Agw==} engines: {node: '>=18'} '@verdaccio/search-indexer@8.0.0-next-8.5': resolution: {integrity: sha512-0GC2tJKstbPg/W2PZl2yE+hoAxffD2ZWilEnEYSEo2e9UQpNIy2zg7KE/uMUq2P72Vf5EVfVzb8jdaH4KV4QeA==} engines: {node: '>=18'} - '@verdaccio/signature@8.0.0-next-8.16': - resolution: {integrity: sha512-JBIpoYJQFjo3ITTRjum1IjXxNrSYcPoBLZTPlt9OLL5Brd7s1fJkTBgs9tbcCRZPrek/XBIJ/cLFZZn8zkc/bQ==} + '@verdaccio/signature@8.0.0-next-8.20': + resolution: {integrity: sha512-1kwO+l7cLiDjXUwqVTvaKXvcI4C23u4cZCVnGGKXRcahrbVm7Hdh12wSIX5V9gV6O3VlWH458JxoimpPAo4Oxw==} engines: {node: '>=18'} '@verdaccio/streams@10.2.1': resolution: {integrity: sha512-OojIG/f7UYKxC4dYX8x5ax8QhRx1b8OYUAMz82rUottCuzrssX/4nn5QE7Ank0DUSX3C9l/HPthc4d9uKRJqJQ==} engines: {node: '>=12', npm: '>=5'} - '@verdaccio/tarball@13.0.0-next-8.24': - resolution: {integrity: sha512-rDz8gWukO7dcaWzMTr7wMvKPUsRHclVzZljyTERplpIX9NWGHRsMRO7NjoTIUWtDS0euMlRVDnR8QVnYDWl2uA==} + '@verdaccio/tarball@13.0.0-next-8.28': + resolution: {integrity: sha512-89FHelT4xsrBeAk6WYhhUR6cgpky1IAgBiN3VtWs2b3KA8XesZeG7G0UDygTVEe2O0etXYtrz9vlqZ9nYEaAGA==} engines: {node: '>=18'} - '@verdaccio/ui-theme@8.0.0-next-8.24': - resolution: {integrity: sha512-A8lMenzJmC0EioBjQ9+L2e8tv/iEB/jLJKH4WJjPYa8B1xlm/LLUuk2aBg7pYD1fPWuazvJiH/NAnkrA09541g==} + '@verdaccio/ui-theme@8.0.0-next-8.28': + resolution: {integrity: sha512-TzhdphchcvsI38UPP5hdNJh+LAFvRhYlfrtBuqlZy0BHeGM2i2MNioGl2il2NLVteqXAK9MqnuC5WGxCFXoDYw==} - '@verdaccio/url@13.0.0-next-8.24': - resolution: {integrity: sha512-zNHR9qgiDTXp+IuOtz925tzGteXGj18IZphvxo2apgz3cAeUpL/nnmp4ZScczpxWxE8sT/88xVYgJm+Ec8fNCA==} + '@verdaccio/url@13.0.0-next-8.28': + resolution: {integrity: sha512-42wA5LkXWpY42pONEHQhAC5h8nFXdDHChP22swOitTp1xzvAs+PmwUlrol7kMXMWO04skDCSSW1Q54eNkTx/rA==} engines: {node: '>=18'} - '@verdaccio/utils@8.1.0-next-8.24': - resolution: {integrity: sha512-2e54Z1J1+OPM0LCxjkJHgwFm8jESsCYaX6ARs3+29hjoI75uiSphxFI3Hrhr+67ho/7Mtul0oyakK6l18MN/Dg==} + '@verdaccio/utils@8.1.0-next-8.28': + resolution: {integrity: sha512-/AYNGafG9T90NPGsq6eDMuXx+41tlWfiYsCchgwz074GWEitZ2nAZQWWNvYvFVB2hXzord52muEVTWjgaZPOdQ==} engines: {node: '>=18'} '@vitejs/plugin-basic-ssl@2.1.0': @@ -4008,20 +3808,20 @@ packages: peerDependencies: vite: ^6.0.0 || ^7.0.0 - '@vitest/coverage-v8@4.0.8': - resolution: {integrity: sha512-wQgmtW6FtPNn4lWUXi8ZSYLpOIb92j3QCujxX3sQ81NTfQ/ORnE0HtK7Kqf2+7J9jeveMGyGyc4NWc5qy3rC4A==} + '@vitest/coverage-v8@4.0.15': + resolution: {integrity: sha512-FUJ+1RkpTFW7rQITdgTi93qOCWJobWhBirEPCeXh2SW2wsTlFxy51apDz5gzG+ZEYt/THvWeNmhdAoS9DTwpCw==} peerDependencies: - '@vitest/browser': 4.0.8 - vitest: 4.0.8 + '@vitest/browser': 4.0.15 + vitest: 4.0.15 peerDependenciesMeta: '@vitest/browser': optional: true - '@vitest/expect@4.0.8': - resolution: {integrity: sha512-Rv0eabdP/xjAHQGr8cjBm+NnLHNoL268lMDK85w2aAGLFoVKLd8QGnVon5lLtkXQCoYaNL0wg04EGnyKkkKhPA==} + '@vitest/expect@4.0.15': + resolution: {integrity: sha512-Gfyva9/GxPAWXIWjyGDli9O+waHDC0Q0jaLdFP1qPAUUfo1FEXPXUfUkp3eZA0sSq340vPycSyOlYUeM15Ft1w==} - '@vitest/mocker@4.0.8': - resolution: {integrity: sha512-9FRM3MZCedXH3+pIh+ME5Up2NBBHDq0wqwhOKkN4VnvCiKbVxddqH9mSGPZeawjd12pCOGnl+lo/ZGHt0/dQSg==} + '@vitest/mocker@4.0.15': + resolution: {integrity: sha512-CZ28GLfOEIFkvCFngN8Sfx5h+Se0zN+h4B7yOsPVCcgtiO7t5jt9xQh2E1UkFep+eb9fjyMfuC5gBypwb07fvQ==} peerDependencies: msw: ^2.4.9 vite: ^6.0.0 || ^7.0.0-0 @@ -4031,20 +3831,20 @@ packages: vite: optional: true - '@vitest/pretty-format@4.0.8': - resolution: {integrity: sha512-qRrjdRkINi9DaZHAimV+8ia9Gq6LeGz2CgIEmMLz3sBDYV53EsnLZbJMR1q84z1HZCMsf7s0orDgZn7ScXsZKg==} + '@vitest/pretty-format@4.0.15': + resolution: {integrity: sha512-SWdqR8vEv83WtZcrfLNqlqeQXlQLh2iilO1Wk1gv4eiHKjEzvgHb2OVc3mIPyhZE6F+CtfYjNlDJwP5MN6Km7A==} - '@vitest/runner@4.0.8': - resolution: {integrity: sha512-mdY8Sf1gsM8hKJUQfiPT3pn1n8RF4QBcJYFslgWh41JTfrK1cbqY8whpGCFzBl45LN028g0njLCYm0d7XxSaQQ==} + '@vitest/runner@4.0.15': + resolution: {integrity: sha512-+A+yMY8dGixUhHmNdPUxOh0la6uVzun86vAbuMT3hIDxMrAOmn5ILBHm8ajrqHE0t8R9T1dGnde1A5DTnmi3qw==} - '@vitest/snapshot@4.0.8': - resolution: {integrity: sha512-Nar9OTU03KGiubrIOFhcfHg8FYaRaNT+bh5VUlNz8stFhCZPNrJvmZkhsr1jtaYvuefYFwK2Hwrq026u4uPWCw==} + '@vitest/snapshot@4.0.15': + resolution: {integrity: sha512-A7Ob8EdFZJIBjLjeO0DZF4lqR6U7Ydi5/5LIZ0xcI+23lYlsYJAfGn8PrIWTYdZQRNnSRlzhg0zyGu37mVdy5g==} - '@vitest/spy@4.0.8': - resolution: {integrity: sha512-nvGVqUunyCgZH7kmo+Ord4WgZ7lN0sOULYXUOYuHr55dvg9YvMz3izfB189Pgp28w0vWFbEEfNc/c3VTrqrXeA==} + '@vitest/spy@4.0.15': + resolution: {integrity: sha512-+EIjOJmnY6mIfdXtE/bnozKEvTC4Uczg19yeZ2vtCz5Yyb0QQ31QWVQ8hswJ3Ysx/K2EqaNsVanjr//2+P3FHw==} - '@vitest/utils@4.0.8': - resolution: {integrity: sha512-pdk2phO5NDvEFfUTxcTP8RFYjVj/kfLSPIN5ebP2Mu9kcIMeAQTbknqcFEyBcC4z2pJlJI9aS5UQjcYfhmKAow==} + '@vitest/utils@4.0.15': + resolution: {integrity: sha512-HXjPW2w5dxhTD0dLwtYHDnelK3j8sR8cWIaLxr22evTyY6q8pRCjZSmhRWVjBaOVXChQd6AwMzi9pucorXCPZA==} '@web/browser-logs@0.4.1': resolution: {integrity: sha512-ypmMG+72ERm+LvP+loj9A64MTXvWMXHUOu773cPO4L1SV/VWg6xA9Pv7vkvkXQX+ItJtCJt+KQ+U6ui2HhSFUw==} @@ -4158,9 +3958,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==} @@ -4236,8 +4036,8 @@ packages: ajv@8.17.1: resolution: {integrity: sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==} - algoliasearch@5.43.0: - resolution: {integrity: sha512-hbkK41JsuGYhk+atBDxlcKxskjDCh3OOEDpdKZPtw+3zucBqhlojRG5e5KtCmByGyYvwZswVeaSWglgLn2fibg==} + algoliasearch@5.46.0: + resolution: {integrity: sha512-7ML6fa2K93FIfifG3GMWhDEwT5qQzPTmoHKCTvhzGEwdbQ4n0yYUWZlLYT75WllTGJCJtNUI0C1ybN4BCegqvg==} engines: {node: '>= 14.0.0'} ansi-colors@4.1.3: @@ -4248,8 +4048,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: @@ -4373,8 +4173,8 @@ packages: resolution: {integrity: sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w==} engines: {node: '>=4'} - ast-v8-to-istanbul@0.3.8: - resolution: {integrity: sha512-szgSZqUxI5T8mLKvS7WTjF9is+MVbOeLADU73IseOcrqhxr/VAvy6wfoVE39KnKzA7JRhjF5eUagNlHwvZPlKQ==} + ast-v8-to-istanbul@0.3.9: + resolution: {integrity: sha512-dSC6tJeOJxbZrPzPbv5mMd6CMiQ1ugaVXXPRad2fXUSsy1kstFn9XQWemV9VW7Y7kpxgQ/4WMoZfwdH8XSU48w==} astral-regex@2.0.0: resolution: {integrity: sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==} @@ -4401,8 +4201,8 @@ packages: resolution: {integrity: sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==} engines: {node: '>=8.0.0'} - autoprefixer@10.4.22: - resolution: {integrity: sha512-ARe0v/t9gO28Bznv6GgqARmVqcWOV3mfgUPn9becPHMiD3o9BwlRgaeccZnwTpZ7Zwqrm+c1sUSsMxIzQzc8Xg==} + autoprefixer@10.4.23: + resolution: {integrity: sha512-YYTXSFulfwytnjAPlw8QHncHJmlvFKtczb8InXaAx9Q0LbfDnfEYDE55omerIJKihhmU61Ft+cAOSzQVaBUmeA==} engines: {node: ^10 || ^12 || >=14} hasBin: true peerDependencies: @@ -4451,16 +4251,16 @@ packages: balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} - bare-events@2.8.1: - resolution: {integrity: sha512-oxSAxTS1hRfnyit2CL5QpAOS5ixfBjj6ex3yTNvXyY/kE719jQ/IjuESJBK2w5v4wwQRAHGseVJXx9QBYOtFGQ==} + 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.5.0: - resolution: {integrity: sha512-GljgCjeupKZJNetTqxKaQArLK10vpmK28or0+RwWjEl5Rk+/xG3wkpmkv+WrcBm3q1BwHKlnhXzR8O37kcvkXQ==} + bare-fs@4.5.2: + resolution: {integrity: sha512-veTnRzkb6aPHOvSKIOy60KzURfBdUflr5VReI+NSaPL6xf+XLdONQgZgpYvUuZLVQ8dCqxpBAudaOM1+KpAUxw==} engines: {bare: '>=1.16.0'} peerDependencies: bare-buffer: '*' @@ -4496,8 +4296,8 @@ packages: resolution: {integrity: sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==} engines: {node: ^4.5.0 || >= 5.9} - baseline-browser-mapping@2.8.23: - resolution: {integrity: sha512-616V5YX4bepJFzNyOfce5Fa8fDJMfoxzOIzDCZwaGL8MKVpFrXqfNUoIpRn9YMI5pXf/VKgzjB4htFMsFKKdiQ==} + baseline-browser-mapping@2.9.11: + resolution: {integrity: sha512-Sg0xJUNDU1sJNGdfGWhVHX0kkZ+HWcvmVymJbj6NSgZZmW/8S9Y2HQ5euytnIgakgxN6papOAWiwDo1ctFDcoQ==} hasBin: true basic-ftp@5.0.5: @@ -4548,8 +4348,12 @@ 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@1.20.4: + resolution: {integrity: sha512-ZTgYYLMOXY9qKU/57FAo8F+HA2dGX7bqGc71txDRC1rS4frdFI5R7NhluHxH6M0YItAP0sHB4uqAOcYKxO6uGA==} + engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} + + body-parser@2.2.1: + resolution: {integrity: sha512-nfDwkulwiZYQIGwxdy0RUmowMhKcFVcYXUU7m4QlKYim1rUtg83xm2yjZ40QjDuc291AJjjeSc9b++AWHSgSHw==} engines: {node: '>=18'} bonjour-service@1.3.0: @@ -4586,8 +4390,8 @@ packages: browserify-zlib@0.1.4: resolution: {integrity: sha512-19OEpq7vWgsH6WkvkBJQDFvJS1uPcbFOQ4v9CU839dO+ZZXUZO6XpE6hNCqvlIIj+4fZvRiJ6DsAQ382GwiyTQ==} - browserslist@4.27.0: - resolution: {integrity: sha512-AXVQwdhot1eqLihwasPElhX2tAZiBjWdJ9i/Zcj2S6QYIjkx62OKSfnobkriB81C3l4w0rVy3Nt4jaTBltYEpw==} + browserslist@4.28.1: + resolution: {integrity: sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==} engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true @@ -4624,12 +4428,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: @@ -4668,17 +4468,14 @@ packages: resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==} engines: {node: '>=10'} - caniuse-lite@1.0.30001753: - resolution: {integrity: sha512-Bj5H35MD/ebaOV4iDLqPEtiliTN29qkGtEHCwawWn4cYm+bPJM2NsaP30vtZcnERClMzp52J4+aw2UNbK4o+zw==} - - caniuse-lite@1.0.30001754: - resolution: {integrity: sha512-x6OeBXueoAceOmotzx3PO4Zpt4rzpeIFsSr6AAePTZxSkXiYDUmpypEl7e2+8NCd9bD7bXjqyef8CJYPC1jfxg==} + caniuse-lite@1.0.30001761: + resolution: {integrity: sha512-JF9ptu1vP2coz98+5051jZ4PwQgd2ni8A+gYSN7EA7dPKIMf0pDlSUxhdmVOaV3/fYK5uWBkgSXJaRLr4+3A6g==} 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: @@ -4711,6 +4508,10 @@ packages: resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==} engines: {node: '>= 14.16.0'} + chokidar@5.0.0: + resolution: {integrity: sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==} + engines: {node: '>= 20.19.0'} + chownr@1.1.4: resolution: {integrity: sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==} @@ -4727,8 +4528,8 @@ packages: resolution: {integrity: sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==} engines: {node: '>=6.0'} - chromium-bidi@10.5.1: - resolution: {integrity: sha512-rlj6OyhKhVTnk4aENcUme3Jl9h+cq4oXu4AzBcvr8RMmT6BR4a3zSNT9dbIfXr9/BS6ibzRyDhowuw4n2GgzsQ==} + chromium-bidi@12.0.1: + resolution: {integrity: sha512-fGg+6jr0xjQhzpy5N4ErZxQ4wF7KLEvhGZXD6EgvZKDhu7iOhZXnZhcDxPJDcwTcrD48NPzOCo84RP2lv3Z+Cg==} peerDependencies: devtools-protocol: '*' @@ -4869,9 +4670,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==} @@ -4895,6 +4696,9 @@ packages: cookie-signature@1.0.6: resolution: {integrity: sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==} + cookie-signature@1.0.7: + resolution: {integrity: sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA==} + cookie-signature@1.2.2: resolution: {integrity: sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==} engines: {node: '>=6.6.0'} @@ -4920,8 +4724,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==} @@ -4983,8 +4787,8 @@ packages: engines: {node: '>=4'} hasBin: true - cssstyle@5.3.3: - resolution: {integrity: sha512-OytmFH+13/QXONJcC75QNdMtKpceNk3u8ThBjyyYjkEcy/ekBwR1mMAuNvi3gdBPW3N5TlCzQ0WZw8H0lN/bDw==} + cssstyle@5.3.5: + resolution: {integrity: sha512-GlsEptulso7Jg0VaOZ8BXQi3AkYM5BOJKEO/rjMidSCq70FkIC5y0eawrCXeYzxgt3OCf4Ls+eoxN+/05vN0Ag==} engines: {node: '>=20'} custom-event@1.0.1: @@ -5110,12 +4914,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: @@ -5200,8 +5004,8 @@ packages: devtools-protocol@0.0.1045489: resolution: {integrity: sha512-D+PTmWulkuQW4D1NTiCRCFxF7pQPn0hgp4YyX4wAQ6xYXKOadSWPR3ENGDQ47MW/Ewc9v2rpC/UEEGahgBYpSQ==} - devtools-protocol@0.0.1521046: - resolution: {integrity: sha512-vhE6eymDQSKWUXwwA37NtTTVEzjtGVfDr3pRbsWEQ5onH/Snp2c+2xZHWJJawG/0hCCJLRGt4xVtEVUVILol4w==} + devtools-protocol@0.0.1534754: + resolution: {integrity: sha512-26T91cV5dbOYnXdJi5qQHoTtUoNEqwkHcAyu/IKtjIAxiEqPMrDiRkDOPWVsGfNZGmlQVHQbZRSjD8sxagWVsQ==} di@0.0.1: resolution: {integrity: sha512-uJaamHkagcZtHPqCIHZxnFrXlunQXgBOsZSUOWwFw31QJCAbyTBoHMW75YOTur5ZNx8pIeAKgf6GWIgaqqiLhA==} @@ -5277,8 +5081,8 @@ packages: engines: {node: '>=0.10.0'} hasBin: true - electron-to-chromium@1.5.244: - resolution: {integrity: sha512-OszpBN7xZX4vWMPJwB9illkN/znA8M36GQqQxi6MNy9axWxhOfJyZZJtSLQCpEFLHP2xK33BiWx9aIuIEXVCcw==} + electron-to-chromium@1.5.267: + resolution: {integrity: sha512-0Drusm6MVRXSOJpGbaSVgcQsuB4hEkMpHXaVstcPmhu5LIedxs1xNK/nIxmQIU/RPC0+1/o0AVZfBTkTNJOdUw==} emoji-regex@10.6.0: resolution: {integrity: sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==} @@ -5318,8 +5122,8 @@ packages: resolution: {integrity: sha512-ZCkIjSYNDyGn0R6ewHDtXgns/Zre/NT6Agvq1/WobF7JXgFff4SeDroKiCO3fNJreU9YG429Sc81o4w5ok/W5g==} engines: {node: '>=10.2.0'} - enhanced-resolve@5.18.3: - resolution: {integrity: sha512-d4lC8xfavMeBjzGr2vECC3fsGXziXZQyJxD868h2M/mBI3PwAuODxAkLkq5HYuvrPYcUtiLzsTo8U3PgX3Ocww==} + enhanced-resolve@5.18.4: + resolution: {integrity: sha512-LgQMM4WXU3QI+SYgEc2liRgznaD5ojbmY3sb8LxyguVkIg5FxdpTkvk72te2R38/TGKxH634oLxXRGY6d7AP+Q==} engines: {node: '>=10.13.0'} ent@2.2.2: @@ -5360,8 +5164,8 @@ packages: errorstacks@2.4.1: resolution: {integrity: sha512-jE4i0SMYevwu/xxAuzhly/KTwtj0xDhbzB6m1xPImxTkw8wcCbgarOQPfCVMi5JKVyW7in29pNJCCJrry3Ynnw==} - es-abstract@1.24.0: - resolution: {integrity: sha512-WSzPgsdLtTcQwm4CROfS5ju2Wa1QQcVeT37jFjYzdFz1r9ahadC8B8/a4qxJxM+09F18iumCdRmlr96ZYkQvEg==} + es-abstract@1.24.1: + resolution: {integrity: sha512-zHXBLhP+QehSSbsS9Pt23Gg964240DPd6QCf8WpkqEXxQ7fhdZzYsocOr5u7apWonsS5EjZDmTF+/slGMyasvw==} engines: {node: '>= 0.4'} es-define-property@1.0.1: @@ -5375,6 +5179,9 @@ packages: es-module-lexer@1.7.0: resolution: {integrity: sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==} + es-module-lexer@2.0.0: + resolution: {integrity: sha512-5POEcUuZybH7IdmGsD8wlf0AI55wMecM9rVBTI/qEAy2c1kTOm3DjFYjrBdI2K3BaJjJYfYFeRtM0t9ssnRuxw==} + es-object-atoms@1.1.1: resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} engines: {node: '>= 0.4'} @@ -5397,18 +5204,13 @@ packages: es6-promisify@5.0.0: resolution: {integrity: sha512-C+d6UdsYDk0lMebHNR4S2NybQMMngAOnOwYBQjTOiv0MkoJMP0Myw2mgpDLBcpfCmRLxyFqYhS/CfOENq4SJhQ==} - esbuild-wasm@0.27.0: - resolution: {integrity: sha512-4XpLDOY4sOzqgiezPXDkHTn25cBA1MKN5OTotehoFR7/wTpSYCK76OA8rQfpiuz12G27JpGxxdh+/D9GcNl61w==} - engines: {node: '>=18'} - hasBin: true - - esbuild@0.25.12: - resolution: {integrity: sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==} + esbuild-wasm@0.27.1: + resolution: {integrity: sha512-NjueSyuuMjX6F/mnqQ8g4rkwAFTct7JT/A/oXjXhGTFbWiTm3zU59BMulOrT+KuCHj+oShTBARQCxCDDvVxwFA==} engines: {node: '>=18'} hasBin: true - esbuild@0.27.0: - resolution: {integrity: sha512-jd0f4NHbD6cALCyGElNpGAOtWxSq46l9X/sWB0Nzd5er4Kz2YTm+Vl0qKFT9KUJvD8+fiO8AvoHhFvEatfVixA==} + esbuild@0.27.1: + resolution: {integrity: sha512-yY35KZckJJuVVPXpvjgxiCuVEJT67F6zDeVTv4rizyPrfGBUpZQsvmxnN+C371c2esD/hNMjj4tpBhuueLN7aA==} engines: {node: '>=18'} hasBin: true @@ -5493,8 +5295,8 @@ packages: resolution: {integrity: sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - eslint@9.39.1: - resolution: {integrity: sha512-BhHmn2yNOFA9H9JmmIVKJmd288g9hrVRDkdoIgRCRuSySRUHH7r/DI6aAXW9T1WwUuY3DFgrcaqB+deURBLR5g==} + eslint@9.39.2: + resolution: {integrity: sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} hasBin: true peerDependencies: @@ -5578,8 +5380,8 @@ packages: resolution: {integrity: sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==} engines: {node: '>= 0.8.0'} - expect-type@1.2.2: - resolution: {integrity: sha512-JhFGDVJ7tmDJItKhYgJCGLOWjuK9vPxiXoUFLwLDc99NlmklilbiQJwoctZtt13+xMw91MCk/REan6MWHqDjyA==} + expect-type@1.3.0: + resolution: {integrity: sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==} engines: {node: '>=12.0.0'} exponential-backoff@3.1.3: @@ -5598,8 +5400,12 @@ packages: resolution: {integrity: sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==} engines: {node: '>= 0.10.0'} - express@5.1.0: - resolution: {integrity: sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA==} + express@4.22.1: + resolution: {integrity: sha512-F2X8g9P1X7uCPZMA3MVf9wcTqlyNp7IhH5qPCI0izhaOIYXaW9L535tGA3qmjRzpH+bZczqq7hVKxTR4NWnu+g==} + engines: {node: '>= 0.10.0'} + + express@5.2.1: + resolution: {integrity: sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==} engines: {node: '>= 18'} extend@3.0.2: @@ -5682,10 +5488,14 @@ packages: resolution: {integrity: sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==} engines: {node: '>= 0.8'} - finalhandler@2.1.0: - resolution: {integrity: sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q==} + finalhandler@1.3.2: + resolution: {integrity: sha512-aA4RyPcd3badbdABGDuTXCMTtOneUCAYH/gxoYRTZlIJdF0YPWuGqiAsIrhNnnqdXGswYk6dGujem4w80UJFhg==} engines: {node: '>= 0.8'} + finalhandler@2.1.1: + resolution: {integrity: sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==} + engines: {node: '>= 18.0.0'} + find-cache-directory@6.0.0: resolution: {integrity: sha512-CvFd5ivA6HcSHbD+59P7CyzINHXzwhuQK8RY7CxJZtgDSAtRlHiCaQpZQ2lMR/WRyUIEmzUvL6G2AGurMfegZA==} engines: {node: '>=20'} @@ -5706,8 +5516,8 @@ packages: resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} engines: {node: '>=10'} - firebase@12.5.0: - resolution: {integrity: sha512-Ak8JcpH7FL6kiv0STwkv5+3CYEROO9iFWSx7OCZVvc4kIIABAIyAGs1mPGaHRxGUIApFZdMCXA7baq17uS6Mow==} + firebase@12.7.0: + resolution: {integrity: sha512-ZBZg9jFo8uH4Emd7caOqtalKJfDGHnHQSrCPiqRAdTFQd0wL3ERilUBfhnhBLnlernugkN/o7nJa0p+sE71Izg==} flat-cache@4.0.1: resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==} @@ -5752,8 +5562,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: @@ -5887,14 +5697,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==} @@ -5924,12 +5733,12 @@ packages: resolution: {integrity: sha512-7ABviyMOlX5hIVD60YOfHw4/CxOfBhyduaYB+wbFWCWoni4N7SLcV46hrVRktuBbZjFC9ONyqamZITN7q3n32w==} engines: {node: '>=18'} - google-gax@5.0.5: - resolution: {integrity: sha512-VuC6nVnPVfo/M1WudLoS4Y3dTepVndZatUmeb0nUNmfzft6mKSy8ffDh4h5qxR7L9lslDxNpWPYsuPrFboOmTw==} + google-gax@5.0.6: + resolution: {integrity: sha512-1kGbqVQBZPAAu4+/R1XxPQKP0ydbNYoLAr4l0ZO2bMV0kLyLW4I1gAk++qBLWt7DPORTzmWRMsCZe86gDjShJA==} engines: {node: '>=18'} - google-logging-utils@1.1.2: - resolution: {integrity: sha512-YsFPGVgDFf4IzSwbwIR0iaFJQFmR5Jp7V1WuYSjuRgAm9yWqsMhKE9YPlL+wvFLnc/wMiFV4SQUD9Y/JMpxIxQ==} + google-logging-utils@1.1.3: + resolution: {integrity: sha512-eAmLkjDjAFCVXg7A1unxHsLf961m6y17QFqXqAXGj/gVkKFrEICfStRfwUlGNfeCEjNRa32JEWOUTlYXPyyKvA==} engines: {node: '>=14'} gopd@1.2.0: @@ -5943,9 +5752,6 @@ packages: graceful-fs@4.2.11: resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} - graphemer@1.4.0: - resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} - graphql-tag@2.12.6: resolution: {integrity: sha512-FdSNcu2QQcWnM2VNvSCCDCVS5PpPqpzgFT8+GXzqJuoDd0CBncxCY278u4mhRO7tMgo2JjgJA5aZ+nWSQ/Z+xg==} engines: {node: '>=10'} @@ -6060,6 +5866,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==} @@ -6136,8 +5946,8 @@ packages: resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} engines: {node: '>=0.10.0'} - iconv-lite@0.7.0: - resolution: {integrity: sha512-cf6L2Ds3h57VVmkZe+Pn+5APsT7FpqJtEhhieDCvrE2MK5Qk9MyffgQyuxQTm6BChfeZNtcOLHp9IcWRVcIcBQ==} + iconv-lite@0.7.1: + resolution: {integrity: sha512-2Tth85cXwGFHfvRgZWszZSvdo+0Xsqmw8k8ZwxScfcBneNUraK+dxRxRm24nszx80Y0TVio8kKLt5sLE7ZCLlw==} engines: {node: '>=0.10.0'} icss-utils@5.1.0: @@ -6204,10 +6014,6 @@ packages: ini@1.3.8: resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==} - ini@5.0.0: - resolution: {integrity: sha512-+N0ngpO3e7cRUWOJAS7qw0IZIVc6XPrW4MlFBdD066F2L4k1L6ker3hLqSq7iXxU5tgS4WGkIUElWn5vogAEnw==} - engines: {node: ^18.17.0 || >=20.5.0} - ini@6.0.0: resolution: {integrity: sha512-IBTdIkzZNOpqm7q3dRqJvMaldXjDHWkEDfrwGEQTs5eaQMWV+djAhR+wahyNNMAa+qpbDUhBMVt4ZKNwpPm7xQ==} engines: {node: ^20.17.0 || >=22.9.0} @@ -6223,8 +6029,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: @@ -6235,8 +6041,8 @@ packages: resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==} engines: {node: '>= 0.10'} - ipaddr.js@2.2.0: - resolution: {integrity: sha512-Ag3wB2o37wslZS19hZqorUnrnzSkpOVy+IiiDEiTqNubEYpYuHWIf6K4psgN2ZWKExS4xhVCrRVfb/wfW8fWJA==} + ipaddr.js@2.3.0: + resolution: {integrity: sha512-Zv/pA+ciVFbCSBBjGfaKUya/CcGmUHzTydLMaTwrUUEM2DIEO3iZvueGxmacvmN50fGpGVKeTXpb2LcYQxeVdg==} engines: {node: '>= 10'} is-array-buffer@3.0.5: @@ -6319,6 +6125,10 @@ packages: resolution: {integrity: sha512-rcfALRIb1YewtnksfRIHGcIY93QnK8BIQ/2c9yDYcG/Y6+vRoJuTWBmmSEbyLLYtXm7q35pHOHbZFQBaLrhlWQ==} engines: {node: '>=0.10.0'} + is-in-ssh@1.0.0: + resolution: {integrity: sha512-jYa6Q9rH90kR1vKB6NM7qqd1mge3Fx4Dhw5TVlK1MUBqhEOuCagrEHMevNuCcbECmXZ0ThXkRm+Ymr51HwEPAw==} + engines: {node: '>=20'} + is-inside-container@1.0.0: resolution: {integrity: sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==} engines: {node: '>=14.16'} @@ -6479,8 +6289,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: @@ -6528,10 +6338,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'} @@ -6543,8 +6349,8 @@ packages: jasmine-core@4.6.1: resolution: {integrity: sha512-VYz/BjjmC3klLJlLwA4Kw8ytk0zDSmbbDLNs794VnWmkcCB7I9aAL/D48VNQtmITyPvea2C3jdUMfc3kAoy0PQ==} - jasmine-core@5.12.1: - resolution: {integrity: sha512-P/UbRZ0LKwXe7wEpwDheuhunPwITn4oPALhrJEQJo6756EwNGnsK/TSQrWojBB4cQDQ+VaxWYws9tFNDuiMh2Q==} + jasmine-core@5.13.0: + resolution: {integrity: sha512-vsYjfh7lyqvZX5QgqKc4YH8phs7g96Z8bsdIFNEU3VqXhlHaq+vov/Fgn/sr6MiUczdZkyXRC3TX369Ll4Nzbw==} jasmine-reporters@2.5.2: resolution: {integrity: sha512-qdewRUuFOSiWhiyWZX8Yx3YNQ9JG51ntBEO4ekLQRpktxFTwUHy24a86zD/Oi2BRTKksEdfWQZcQFqzjqIkPig==} @@ -6556,8 +6362,8 @@ packages: resolution: {integrity: sha512-KbdGQTf5jbZgltoHs31XGiChAPumMSY64OZMWLNYnEnMfG5uwGBhffePwuskexjT+/Jea/gU3qAU8344hNohSw==} hasBin: true - jasmine@5.12.0: - resolution: {integrity: sha512-KmKeTNuH8rgAuPRL5AUsXWSdJVlDu+pgqi2dLXoZUSH/g3kR+7Ho8B7hEhwDu0fu1PLuiXZtfaxmQ/mB5wqihw==} + jasmine@5.13.0: + resolution: {integrity: sha512-oLCXIhEb5e0zzjn9GyuvcuisvLBwUjmgz7a0RNGWKwQtJCDld4m+vwKUpAIJVLB5vbmQFdtKhT86/tIZlJ5gYw==} hasBin: true jasminewd2@2.2.0: @@ -6572,6 +6378,9 @@ packages: resolution: {integrity: sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==} hasBin: true + jose@6.1.3: + resolution: {integrity: sha512-0TpaTfihd4QMNwrz/ob2Bp7X04yuxJkjRGi4aKmOqwhov54i6u79oCv7T+C7lo70MKH6BesI3vscD1yb/yzKXQ==} + js-base64@3.7.8: resolution: {integrity: sha512-hNngCeKxIUQiEUN3GPJOkz4wF/YvdUdbNL9hsBcMQTkKzboD7T/q3OYOuuPZLUE6dBxSGpwhk5mwuDud7JVAow==} @@ -6581,15 +6390,15 @@ packages: js-tokens@9.0.1: resolution: {integrity: sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==} - js-yaml@4.1.0: - resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} + js-yaml@4.1.1: + resolution: {integrity: sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==} hasBin: true jsbn@0.1.1: resolution: {integrity: sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==} - jsdom@27.2.0: - resolution: {integrity: sha512-454TI39PeRDW1LgpyLPyURtB4Zx1tklSr6+OFOipsxGUH1WMTvk6C65JQdrj455+DP2uJ1+veBEHTGFKWVLFoA==} + jsdom@27.3.0: + resolution: {integrity: sha512-GtldT42B8+jefDUC4yUKAvsaOrH7PDHmZxZXNgF2xMmymjUbRYJvpAybZAKEmXDGTM0mCsz8duOa4vTm5AY2Kg==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} peerDependencies: canvas: ^3.0.0 @@ -6611,9 +6420,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==} @@ -6621,6 +6430,9 @@ packages: json-schema-traverse@1.0.0: resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} + json-schema-typed@8.0.2: + resolution: {integrity: sha512-fQhoXdcvc3V28x7C7BMs4P5+kNlgUURe2jmUT1T//oBRMDrqy1QPelJimwZGo7Hg9VPV3EQV5Bnq4hbFy2vetA==} + json-schema@0.4.0: resolution: {integrity: sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==} @@ -6673,11 +6485,11 @@ packages: jwa@2.0.1: resolution: {integrity: sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==} - jws@3.2.2: - resolution: {integrity: sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==} + jws@3.2.3: + resolution: {integrity: sha512-byiJ0FLRdLdSVSReO/U4E7RoEyOCKnEnEPMjq3HxWtvzLsV08/i5RQKsFVNkCldrCaPr2vDNAOMsfs8T/Hze7g==} - jws@4.0.0: - resolution: {integrity: sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==} + jws@4.0.1: + resolution: {integrity: sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA==} karma-chrome-launcher@3.2.0: resolution: {integrity: sha512-rE9RkUPI7I9mAxByQWkGJFXfFD6lE4gC5nPuZdobf/QdTEJI6EU4yIay/cfU/xV4ZxlM5JiTv7zWYgA64NpS5Q==} @@ -6886,8 +6698,8 @@ packages: lru-cache@10.4.3: resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} - lru-cache@11.2.2: - resolution: {integrity: sha512-F9ODfyqML2coTIsQpSkRHnLSZMtkU8Q+mSfcaIyKwy58u+8k5nvAYeiNhsyMARvzNcXJ9QfWVrcPsC9e9rAxtg==} + lru-cache@11.2.4: + resolution: {integrity: sha512-B5Y16Jr9LB9dHVkh6ZevG+vAbOsNOYCX+sXvFWFu7B3Iz5mijW3zdbMyhsh8ANd2mSWBYdJgnqi+mL7/LrOPYg==} engines: {node: 20 || >=22} lru-cache@5.1.1: @@ -6918,12 +6730,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: @@ -6944,8 +6752,8 @@ packages: resolution: {integrity: sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==} engines: {node: '>= 0.8'} - memfs@4.50.0: - resolution: {integrity: sha512-N0LUYQMUA1yS5tJKmMtU9yprPm6ZIg24yr/OVv/7t6q0kKDIho4cBbXRi1XKttUmNYDYgF/q45qrKE/UhGO0CA==} + memfs@4.51.1: + resolution: {integrity: sha512-Eyt3XrufitN2ZL9c/uIRMyDwXanLI88h/L3MoWqNY747ha3dMR9dWqp8cRT5ntjZ0U1TNuq4U91ZXK0sMBjYOQ==} meow@13.2.0: resolution: {integrity: sha512-pxQJQzB6djGPXh08dacEloMFopsOqGVRKFPYvPOt9XDZ1HasbgDZA74CJGreSU4G3Ak7EFJGoiH2auq+yXISgA==} @@ -6985,9 +6793,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==} @@ -7055,9 +6863,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==} @@ -7118,8 +6926,8 @@ packages: resolution: {integrity: sha512-P0efT1C9jIdVRefqjzOQ9Xml57zpOXnIuS+csaB4MdZbTdmGDLo8XhzBG1N7aO11gKDDkJvBLULeFTo46wwreA==} hasBin: true - msgpackr@1.11.5: - resolution: {integrity: sha512-UjkUHN0yqp9RWKy0Lplhh+wlpdt9oQBYgULZOiFhV3VclSF1JnSQWZ5r9gORQlNYaUKQoR8itv7g7z1xDDuACA==} + msgpackr@1.11.8: + resolution: {integrity: sha512-bC4UGzHhVvgDNS7kn9tV8fAucIYUBuGojcaLiz7v+P63Lmtm0Xeji8B/8tYKddALXxJLpwIeBmUN3u64C4YkRA==} multicast-dns@7.2.5: resolution: {integrity: sha512-2eznPJP8z2BFLX50tf0LuODrpINqP1RVIm/CObbTcBRITQgmC/TjcREF1NeTBzIcR5XO/ukWo+YHOjBbFwIupg==} @@ -7133,6 +6941,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==} @@ -7168,12 +6980,12 @@ packages: resolution: {integrity: sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg==} engines: {node: '>= 0.4.0'} - ng-packagr@21.0.0-rc.1: - resolution: {integrity: sha512-e3R45a5fuQ/bxdegHO+P+10+YjamgZvInx+cKD0exHPTLwqTN9JNWkyfL/pZf4wyyDzTvmcmADNvqPOml9GJ0w==} + ng-packagr@21.1.0-next.0: + resolution: {integrity: sha512-DuNSGSiZVEOiMOWFjNENx04AEXuK6A6d1iOxYFYuAotYXLSnG1ghUSDN83c5lmdE1ISRXLvbnJEGRKu0vqBK7g==} 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.1.0-next || ^21.0.0 tailwindcss: ^2.0.0 || ^3.0.0 || ^4.0.0 tslib: ^2.3.0 typescript: '>=5.9 <6.0' @@ -7221,8 +7033,8 @@ packages: resolution: {integrity: sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - node-forge@1.3.1: - resolution: {integrity: sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==} + node-forge@1.3.3: + resolution: {integrity: sha512-rLvcdSyRCyouf6jcOIPe/BgwG/d7hKjzMKOas33/pHEr6gbq18IK9zV7DiPvzsz0oBJPme6qr6H6kGZuI9/DZg==} engines: {node: '>= 6.13.0'} node-gyp-build-optional-packages@5.2.2: @@ -7233,49 +7045,41 @@ 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.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: resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} engines: {node: '>=0.10.0'} - normalize-range@0.1.2: - resolution: {integrity: sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==} - engines: {node: '>=0.10.0'} - normalize-url@6.1.0: resolution: {integrity: sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==} engines: {node: '>=10'} - npm-bundled@4.0.0: - resolution: {integrity: sha512-IxaQZDMsqfQ2Lz37VvyyEtKLe8FsRZuysmedy/N06TU1RyVppYKXrO4xIhR0F+7ubIBox6Q7nir6fQI3ej39iA==} - engines: {node: ^18.17.0 || >=20.5.0} + npm-bundled@5.0.0: + resolution: {integrity: sha512-JLSpbzh6UUXIEoqPsYBvVNVmyrjVZ1fzEFbqxKkTJQkWBO3xFzFT+KDnSKQWwOQNbuWRwt5LSD6HOTLGIWzfrw==} + engines: {node: ^20.17.0 || >=22.9.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==} + npm-package-arg@13.0.2: + resolution: {integrity: sha512-IciCE3SY3uE84Ld8WZU23gAPPV9rIYod4F+rc+vJ7h7cwAJt9Vk6TVsK60ry7Uj3SRS3bqRRIGuTp9YVlk6WNA==} engines: {node: ^20.17.0 || >=22.9.0} npm-packlist@10.0.3: @@ -7286,8 +7090,8 @@ packages: resolution: {integrity: sha512-buzyCfeoGY/PxKqmBqn1IUJrZnUi1VVJTdSSRPGI60tJdUhUoSQFhs0zycJokDdOznQentgrpf8LayEHyyYlqQ==} engines: {node: ^20.17.0 || >=22.9.0} - npm-registry-fetch@19.1.0: - resolution: {integrity: sha512-xyZLfs7TxPu/WKjHUs0jZOPinzBAI32kEUel6za0vH+JUTnFZ5zbHI1ZoGZRDm6oMjADtrli6FxtMlk/5ABPNw==} + 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: @@ -7335,6 +7139,9 @@ packages: obuf@1.1.2: resolution: {integrity: sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==} + obug@2.1.1: + resolution: {integrity: sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==} + on-exit-leak-free@2.1.2: resolution: {integrity: sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==} engines: {node: '>=14.0.0'} @@ -7369,6 +7176,10 @@ packages: resolution: {integrity: sha512-YgBpdJHPyQ2UE5x+hlSXcnejzAvD0b22U2OuAP+8OnlJT+PjWPxtgmGqKKc+RgTM63U9gN0YzrYc71R2WT/hTA==} engines: {node: '>=18'} + open@11.0.0: + resolution: {integrity: sha512-smsWv2LzFjP03xmvFoJ331ss6h+jixfA4UUV/Bsiyuu4YJPfN+FIQGOIiv4w9/+MoHkfkJ22UIaQWRVFRfH6Vw==} + engines: {node: '>=20'} + open@8.4.2: resolution: {integrity: sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==} engines: {node: '>=12'} @@ -7427,8 +7238,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: @@ -7458,8 +7269,8 @@ packages: package-json-from-dist@1.0.1: resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==} - pacote@21.0.3: - resolution: {integrity: sha512-itdFlanxO0nmQv4ORsvA9K1wv40IPfB9OmWqfaJWvoJ30VKyHsqNgDVeG+TVhI7Gk7XW8slUy7cA9r6dF5qohw==} + pacote@21.0.4: + resolution: {integrity: sha512-RplP/pDW0NNNDh3pnaoIWYPvNenS7UqMbXyvMqJczosiFWTeGGwJC2NQBLqKf4rGLFfwCOnntw1aEp9Jiqm1MA==} engines: {node: ^20.17.0 || >=22.9.0} hasBin: true @@ -7519,8 +7330,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: @@ -7588,16 +7399,16 @@ packages: pino-std-serializers@7.0.0: resolution: {integrity: sha512-e906FRY0+tV27iq4juKzSYPbUj2do2X2JX4EzSca1631EB2QJQUqGbDuERal7LCtOpxl6x3+nvo9NPZcmjkiFA==} - pino@9.13.1: - resolution: {integrity: sha512-Szuj+ViDTjKPQYiKumGmEn3frdl+ZPSdosHyt9SnUevFosOkMY2b7ipxlEctNKPmMD/VibeBI+ZcZCJK+4DPuw==} + pino@9.14.0: + resolution: {integrity: sha512-8OEwKp5juEvb/MjpIc4hjqfgCNysrS94RIOMXYvpYCdm/jglrKEiAYmiumbmGhCvs+IcInsphYDFwqrjr7398w==} hasBin: true piscina@5.1.4: resolution: {integrity: sha512-7uU4ZnKeQq22t9AsmHGD2w4OYQGonwFnTypDypaWi7Qr2EvQIFVtG8J5D/3bE7W123Wdc9+v4CZDu5hJXVCtBg==} 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: @@ -7660,8 +7471,8 @@ packages: peerDependencies: postcss: ^8.1.0 - postcss-selector-parser@7.1.0: - resolution: {integrity: sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==} + postcss-selector-parser@7.1.1: + resolution: {integrity: sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg==} engines: {node: '>=4'} postcss-value-parser@4.2.0: @@ -7671,21 +7482,21 @@ packages: resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==} engines: {node: ^10 || ^12 || >=14} + powershell-utils@0.1.0: + resolution: {integrity: sha512-dM0jVuXJPsDN6DvRpea484tCUaMiXWjuCn++HGTqUWzGDjv5tZkEZldAJ/UMlqRYGFrD/etByo4/xOuC/snX2A==} + engines: {node: '>=20'} + prelude-ls@1.2.1: resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} engines: {node: '>= 0.8.0'} - prettier@3.6.2: - resolution: {integrity: sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==} + prettier@3.7.4: + resolution: {integrity: sha512-v6UNi1+3hSlVvv8fSaoUbggEM5VErKmmpGA7Pl3HF8V6uKY7rvClBOJlH6yNwQtfTueNkGVpOv/mtWL9L4bgRA==} engines: {node: '>=14'} hasBin: true - proc-log@5.0.0: - 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==} + proc-log@6.1.0: + resolution: {integrity: sha512-iG+GYldRf2BQ0UDUAd6JQ/RwzaQy6mXmsk/IzlYyal4A4SNFw54MeH4/tLkF4I5WoWG9SQwuqWzS99jaFQHBuQ==} engines: {node: ^20.17.0 || >=22.9.0} process-nextick-args@2.0.1: @@ -7764,8 +7575,8 @@ packages: resolution: {integrity: sha512-MRtTAZfQTluz3U2oU/X2VqVWPcR1+94nbA2V6ZrSZRVEwLqZ8eclZ551qGFQD/vD2PYqHJwWOW/fpC721uznVw==} engines: {node: '>=14.1.0'} - puppeteer-core@24.27.0: - resolution: {integrity: sha512-yubwj2XXmTM3wRIpbhO5nCjbByPgpFHlgrsD4IK+gMPqO7/a5FfnoSXDKjmqi8A2M1Ewusz0rTI/r+IN0GU0MA==} + puppeteer-core@24.34.0: + resolution: {integrity: sha512-24evawO+mUGW4mvS2a2ivwLdX3gk8zRLZr9HP+7+VT2vBQnm0oh9jJEZmUE3ePJhRkYlZ93i7OMpdcoi2qNCLg==} engines: {node: '>=18'} puppeteer@18.2.1: @@ -7821,8 +7632,12 @@ 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@2.5.3: + resolution: {integrity: sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA==} + engines: {node: '>= 0.8'} + + raw-body@3.0.2: + resolution: {integrity: sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==} engines: {node: '>= 0.10'} readable-stream@2.3.8: @@ -7848,6 +7663,10 @@ packages: resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==} engines: {node: '>= 14.18.0'} + readdirp@5.0.0: + resolution: {integrity: sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ==} + engines: {node: '>= 20.19.0'} + real-require@0.2.0: resolution: {integrity: sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==} engines: {node: '>= 12.13.0'} @@ -7974,8 +7793,8 @@ packages: resolution: {integrity: sha512-l0OE8wL34P4nJH/H2ffoaniAokM2qSmrtXHmlpvYr5AVVX8msAyW0l8NVJFDxlSK4u3Uh/f41cQheDVdnYijwQ==} hasBin: true - rolldown@1.0.0-beta.50: - resolution: {integrity: sha512-JFULvCNl/anKn99eKjOSEubi0lLmNqQDAjyEMME2T4CwezUDL0i6t1O9xZsu2OMehPnV2caNefWpGF+8TnzB6A==} + rolldown@1.0.0-beta.54: + resolution: {integrity: sha512-3lIvjCWgjPL3gmiATUdV1NeVBGJZy6FdtwgLPol25tAkn46Q/MsVGfCSNswXwFOxGrxglPaN20IeALSIFuFyEg==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true @@ -7983,8 +7802,8 @@ packages: resolution: {integrity: sha512-wt0sqnedDjYVUnalyDEajrVAiup2V3O30I5PzXDS3syN9liSRKrZ8H6EECiV3OEZRs2fLauwT9t0QZGLuVTs2Q==} engines: {node: '>=18.0.0'} - rollup-plugin-dts@6.2.3: - resolution: {integrity: sha512-UgnEsfciXSPpASuOelix7m4DrmyQgiaWBnvI0TM4GxuDh5FkqW8E5hu57bCxXB90VvR1WNfLV80yEDN18UogSA==} + rollup-plugin-dts@6.3.0: + resolution: {integrity: sha512-d0UrqxYd8KyZ6i3M2Nx7WOMy708qsV/7fTHMHxCMCBOAe3V/U7OMPu5GkX8hC+cmkHhzGnfeYongl1IgiooddA==} engines: {node: '>=16'} peerDependencies: rollup: ^3.29.4 || ^4 @@ -8000,13 +7819,8 @@ packages: '@types/node': optional: true - rollup@4.53.1: - resolution: {integrity: sha512-n2I0V0lN3E9cxxMqBCT3opWOiQBzRN7UG60z/WDKqdX2zHUS/39lezBcsckZFsV6fUTSnfqI7kHf60jDAPGKug==} - engines: {node: '>=18.0.0', npm: '>=8.0.0'} - hasBin: true - - rollup@4.53.2: - resolution: {integrity: sha512-MHngMYwGJVi6Fmnk6ISmnk7JAHRNF0UkuucA0CUW3N3a4KnONPEZz+vUanQP/ZC/iY1Qkf3bwPWzyY84wEks1g==} + rollup@4.53.5: + resolution: {integrity: sha512-iTNAbFSlRpcHeeWu73ywU/8KuU/LZmNCSxp6fjQkJBD3ivUb8tpDrXhIxEzA05HlYMEwmtaUnb3RP+YNv162OQ==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true @@ -8073,16 +7887,16 @@ packages: webpack: optional: true - sass@1.94.0: - resolution: {integrity: sha512-Dqh7SiYcaFtdv5Wvku6QgS5IGPm281L+ZtVD1U2FJa7Q0EFRlq8Z3sjYtz6gYObsYThUOz9ArwFqPZx+1azILQ==} + sass@1.97.0: + resolution: {integrity: sha512-KR0igP1z4avUJetEuIeOdDlwaUDvkH8wSx7FdSjyYBS3dpyX3TzHfAMO0G1Q4/3cdjcmi3r7idh+KCmKqS+KeQ==} engines: {node: '>=14.0.0'} hasBin: true 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==} @@ -8125,12 +7939,12 @@ packages: resolution: {integrity: sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==} engines: {node: '>= 0.8.0'} - send@0.19.1: - resolution: {integrity: sha512-p4rRk4f23ynFEfcD9LA0xRYngj+IyGiEYyqqOak8kaN0TvNmuxC2dcVeBn62GpCeR2CpWqyHCNScTP91QbAVFg==} + send@0.19.2: + resolution: {integrity: sha512-VMbMxbDeehAxpOtWJXlcUS5E8iXh6QmN+BkRX1GARS3wRaXEEgzCcB10gTQazO42tpNIya8xIyNx8fll1OFPrg==} engines: {node: '>= 0.8.0'} - send@1.2.0: - resolution: {integrity: sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw==} + send@1.2.1: + resolution: {integrity: sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==} engines: {node: '>= 18'} serialize-javascript@6.0.2: @@ -8144,8 +7958,12 @@ packages: resolution: {integrity: sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==} engines: {node: '>= 0.8.0'} - serve-static@2.2.0: - resolution: {integrity: sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ==} + serve-static@1.16.3: + resolution: {integrity: sha512-x0RTqQel6g5SY7Lg6ZreMmsOzncHFU7nhnRWkKgWuMTu5NN0DR5oruckMqRvacAN9d5w6ARnRBXl9xhDCgfMeA==} + engines: {node: '>= 0.8.0'} + + serve-static@2.2.1: + resolution: {integrity: sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==} engines: {node: '>= 18'} server-destroy@1.0.1: @@ -8217,8 +8035,8 @@ packages: resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} engines: {node: '>=14'} - sigstore@4.0.0: - resolution: {integrity: sha512-Gw/FgHtrLM9WP8P5lLcSGh9OQcrTruWCELAiS48ik1QbL0cH+dfjomiRTUE9zzz+D1N6rOLkwXUvVmXZAsNE0Q==} + sigstore@4.1.0: + resolution: {integrity: sha512-/fUgUhYghuLzVT/gaJoeVehLCgZiUxPCPMcyVNY0lIf/cTCz58K/WTI7PefDarXxp9nUKpEwg1yyz3eSBMTtgA==} engines: {node: ^20.17.0 || >=22.9.0} slash@3.0.0: @@ -8233,9 +8051,6 @@ packages: resolution: {integrity: sha512-iOBWFgUX7caIZiuutICxVgX1SdxwAVFFKwt1EvMYYec/NWO5meOJ6K5uQxhrYBdQJne4KxiqZc+KptFOWFSI9w==} engines: {node: '>=18'} - slow-redact@0.3.2: - resolution: {integrity: sha512-MseHyi2+E/hBRqdOi5COy6wZ7j7DxXRz9NkseavNYSvvWC06D8a5cidVZX3tcG5eCW3NIyVU4zT63hw0Q486jw==} - smart-buffer@4.2.0: resolution: {integrity: sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==} engines: {node: '>= 6.0.0', npm: '>= 3.0.0'} @@ -8341,9 +8156,9 @@ packages: resolution: {integrity: sha512-bSf16tAFkGeRlUNDjXu8FzaMQt6g2HZJrun7mtMbIPOddxt3GLMSz5VWUWcqTJUPfLEaDIepGxv+bYQW49596A==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - ssri@12.0.0: - 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==} @@ -8515,8 +8330,8 @@ packages: resolution: {integrity: sha512-3ZnLvgWF29jikg1sAQ1g0o+lr5JX6sVgYvfUJazn7ZjJroDBUTWp44/+cFVX0bULjv4vci+rBD+oGVAkWqhUbw==} engines: {node: '>=18'} - terser-webpack-plugin@5.3.14: - resolution: {integrity: sha512-vkZjpUjb6OMS7dhV+tILUW6BhpDR7P2L/aQSAv+Uwk+m8KATX9EccViHTJR2qDtACKPIYndLGCyl3FMo+r2LMw==} + terser-webpack-plugin@5.3.16: + resolution: {integrity: sha512-h9oBFCWrq78NyWWVcSwZarJkZ01c2AyGrzs1crmHZO3QUg9D61Wu4NPjBy69n7JqylFF5y+CsUZYmYEIZ3mR+Q==} engines: {node: '>= 10.13.0'} peerDependencies: '@swc/core': '*' @@ -8566,8 +8381,9 @@ packages: tinybench@2.9.0: resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} - tinyexec@0.3.2: - resolution: {integrity: sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==} + tinyexec@1.0.2: + resolution: {integrity: sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg==} + engines: {node: '>=18'} tinyglobby@0.2.15: resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==} @@ -8580,15 +8396,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: @@ -8674,13 +8490,13 @@ packages: resolution: {integrity: sha512-LxhtAkPDTkVCMQjt2h6eBVY28KCjikZqZfMcC15YBeNjkgUpdCfBu5HoiOTDu86v6smE8yOjyEktJ8hlbANHQA==} engines: {node: '>=0.6.x'} - tsx@4.20.6: - resolution: {integrity: sha512-ytQKuwgmrrkDTFP4LjR0ToE2nqgy886GpvRSpU0JAnrdBYppuY5rLkRUYPU1yCryb24SsKBTL/hlDQAEFVwtZg==} + tsx@4.21.0: + resolution: {integrity: sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==} engines: {node: '>=18.0.0'} hasBin: true - tuf-js@4.0.0: - resolution: {integrity: sha512-Lq7ieeGvXDXwpoSmOSgLWVdsGGV9J4a77oDTAPe/Ltrqnnm/ETaRlBAQTH5JatEh8KXuE6sddf9qAv1Q2282Hg==} + tuf-js@4.1.0: + resolution: {integrity: sha512-50QV99kCKH5P/Vs4E2Gzp7BopNV+KzTXqWeaxrfu5IQJBOULRsTIS9seSsOVT8ZnGXzCyx55nYWAi4qJzpZKEQ==} engines: {node: ^20.17.0 || >=22.9.0} tunnel-agent@0.6.0: @@ -8806,13 +8622,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==} @@ -8831,8 +8647,8 @@ packages: resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==} engines: {node: '>= 0.8'} - update-browserslist-db@1.1.4: - resolution: {integrity: sha512-q0SPT4xyU84saUX+tomz1WLkxUbuaJnR1xWt17M7fJtEJigJeWUNGUqrauFXsHnqev9y9JTRGwk13tFBuKby4A==} + update-browserslist-db@1.2.3: + resolution: {integrity: sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==} hasBin: true peerDependencies: browserslist: '>= 4.21.0' @@ -8873,32 +8689,32 @@ packages: validate-npm-package-license@3.0.4: resolution: {integrity: sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==} - validate-npm-package-name@6.0.2: - resolution: {integrity: sha512-IUoow1YUtvoBBC06dXs8bR8B9vuA3aJfmQNKMoaPG/OFsPmoQvw8xh+6Ye25Gx9DQhoEom3Pcu9MKHerm/NpUQ==} - engines: {node: ^18.17.0 || >=20.5.0} + validate-npm-package-name@7.0.1: + resolution: {integrity: sha512-BM0Upcemlce8/9+HE+/VpWqn3u3mYh6Om/FEC8yPMnEHwf710fW5Q6fhjT1SQyRlZD1G9CJbgfH+rWgAcIvjlQ==} + engines: {node: ^20.17.0 || >=22.9.0} - validator@13.15.15: - resolution: {integrity: sha512-BgWVbCI72aIQy937xbawcs+hrVaN/CZ2UwutgaJ36hGqRrLNM+f5LUT/YPRbo8IV/ASeFzXszezV+y2+rq3l8A==} + validator@13.15.23: + resolution: {integrity: sha512-4yoz1kEWqUjzi5zsPbAS/903QXSYp0UOtHsPpp7p9rHAw/W+dkInskAE386Fat3oKRROwO98d9ZB0G4cObgUyw==} engines: {node: '>= 0.10'} vary@1.1.2: resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} engines: {node: '>= 0.8'} - verdaccio-audit@13.0.0-next-8.24: - resolution: {integrity: sha512-dXqsnhTGqOuIsZq/MrW05YKwhuKg94VtL0tcYI4kcT+J+fN3gKiZ1Q3wDPaVzCMc081stBlKhi+SL66gIidHdA==} + verdaccio-audit@13.0.0-next-8.28: + resolution: {integrity: sha512-vcl+V4R43QFSrch0QggG92hEd+aGh7fGBqkA1pcF/m4eJ2JQw7+/8JD5VJ/qCnt7Udnz9Jmey6SvgFgxsB6ODA==} engines: {node: '>=18'} verdaccio-auth-memory@10.3.1: resolution: {integrity: sha512-3m4VH5lD3qdRkDvcvVzHP6YftcsRXgu3WZuspcrH0/76+ugI2wkVezgBDbu4WRwJqRvG0MEf8Zxoup9zVK5SVw==} engines: {node: '>=18'} - verdaccio-htpasswd@13.0.0-next-8.24: - resolution: {integrity: sha512-nZ+V/szt3lXQRIyqvOpJlW0MceLM3tUyTGwqy4y0uwq7w9KGD/VqytnCpiQbkjVmmfScimXwRW7PHjHyRXLCVw==} + verdaccio-htpasswd@13.0.0-next-8.28: + resolution: {integrity: sha512-iAkhusaNUEvBeq+7Xn8YUqq4hXoVuAteZPrG4dwsqSjVliS7YQGdysQKYG89QnN9iMKAEvuSzhb+GP0c5dbL0g==} engines: {node: '>=18'} - verdaccio@6.2.1: - resolution: {integrity: sha512-b7EjPyVKvO/7J2BtLaybQqDd8dh4uUsuQL1zQMVLsw3aYqBsHCAOa6T1zb6gpCg68cNUHluw7IjLs2hha72TZA==} + verdaccio@6.2.4: + resolution: {integrity: sha512-1riItDS5ZmkLVclEOI4ibmJPCTfg1f8iEbdZWo7mgaEDHs1KS2JJnGq+dnoDlbo4efJ5mCyy1g7p32k/xx2+wg==} engines: {node: '>=18'} hasBin: true @@ -8906,8 +8722,8 @@ packages: resolution: {integrity: sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==} engines: {'0': node >=0.6.0} - vite@7.2.2: - resolution: {integrity: sha512-BxAKBWmIbrDgrokdGZH1IgkIk/5mMHDreLDmCJ0qpyJaAteP8NvMhkwr/ZCQNqNH97bw/dANTE9PDzqwJghfMQ==} + vite@7.3.0: + resolution: {integrity: sha512-dZwN5L1VlUBewiP6H9s2+B3e3Jg96D0vzN+Ry73sOefebhYr9f94wwkMNN/9ouoU8pV1BqA1d1zGk8928cx0rg==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true peerDependencies: @@ -8946,24 +8762,24 @@ packages: yaml: optional: true - vitest@4.0.8: - resolution: {integrity: sha512-urzu3NCEV0Qa0Y2PwvBtRgmNtxhj5t5ULw7cuKhIHh3OrkKTLlut0lnBOv9qe5OvbkMH2g38G7KPDCTpIytBVg==} + vitest@4.0.15: + resolution: {integrity: sha512-n1RxDp8UJm6N0IbJLQo+yzLZ2sQCDyl1o0LeugbPWf8+8Fttp29GghsQBjYJVmWq3gBFfe9Hs1spR44vovn2wA==} engines: {node: ^20.0.0 || ^22.0.0 || >=24.0.0} hasBin: true peerDependencies: '@edge-runtime/vm': '*' - '@types/debug': ^4.1.12 + '@opentelemetry/api': ^1.9.0 '@types/node': ^20.0.0 || ^22.0.0 || >=24.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 + '@vitest/browser-playwright': 4.0.15 + '@vitest/browser-preview': 4.0.15 + '@vitest/browser-webdriverio': 4.0.15 + '@vitest/ui': 4.0.15 happy-dom: '*' jsdom: '*' peerDependenciesMeta: '@edge-runtime/vm': optional: true - '@types/debug': + '@opentelemetry/api': optional: true '@types/node': optional: true @@ -9005,8 +8821,8 @@ packages: web-vitals@4.2.4: resolution: {integrity: sha512-r4DIlprAGwJ7YM11VZp4R884m0Vmgr6EAKe3P+kO0PPj3Unqyvv59rczf6UiGcb9Z8QxZVcqKNwv/g0WNdWwsw==} - webdriver-bidi-protocol@0.3.8: - resolution: {integrity: sha512-21Yi2GhGntMc671vNBCjiAeEVknXjVRoyu+k+9xOMShu+ZQfpGQwnBqbNz/Sv4GXZ6JmutlPAi2nIJcrymAWuQ==} + webdriver-bidi-protocol@0.3.10: + resolution: {integrity: sha512-5LAE43jAVLOhB/QqX4bwSiv0Hg1HBfMmOuwBSXHdvg4GMGu9Y0lIq7p4R/yySu6w74WmaR4GM4H9t2IwLW7hgw==} webdriver-js-extender@2.1.0: resolution: {integrity: sha512-lcUKrjbBfCK6MNsh7xaY2UAUmZwe+/ib03AjVOpFobX4O7+83BUveSrLfU0Qsyb1DaKJdQRbuU+kM9aZ6QUhiQ==} @@ -9068,8 +8884,8 @@ packages: html-webpack-plugin: optional: true - webpack@5.102.1: - resolution: {integrity: sha512-7h/weGm9d/ywQ6qzJ+Xy+r9n/3qgp/thalBbpOi5i223dPXKi04IBtqPN9nTd+jBc7QKfvDbaBnFipYp4sJAUQ==} + webpack@5.104.0: + resolution: {integrity: sha512-5DeICTX8BVgNp6afSPYXAFjskIgWGlygQH58bcozPOXgo2r/6xx39Y1+cULZ3gTxUYQP88jmwLj2anu4Xaq84g==} engines: {node: '>=10.13.0'} hasBin: true peerDependencies: @@ -9133,11 +8949,6 @@ packages: engines: {node: '>= 8'} hasBin: true - which@5.0.0: - resolution: {integrity: sha512-JEdGzHwwkrbWoGOlIHqQ5gtprKGOenpDHpxE9zVR1bWbOtYRyPPHMe9FaP6x61CmNaTThSkb0DAJte5jD+DmzQ==} - 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} @@ -9233,6 +9044,10 @@ packages: resolution: {integrity: sha512-h3Fbisa2nKGPxCpm89Hk33lBLsnaGBvctQopaBSOW/uIs6FTe1ATyAnKFJrzVs9vpGdsTe73WF3V4lIsk4Gacw==} engines: {node: '>=18'} + wsl-utils@0.3.0: + resolution: {integrity: sha512-3sFIGLiaDP7rTO4xh3g+b3AzhYDIUGGywE/WsmqzJWDxus5aJXVnPTNC/6L+r2WzrwXqVOdD262OaO+cEyPMSQ==} + engines: {node: '>=20'} + xhr2@0.2.1: resolution: {integrity: sha512-sID0rrVCqkVNUn8t6xuv9+6FViXjUVXq8H5rWOH2rz9fDNQEd4g0EA2XlcEdJXRz5BMEn4O1pJFdT+z4YHhoWw==} engines: {node: '>= 6'} @@ -9277,8 +9092,8 @@ packages: resolution: {integrity: sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==} engines: {node: '>=18'} - yaml@2.8.1: - resolution: {integrity: sha512-lcYcMxX2PO9XMGvAJkJ3OsNMw+/7FKes7/hgerGUYWIoWu5j/+YQqcZr5JnPZWzOsEBgMbSbiSTn/dv/69Mkpw==} + yaml@2.8.2: + resolution: {integrity: sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A==} engines: {node: '>= 14.6'} hasBin: true @@ -9337,154 +9152,155 @@ 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==} - zod@4.1.12: - resolution: {integrity: sha512-JInaHOamG8pt5+Ey8kGmdcAcg3OL9reK8ltczgHTAwNhMys/6ThXHityHxVV2p3fkw/c+MAvBHFVYHFZDmjMCQ==} + zod@4.2.1: + resolution: {integrity: sha512-0wZ1IRqGGhMP76gLqz8EyfBXKk0J2qo2+H3fi4mcUP/KtTocoX08nmIAHl1Z2kJIZbZee8KOpBCSNPRgauucjw==} - zone.js@0.15.1: - resolution: {integrity: sha512-XE96n56IQpJM7NAoXswY3XRLcWFW83xe0BiAOeMD7K5k5xecOeul3Qcpx6GqEeeHNkW5DWL5zOyTbEfB4eti8w==} + zone.js@0.16.0: + resolution: {integrity: sha512-LqLPpIQANebrlxY6jKcYKdgN5DTXyyHAKnnWWjE5pPfEQ4n7j5zn7mOEEpwNZVKGqx3kKKmvplEmoBrvpgROTA==} snapshots: - '@acemir/cssom@0.9.23': {} + '@acemir/cssom@0.9.29': {} - '@actions/core@1.11.1': + '@actions/core@2.0.1': dependencies: - '@actions/exec': 1.1.1 - '@actions/http-client': 2.2.3 + '@actions/exec': 2.0.0 + '@actions/http-client': 3.0.0 - '@actions/exec@1.1.1': + '@actions/exec@2.0.0': dependencies: - '@actions/io': 1.1.3 + '@actions/io': 2.0.0 - '@actions/http-client@2.2.3': + '@actions/http-client@3.0.0': dependencies: tunnel: 0.0.6 undici: 5.29.0 - '@actions/io@1.1.3': {} + '@actions/io@2.0.0': {} - '@algolia/abtesting@1.9.0': + '@algolia/abtesting@1.12.0': dependencies: - '@algolia/client-common': 5.43.0 - '@algolia/requester-browser-xhr': 5.43.0 - '@algolia/requester-fetch': 5.43.0 - '@algolia/requester-node-http': 5.43.0 + '@algolia/client-common': 5.46.0 + '@algolia/requester-browser-xhr': 5.46.0 + '@algolia/requester-fetch': 5.46.0 + '@algolia/requester-node-http': 5.46.0 - '@algolia/client-abtesting@5.43.0': + '@algolia/client-abtesting@5.46.0': dependencies: - '@algolia/client-common': 5.43.0 - '@algolia/requester-browser-xhr': 5.43.0 - '@algolia/requester-fetch': 5.43.0 - '@algolia/requester-node-http': 5.43.0 + '@algolia/client-common': 5.46.0 + '@algolia/requester-browser-xhr': 5.46.0 + '@algolia/requester-fetch': 5.46.0 + '@algolia/requester-node-http': 5.46.0 - '@algolia/client-analytics@5.43.0': + '@algolia/client-analytics@5.46.0': dependencies: - '@algolia/client-common': 5.43.0 - '@algolia/requester-browser-xhr': 5.43.0 - '@algolia/requester-fetch': 5.43.0 - '@algolia/requester-node-http': 5.43.0 + '@algolia/client-common': 5.46.0 + '@algolia/requester-browser-xhr': 5.46.0 + '@algolia/requester-fetch': 5.46.0 + '@algolia/requester-node-http': 5.46.0 - '@algolia/client-common@5.43.0': {} + '@algolia/client-common@5.46.0': {} - '@algolia/client-insights@5.43.0': + '@algolia/client-insights@5.46.0': dependencies: - '@algolia/client-common': 5.43.0 - '@algolia/requester-browser-xhr': 5.43.0 - '@algolia/requester-fetch': 5.43.0 - '@algolia/requester-node-http': 5.43.0 + '@algolia/client-common': 5.46.0 + '@algolia/requester-browser-xhr': 5.46.0 + '@algolia/requester-fetch': 5.46.0 + '@algolia/requester-node-http': 5.46.0 - '@algolia/client-personalization@5.43.0': + '@algolia/client-personalization@5.46.0': dependencies: - '@algolia/client-common': 5.43.0 - '@algolia/requester-browser-xhr': 5.43.0 - '@algolia/requester-fetch': 5.43.0 - '@algolia/requester-node-http': 5.43.0 + '@algolia/client-common': 5.46.0 + '@algolia/requester-browser-xhr': 5.46.0 + '@algolia/requester-fetch': 5.46.0 + '@algolia/requester-node-http': 5.46.0 - '@algolia/client-query-suggestions@5.43.0': + '@algolia/client-query-suggestions@5.46.0': dependencies: - '@algolia/client-common': 5.43.0 - '@algolia/requester-browser-xhr': 5.43.0 - '@algolia/requester-fetch': 5.43.0 - '@algolia/requester-node-http': 5.43.0 + '@algolia/client-common': 5.46.0 + '@algolia/requester-browser-xhr': 5.46.0 + '@algolia/requester-fetch': 5.46.0 + '@algolia/requester-node-http': 5.46.0 - '@algolia/client-search@5.43.0': + '@algolia/client-search@5.46.0': dependencies: - '@algolia/client-common': 5.43.0 - '@algolia/requester-browser-xhr': 5.43.0 - '@algolia/requester-fetch': 5.43.0 - '@algolia/requester-node-http': 5.43.0 + '@algolia/client-common': 5.46.0 + '@algolia/requester-browser-xhr': 5.46.0 + '@algolia/requester-fetch': 5.46.0 + '@algolia/requester-node-http': 5.46.0 - '@algolia/ingestion@1.43.0': + '@algolia/ingestion@1.46.0': dependencies: - '@algolia/client-common': 5.43.0 - '@algolia/requester-browser-xhr': 5.43.0 - '@algolia/requester-fetch': 5.43.0 - '@algolia/requester-node-http': 5.43.0 + '@algolia/client-common': 5.46.0 + '@algolia/requester-browser-xhr': 5.46.0 + '@algolia/requester-fetch': 5.46.0 + '@algolia/requester-node-http': 5.46.0 - '@algolia/monitoring@1.43.0': + '@algolia/monitoring@1.46.0': dependencies: - '@algolia/client-common': 5.43.0 - '@algolia/requester-browser-xhr': 5.43.0 - '@algolia/requester-fetch': 5.43.0 - '@algolia/requester-node-http': 5.43.0 + '@algolia/client-common': 5.46.0 + '@algolia/requester-browser-xhr': 5.46.0 + '@algolia/requester-fetch': 5.46.0 + '@algolia/requester-node-http': 5.46.0 - '@algolia/recommend@5.43.0': + '@algolia/recommend@5.46.0': dependencies: - '@algolia/client-common': 5.43.0 - '@algolia/requester-browser-xhr': 5.43.0 - '@algolia/requester-fetch': 5.43.0 - '@algolia/requester-node-http': 5.43.0 + '@algolia/client-common': 5.46.0 + '@algolia/requester-browser-xhr': 5.46.0 + '@algolia/requester-fetch': 5.46.0 + '@algolia/requester-node-http': 5.46.0 - '@algolia/requester-browser-xhr@5.43.0': + '@algolia/requester-browser-xhr@5.46.0': dependencies: - '@algolia/client-common': 5.43.0 + '@algolia/client-common': 5.46.0 - '@algolia/requester-fetch@5.43.0': + '@algolia/requester-fetch@5.46.0': dependencies: - '@algolia/client-common': 5.43.0 + '@algolia/client-common': 5.46.0 - '@algolia/requester-node-http@5.43.0': + '@algolia/requester-node-http@5.46.0': dependencies: - '@algolia/client-common': 5.43.0 + '@algolia/client-common': 5.46.0 '@ampproject/remapping@2.3.0': dependencies: '@jridgewell/gen-mapping': 0.3.13 '@jridgewell/trace-mapping': 0.3.31 - '@angular/animations@21.0.0-rc.2(@angular/core@21.0.0-rc.2(@angular/compiler@21.0.0-rc.2)(rxjs@7.8.2)(zone.js@0.15.1))': + '@angular/animations@21.1.0-next.4(@angular/core@21.1.0-next.4(@angular/compiler@21.1.0-next.4)(rxjs@7.8.2)(zone.js@0.16.0))': dependencies: - '@angular/core': 21.0.0-rc.2(@angular/compiler@21.0.0-rc.2)(rxjs@7.8.2)(zone.js@0.15.1) + '@angular/core': 21.1.0-next.4(@angular/compiler@21.1.0-next.4)(rxjs@7.8.2)(zone.js@0.16.0) tslib: 2.8.1 - '@angular/cdk@21.0.0-rc.2(@angular/common@21.0.0-rc.2(@angular/core@21.0.0-rc.2(@angular/compiler@21.0.0-rc.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@21.0.0-rc.2(@angular/compiler@21.0.0-rc.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2)': + '@angular/cdk@21.1.0-next.3(@angular/common@21.1.0-next.4(@angular/core@21.1.0-next.4(@angular/compiler@21.1.0-next.4)(rxjs@7.8.2)(zone.js@0.16.0))(rxjs@7.8.2))(@angular/core@21.1.0-next.4(@angular/compiler@21.1.0-next.4)(rxjs@7.8.2)(zone.js@0.16.0))(@angular/platform-browser@21.1.0-next.4(@angular/animations@21.1.0-next.4(@angular/core@21.1.0-next.4(@angular/compiler@21.1.0-next.4)(rxjs@7.8.2)(zone.js@0.16.0)))(@angular/common@21.1.0-next.4(@angular/core@21.1.0-next.4(@angular/compiler@21.1.0-next.4)(rxjs@7.8.2)(zone.js@0.16.0))(rxjs@7.8.2))(@angular/core@21.1.0-next.4(@angular/compiler@21.1.0-next.4)(rxjs@7.8.2)(zone.js@0.16.0)))(rxjs@7.8.2)': dependencies: - '@angular/common': 21.0.0-rc.2(@angular/core@21.0.0-rc.2(@angular/compiler@21.0.0-rc.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2) - '@angular/core': 21.0.0-rc.2(@angular/compiler@21.0.0-rc.2)(rxjs@7.8.2)(zone.js@0.15.1) + '@angular/common': 21.1.0-next.4(@angular/core@21.1.0-next.4(@angular/compiler@21.1.0-next.4)(rxjs@7.8.2)(zone.js@0.16.0))(rxjs@7.8.2) + '@angular/core': 21.1.0-next.4(@angular/compiler@21.1.0-next.4)(rxjs@7.8.2)(zone.js@0.16.0) + '@angular/platform-browser': 21.1.0-next.4(@angular/animations@21.1.0-next.4(@angular/core@21.1.0-next.4(@angular/compiler@21.1.0-next.4)(rxjs@7.8.2)(zone.js@0.16.0)))(@angular/common@21.1.0-next.4(@angular/core@21.1.0-next.4(@angular/compiler@21.1.0-next.4)(rxjs@7.8.2)(zone.js@0.16.0))(rxjs@7.8.2))(@angular/core@21.1.0-next.4(@angular/compiler@21.1.0-next.4)(rxjs@7.8.2)(zone.js@0.16.0)) parse5: 8.0.0 rxjs: 7.8.2 tslib: 2.8.1 - '@angular/common@21.0.0-rc.2(@angular/core@21.0.0-rc.2(@angular/compiler@21.0.0-rc.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2)': + '@angular/common@21.1.0-next.4(@angular/core@21.1.0-next.4(@angular/compiler@21.1.0-next.4)(rxjs@7.8.2)(zone.js@0.16.0))(rxjs@7.8.2)': dependencies: - '@angular/core': 21.0.0-rc.2(@angular/compiler@21.0.0-rc.2)(rxjs@7.8.2)(zone.js@0.15.1) + '@angular/core': 21.1.0-next.4(@angular/compiler@21.1.0-next.4)(rxjs@7.8.2)(zone.js@0.16.0) rxjs: 7.8.2 tslib: 2.8.1 - '@angular/compiler-cli@21.0.0-rc.2(@angular/compiler@21.0.0-rc.2)(typescript@5.9.3)': + '@angular/compiler-cli@21.1.0-next.4(@angular/compiler@21.1.0-next.4)(typescript@5.9.3)': dependencies: - '@angular/compiler': 21.0.0-rc.2 - '@babel/core': 7.28.4 + '@angular/compiler': 21.1.0-next.4 + '@babel/core': 7.28.5 '@jridgewell/sourcemap-codec': 1.5.5 - chokidar: 4.0.3 + chokidar: 5.0.0 convert-source-map: 1.9.0 reflect-metadata: 0.2.2 semver: 7.7.3 @@ -9495,91 +9311,90 @@ snapshots: transitivePeerDependencies: - supports-color - '@angular/compiler@21.0.0-rc.2': + '@angular/compiler@21.1.0-next.4': dependencies: tslib: 2.8.1 - '@angular/core@21.0.0-rc.2(@angular/compiler@21.0.0-rc.2)(rxjs@7.8.2)(zone.js@0.15.1)': + '@angular/core@21.1.0-next.4(@angular/compiler@21.1.0-next.4)(rxjs@7.8.2)(zone.js@0.16.0)': dependencies: rxjs: 7.8.2 tslib: 2.8.1 optionalDependencies: - '@angular/compiler': 21.0.0-rc.2 - zone.js: 0.15.1 + '@angular/compiler': 21.1.0-next.4 + zone.js: 0.16.0 - '@angular/forms@21.0.0-rc.2(@angular/common@21.0.0-rc.2(@angular/core@21.0.0-rc.2(@angular/compiler@21.0.0-rc.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@21.0.0-rc.2(@angular/compiler@21.0.0-rc.2)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@21.0.0-rc.2(@angular/animations@21.0.0-rc.2(@angular/core@21.0.0-rc.2(@angular/compiler@21.0.0-rc.2)(rxjs@7.8.2)(zone.js@0.15.1)))(@angular/common@21.0.0-rc.2(@angular/core@21.0.0-rc.2(@angular/compiler@21.0.0-rc.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@21.0.0-rc.2(@angular/compiler@21.0.0-rc.2)(rxjs@7.8.2)(zone.js@0.15.1)))(@standard-schema/spec@1.0.0)(rxjs@7.8.2)': + '@angular/forms@21.1.0-next.4(@angular/common@21.1.0-next.4(@angular/core@21.1.0-next.4(@angular/compiler@21.1.0-next.4)(rxjs@7.8.2)(zone.js@0.16.0))(rxjs@7.8.2))(@angular/core@21.1.0-next.4(@angular/compiler@21.1.0-next.4)(rxjs@7.8.2)(zone.js@0.16.0))(@angular/platform-browser@21.1.0-next.4(@angular/animations@21.1.0-next.4(@angular/core@21.1.0-next.4(@angular/compiler@21.1.0-next.4)(rxjs@7.8.2)(zone.js@0.16.0)))(@angular/common@21.1.0-next.4(@angular/core@21.1.0-next.4(@angular/compiler@21.1.0-next.4)(rxjs@7.8.2)(zone.js@0.16.0))(rxjs@7.8.2))(@angular/core@21.1.0-next.4(@angular/compiler@21.1.0-next.4)(rxjs@7.8.2)(zone.js@0.16.0)))(rxjs@7.8.2)': dependencies: - '@angular/common': 21.0.0-rc.2(@angular/core@21.0.0-rc.2(@angular/compiler@21.0.0-rc.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2) - '@angular/core': 21.0.0-rc.2(@angular/compiler@21.0.0-rc.2)(rxjs@7.8.2)(zone.js@0.15.1) - '@angular/platform-browser': 21.0.0-rc.2(@angular/animations@21.0.0-rc.2(@angular/core@21.0.0-rc.2(@angular/compiler@21.0.0-rc.2)(rxjs@7.8.2)(zone.js@0.15.1)))(@angular/common@21.0.0-rc.2(@angular/core@21.0.0-rc.2(@angular/compiler@21.0.0-rc.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@21.0.0-rc.2(@angular/compiler@21.0.0-rc.2)(rxjs@7.8.2)(zone.js@0.15.1)) - '@standard-schema/spec': 1.0.0 + '@angular/common': 21.1.0-next.4(@angular/core@21.1.0-next.4(@angular/compiler@21.1.0-next.4)(rxjs@7.8.2)(zone.js@0.16.0))(rxjs@7.8.2) + '@angular/core': 21.1.0-next.4(@angular/compiler@21.1.0-next.4)(rxjs@7.8.2)(zone.js@0.16.0) + '@angular/platform-browser': 21.1.0-next.4(@angular/animations@21.1.0-next.4(@angular/core@21.1.0-next.4(@angular/compiler@21.1.0-next.4)(rxjs@7.8.2)(zone.js@0.16.0)))(@angular/common@21.1.0-next.4(@angular/core@21.1.0-next.4(@angular/compiler@21.1.0-next.4)(rxjs@7.8.2)(zone.js@0.16.0))(rxjs@7.8.2))(@angular/core@21.1.0-next.4(@angular/compiler@21.1.0-next.4)(rxjs@7.8.2)(zone.js@0.16.0)) + '@standard-schema/spec': 1.1.0 rxjs: 7.8.2 tslib: 2.8.1 - '@angular/localize@21.0.0-rc.2(@angular/compiler-cli@21.0.0-rc.2(@angular/compiler@21.0.0-rc.2)(typescript@5.9.3))(@angular/compiler@21.0.0-rc.2)': + '@angular/localize@21.1.0-next.4(@angular/compiler-cli@21.1.0-next.4(@angular/compiler@21.1.0-next.4)(typescript@5.9.3))(@angular/compiler@21.1.0-next.4)': dependencies: - '@angular/compiler': 21.0.0-rc.2 - '@angular/compiler-cli': 21.0.0-rc.2(@angular/compiler@21.0.0-rc.2)(typescript@5.9.3) - '@babel/core': 7.28.4 + '@angular/compiler': 21.1.0-next.4 + '@angular/compiler-cli': 21.1.0-next.4(@angular/compiler@21.1.0-next.4)(typescript@5.9.3) + '@babel/core': 7.28.5 '@types/babel__core': 7.20.5 tinyglobby: 0.2.15 yargs: 18.0.0 transitivePeerDependencies: - supports-color - '@angular/material@21.0.0-rc.2(4c9afee98155400dc203886c5442fc76)': + '@angular/material@21.1.0-next.3(5911ac44acdb5e81564606f5886cc827)': dependencies: - '@angular/cdk': 21.0.0-rc.2(@angular/common@21.0.0-rc.2(@angular/core@21.0.0-rc.2(@angular/compiler@21.0.0-rc.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@21.0.0-rc.2(@angular/compiler@21.0.0-rc.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2) - '@angular/common': 21.0.0-rc.2(@angular/core@21.0.0-rc.2(@angular/compiler@21.0.0-rc.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2) - '@angular/core': 21.0.0-rc.2(@angular/compiler@21.0.0-rc.2)(rxjs@7.8.2)(zone.js@0.15.1) - '@angular/forms': 21.0.0-rc.2(@angular/common@21.0.0-rc.2(@angular/core@21.0.0-rc.2(@angular/compiler@21.0.0-rc.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@21.0.0-rc.2(@angular/compiler@21.0.0-rc.2)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@21.0.0-rc.2(@angular/animations@21.0.0-rc.2(@angular/core@21.0.0-rc.2(@angular/compiler@21.0.0-rc.2)(rxjs@7.8.2)(zone.js@0.15.1)))(@angular/common@21.0.0-rc.2(@angular/core@21.0.0-rc.2(@angular/compiler@21.0.0-rc.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@21.0.0-rc.2(@angular/compiler@21.0.0-rc.2)(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-rc.2(@angular/animations@21.0.0-rc.2(@angular/core@21.0.0-rc.2(@angular/compiler@21.0.0-rc.2)(rxjs@7.8.2)(zone.js@0.15.1)))(@angular/common@21.0.0-rc.2(@angular/core@21.0.0-rc.2(@angular/compiler@21.0.0-rc.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@21.0.0-rc.2(@angular/compiler@21.0.0-rc.2)(rxjs@7.8.2)(zone.js@0.15.1)) + '@angular/cdk': 21.1.0-next.3(@angular/common@21.1.0-next.4(@angular/core@21.1.0-next.4(@angular/compiler@21.1.0-next.4)(rxjs@7.8.2)(zone.js@0.16.0))(rxjs@7.8.2))(@angular/core@21.1.0-next.4(@angular/compiler@21.1.0-next.4)(rxjs@7.8.2)(zone.js@0.16.0))(@angular/platform-browser@21.1.0-next.4(@angular/animations@21.1.0-next.4(@angular/core@21.1.0-next.4(@angular/compiler@21.1.0-next.4)(rxjs@7.8.2)(zone.js@0.16.0)))(@angular/common@21.1.0-next.4(@angular/core@21.1.0-next.4(@angular/compiler@21.1.0-next.4)(rxjs@7.8.2)(zone.js@0.16.0))(rxjs@7.8.2))(@angular/core@21.1.0-next.4(@angular/compiler@21.1.0-next.4)(rxjs@7.8.2)(zone.js@0.16.0)))(rxjs@7.8.2) + '@angular/common': 21.1.0-next.4(@angular/core@21.1.0-next.4(@angular/compiler@21.1.0-next.4)(rxjs@7.8.2)(zone.js@0.16.0))(rxjs@7.8.2) + '@angular/core': 21.1.0-next.4(@angular/compiler@21.1.0-next.4)(rxjs@7.8.2)(zone.js@0.16.0) + '@angular/forms': 21.1.0-next.4(@angular/common@21.1.0-next.4(@angular/core@21.1.0-next.4(@angular/compiler@21.1.0-next.4)(rxjs@7.8.2)(zone.js@0.16.0))(rxjs@7.8.2))(@angular/core@21.1.0-next.4(@angular/compiler@21.1.0-next.4)(rxjs@7.8.2)(zone.js@0.16.0))(@angular/platform-browser@21.1.0-next.4(@angular/animations@21.1.0-next.4(@angular/core@21.1.0-next.4(@angular/compiler@21.1.0-next.4)(rxjs@7.8.2)(zone.js@0.16.0)))(@angular/common@21.1.0-next.4(@angular/core@21.1.0-next.4(@angular/compiler@21.1.0-next.4)(rxjs@7.8.2)(zone.js@0.16.0))(rxjs@7.8.2))(@angular/core@21.1.0-next.4(@angular/compiler@21.1.0-next.4)(rxjs@7.8.2)(zone.js@0.16.0)))(rxjs@7.8.2) + '@angular/platform-browser': 21.1.0-next.4(@angular/animations@21.1.0-next.4(@angular/core@21.1.0-next.4(@angular/compiler@21.1.0-next.4)(rxjs@7.8.2)(zone.js@0.16.0)))(@angular/common@21.1.0-next.4(@angular/core@21.1.0-next.4(@angular/compiler@21.1.0-next.4)(rxjs@7.8.2)(zone.js@0.16.0))(rxjs@7.8.2))(@angular/core@21.1.0-next.4(@angular/compiler@21.1.0-next.4)(rxjs@7.8.2)(zone.js@0.16.0)) rxjs: 7.8.2 tslib: 2.8.1 - '@angular/ng-dev@https://codeload.github.com/angular/dev-infra-private-ng-dev-builds/tar.gz/49d7316da246484759b94f87e6eda47fde86b992(@modelcontextprotocol/sdk@1.21.1)': + '@angular/ng-dev@https://codeload.github.com/angular/dev-infra-private-ng-dev-builds/tar.gz/ddc3809c1993612732eaae62d28e828b2ed789e5(@modelcontextprotocol/sdk@1.25.0(zod@4.2.1))': dependencies: - '@actions/core': 1.11.1 + '@actions/core': 2.0.1 '@google-cloud/spanner': 8.0.0(supports-color@10.2.2) - '@google/genai': 1.29.0(@modelcontextprotocol/sdk@1.21.1)(bufferutil@4.0.9)(supports-color@10.2.2)(utf-8-validate@6.0.5) - '@inquirer/prompts': 7.10.0(@types/node@24.10.0) - '@inquirer/type': 3.0.10(@types/node@24.10.0) + '@google/genai': 1.33.0(@modelcontextprotocol/sdk@1.25.0(zod@4.2.1))(bufferutil@4.0.9)(supports-color@10.2.2)(utf-8-validate@6.0.5) + '@inquirer/prompts': 8.1.0(@types/node@24.10.4) + '@inquirer/type': 4.0.2(@types/node@24.10.4) '@octokit/auth-app': 8.1.2 '@octokit/core': 7.0.6 '@octokit/graphql': 9.0.3 - '@octokit/graphql-schema': 15.26.0 + '@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.0.2 + '@octokit/request-error': 7.1.0 '@octokit/rest': 22.0.1 '@octokit/types': 16.0.0 - '@pnpm/dependency-path': 1001.1.4 + '@pnpm/dependency-path': 1001.1.8 '@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.1 - '@types/jasmine': 5.1.12 - '@types/node': 24.10.0 + '@types/jasmine': 5.1.13 + '@types/node': 24.10.4 '@types/semver': 7.7.1 '@types/which': 3.0.4 - '@types/yargs': 17.0.34 + '@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.5.0 + firebase: 12.7.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.1 + jasmine: 5.13.0 + jasmine-core: 5.13.0 jasmine-reporters: 2.5.2 jsonc-parser: 3.3.1 minimatch: 10.1.1 @@ -9587,64 +9402,64 @@ snapshots: nock: 14.0.10 semver: 7.7.3 supports-color: 10.2.2 - tsx: 4.20.6 + tsx: 4.21.0 typed-graphqlify: 3.1.6 typescript: 5.9.3 utf-8-validate: 6.0.5 which: 6.0.0 - yaml: 2.8.1 + yaml: 2.8.2 yargs: 18.0.0 transitivePeerDependencies: - '@modelcontextprotocol/sdk' - '@react-native-async-storage/async-storage' - '@angular/platform-browser@21.0.0-rc.2(@angular/animations@21.0.0-rc.2(@angular/core@21.0.0-rc.2(@angular/compiler@21.0.0-rc.2)(rxjs@7.8.2)(zone.js@0.15.1)))(@angular/common@21.0.0-rc.2(@angular/core@21.0.0-rc.2(@angular/compiler@21.0.0-rc.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@21.0.0-rc.2(@angular/compiler@21.0.0-rc.2)(rxjs@7.8.2)(zone.js@0.15.1))': + '@angular/platform-browser@21.1.0-next.4(@angular/animations@21.1.0-next.4(@angular/core@21.1.0-next.4(@angular/compiler@21.1.0-next.4)(rxjs@7.8.2)(zone.js@0.16.0)))(@angular/common@21.1.0-next.4(@angular/core@21.1.0-next.4(@angular/compiler@21.1.0-next.4)(rxjs@7.8.2)(zone.js@0.16.0))(rxjs@7.8.2))(@angular/core@21.1.0-next.4(@angular/compiler@21.1.0-next.4)(rxjs@7.8.2)(zone.js@0.16.0))': dependencies: - '@angular/common': 21.0.0-rc.2(@angular/core@21.0.0-rc.2(@angular/compiler@21.0.0-rc.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2) - '@angular/core': 21.0.0-rc.2(@angular/compiler@21.0.0-rc.2)(rxjs@7.8.2)(zone.js@0.15.1) + '@angular/common': 21.1.0-next.4(@angular/core@21.1.0-next.4(@angular/compiler@21.1.0-next.4)(rxjs@7.8.2)(zone.js@0.16.0))(rxjs@7.8.2) + '@angular/core': 21.1.0-next.4(@angular/compiler@21.1.0-next.4)(rxjs@7.8.2)(zone.js@0.16.0) tslib: 2.8.1 optionalDependencies: - '@angular/animations': 21.0.0-rc.2(@angular/core@21.0.0-rc.2(@angular/compiler@21.0.0-rc.2)(rxjs@7.8.2)(zone.js@0.15.1)) + '@angular/animations': 21.1.0-next.4(@angular/core@21.1.0-next.4(@angular/compiler@21.1.0-next.4)(rxjs@7.8.2)(zone.js@0.16.0)) - '@angular/platform-server@21.0.0-rc.2(@angular/common@21.0.0-rc.2(@angular/core@21.0.0-rc.2(@angular/compiler@21.0.0-rc.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/compiler@21.0.0-rc.2)(@angular/core@21.0.0-rc.2(@angular/compiler@21.0.0-rc.2)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@21.0.0-rc.2(@angular/animations@21.0.0-rc.2(@angular/core@21.0.0-rc.2(@angular/compiler@21.0.0-rc.2)(rxjs@7.8.2)(zone.js@0.15.1)))(@angular/common@21.0.0-rc.2(@angular/core@21.0.0-rc.2(@angular/compiler@21.0.0-rc.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@21.0.0-rc.2(@angular/compiler@21.0.0-rc.2)(rxjs@7.8.2)(zone.js@0.15.1)))(rxjs@7.8.2)': + '@angular/platform-server@21.1.0-next.4(@angular/common@21.1.0-next.4(@angular/core@21.1.0-next.4(@angular/compiler@21.1.0-next.4)(rxjs@7.8.2)(zone.js@0.16.0))(rxjs@7.8.2))(@angular/compiler@21.1.0-next.4)(@angular/core@21.1.0-next.4(@angular/compiler@21.1.0-next.4)(rxjs@7.8.2)(zone.js@0.16.0))(@angular/platform-browser@21.1.0-next.4(@angular/animations@21.1.0-next.4(@angular/core@21.1.0-next.4(@angular/compiler@21.1.0-next.4)(rxjs@7.8.2)(zone.js@0.16.0)))(@angular/common@21.1.0-next.4(@angular/core@21.1.0-next.4(@angular/compiler@21.1.0-next.4)(rxjs@7.8.2)(zone.js@0.16.0))(rxjs@7.8.2))(@angular/core@21.1.0-next.4(@angular/compiler@21.1.0-next.4)(rxjs@7.8.2)(zone.js@0.16.0)))(rxjs@7.8.2)': dependencies: - '@angular/common': 21.0.0-rc.2(@angular/core@21.0.0-rc.2(@angular/compiler@21.0.0-rc.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2) - '@angular/compiler': 21.0.0-rc.2 - '@angular/core': 21.0.0-rc.2(@angular/compiler@21.0.0-rc.2)(rxjs@7.8.2)(zone.js@0.15.1) - '@angular/platform-browser': 21.0.0-rc.2(@angular/animations@21.0.0-rc.2(@angular/core@21.0.0-rc.2(@angular/compiler@21.0.0-rc.2)(rxjs@7.8.2)(zone.js@0.15.1)))(@angular/common@21.0.0-rc.2(@angular/core@21.0.0-rc.2(@angular/compiler@21.0.0-rc.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@21.0.0-rc.2(@angular/compiler@21.0.0-rc.2)(rxjs@7.8.2)(zone.js@0.15.1)) + '@angular/common': 21.1.0-next.4(@angular/core@21.1.0-next.4(@angular/compiler@21.1.0-next.4)(rxjs@7.8.2)(zone.js@0.16.0))(rxjs@7.8.2) + '@angular/compiler': 21.1.0-next.4 + '@angular/core': 21.1.0-next.4(@angular/compiler@21.1.0-next.4)(rxjs@7.8.2)(zone.js@0.16.0) + '@angular/platform-browser': 21.1.0-next.4(@angular/animations@21.1.0-next.4(@angular/core@21.1.0-next.4(@angular/compiler@21.1.0-next.4)(rxjs@7.8.2)(zone.js@0.16.0)))(@angular/common@21.1.0-next.4(@angular/core@21.1.0-next.4(@angular/compiler@21.1.0-next.4)(rxjs@7.8.2)(zone.js@0.16.0))(rxjs@7.8.2))(@angular/core@21.1.0-next.4(@angular/compiler@21.1.0-next.4)(rxjs@7.8.2)(zone.js@0.16.0)) rxjs: 7.8.2 tslib: 2.8.1 xhr2: 0.2.1 - '@angular/router@21.0.0-rc.2(@angular/common@21.0.0-rc.2(@angular/core@21.0.0-rc.2(@angular/compiler@21.0.0-rc.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@21.0.0-rc.2(@angular/compiler@21.0.0-rc.2)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@21.0.0-rc.2(@angular/animations@21.0.0-rc.2(@angular/core@21.0.0-rc.2(@angular/compiler@21.0.0-rc.2)(rxjs@7.8.2)(zone.js@0.15.1)))(@angular/common@21.0.0-rc.2(@angular/core@21.0.0-rc.2(@angular/compiler@21.0.0-rc.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@21.0.0-rc.2(@angular/compiler@21.0.0-rc.2)(rxjs@7.8.2)(zone.js@0.15.1)))(rxjs@7.8.2)': + '@angular/router@21.1.0-next.4(@angular/common@21.1.0-next.4(@angular/core@21.1.0-next.4(@angular/compiler@21.1.0-next.4)(rxjs@7.8.2)(zone.js@0.16.0))(rxjs@7.8.2))(@angular/core@21.1.0-next.4(@angular/compiler@21.1.0-next.4)(rxjs@7.8.2)(zone.js@0.16.0))(@angular/platform-browser@21.1.0-next.4(@angular/animations@21.1.0-next.4(@angular/core@21.1.0-next.4(@angular/compiler@21.1.0-next.4)(rxjs@7.8.2)(zone.js@0.16.0)))(@angular/common@21.1.0-next.4(@angular/core@21.1.0-next.4(@angular/compiler@21.1.0-next.4)(rxjs@7.8.2)(zone.js@0.16.0))(rxjs@7.8.2))(@angular/core@21.1.0-next.4(@angular/compiler@21.1.0-next.4)(rxjs@7.8.2)(zone.js@0.16.0)))(rxjs@7.8.2)': dependencies: - '@angular/common': 21.0.0-rc.2(@angular/core@21.0.0-rc.2(@angular/compiler@21.0.0-rc.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2) - '@angular/core': 21.0.0-rc.2(@angular/compiler@21.0.0-rc.2)(rxjs@7.8.2)(zone.js@0.15.1) - '@angular/platform-browser': 21.0.0-rc.2(@angular/animations@21.0.0-rc.2(@angular/core@21.0.0-rc.2(@angular/compiler@21.0.0-rc.2)(rxjs@7.8.2)(zone.js@0.15.1)))(@angular/common@21.0.0-rc.2(@angular/core@21.0.0-rc.2(@angular/compiler@21.0.0-rc.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@21.0.0-rc.2(@angular/compiler@21.0.0-rc.2)(rxjs@7.8.2)(zone.js@0.15.1)) + '@angular/common': 21.1.0-next.4(@angular/core@21.1.0-next.4(@angular/compiler@21.1.0-next.4)(rxjs@7.8.2)(zone.js@0.16.0))(rxjs@7.8.2) + '@angular/core': 21.1.0-next.4(@angular/compiler@21.1.0-next.4)(rxjs@7.8.2)(zone.js@0.16.0) + '@angular/platform-browser': 21.1.0-next.4(@angular/animations@21.1.0-next.4(@angular/core@21.1.0-next.4(@angular/compiler@21.1.0-next.4)(rxjs@7.8.2)(zone.js@0.16.0)))(@angular/common@21.1.0-next.4(@angular/core@21.1.0-next.4(@angular/compiler@21.1.0-next.4)(rxjs@7.8.2)(zone.js@0.16.0))(rxjs@7.8.2))(@angular/core@21.1.0-next.4(@angular/compiler@21.1.0-next.4)(rxjs@7.8.2)(zone.js@0.16.0)) rxjs: 7.8.2 tslib: 2.8.1 - '@angular/service-worker@21.0.0-rc.2(@angular/core@21.0.0-rc.2(@angular/compiler@21.0.0-rc.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2)': + '@angular/service-worker@21.1.0-next.4(@angular/core@21.1.0-next.4(@angular/compiler@21.1.0-next.4)(rxjs@7.8.2)(zone.js@0.16.0))(rxjs@7.8.2)': dependencies: - '@angular/core': 21.0.0-rc.2(@angular/compiler@21.0.0-rc.2)(rxjs@7.8.2)(zone.js@0.15.1) + '@angular/core': 21.1.0-next.4(@angular/compiler@21.1.0-next.4)(rxjs@7.8.2)(zone.js@0.16.0) rxjs: 7.8.2 tslib: 2.8.1 - '@asamuzakjp/css-color@4.0.5': + '@asamuzakjp/css-color@4.1.1': 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) '@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4) '@csstools/css-tokenizer': 3.0.4 - lru-cache: 11.2.2 + lru-cache: 11.2.4 - '@asamuzakjp/dom-selector@6.7.4': + '@asamuzakjp/dom-selector@6.7.6': dependencies: '@asamuzakjp/nwsapi': 2.3.9 bidi-js: 1.0.3 css-tree: 3.1.0 is-potential-custom-element-name: 1.0.1 - lru-cache: 11.2.2 + lru-cache: 11.2.4 '@asamuzakjp/nwsapi@2.3.9': {} @@ -9656,26 +9471,6 @@ snapshots: '@babel/compat-data@7.28.5': {} - '@babel/core@7.28.4': - dependencies: - '@babel/code-frame': 7.27.1 - '@babel/generator': 7.28.5 - '@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.5 - '@babel/template': 7.27.2 - '@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) - gensync: 1.0.0-beta.2 - json5: 2.2.3 - semver: 6.3.1 - transitivePeerDependencies: - - supports-color - '@babel/core@7.28.5': dependencies: '@babel/code-frame': 7.27.1 @@ -9712,7 +9507,7 @@ snapshots: dependencies: '@babel/compat-data': 7.28.5 '@babel/helper-validator-option': 7.27.1 - browserslist: 4.27.0 + browserslist: 4.28.1 lru-cache: 5.1.1 semver: 6.3.1 @@ -9763,15 +9558,6 @@ snapshots: transitivePeerDependencies: - supports-color - '@babel/helper-module-transforms@7.28.3(@babel/core@7.28.4)': - dependencies: - '@babel/core': 7.28.4 - '@babel/helper-module-imports': 7.27.1 - '@babel/helper-validator-identifier': 7.28.5 - '@babel/traverse': 7.28.5 - transitivePeerDependencies: - - supports-color - '@babel/helper-module-transforms@7.28.3(@babel/core@7.28.5)': dependencies: '@babel/core': 7.28.5 @@ -10310,7 +10096,7 @@ snapshots: babel-plugin-polyfill-corejs2: 0.4.14(@babel/core@7.28.5) babel-plugin-polyfill-corejs3: 0.13.0(@babel/core@7.28.5) babel-plugin-polyfill-regenerator: 0.6.5(@babel/core@7.28.5) - core-js-compat: 3.46.0 + core-js-compat: 3.47.0 semver: 6.3.1 transitivePeerDependencies: - supports-color @@ -10385,7 +10171,7 @@ snapshots: dependencies: '@csstools/css-tokenizer': 3.0.4 - '@csstools/css-syntax-patches-for-csstree@1.0.15': {} + '@csstools/css-syntax-patches-for-csstree@1.0.22': {} '@csstools/css-tokenizer@3.0.4': {} @@ -10397,7 +10183,7 @@ snapshots: combined-stream: 1.0.8 extend: 3.0.2 forever-agent: 0.6.1 - form-data: 4.0.4 + form-data: 4.0.5 http-signature: 1.4.0 is-typedarray: 1.0.0 isstream: 0.1.2 @@ -10412,13 +10198,13 @@ snapshots: '@discoveryjs/json-ext@0.6.3': {} - '@emnapi/core@1.7.0': + '@emnapi/core@1.7.1': dependencies: '@emnapi/wasi-threads': 1.1.0 tslib: 2.8.1 optional: true - '@emnapi/runtime@1.7.0': + '@emnapi/runtime@1.7.1': dependencies: tslib: 2.8.1 optional: true @@ -10428,174 +10214,96 @@ snapshots: tslib: 2.8.1 optional: true - '@esbuild/aix-ppc64@0.25.12': - optional: true - - '@esbuild/aix-ppc64@0.27.0': - optional: true - - '@esbuild/android-arm64@0.25.12': - optional: true - - '@esbuild/android-arm64@0.27.0': - optional: true - - '@esbuild/android-arm@0.25.12': - optional: true - - '@esbuild/android-arm@0.27.0': - optional: true - - '@esbuild/android-x64@0.25.12': - optional: true - - '@esbuild/android-x64@0.27.0': - optional: true - - '@esbuild/darwin-arm64@0.25.12': - optional: true - - '@esbuild/darwin-arm64@0.27.0': - optional: true - - '@esbuild/darwin-x64@0.25.12': - optional: true - - '@esbuild/darwin-x64@0.27.0': - optional: true - - '@esbuild/freebsd-arm64@0.25.12': - optional: true - - '@esbuild/freebsd-arm64@0.27.0': - optional: true - - '@esbuild/freebsd-x64@0.25.12': - optional: true - - '@esbuild/freebsd-x64@0.27.0': - optional: true - - '@esbuild/linux-arm64@0.25.12': - optional: true - - '@esbuild/linux-arm64@0.27.0': - optional: true - - '@esbuild/linux-arm@0.25.12': - optional: true - - '@esbuild/linux-arm@0.27.0': - optional: true - - '@esbuild/linux-ia32@0.25.12': - optional: true - - '@esbuild/linux-ia32@0.27.0': - optional: true - - '@esbuild/linux-loong64@0.25.12': - optional: true - - '@esbuild/linux-loong64@0.27.0': - optional: true - - '@esbuild/linux-mips64el@0.25.12': + '@esbuild/aix-ppc64@0.27.1': optional: true - '@esbuild/linux-mips64el@0.27.0': + '@esbuild/android-arm64@0.27.1': optional: true - '@esbuild/linux-ppc64@0.25.12': + '@esbuild/android-arm@0.27.1': optional: true - '@esbuild/linux-ppc64@0.27.0': + '@esbuild/android-x64@0.27.1': optional: true - '@esbuild/linux-riscv64@0.25.12': + '@esbuild/darwin-arm64@0.27.1': optional: true - '@esbuild/linux-riscv64@0.27.0': + '@esbuild/darwin-x64@0.27.1': optional: true - '@esbuild/linux-s390x@0.25.12': + '@esbuild/freebsd-arm64@0.27.1': optional: true - '@esbuild/linux-s390x@0.27.0': + '@esbuild/freebsd-x64@0.27.1': optional: true - '@esbuild/linux-x64@0.25.12': + '@esbuild/linux-arm64@0.27.1': optional: true - '@esbuild/linux-x64@0.27.0': + '@esbuild/linux-arm@0.27.1': optional: true - '@esbuild/netbsd-arm64@0.25.12': + '@esbuild/linux-ia32@0.27.1': optional: true - '@esbuild/netbsd-arm64@0.27.0': + '@esbuild/linux-loong64@0.27.1': optional: true - '@esbuild/netbsd-x64@0.25.12': + '@esbuild/linux-mips64el@0.27.1': optional: true - '@esbuild/netbsd-x64@0.27.0': + '@esbuild/linux-ppc64@0.27.1': optional: true - '@esbuild/openbsd-arm64@0.25.12': + '@esbuild/linux-riscv64@0.27.1': optional: true - '@esbuild/openbsd-arm64@0.27.0': + '@esbuild/linux-s390x@0.27.1': optional: true - '@esbuild/openbsd-x64@0.25.12': + '@esbuild/linux-x64@0.27.1': optional: true - '@esbuild/openbsd-x64@0.27.0': + '@esbuild/netbsd-arm64@0.27.1': optional: true - '@esbuild/openharmony-arm64@0.25.12': + '@esbuild/netbsd-x64@0.27.1': optional: true - '@esbuild/openharmony-arm64@0.27.0': + '@esbuild/openbsd-arm64@0.27.1': optional: true - '@esbuild/sunos-x64@0.25.12': + '@esbuild/openbsd-x64@0.27.1': optional: true - '@esbuild/sunos-x64@0.27.0': + '@esbuild/openharmony-arm64@0.27.1': optional: true - '@esbuild/win32-arm64@0.25.12': + '@esbuild/sunos-x64@0.27.1': optional: true - '@esbuild/win32-arm64@0.27.0': + '@esbuild/win32-arm64@0.27.1': optional: true - '@esbuild/win32-ia32@0.25.12': + '@esbuild/win32-ia32@0.27.1': optional: true - '@esbuild/win32-ia32@0.27.0': + '@esbuild/win32-x64@0.27.1': optional: true - '@esbuild/win32-x64@0.25.12': - optional: true - - '@esbuild/win32-x64@0.27.0': - optional: true - - '@eslint-community/eslint-utils@4.9.0(eslint@9.39.1(jiti@2.6.1))': + '@eslint-community/eslint-utils@4.9.0(eslint@9.39.2(jiti@2.6.1))': dependencies: - eslint: 9.39.1(jiti@2.6.1) + eslint: 9.39.2(jiti@2.6.1) eslint-visitor-keys: 3.4.3 '@eslint-community/regexpp@4.12.2': {} - '@eslint/compat@1.4.1(eslint@9.39.1(jiti@2.6.1))': + '@eslint/compat@2.0.0(eslint@9.39.2(jiti@2.6.1))': dependencies: - '@eslint/core': 0.17.0 + '@eslint/core': 1.0.0 optionalDependencies: - eslint: 9.39.1(jiti@2.6.1) + eslint: 9.39.2(jiti@2.6.1) '@eslint/config-array@0.21.1': dependencies: @@ -10613,7 +10321,11 @@ snapshots: dependencies: '@types/json-schema': 7.0.15 - '@eslint/eslintrc@3.3.1': + '@eslint/core@1.0.0': + dependencies: + '@types/json-schema': 7.0.15 + + '@eslint/eslintrc@3.3.3': dependencies: ajv: 6.12.6 debug: 4.4.3(supports-color@10.2.2) @@ -10621,13 +10333,13 @@ 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: - supports-color - '@eslint/js@9.39.1': {} + '@eslint/js@9.39.2': {} '@eslint/object-schema@2.1.7': {} @@ -10638,9 +10350,9 @@ snapshots: '@fastify/busboy@2.1.1': {} - '@firebase/ai@2.5.0(@firebase/app-types@0.9.3)(@firebase/app@0.14.5)': + '@firebase/ai@2.6.1(@firebase/app-types@0.9.3)(@firebase/app@0.14.6)': dependencies: - '@firebase/app': 0.14.5 + '@firebase/app': 0.14.6 '@firebase/app-check-interop-types': 0.3.3 '@firebase/app-types': 0.9.3 '@firebase/component': 0.7.0 @@ -10648,11 +10360,11 @@ snapshots: '@firebase/util': 1.13.0 tslib: 2.8.1 - '@firebase/analytics-compat@0.2.25(@firebase/app-compat@0.5.5)(@firebase/app@0.14.5)': + '@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.5) + '@firebase/analytics': 0.10.19(@firebase/app@0.14.6) '@firebase/analytics-types': 0.8.3 - '@firebase/app-compat': 0.5.5 + '@firebase/app-compat': 0.5.6 '@firebase/component': 0.7.0 '@firebase/util': 1.13.0 tslib: 2.8.1 @@ -10661,20 +10373,20 @@ snapshots: '@firebase/analytics-types@0.8.3': {} - '@firebase/analytics@0.10.19(@firebase/app@0.14.5)': + '@firebase/analytics@0.10.19(@firebase/app@0.14.6)': dependencies: - '@firebase/app': 0.14.5 + '@firebase/app': 0.14.6 '@firebase/component': 0.7.0 - '@firebase/installations': 0.6.19(@firebase/app@0.14.5) + '@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.5)(@firebase/app@0.14.5)': + '@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.5) + '@firebase/app-check': 0.11.0(@firebase/app@0.14.6) '@firebase/app-check-types': 0.5.3 - '@firebase/app-compat': 0.5.5 + '@firebase/app-compat': 0.5.6 '@firebase/component': 0.7.0 '@firebase/logger': 0.5.0 '@firebase/util': 1.13.0 @@ -10686,17 +10398,17 @@ snapshots: '@firebase/app-check-types@0.5.3': {} - '@firebase/app-check@0.11.0(@firebase/app@0.14.5)': + '@firebase/app-check@0.11.0(@firebase/app@0.14.6)': dependencies: - '@firebase/app': 0.14.5 + '@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.5': + '@firebase/app-compat@0.5.6': dependencies: - '@firebase/app': 0.14.5 + '@firebase/app': 0.14.6 '@firebase/component': 0.7.0 '@firebase/logger': 0.5.0 '@firebase/util': 1.13.0 @@ -10704,7 +10416,7 @@ snapshots: '@firebase/app-types@0.9.3': {} - '@firebase/app@0.14.5': + '@firebase/app@0.14.6': dependencies: '@firebase/component': 0.7.0 '@firebase/logger': 0.5.0 @@ -10712,10 +10424,10 @@ snapshots: idb: 7.1.1 tslib: 2.8.1 - '@firebase/auth-compat@0.6.1(@firebase/app-compat@0.5.5)(@firebase/app-types@0.9.3)(@firebase/app@0.14.5)': + '@firebase/auth-compat@0.6.2(@firebase/app-compat@0.5.6)(@firebase/app-types@0.9.3)(@firebase/app@0.14.6)': dependencies: - '@firebase/app-compat': 0.5.5 - '@firebase/auth': 1.11.1(@firebase/app@0.14.5) + '@firebase/app-compat': 0.5.6 + '@firebase/auth': 1.12.0(@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 @@ -10732,9 +10444,9 @@ snapshots: '@firebase/app-types': 0.9.3 '@firebase/util': 1.13.0 - '@firebase/auth@1.11.1(@firebase/app@0.14.5)': + '@firebase/auth@1.12.0(@firebase/app@0.14.6)': dependencies: - '@firebase/app': 0.14.5 + '@firebase/app': 0.14.6 '@firebase/component': 0.7.0 '@firebase/logger': 0.5.0 '@firebase/util': 1.13.0 @@ -10745,9 +10457,9 @@ snapshots: '@firebase/util': 1.13.0 tslib: 2.8.1 - '@firebase/data-connect@0.3.11(@firebase/app@0.14.5)': + '@firebase/data-connect@0.3.12(@firebase/app@0.14.6)': dependencies: - '@firebase/app': 0.14.5 + '@firebase/app': 0.14.6 '@firebase/auth-interop-types': 0.2.4 '@firebase/component': 0.7.0 '@firebase/logger': 0.5.0 @@ -10778,11 +10490,11 @@ snapshots: faye-websocket: 0.11.4 tslib: 2.8.1 - '@firebase/firestore-compat@0.4.2(@firebase/app-compat@0.5.5)(@firebase/app-types@0.9.3)(@firebase/app@0.14.5)': + '@firebase/firestore-compat@0.4.3(@firebase/app-compat@0.5.6)(@firebase/app-types@0.9.3)(@firebase/app@0.14.6)': dependencies: - '@firebase/app-compat': 0.5.5 + '@firebase/app-compat': 0.5.6 '@firebase/component': 0.7.0 - '@firebase/firestore': 4.9.2(@firebase/app@0.14.5) + '@firebase/firestore': 4.9.3(@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 @@ -10795,9 +10507,9 @@ snapshots: '@firebase/app-types': 0.9.3 '@firebase/util': 1.13.0 - '@firebase/firestore@4.9.2(@firebase/app@0.14.5)': + '@firebase/firestore@4.9.3(@firebase/app@0.14.6)': dependencies: - '@firebase/app': 0.14.5 + '@firebase/app': 0.14.6 '@firebase/component': 0.7.0 '@firebase/logger': 0.5.0 '@firebase/util': 1.13.0 @@ -10806,11 +10518,11 @@ snapshots: '@grpc/proto-loader': 0.7.15 tslib: 2.8.1 - '@firebase/functions-compat@0.4.1(@firebase/app-compat@0.5.5)(@firebase/app@0.14.5)': + '@firebase/functions-compat@0.4.1(@firebase/app-compat@0.5.6)(@firebase/app@0.14.6)': dependencies: - '@firebase/app-compat': 0.5.5 + '@firebase/app-compat': 0.5.6 '@firebase/component': 0.7.0 - '@firebase/functions': 0.13.1(@firebase/app@0.14.5) + '@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 @@ -10819,9 +10531,9 @@ snapshots: '@firebase/functions-types@0.6.3': {} - '@firebase/functions@0.13.1(@firebase/app@0.14.5)': + '@firebase/functions@0.13.1(@firebase/app@0.14.6)': dependencies: - '@firebase/app': 0.14.5 + '@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 @@ -10829,11 +10541,11 @@ snapshots: '@firebase/util': 1.13.0 tslib: 2.8.1 - '@firebase/installations-compat@0.2.19(@firebase/app-compat@0.5.5)(@firebase/app-types@0.9.3)(@firebase/app@0.14.5)': + '@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.5 + '@firebase/app-compat': 0.5.6 '@firebase/component': 0.7.0 - '@firebase/installations': 0.6.19(@firebase/app@0.14.5) + '@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 @@ -10845,9 +10557,9 @@ snapshots: dependencies: '@firebase/app-types': 0.9.3 - '@firebase/installations@0.6.19(@firebase/app@0.14.5)': + '@firebase/installations@0.6.19(@firebase/app@0.14.6)': dependencies: - '@firebase/app': 0.14.5 + '@firebase/app': 0.14.6 '@firebase/component': 0.7.0 '@firebase/util': 1.13.0 idb: 7.1.1 @@ -10857,11 +10569,11 @@ snapshots: dependencies: tslib: 2.8.1 - '@firebase/messaging-compat@0.2.23(@firebase/app-compat@0.5.5)(@firebase/app@0.14.5)': + '@firebase/messaging-compat@0.2.23(@firebase/app-compat@0.5.6)(@firebase/app@0.14.6)': dependencies: - '@firebase/app-compat': 0.5.5 + '@firebase/app-compat': 0.5.6 '@firebase/component': 0.7.0 - '@firebase/messaging': 0.12.23(@firebase/app@0.14.5) + '@firebase/messaging': 0.12.23(@firebase/app@0.14.6) '@firebase/util': 1.13.0 tslib: 2.8.1 transitivePeerDependencies: @@ -10869,22 +10581,22 @@ snapshots: '@firebase/messaging-interop-types@0.2.3': {} - '@firebase/messaging@0.12.23(@firebase/app@0.14.5)': + '@firebase/messaging@0.12.23(@firebase/app@0.14.6)': dependencies: - '@firebase/app': 0.14.5 + '@firebase/app': 0.14.6 '@firebase/component': 0.7.0 - '@firebase/installations': 0.6.19(@firebase/app@0.14.5) + '@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.5)(@firebase/app@0.14.5)': + '@firebase/performance-compat@0.2.22(@firebase/app-compat@0.5.6)(@firebase/app@0.14.6)': dependencies: - '@firebase/app-compat': 0.5.5 + '@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.5) + '@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 @@ -10893,22 +10605,22 @@ snapshots: '@firebase/performance-types@0.2.3': {} - '@firebase/performance@0.7.9(@firebase/app@0.14.5)': + '@firebase/performance@0.7.9(@firebase/app@0.14.6)': dependencies: - '@firebase/app': 0.14.5 + '@firebase/app': 0.14.6 '@firebase/component': 0.7.0 - '@firebase/installations': 0.6.19(@firebase/app@0.14.5) + '@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.5)(@firebase/app@0.14.5)': + '@firebase/remote-config-compat@0.2.20(@firebase/app-compat@0.5.6)(@firebase/app@0.14.6)': dependencies: - '@firebase/app-compat': 0.5.5 + '@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.5) + '@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 @@ -10917,20 +10629,20 @@ snapshots: '@firebase/remote-config-types@0.5.0': {} - '@firebase/remote-config@0.7.0(@firebase/app@0.14.5)': + '@firebase/remote-config@0.7.0(@firebase/app@0.14.6)': dependencies: - '@firebase/app': 0.14.5 + '@firebase/app': 0.14.6 '@firebase/component': 0.7.0 - '@firebase/installations': 0.6.19(@firebase/app@0.14.5) + '@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.5)(@firebase/app-types@0.9.3)(@firebase/app@0.14.5)': + '@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.5 + '@firebase/app-compat': 0.5.6 '@firebase/component': 0.7.0 - '@firebase/storage': 0.14.0(@firebase/app@0.14.5) + '@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 @@ -10943,9 +10655,9 @@ snapshots: '@firebase/app-types': 0.9.3 '@firebase/util': 1.13.0 - '@firebase/storage@0.14.0(@firebase/app@0.14.5)': + '@firebase/storage@0.14.0(@firebase/app@0.14.6)': dependencies: - '@firebase/app': 0.14.5 + '@firebase/app': 0.14.6 '@firebase/component': 0.7.0 '@firebase/util': 1.13.0 tslib: 2.8.1 @@ -10992,7 +10704,7 @@ snapshots: '@opentelemetry/api': 1.9.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.37.0 + '@opentelemetry/semantic-conventions': 1.38.0 '@types/big.js': 6.2.2 '@types/stack-trace': 0.0.33 big.js: 7.0.1 @@ -11001,7 +10713,7 @@ snapshots: events-intercept: 2.0.0 extend: 3.0.2 google-auth-library: 10.5.0(supports-color@10.2.2) - google-gax: 5.0.5(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 @@ -11017,18 +10729,18 @@ snapshots: transitivePeerDependencies: - supports-color - '@google/genai@1.29.0(@modelcontextprotocol/sdk@1.21.1)(bufferutil@4.0.9)(supports-color@10.2.2)(utf-8-validate@6.0.5)': + '@google/genai@1.33.0(@modelcontextprotocol/sdk@1.25.0(zod@4.2.1))(bufferutil@4.0.9)(supports-color@10.2.2)(utf-8-validate@6.0.5)': dependencies: 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.21.1 + '@modelcontextprotocol/sdk': 1.25.0(zod@4.2.1) transitivePeerDependencies: - bufferutil - supports-color - utf-8-validate - '@grpc/grpc-js@1.14.0': + '@grpc/grpc-js@1.14.3': dependencies: '@grpc/proto-loader': 0.8.0 '@js-sdsl/ordered-map': 4.4.2 @@ -11036,7 +10748,7 @@ snapshots: '@grpc/grpc-js@1.9.15': dependencies: '@grpc/proto-loader': 0.7.15 - '@types/node': 22.19.0 + '@types/node': 22.19.3 '@grpc/proto-loader@0.7.15': dependencies: @@ -11054,6 +10766,8 @@ snapshots: '@hapi/bourne@3.0.0': {} + '@hono/node-server@1.19.7': {} + '@humanfs/core@0.19.1': {} '@humanfs/node@0.16.7': @@ -11067,147 +10781,247 @@ snapshots: '@inquirer/ansi@1.0.2': {} - '@inquirer/checkbox@4.3.2(@types/node@24.10.0)': + '@inquirer/ansi@2.0.2': {} + + '@inquirer/checkbox@4.3.2(@types/node@24.10.4)': dependencies: '@inquirer/ansi': 1.0.2 - '@inquirer/core': 10.3.2(@types/node@24.10.0) + '@inquirer/core': 10.3.2(@types/node@24.10.4) '@inquirer/figures': 1.0.15 - '@inquirer/type': 3.0.10(@types/node@24.10.0) + '@inquirer/type': 3.0.10(@types/node@24.10.4) yoctocolors-cjs: 2.1.3 optionalDependencies: - '@types/node': 24.10.0 + '@types/node': 24.10.4 + + '@inquirer/checkbox@5.0.3(@types/node@24.10.4)': + dependencies: + '@inquirer/ansi': 2.0.2 + '@inquirer/core': 11.1.0(@types/node@24.10.4) + '@inquirer/figures': 2.0.2 + '@inquirer/type': 4.0.2(@types/node@24.10.4) + optionalDependencies: + '@types/node': 24.10.4 - '@inquirer/confirm@5.1.21(@types/node@24.10.0)': + '@inquirer/confirm@5.1.21(@types/node@24.10.4)': dependencies: - '@inquirer/core': 10.3.2(@types/node@24.10.0) - '@inquirer/type': 3.0.10(@types/node@24.10.0) + '@inquirer/core': 10.3.2(@types/node@24.10.4) + '@inquirer/type': 3.0.10(@types/node@24.10.4) optionalDependencies: - '@types/node': 24.10.0 + '@types/node': 24.10.4 - '@inquirer/core@10.3.2(@types/node@24.10.0)': + '@inquirer/confirm@6.0.3(@types/node@24.10.4)': + dependencies: + '@inquirer/core': 11.1.0(@types/node@24.10.4) + '@inquirer/type': 4.0.2(@types/node@24.10.4) + optionalDependencies: + '@types/node': 24.10.4 + + '@inquirer/core@10.3.2(@types/node@24.10.4)': dependencies: '@inquirer/ansi': 1.0.2 '@inquirer/figures': 1.0.15 - '@inquirer/type': 3.0.10(@types/node@24.10.0) + '@inquirer/type': 3.0.10(@types/node@24.10.4) 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.10.0 + '@types/node': 24.10.4 + + '@inquirer/core@11.1.0(@types/node@24.10.4)': + dependencies: + '@inquirer/ansi': 2.0.2 + '@inquirer/figures': 2.0.2 + '@inquirer/type': 4.0.2(@types/node@24.10.4) + 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.4 + + '@inquirer/editor@4.2.23(@types/node@24.10.4)': + dependencies: + '@inquirer/core': 10.3.2(@types/node@24.10.4) + '@inquirer/external-editor': 1.0.3(@types/node@24.10.4) + '@inquirer/type': 3.0.10(@types/node@24.10.4) + optionalDependencies: + '@types/node': 24.10.4 + + '@inquirer/editor@5.0.3(@types/node@24.10.4)': + dependencies: + '@inquirer/core': 11.1.0(@types/node@24.10.4) + '@inquirer/external-editor': 2.0.2(@types/node@24.10.4) + '@inquirer/type': 4.0.2(@types/node@24.10.4) + optionalDependencies: + '@types/node': 24.10.4 + + '@inquirer/expand@4.0.23(@types/node@24.10.4)': + dependencies: + '@inquirer/core': 10.3.2(@types/node@24.10.4) + '@inquirer/type': 3.0.10(@types/node@24.10.4) + yoctocolors-cjs: 2.1.3 + optionalDependencies: + '@types/node': 24.10.4 - '@inquirer/editor@4.2.23(@types/node@24.10.0)': + '@inquirer/expand@5.0.3(@types/node@24.10.4)': dependencies: - '@inquirer/core': 10.3.2(@types/node@24.10.0) - '@inquirer/external-editor': 1.0.3(@types/node@24.10.0) - '@inquirer/type': 3.0.10(@types/node@24.10.0) + '@inquirer/core': 11.1.0(@types/node@24.10.4) + '@inquirer/type': 4.0.2(@types/node@24.10.4) optionalDependencies: - '@types/node': 24.10.0 + '@types/node': 24.10.4 - '@inquirer/expand@4.0.23(@types/node@24.10.0)': + '@inquirer/external-editor@1.0.3(@types/node@24.10.4)': dependencies: - '@inquirer/core': 10.3.2(@types/node@24.10.0) - '@inquirer/type': 3.0.10(@types/node@24.10.0) - yoctocolors-cjs: 2.1.3 + chardet: 2.1.1 + iconv-lite: 0.7.1 optionalDependencies: - '@types/node': 24.10.0 + '@types/node': 24.10.4 - '@inquirer/external-editor@1.0.3(@types/node@24.10.0)': + '@inquirer/external-editor@2.0.2(@types/node@24.10.4)': dependencies: chardet: 2.1.1 - iconv-lite: 0.7.0 + iconv-lite: 0.7.1 optionalDependencies: - '@types/node': 24.10.0 + '@types/node': 24.10.4 '@inquirer/figures@1.0.15': {} - '@inquirer/input@4.3.1(@types/node@24.10.0)': + '@inquirer/figures@2.0.2': {} + + '@inquirer/input@4.3.1(@types/node@24.10.4)': + dependencies: + '@inquirer/core': 10.3.2(@types/node@24.10.4) + '@inquirer/type': 3.0.10(@types/node@24.10.4) + optionalDependencies: + '@types/node': 24.10.4 + + '@inquirer/input@5.0.3(@types/node@24.10.4)': + dependencies: + '@inquirer/core': 11.1.0(@types/node@24.10.4) + '@inquirer/type': 4.0.2(@types/node@24.10.4) + optionalDependencies: + '@types/node': 24.10.4 + + '@inquirer/number@3.0.23(@types/node@24.10.4)': dependencies: - '@inquirer/core': 10.3.2(@types/node@24.10.0) - '@inquirer/type': 3.0.10(@types/node@24.10.0) + '@inquirer/core': 10.3.2(@types/node@24.10.4) + '@inquirer/type': 3.0.10(@types/node@24.10.4) optionalDependencies: - '@types/node': 24.10.0 + '@types/node': 24.10.4 - '@inquirer/number@3.0.23(@types/node@24.10.0)': + '@inquirer/number@4.0.3(@types/node@24.10.4)': dependencies: - '@inquirer/core': 10.3.2(@types/node@24.10.0) - '@inquirer/type': 3.0.10(@types/node@24.10.0) + '@inquirer/core': 11.1.0(@types/node@24.10.4) + '@inquirer/type': 4.0.2(@types/node@24.10.4) optionalDependencies: - '@types/node': 24.10.0 + '@types/node': 24.10.4 - '@inquirer/password@4.0.23(@types/node@24.10.0)': + '@inquirer/password@4.0.23(@types/node@24.10.4)': dependencies: '@inquirer/ansi': 1.0.2 - '@inquirer/core': 10.3.2(@types/node@24.10.0) - '@inquirer/type': 3.0.10(@types/node@24.10.0) + '@inquirer/core': 10.3.2(@types/node@24.10.4) + '@inquirer/type': 3.0.10(@types/node@24.10.4) + optionalDependencies: + '@types/node': 24.10.4 + + '@inquirer/password@5.0.3(@types/node@24.10.4)': + dependencies: + '@inquirer/ansi': 2.0.2 + '@inquirer/core': 11.1.0(@types/node@24.10.4) + '@inquirer/type': 4.0.2(@types/node@24.10.4) optionalDependencies: - '@types/node': 24.10.0 - - '@inquirer/prompts@7.10.0(@types/node@24.10.0)': - dependencies: - '@inquirer/checkbox': 4.3.2(@types/node@24.10.0) - '@inquirer/confirm': 5.1.21(@types/node@24.10.0) - '@inquirer/editor': 4.2.23(@types/node@24.10.0) - '@inquirer/expand': 4.0.23(@types/node@24.10.0) - '@inquirer/input': 4.3.1(@types/node@24.10.0) - '@inquirer/number': 3.0.23(@types/node@24.10.0) - '@inquirer/password': 4.0.23(@types/node@24.10.0) - '@inquirer/rawlist': 4.1.11(@types/node@24.10.0) - '@inquirer/search': 3.2.2(@types/node@24.10.0) - '@inquirer/select': 4.4.2(@types/node@24.10.0) + '@types/node': 24.10.4 + + '@inquirer/prompts@7.10.1(@types/node@24.10.4)': + dependencies: + '@inquirer/checkbox': 4.3.2(@types/node@24.10.4) + '@inquirer/confirm': 5.1.21(@types/node@24.10.4) + '@inquirer/editor': 4.2.23(@types/node@24.10.4) + '@inquirer/expand': 4.0.23(@types/node@24.10.4) + '@inquirer/input': 4.3.1(@types/node@24.10.4) + '@inquirer/number': 3.0.23(@types/node@24.10.4) + '@inquirer/password': 4.0.23(@types/node@24.10.4) + '@inquirer/rawlist': 4.1.11(@types/node@24.10.4) + '@inquirer/search': 3.2.2(@types/node@24.10.4) + '@inquirer/select': 4.4.2(@types/node@24.10.4) optionalDependencies: - '@types/node': 24.10.0 - - '@inquirer/prompts@7.10.1(@types/node@24.10.0)': - dependencies: - '@inquirer/checkbox': 4.3.2(@types/node@24.10.0) - '@inquirer/confirm': 5.1.21(@types/node@24.10.0) - '@inquirer/editor': 4.2.23(@types/node@24.10.0) - '@inquirer/expand': 4.0.23(@types/node@24.10.0) - '@inquirer/input': 4.3.1(@types/node@24.10.0) - '@inquirer/number': 3.0.23(@types/node@24.10.0) - '@inquirer/password': 4.0.23(@types/node@24.10.0) - '@inquirer/rawlist': 4.1.11(@types/node@24.10.0) - '@inquirer/search': 3.2.2(@types/node@24.10.0) - '@inquirer/select': 4.4.2(@types/node@24.10.0) + '@types/node': 24.10.4 + + '@inquirer/prompts@8.1.0(@types/node@24.10.4)': + dependencies: + '@inquirer/checkbox': 5.0.3(@types/node@24.10.4) + '@inquirer/confirm': 6.0.3(@types/node@24.10.4) + '@inquirer/editor': 5.0.3(@types/node@24.10.4) + '@inquirer/expand': 5.0.3(@types/node@24.10.4) + '@inquirer/input': 5.0.3(@types/node@24.10.4) + '@inquirer/number': 4.0.3(@types/node@24.10.4) + '@inquirer/password': 5.0.3(@types/node@24.10.4) + '@inquirer/rawlist': 5.1.0(@types/node@24.10.4) + '@inquirer/search': 4.0.3(@types/node@24.10.4) + '@inquirer/select': 5.0.3(@types/node@24.10.4) optionalDependencies: - '@types/node': 24.10.0 + '@types/node': 24.10.4 - '@inquirer/rawlist@4.1.11(@types/node@24.10.0)': + '@inquirer/rawlist@4.1.11(@types/node@24.10.4)': dependencies: - '@inquirer/core': 10.3.2(@types/node@24.10.0) - '@inquirer/type': 3.0.10(@types/node@24.10.0) + '@inquirer/core': 10.3.2(@types/node@24.10.4) + '@inquirer/type': 3.0.10(@types/node@24.10.4) yoctocolors-cjs: 2.1.3 optionalDependencies: - '@types/node': 24.10.0 + '@types/node': 24.10.4 - '@inquirer/search@3.2.2(@types/node@24.10.0)': + '@inquirer/rawlist@5.1.0(@types/node@24.10.4)': dependencies: - '@inquirer/core': 10.3.2(@types/node@24.10.0) + '@inquirer/core': 11.1.0(@types/node@24.10.4) + '@inquirer/type': 4.0.2(@types/node@24.10.4) + optionalDependencies: + '@types/node': 24.10.4 + + '@inquirer/search@3.2.2(@types/node@24.10.4)': + dependencies: + '@inquirer/core': 10.3.2(@types/node@24.10.4) '@inquirer/figures': 1.0.15 - '@inquirer/type': 3.0.10(@types/node@24.10.0) + '@inquirer/type': 3.0.10(@types/node@24.10.4) yoctocolors-cjs: 2.1.3 optionalDependencies: - '@types/node': 24.10.0 + '@types/node': 24.10.4 + + '@inquirer/search@4.0.3(@types/node@24.10.4)': + dependencies: + '@inquirer/core': 11.1.0(@types/node@24.10.4) + '@inquirer/figures': 2.0.2 + '@inquirer/type': 4.0.2(@types/node@24.10.4) + optionalDependencies: + '@types/node': 24.10.4 - '@inquirer/select@4.4.2(@types/node@24.10.0)': + '@inquirer/select@4.4.2(@types/node@24.10.4)': dependencies: '@inquirer/ansi': 1.0.2 - '@inquirer/core': 10.3.2(@types/node@24.10.0) + '@inquirer/core': 10.3.2(@types/node@24.10.4) '@inquirer/figures': 1.0.15 - '@inquirer/type': 3.0.10(@types/node@24.10.0) + '@inquirer/type': 3.0.10(@types/node@24.10.4) yoctocolors-cjs: 2.1.3 optionalDependencies: - '@types/node': 24.10.0 + '@types/node': 24.10.4 + + '@inquirer/select@5.0.3(@types/node@24.10.4)': + dependencies: + '@inquirer/ansi': 2.0.2 + '@inquirer/core': 11.1.0(@types/node@24.10.4) + '@inquirer/figures': 2.0.2 + '@inquirer/type': 4.0.2(@types/node@24.10.4) + optionalDependencies: + '@types/node': 24.10.4 - '@inquirer/type@3.0.10(@types/node@24.10.0)': + '@inquirer/type@3.0.10(@types/node@24.10.4)': optionalDependencies: - '@types/node': 24.10.0 + '@types/node': 24.10.4 - '@inquirer/type@3.0.9(@types/node@24.10.0)': + '@inquirer/type@4.0.2(@types/node@24.10.4)': optionalDependencies: - '@types/node': 24.10.0 + '@types/node': 24.10.4 '@isaacs/balanced-match@4.0.1': {} @@ -11299,10 +11113,10 @@ snapshots: '@leichtgewicht/ip-codec@2.0.5': {} - '@listr2/prompt-adapter-inquirer@3.0.5(@inquirer/prompts@7.10.1(@types/node@24.10.0))(@types/node@24.10.0)(listr2@9.0.5)': + '@listr2/prompt-adapter-inquirer@3.0.5(@inquirer/prompts@7.10.1(@types/node@24.10.4))(@types/node@24.10.4)(listr2@9.0.5)': dependencies: - '@inquirer/prompts': 7.10.1(@types/node@24.10.0) - '@inquirer/type': 3.0.9(@types/node@24.10.0) + '@inquirer/prompts': 7.10.1(@types/node@24.10.4) + '@inquirer/type': 3.0.10(@types/node@24.10.4) listr2: 9.0.5 transitivePeerDependencies: - '@types/node' @@ -11328,8 +11142,9 @@ snapshots: '@lmdb/lmdb-win32-x64@3.4.4': optional: true - '@modelcontextprotocol/sdk@1.21.1': + '@modelcontextprotocol/sdk@1.25.0(zod@4.2.1)': dependencies: + '@hono/node-server': 1.19.7 ajv: 8.17.1 ajv-formats: 3.0.1(ajv@8.17.1) content-type: 1.0.5 @@ -11337,13 +11152,16 @@ snapshots: cross-spawn: 7.0.6 eventsource: 3.0.7 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 - zod: 3.25.76 - zod-to-json-schema: 3.24.6(zod@3.25.76) + express: 5.2.1 + express-rate-limit: 7.5.1(express@5.2.1) + jose: 6.1.3 + json-schema-typed: 8.0.2 + pkce-challenge: 5.0.1 + raw-body: 3.0.2 + zod: 4.2.1 + zod-to-json-schema: 3.25.0(zod@4.2.1) transitivePeerDependencies: + - hono - supports-color '@msgpackr-extract/msgpackr-extract-darwin-arm64@3.0.3': @@ -11445,10 +11263,10 @@ snapshots: '@napi-rs/nice-win32-x64-msvc': 1.1.1 optional: true - '@napi-rs/wasm-runtime@1.0.7': + '@napi-rs/wasm-runtime@1.1.0': dependencies: - '@emnapi/core': 1.7.0 - '@emnapi/runtime': 1.7.0 + '@emnapi/core': 1.7.1 + '@emnapi/runtime': 1.7.1 '@tybys/wasm-util': 0.10.1 optional: true @@ -11464,76 +11282,62 @@ 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 http-proxy-agent: 7.0.2 https-proxy-agent: 7.0.6(supports-color@10.2.2) - lru-cache: 11.2.2 + lru-cache: 11.2.4 socks-proxy-agent: 8.0.5 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 - lru-cache: 11.2.2 + '@npmcli/promise-spawn': 9.0.1 + ini: 6.0.0 + lru-cache: 11.2.4 npm-pick-manifest: 11.0.3 - proc-log: 5.0.0 + proc-log: 6.1.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': + '@npmcli/installed-package-contents@4.0.0': dependencies: - npm-bundled: 4.0.0 - npm-normalize-package-bin: 4.0.0 + npm-bundled: 5.0.0 + npm-normalize-package-bin: 5.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.1.0 semver: 7.7.3 validate-npm-package-license: 3.0.4 - '@npmcli/promise-spawn@8.0.3': - dependencies: - which: 5.0.0 - - '@npmcli/promise-spawn@9.0.0': + '@npmcli/promise-spawn@9.0.1': dependencies: - which: 5.0.0 + which: 6.0.0 - '@npmcli/redact@3.2.2': {} + '@npmcli/redact@4.0.0': {} - '@npmcli/run-script@10.0.2': + '@npmcli/run-script@10.0.3': dependencies: '@npmcli/node-gyp': 5.0.0 - '@npmcli/package-json': 7.0.1 - '@npmcli/promise-spawn': 9.0.0 - node-gyp: 11.5.0 - proc-log: 6.0.0 - which: 5.0.0 + '@npmcli/package-json': 7.0.4 + '@npmcli/promise-spawn': 9.0.1 + node-gyp: 12.1.0 + proc-log: 6.1.0 + which: 6.0.0 transitivePeerDependencies: - supports-color @@ -11541,8 +11345,8 @@ snapshots: dependencies: '@octokit/auth-oauth-app': 9.0.3 '@octokit/auth-oauth-user': 6.0.2 - '@octokit/request': 10.0.6 - '@octokit/request-error': 7.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 @@ -11552,14 +11356,14 @@ snapshots: dependencies: '@octokit/auth-oauth-device': 8.0.3 '@octokit/auth-oauth-user': 6.0.2 - '@octokit/request': 10.0.6 + '@octokit/request': 10.0.7 '@octokit/types': 16.0.0 universal-user-agent: 7.0.3 '@octokit/auth-oauth-device@8.0.3': dependencies: '@octokit/oauth-methods': 6.0.2 - '@octokit/request': 10.0.6 + '@octokit/request': 10.0.7 '@octokit/types': 16.0.0 universal-user-agent: 7.0.3 @@ -11567,7 +11371,7 @@ snapshots: dependencies: '@octokit/auth-oauth-device': 8.0.3 '@octokit/oauth-methods': 6.0.2 - '@octokit/request': 10.0.6 + '@octokit/request': 10.0.7 '@octokit/types': 16.0.0 universal-user-agent: 7.0.3 @@ -11577,8 +11381,8 @@ snapshots: dependencies: '@octokit/auth-token': 6.0.0 '@octokit/graphql': 9.0.3 - '@octokit/request': 10.0.6 - '@octokit/request-error': 7.0.2 + '@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 @@ -11588,14 +11392,14 @@ snapshots: '@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.12.0 graphql-tag: 2.12.6(graphql@16.12.0) '@octokit/graphql@9.0.3': dependencies: - '@octokit/request': 10.0.6 + '@octokit/request': 10.0.7 '@octokit/types': 16.0.0 universal-user-agent: 7.0.3 @@ -11604,8 +11408,8 @@ snapshots: '@octokit/oauth-methods@6.0.2': dependencies: '@octokit/oauth-authorization-url': 8.0.0 - '@octokit/request': 10.0.6 - '@octokit/request-error': 7.0.2 + '@octokit/request': 10.0.7 + '@octokit/request-error': 7.1.0 '@octokit/types': 16.0.0 '@octokit/openapi-types@27.0.0': {} @@ -11624,14 +11428,14 @@ snapshots: '@octokit/core': 7.0.6 '@octokit/types': 16.0.0 - '@octokit/request-error@7.0.2': + '@octokit/request-error@7.1.0': dependencies: '@octokit/types': 16.0.0 - '@octokit/request@10.0.6': + '@octokit/request@10.0.7': dependencies: '@octokit/endpoint': 11.0.2 - '@octokit/request-error': 7.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 @@ -11665,11 +11469,11 @@ snapshots: '@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.97.0': {} + '@oxc-project/types@0.102.0': {} '@parcel/watcher-android-arm64@2.5.1': optional: true @@ -11732,6 +11536,8 @@ snapshots: '@parcel/watcher-win32-x64': 2.5.1 optional: true + '@pinojs/redact@0.4.0': {} + '@pkgjs/parseargs@0.11.0': optional: true @@ -11743,17 +11549,17 @@ snapshots: '@pnpm/crypto.polyfill@1000.1.0': {} - '@pnpm/dependency-path@1001.1.4': + '@pnpm/dependency-path@1001.1.8': dependencies: '@pnpm/crypto.hash': 1000.2.1 - '@pnpm/types': 1001.0.0 + '@pnpm/types': 1001.2.0 semver: 7.7.3 '@pnpm/graceful-fs@1000.0.1': dependencies: graceful-fs: 4.2.11 - '@pnpm/types@1001.0.0': {} + '@pnpm/types@1001.2.0': {} '@protobufjs/aspromise@1.1.2': {} @@ -11778,7 +11584,7 @@ snapshots: '@protobufjs/utf8@1.1.0': {} - '@puppeteer/browsers@2.10.12': + '@puppeteer/browsers@2.11.0': dependencies: debug: 4.4.3(supports-color@10.2.2) extract-zip: 2.0.1 @@ -11793,59 +11599,56 @@ snapshots: - react-native-b4a - supports-color - '@rolldown/binding-android-arm64@1.0.0-beta.50': + '@rolldown/binding-android-arm64@1.0.0-beta.54': optional: true - '@rolldown/binding-darwin-arm64@1.0.0-beta.50': + '@rolldown/binding-darwin-arm64@1.0.0-beta.54': optional: true - '@rolldown/binding-darwin-x64@1.0.0-beta.50': + '@rolldown/binding-darwin-x64@1.0.0-beta.54': optional: true - '@rolldown/binding-freebsd-x64@1.0.0-beta.50': + '@rolldown/binding-freebsd-x64@1.0.0-beta.54': optional: true - '@rolldown/binding-linux-arm-gnueabihf@1.0.0-beta.50': + '@rolldown/binding-linux-arm-gnueabihf@1.0.0-beta.54': optional: true - '@rolldown/binding-linux-arm64-gnu@1.0.0-beta.50': + '@rolldown/binding-linux-arm64-gnu@1.0.0-beta.54': optional: true - '@rolldown/binding-linux-arm64-musl@1.0.0-beta.50': + '@rolldown/binding-linux-arm64-musl@1.0.0-beta.54': optional: true - '@rolldown/binding-linux-x64-gnu@1.0.0-beta.50': + '@rolldown/binding-linux-x64-gnu@1.0.0-beta.54': optional: true - '@rolldown/binding-linux-x64-musl@1.0.0-beta.50': + '@rolldown/binding-linux-x64-musl@1.0.0-beta.54': optional: true - '@rolldown/binding-openharmony-arm64@1.0.0-beta.50': + '@rolldown/binding-openharmony-arm64@1.0.0-beta.54': optional: true - '@rolldown/binding-wasm32-wasi@1.0.0-beta.50': + '@rolldown/binding-wasm32-wasi@1.0.0-beta.54': dependencies: - '@napi-rs/wasm-runtime': 1.0.7 - optional: true - - '@rolldown/binding-win32-arm64-msvc@1.0.0-beta.50': + '@napi-rs/wasm-runtime': 1.1.0 optional: true - '@rolldown/binding-win32-ia32-msvc@1.0.0-beta.50': + '@rolldown/binding-win32-arm64-msvc@1.0.0-beta.54': optional: true - '@rolldown/binding-win32-x64-msvc@1.0.0-beta.50': + '@rolldown/binding-win32-x64-msvc@1.0.0-beta.54': optional: true - '@rolldown/pluginutils@1.0.0-beta.50': {} + '@rolldown/pluginutils@1.0.0-beta.54': {} - '@rollup/plugin-alias@6.0.0(rollup@4.53.2)': + '@rollup/plugin-alias@6.0.0(rollup@4.53.5)': optionalDependencies: - rollup: 4.53.2 + rollup: 4.53.5 - '@rollup/plugin-commonjs@29.0.0(rollup@4.53.2)': + '@rollup/plugin-commonjs@29.0.0(rollup@4.53.5)': dependencies: - '@rollup/pluginutils': 5.3.0(rollup@4.53.2) + '@rollup/pluginutils': 5.3.0(rollup@4.53.5) commondir: 1.0.1 estree-walker: 2.0.2 fdir: 6.5.0(picomatch@4.0.3) @@ -11853,183 +11656,117 @@ snapshots: magic-string: 0.30.21 picomatch: 4.0.3 optionalDependencies: - rollup: 4.53.2 + rollup: 4.53.5 - '@rollup/plugin-json@6.1.0(rollup@4.53.2)': + '@rollup/plugin-json@6.1.0(rollup@4.53.5)': dependencies: - '@rollup/pluginutils': 5.3.0(rollup@4.53.2) + '@rollup/pluginutils': 5.3.0(rollup@4.53.5) optionalDependencies: - rollup: 4.53.2 + rollup: 4.53.5 - '@rollup/plugin-node-resolve@15.3.1(rollup@4.53.2)': + '@rollup/plugin-node-resolve@15.3.1(rollup@4.53.5)': dependencies: - '@rollup/pluginutils': 5.3.0(rollup@4.53.2) + '@rollup/pluginutils': 5.3.0(rollup@4.53.5) '@types/resolve': 1.20.2 deepmerge: 4.3.1 is-module: 1.0.0 resolve: 1.22.11 optionalDependencies: - rollup: 4.53.2 + rollup: 4.53.5 - '@rollup/plugin-node-resolve@16.0.3(rollup@4.53.2)': + '@rollup/plugin-node-resolve@16.0.3(rollup@4.53.5)': dependencies: - '@rollup/pluginutils': 5.3.0(rollup@4.53.2) + '@rollup/pluginutils': 5.3.0(rollup@4.53.5) '@types/resolve': 1.20.2 deepmerge: 4.3.1 is-module: 1.0.0 resolve: 1.22.11 optionalDependencies: - rollup: 4.53.2 + rollup: 4.53.5 - '@rollup/pluginutils@5.2.0(rollup@4.53.2)': + '@rollup/pluginutils@5.2.0(rollup@4.53.5)': dependencies: '@types/estree': 1.0.8 estree-walker: 2.0.2 picomatch: 4.0.3 optionalDependencies: - rollup: 4.53.2 + rollup: 4.53.5 - '@rollup/pluginutils@5.3.0(rollup@4.53.2)': + '@rollup/pluginutils@5.3.0(rollup@4.53.5)': dependencies: '@types/estree': 1.0.8 estree-walker: 2.0.2 picomatch: 4.0.3 optionalDependencies: - rollup: 4.53.2 - - '@rollup/rollup-android-arm-eabi@4.53.1': - optional: true - - '@rollup/rollup-android-arm-eabi@4.53.2': - optional: true - - '@rollup/rollup-android-arm64@4.53.1': - optional: true - - '@rollup/rollup-android-arm64@4.53.2': - optional: true - - '@rollup/rollup-darwin-arm64@4.53.1': - optional: true - - '@rollup/rollup-darwin-arm64@4.53.2': - optional: true - - '@rollup/rollup-darwin-x64@4.53.1': - optional: true - - '@rollup/rollup-darwin-x64@4.53.2': - optional: true - - '@rollup/rollup-freebsd-arm64@4.53.1': - optional: true - - '@rollup/rollup-freebsd-arm64@4.53.2': - optional: true - - '@rollup/rollup-freebsd-x64@4.53.1': - optional: true - - '@rollup/rollup-freebsd-x64@4.53.2': - optional: true + rollup: 4.53.5 - '@rollup/rollup-linux-arm-gnueabihf@4.53.1': + '@rollup/rollup-android-arm-eabi@4.53.5': optional: true - '@rollup/rollup-linux-arm-gnueabihf@4.53.2': + '@rollup/rollup-android-arm64@4.53.5': optional: true - '@rollup/rollup-linux-arm-musleabihf@4.53.1': + '@rollup/rollup-darwin-arm64@4.53.5': optional: true - '@rollup/rollup-linux-arm-musleabihf@4.53.2': + '@rollup/rollup-darwin-x64@4.53.5': optional: true - '@rollup/rollup-linux-arm64-gnu@4.53.1': + '@rollup/rollup-freebsd-arm64@4.53.5': optional: true - '@rollup/rollup-linux-arm64-gnu@4.53.2': + '@rollup/rollup-freebsd-x64@4.53.5': optional: true - '@rollup/rollup-linux-arm64-musl@4.53.1': + '@rollup/rollup-linux-arm-gnueabihf@4.53.5': optional: true - '@rollup/rollup-linux-arm64-musl@4.53.2': + '@rollup/rollup-linux-arm-musleabihf@4.53.5': optional: true - '@rollup/rollup-linux-loong64-gnu@4.53.1': + '@rollup/rollup-linux-arm64-gnu@4.53.5': optional: true - '@rollup/rollup-linux-loong64-gnu@4.53.2': + '@rollup/rollup-linux-arm64-musl@4.53.5': optional: true - '@rollup/rollup-linux-ppc64-gnu@4.53.1': + '@rollup/rollup-linux-loong64-gnu@4.53.5': optional: true - '@rollup/rollup-linux-ppc64-gnu@4.53.2': + '@rollup/rollup-linux-ppc64-gnu@4.53.5': optional: true - '@rollup/rollup-linux-riscv64-gnu@4.53.1': + '@rollup/rollup-linux-riscv64-gnu@4.53.5': optional: true - '@rollup/rollup-linux-riscv64-gnu@4.53.2': + '@rollup/rollup-linux-riscv64-musl@4.53.5': optional: true - '@rollup/rollup-linux-riscv64-musl@4.53.1': + '@rollup/rollup-linux-s390x-gnu@4.53.5': optional: true - '@rollup/rollup-linux-riscv64-musl@4.53.2': + '@rollup/rollup-linux-x64-gnu@4.53.5': optional: true - '@rollup/rollup-linux-s390x-gnu@4.53.1': + '@rollup/rollup-linux-x64-musl@4.53.5': optional: true - '@rollup/rollup-linux-s390x-gnu@4.53.2': + '@rollup/rollup-openharmony-arm64@4.53.5': optional: true - '@rollup/rollup-linux-x64-gnu@4.53.1': + '@rollup/rollup-win32-arm64-msvc@4.53.5': optional: true - '@rollup/rollup-linux-x64-gnu@4.53.2': + '@rollup/rollup-win32-ia32-msvc@4.53.5': optional: true - '@rollup/rollup-linux-x64-musl@4.53.1': + '@rollup/rollup-win32-x64-gnu@4.53.5': optional: true - '@rollup/rollup-linux-x64-musl@4.53.2': + '@rollup/rollup-win32-x64-msvc@4.53.5': optional: true - '@rollup/rollup-openharmony-arm64@4.53.1': - optional: true - - '@rollup/rollup-openharmony-arm64@4.53.2': - optional: true - - '@rollup/rollup-win32-arm64-msvc@4.53.1': - optional: true - - '@rollup/rollup-win32-arm64-msvc@4.53.2': - optional: true - - '@rollup/rollup-win32-ia32-msvc@4.53.1': - optional: true - - '@rollup/rollup-win32-ia32-msvc@4.53.2': - optional: true - - '@rollup/rollup-win32-x64-gnu@4.53.1': - optional: true - - '@rollup/rollup-win32-x64-gnu@4.53.2': - optional: true - - '@rollup/rollup-win32-x64-msvc@4.53.1': - optional: true - - '@rollup/rollup-win32-x64-msvc@4.53.2': - optional: true - - '@rollup/wasm-node@4.52.5': + '@rollup/wasm-node@4.54.0': dependencies: '@types/estree': 1.0.8 optionalDependencies: @@ -12041,45 +11778,45 @@ snapshots: dependencies: '@sigstore/protobuf-specs': 0.5.0 - '@sigstore/core@3.0.0': {} + '@sigstore/core@3.1.0': {} '@sigstore/protobuf-specs@0.5.0': {} - '@sigstore/sign@4.0.1': + '@sigstore/sign@4.1.0': dependencies: '@sigstore/bundle': 4.0.0 - '@sigstore/core': 3.0.0 + '@sigstore/core': 3.1.0 '@sigstore/protobuf-specs': 0.5.0 - make-fetch-happen: 15.0.2 - proc-log: 5.0.0 + make-fetch-happen: 15.0.3 + proc-log: 6.1.0 promise-retry: 2.0.1 transitivePeerDependencies: - supports-color - '@sigstore/tuf@4.0.0': + '@sigstore/tuf@4.0.1': dependencies: '@sigstore/protobuf-specs': 0.5.0 - tuf-js: 4.0.0 + tuf-js: 4.1.0 transitivePeerDependencies: - supports-color - '@sigstore/verify@3.0.0': + '@sigstore/verify@3.1.0': dependencies: '@sigstore/bundle': 4.0.0 - '@sigstore/core': 3.0.0 + '@sigstore/core': 3.1.0 '@sigstore/protobuf-specs': 0.5.0 '@sindresorhus/is@4.6.0': {} '@socket.io/component-emitter@3.1.2': {} - '@standard-schema/spec@1.0.0': {} + '@standard-schema/spec@1.1.0': {} - '@stylistic/eslint-plugin@5.5.0(eslint@9.39.1(jiti@2.6.1))': + '@stylistic/eslint-plugin@5.6.1(eslint@9.39.2(jiti@2.6.1))': dependencies: - '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.1(jiti@2.6.1)) - '@typescript-eslint/types': 8.46.2 - eslint: 9.39.1(jiti@2.6.1) + '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.2(jiti@2.6.1)) + '@typescript-eslint/types': 8.50.0 + eslint: 9.39.2(jiti@2.6.1) eslint-visitor-keys: 4.2.1 espree: 10.4.0 estraverse: 5.3.0 @@ -12093,7 +11830,7 @@ snapshots: '@tootallnate/quickjs-emscripten@0.23.0': {} - '@tsconfig/node10@1.0.11': {} + '@tsconfig/node10@1.0.12': {} '@tsconfig/node12@1.0.11': {} @@ -12103,10 +11840,10 @@ snapshots: '@tufjs/canonical-json@2.0.0': {} - '@tufjs/models@4.0.0': + '@tufjs/models@4.1.0': dependencies: '@tufjs/canonical-json': 2.0.0 - minimatch: 9.0.5 + minimatch: 10.1.1 '@tybys/wasm-util@0.10.1': dependencies: @@ -12115,7 +11852,7 @@ snapshots: '@types/accepts@1.3.7': dependencies: - '@types/node': 22.19.0 + '@types/node': 22.19.3 '@types/babel__code-frame@7.0.6': {} @@ -12145,16 +11882,16 @@ snapshots: '@types/body-parser@1.19.6': dependencies: '@types/connect': 3.4.38 - '@types/node': 22.19.0 + '@types/node': 22.19.3 '@types/bonjour@3.5.13': dependencies: - '@types/node': 22.19.0 + '@types/node': 22.19.3 '@types/browser-sync@2.29.1': dependencies: '@types/micromatch': 2.3.35 - '@types/node': 22.19.0 + '@types/node': 22.19.3 '@types/serve-static': 2.2.0 chokidar: 3.6.0 @@ -12165,11 +11902,11 @@ snapshots: '@types/cli-progress@3.11.6': dependencies: - '@types/node': 22.19.0 + '@types/node': 22.19.3 '@types/co-body@6.1.3': dependencies: - '@types/node': 22.19.0 + '@types/node': 22.19.3 '@types/qs': 6.14.0 '@types/command-line-args@5.2.3': {} @@ -12177,11 +11914,11 @@ snapshots: '@types/connect-history-api-fallback@1.5.4': dependencies: '@types/express-serve-static-core': 4.19.7 - '@types/node': 22.19.0 + '@types/node': 22.19.3 '@types/connect@3.4.38': dependencies: - '@types/node': 22.19.0 + '@types/node': 22.19.3 '@types/content-disposition@0.5.9': {} @@ -12190,13 +11927,13 @@ snapshots: '@types/cookies@0.9.2': dependencies: '@types/connect': 3.4.38 - '@types/express': 5.0.5 + '@types/express': 5.0.6 '@types/keygrip': 1.0.6 - '@types/node': 22.19.0 + '@types/node': 22.19.3 '@types/cors@2.8.19': dependencies: - '@types/node': 22.19.0 + '@types/node': 22.19.3 '@types/debounce@1.2.4': {} @@ -12204,7 +11941,7 @@ snapshots: '@types/duplexify@3.6.5': dependencies: - '@types/node': 22.19.0 + '@types/node': 22.19.3 '@types/ejs@3.1.5': {} @@ -12224,14 +11961,14 @@ snapshots: '@types/express-serve-static-core@4.19.7': dependencies: - '@types/node': 22.19.0 + '@types/node': 22.19.3 '@types/qs': 6.14.0 '@types/range-parser': 1.2.7 '@types/send': 1.2.1 '@types/express-serve-static-core@5.1.0': dependencies: - '@types/node': 22.19.0 + '@types/node': 22.19.3 '@types/qs': 6.14.0 '@types/range-parser': 1.2.7 '@types/send': 1.2.1 @@ -12243,21 +11980,21 @@ snapshots: '@types/qs': 6.14.0 '@types/serve-static': 1.15.10 - '@types/express@5.0.5': + '@types/express@5.0.6': dependencies: '@types/body-parser': 1.19.6 '@types/express-serve-static-core': 5.1.0 - '@types/serve-static': 1.15.10 + '@types/serve-static': 2.2.0 '@types/folder-hash@4.0.4': {} '@types/git-raw-commits@5.0.1': dependencies: - '@types/node': 22.19.0 + '@types/node': 22.19.3 '@types/graceful-fs@4.1.9': dependencies: - '@types/node': 22.19.0 + '@types/node': 22.19.3 '@types/http-assert@1.5.6': {} @@ -12265,7 +12002,7 @@ snapshots: '@types/http-proxy@1.17.17': dependencies: - '@types/node': 22.19.0 + '@types/node': 22.19.3 '@types/ini@4.1.1': {} @@ -12281,9 +12018,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': {} @@ -12291,7 +12028,7 @@ snapshots: '@types/karma@6.3.9': dependencies: - '@types/node': 22.19.0 + '@types/node': 22.19.3 log4js: 6.9.1 transitivePeerDependencies: - supports-color @@ -12311,21 +12048,21 @@ snapshots: '@types/http-errors': 2.0.5 '@types/keygrip': 1.0.6 '@types/koa-compose': 3.2.9 - '@types/node': 22.19.0 + '@types/node': 22.19.3 '@types/less@3.0.8': {} - '@types/loader-utils@3.0.0(esbuild@0.27.0)': + '@types/loader-utils@3.0.0(esbuild@0.27.1)': dependencies: - '@types/node': 22.19.0 - webpack: 5.102.1(esbuild@0.27.0) + '@types/node': 22.19.3 + webpack: 5.104.0(esbuild@0.27.1) transitivePeerDependencies: - '@swc/core' - esbuild - uglify-js - webpack-cli - '@types/lodash@4.17.20': {} + '@types/lodash@4.17.21': {} '@types/micromatch@2.3.35': dependencies: @@ -12335,18 +12072,18 @@ snapshots: '@types/node-fetch@2.6.13': dependencies: - '@types/node': 22.19.0 - form-data: 4.0.4 + '@types/node': 22.19.3 + form-data: 4.0.5 '@types/node-forge@1.3.14': dependencies: - '@types/node': 22.19.0 + '@types/node': 22.19.3 - '@types/node@22.19.0': + '@types/node@22.19.3': dependencies: undici-types: 7.16.0 - '@types/node@24.10.0': + '@types/node@24.10.4': dependencies: undici-types: 7.16.0 @@ -12354,7 +12091,7 @@ snapshots: '@types/npm-registry-fetch@8.0.9': dependencies: - '@types/node': 22.19.0 + '@types/node': 22.19.3 '@types/node-fetch': 2.6.13 '@types/npm-package-arg': 6.1.4 '@types/npmlog': 7.0.0 @@ -12362,11 +12099,11 @@ snapshots: '@types/npmlog@7.0.0': dependencies: - '@types/node': 22.19.0 + '@types/node': 22.19.3 '@types/pacote@11.1.8': dependencies: - '@types/node': 22.19.0 + '@types/node': 22.19.3 '@types/npm-registry-fetch': 8.0.9 '@types/npmlog': 7.0.0 '@types/ssri': 7.1.5 @@ -12379,12 +12116,12 @@ snapshots: '@types/progress@2.0.7': dependencies: - '@types/node': 22.19.0 + '@types/node': 22.19.3 '@types/pumpify@1.4.5': dependencies: '@types/duplexify': 3.6.5 - '@types/node': 22.19.0 + '@types/node': 22.19.3 '@types/q@0.0.32': {} @@ -12398,7 +12135,7 @@ snapshots: '@types/responselike@1.0.0': dependencies: - '@types/node': 22.19.0 + '@types/node': 22.19.3 '@types/retry@0.12.2': {} @@ -12409,55 +12146,59 @@ snapshots: '@types/send@0.17.6': dependencies: '@types/mime': 1.3.5 - '@types/node': 22.19.0 + '@types/node': 22.19.3 '@types/send@1.2.1': dependencies: - '@types/node': 22.19.0 + '@types/node': 22.19.3 '@types/serve-index@1.9.4': dependencies: - '@types/express': 5.0.5 + '@types/express': 5.0.6 '@types/serve-static@1.15.10': dependencies: '@types/http-errors': 2.0.5 - '@types/node': 22.19.0 + '@types/node': 22.19.3 '@types/send': 0.17.6 '@types/serve-static@2.2.0': dependencies: '@types/http-errors': 2.0.5 - '@types/node': 22.19.0 + '@types/node': 22.19.3 '@types/sockjs@0.3.36': dependencies: - '@types/node': 22.19.0 + '@types/node': 22.19.3 '@types/ssri@7.1.5': dependencies: - '@types/node': 22.19.0 + '@types/node': 22.19.3 '@types/stack-trace@0.0.33': {} - '@types/watchpack@2.4.4': + '@types/tar-stream@3.1.4': + dependencies: + '@types/node': 22.19.3 + + '@types/watchpack@2.4.5': dependencies: '@types/graceful-fs': 4.1.9 - '@types/node': 22.19.0 + '@types/node': 22.19.3 '@types/which@3.0.4': {} '@types/ws@7.4.7': dependencies: - '@types/node': 22.19.0 + '@types/node': 22.19.3 '@types/ws@8.18.1': dependencies: - '@types/node': 22.19.0 + '@types/node': 22.19.3 '@types/yargs-parser@21.0.3': {} - '@types/yargs@17.0.34': + '@types/yargs@17.0.35': dependencies: '@types/yargs-parser': 21.0.3 @@ -12465,19 +12206,18 @@ snapshots: '@types/yauzl@2.10.3': dependencies: - '@types/node': 22.19.0 + '@types/node': 22.19.3 optional: true - '@typescript-eslint/eslint-plugin@8.46.4(@typescript-eslint/parser@8.46.4(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)': + '@typescript-eslint/eslint-plugin@8.50.0(@typescript-eslint/parser@8.50.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)': dependencies: '@eslint-community/regexpp': 4.12.2 - '@typescript-eslint/parser': 8.46.4(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3) - '@typescript-eslint/scope-manager': 8.46.4 - '@typescript-eslint/type-utils': 8.46.4(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3) - '@typescript-eslint/utils': 8.46.4(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3) - '@typescript-eslint/visitor-keys': 8.46.4 - eslint: 9.39.1(jiti@2.6.1) - graphemer: 1.4.0 + '@typescript-eslint/parser': 8.50.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/scope-manager': 8.50.0 + '@typescript-eslint/type-utils': 8.50.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/utils': 8.50.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/visitor-keys': 8.50.0 + eslint: 9.39.2(jiti@2.6.1) ignore: 7.0.5 natural-compare: 1.4.0 ts-api-utils: 2.1.0(typescript@5.9.3) @@ -12485,103 +12225,99 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@8.46.4(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)': + '@typescript-eslint/parser@8.50.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)': dependencies: - '@typescript-eslint/scope-manager': 8.46.4 - '@typescript-eslint/types': 8.46.4 - '@typescript-eslint/typescript-estree': 8.46.4(typescript@5.9.3) - '@typescript-eslint/visitor-keys': 8.46.4 + '@typescript-eslint/scope-manager': 8.50.0 + '@typescript-eslint/types': 8.50.0 + '@typescript-eslint/typescript-estree': 8.50.0(typescript@5.9.3) + '@typescript-eslint/visitor-keys': 8.50.0 debug: 4.4.3(supports-color@10.2.2) - eslint: 9.39.1(jiti@2.6.1) + eslint: 9.39.2(jiti@2.6.1) typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/project-service@8.46.4(typescript@5.9.3)': + '@typescript-eslint/project-service@8.50.0(typescript@5.9.3)': dependencies: - '@typescript-eslint/tsconfig-utils': 8.46.4(typescript@5.9.3) - '@typescript-eslint/types': 8.46.4 + '@typescript-eslint/tsconfig-utils': 8.50.0(typescript@5.9.3) + '@typescript-eslint/types': 8.50.0 debug: 4.4.3(supports-color@10.2.2) typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/scope-manager@8.46.4': + '@typescript-eslint/scope-manager@8.50.0': dependencies: - '@typescript-eslint/types': 8.46.4 - '@typescript-eslint/visitor-keys': 8.46.4 + '@typescript-eslint/types': 8.50.0 + '@typescript-eslint/visitor-keys': 8.50.0 - '@typescript-eslint/tsconfig-utils@8.46.4(typescript@5.9.3)': + '@typescript-eslint/tsconfig-utils@8.50.0(typescript@5.9.3)': dependencies: typescript: 5.9.3 - '@typescript-eslint/type-utils@8.46.4(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)': + '@typescript-eslint/type-utils@8.50.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)': dependencies: - '@typescript-eslint/types': 8.46.4 - '@typescript-eslint/typescript-estree': 8.46.4(typescript@5.9.3) - '@typescript-eslint/utils': 8.46.4(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/types': 8.50.0 + '@typescript-eslint/typescript-estree': 8.50.0(typescript@5.9.3) + '@typescript-eslint/utils': 8.50.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) debug: 4.4.3(supports-color@10.2.2) - eslint: 9.39.1(jiti@2.6.1) + eslint: 9.39.2(jiti@2.6.1) ts-api-utils: 2.1.0(typescript@5.9.3) typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/types@8.46.2': {} + '@typescript-eslint/types@8.50.0': {} - '@typescript-eslint/types@8.46.4': {} - - '@typescript-eslint/typescript-estree@8.46.4(typescript@5.9.3)': + '@typescript-eslint/typescript-estree@8.50.0(typescript@5.9.3)': dependencies: - '@typescript-eslint/project-service': 8.46.4(typescript@5.9.3) - '@typescript-eslint/tsconfig-utils': 8.46.4(typescript@5.9.3) - '@typescript-eslint/types': 8.46.4 - '@typescript-eslint/visitor-keys': 8.46.4 + '@typescript-eslint/project-service': 8.50.0(typescript@5.9.3) + '@typescript-eslint/tsconfig-utils': 8.50.0(typescript@5.9.3) + '@typescript-eslint/types': 8.50.0 + '@typescript-eslint/visitor-keys': 8.50.0 debug: 4.4.3(supports-color@10.2.2) - fast-glob: 3.3.3 - is-glob: 4.0.3 minimatch: 9.0.5 semver: 7.7.3 + tinyglobby: 0.2.15 ts-api-utils: 2.1.0(typescript@5.9.3) typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@8.46.4(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)': + '@typescript-eslint/utils@8.50.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)': dependencies: - '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.1(jiti@2.6.1)) - '@typescript-eslint/scope-manager': 8.46.4 - '@typescript-eslint/types': 8.46.4 - '@typescript-eslint/typescript-estree': 8.46.4(typescript@5.9.3) - eslint: 9.39.1(jiti@2.6.1) + '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.2(jiti@2.6.1)) + '@typescript-eslint/scope-manager': 8.50.0 + '@typescript-eslint/types': 8.50.0 + '@typescript-eslint/typescript-estree': 8.50.0(typescript@5.9.3) + eslint: 9.39.2(jiti@2.6.1) typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/visitor-keys@8.46.4': + '@typescript-eslint/visitor-keys@8.50.0': dependencies: - '@typescript-eslint/types': 8.46.4 + '@typescript-eslint/types': 8.50.0 eslint-visitor-keys: 4.2.1 - '@verdaccio/auth@8.0.0-next-8.24': + '@verdaccio/auth@8.0.0-next-8.28': dependencies: - '@verdaccio/config': 8.0.0-next-8.24 - '@verdaccio/core': 8.0.0-next-8.24 - '@verdaccio/loaders': 8.0.0-next-8.14 - '@verdaccio/signature': 8.0.0-next-8.16 + '@verdaccio/config': 8.0.0-next-8.28 + '@verdaccio/core': 8.0.0-next-8.28 + '@verdaccio/loaders': 8.0.0-next-8.18 + '@verdaccio/signature': 8.0.0-next-8.20 debug: 4.4.3(supports-color@10.2.2) lodash: 4.17.21 - verdaccio-htpasswd: 13.0.0-next-8.24 + verdaccio-htpasswd: 13.0.0-next-8.28 transitivePeerDependencies: - supports-color - '@verdaccio/config@8.0.0-next-8.24': + '@verdaccio/config@8.0.0-next-8.28': dependencies: - '@verdaccio/core': 8.0.0-next-8.24 + '@verdaccio/core': 8.0.0-next-8.28 debug: 4.4.3(supports-color@10.2.2) - js-yaml: 4.1.0 + js-yaml: 4.1.1 lodash: 4.17.21 - minimatch: 7.4.6 transitivePeerDependencies: - supports-color @@ -12594,7 +12330,7 @@ snapshots: process-warning: 1.0.0 semver: 7.7.2 - '@verdaccio/core@8.0.0-next-8.24': + '@verdaccio/core@8.0.0-next-8.28': dependencies: ajv: 8.17.1 http-errors: 2.0.0 @@ -12611,19 +12347,19 @@ snapshots: dependencies: lockfile: 1.0.4 - '@verdaccio/hooks@8.0.0-next-8.24': + '@verdaccio/hooks@8.0.0-next-8.28': dependencies: - '@verdaccio/core': 8.0.0-next-8.24 - '@verdaccio/logger': 8.0.0-next-8.24 + '@verdaccio/core': 8.0.0-next-8.28 + '@verdaccio/logger': 8.0.0-next-8.28 debug: 4.4.3(supports-color@10.2.2) got-cjs: 12.5.4 handlebars: 4.7.8 transitivePeerDependencies: - supports-color - '@verdaccio/loaders@8.0.0-next-8.14': + '@verdaccio/loaders@8.0.0-next-8.18': dependencies: - '@verdaccio/core': 8.0.0-next-8.24 + '@verdaccio/core': 8.0.0-next-8.28 debug: 4.4.3(supports-color@10.2.2) lodash: 4.17.21 transitivePeerDependencies: @@ -12642,9 +12378,9 @@ snapshots: transitivePeerDependencies: - supports-color - '@verdaccio/logger-commons@8.0.0-next-8.24': + '@verdaccio/logger-commons@8.0.0-next-8.28': dependencies: - '@verdaccio/core': 8.0.0-next-8.24 + '@verdaccio/core': 8.0.0-next-8.28 '@verdaccio/logger-prettify': 8.0.0-next-8.4 colorette: 2.0.20 debug: 4.4.3(supports-color@10.2.2) @@ -12660,33 +12396,32 @@ snapshots: pino-abstract-transport: 1.2.0 sonic-boom: 3.8.1 - '@verdaccio/logger@8.0.0-next-8.24': + '@verdaccio/logger@8.0.0-next-8.28': dependencies: - '@verdaccio/logger-commons': 8.0.0-next-8.24 - pino: 9.13.1 + '@verdaccio/logger-commons': 8.0.0-next-8.28 + pino: 9.14.0 transitivePeerDependencies: - supports-color - '@verdaccio/middleware@8.0.0-next-8.24': + '@verdaccio/middleware@8.0.0-next-8.28': dependencies: - '@verdaccio/config': 8.0.0-next-8.24 - '@verdaccio/core': 8.0.0-next-8.24 - '@verdaccio/url': 13.0.0-next-8.24 + '@verdaccio/config': 8.0.0-next-8.28 + '@verdaccio/core': 8.0.0-next-8.28 + '@verdaccio/url': 13.0.0-next-8.28 debug: 4.4.3(supports-color@10.2.2) express: 4.21.2 express-rate-limit: 5.5.1 lodash: 4.17.21 lru-cache: 7.18.3 - mime: 2.6.0 transitivePeerDependencies: - supports-color '@verdaccio/search-indexer@8.0.0-next-8.5': {} - '@verdaccio/signature@8.0.0-next-8.16': + '@verdaccio/signature@8.0.0-next-8.20': dependencies: - '@verdaccio/config': 8.0.0-next-8.24 - '@verdaccio/core': 8.0.0-next-8.24 + '@verdaccio/config': 8.0.0-next-8.28 + '@verdaccio/core': 8.0.0-next-8.28 debug: 4.4.3(supports-color@10.2.2) jsonwebtoken: 9.0.2 transitivePeerDependencies: @@ -12694,10 +12429,10 @@ snapshots: '@verdaccio/streams@10.2.1': {} - '@verdaccio/tarball@13.0.0-next-8.24': + '@verdaccio/tarball@13.0.0-next-8.28': dependencies: - '@verdaccio/core': 8.0.0-next-8.24 - '@verdaccio/url': 13.0.0-next-8.24 + '@verdaccio/core': 8.0.0-next-8.28 + '@verdaccio/url': 13.0.0-next-8.28 debug: 4.4.3(supports-color@10.2.2) gunzip-maybe: 1.4.2 tar-stream: 3.1.7 @@ -12706,81 +12441,80 @@ snapshots: - react-native-b4a - supports-color - '@verdaccio/ui-theme@8.0.0-next-8.24': {} + '@verdaccio/ui-theme@8.0.0-next-8.28': {} - '@verdaccio/url@13.0.0-next-8.24': + '@verdaccio/url@13.0.0-next-8.28': dependencies: - '@verdaccio/core': 8.0.0-next-8.24 + '@verdaccio/core': 8.0.0-next-8.28 debug: 4.4.3(supports-color@10.2.2) - lodash: 4.17.21 - validator: 13.15.15 + validator: 13.15.23 transitivePeerDependencies: - supports-color - '@verdaccio/utils@8.1.0-next-8.24': + '@verdaccio/utils@8.1.0-next-8.28': dependencies: - '@verdaccio/core': 8.0.0-next-8.24 + '@verdaccio/core': 8.0.0-next-8.28 lodash: 4.17.21 minimatch: 7.4.6 - '@vitejs/plugin-basic-ssl@2.1.0(vite@7.2.2(@types/node@24.10.0)(jiti@2.6.1)(less@4.4.2)(sass@1.94.0)(terser@5.44.1)(tsx@4.20.6)(yaml@2.8.1))': + '@vitejs/plugin-basic-ssl@2.1.0(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(less@4.4.2)(sass@1.97.0)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))': dependencies: - vite: 7.2.2(@types/node@24.10.0)(jiti@2.6.1)(less@4.4.2)(sass@1.94.0)(terser@5.44.1)(tsx@4.20.6)(yaml@2.8.1) + vite: 7.3.0(@types/node@24.10.4)(jiti@2.6.1)(less@4.4.2)(sass@1.97.0)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2) - '@vitest/coverage-v8@4.0.8(vitest@4.0.8(@types/node@24.10.0)(jiti@2.6.1)(jsdom@27.2.0(bufferutil@4.0.9)(utf-8-validate@6.0.5))(less@4.4.2)(sass@1.94.0)(terser@5.44.1)(tsx@4.20.6)(yaml@2.8.1))': + '@vitest/coverage-v8@4.0.15(vitest@4.0.15(@opentelemetry/api@1.9.0)(@types/node@24.10.4)(jiti@2.6.1)(jsdom@27.3.0(bufferutil@4.0.9)(utf-8-validate@6.0.5))(less@4.4.2)(sass@1.97.0)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))': dependencies: '@bcoe/v8-coverage': 1.0.2 - '@vitest/utils': 4.0.8 - ast-v8-to-istanbul: 0.3.8 - debug: 4.4.3(supports-color@10.2.2) + '@vitest/utils': 4.0.15 + ast-v8-to-istanbul: 0.3.9 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.5.1 + obug: 2.1.1 std-env: 3.10.0 tinyrainbow: 3.0.3 - vitest: 4.0.8(@types/node@24.10.0)(jiti@2.6.1)(jsdom@27.2.0(bufferutil@4.0.9)(utf-8-validate@6.0.5))(less@4.4.2)(sass@1.94.0)(terser@5.44.1)(tsx@4.20.6)(yaml@2.8.1) + vitest: 4.0.15(@opentelemetry/api@1.9.0)(@types/node@24.10.4)(jiti@2.6.1)(jsdom@27.3.0(bufferutil@4.0.9)(utf-8-validate@6.0.5))(less@4.4.2)(sass@1.97.0)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2) transitivePeerDependencies: - supports-color - '@vitest/expect@4.0.8': + '@vitest/expect@4.0.15': dependencies: - '@standard-schema/spec': 1.0.0 + '@standard-schema/spec': 1.1.0 '@types/chai': 5.2.3 - '@vitest/spy': 4.0.8 - '@vitest/utils': 4.0.8 - chai: 6.2.0 + '@vitest/spy': 4.0.15 + '@vitest/utils': 4.0.15 + chai: 6.2.1 tinyrainbow: 3.0.3 - '@vitest/mocker@4.0.8(vite@7.2.2(@types/node@24.10.0)(jiti@2.6.1)(less@4.4.2)(sass@1.94.0)(terser@5.44.1)(tsx@4.20.6)(yaml@2.8.1))': + '@vitest/mocker@4.0.15(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(less@4.4.2)(sass@1.97.0)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))': dependencies: - '@vitest/spy': 4.0.8 + '@vitest/spy': 4.0.15 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: - vite: 7.2.2(@types/node@24.10.0)(jiti@2.6.1)(less@4.4.2)(sass@1.94.0)(terser@5.44.1)(tsx@4.20.6)(yaml@2.8.1) + vite: 7.3.0(@types/node@24.10.4)(jiti@2.6.1)(less@4.4.2)(sass@1.97.0)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2) - '@vitest/pretty-format@4.0.8': + '@vitest/pretty-format@4.0.15': dependencies: tinyrainbow: 3.0.3 - '@vitest/runner@4.0.8': + '@vitest/runner@4.0.15': dependencies: - '@vitest/utils': 4.0.8 + '@vitest/utils': 4.0.15 pathe: 2.0.3 - '@vitest/snapshot@4.0.8': + '@vitest/snapshot@4.0.15': dependencies: - '@vitest/pretty-format': 4.0.8 + '@vitest/pretty-format': 4.0.15 magic-string: 0.30.21 pathe: 2.0.3 - '@vitest/spy@4.0.8': {} + '@vitest/spy@4.0.15': {} - '@vitest/utils@4.0.8': + '@vitest/utils@4.0.15': dependencies: - '@vitest/pretty-format': 4.0.8 + '@vitest/pretty-format': 4.0.15 tinyrainbow: 3.0.3 '@web/browser-logs@0.4.1': @@ -12799,7 +12533,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 @@ -12816,11 +12550,11 @@ snapshots: '@web/dev-server-rollup@0.6.4(bufferutil@4.0.9)': dependencies: - '@rollup/plugin-node-resolve': 15.3.1(rollup@4.53.2) + '@rollup/plugin-node-resolve': 15.3.1(rollup@4.53.5) '@web/dev-server-core': 0.7.5(bufferutil@4.0.9) nanocolors: 0.2.13 parse5: 6.0.1 - rollup: 4.53.2 + rollup: 4.53.5 whatwg-url: 14.2.0 transitivePeerDependencies: - bufferutil @@ -12858,7 +12592,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.27.0(bufferutil@4.0.9) + puppeteer-core: 24.34.0(bufferutil@4.0.9) transitivePeerDependencies: - bare-abort-controller - bare-buffer @@ -13044,7 +12778,7 @@ snapshots: jsonparse: 1.3.1 through: 2.3.8 - abbrev@3.0.1: {} + abbrev@4.0.0: {} abort-controller@3.0.0: dependencies: @@ -13057,7 +12791,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): @@ -13120,22 +12854,22 @@ snapshots: json-schema-traverse: 1.0.0 require-from-string: 2.0.2 - algoliasearch@5.43.0: - dependencies: - '@algolia/abtesting': 1.9.0 - '@algolia/client-abtesting': 5.43.0 - '@algolia/client-analytics': 5.43.0 - '@algolia/client-common': 5.43.0 - '@algolia/client-insights': 5.43.0 - '@algolia/client-personalization': 5.43.0 - '@algolia/client-query-suggestions': 5.43.0 - '@algolia/client-search': 5.43.0 - '@algolia/ingestion': 1.43.0 - '@algolia/monitoring': 1.43.0 - '@algolia/recommend': 5.43.0 - '@algolia/requester-browser-xhr': 5.43.0 - '@algolia/requester-fetch': 5.43.0 - '@algolia/requester-node-http': 5.43.0 + algoliasearch@5.46.0: + dependencies: + '@algolia/abtesting': 1.12.0 + '@algolia/client-abtesting': 5.46.0 + '@algolia/client-analytics': 5.46.0 + '@algolia/client-common': 5.46.0 + '@algolia/client-insights': 5.46.0 + '@algolia/client-personalization': 5.46.0 + '@algolia/client-query-suggestions': 5.46.0 + '@algolia/client-search': 5.46.0 + '@algolia/ingestion': 1.46.0 + '@algolia/monitoring': 1.46.0 + '@algolia/recommend': 5.46.0 + '@algolia/requester-browser-xhr': 5.46.0 + '@algolia/requester-fetch': 5.46.0 + '@algolia/requester-node-http': 5.46.0 ansi-colors@4.1.3: {} @@ -13143,7 +12877,7 @@ snapshots: dependencies: type-fest: 0.21.3 - ansi-escapes@7.1.1: + ansi-escapes@7.2.0: dependencies: environment: 1.1.0 @@ -13192,7 +12926,7 @@ snapshots: call-bind: 1.0.8 call-bound: 1.0.4 define-properties: 1.2.1 - es-abstract: 1.24.0 + es-abstract: 1.24.1 es-object-atoms: 1.1.1 get-intrinsic: 1.3.0 is-string: 1.1.1 @@ -13213,7 +12947,7 @@ snapshots: call-bind: 1.0.8 call-bound: 1.0.4 define-properties: 1.2.1 - es-abstract: 1.24.0 + es-abstract: 1.24.1 es-errors: 1.3.0 es-object-atoms: 1.1.1 es-shim-unscopables: 1.1.0 @@ -13222,14 +12956,14 @@ snapshots: dependencies: call-bind: 1.0.8 define-properties: 1.2.1 - es-abstract: 1.24.0 + es-abstract: 1.24.1 es-shim-unscopables: 1.1.0 array.prototype.flatmap@1.3.3: dependencies: call-bind: 1.0.8 define-properties: 1.2.1 - es-abstract: 1.24.0 + es-abstract: 1.24.1 es-shim-unscopables: 1.1.0 arraybuffer.prototype.slice@1.0.4: @@ -13237,7 +12971,7 @@ snapshots: array-buffer-byte-length: 1.0.2 call-bind: 1.0.8 define-properties: 1.2.1 - es-abstract: 1.24.0 + es-abstract: 1.24.1 es-errors: 1.3.0 get-intrinsic: 1.3.0 is-array-buffer: 3.0.5 @@ -13258,7 +12992,7 @@ snapshots: dependencies: tslib: 2.8.1 - ast-v8-to-istanbul@0.3.8: + ast-v8-to-istanbul@0.3.9: dependencies: '@jridgewell/trace-mapping': 0.3.31 estree-walker: 3.0.3 @@ -13280,12 +13014,11 @@ snapshots: atomic-sleep@1.0.0: {} - autoprefixer@10.4.22(postcss@8.5.6): + autoprefixer@10.4.23(postcss@8.5.6): dependencies: - browserslist: 4.27.0 - caniuse-lite: 1.0.30001754 + browserslist: 4.28.1 + caniuse-lite: 1.0.30001761 fraction.js: 5.3.4 - normalize-range: 0.1.2 picocolors: 1.1.1 postcss: 8.5.6 postcss-value-parser: 4.2.0 @@ -13300,11 +13033,11 @@ snapshots: b4a@1.7.3: {} - babel-loader@10.0.0(@babel/core@7.28.5)(webpack@5.102.1(esbuild@0.27.0)): + babel-loader@10.0.0(@babel/core@7.28.5)(webpack@5.104.0(esbuild@0.27.1)): dependencies: '@babel/core': 7.28.5 find-up: 5.0.0 - webpack: 5.102.1(esbuild@0.27.0) + webpack: 5.104.0(esbuild@0.27.1) babel-plugin-polyfill-corejs2@0.4.14(@babel/core@7.28.5): dependencies: @@ -13319,7 +13052,7 @@ snapshots: dependencies: '@babel/core': 7.28.5 '@babel/helper-define-polyfill-provider': 0.6.5(@babel/core@7.28.5) - core-js-compat: 3.46.0 + core-js-compat: 3.47.0 transitivePeerDependencies: - supports-color @@ -13332,13 +13065,13 @@ snapshots: balanced-match@1.0.2: {} - bare-events@2.8.1: {} + bare-events@2.8.2: {} - bare-fs@4.5.0: + bare-fs@4.5.2: dependencies: - bare-events: 2.8.1 + bare-events: 2.8.2 bare-path: 3.0.0 - bare-stream: 2.7.0(bare-events@2.8.1) + bare-stream: 2.7.0(bare-events@2.8.2) bare-url: 2.3.2 fast-fifo: 1.3.2 transitivePeerDependencies: @@ -13354,11 +13087,11 @@ snapshots: bare-os: 3.6.2 optional: true - bare-stream@2.7.0(bare-events@2.8.1): + bare-stream@2.7.0(bare-events@2.8.2): dependencies: streamx: 2.23.0 optionalDependencies: - bare-events: 2.8.1 + bare-events: 2.8.2 transitivePeerDependencies: - bare-abort-controller - react-native-b4a @@ -13373,7 +13106,7 @@ snapshots: base64id@2.0.0: {} - baseline-browser-mapping@2.8.23: {} + baseline-browser-mapping@2.9.11: {} basic-ftp@5.0.5: {} @@ -13437,16 +13170,33 @@ snapshots: transitivePeerDependencies: - supports-color - body-parser@2.2.0: + body-parser@1.20.4: + dependencies: + bytes: 3.1.2 + content-type: 1.0.5 + debug: 2.6.9 + depd: 2.0.0 + destroy: 1.2.0 + http-errors: 2.0.1 + iconv-lite: 0.4.24 + on-finished: 2.4.1 + qs: 6.14.0 + raw-body: 2.5.3 + type-is: 1.6.18 + unpipe: 1.0.0 + transitivePeerDependencies: + - supports-color + + 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.1 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 @@ -13513,12 +13263,12 @@ snapshots: micromatch: 4.0.8 opn: 5.3.0 portscanner: 2.2.0 - raw-body: 2.5.2 + raw-body: 2.5.3 resp-modifier: 6.0.2 rx: 4.1.0 - send: 0.19.1 + send: 0.19.2 serve-index: 1.9.1 - serve-static: 1.16.2 + serve-static: 1.16.3 server-destroy: 1.0.1 socket.io: 4.8.1(bufferutil@4.0.9)(utf-8-validate@6.0.5) ua-parser-js: 1.0.41 @@ -13533,13 +13283,13 @@ snapshots: dependencies: pako: 0.2.9 - browserslist@4.27.0: + browserslist@4.28.1: dependencies: - baseline-browser-mapping: 2.8.23 - caniuse-lite: 1.0.30001753 - electron-to-chromium: 1.5.244 + baseline-browser-mapping: 2.9.11 + caniuse-lite: 1.0.30001761 + electron-to-chromium: 1.5.267 node-releases: 2.0.27 - update-browserslist-db: 1.1.4(browserslist@4.27.0) + update-browserslist-db: 1.2.3(browserslist@4.28.1) browserstack@1.6.1: dependencies: @@ -13575,34 +13325,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.2 - 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 - lru-cache: 11.2.2 + glob: 13.0.0 + lru-cache: 11.2.4 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: @@ -13644,13 +13379,11 @@ snapshots: camelcase@6.3.0: {} - caniuse-lite@1.0.30001753: {} - - caniuse-lite@1.0.30001754: {} + caniuse-lite@1.0.30001761: {} caseless@0.12.0: {} - chai@6.2.0: {} + chai@6.2.1: {} chalk-template@0.4.0: dependencies: @@ -13697,13 +13430,17 @@ snapshots: dependencies: readdirp: 4.1.2 + chokidar@5.0.0: + dependencies: + readdirp: 5.0.0 + chownr@1.1.4: {} chownr@3.0.0: {} chrome-launcher@0.15.2: dependencies: - '@types/node': 22.19.0 + '@types/node': 22.19.3 escape-string-regexp: 4.0.0 is-wsl: 2.2.0 lighthouse-logger: 1.4.2 @@ -13712,9 +13449,9 @@ snapshots: chrome-trace-event@1.0.4: {} - chromium-bidi@10.5.1(devtools-protocol@0.0.1521046): + chromium-bidi@12.0.1(devtools-protocol@0.0.1534754): dependencies: - devtools-protocol: 0.0.1521046 + devtools-protocol: 0.0.1534754 mitt: 3.0.1 zod: 3.25.76 @@ -13784,7 +13521,7 @@ snapshots: '@hapi/bourne': 3.0.0 inflation: 2.1.0 qs: 6.14.0 - raw-body: 2.5.2 + raw-body: 2.5.3 type-is: 1.6.18 co@4.6.0: {} @@ -13873,9 +13610,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: {} @@ -13891,6 +13626,8 @@ snapshots: cookie-signature@1.0.6: {} + cookie-signature@1.0.7: {} + cookie-signature@1.2.2: {} cookie@0.7.1: {} @@ -13906,18 +13643,18 @@ snapshots: dependencies: is-what: 3.14.1 - copy-webpack-plugin@13.0.1(webpack@5.102.1(esbuild@0.27.0)): + copy-webpack-plugin@13.0.1(webpack@5.104.0(esbuild@0.27.1)): 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.27.0) + webpack: 5.104.0(esbuild@0.27.1) - core-js-compat@3.46.0: + core-js-compat@3.47.0: dependencies: - browserslist: 4.27.0 + browserslist: 4.28.1 core-util-is@1.0.2: {} @@ -13932,7 +13669,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 @@ -13957,7 +13694,7 @@ snapshots: shebang-command: 2.0.0 which: 2.0.2 - css-loader@7.1.2(webpack@5.102.1(esbuild@0.27.0)): + css-loader@7.1.2(webpack@5.104.0(esbuild@0.27.1)): dependencies: icss-utils: 5.1.0(postcss@8.5.6) postcss: 8.5.6 @@ -13968,7 +13705,7 @@ snapshots: postcss-value-parser: 4.2.0 semver: 7.7.3 optionalDependencies: - webpack: 5.102.1(esbuild@0.27.0) + webpack: 5.104.0(esbuild@0.27.1) css-select@6.0.0: dependencies: @@ -13987,10 +13724,10 @@ snapshots: cssesc@3.0.0: {} - cssstyle@5.3.3: + cssstyle@5.3.5: dependencies: - '@asamuzakjp/css-color': 4.0.5 - '@csstools/css-syntax-patches-for-csstree': 1.0.15 + '@asamuzakjp/css-color': 4.1.1 + '@csstools/css-syntax-patches-for-csstree': 1.0.22 css-tree: 3.1.0 custom-event@1.0.1: {} @@ -14078,12 +13815,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: @@ -14151,7 +13888,7 @@ snapshots: devtools-protocol@0.0.1045489: {} - devtools-protocol@0.0.1521046: {} + devtools-protocol@0.0.1534754: {} di@0.0.1: {} @@ -14241,7 +13978,7 @@ snapshots: dependencies: jake: 10.9.4 - electron-to-chromium@1.5.244: {} + electron-to-chromium@1.5.267: {} emoji-regex@10.6.0: {} @@ -14280,7 +14017,7 @@ snapshots: engine.io@6.6.4(bufferutil@4.0.9)(utf-8-validate@6.0.5): dependencies: '@types/cors': 2.8.19 - '@types/node': 22.19.0 + '@types/node': 22.19.3 accepts: 1.3.8 base64id: 2.0.0 cookie: 0.7.2 @@ -14293,7 +14030,7 @@ snapshots: - supports-color - utf-8-validate - enhanced-resolve@5.18.3: + enhanced-resolve@5.18.4: dependencies: graceful-fs: 4.2.11 tapable: 2.3.0 @@ -14328,7 +14065,7 @@ snapshots: errorstacks@2.4.1: {} - es-abstract@1.24.0: + es-abstract@1.24.1: dependencies: array-buffer-byte-length: 1.0.2 arraybuffer.prototype.slice: 1.0.4 @@ -14391,6 +14128,8 @@ snapshots: es-module-lexer@1.7.0: {} + es-module-lexer@2.0.0: {} + es-object-atoms@1.1.1: dependencies: es-errors: 1.3.0 @@ -14418,65 +14157,36 @@ snapshots: dependencies: es6-promise: 4.2.8 - esbuild-wasm@0.27.0: {} + esbuild-wasm@0.27.1: {} - 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.27.0: + esbuild@0.27.1: optionalDependencies: - '@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 + '@esbuild/aix-ppc64': 0.27.1 + '@esbuild/android-arm': 0.27.1 + '@esbuild/android-arm64': 0.27.1 + '@esbuild/android-x64': 0.27.1 + '@esbuild/darwin-arm64': 0.27.1 + '@esbuild/darwin-x64': 0.27.1 + '@esbuild/freebsd-arm64': 0.27.1 + '@esbuild/freebsd-x64': 0.27.1 + '@esbuild/linux-arm': 0.27.1 + '@esbuild/linux-arm64': 0.27.1 + '@esbuild/linux-ia32': 0.27.1 + '@esbuild/linux-loong64': 0.27.1 + '@esbuild/linux-mips64el': 0.27.1 + '@esbuild/linux-ppc64': 0.27.1 + '@esbuild/linux-riscv64': 0.27.1 + '@esbuild/linux-s390x': 0.27.1 + '@esbuild/linux-x64': 0.27.1 + '@esbuild/netbsd-arm64': 0.27.1 + '@esbuild/netbsd-x64': 0.27.1 + '@esbuild/openbsd-arm64': 0.27.1 + '@esbuild/openbsd-x64': 0.27.1 + '@esbuild/openharmony-arm64': 0.27.1 + '@esbuild/sunos-x64': 0.27.1 + '@esbuild/win32-arm64': 0.27.1 + '@esbuild/win32-ia32': 0.27.1 + '@esbuild/win32-x64': 0.27.1 escalade@3.2.0: {} @@ -14494,9 +14204,9 @@ snapshots: optionalDependencies: source-map: 0.6.1 - eslint-config-prettier@10.1.8(eslint@9.39.1(jiti@2.6.1)): + eslint-config-prettier@10.1.8(eslint@9.39.2(jiti@2.6.1)): dependencies: - eslint: 9.39.1(jiti@2.6.1) + eslint: 9.39.2(jiti@2.6.1) eslint-import-resolver-node@0.3.9: dependencies: @@ -14506,21 +14216,21 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-module-utils@2.12.1(@typescript-eslint/parser@8.46.4(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint@9.39.1(jiti@2.6.1)): + eslint-module-utils@2.12.1(@typescript-eslint/parser@8.50.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint@9.39.2(jiti@2.6.1)): dependencies: debug: 3.2.7 optionalDependencies: - '@typescript-eslint/parser': 8.46.4(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3) - eslint: 9.39.1(jiti@2.6.1) + '@typescript-eslint/parser': 8.50.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) + eslint: 9.39.2(jiti@2.6.1) eslint-import-resolver-node: 0.3.9 transitivePeerDependencies: - supports-color - eslint-plugin-header@3.1.1(eslint@9.39.1(jiti@2.6.1)): + eslint-plugin-header@3.1.1(eslint@9.39.2(jiti@2.6.1)): dependencies: - eslint: 9.39.1(jiti@2.6.1) + eslint: 9.39.2(jiti@2.6.1) - eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.46.4(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.1(jiti@2.6.1)): + eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.50.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1)): dependencies: '@rtsao/scc': 1.1.0 array-includes: 3.1.9 @@ -14529,9 +14239,9 @@ snapshots: array.prototype.flatmap: 1.3.3 debug: 3.2.7 doctrine: 2.1.0 - eslint: 9.39.1(jiti@2.6.1) + eslint: 9.39.2(jiti@2.6.1) eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.46.4(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint@9.39.1(jiti@2.6.1)) + eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.50.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint@9.39.2(jiti@2.6.1)) hasown: 2.0.2 is-core-module: 2.16.1 is-glob: 4.0.3 @@ -14543,7 +14253,7 @@ snapshots: string.prototype.trimend: 1.0.9 tsconfig-paths: 3.15.0 optionalDependencies: - '@typescript-eslint/parser': 8.46.4(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/parser': 8.50.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) transitivePeerDependencies: - eslint-import-resolver-typescript - eslint-import-resolver-webpack @@ -14563,15 +14273,15 @@ snapshots: eslint-visitor-keys@4.2.1: {} - eslint@9.39.1(jiti@2.6.1): + eslint@9.39.2(jiti@2.6.1): dependencies: - '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.1(jiti@2.6.1)) + '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.2(jiti@2.6.1)) '@eslint-community/regexpp': 4.12.2 '@eslint/config-array': 0.21.1 '@eslint/config-helpers': 0.4.2 '@eslint/core': 0.17.0 - '@eslint/eslintrc': 3.3.1 - '@eslint/js': 9.39.1 + '@eslint/eslintrc': 3.3.3 + '@eslint/js': 9.39.2 '@eslint/plugin-kit': 0.4.1 '@humanfs/node': 0.16.7 '@humanwhocodes/module-importer': 1.0.1 @@ -14644,7 +14354,7 @@ snapshots: events-universal@1.0.1: dependencies: - bare-events: 2.8.1 + bare-events: 2.8.2 transitivePeerDependencies: - bare-abort-controller @@ -14670,15 +14380,15 @@ snapshots: exit@0.1.2: {} - expect-type@1.2.2: {} + expect-type@1.3.0: {} exponential-backoff@3.1.3: {} express-rate-limit@5.5.1: {} - express-rate-limit@7.5.1(express@5.1.0): + express-rate-limit@7.5.1(express@5.2.1): dependencies: - express: 5.1.0 + express: 5.2.1 express@4.21.2: dependencies: @@ -14716,23 +14426,60 @@ snapshots: transitivePeerDependencies: - supports-color - express@5.1.0: + express@4.22.1: + dependencies: + accepts: 1.3.8 + array-flatten: 1.1.1 + body-parser: 1.20.4 + content-disposition: 0.5.4 + content-type: 1.0.5 + cookie: 0.7.2 + cookie-signature: 1.0.7 + debug: 2.6.9 + depd: 2.0.0 + encodeurl: 2.0.0 + escape-html: 1.0.3 + etag: 1.8.1 + finalhandler: 1.3.2 + fresh: 0.5.2 + http-errors: 2.0.1 + merge-descriptors: 1.0.3 + methods: 1.1.2 + on-finished: 2.4.1 + parseurl: 1.3.3 + path-to-regexp: 0.1.12 + proxy-addr: 2.0.7 + qs: 6.14.0 + range-parser: 1.2.1 + safe-buffer: 5.2.1 + send: 0.19.2 + serve-static: 1.16.3 + setprototypeof: 1.2.0 + statuses: 2.0.2 + type-is: 1.6.18 + utils-merge: 1.0.1 + vary: 1.1.2 + transitivePeerDependencies: + - supports-color + + express@5.2.1: 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 debug: 4.4.3(supports-color@10.2.2) + depd: 2.0.0 encodeurl: 2.0.0 escape-html: 1.0.3 etag: 1.8.1 - finalhandler: 2.1.0 + finalhandler: 2.1.1 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 @@ -14740,8 +14487,8 @@ snapshots: qs: 6.14.0 range-parser: 1.2.1 router: 2.2.0 - send: 1.2.0 - serve-static: 2.2.0 + send: 1.2.1 + serve-static: 2.2.1 statuses: 2.0.2 type-is: 2.0.1 vary: 1.1.2 @@ -14752,7 +14499,7 @@ snapshots: extract-zip@2.0.1: dependencies: - debug: 4.4.3(supports-color@10.2.2) + debug: 4.3.4 get-stream: 5.2.0 yauzl: 2.10.0 optionalDependencies: @@ -14851,7 +14598,19 @@ snapshots: transitivePeerDependencies: - supports-color - finalhandler@2.1.0: + finalhandler@1.3.2: + dependencies: + debug: 2.6.9 + encodeurl: 2.0.0 + escape-html: 1.0.3 + on-finished: 2.4.1 + parseurl: 1.3.3 + statuses: 2.0.2 + unpipe: 1.0.0 + transitivePeerDependencies: + - supports-color + + finalhandler@2.1.1: dependencies: debug: 4.4.3(supports-color@10.2.2) encodeurl: 2.0.0 @@ -14883,35 +14642,35 @@ snapshots: locate-path: 6.0.0 path-exists: 4.0.0 - firebase@12.5.0: + firebase@12.7.0: dependencies: - '@firebase/ai': 2.5.0(@firebase/app-types@0.9.3)(@firebase/app@0.14.5) - '@firebase/analytics': 0.10.19(@firebase/app@0.14.5) - '@firebase/analytics-compat': 0.2.25(@firebase/app-compat@0.5.5)(@firebase/app@0.14.5) - '@firebase/app': 0.14.5 - '@firebase/app-check': 0.11.0(@firebase/app@0.14.5) - '@firebase/app-check-compat': 0.4.0(@firebase/app-compat@0.5.5)(@firebase/app@0.14.5) - '@firebase/app-compat': 0.5.5 + '@firebase/ai': 2.6.1(@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.1(@firebase/app@0.14.5) - '@firebase/auth-compat': 0.6.1(@firebase/app-compat@0.5.5)(@firebase/app-types@0.9.3)(@firebase/app@0.14.5) - '@firebase/data-connect': 0.3.11(@firebase/app@0.14.5) + '@firebase/auth': 1.12.0(@firebase/app@0.14.6) + '@firebase/auth-compat': 0.6.2(@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.5) - '@firebase/firestore-compat': 0.4.2(@firebase/app-compat@0.5.5)(@firebase/app-types@0.9.3)(@firebase/app@0.14.5) - '@firebase/functions': 0.13.1(@firebase/app@0.14.5) - '@firebase/functions-compat': 0.4.1(@firebase/app-compat@0.5.5)(@firebase/app@0.14.5) - '@firebase/installations': 0.6.19(@firebase/app@0.14.5) - '@firebase/installations-compat': 0.2.19(@firebase/app-compat@0.5.5)(@firebase/app-types@0.9.3)(@firebase/app@0.14.5) - '@firebase/messaging': 0.12.23(@firebase/app@0.14.5) - '@firebase/messaging-compat': 0.2.23(@firebase/app-compat@0.5.5)(@firebase/app@0.14.5) - '@firebase/performance': 0.7.9(@firebase/app@0.14.5) - '@firebase/performance-compat': 0.2.22(@firebase/app-compat@0.5.5)(@firebase/app@0.14.5) - '@firebase/remote-config': 0.7.0(@firebase/app@0.14.5) - '@firebase/remote-config-compat': 0.2.20(@firebase/app-compat@0.5.5)(@firebase/app@0.14.5) - '@firebase/storage': 0.14.0(@firebase/app@0.14.5) - '@firebase/storage-compat': 0.4.0(@firebase/app-compat@0.5.5)(@firebase/app-types@0.9.3)(@firebase/app@0.14.5) + '@firebase/firestore': 4.9.3(@firebase/app@0.14.6) + '@firebase/firestore-compat': 0.4.3(@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' @@ -14955,7 +14714,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 @@ -15023,7 +14782,7 @@ snapshots: gcp-metadata@8.1.2(supports-color@10.2.2): dependencies: gaxios: 7.1.3(supports-color@10.2.2) - google-logging-utils: 1.1.2 + google-logging-utils: 1.1.3 json-bigint: 1.0.0 transitivePeerDependencies: - supports-color @@ -15106,7 +14865,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 @@ -15115,14 +14874,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.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: @@ -15166,19 +14922,19 @@ snapshots: ecdsa-sig-formatter: 1.0.11 gaxios: 7.1.3(supports-color@10.2.2) gcp-metadata: 8.1.2(supports-color@10.2.2) - google-logging-utils: 1.1.2 + google-logging-utils: 1.1.3 gtoken: 8.0.0(supports-color@10.2.2) - jws: 4.0.0 + jws: 4.0.1 transitivePeerDependencies: - supports-color - google-gax@5.0.5(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.3 '@grpc/proto-loader': 0.8.0 duplexify: 4.1.3 google-auth-library: 10.5.0(supports-color@10.2.2) - google-logging-utils: 1.1.2 + google-logging-utils: 1.1.3 node-fetch: 3.3.2 object-hash: 3.0.0 proto3-json-serializer: 3.0.4 @@ -15188,7 +14944,7 @@ snapshots: transitivePeerDependencies: - supports-color - google-logging-utils@1.1.2: {} + google-logging-utils@1.1.3: {} gopd@1.2.0: {} @@ -15209,8 +14965,6 @@ snapshots: graceful-fs@4.2.11: {} - graphemer@1.4.0: {} - graphql-tag@2.12.6(graphql@16.12.0): dependencies: graphql: 16.12.0 @@ -15220,13 +14974,13 @@ snapshots: grpc-gcp@1.0.1(protobufjs@7.5.4): dependencies: - '@grpc/grpc-js': 1.14.0 + '@grpc/grpc-js': 1.14.3 protobufjs: 7.5.4 gtoken@8.0.0(supports-color@10.2.2): dependencies: gaxios: 7.1.3(supports-color@10.2.2) - jws: 4.0.0 + jws: 4.0.1 transitivePeerDependencies: - supports-color @@ -15285,7 +15039,7 @@ snapshots: hosted-git-info@9.0.2: dependencies: - lru-cache: 11.2.2 + lru-cache: 11.2.4 hpack.js@2.1.6: dependencies: @@ -15341,6 +15095,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): @@ -15443,7 +15205,7 @@ snapshots: dependencies: safer-buffer: 2.1.2 - iconv-lite@0.7.0: + iconv-lite@0.7.1: dependencies: safer-buffer: 2.1.2 @@ -15492,8 +15254,6 @@ snapshots: ini@1.3.8: {} - ini@5.0.0: {} - ini@6.0.0: {} injection-js@2.6.1: @@ -15513,13 +15273,13 @@ 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: {} ipaddr.js@1.9.1: {} - ipaddr.js@2.2.0: {} + ipaddr.js@2.3.0: {} is-array-buffer@3.0.5: dependencies: @@ -15599,6 +15359,8 @@ snapshots: is-gzip@1.0.0: {} + is-in-ssh@1.0.0: {} + is-inside-container@1.0.0: dependencies: is-docker: 3.0.0 @@ -15727,7 +15489,7 @@ snapshots: isbinaryfile@4.0.10: {} - isbinaryfile@5.0.6: {} + isbinaryfile@5.0.7: {} isexe@2.0.0: {} @@ -15792,10 +15554,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 @@ -15806,7 +15564,7 @@ snapshots: jasmine-core@4.6.1: {} - jasmine-core@5.12.1: {} + jasmine-core@5.13.0: {} jasmine-reporters@2.5.2: dependencies: @@ -15823,38 +15581,40 @@ snapshots: glob: 7.2.3 jasmine-core: 2.8.0 - jasmine@5.12.0: + jasmine@5.13.0: dependencies: - glob: 10.4.5 - jasmine-core: 5.12.1 + glob: 10.5.0 + jasmine-core: 5.13.0 jasminewd2@2.2.0: {} jest-worker@27.5.1: dependencies: - '@types/node': 22.19.0 + '@types/node': 22.19.3 merge-stream: 2.0.0 supports-color: 8.1.1 jiti@2.6.1: {} + jose@6.1.3: {} + js-base64@3.7.8: {} js-tokens@4.0.0: {} js-tokens@9.0.1: {} - js-yaml@4.1.0: + js-yaml@4.1.1: dependencies: argparse: 2.0.1 jsbn@0.1.1: {} - jsdom@27.2.0(bufferutil@4.0.9)(utf-8-validate@6.0.5): + jsdom@27.3.0(bufferutil@4.0.9)(utf-8-validate@6.0.5): dependencies: - '@acemir/cssom': 0.9.23 - '@asamuzakjp/dom-selector': 6.7.4 - cssstyle: 5.3.3 + '@acemir/cssom': 0.9.29 + '@asamuzakjp/dom-selector': 6.7.6 + cssstyle: 5.3.5 data-urls: 6.0.0 decimal.js: 10.6.0 html-encoding-sniffer: 4.0.0 @@ -15887,12 +15647,14 @@ 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: {} json-schema-traverse@1.0.0: {} + json-schema-typed@8.0.2: {} + json-schema@0.4.0: {} json-stable-stringify-without-jsonify@1.0.1: {} @@ -15919,7 +15681,7 @@ snapshots: jsonwebtoken@9.0.2: dependencies: - jws: 3.2.2 + jws: 3.2.3 lodash.includes: 4.3.0 lodash.isboolean: 3.0.3 lodash.isinteger: 4.0.4 @@ -15963,12 +15725,12 @@ snapshots: ecdsa-sig-formatter: 1.0.11 safe-buffer: 5.2.1 - jws@3.2.2: + jws@3.2.3: dependencies: jwa: 1.4.2 safe-buffer: 5.2.1 - jws@4.0.0: + jws@4.0.1: dependencies: jwa: 2.0.1 safe-buffer: 5.2.1 @@ -15988,9 +15750,9 @@ snapshots: transitivePeerDependencies: - supports-color - 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)): + karma-jasmine-html-reporter@2.1.0(jasmine-core@5.13.0)(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.1 + jasmine-core: 5.13.0 karma: 6.4.4(bufferutil@4.0.9) karma-jasmine: 5.1.0(karma@6.4.4(bufferutil@4.0.9)) @@ -16006,7 +15768,7 @@ snapshots: karma@6.4.4(bufferutil@4.0.9): dependencies: '@colors/colors': 1.5.0 - body-parser: 1.20.3 + body-parser: 1.20.4 braces: 3.0.3 chokidar: 3.6.0 connect: 3.7.0 @@ -16104,11 +15866,11 @@ snapshots: picocolors: 1.1.1 shell-quote: 1.8.3 - less-loader@12.3.0(less@4.4.2)(webpack@5.102.1(esbuild@0.27.0)): + less-loader@12.3.0(less@4.4.2)(webpack@5.104.0(esbuild@0.27.1)): dependencies: less: 4.4.2 optionalDependencies: - webpack: 5.102.1(esbuild@0.27.0) + webpack: 5.104.0(esbuild@0.27.1) less@4.4.2: dependencies: @@ -16129,11 +15891,11 @@ snapshots: prelude-ls: 1.2.1 type-check: 0.4.0 - license-webpack-plugin@4.0.2(webpack@5.102.1(esbuild@0.27.0)): + license-webpack-plugin@4.0.2(webpack@5.104.0(esbuild@0.27.1)): dependencies: webpack-sources: 3.3.3 optionalDependencies: - webpack: 5.102.1(esbuild@0.27.0) + webpack: 5.104.0(esbuild@0.27.1) lie@3.3.0: dependencies: @@ -16161,7 +15923,7 @@ snapshots: lmdb@3.4.4: dependencies: - msgpackr: 1.11.5 + msgpackr: 1.11.8 node-addon-api: 6.1.0 node-gyp-build-optional-packages: 5.2.2 ordered-binary: 1.6.0 @@ -16238,7 +16000,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 @@ -16268,7 +16030,7 @@ snapshots: lru-cache@10.4.3: {} - lru-cache@11.2.2: {} + lru-cache@11.2.4: {} lru-cache@5.1.1: dependencies: @@ -16300,35 +16062,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.1.0 promise-retry: 2.0.1 - ssri: 12.0.0 + ssri: 13.0.0 transitivePeerDependencies: - supports-color @@ -16342,7 +16088,7 @@ snapshots: media-typer@1.1.0: {} - memfs@4.50.0: + memfs@4.51.1: dependencies: '@jsonjoy.com/json-pack': 1.21.0(tslib@2.8.1) '@jsonjoy.com/util': 1.9.0(tslib@2.8.1) @@ -16376,7 +16122,7 @@ snapshots: dependencies: mime-db: 1.52.0 - mime-types@3.0.1: + mime-types@3.0.2: dependencies: mime-db: 1.54.0 @@ -16394,11 +16140,11 @@ snapshots: mimic-response@3.1.0: {} - mini-css-extract-plugin@2.9.4(webpack@5.102.1(esbuild@0.27.0)): + mini-css-extract-plugin@2.9.4(webpack@5.104.0(esbuild@0.27.1)): dependencies: schema-utils: 4.3.3 tapable: 2.3.0 - webpack: 5.102.1(esbuild@0.27.0) + webpack: 5.104.0(esbuild@0.27.1) minimalistic-assert@1.0.1: {} @@ -16428,7 +16174,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 @@ -16490,7 +16236,7 @@ snapshots: '@msgpackr-extract/msgpackr-extract-win32-x64': 3.0.3 optional: true - msgpackr@1.11.5: + msgpackr@1.11.8: optionalDependencies: msgpackr-extract: 3.0.3 optional: true @@ -16508,6 +16254,8 @@ snapshots: mute-stream@2.0.0: {} + mute-stream@3.0.0: {} + nanocolors@0.2.13: {} nanoid@3.3.11: {} @@ -16517,7 +16265,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: {} @@ -16530,19 +16278,19 @@ snapshots: netmask@2.0.2: {} - ng-packagr@21.0.0-rc.1(@angular/compiler-cli@21.0.0-rc.2(@angular/compiler@21.0.0-rc.2)(typescript@5.9.3))(tslib@2.8.1)(typescript@5.9.3): + ng-packagr@21.1.0-next.0(@angular/compiler-cli@21.1.0-next.4(@angular/compiler@21.1.0-next.4)(typescript@5.9.3))(tslib@2.8.1)(typescript@5.9.3): dependencies: '@ampproject/remapping': 2.3.0 - '@angular/compiler-cli': 21.0.0-rc.2(@angular/compiler@21.0.0-rc.2)(typescript@5.9.3) - '@rollup/plugin-json': 6.1.0(rollup@4.53.2) - '@rollup/wasm-node': 4.52.5 + '@angular/compiler-cli': 21.1.0-next.4(@angular/compiler@21.1.0-next.4)(typescript@5.9.3) + '@rollup/plugin-json': 6.1.0(rollup@4.53.5) + '@rollup/wasm-node': 4.54.0 ajv: 8.17.1 ansi-colors: 4.1.3 - browserslist: 4.27.0 + browserslist: 4.28.1 chokidar: 4.0.3 commander: 14.0.2 dependency-graph: 1.0.0 - esbuild: 0.27.0 + esbuild: 0.27.1 find-cache-directory: 6.0.0 injection-js: 2.6.1 jsonc-parser: 3.3.1 @@ -16550,14 +16298,14 @@ snapshots: ora: 9.0.0 piscina: 5.1.4 postcss: 8.5.6 - rollup-plugin-dts: 6.2.3(rollup@4.53.2)(typescript@5.9.3) + rollup-plugin-dts: 6.3.0(rollup@4.53.5)(typescript@5.9.3) rxjs: 7.8.2 - sass: 1.94.0 + sass: 1.97.0 tinyglobby: 0.2.15 tslib: 2.8.1 typescript: 5.9.3 optionalDependencies: - rollup: 4.53.2 + rollup: 4.53.5 nock@14.0.10: dependencies: @@ -16593,7 +16341,7 @@ snapshots: fetch-blob: 3.2.0 formdata-polyfill: 4.0.10 - node-forge@1.3.1: {} + node-forge@1.3.3: {} node-gyp-build-optional-packages@5.2.2: dependencies: @@ -16602,74 +16350,70 @@ 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.1.0 semver: 7.7.3 tar: 7.5.2 tinyglobby: 0.2.15 - which: 5.0.0 + which: 6.0.0 transitivePeerDependencies: - supports-color 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: {} - normalize-range@0.1.2: {} - normalize-url@6.1.0: {} - npm-bundled@4.0.0: + npm-bundled@5.0.0: dependencies: - npm-normalize-package-bin: 4.0.0 + npm-normalize-package-bin: 5.0.0 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: + npm-package-arg@13.0.2: dependencies: hosted-git-info: 9.0.2 - proc-log: 5.0.0 + proc-log: 6.1.0 semver: 7.7.3 - validate-npm-package-name: 6.0.2 + validate-npm-package-name: 7.0.1 npm-packlist@10.0.3: dependencies: ignore-walk: 8.0.0 - proc-log: 6.0.0 + proc-log: 6.1.0 npm-pick-manifest@11.0.3: dependencies: npm-install-checks: 8.0.0 npm-normalize-package-bin: 5.0.0 - npm-package-arg: 13.0.1 + npm-package-arg: 13.0.2 semver: 7.7.3 - npm-registry-fetch@19.1.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 + npm-package-arg: 13.0.2 + proc-log: 6.1.0 transitivePeerDependencies: - supports-color @@ -16704,14 +16448,14 @@ snapshots: dependencies: call-bind: 1.0.8 define-properties: 1.2.1 - es-abstract: 1.24.0 + es-abstract: 1.24.1 es-object-atoms: 1.1.1 object.groupby@1.0.3: dependencies: call-bind: 1.0.8 define-properties: 1.2.1 - es-abstract: 1.24.0 + es-abstract: 1.24.1 object.values@1.2.1: dependencies: @@ -16722,6 +16466,8 @@ snapshots: obuf@1.1.2: {} + obug@2.1.1: {} + on-exit-leak-free@2.1.2: {} on-finished@2.3.0: @@ -16750,11 +16496,20 @@ 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 + open@11.0.0: + dependencies: + default-browser: 5.4.0 + define-lazy-prop: 3.0.0 + is-in-ssh: 1.0.0 + is-inside-container: 1.0.0 + powershell-utils: 0.1.0 + wsl-utils: 0.3.0 + open@8.4.2: dependencies: define-lazy-prop: 2.0.0 @@ -16823,7 +16578,7 @@ snapshots: dependencies: p-limit: 3.1.0 - p-map@7.0.3: {} + p-map@7.0.4: {} p-queue@6.6.2: dependencies: @@ -16862,24 +16617,24 @@ snapshots: package-json-from-dist@1.0.1: {} - pacote@21.0.3: + pacote@21.0.4: dependencies: - '@npmcli/git': 7.0.0 - '@npmcli/installed-package-contents': 3.0.0 - '@npmcli/package-json': 7.0.1 - '@npmcli/promise-spawn': 8.0.3 - '@npmcli/run-script': 10.0.2 - cacache: 20.0.1 + '@npmcli/git': 7.0.1 + '@npmcli/installed-package-contents': 4.0.0 + '@npmcli/package-json': 7.0.4 + '@npmcli/promise-spawn': 9.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-package-arg: 13.0.2 npm-packlist: 10.0.3 npm-pick-manifest: 11.0.3 - npm-registry-fetch: 19.1.0 - proc-log: 5.0.0 + npm-registry-fetch: 19.1.1 + proc-log: 6.1.0 promise-retry: 2.0.1 - sigstore: 4.0.0 - ssri: 12.0.0 + sigstore: 4.1.0 + ssri: 13.0.0 tar: 7.5.2 transitivePeerDependencies: - supports-color @@ -16934,9 +16689,9 @@ 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 + lru-cache: 11.2.4 minipass: 7.1.2 path-to-regexp@0.1.12: {} @@ -16989,8 +16744,9 @@ snapshots: pino-std-serializers@7.0.0: {} - pino@9.13.1: + pino@9.14.0: dependencies: + '@pinojs/redact': 0.4.0 atomic-sleep: 1.0.0 on-exit-leak-free: 2.1.2 pino-abstract-transport: 2.0.0 @@ -16999,7 +16755,6 @@ snapshots: quick-format-unescaped: 4.0.4 real-require: 0.2.0 safe-stable-stringify: 2.5.0 - slow-redact: 0.3.2 sonic-boom: 4.2.0 thread-stream: 3.1.0 @@ -17007,7 +16762,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: @@ -17029,14 +16784,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.27.0)): + postcss-loader@8.2.0(postcss@8.5.6)(typescript@5.9.3)(webpack@5.104.0(esbuild@0.27.1)): 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.27.0) + webpack: 5.104.0(esbuild@0.27.1) transitivePeerDependencies: - typescript @@ -17050,20 +16805,20 @@ snapshots: dependencies: icss-utils: 5.1.0(postcss@8.5.6) postcss: 8.5.6 - postcss-selector-parser: 7.1.0 + postcss-selector-parser: 7.1.1 postcss-value-parser: 4.2.0 postcss-modules-scope@3.2.1(postcss@8.5.6): dependencies: postcss: 8.5.6 - postcss-selector-parser: 7.1.0 + postcss-selector-parser: 7.1.1 postcss-modules-values@4.0.0(postcss@8.5.6): dependencies: icss-utils: 5.1.0(postcss@8.5.6) postcss: 8.5.6 - postcss-selector-parser@7.1.0: + postcss-selector-parser@7.1.1: dependencies: cssesc: 3.0.0 util-deprecate: 1.0.2 @@ -17076,13 +16831,13 @@ snapshots: picocolors: 1.1.1 source-map-js: 1.2.1 - prelude-ls@1.2.1: {} + powershell-utils@0.1.0: {} - prettier@3.6.2: {} + prelude-ls@1.2.1: {} - proc-log@5.0.0: {} + prettier@3.7.4: {} - proc-log@6.0.0: {} + proc-log@6.1.0: {} process-nextick-args@2.0.1: {} @@ -17117,7 +16872,7 @@ snapshots: '@protobufjs/path': 1.1.2 '@protobufjs/pool': 1.1.0 '@protobufjs/utf8': 1.1.0 - '@types/node': 22.19.0 + '@types/node': 22.19.3 long: 5.3.2 protractor@7.0.0: @@ -17205,14 +16960,14 @@ snapshots: - supports-color - utf-8-validate - puppeteer-core@24.27.0(bufferutil@4.0.9): + puppeteer-core@24.34.0(bufferutil@4.0.9): dependencies: - '@puppeteer/browsers': 2.10.12 - chromium-bidi: 10.5.1(devtools-protocol@0.0.1521046) + '@puppeteer/browsers': 2.11.0 + chromium-bidi: 12.0.1(devtools-protocol@0.0.1534754) debug: 4.4.3(supports-color@10.2.2) - devtools-protocol: 0.0.1521046 + devtools-protocol: 0.0.1534754 typed-query-selector: 2.12.0 - webdriver-bidi-protocol: 0.3.8 + webdriver-bidi-protocol: 0.3.10 ws: 8.18.3(bufferutil@4.0.9)(utf-8-validate@6.0.5) transitivePeerDependencies: - bare-abort-controller @@ -17269,7 +17024,7 @@ snapshots: unicode-properties: 1.4.1 urijs: 1.19.11 wordwrap: 1.0.0 - yaml: 2.8.1 + yaml: 2.8.2 transitivePeerDependencies: - encoding @@ -17286,11 +17041,18 @@ snapshots: iconv-lite: 0.4.24 unpipe: 1.0.0 - raw-body@3.0.1: + raw-body@2.5.3: dependencies: bytes: 3.1.2 - http-errors: 2.0.0 - iconv-lite: 0.7.0 + http-errors: 2.0.1 + iconv-lite: 0.4.24 + unpipe: 1.0.0 + + raw-body@3.0.2: + dependencies: + bytes: 3.1.2 + http-errors: 2.0.1 + iconv-lite: 0.7.1 unpipe: 1.0.0 readable-stream@2.3.8: @@ -17331,6 +17093,8 @@ snapshots: readdirp@4.1.2: {} + readdirp@5.0.0: {} + real-require@0.2.0: {} reflect-metadata@0.2.2: {} @@ -17339,7 +17103,7 @@ snapshots: dependencies: call-bind: 1.0.8 define-properties: 1.2.1 - es-abstract: 1.24.0 + es-abstract: 1.24.1 es-errors: 1.3.0 es-object-atoms: 1.1.1 get-intrinsic: 1.3.0 @@ -17480,27 +17244,26 @@ snapshots: rimraf@5.0.10: dependencies: - glob: 10.4.5 + glob: 10.5.0 - rolldown@1.0.0-beta.50: + rolldown@1.0.0-beta.54: dependencies: - '@oxc-project/types': 0.97.0 - '@rolldown/pluginutils': 1.0.0-beta.50 + '@oxc-project/types': 0.102.0 + '@rolldown/pluginutils': 1.0.0-beta.54 optionalDependencies: - '@rolldown/binding-android-arm64': 1.0.0-beta.50 - '@rolldown/binding-darwin-arm64': 1.0.0-beta.50 - '@rolldown/binding-darwin-x64': 1.0.0-beta.50 - '@rolldown/binding-freebsd-x64': 1.0.0-beta.50 - '@rolldown/binding-linux-arm-gnueabihf': 1.0.0-beta.50 - '@rolldown/binding-linux-arm64-gnu': 1.0.0-beta.50 - '@rolldown/binding-linux-arm64-musl': 1.0.0-beta.50 - '@rolldown/binding-linux-x64-gnu': 1.0.0-beta.50 - '@rolldown/binding-linux-x64-musl': 1.0.0-beta.50 - '@rolldown/binding-openharmony-arm64': 1.0.0-beta.50 - '@rolldown/binding-wasm32-wasi': 1.0.0-beta.50 - '@rolldown/binding-win32-arm64-msvc': 1.0.0-beta.50 - '@rolldown/binding-win32-ia32-msvc': 1.0.0-beta.50 - '@rolldown/binding-win32-x64-msvc': 1.0.0-beta.50 + '@rolldown/binding-android-arm64': 1.0.0-beta.54 + '@rolldown/binding-darwin-arm64': 1.0.0-beta.54 + '@rolldown/binding-darwin-x64': 1.0.0-beta.54 + '@rolldown/binding-freebsd-x64': 1.0.0-beta.54 + '@rolldown/binding-linux-arm-gnueabihf': 1.0.0-beta.54 + '@rolldown/binding-linux-arm64-gnu': 1.0.0-beta.54 + '@rolldown/binding-linux-arm64-musl': 1.0.0-beta.54 + '@rolldown/binding-linux-x64-gnu': 1.0.0-beta.54 + '@rolldown/binding-linux-x64-musl': 1.0.0-beta.54 + '@rolldown/binding-openharmony-arm64': 1.0.0-beta.54 + '@rolldown/binding-wasm32-wasi': 1.0.0-beta.54 + '@rolldown/binding-win32-arm64-msvc': 1.0.0-beta.54 + '@rolldown/binding-win32-x64-msvc': 1.0.0-beta.54 rollup-license-plugin@3.1.0: dependencies: @@ -17509,75 +17272,47 @@ snapshots: semver: 7.7.3 spdx-expression-validate: 2.0.0 - rollup-plugin-dts@6.2.3(rollup@4.53.2)(typescript@5.9.3): + rollup-plugin-dts@6.3.0(rollup@4.53.5)(typescript@5.9.3): dependencies: magic-string: 0.30.21 - rollup: 4.53.2 + rollup: 4.53.5 typescript: 5.9.3 optionalDependencies: '@babel/code-frame': 7.27.1 - rollup-plugin-sourcemaps2@0.5.4(@types/node@22.19.0)(rollup@4.53.2): - dependencies: - '@rollup/pluginutils': 5.2.0(rollup@4.53.2) - rollup: 4.53.2 - optionalDependencies: - '@types/node': 22.19.0 - - rollup@4.53.1: + rollup-plugin-sourcemaps2@0.5.4(@types/node@22.19.3)(rollup@4.53.5): dependencies: - '@types/estree': 1.0.8 + '@rollup/pluginutils': 5.2.0(rollup@4.53.5) + rollup: 4.53.5 optionalDependencies: - '@rollup/rollup-android-arm-eabi': 4.53.1 - '@rollup/rollup-android-arm64': 4.53.1 - '@rollup/rollup-darwin-arm64': 4.53.1 - '@rollup/rollup-darwin-x64': 4.53.1 - '@rollup/rollup-freebsd-arm64': 4.53.1 - '@rollup/rollup-freebsd-x64': 4.53.1 - '@rollup/rollup-linux-arm-gnueabihf': 4.53.1 - '@rollup/rollup-linux-arm-musleabihf': 4.53.1 - '@rollup/rollup-linux-arm64-gnu': 4.53.1 - '@rollup/rollup-linux-arm64-musl': 4.53.1 - '@rollup/rollup-linux-loong64-gnu': 4.53.1 - '@rollup/rollup-linux-ppc64-gnu': 4.53.1 - '@rollup/rollup-linux-riscv64-gnu': 4.53.1 - '@rollup/rollup-linux-riscv64-musl': 4.53.1 - '@rollup/rollup-linux-s390x-gnu': 4.53.1 - '@rollup/rollup-linux-x64-gnu': 4.53.1 - '@rollup/rollup-linux-x64-musl': 4.53.1 - '@rollup/rollup-openharmony-arm64': 4.53.1 - '@rollup/rollup-win32-arm64-msvc': 4.53.1 - '@rollup/rollup-win32-ia32-msvc': 4.53.1 - '@rollup/rollup-win32-x64-gnu': 4.53.1 - '@rollup/rollup-win32-x64-msvc': 4.53.1 - fsevents: 2.3.3 + '@types/node': 22.19.3 - rollup@4.53.2: + rollup@4.53.5: dependencies: '@types/estree': 1.0.8 optionalDependencies: - '@rollup/rollup-android-arm-eabi': 4.53.2 - '@rollup/rollup-android-arm64': 4.53.2 - '@rollup/rollup-darwin-arm64': 4.53.2 - '@rollup/rollup-darwin-x64': 4.53.2 - '@rollup/rollup-freebsd-arm64': 4.53.2 - '@rollup/rollup-freebsd-x64': 4.53.2 - '@rollup/rollup-linux-arm-gnueabihf': 4.53.2 - '@rollup/rollup-linux-arm-musleabihf': 4.53.2 - '@rollup/rollup-linux-arm64-gnu': 4.53.2 - '@rollup/rollup-linux-arm64-musl': 4.53.2 - '@rollup/rollup-linux-loong64-gnu': 4.53.2 - '@rollup/rollup-linux-ppc64-gnu': 4.53.2 - '@rollup/rollup-linux-riscv64-gnu': 4.53.2 - '@rollup/rollup-linux-riscv64-musl': 4.53.2 - '@rollup/rollup-linux-s390x-gnu': 4.53.2 - '@rollup/rollup-linux-x64-gnu': 4.53.2 - '@rollup/rollup-linux-x64-musl': 4.53.2 - '@rollup/rollup-openharmony-arm64': 4.53.2 - '@rollup/rollup-win32-arm64-msvc': 4.53.2 - '@rollup/rollup-win32-ia32-msvc': 4.53.2 - '@rollup/rollup-win32-x64-gnu': 4.53.2 - '@rollup/rollup-win32-x64-msvc': 4.53.2 + '@rollup/rollup-android-arm-eabi': 4.53.5 + '@rollup/rollup-android-arm64': 4.53.5 + '@rollup/rollup-darwin-arm64': 4.53.5 + '@rollup/rollup-darwin-x64': 4.53.5 + '@rollup/rollup-freebsd-arm64': 4.53.5 + '@rollup/rollup-freebsd-x64': 4.53.5 + '@rollup/rollup-linux-arm-gnueabihf': 4.53.5 + '@rollup/rollup-linux-arm-musleabihf': 4.53.5 + '@rollup/rollup-linux-arm64-gnu': 4.53.5 + '@rollup/rollup-linux-arm64-musl': 4.53.5 + '@rollup/rollup-linux-loong64-gnu': 4.53.5 + '@rollup/rollup-linux-ppc64-gnu': 4.53.5 + '@rollup/rollup-linux-riscv64-gnu': 4.53.5 + '@rollup/rollup-linux-riscv64-musl': 4.53.5 + '@rollup/rollup-linux-s390x-gnu': 4.53.5 + '@rollup/rollup-linux-x64-gnu': 4.53.5 + '@rollup/rollup-linux-x64-musl': 4.53.5 + '@rollup/rollup-openharmony-arm64': 4.53.5 + '@rollup/rollup-win32-arm64-msvc': 4.53.5 + '@rollup/rollup-win32-ia32-msvc': 4.53.5 + '@rollup/rollup-win32-x64-gnu': 4.53.5 + '@rollup/rollup-win32-x64-msvc': 4.53.5 fsevents: 2.3.3 router@2.2.0: @@ -17629,14 +17364,14 @@ snapshots: safer-buffer@2.1.2: {} - sass-loader@16.0.6(sass@1.94.0)(webpack@5.102.1(esbuild@0.27.0)): + sass-loader@16.0.6(sass@1.97.0)(webpack@5.104.0(esbuild@0.27.1)): dependencies: neo-async: 2.6.2 optionalDependencies: - sass: 1.94.0 - webpack: 5.102.1(esbuild@0.27.0) + sass: 1.97.0 + webpack: 5.104.0(esbuild@0.27.1) - sass@1.94.0: + sass@1.97.0: dependencies: chokidar: 4.0.3 immutable: 5.1.4 @@ -17650,7 +17385,7 @@ snapshots: transitivePeerDependencies: - supports-color - sax@1.4.1: {} + sax@1.4.3: {} saxes@6.0.0: dependencies: @@ -17675,7 +17410,7 @@ snapshots: selfsigned@2.4.1: dependencies: '@types/node-forge': 1.3.14 - node-forge: 1.3.1 + node-forge: 1.3.3 semver@5.7.2: {} @@ -17703,7 +17438,7 @@ snapshots: transitivePeerDependencies: - supports-color - send@0.19.1: + send@0.19.2: dependencies: debug: 2.6.9 depd: 2.0.0 @@ -17712,24 +17447,24 @@ snapshots: escape-html: 1.0.3 etag: 1.8.1 fresh: 0.5.2 - http-errors: 2.0.0 + http-errors: 2.0.1 mime: 1.6.0 ms: 2.1.3 on-finished: 2.4.1 range-parser: 1.2.1 - statuses: 2.0.1 + statuses: 2.0.2 transitivePeerDependencies: - supports-color - send@1.2.0: + send@1.2.1: dependencies: debug: 4.4.3(supports-color@10.2.2) encodeurl: 2.0.0 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 @@ -17762,12 +17497,21 @@ snapshots: transitivePeerDependencies: - supports-color - serve-static@2.2.0: + serve-static@1.16.3: + dependencies: + encodeurl: 2.0.0 + escape-html: 1.0.3 + parseurl: 1.3.3 + send: 0.19.2 + transitivePeerDependencies: + - supports-color + + serve-static@2.2.1: dependencies: encodeurl: 2.0.0 escape-html: 1.0.3 parseurl: 1.3.3 - send: 1.2.0 + send: 1.2.1 transitivePeerDependencies: - supports-color @@ -17849,14 +17593,14 @@ snapshots: signal-exit@4.1.0: {} - sigstore@4.0.0: + sigstore@4.1.0: dependencies: '@sigstore/bundle': 4.0.0 - '@sigstore/core': 3.0.0 + '@sigstore/core': 3.1.0 '@sigstore/protobuf-specs': 0.5.0 - '@sigstore/sign': 4.0.1 - '@sigstore/tuf': 4.0.0 - '@sigstore/verify': 3.0.0 + '@sigstore/sign': 4.1.0 + '@sigstore/tuf': 4.0.1 + '@sigstore/verify': 3.1.0 transitivePeerDependencies: - supports-color @@ -17873,8 +17617,6 @@ snapshots: ansi-styles: 6.2.3 is-fullwidth-code-point: 5.1.0 - slow-redact@0.3.2: {} - smart-buffer@4.2.0: {} socket.io-adapter@2.5.5(bufferutil@4.0.9)(utf-8-validate@6.0.5): @@ -17934,7 +17676,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: @@ -17947,11 +17689,11 @@ snapshots: source-map-js@1.2.1: {} - source-map-loader@5.0.0(webpack@5.102.1(esbuild@0.27.0)): + source-map-loader@5.0.0(webpack@5.104.0(esbuild@0.27.1)): dependencies: iconv-lite: 0.6.3 source-map-js: 1.2.1 - webpack: 5.102.1(esbuild@0.27.0) + webpack: 5.104.0(esbuild@0.27.1) source-map-support@0.4.18: dependencies: @@ -18034,7 +17776,7 @@ snapshots: dependencies: minipass: 7.1.2 - ssri@12.0.0: + ssri@13.0.0: dependencies: minipass: 7.1.2 @@ -18122,7 +17864,7 @@ snapshots: call-bound: 1.0.4 define-data-property: 1.1.4 define-properties: 1.2.1 - es-abstract: 1.24.0 + es-abstract: 1.24.1 es-object-atoms: 1.1.1 has-property-descriptors: 1.0.2 @@ -18202,7 +17944,7 @@ snapshots: pump: 3.0.3 tar-stream: 3.1.7 optionalDependencies: - bare-fs: 4.5.0 + bare-fs: 4.5.2 bare-path: 3.0.0 transitivePeerDependencies: - bare-abort-controller @@ -18243,16 +17985,16 @@ snapshots: transitivePeerDependencies: - supports-color - terser-webpack-plugin@5.3.14(esbuild@0.27.0)(webpack@5.102.1(esbuild@0.27.0)): + terser-webpack-plugin@5.3.16(esbuild@0.27.1)(webpack@5.104.0(esbuild@0.27.1)): 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.1 - webpack: 5.102.1(esbuild@0.27.0) + webpack: 5.104.0(esbuild@0.27.1) optionalDependencies: - esbuild: 0.27.0 + esbuild: 0.27.1 terser@5.44.1: dependencies: @@ -18292,7 +18034,7 @@ snapshots: tinybench@2.9.0: {} - tinyexec@0.3.2: {} + tinyexec@1.0.2: {} tinyglobby@0.2.15: dependencies: @@ -18303,15 +18045,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: @@ -18338,7 +18080,7 @@ snapshots: tough-cookie@6.0.0: dependencies: - tldts: 7.0.17 + tldts: 7.0.19 tr46@0.0.3: {} @@ -18360,14 +18102,14 @@ snapshots: dependencies: typescript: 5.9.3 - ts-node@10.9.2(@types/node@22.19.0)(typescript@5.9.3): + ts-node@10.9.2(@types/node@22.19.3)(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.19.0 + '@types/node': 22.19.3 acorn: 8.15.0 acorn-walk: 8.3.4 arg: 4.1.3 @@ -18389,18 +18131,18 @@ snapshots: tsscmp@1.0.6: {} - tsx@4.20.6: + tsx@4.21.0: dependencies: - esbuild: 0.25.12 + esbuild: 0.27.1 get-tsconfig: 4.13.0 optionalDependencies: fsevents: 2.3.3 - tuf-js@4.0.0: + tuf-js@4.1.0: dependencies: - '@tufjs/models': 4.0.0 + '@tufjs/models': 4.1.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 @@ -18429,7 +18171,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: @@ -18532,11 +18274,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 @@ -18550,9 +18292,9 @@ snapshots: unpipe@1.0.0: {} - update-browserslist-db@1.1.4(browserslist@4.27.0): + update-browserslist-db@1.2.3(browserslist@4.28.1): dependencies: - browserslist: 4.27.0 + browserslist: 4.28.1 escalade: 3.2.0 picocolors: 1.1.1 @@ -18587,16 +18329,16 @@ snapshots: spdx-correct: 3.2.0 spdx-expression-parse: 3.0.1 - validate-npm-package-name@6.0.2: {} + validate-npm-package-name@7.0.1: {} - validator@13.15.15: {} + validator@13.15.23: {} vary@1.1.2: {} - verdaccio-audit@13.0.0-next-8.24(encoding@0.1.13): + verdaccio-audit@13.0.0-next-8.28(encoding@0.1.13): dependencies: - '@verdaccio/config': 8.0.0-next-8.24 - '@verdaccio/core': 8.0.0-next-8.24 + '@verdaccio/config': 8.0.0-next-8.28 + '@verdaccio/core': 8.0.0-next-8.28 express: 4.21.2 https-proxy-agent: 5.0.1(supports-color@10.2.2) node-fetch: 2.6.7(encoding@0.1.13) @@ -18611,9 +18353,9 @@ snapshots: transitivePeerDependencies: - supports-color - verdaccio-htpasswd@13.0.0-next-8.24: + verdaccio-htpasswd@13.0.0-next-8.28: dependencies: - '@verdaccio/core': 8.0.0-next-8.24 + '@verdaccio/core': 8.0.0-next-8.28 '@verdaccio/file-locking': 13.0.0-next-8.6 apache-md5: 1.1.8 bcryptjs: 2.4.3 @@ -18623,24 +18365,24 @@ snapshots: transitivePeerDependencies: - supports-color - verdaccio@6.2.1(encoding@0.1.13): + verdaccio@6.2.4(encoding@0.1.13): dependencies: '@cypress/request': 3.0.9 - '@verdaccio/auth': 8.0.0-next-8.24 - '@verdaccio/config': 8.0.0-next-8.24 - '@verdaccio/core': 8.0.0-next-8.24 - '@verdaccio/hooks': 8.0.0-next-8.24 - '@verdaccio/loaders': 8.0.0-next-8.14 + '@verdaccio/auth': 8.0.0-next-8.28 + '@verdaccio/config': 8.0.0-next-8.28 + '@verdaccio/core': 8.0.0-next-8.28 + '@verdaccio/hooks': 8.0.0-next-8.28 + '@verdaccio/loaders': 8.0.0-next-8.18 '@verdaccio/local-storage-legacy': 11.1.1 - '@verdaccio/logger': 8.0.0-next-8.24 - '@verdaccio/middleware': 8.0.0-next-8.24 + '@verdaccio/logger': 8.0.0-next-8.28 + '@verdaccio/middleware': 8.0.0-next-8.28 '@verdaccio/search-indexer': 8.0.0-next-8.5 - '@verdaccio/signature': 8.0.0-next-8.16 + '@verdaccio/signature': 8.0.0-next-8.20 '@verdaccio/streams': 10.2.1 - '@verdaccio/tarball': 13.0.0-next-8.24 - '@verdaccio/ui-theme': 8.0.0-next-8.24 - '@verdaccio/url': 13.0.0-next-8.24 - '@verdaccio/utils': 8.1.0-next-8.24 + '@verdaccio/tarball': 13.0.0-next-8.28 + '@verdaccio/ui-theme': 8.0.0-next-8.28 + '@verdaccio/url': 13.0.0-next-8.28 + '@verdaccio/utils': 8.1.0-next-8.28 JSONStream: 1.3.5 async: 3.2.6 clipanion: 4.0.0-rc.4 @@ -18652,9 +18394,9 @@ snapshots: lodash: 4.17.21 lru-cache: 7.18.3 mime: 3.0.0 - semver: 7.7.2 - verdaccio-audit: 13.0.0-next-8.24(encoding@0.1.13) - verdaccio-htpasswd: 13.0.0-next-8.24 + semver: 7.7.3 + verdaccio-audit: 13.0.0-next-8.28(encoding@0.1.13) + verdaccio-htpasswd: 13.0.0-next-8.28 transitivePeerDependencies: - bare-abort-controller - encoding @@ -18667,49 +18409,50 @@ snapshots: core-util-is: 1.0.2 extsprintf: 1.3.0 - vite@7.2.2(@types/node@24.10.0)(jiti@2.6.1)(less@4.4.2)(sass@1.94.0)(terser@5.44.1)(tsx@4.20.6)(yaml@2.8.1): + vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(less@4.4.2)(sass@1.97.0)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2): dependencies: - esbuild: 0.25.12 + esbuild: 0.27.1 fdir: 6.5.0(picomatch@4.0.3) picomatch: 4.0.3 postcss: 8.5.6 - rollup: 4.53.1 + rollup: 4.53.5 tinyglobby: 0.2.15 optionalDependencies: - '@types/node': 24.10.0 + '@types/node': 24.10.4 fsevents: 2.3.3 jiti: 2.6.1 less: 4.4.2 - sass: 1.94.0 + sass: 1.97.0 terser: 5.44.1 - tsx: 4.20.6 - yaml: 2.8.1 - - vitest@4.0.8(@types/node@24.10.0)(jiti@2.6.1)(jsdom@27.2.0(bufferutil@4.0.9)(utf-8-validate@6.0.5))(less@4.4.2)(sass@1.94.0)(terser@5.44.1)(tsx@4.20.6)(yaml@2.8.1): - dependencies: - '@vitest/expect': 4.0.8 - '@vitest/mocker': 4.0.8(vite@7.2.2(@types/node@24.10.0)(jiti@2.6.1)(less@4.4.2)(sass@1.94.0)(terser@5.44.1)(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) + tsx: 4.21.0 + yaml: 2.8.2 + + vitest@4.0.15(@opentelemetry/api@1.9.0)(@types/node@24.10.4)(jiti@2.6.1)(jsdom@27.3.0(bufferutil@4.0.9)(utf-8-validate@6.0.5))(less@4.4.2)(sass@1.97.0)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2): + dependencies: + '@vitest/expect': 4.0.15 + '@vitest/mocker': 4.0.15(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(less@4.4.2)(sass@1.97.0)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)) + '@vitest/pretty-format': 4.0.15 + '@vitest/runner': 4.0.15 + '@vitest/snapshot': 4.0.15 + '@vitest/spy': 4.0.15 + '@vitest/utils': 4.0.15 es-module-lexer: 1.7.0 - expect-type: 1.2.2 + expect-type: 1.3.0 magic-string: 0.30.21 + obug: 2.1.1 pathe: 2.0.3 picomatch: 4.0.3 std-env: 3.10.0 tinybench: 2.9.0 - tinyexec: 0.3.2 + tinyexec: 1.0.2 tinyglobby: 0.2.15 tinyrainbow: 3.0.3 - vite: 7.2.2(@types/node@24.10.0)(jiti@2.6.1)(less@4.4.2)(sass@1.94.0)(terser@5.44.1)(tsx@4.20.6)(yaml@2.8.1) + vite: 7.3.0(@types/node@24.10.4)(jiti@2.6.1)(less@4.4.2)(sass@1.97.0)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2) why-is-node-running: 2.3.0 optionalDependencies: - '@types/node': 24.10.0 - jsdom: 27.2.0(bufferutil@4.0.9)(utf-8-validate@6.0.5) + '@opentelemetry/api': 1.9.0 + '@types/node': 24.10.4 + jsdom: 27.3.0(bufferutil@4.0.9)(utf-8-validate@6.0.5) transitivePeerDependencies: - jiti - less @@ -18719,7 +18462,6 @@ snapshots: - sass-embedded - stylus - sugarss - - supports-color - terser - tsx - yaml @@ -18746,7 +18488,7 @@ snapshots: web-vitals@4.2.4: {} - webdriver-bidi-protocol@0.3.8: {} + webdriver-bidi-protocol@0.3.10: {} webdriver-js-extender@2.1.0: dependencies: @@ -18773,18 +18515,18 @@ snapshots: webidl-conversions@8.0.0: {} - webpack-dev-middleware@7.4.5(webpack@5.102.1(esbuild@0.27.0)): + webpack-dev-middleware@7.4.5(webpack@5.104.0(esbuild@0.27.1)): dependencies: colorette: 2.0.20 - memfs: 4.50.0 - mime-types: 3.0.1 + memfs: 4.51.1 + 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.27.0) + webpack: 5.104.0(esbuild@0.27.1) - webpack-dev-server@5.2.2(bufferutil@4.0.9)(utf-8-validate@6.0.5)(webpack@5.102.1(esbuild@0.27.0)): + webpack-dev-server@5.2.2(bufferutil@4.0.9)(utf-8-validate@6.0.5)(webpack@5.104.0(esbuild@0.27.1)): dependencies: '@types/bonjour': 3.5.13 '@types/connect-history-api-fallback': 1.5.4 @@ -18800,10 +18542,10 @@ snapshots: colorette: 2.0.20 compression: 1.8.1 connect-history-api-fallback: 2.0.0 - express: 4.21.2 + express: 4.22.1 graceful-fs: 4.2.11 http-proxy-middleware: 2.0.9(@types/express@4.17.25) - ipaddr.js: 2.2.0 + ipaddr.js: 2.3.0 launch-editor: 2.12.0 open: 10.2.0 p-retry: 6.2.1 @@ -18812,10 +18554,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.27.0)) + webpack-dev-middleware: 7.4.5(webpack@5.104.0(esbuild@0.27.1)) ws: 8.18.3(bufferutil@4.0.9)(utf-8-validate@6.0.5) optionalDependencies: - webpack: 5.102.1(esbuild@0.27.0) + webpack: 5.104.0(esbuild@0.27.1) transitivePeerDependencies: - bufferutil - debug @@ -18830,12 +18572,12 @@ snapshots: webpack-sources@3.3.3: {} - webpack-subresource-integrity@5.1.0(webpack@5.102.1(esbuild@0.27.0)): + webpack-subresource-integrity@5.1.0(webpack@5.104.0(esbuild@0.27.1)): dependencies: typed-assert: 1.0.9 - webpack: 5.102.1(esbuild@0.27.0) + webpack: 5.104.0(esbuild@0.27.1) - webpack@5.102.1(esbuild@0.27.0): + webpack@5.104.0(esbuild@0.27.1): dependencies: '@types/eslint-scope': 3.7.7 '@types/estree': 1.0.8 @@ -18845,10 +18587,10 @@ snapshots: '@webassemblyjs/wasm-parser': 1.14.1 acorn: 8.15.0 acorn-import-phases: 1.0.4(acorn@8.15.0) - browserslist: 4.27.0 + browserslist: 4.28.1 chrome-trace-event: 1.0.4 - enhanced-resolve: 5.18.3 - es-module-lexer: 1.7.0 + enhanced-resolve: 5.18.4 + es-module-lexer: 2.0.0 eslint-scope: 5.1.1 events: 3.3.0 glob-to-regexp: 0.4.1 @@ -18859,7 +18601,7 @@ snapshots: neo-async: 2.6.2 schema-utils: 4.3.3 tapable: 2.3.0 - terser-webpack-plugin: 5.3.14(esbuild@0.27.0)(webpack@5.102.1(esbuild@0.27.0)) + terser-webpack-plugin: 5.3.16(esbuild@0.27.1)(webpack@5.104.0(esbuild@0.27.1)) watchpack: 2.4.4 webpack-sources: 3.3.3 transitivePeerDependencies: @@ -18947,10 +18689,6 @@ snapshots: dependencies: isexe: 2.0.0 - which@5.0.0: - dependencies: - isexe: 3.1.1 - which@6.0.0: dependencies: isexe: 3.1.1 @@ -19016,13 +18754,18 @@ snapshots: dependencies: is-wsl: 3.1.0 + wsl-utils@0.3.0: + dependencies: + is-wsl: 3.1.0 + powershell-utils: 0.1.0 + xhr2@0.2.1: {} xml-name-validator@5.0.0: {} xml2js@0.4.23: dependencies: - sax: 1.4.1 + sax: 1.4.3 xmlbuilder: 11.0.1 xmlbuilder@11.0.1: {} @@ -19043,7 +18786,7 @@ snapshots: yallist@5.0.0: {} - yaml@2.8.1: {} + yaml@2.8.2: {} yargs-parser@18.1.3: dependencies: @@ -19114,12 +18857,12 @@ snapshots: yoctocolors@2.1.2: {} - zod-to-json-schema@3.24.6(zod@3.25.76): + zod-to-json-schema@3.25.0(zod@4.2.1): dependencies: - zod: 3.25.76 + zod: 4.2.1 zod@3.25.76: {} - zod@4.1.12: {} + zod@4.2.1: {} - zone.js@0.15.1: {} + zone.js@0.16.0: {} diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index d1752e85bda6..ecfbec6f3cb9 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -15,7 +15,6 @@ packages: - packages/ngtools/webpack - modules/testing/builder - tests - - tools/baseline_browserslist # The minimum age of a release to be considered for dependency installation. # The value is in minutes (1440 minutes = 1 day). minimumReleaseAge: 1440 diff --git a/renovate.json b/renovate.json index d1885952a2bd..3b2c5ab2f6a6 100644 --- a/renovate.json +++ b/renovate.json @@ -1,25 +1,14 @@ { "$schema": "https://docs.renovatebot.com/renovate-schema.json", - "baseBranchPatterns": [ - "main", - "21.0.x" - ], - "extends": [ - "github>angular/dev-infra//renovate-presets/default.json5" - ], - "ignorePaths": [ - "tests/legacy-cli/e2e/assets/**", - "tests/schematics/update/packages/**" - ], + "baseBranchPatterns": ["main", "21.0.x"], + "extends": ["github>angular/dev-infra//renovate-presets/default.json5"], + "ignoreDeps": ["less"], + "ignorePaths": ["tests/e2e/assets/**", "tests/schematics/update/packages/**"], "packageRules": [ { "enabled": false, - "matchFileNames": [ - "tests/legacy-cli/e2e/ng-snapshot/package.json" - ], - "matchBaseBranches": [ - "!main" - ] + "matchFileNames": ["tests/e2e/ng-snapshot/package.json"], + "matchBaseBranches": ["!main"] }, { "matchFileNames": [ @@ -27,13 +16,11 @@ "packages/angular_devkit/schematics_cli/schematic/files/package.json", "packages/schematics/angular/utility/latest-versions/package.json" ], - "matchPackageNames": [ - "*" - ], + "matchPackageNames": ["*"], "groupName": "schematics dependencies", "lockFileMaintenance": { "enabled": false } } ] -} \ No newline at end of file +} diff --git a/scripts/build-packages-dist.mts b/scripts/build-packages-dist.mts index 9a4fa1c764b9..2a424d68aea7 100644 --- a/scripts/build-packages-dist.mts +++ b/scripts/build-packages-dist.mts @@ -12,7 +12,7 @@ * distribution folder within the project. */ -import { BuiltPackage } from '@angular/ng-dev'; +import type { BuiltPackage } from '@angular/ng-dev'; import { execSync } from 'node:child_process'; import { chmodSync, @@ -75,7 +75,7 @@ function buildReleasePackages( // List of targets to build. e.g. "packages/angular/cli:npm_package" const targets = exec(queryPackagesCmd, true).split(/\r?\n/); const packageNames = getPackageNamesOfTargets(targets); - const bazelBinPath = exec(`${bazelCmd} info bazel-bin`, true); + const bazelBinPath = join(import.meta.dirname, '../dist/bin'); const getBazelOutputPath = (pkgName: string) => join(bazelBinPath, 'packages', pkgName, 'npm_package'); const getDistPath = (pkgName: string) => join(distPath, pkgName); 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/scripts/build.mts b/scripts/build.mts index b78df0d6b904..9f100b513352 100644 --- a/scripts/build.mts +++ b/scripts/build.mts @@ -122,8 +122,7 @@ export default async function ( argv: { local?: boolean; snapshot?: boolean } = {}, ): Promise<{ name: string; outputPath: string; tarPath: string }[]> { const logger = globalThis.console; - - const bazelBin = await _exec(`${bazelCmd} info bazel-bin`, true, logger); + const bazelBin = join(import.meta.dirname, '../dist/bin'); await _clean(logger); diff --git a/scripts/circular-deps-test.conf.mjs b/scripts/circular-deps-test.conf.mjs index 790be477858d..1505630371b0 100644 --- a/scripts/circular-deps-test.conf.mjs +++ b/scripts/circular-deps-test.conf.mjs @@ -9,7 +9,7 @@ import { statSync } from 'node:fs'; import { join } from 'node:path'; -import { packages } from './packages.mjs'; +import { packages } from './packages.mts'; export const baseDir = '../'; export const goldenFile = '../goldens/circular-deps/packages.json'; diff --git a/scripts/create.mts b/scripts/create.mts index 9ed02a64b316..56e125da46f2 100644 --- a/scripts/create.mts +++ b/scripts/create.mts @@ -11,8 +11,8 @@ import * as child_process from 'node:child_process'; import { copyFile, readFile, rm, writeFile } from 'node:fs/promises'; import * as path from 'node:path'; import { pathToFileURL } from 'node:url'; -import build from './build.mjs'; -import { packages } from './packages.mjs'; +import build from './build.mts'; +import { packages } from './packages.mts'; export interface CreateOptions extends Record { _: string[]; diff --git a/scripts/devkit-admin.mts b/scripts/devkit-admin.mts index 3240759f3b54..0a17df9f45a1 100644 --- a/scripts/devkit-admin.mts +++ b/scripts/devkit-admin.mts @@ -7,17 +7,25 @@ * found in the LICENSE file at https://angular.dev/license */ -import colors from 'ansi-colors'; import path from 'node:path'; -import yargsParser from 'yargs-parser'; +import { parseArgs, styleText } from 'node:util'; -const args = yargsParser(process.argv.slice(2), { - boolean: ['verbose'], - configuration: { - 'camel-case-expansion': false, +const { values, positionals } = parseArgs({ + args: process.argv.slice(2), + options: { + verbose: { + type: 'boolean', + }, }, + allowPositionals: true, + strict: false, // Allow unknown options to pass through. }); -const scriptName = args._.shift(); + +const scriptName = positionals.shift(); +const args = { + ...values, + _: positionals, +}; const cwd = process.cwd(); const scriptDir = import.meta.dirname; @@ -26,15 +34,15 @@ process.chdir(path.join(scriptDir, '..')); const originalConsole = { ...console }; console.warn = function (...args) { const [m, ...rest] = args; - originalConsole.warn(colors.yellow(m), ...rest); + originalConsole.warn(styleText(['yellow'], m), ...rest); }; console.error = function (...args) { const [m, ...rest] = args; - originalConsole.error(colors.red(m), ...rest); + originalConsole.error(styleText(['red'], m), ...rest); }; try { - const script = await import(`./${scriptName}.mjs`); + const script = await import(`./${scriptName}.mts`); const exitCode = await script.default(args, cwd); process.exitCode = typeof exitCode === 'number' ? exitCode : 0; // eslint-disable-next-line @typescript-eslint/no-explicit-any diff --git a/scripts/diff-release-package.mts b/scripts/diff-release-package.mts index 8ec6b6df48db..ae2d4ec2e87b 100644 --- a/scripts/diff-release-package.mts +++ b/scripts/diff-release-package.mts @@ -62,17 +62,7 @@ async function main(packageName: string) { git.run(['clone', `https://github.com/${snapshotRepoName}.git`, tmpDir]); console.log(`--> Cloned snapshot repo.`); - const bazelBinDir = childProcess - .spawnSync(`${bazel} info bazel-bin`, { - shell: true, - encoding: 'utf8', - stdio: ['pipe', 'pipe', 'inherit'], - }) - .stdout.trim(); - if (bazelBinDir === '') { - throw new Error('Could not determine bazel-bin directory.'); - } - + const bazelBinDir = join(import.meta.dirname, '../dist/bin'); const outputPath = path.join(bazelBinDir, 'packages/', targetDir, 'npm_package'); // Delete old directory to avoid surprises, or stamping being outdated. diff --git a/scripts/json-help.mts b/scripts/json-help.mts index 34271a8925bc..739c79463484 100644 --- a/scripts/json-help.mts +++ b/scripts/json-help.mts @@ -10,7 +10,7 @@ import { spawnSync } from 'node:child_process'; import { promises as fs } from 'node:fs'; import * as os from 'node:os'; import * as path from 'node:path'; -import create from './create.mjs'; +import create from './create.mts'; const __dirname = import.meta.dirname; diff --git a/scripts/release-checks/dependency-ranges/index.mts b/scripts/release-checks/dependency-ranges/index.mts index 9f507a8b4425..cc163fbd7377 100644 --- a/scripts/release-checks/dependency-ranges/index.mts +++ b/scripts/release-checks/dependency-ranges/index.mts @@ -8,8 +8,8 @@ import { Log, ReleasePrecheckError, bold } from '@angular/ng-dev'; import semver from 'semver'; -import { checkSchematicsAngularLatestVersion } from './latest-versions-check.mjs'; -import { PackageJson, checkPeerDependencies } from './peer-deps-check.mjs'; +import { checkSchematicsAngularLatestVersion } from './latest-versions-check.mts'; +import { type PackageJson, checkPeerDependencies } from './peer-deps-check.mts'; /** Environment variable that can be used to skip this pre-check. */ const skipEnvVar = 'SKIP_DEPENDENCY_RANGE_PRECHECK'; diff --git a/scripts/snapshots.mts b/scripts/snapshots.mts index 1e7a751aeab2..5ab1e161492b 100644 --- a/scripts/snapshots.mts +++ b/scripts/snapshots.mts @@ -10,9 +10,9 @@ import { execSync, spawnSync } from 'node:child_process'; import * as fs from 'node:fs'; import * as os from 'node:os'; import * as path from 'node:path'; -import build from './build.mjs'; -import jsonHelp, { createTemporaryProject } from './json-help.mjs'; -import { PackageInfo, packages } from './packages.mjs'; +import build from './build.mts'; +import jsonHelp, { createTemporaryProject } from './json-help.mts'; +import { type PackageInfo, packages } from './packages.mts'; const __dirname = import.meta.dirname; diff --git a/scripts/templates.mts b/scripts/templates.mts index 06623a193a4d..c36ec8aea364 100644 --- a/scripts/templates.mts +++ b/scripts/templates.mts @@ -7,40 +7,42 @@ */ import lodash from 'lodash'; -import * as fs from 'node:fs'; +import { readFile, writeFile } from 'node:fs/promises'; import * as path from 'node:path'; -import { releasePackages } from './packages.mjs'; +import { releasePackages } from './packages.mts'; const __dirname = import.meta.dirname; -async function _runTemplate(inputPath: string, outputPath: string) { +async function runTemplate(inputPath: string, outputPath: string) { inputPath = path.resolve(__dirname, inputPath); outputPath = path.resolve(__dirname, outputPath); console.info(`Building ${path.relative(path.dirname(__dirname), outputPath)}...`); - // TODO(ESM): Consider making this an actual import statement. - const { COMMIT_TYPES, ScopeRequirement } = await new Function( - `return import('@angular/ng-dev');`, - )(); + const { COMMIT_TYPES, ScopeRequirement } = await import('@angular/ng-dev'); - const monorepo = JSON.parse(fs.readFileSync('./.monorepo.json', 'utf-8')); - const content = lodash.template(fs.readFileSync(inputPath, 'utf-8'))({ + const [monorepoRaw, templateContent] = await Promise.all([ + readFile('./.monorepo.json', 'utf-8'), + readFile(inputPath, 'utf-8'), + ]); + + const monorepo = JSON.parse(monorepoRaw); + const content = lodash.template(templateContent)({ monorepo, packages: releasePackages.map(({ name }) => name), - encode: (x: string) => global.encodeURIComponent(x), + encode: (x: string) => encodeURIComponent(x), // Pass-through `ng-dev` ESM commit message information for the `contributing.ejs` // template. EJS templates using the devkit template cannot use ESM. COMMIT_TYPES: COMMIT_TYPES, ScopeRequirement: ScopeRequirement, }); - fs.writeFileSync(outputPath, content, 'utf-8'); + await writeFile(outputPath, content, 'utf-8'); } -export default async function (_options: {}): Promise { +export default async function (): Promise { await Promise.all([ - _runTemplate('./templates/readme.ejs', '../README.md'), - _runTemplate('./templates/contributing.ejs', '../CONTRIBUTING.md'), + runTemplate('./templates/readme.ejs', '../README.md'), + runTemplate('./templates/contributing.ejs', '../CONTRIBUTING.md'), ]); return 0; diff --git a/scripts/tsconfig.json b/scripts/tsconfig.json index 2a26627bc905..4b4f57994123 100644 --- a/scripts/tsconfig.json +++ b/scripts/tsconfig.json @@ -3,6 +3,9 @@ "compilerOptions": { "module": "Node16", "moduleResolution": "Node16", + "erasableSyntaxOnly": true, + "verbatimModuleSyntax": true, + "rewriteRelativeImportExtensions": true, "noEmit": true, "types": [] }, diff --git a/scripts/validate-user-analytics.mts b/scripts/validate-user-analytics.mts index 521e17bbf924..d66d84dee809 100644 --- a/scripts/validate-user-analytics.mts +++ b/scripts/validate-user-analytics.mts @@ -15,7 +15,7 @@ import { EventCustomDimension, EventCustomMetric, UserCustomDimension, -} from '../packages/angular/cli/src/analytics/analytics-parameters.mjs'; +} from '../packages/angular/cli/src/analytics/analytics-parameters.ts'; const __dirname = import.meta.dirname; const userAnalyticsTable = lodash.template( @@ -82,7 +82,7 @@ async function _checkDimensions(dimensionsTable: string) { }; // Find all the schemas - const { packages } = await import('./packages.mjs'); + const { packages } = await import('./packages.mts'); const packagesPaths = packages.map(({ root }) => root); for (const packagePath of packagesPaths) { const schemasPaths = await glob('**/schema.json', { cwd: packagePath }); diff --git a/scripts/validate.mts b/scripts/validate.mts index 1de28c3e5de2..70d0f27a31d1 100644 --- a/scripts/validate.mts +++ b/scripts/validate.mts @@ -7,8 +7,8 @@ */ import { execSync } from 'node:child_process'; -import templates from './templates.mjs'; -import validateUserAnalytics from './validate-user-analytics.mjs'; +import templates from './templates.mts'; +import validateUserAnalytics from './validate-user-analytics.mts'; export default async function (options: { verbose: boolean }) { let error = false; @@ -24,7 +24,7 @@ export default async function (options: { verbose: boolean }) { } console.info('Running templates validation...'); - await templates({}); + await templates(); if (execSync(`git status --porcelain`).toString()) { console.error( 'Running templates updated files... Please run "devkit-admin templates" before submitting a PR.', diff --git a/tests/BUILD.bazel b/tests/BUILD.bazel index 2c0b57b1fe3e..318bf3b965e4 100644 --- a/tests/BUILD.bazel +++ b/tests/BUILD.bazel @@ -1,11 +1,79 @@ +load("@aspect_bazel_lib//lib:directory_path.bzl", "directory_path") load("@npm//:defs.bzl", "npm_link_all_packages") +load("@npm//:rollup/package_json.bzl", rollup = "bin") +load("//tools:defaults.bzl", "ts_project") +load(":e2e.bzl", "e2e_suites") -# Copyright Google Inc. 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 package(default_visibility = ["//visibility:public"]) licenses(["notice"]) npm_link_all_packages() + +ts_project( + name = "runner", + testonly = True, + srcs = [ + "e2e_runner.ts", + ], + deps = [ + "//:node_modules/@types/node", + "//:node_modules/fast-glob", + "//packages/angular_devkit/core", + "//packages/angular_devkit/core/node", + "//tests/e2e/utils", + ], +) + +rollup.rollup( + name = "runner_bundled", + testonly = True, + srcs = [ + "rollup.config.mjs", + ":runner", + "//:node_modules/@rollup/plugin-alias", + "//:node_modules/@rollup/plugin-commonjs", + "//:node_modules/@rollup/plugin-json", + "//:node_modules/@rollup/plugin-node-resolve", + "//:node_modules/fast-glob", + "//tests/e2e/initialize", + "//tests/e2e/ng-snapshot", + "//tests/e2e/setup", + "//tests/e2e/tests", + ], + args = [ + "--format=cjs", + "--config=./rollup.config.mjs", + ], + chdir = package_name(), + out_dirs = ["runner_bundled_out"], + progress_message = "Bundling e2e test runner", +) + +directory_path( + name = "runner_entrypoint", + testonly = True, + directory = ":runner_bundled", + path = "./e2e_runner.js", +) + +e2e_suites( + name = "e2e", + data = [ + ":runner_bundled", + "verdaccio.yaml", + "verdaccio_auth.yaml", + + # Dynamically loaded. + "//tests/e2e/assets", + "//:node_modules/verdaccio", + "//:node_modules/verdaccio-auth-memory", + + # Extra runtime deps due to bundling issues. + # TODO: Clean this up. + "//:node_modules/express", + "//:node_modules/undici", + "//:node_modules/puppeteer", + ], + runner = ":runner_entrypoint", +) diff --git a/tests/README.md b/tests/README.md deleted file mode 100644 index e507add2cea7..000000000000 --- a/tests/README.md +++ /dev/null @@ -1,11 +0,0 @@ -# `/tests` Folder - -Contains all e2e tests and test assets. - -## `legacy-cli/` - -Contains all assets and all e2e tests from the legacy CLI repo. - -## Others - -Other folders contain test assets related to the Package namespace folders matching their name. diff --git a/tests/legacy-cli/e2e.bzl b/tests/e2e.bzl similarity index 97% rename from tests/legacy-cli/e2e.bzl rename to tests/e2e.bzl index 62a89ee6a76f..07004fcb09d0 100644 --- a/tests/legacy-cli/e2e.bzl +++ b/tests/e2e.bzl @@ -83,11 +83,14 @@ def e2e_suites(name, runner, data): # Default target meant to be run manually for debugging, customizing test cli via bazel _e2e_tests(name + "_" + toolchain_name, runner, data = data, toolchain = toolchain, tags = ["manual"]) - _e2e_suite(name, runner, "npm", data, toolchain_name, toolchain) + # Main test suites + _e2e_suite(name, runner, "webpack", data, toolchain_name, toolchain) + _e2e_suite(name, runner, "esbuild", data, toolchain_name, toolchain) + + # Package manager subsets _e2e_suite(name, runner, "bun", data, toolchain_name, toolchain) _e2e_suite(name, runner, "pnpm", data, toolchain_name, toolchain) _e2e_suite(name, runner, "yarn", data, toolchain_name, toolchain) - _e2e_suite(name, runner, "esbuild", data, toolchain_name, toolchain) # Saucelabs tests are only run on the default toolchain _e2e_suite(name, runner, "saucelabs", data) @@ -162,7 +165,7 @@ def _e2e_suite(name, runner, type, data, toolchain_name = "", toolchain = None): args.append("--esbuild") tests = BROWSER_TESTS ignore = None - elif type == "npm": + elif type == "webpack": tests = None ignore = BROWSER_TESTS + WEBPACK_IGNORE_TESTS diff --git a/tests/legacy-cli/e2e/assets/18.0-project/.editorconfig b/tests/e2e/assets/19.0-project/.editorconfig similarity index 100% rename from tests/legacy-cli/e2e/assets/18.0-project/.editorconfig rename to tests/e2e/assets/19.0-project/.editorconfig diff --git a/tests/legacy-cli/e2e/assets/18.0-project/.gitignore b/tests/e2e/assets/19.0-project/.gitignore similarity index 100% rename from tests/legacy-cli/e2e/assets/18.0-project/.gitignore rename to tests/e2e/assets/19.0-project/.gitignore diff --git a/tests/e2e/assets/19.0-project/README.md b/tests/e2e/assets/19.0-project/README.md new file mode 100644 index 000000000000..80d80f5a3f1f --- /dev/null +++ b/tests/e2e/assets/19.0-project/README.md @@ -0,0 +1,59 @@ +# NineteenProject + +This project was generated using [Angular CLI](https://github.com/angular/angular-cli) version 19.2.13. + +## Development server + +To start a local development server, run: + +```bash +ng serve +``` + +Once the server is running, open your browser and navigate to `http://localhost:4200/`. The application will automatically reload whenever you modify any of the source files. + +## Code scaffolding + +Angular CLI includes powerful code scaffolding tools. To generate a new component, run: + +```bash +ng generate component component-name +``` + +For a complete list of available schematics (such as `components`, `directives`, or `pipes`), run: + +```bash +ng generate --help +``` + +## Building + +To build the project run: + +```bash +ng build +``` + +This will compile your project and store the build artifacts in the `dist/` directory. By default, the production build optimizes your application for performance and speed. + +## Running unit tests + +To execute unit tests with the [Karma](https://karma-runner.github.io) test runner, use the following command: + +```bash +ng test +``` + +## Running end-to-end tests + +For end-to-end (e2e) testing, run: + +```bash +ng e2e +``` + +Angular CLI does not come with an end-to-end testing framework by default. You can choose one that suits your needs. + +## Additional Resources + +For more information on using the Angular CLI, including detailed command references, visit the [Angular CLI Overview and Command Reference](https://angular.dev/tools/cli) page. diff --git a/tests/legacy-cli/e2e/assets/18.0-project/angular.json b/tests/e2e/assets/19.0-project/angular.json similarity index 77% rename from tests/legacy-cli/e2e/assets/18.0-project/angular.json rename to tests/e2e/assets/19.0-project/angular.json index 70c98c792416..b435223e9930 100644 --- a/tests/legacy-cli/e2e/assets/18.0-project/angular.json +++ b/tests/e2e/assets/19.0-project/angular.json @@ -3,7 +3,7 @@ "version": 1, "newProjectRoot": "projects", "projects": { - "eighteen-project": { + "nineteen-project": { "projectType": "application", "schematics": {}, "root": "", @@ -13,12 +13,10 @@ "build": { "builder": "@angular-devkit/build-angular:application", "options": { - "outputPath": "dist/eighteen-project", + "outputPath": "dist/nineteen-project", "index": "src/index.html", "browser": "src/main.ts", - "polyfills": [ - "zone.js" - ], + "polyfills": ["zone.js"], "tsConfig": "tsconfig.app.json", "assets": [ { @@ -26,9 +24,7 @@ "input": "public" } ], - "styles": [ - "src/styles.css" - ], + "styles": ["src/styles.css"], "scripts": [] }, "configurations": { @@ -41,8 +37,8 @@ }, { "type": "anyComponentStyle", - "maximumWarning": "2kB", - "maximumError": "4kB" + "maximumWarning": "4kB", + "maximumError": "8kB" } ], "outputHashing": "all" @@ -59,10 +55,10 @@ "builder": "@angular-devkit/build-angular:dev-server", "configurations": { "production": { - "buildTarget": "eighteen-project:build:production" + "buildTarget": "nineteen-project:build:production" }, "development": { - "buildTarget": "eighteen-project:build:development" + "buildTarget": "nineteen-project:build:development" } }, "defaultConfiguration": "development" @@ -73,10 +69,7 @@ "test": { "builder": "@angular-devkit/build-angular:karma", "options": { - "polyfills": [ - "zone.js", - "zone.js/testing" - ], + "polyfills": ["zone.js", "zone.js/testing"], "tsConfig": "tsconfig.spec.json", "assets": [ { @@ -84,9 +77,7 @@ "input": "public" } ], - "styles": [ - "src/styles.css" - ], + "styles": ["src/styles.css"], "scripts": [] } } diff --git a/tests/e2e/assets/19.0-project/package.json b/tests/e2e/assets/19.0-project/package.json new file mode 100644 index 000000000000..7b65d66807a2 --- /dev/null +++ b/tests/e2e/assets/19.0-project/package.json @@ -0,0 +1,37 @@ +{ + "name": "nineteen-project", + "version": "0.0.0", + "scripts": { + "ng": "ng", + "start": "ng serve", + "build": "ng build", + "watch": "ng build --watch --configuration development", + "test": "ng test" + }, + "private": true, + "dependencies": { + "@angular/common": "^19.2.0", + "@angular/compiler": "^19.2.0", + "@angular/core": "^19.2.0", + "@angular/forms": "^19.2.0", + "@angular/platform-browser": "^19.2.0", + "@angular/platform-browser-dynamic": "^19.2.0", + "@angular/router": "^19.2.0", + "rxjs": "~7.8.0", + "tslib": "^2.3.0", + "zone.js": "~0.15.0" + }, + "devDependencies": { + "@angular-devkit/build-angular": "^19.2.13", + "@angular/cli": "^19.2.13", + "@angular/compiler-cli": "^19.2.0", + "@types/jasmine": "~5.1.0", + "jasmine-core": "~5.6.0", + "karma": "~6.4.0", + "karma-chrome-launcher": "~3.2.0", + "karma-coverage": "~2.2.0", + "karma-jasmine": "~5.1.0", + "karma-jasmine-html-reporter": "~2.1.0", + "typescript": "~5.7.2" + } +} diff --git a/tests/legacy-cli/e2e/assets/18.0-project/public/favicon.ico b/tests/e2e/assets/19.0-project/public/favicon.ico similarity index 100% rename from tests/legacy-cli/e2e/assets/18.0-project/public/favicon.ico rename to tests/e2e/assets/19.0-project/public/favicon.ico diff --git a/tests/legacy-cli/e2e/assets/18.0-project/src/app/app.component.css b/tests/e2e/assets/19.0-project/src/app/app.component.css similarity index 100% rename from tests/legacy-cli/e2e/assets/18.0-project/src/app/app.component.css rename to tests/e2e/assets/19.0-project/src/app/app.component.css diff --git a/tests/legacy-cli/e2e/assets/18.0-project/src/app/app.component.html b/tests/e2e/assets/19.0-project/src/app/app.component.html similarity index 93% rename from tests/legacy-cli/e2e/assets/18.0-project/src/app/app.component.html rename to tests/e2e/assets/19.0-project/src/app/app.component.html index 36093e187977..f8135391366c 100644 --- a/tests/legacy-cli/e2e/assets/18.0-project/src/app/app.component.html +++ b/tests/e2e/assets/19.0-project/src/app/app.component.html @@ -36,9 +36,18 @@ --pill-accent: var(--bright-blue); - font-family: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, - Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", - "Segoe UI Symbol"; + font-family: + 'Inter', + -apple-system, + BlinkMacSystemFont, + 'Segoe UI', + Roboto, + Helvetica, + Arial, + sans-serif, + 'Apple Color Emoji', + 'Segoe UI Emoji', + 'Segoe UI Symbol'; box-sizing: border-box; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; @@ -51,9 +60,18 @@ line-height: 100%; letter-spacing: -0.125rem; margin: 0; - font-family: "Inter Tight", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, - Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", - "Segoe UI Symbol"; + font-family: + 'Inter Tight', + -apple-system, + BlinkMacSystemFont, + 'Segoe UI', + Roboto, + Helvetica, + Arial, + sans-serif, + 'Apple Color Emoji', + 'Segoe UI Emoji', + 'Segoe UI Symbol'; } p { @@ -209,14 +227,7 @@ - + @@ -231,19 +242,20 @@

Hello, {{ title }}

- @for (item of [ - { title: 'Explore the Docs', link: 'https://angular.dev' }, - { title: 'Learn with Tutorials', link: 'https://angular.dev/tutorials' }, - { title: 'CLI Docs', link: 'https://angular.dev/tools/cli' }, - { title: 'Angular Language Service', link: 'https://angular.dev/tools/language-service' }, - { title: 'Angular DevTools', link: 'https://angular.dev/tools/devtools' }, - ]; track item.title) { - + @for ( + item of [ + { title: 'Explore the Docs', link: 'https://angular.dev' }, + { title: 'Learn with Tutorials', link: 'https://angular.dev/tutorials' }, + { title: 'CLI Docs', link: 'https://angular.dev/tools/cli' }, + { + title: 'Angular Language Service', + link: 'https://angular.dev/tools/language-service', + }, + { title: 'Angular DevTools', link: 'https://angular.dev/tools/devtools' }, + ]; + track item.title + ) { + {{ item.title }} Hello, {{ title }} /> - + Hello, {{ title }} - diff --git a/tests/legacy-cli/e2e/assets/18.0-project/src/app/app.component.spec.ts b/tests/e2e/assets/19.0-project/src/app/app.component.spec.ts similarity index 85% rename from tests/legacy-cli/e2e/assets/18.0-project/src/app/app.component.spec.ts rename to tests/e2e/assets/19.0-project/src/app/app.component.spec.ts index 6e84e2cd2b04..e390fd7bd137 100644 --- a/tests/legacy-cli/e2e/assets/18.0-project/src/app/app.component.spec.ts +++ b/tests/e2e/assets/19.0-project/src/app/app.component.spec.ts @@ -14,16 +14,16 @@ describe('AppComponent', () => { expect(app).toBeTruthy(); }); - it(`should have the 'eighteen-project' title`, () => { + it(`should have the 'nineteen-project' title`, () => { const fixture = TestBed.createComponent(AppComponent); const app = fixture.componentInstance; - expect(app.title).toEqual('eighteen-project'); + expect(app.title).toEqual('nineteen-project'); }); it('should render title', () => { const fixture = TestBed.createComponent(AppComponent); fixture.detectChanges(); const compiled = fixture.nativeElement as HTMLElement; - expect(compiled.querySelector('h1')?.textContent).toContain('Hello, eighteen-project'); + expect(compiled.querySelector('h1')?.textContent).toContain('Hello, nineteen-project'); }); }); diff --git a/tests/legacy-cli/e2e/assets/18.0-project/src/app/app.component.ts b/tests/e2e/assets/19.0-project/src/app/app.component.ts similarity index 73% rename from tests/legacy-cli/e2e/assets/18.0-project/src/app/app.component.ts rename to tests/e2e/assets/19.0-project/src/app/app.component.ts index 9b1edc2b9399..620c8a058372 100644 --- a/tests/legacy-cli/e2e/assets/18.0-project/src/app/app.component.ts +++ b/tests/e2e/assets/19.0-project/src/app/app.component.ts @@ -3,11 +3,10 @@ import { RouterOutlet } from '@angular/router'; @Component({ selector: 'app-root', - standalone: true, imports: [RouterOutlet], templateUrl: './app.component.html', - styleUrl: './app.component.css' + styleUrl: './app.component.css', }) export class AppComponent { - title = 'eighteen-project'; + title = 'nineteen-project'; } diff --git a/tests/legacy-cli/e2e/assets/18.0-project/src/app/app.config.ts b/tests/e2e/assets/19.0-project/src/app/app.config.ts similarity index 90% rename from tests/legacy-cli/e2e/assets/18.0-project/src/app/app.config.ts rename to tests/e2e/assets/19.0-project/src/app/app.config.ts index a1e7d6f864c1..7afc797fbab7 100644 --- a/tests/legacy-cli/e2e/assets/18.0-project/src/app/app.config.ts +++ b/tests/e2e/assets/19.0-project/src/app/app.config.ts @@ -4,5 +4,5 @@ import { provideRouter } from '@angular/router'; import { routes } from './app.routes'; export const appConfig: ApplicationConfig = { - providers: [provideZoneChangeDetection({ eventCoalescing: true }), provideRouter(routes)] + providers: [provideZoneChangeDetection({ eventCoalescing: true }), provideRouter(routes)], }; diff --git a/tests/legacy-cli/e2e/assets/18.0-project/src/app/app.routes.ts b/tests/e2e/assets/19.0-project/src/app/app.routes.ts similarity index 100% rename from tests/legacy-cli/e2e/assets/18.0-project/src/app/app.routes.ts rename to tests/e2e/assets/19.0-project/src/app/app.routes.ts diff --git a/tests/e2e/assets/19.0-project/src/index.html b/tests/e2e/assets/19.0-project/src/index.html new file mode 100644 index 000000000000..f374b0fe3d5e --- /dev/null +++ b/tests/e2e/assets/19.0-project/src/index.html @@ -0,0 +1,13 @@ + + + + + NineteenProject + + + + + + + + diff --git a/tests/legacy-cli/e2e/assets/18.0-project/src/main.ts b/tests/e2e/assets/19.0-project/src/main.ts similarity index 66% rename from tests/legacy-cli/e2e/assets/18.0-project/src/main.ts rename to tests/e2e/assets/19.0-project/src/main.ts index 35b00f346331..17447a5dce2c 100644 --- a/tests/legacy-cli/e2e/assets/18.0-project/src/main.ts +++ b/tests/e2e/assets/19.0-project/src/main.ts @@ -2,5 +2,4 @@ import { bootstrapApplication } from '@angular/platform-browser'; import { appConfig } from './app/app.config'; import { AppComponent } from './app/app.component'; -bootstrapApplication(AppComponent, appConfig) - .catch((err) => console.error(err)); +bootstrapApplication(AppComponent, appConfig).catch((err) => console.error(err)); diff --git a/tests/legacy-cli/e2e/assets/18.0-project/src/styles.css b/tests/e2e/assets/19.0-project/src/styles.css similarity index 100% rename from tests/legacy-cli/e2e/assets/18.0-project/src/styles.css rename to tests/e2e/assets/19.0-project/src/styles.css diff --git a/tests/legacy-cli/e2e/assets/18.0-project/tsconfig.app.json b/tests/e2e/assets/19.0-project/tsconfig.app.json similarity index 82% rename from tests/legacy-cli/e2e/assets/18.0-project/tsconfig.app.json rename to tests/e2e/assets/19.0-project/tsconfig.app.json index 3775b37e3bbc..8886e903f8d0 100644 --- a/tests/legacy-cli/e2e/assets/18.0-project/tsconfig.app.json +++ b/tests/e2e/assets/19.0-project/tsconfig.app.json @@ -6,10 +6,6 @@ "outDir": "./out-tsc/app", "types": [] }, - "files": [ - "src/main.ts" - ], - "include": [ - "src/**/*.d.ts" - ] + "files": ["src/main.ts"], + "include": ["src/**/*.d.ts"] } diff --git a/tests/legacy-cli/e2e/assets/18.0-project/tsconfig.json b/tests/e2e/assets/19.0-project/tsconfig.json similarity index 88% rename from tests/legacy-cli/e2e/assets/18.0-project/tsconfig.json rename to tests/e2e/assets/19.0-project/tsconfig.json index a8bb65b6e220..5525117c6744 100644 --- a/tests/legacy-cli/e2e/assets/18.0-project/tsconfig.json +++ b/tests/e2e/assets/19.0-project/tsconfig.json @@ -12,17 +12,11 @@ "skipLibCheck": true, "isolatedModules": true, "esModuleInterop": true, - "sourceMap": true, - "declaration": false, "experimentalDecorators": true, "moduleResolution": "bundler", "importHelpers": true, "target": "ES2022", - "module": "ES2022", - "lib": [ - "ES2022", - "dom" - ] + "module": "ES2022" }, "angularCompilerOptions": { "enableI18nLegacyMessageIdFormat": false, diff --git a/tests/legacy-cli/e2e/assets/18.0-project/tsconfig.spec.json b/tests/e2e/assets/19.0-project/tsconfig.spec.json similarity index 76% rename from tests/legacy-cli/e2e/assets/18.0-project/tsconfig.spec.json rename to tests/e2e/assets/19.0-project/tsconfig.spec.json index 5fb748d9207a..e00e30e6d4fb 100644 --- a/tests/legacy-cli/e2e/assets/18.0-project/tsconfig.spec.json +++ b/tests/e2e/assets/19.0-project/tsconfig.spec.json @@ -4,12 +4,7 @@ "extends": "./tsconfig.json", "compilerOptions": { "outDir": "./out-tsc/spec", - "types": [ - "jasmine" - ] + "types": ["jasmine"] }, - "include": [ - "src/**/*.spec.ts", - "src/**/*.d.ts" - ] + "include": ["src/**/*.spec.ts", "src/**/*.d.ts"] } diff --git a/tests/legacy-cli/e2e/assets/BUILD.bazel b/tests/e2e/assets/BUILD.bazel similarity index 59% rename from tests/legacy-cli/e2e/assets/BUILD.bazel rename to tests/e2e/assets/BUILD.bazel index 946db62d0d5a..11bc738d4a29 100644 --- a/tests/legacy-cli/e2e/assets/BUILD.bazel +++ b/tests/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-peer-bad/collection.json b/tests/e2e/assets/add-collection-dir/collection.json similarity index 100% rename from tests/legacy-cli/e2e/assets/add-collection-peer-bad/collection.json rename to tests/e2e/assets/add-collection-dir/collection.json diff --git a/tests/e2e/assets/add-collection-dir/index.js b/tests/e2e/assets/add-collection-dir/index.js new file mode 100644 index 000000000000..0c38ee4e9fd9 --- /dev/null +++ b/tests/e2e/assets/add-collection-dir/index.js @@ -0,0 +1 @@ +exports.default = (options) => (tree) => tree.create(options.name || 'empty-file', ''); diff --git a/tests/legacy-cli/e2e/assets/add-collection/package.json b/tests/e2e/assets/add-collection-dir/package.json similarity index 100% rename from tests/legacy-cli/e2e/assets/add-collection/package.json rename to tests/e2e/assets/add-collection-dir/package.json diff --git a/tests/legacy-cli/e2e/assets/add-collection-peer-good/collection.json b/tests/e2e/assets/add-collection-peer-bad/collection.json similarity index 100% rename from tests/legacy-cli/e2e/assets/add-collection-peer-good/collection.json rename to tests/e2e/assets/add-collection-peer-bad/collection.json diff --git a/tests/e2e/assets/add-collection-peer-bad/index.js b/tests/e2e/assets/add-collection-peer-bad/index.js new file mode 100644 index 000000000000..df08babad74f --- /dev/null +++ b/tests/e2e/assets/add-collection-peer-bad/index.js @@ -0,0 +1 @@ +exports.default = (options) => (tree) => tree.create(options.name || 'empty-file-peer-bad', ''); diff --git a/tests/legacy-cli/e2e/assets/add-collection-peer-bad/package.json b/tests/e2e/assets/add-collection-peer-bad/package.json similarity index 100% rename from tests/legacy-cli/e2e/assets/add-collection-peer-bad/package.json rename to tests/e2e/assets/add-collection-peer-bad/package.json diff --git a/tests/legacy-cli/e2e/assets/add-collection/collection.json b/tests/e2e/assets/add-collection-peer-good/collection.json similarity index 100% rename from tests/legacy-cli/e2e/assets/add-collection/collection.json rename to tests/e2e/assets/add-collection-peer-good/collection.json diff --git a/tests/e2e/assets/add-collection-peer-good/index.js b/tests/e2e/assets/add-collection-peer-good/index.js new file mode 100644 index 000000000000..bddee127cebf --- /dev/null +++ b/tests/e2e/assets/add-collection-peer-good/index.js @@ -0,0 +1 @@ +exports.default = (options) => (tree) => tree.create(options.name || 'empty-file-peer-good', ''); diff --git a/tests/legacy-cli/e2e/assets/add-collection-peer-good/package.json b/tests/e2e/assets/add-collection-peer-good/package.json similarity index 100% rename from tests/legacy-cli/e2e/assets/add-collection-peer-good/package.json rename to tests/e2e/assets/add-collection-peer-good/package.json diff --git a/tests/legacy-cli/e2e/assets/add-collection.tgz b/tests/e2e/assets/add-collection.tgz similarity index 100% rename from tests/legacy-cli/e2e/assets/add-collection.tgz rename to tests/e2e/assets/add-collection.tgz diff --git a/tests/legacy-cli/e2e/assets/images/spectrum.png b/tests/e2e/assets/images/spectrum.png similarity index 100% rename from tests/legacy-cli/e2e/assets/images/spectrum.png rename to tests/e2e/assets/images/spectrum.png diff --git a/tests/legacy-cli/e2e/assets/nested-schematic-dependency/collection.json b/tests/e2e/assets/nested-schematic-dependency/collection.json similarity index 100% rename from tests/legacy-cli/e2e/assets/nested-schematic-dependency/collection.json rename to tests/e2e/assets/nested-schematic-dependency/collection.json diff --git a/tests/e2e/assets/nested-schematic-dependency/index.js b/tests/e2e/assets/nested-schematic-dependency/index.js new file mode 100644 index 000000000000..0c38ee4e9fd9 --- /dev/null +++ b/tests/e2e/assets/nested-schematic-dependency/index.js @@ -0,0 +1 @@ +exports.default = (options) => (tree) => tree.create(options.name || 'empty-file', ''); diff --git a/tests/legacy-cli/e2e/assets/nested-schematic-dependency/package.json b/tests/e2e/assets/nested-schematic-dependency/package.json similarity index 100% rename from tests/legacy-cli/e2e/assets/nested-schematic-dependency/package.json rename to tests/e2e/assets/nested-schematic-dependency/package.json diff --git a/tests/legacy-cli/e2e/assets/nested-schematic-main/collection.json b/tests/e2e/assets/nested-schematic-main/collection.json similarity index 100% rename from tests/legacy-cli/e2e/assets/nested-schematic-main/collection.json rename to tests/e2e/assets/nested-schematic-main/collection.json diff --git a/tests/e2e/assets/nested-schematic-main/index.js b/tests/e2e/assets/nested-schematic-main/index.js new file mode 100644 index 000000000000..1894dbd19b88 --- /dev/null +++ b/tests/e2e/assets/nested-schematic-main/index.js @@ -0,0 +1,2 @@ +exports.default = (options) => + require('@angular-devkit/schematics').externalSchematic('empty-app-nested', 'nested', {}); diff --git a/tests/legacy-cli/e2e/assets/nested-schematic-main/package.json b/tests/e2e/assets/nested-schematic-main/package.json similarity index 100% rename from tests/legacy-cli/e2e/assets/nested-schematic-main/package.json rename to tests/e2e/assets/nested-schematic-main/package.json diff --git a/tests/legacy-cli/e2e/assets/protractor-saucelabs.conf.js b/tests/e2e/assets/protractor-saucelabs.conf.js similarity index 100% rename from tests/legacy-cli/e2e/assets/protractor-saucelabs.conf.js rename to tests/e2e/assets/protractor-saucelabs.conf.js diff --git a/tests/legacy-cli/e2e/assets/schematic-allow-scripts/collection.json b/tests/e2e/assets/schematic-allow-scripts/collection.json similarity index 100% rename from tests/legacy-cli/e2e/assets/schematic-allow-scripts/collection.json rename to tests/e2e/assets/schematic-allow-scripts/collection.json diff --git a/tests/e2e/assets/schematic-allow-scripts/index.js b/tests/e2e/assets/schematic-allow-scripts/index.js new file mode 100644 index 000000000000..b4a44b829a15 --- /dev/null +++ b/tests/e2e/assets/schematic-allow-scripts/index.js @@ -0,0 +1,24 @@ +const tasks = require('@angular-devkit/schematics/tasks'); + +exports.default = ({ allowScripts, ignoreScripts = false }) => { + return (tree, context) => { + tree.create( + '/install-test/package.json', + JSON.stringify({ + name: 'install-test', + version: '0.0.0', + scripts: { + postinstall: `node run-post.js`, + }, + }), + ); + tree.create('/install-test/.npmrc', `ignore-scripts=${ignoreScripts}`); + tree.create( + '/install-test/run-post.js', + 'require("fs").writeFileSync(__dirname + "/post-script-ran", "12345");', + ); + context.addTask( + new tasks.NodePackageInstallTask({ workingDirectory: 'install-test', allowScripts }), + ); + }; +}; diff --git a/tests/legacy-cli/e2e/assets/schematic-allow-scripts/package.json b/tests/e2e/assets/schematic-allow-scripts/package.json similarity index 100% rename from tests/legacy-cli/e2e/assets/schematic-allow-scripts/package.json rename to tests/e2e/assets/schematic-allow-scripts/package.json diff --git a/tests/e2e/assets/schematic-allow-scripts/schema.json b/tests/e2e/assets/schematic-allow-scripts/schema.json new file mode 100644 index 000000000000..3dc7b38c6f8d --- /dev/null +++ b/tests/e2e/assets/schematic-allow-scripts/schema.json @@ -0,0 +1,13 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema", + "type": "object", + "additionalProperties": false, + "properties": { + "allowScripts": { + "type": "boolean" + }, + "ignoreScripts": { + "type": "boolean" + } + } +} diff --git a/tests/legacy-cli/e2e/assets/schematic-boolean-option-negated/collection.json b/tests/e2e/assets/schematic-boolean-option-negated/collection.json similarity index 100% rename from tests/legacy-cli/e2e/assets/schematic-boolean-option-negated/collection.json rename to tests/e2e/assets/schematic-boolean-option-negated/collection.json diff --git a/tests/legacy-cli/e2e/assets/schematic-boolean-option-negated/index.js b/tests/e2e/assets/schematic-boolean-option-negated/index.js similarity index 100% rename from tests/legacy-cli/e2e/assets/schematic-boolean-option-negated/index.js rename to tests/e2e/assets/schematic-boolean-option-negated/index.js diff --git a/tests/legacy-cli/e2e/assets/schematic-boolean-option-negated/package.json b/tests/e2e/assets/schematic-boolean-option-negated/package.json similarity index 100% rename from tests/legacy-cli/e2e/assets/schematic-boolean-option-negated/package.json rename to tests/e2e/assets/schematic-boolean-option-negated/package.json diff --git a/tests/legacy-cli/e2e/assets/schematic-boolean-option-negated/schema.json b/tests/e2e/assets/schematic-boolean-option-negated/schema.json similarity index 100% rename from tests/legacy-cli/e2e/assets/schematic-boolean-option-negated/schema.json rename to tests/e2e/assets/schematic-boolean-option-negated/schema.json diff --git a/tests/legacy-cli/e2e/assets/ssr-project-webpack/.gitignore b/tests/e2e/assets/ssr-project-webpack/.gitignore similarity index 100% rename from tests/legacy-cli/e2e/assets/ssr-project-webpack/.gitignore rename to tests/e2e/assets/ssr-project-webpack/.gitignore diff --git a/tests/legacy-cli/e2e/assets/ssr-project-webpack/README.md b/tests/e2e/assets/ssr-project-webpack/README.md similarity index 100% rename from tests/legacy-cli/e2e/assets/ssr-project-webpack/README.md rename to tests/e2e/assets/ssr-project-webpack/README.md diff --git a/tests/legacy-cli/e2e/assets/ssr-project-webpack/angular.json b/tests/e2e/assets/ssr-project-webpack/angular.json similarity index 87% rename from tests/legacy-cli/e2e/assets/ssr-project-webpack/angular.json rename to tests/e2e/assets/ssr-project-webpack/angular.json index 9a26600b1843..5637f8e484a2 100644 --- a/tests/legacy-cli/e2e/assets/ssr-project-webpack/angular.json +++ b/tests/e2e/assets/ssr-project-webpack/angular.json @@ -16,17 +16,10 @@ "outputPath": "dist/ssr-project-webpack/browser", "index": "src/index.html", "main": "src/main.ts", - "polyfills": [ - "zone.js" - ], + "polyfills": ["zone.js"], "tsConfig": "tsconfig.app.json", - "assets": [ - "src/favicon.ico", - "src/assets" - ], - "styles": [ - "src/styles.css" - ], + "assets": ["src/favicon.ico", "src/assets"], + "styles": ["src/styles.css"], "scripts": [] }, "configurations": { @@ -77,18 +70,10 @@ "test": { "builder": "@angular-devkit/build-angular:karma", "options": { - "polyfills": [ - "zone.js", - "zone.js/testing" - ], + "polyfills": ["zone.js", "zone.js/testing"], "tsConfig": "tsconfig.spec.json", - "assets": [ - "src/favicon.ico", - "src/assets" - ], - "styles": [ - "src/styles.css" - ], + "assets": ["src/favicon.ico", "src/assets"], + "styles": ["src/styles.css"], "scripts": [] } }, @@ -130,9 +115,7 @@ "prerender": { "builder": "@angular-devkit/build-angular:prerender", "options": { - "routes": [ - "/" - ] + "routes": ["/"] }, "configurations": { "production": { diff --git a/tests/legacy-cli/e2e/assets/ssr-project-webpack/package.json b/tests/e2e/assets/ssr-project-webpack/package.json similarity index 100% rename from tests/legacy-cli/e2e/assets/ssr-project-webpack/package.json rename to tests/e2e/assets/ssr-project-webpack/package.json diff --git a/tests/legacy-cli/e2e/assets/ssr-project-webpack/server.ts b/tests/e2e/assets/ssr-project-webpack/server.ts similarity index 93% rename from tests/legacy-cli/e2e/assets/ssr-project-webpack/server.ts rename to tests/e2e/assets/ssr-project-webpack/server.ts index c0db431c8657..59f788024bb6 100644 --- a/tests/legacy-cli/e2e/assets/ssr-project-webpack/server.ts +++ b/tests/e2e/assets/ssr-project-webpack/server.ts @@ -23,9 +23,12 @@ export function app(): express.Express { // Example Express Rest API endpoints // server.get('/api/**', (req, res) => { }); // Serve static files from /browser - server.get('*.*', express.static(distFolder, { - maxAge: '1y' - })); + server.get( + '*.*', + express.static(distFolder, { + maxAge: '1y', + }), + ); // All regular routes use the Angular engine server.get('*', (req, res, next) => { @@ -61,7 +64,7 @@ function run(): void { // The below code is to ensure that the server is run only when not requiring the bundle. declare const __non_webpack_require__: NodeRequire; const mainModule = __non_webpack_require__.main; -const moduleFilename = mainModule && mainModule.filename || ''; +const moduleFilename = (mainModule && mainModule.filename) || ''; if (moduleFilename === __filename || moduleFilename.includes('iisnode')) { run(); } diff --git a/tests/legacy-cli/e2e/assets/ssr-project-webpack/src/app/app-routing.module.ts b/tests/e2e/assets/ssr-project-webpack/src/app/app-routing.module.ts similarity index 75% rename from tests/legacy-cli/e2e/assets/ssr-project-webpack/src/app/app-routing.module.ts rename to tests/e2e/assets/ssr-project-webpack/src/app/app-routing.module.ts index 02972627f8df..f3daf250ad25 100644 --- a/tests/legacy-cli/e2e/assets/ssr-project-webpack/src/app/app-routing.module.ts +++ b/tests/e2e/assets/ssr-project-webpack/src/app/app-routing.module.ts @@ -5,6 +5,6 @@ const routes: Routes = []; @NgModule({ imports: [RouterModule.forRoot(routes)], - exports: [RouterModule] + exports: [RouterModule], }) -export class AppRoutingModule { } +export class AppRoutingModule {} diff --git a/tests/legacy-cli/e2e/assets/ssr-project-webpack/src/app/app.component.css b/tests/e2e/assets/ssr-project-webpack/src/app/app.component.css similarity index 100% rename from tests/legacy-cli/e2e/assets/ssr-project-webpack/src/app/app.component.css rename to tests/e2e/assets/ssr-project-webpack/src/app/app.component.css diff --git a/tests/e2e/assets/ssr-project-webpack/src/app/app.component.html b/tests/e2e/assets/ssr-project-webpack/src/app/app.component.html new file mode 100644 index 000000000000..e99c7ea22c86 --- /dev/null +++ b/tests/e2e/assets/ssr-project-webpack/src/app/app.component.html @@ -0,0 +1,770 @@ + + + + + + + + + + + + + + +
+ +
+ + Rocket Ship + + + + + + + + + + {{ title }} app is running! + + + Rocket Ship Smoke + + +
+ + +

Resources

+

Here are some links to help you get started:

+ + + + +

Next Steps

+

What do you want to do next with your app?

+ + + +
+ + + + + + + + + + + +
+ + +
+
ng generate component xyz
+
ng add @angular/material
+
ng add @angular/pwa
+
ng add _____
+
ng test
+
ng build
+
+ + + + + + + + + Gray Clouds Background + + +
+ + + + + + + + + + diff --git a/tests/legacy-cli/e2e/assets/ssr-project-webpack/src/app/app.component.spec.ts b/tests/e2e/assets/ssr-project-webpack/src/app/app.component.spec.ts similarity index 79% rename from tests/legacy-cli/e2e/assets/ssr-project-webpack/src/app/app.component.spec.ts rename to tests/e2e/assets/ssr-project-webpack/src/app/app.component.spec.ts index 7dfb0b7df47c..c3fc75313bcc 100644 --- a/tests/legacy-cli/e2e/assets/ssr-project-webpack/src/app/app.component.spec.ts +++ b/tests/e2e/assets/ssr-project-webpack/src/app/app.component.spec.ts @@ -3,10 +3,12 @@ import { RouterModule } from '@angular/router'; import { AppComponent } from './app.component'; describe('AppComponent', () => { - beforeEach(() => TestBed.configureTestingModule({ - imports: [RouterModule.forRoot([])], - declarations: [AppComponent] - })); + beforeEach(() => + TestBed.configureTestingModule({ + imports: [RouterModule.forRoot([])], + declarations: [AppComponent], + }), + ); it('should create the app', () => { const fixture = TestBed.createComponent(AppComponent); @@ -24,6 +26,8 @@ describe('AppComponent', () => { const fixture = TestBed.createComponent(AppComponent); fixture.detectChanges(); const compiled = fixture.nativeElement as HTMLElement; - expect(compiled.querySelector('.content span')?.textContent).toContain('20-ssr-project-webpack app is running!'); + expect(compiled.querySelector('.content span')?.textContent).toContain( + '20-ssr-project-webpack app is running!', + ); }); }); diff --git a/tests/legacy-cli/e2e/assets/ssr-project-webpack/src/app/app.component.ts b/tests/e2e/assets/ssr-project-webpack/src/app/app.component.ts similarity index 84% rename from tests/legacy-cli/e2e/assets/ssr-project-webpack/src/app/app.component.ts rename to tests/e2e/assets/ssr-project-webpack/src/app/app.component.ts index f643fddc3586..20b0fef78f45 100644 --- a/tests/legacy-cli/e2e/assets/ssr-project-webpack/src/app/app.component.ts +++ b/tests/e2e/assets/ssr-project-webpack/src/app/app.component.ts @@ -4,7 +4,7 @@ import { Component } from '@angular/core'; selector: 'app-root', standalone: false, templateUrl: './app.component.html', - styleUrls: ['./app.component.css'] + styleUrls: ['./app.component.css'], }) export class AppComponent { title = '20-ssr-project-webpack'; diff --git a/tests/legacy-cli/e2e/assets/ssr-project-webpack/src/app/app.module.server.ts b/tests/e2e/assets/ssr-project-webpack/src/app/app.module.server.ts similarity index 83% rename from tests/legacy-cli/e2e/assets/ssr-project-webpack/src/app/app.module.server.ts rename to tests/e2e/assets/ssr-project-webpack/src/app/app.module.server.ts index 795380cd2294..d182a9f3e994 100644 --- a/tests/legacy-cli/e2e/assets/ssr-project-webpack/src/app/app.module.server.ts +++ b/tests/e2e/assets/ssr-project-webpack/src/app/app.module.server.ts @@ -5,10 +5,7 @@ import { AppModule } from './app.module'; import { AppComponent } from './app.component'; @NgModule({ - imports: [ - AppModule, - ServerModule, - ], + imports: [AppModule, ServerModule], bootstrap: [AppComponent], }) export class AppServerModule {} diff --git a/tests/legacy-cli/e2e/assets/ssr-project-webpack/src/app/app.module.ts b/tests/e2e/assets/ssr-project-webpack/src/app/app.module.ts similarity index 55% rename from tests/legacy-cli/e2e/assets/ssr-project-webpack/src/app/app.module.ts rename to tests/e2e/assets/ssr-project-webpack/src/app/app.module.ts index a06d9e8b06b4..700cb243fffa 100644 --- a/tests/legacy-cli/e2e/assets/ssr-project-webpack/src/app/app.module.ts +++ b/tests/e2e/assets/ssr-project-webpack/src/app/app.module.ts @@ -5,16 +5,9 @@ import { AppRoutingModule } from './app-routing.module'; import { AppComponent } from './app.component'; @NgModule({ - declarations: [ - AppComponent - ], - imports: [ - BrowserModule, - AppRoutingModule - ], - providers: [ - provideClientHydration() - ], - bootstrap: [AppComponent] + declarations: [AppComponent], + imports: [BrowserModule, AppRoutingModule], + providers: [provideClientHydration()], + bootstrap: [AppComponent], }) -export class AppModule { } +export class AppModule {} diff --git a/tests/legacy-cli/e2e/assets/ssr-project-webpack/src/assets/.gitkeep b/tests/e2e/assets/ssr-project-webpack/src/assets/.gitkeep similarity index 100% rename from tests/legacy-cli/e2e/assets/ssr-project-webpack/src/assets/.gitkeep rename to tests/e2e/assets/ssr-project-webpack/src/assets/.gitkeep diff --git a/tests/legacy-cli/e2e/assets/ssr-project-webpack/src/favicon.ico b/tests/e2e/assets/ssr-project-webpack/src/favicon.ico similarity index 100% rename from tests/legacy-cli/e2e/assets/ssr-project-webpack/src/favicon.ico rename to tests/e2e/assets/ssr-project-webpack/src/favicon.ico diff --git a/tests/e2e/assets/ssr-project-webpack/src/index.html b/tests/e2e/assets/ssr-project-webpack/src/index.html new file mode 100644 index 000000000000..28adeacc85ed --- /dev/null +++ b/tests/e2e/assets/ssr-project-webpack/src/index.html @@ -0,0 +1,13 @@ + + + + + 17SsrProjectWebpack + + + + + + + + diff --git a/tests/legacy-cli/e2e/assets/ssr-project-webpack/src/main.server.ts b/tests/e2e/assets/ssr-project-webpack/src/main.server.ts similarity index 100% rename from tests/legacy-cli/e2e/assets/ssr-project-webpack/src/main.server.ts rename to tests/e2e/assets/ssr-project-webpack/src/main.server.ts diff --git a/tests/legacy-cli/e2e/assets/ssr-project-webpack/src/main.ts b/tests/e2e/assets/ssr-project-webpack/src/main.ts similarity index 55% rename from tests/legacy-cli/e2e/assets/ssr-project-webpack/src/main.ts rename to tests/e2e/assets/ssr-project-webpack/src/main.ts index f3a8a045a0a7..55b91297823b 100644 --- a/tests/legacy-cli/e2e/assets/ssr-project-webpack/src/main.ts +++ b/tests/e2e/assets/ssr-project-webpack/src/main.ts @@ -1,5 +1,6 @@ import { platformBrowser } from '@angular/platform-browser'; import { AppModule } from './app/app.module'; -platformBrowser().bootstrapModule(AppModule) - .catch(err => console.error(err)); +platformBrowser() + .bootstrapModule(AppModule) + .catch((err) => console.error(err)); diff --git a/tests/legacy-cli/e2e/assets/ssr-project-webpack/src/styles.css b/tests/e2e/assets/ssr-project-webpack/src/styles.css similarity index 100% rename from tests/legacy-cli/e2e/assets/ssr-project-webpack/src/styles.css rename to tests/e2e/assets/ssr-project-webpack/src/styles.css diff --git a/tests/legacy-cli/e2e/assets/ssr-project-webpack/tsconfig.app.json b/tests/e2e/assets/ssr-project-webpack/tsconfig.app.json similarity index 71% rename from tests/legacy-cli/e2e/assets/ssr-project-webpack/tsconfig.app.json rename to tests/e2e/assets/ssr-project-webpack/tsconfig.app.json index 374cc9d294aa..84f1f992d275 100644 --- a/tests/legacy-cli/e2e/assets/ssr-project-webpack/tsconfig.app.json +++ b/tests/e2e/assets/ssr-project-webpack/tsconfig.app.json @@ -5,10 +5,6 @@ "outDir": "./out-tsc/app", "types": [] }, - "files": [ - "src/main.ts" - ], - "include": [ - "src/**/*.d.ts" - ] + "files": ["src/main.ts"], + "include": ["src/**/*.d.ts"] } diff --git a/tests/legacy-cli/e2e/assets/ssr-project-webpack/tsconfig.json b/tests/e2e/assets/ssr-project-webpack/tsconfig.json similarity index 94% rename from tests/legacy-cli/e2e/assets/ssr-project-webpack/tsconfig.json rename to tests/e2e/assets/ssr-project-webpack/tsconfig.json index 532ea4d30a62..bbc051d01524 100644 --- a/tests/legacy-cli/e2e/assets/ssr-project-webpack/tsconfig.json +++ b/tests/e2e/assets/ssr-project-webpack/tsconfig.json @@ -17,10 +17,7 @@ "importHelpers": true, "target": "ES2022", "module": "ES2022", - "lib": [ - "ES2022", - "dom" - ] + "lib": ["ES2022", "dom"] }, "angularCompilerOptions": { "enableI18nLegacyMessageIdFormat": false, diff --git a/tests/legacy-cli/e2e/assets/ssr-project-webpack/tsconfig.server.json b/tests/e2e/assets/ssr-project-webpack/tsconfig.server.json similarity index 65% rename from tests/legacy-cli/e2e/assets/ssr-project-webpack/tsconfig.server.json rename to tests/e2e/assets/ssr-project-webpack/tsconfig.server.json index e2ebe5a729be..3b9de71a23f6 100644 --- a/tests/legacy-cli/e2e/assets/ssr-project-webpack/tsconfig.server.json +++ b/tests/e2e/assets/ssr-project-webpack/tsconfig.server.json @@ -3,12 +3,7 @@ "extends": "./tsconfig.app.json", "compilerOptions": { "outDir": "./out-tsc/server", - "types": [ - "node" - ] + "types": ["node"] }, - "files": [ - "src/main.server.ts", - "server.ts" - ] + "files": ["src/main.server.ts", "server.ts"] } diff --git a/tests/legacy-cli/e2e/assets/ssr-project-webpack/tsconfig.spec.json b/tests/e2e/assets/ssr-project-webpack/tsconfig.spec.json similarity index 63% rename from tests/legacy-cli/e2e/assets/ssr-project-webpack/tsconfig.spec.json rename to tests/e2e/assets/ssr-project-webpack/tsconfig.spec.json index be7e9da76f7b..47e3dd755170 100644 --- a/tests/legacy-cli/e2e/assets/ssr-project-webpack/tsconfig.spec.json +++ b/tests/e2e/assets/ssr-project-webpack/tsconfig.spec.json @@ -3,12 +3,7 @@ "extends": "./tsconfig.json", "compilerOptions": { "outDir": "./out-tsc/spec", - "types": [ - "jasmine" - ] + "types": ["jasmine"] }, - "include": [ - "src/**/*.spec.ts", - "src/**/*.d.ts" - ] + "include": ["src/**/*.spec.ts", "src/**/*.d.ts"] } diff --git a/tests/legacy-cli/e2e/initialize/300-log-environment.ts b/tests/e2e/initialize/300-log-environment.ts similarity index 100% rename from tests/legacy-cli/e2e/initialize/300-log-environment.ts rename to tests/e2e/initialize/300-log-environment.ts diff --git a/tests/legacy-cli/e2e/initialize/500-create-project.ts b/tests/e2e/initialize/500-create-project.ts similarity index 100% rename from tests/legacy-cli/e2e/initialize/500-create-project.ts rename to tests/e2e/initialize/500-create-project.ts diff --git a/tests/legacy-cli/e2e/initialize/BUILD.bazel b/tests/e2e/initialize/BUILD.bazel similarity index 88% rename from tests/legacy-cli/e2e/initialize/BUILD.bazel rename to tests/e2e/initialize/BUILD.bazel index da2466d90621..2ab5b570925f 100644 --- a/tests/legacy-cli/e2e/initialize/BUILD.bazel +++ b/tests/e2e/initialize/BUILD.bazel @@ -11,6 +11,6 @@ ts_project( ], deps = [ "//:node_modules/@types/node", - "//tests/legacy-cli/e2e/utils", + "//tests/e2e/utils", ], ) diff --git a/tests/legacy-cli/e2e/ng-snapshot/BUILD.bazel b/tests/e2e/ng-snapshot/BUILD.bazel similarity index 100% rename from tests/legacy-cli/e2e/ng-snapshot/BUILD.bazel rename to tests/e2e/ng-snapshot/BUILD.bazel diff --git a/tests/e2e/ng-snapshot/package.json b/tests/e2e/ng-snapshot/package.json new file mode 100644 index 000000000000..a8565caab740 --- /dev/null +++ b/tests/e2e/ng-snapshot/package.json @@ -0,0 +1,22 @@ +{ + "description": "snapshot versions of Angular for e2e testing", + "private": true, + "dependencies": { + "@angular/animations": "github:angular/animations-builds#80524f5d854b7fdb33104e629ca5e1102255f6f5", + "@angular/cdk": "github:angular/cdk-builds#475fd8d473ba20045b3393423d8a14d93a2da938", + "@angular/common": "github:angular/common-builds#8aafef4ce946a2f96a0203c9b9623c29998995bb", + "@angular/compiler": "github:angular/compiler-builds#205d342032c053a41088e14de00ff14e28e56fce", + "@angular/compiler-cli": "github:angular/compiler-cli-builds#110918badaf4f73a2d20eb58f185fb6c1f8ae54a", + "@angular/core": "github:angular/core-builds#969d56512e639d39092dd3edf736bd7ba19a4c7d", + "@angular/forms": "github:angular/forms-builds#fafffa726c829fd644957e6c76bb5e42f09cbed7", + "@angular/language-service": "github:angular/language-service-builds#41e0bb51496678a972f37cb973ece48e40bc5cd1", + "@angular/localize": "github:angular/localize-builds#ef4c1e3562f99602b38312aabccb4cb5fb6bb53f", + "@angular/material": "github:angular/material-builds#998cd492b05bf024e5bb7a9a21e52a94dec740ef", + "@angular/material-moment-adapter": "github:angular/material-moment-adapter-builds#f173ac254a842de20a90694e34628c165989e9b6", + "@angular/platform-browser": "github:angular/platform-browser-builds#90c22fc35fde1feb2bc28435d23062443ddaae68", + "@angular/platform-browser-dynamic": "github:angular/platform-browser-dynamic-builds#fbb14077cfb88dcdb4e5d1b7a177b3d127e9f8be", + "@angular/platform-server": "github:angular/platform-server-builds#0e40e41e36ffd1d1aa740438f11262542da4cd7e", + "@angular/router": "github:angular/router-builds#ac4ab9493afbfbcb3cb457682d651113938d490b", + "@angular/service-worker": "github:angular/service-worker-builds#4ffbe09669ce6958a3ffb669fa7a059aa9e735c6" + } +} diff --git a/tests/legacy-cli/e2e/setup/002-npm-sandbox.ts b/tests/e2e/setup/001-npm-sandbox.ts similarity index 100% rename from tests/legacy-cli/e2e/setup/002-npm-sandbox.ts rename to tests/e2e/setup/001-npm-sandbox.ts diff --git a/tests/legacy-cli/e2e/setup/010-local-publish.ts b/tests/e2e/setup/010-local-publish.ts similarity index 100% rename from tests/legacy-cli/e2e/setup/010-local-publish.ts rename to tests/e2e/setup/010-local-publish.ts diff --git a/tests/legacy-cli/e2e/setup/100-global-cli.ts b/tests/e2e/setup/100-global-cli.ts similarity index 97% rename from tests/legacy-cli/e2e/setup/100-global-cli.ts rename to tests/e2e/setup/100-global-cli.ts index 780c0bbfaf39..9f587fa5c38d 100644 --- a/tests/legacy-cli/e2e/setup/100-global-cli.ts +++ b/tests/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/setup/200-create-project-dir.ts b/tests/e2e/setup/200-create-project-dir.ts similarity index 100% rename from tests/legacy-cli/e2e/setup/200-create-project-dir.ts rename to tests/e2e/setup/200-create-project-dir.ts diff --git a/tests/legacy-cli/e2e/setup/BUILD.bazel b/tests/e2e/setup/BUILD.bazel similarity index 85% rename from tests/legacy-cli/e2e/setup/BUILD.bazel rename to tests/e2e/setup/BUILD.bazel index 0b83d8f92d7f..36fe39fa3409 100644 --- a/tests/legacy-cli/e2e/setup/BUILD.bazel +++ b/tests/e2e/setup/BUILD.bazel @@ -8,6 +8,6 @@ ts_project( srcs = glob(["**/*.ts"]), deps = [ "//:node_modules/@types/node", - "//tests/legacy-cli/e2e/utils", + "//tests/e2e/utils", ], ) diff --git a/tests/legacy-cli/e2e/tests/BUILD.bazel b/tests/e2e/tests/BUILD.bazel similarity index 87% rename from tests/legacy-cli/e2e/tests/BUILD.bazel rename to tests/e2e/tests/BUILD.bazel index 0ed3f83428f7..891814cb24eb 100644 --- a/tests/legacy-cli/e2e/tests/BUILD.bazel +++ b/tests/e2e/tests/BUILD.bazel @@ -12,8 +12,9 @@ ts_project( "//:node_modules/@types/semver", "//:node_modules/express", "//:node_modules/fast-glob", + "//:node_modules/puppeteer", "//:node_modules/semver", "//:node_modules/undici", - "//tests/legacy-cli/e2e/utils", + "//tests/e2e/utils", ], ) diff --git a/tests/e2e/tests/architect_cli/direct_execution.ts b/tests/e2e/tests/architect_cli/direct_execution.ts new file mode 100644 index 000000000000..b91010d46283 --- /dev/null +++ b/tests/e2e/tests/architect_cli/direct_execution.ts @@ -0,0 +1,14 @@ +import * as assert from 'node:assert/strict'; +import { exec } from '../../utils/process'; +import { join } from 'node:path'; + +export default async function () { + // Run help command + const binPath = join('node_modules', '.bin', 'architect'); + const { stdout } = await exec(binPath, '--help'); + + assert.ok( + stdout.includes('architect [project][:target][:configuration] [options, ...]'), + 'Expected stdout to contain usage information.', + ); +} diff --git a/tests/e2e/tests/architect_cli/package_execution.ts b/tests/e2e/tests/architect_cli/package_execution.ts new file mode 100644 index 000000000000..60b3964b7521 --- /dev/null +++ b/tests/e2e/tests/architect_cli/package_execution.ts @@ -0,0 +1,22 @@ +import * as assert from 'node:assert/strict'; +import { exec } from '../../utils/process'; +import { installPackage, uninstallPackage } from '../../utils/packages'; +import { join } from 'node:path'; + +export default async function () { + // Install CLI package + await installPackage('@angular-devkit/architect-cli'); + + try { + // Run help command + const binPath = join('node_modules', '.bin', 'architect'); + const { stdout } = await exec(binPath, '--help'); + + assert.ok( + stdout.includes('architect [project][:target][:configuration] [options, ...]'), + 'Expected stdout to contain usage information.', + ); + } finally { + await uninstallPackage('@angular-devkit/architect-cli'); + } +} diff --git a/tests/legacy-cli/e2e/tests/basic/aot.ts b/tests/e2e/tests/basic/aot.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/basic/aot.ts rename to tests/e2e/tests/basic/aot.ts diff --git a/tests/legacy-cli/e2e/tests/basic/build.ts b/tests/e2e/tests/basic/build.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/basic/build.ts rename to tests/e2e/tests/basic/build.ts diff --git a/tests/legacy-cli/e2e/tests/basic/command-scope.ts b/tests/e2e/tests/basic/command-scope.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/basic/command-scope.ts rename to tests/e2e/tests/basic/command-scope.ts diff --git a/tests/legacy-cli/e2e/tests/basic/rebuild.ts b/tests/e2e/tests/basic/rebuild.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/basic/rebuild.ts rename to tests/e2e/tests/basic/rebuild.ts diff --git a/tests/legacy-cli/e2e/tests/basic/run.ts b/tests/e2e/tests/basic/run.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/basic/run.ts rename to tests/e2e/tests/basic/run.ts diff --git a/tests/legacy-cli/e2e/tests/basic/scripts-array.ts b/tests/e2e/tests/basic/scripts-array.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/basic/scripts-array.ts rename to tests/e2e/tests/basic/scripts-array.ts diff --git a/tests/legacy-cli/e2e/tests/basic/serve.ts b/tests/e2e/tests/basic/serve.ts similarity index 85% rename from tests/legacy-cli/e2e/tests/basic/serve.ts rename to tests/e2e/tests/basic/serve.ts index 7623cc3a6afc..eac4823a3126 100644 --- a/tests/legacy-cli/e2e/tests/basic/serve.ts +++ b/tests/e2e/tests/basic/serve.ts @@ -1,6 +1,7 @@ import assert from 'node:assert/strict'; import { killAllProcesses } from '../../utils/process'; import { ngServe } from '../../utils/project'; +import { executeBrowserTest } from '../../utils/puppeteer'; export default async function () { // Serve works without HMR @@ -11,6 +12,8 @@ export default async function () { // Serve works with HMR const hmrPort = await ngServe('--hmr'); await verifyResponse(hmrPort); + + await executeBrowserTest({ baseUrl: `http://localhost:${hmrPort}/` }); } async function verifyResponse(port: number): Promise { diff --git a/tests/legacy-cli/e2e/tests/basic/styles-array.ts b/tests/e2e/tests/basic/styles-array.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/basic/styles-array.ts rename to tests/e2e/tests/basic/styles-array.ts diff --git a/tests/legacy-cli/e2e/tests/basic/test.ts b/tests/e2e/tests/basic/test.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/basic/test.ts rename to tests/e2e/tests/basic/test.ts diff --git a/tests/legacy-cli/e2e/tests/build/app-shell/app-shell-ngmodule.ts b/tests/e2e/tests/build/app-shell/app-shell-ngmodule.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/build/app-shell/app-shell-ngmodule.ts rename to tests/e2e/tests/build/app-shell/app-shell-ngmodule.ts diff --git a/tests/legacy-cli/e2e/tests/build/app-shell/app-shell-with-schematic.ts b/tests/e2e/tests/build/app-shell/app-shell-with-schematic.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/build/app-shell/app-shell-with-schematic.ts rename to tests/e2e/tests/build/app-shell/app-shell-with-schematic.ts diff --git a/tests/legacy-cli/e2e/tests/build/app-shell/app-shell-with-service-worker.ts b/tests/e2e/tests/build/app-shell/app-shell-with-service-worker.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/build/app-shell/app-shell-with-service-worker.ts rename to tests/e2e/tests/build/app-shell/app-shell-with-service-worker.ts diff --git a/tests/legacy-cli/e2e/tests/build/assets.ts b/tests/e2e/tests/build/assets.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/build/assets.ts rename to tests/e2e/tests/build/assets.ts diff --git a/tests/legacy-cli/e2e/tests/build/auto-csp.ts b/tests/e2e/tests/build/auto-csp.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/build/auto-csp.ts rename to tests/e2e/tests/build/auto-csp.ts diff --git a/tests/legacy-cli/e2e/tests/build/bundle-budgets.ts b/tests/e2e/tests/build/bundle-budgets.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/build/bundle-budgets.ts rename to tests/e2e/tests/build/bundle-budgets.ts diff --git a/tests/legacy-cli/e2e/tests/build/chunk-optimizer-lazy.ts b/tests/e2e/tests/build/chunk-optimizer-lazy.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/build/chunk-optimizer-lazy.ts rename to tests/e2e/tests/build/chunk-optimizer-lazy.ts diff --git a/tests/legacy-cli/e2e/tests/build/chunk-optimizer.ts b/tests/e2e/tests/build/chunk-optimizer.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/build/chunk-optimizer.ts rename to tests/e2e/tests/build/chunk-optimizer.ts diff --git a/tests/legacy-cli/e2e/tests/build/config-file-fallback.ts b/tests/e2e/tests/build/config-file-fallback.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/build/config-file-fallback.ts rename to tests/e2e/tests/build/config-file-fallback.ts diff --git a/tests/legacy-cli/e2e/tests/build/css-urls.ts b/tests/e2e/tests/build/css-urls.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/build/css-urls.ts rename to tests/e2e/tests/build/css-urls.ts diff --git a/tests/legacy-cli/e2e/tests/build/disk-cache-purge.ts b/tests/e2e/tests/build/disk-cache-purge.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/build/disk-cache-purge.ts rename to tests/e2e/tests/build/disk-cache-purge.ts diff --git a/tests/legacy-cli/e2e/tests/build/disk-cache.ts b/tests/e2e/tests/build/disk-cache.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/build/disk-cache.ts rename to tests/e2e/tests/build/disk-cache.ts diff --git a/tests/legacy-cli/e2e/tests/build/esbuild-unsupported.ts b/tests/e2e/tests/build/esbuild-unsupported.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/build/esbuild-unsupported.ts rename to tests/e2e/tests/build/esbuild-unsupported.ts diff --git a/tests/legacy-cli/e2e/tests/build/extract-licenses.ts b/tests/e2e/tests/build/extract-licenses.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/build/extract-licenses.ts rename to tests/e2e/tests/build/extract-licenses.ts diff --git a/tests/legacy-cli/e2e/tests/build/incremental-watch.ts b/tests/e2e/tests/build/incremental-watch.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/build/incremental-watch.ts rename to tests/e2e/tests/build/incremental-watch.ts diff --git a/tests/legacy-cli/e2e/tests/build/jit-ngmodule.ts b/tests/e2e/tests/build/jit-ngmodule.ts similarity index 77% rename from tests/legacy-cli/e2e/tests/build/jit-ngmodule.ts rename to tests/e2e/tests/build/jit-ngmodule.ts index 910f8993a16d..aa6b3fda86bb 100644 --- a/tests/legacy-cli/e2e/tests/build/jit-ngmodule.ts +++ b/tests/e2e/tests/build/jit-ngmodule.ts @@ -1,13 +1,10 @@ import { getGlobalVariable } from '../../utils/env'; import { ng } from '../../utils/process'; -import { updateJsonFile, useCIChrome, useCIDefaults } from '../../utils/project'; +import { updateJsonFile, useCIDefaults } from '../../utils/project'; +import { executeBrowserTest } from '../../utils/puppeteer'; export default async function () { await ng('generate', 'app', 'test-project-two', '--no-standalone', '--skip-install'); - await ng('generate', 'private-e2e', '--related-app-name=test-project-two'); - - // Setup testing to use CI Chrome. - await useCIChrome('test-project-two', './projects/test-project-two/e2e'); await useCIDefaults('test-project-two'); // Make prod use JIT. @@ -46,6 +43,6 @@ export default async function () { serve.builder = '@angular-devkit/build-angular:dev-server'; }); // Test it works - await ng('e2e', 'test-project-two', '--configuration=production'); - await ng('e2e', 'test-project-two', '--configuration=development'); + await executeBrowserTest({ project: 'test-project-two', configuration: 'production' }); + await executeBrowserTest({ project: 'test-project-two', configuration: 'development' }); } diff --git a/tests/legacy-cli/e2e/tests/build/jit-prod.ts b/tests/e2e/tests/build/jit-prod.ts similarity index 85% rename from tests/legacy-cli/e2e/tests/build/jit-prod.ts rename to tests/e2e/tests/build/jit-prod.ts index 2042b0a8c93d..b2dc9d0bdddc 100644 --- a/tests/legacy-cli/e2e/tests/build/jit-prod.ts +++ b/tests/e2e/tests/build/jit-prod.ts @@ -1,6 +1,6 @@ import { getGlobalVariable } from '../../utils/env'; -import { ng } from '../../utils/process'; import { updateJsonFile } from '../../utils/project'; +import { executeBrowserTest } from '../../utils/puppeteer'; export default async function () { // Make prod use JIT. @@ -18,5 +18,5 @@ export default async function () { }); // Test it works - await ng('e2e', '--configuration=production'); + await executeBrowserTest({ configuration: 'production' }); } diff --git a/tests/legacy-cli/e2e/tests/build/lazy-load-syntax.ts b/tests/e2e/tests/build/lazy-load-syntax.ts similarity index 52% rename from tests/legacy-cli/e2e/tests/build/lazy-load-syntax.ts rename to tests/e2e/tests/build/lazy-load-syntax.ts index 2b91b3f63b45..bc0a375673dc 100644 --- a/tests/legacy-cli/e2e/tests/build/lazy-load-syntax.ts +++ b/tests/e2e/tests/build/lazy-load-syntax.ts @@ -5,10 +5,11 @@ * 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 { setTimeout } from 'node:timers/promises'; -import { replaceInFile, writeFile } from '../../utils/fs'; + +import { replaceInFile } from '../../utils/fs'; import { ng } from '../../utils/process'; import { updateJsonFile } from '../../utils/project'; +import { executeBrowserTest } from '../../utils/puppeteer'; export default async function () { // Add lazy route. @@ -22,29 +23,6 @@ export default async function () { }];`, ); - // Add lazy route e2e - await writeFile( - 'e2e/src/app.e2e-spec.ts', - ` - import { browser, logging, element, by } from 'protractor'; - - describe('workspace-project App', () => { - it('should display lazy route', async () => { - await browser.get(browser.baseUrl + '/lazy'); - expect(await element(by.css('app-lazy-comp p')).getText()).toEqual('lazy-comp works!'); - }); - - afterEach(async () => { - // Assert that there are no errors emitted from the browser - const logs = await browser.manage().logs().get(logging.Type.BROWSER); - expect(logs).not.toContain(jasmine.objectContaining({ - level: logging.Level.SEVERE, - })); - }); - }); - `, - ); - // Convert the default config to use JIT and prod to just do AOT. // This way we can use `ng e2e` to test JIT and `ng e2e --configuration=production` to test AOT. await updateJsonFile('angular.json', (json) => { @@ -53,7 +31,17 @@ export default async function () { buildTarget['configurations']['development']['aot'] = false; }); - await ng('e2e'); - await setTimeout(500); - await ng('e2e', '--configuration=production'); + const checkFn = async (page: any) => { + await page.goto(page.url() + 'lazy'); + await page.waitForFunction( + () => + !!(globalThis as any).document + .querySelector('app-lazy-comp p') + ?.textContent?.includes('lazy-comp works!'), + { timeout: 10000 }, + ); + }; + + await executeBrowserTest({ checkFn }); + await executeBrowserTest({ configuration: 'production', checkFn }); } diff --git a/tests/legacy-cli/e2e/tests/build/library-with-demo-app.ts b/tests/e2e/tests/build/library-with-demo-app.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/build/library-with-demo-app.ts rename to tests/e2e/tests/build/library-with-demo-app.ts diff --git a/tests/e2e/tests/build/library/lib-consumption-full-aot.ts b/tests/e2e/tests/build/library/lib-consumption-full-aot.ts new file mode 100644 index 000000000000..08d114f1de4a --- /dev/null +++ b/tests/e2e/tests/build/library/lib-consumption-full-aot.ts @@ -0,0 +1,14 @@ +import { ng } from '../../../utils/process'; +import { executeBrowserTest } from '../../../utils/puppeteer'; +import { browserCheck, libraryConsumptionSetup } from './setup'; + +export default async function () { + await libraryConsumptionSetup(); + + // Build library in full mode (development) + await ng('build', 'my-lib', '--configuration=development'); + + // Check that the e2e succeeds prod and non prod mode + await executeBrowserTest({ configuration: 'production', checkFn: browserCheck }); + await executeBrowserTest({ configuration: 'development', checkFn: browserCheck }); +} diff --git a/tests/legacy-cli/e2e/tests/build/library/lib-consumption-full-jit.ts b/tests/e2e/tests/build/library/lib-consumption-full-jit.ts similarity index 73% rename from tests/legacy-cli/e2e/tests/build/library/lib-consumption-full-jit.ts rename to tests/e2e/tests/build/library/lib-consumption-full-jit.ts index e63130e77c86..906a920dba44 100644 --- a/tests/legacy-cli/e2e/tests/build/library/lib-consumption-full-jit.ts +++ b/tests/e2e/tests/build/library/lib-consumption-full-jit.ts @@ -1,8 +1,8 @@ -import { setTimeout } from 'node:timers/promises'; import { updateJsonFile } from '../../../utils/project'; import { expectFileToMatch } from '../../../utils/fs'; import { ng } from '../../../utils/process'; -import { libraryConsumptionSetup } from './setup'; +import { executeBrowserTest } from '../../../utils/puppeteer'; +import { browserCheck, libraryConsumptionSetup } from './setup'; import { getGlobalVariable } from '../../../utils/env'; export default async function () { @@ -21,10 +21,9 @@ export default async function () { } }); - // Check that the e2e succeeds prod and non prod mode - await ng('e2e', '--configuration=production'); - await setTimeout(500); - await ng('e2e', '--configuration=development'); + // Ensure app works in prod and non prod mode + await executeBrowserTest({ configuration: 'production', checkFn: browserCheck }); + await executeBrowserTest({ configuration: 'development', checkFn: browserCheck }); // Validate that sourcemaps for the library exists. await ng('build', '--configuration=development'); diff --git a/tests/e2e/tests/build/library/lib-consumption-partial-aot.ts b/tests/e2e/tests/build/library/lib-consumption-partial-aot.ts new file mode 100644 index 000000000000..f906be54b0e6 --- /dev/null +++ b/tests/e2e/tests/build/library/lib-consumption-partial-aot.ts @@ -0,0 +1,14 @@ +import { ng } from '../../../utils/process'; +import { executeBrowserTest } from '../../../utils/puppeteer'; +import { browserCheck, libraryConsumptionSetup } from './setup'; + +export default async function () { + await libraryConsumptionSetup(); + + // Build library in partial mode (production) + await ng('build', 'my-lib', '--configuration=production'); + + // Check that the e2e succeeds prod and non prod mode + await executeBrowserTest({ configuration: 'production', checkFn: browserCheck }); + await executeBrowserTest({ configuration: 'development', checkFn: browserCheck }); +} diff --git a/tests/legacy-cli/e2e/tests/build/library/lib-consumption-partial-jit.ts b/tests/e2e/tests/build/library/lib-consumption-partial-jit.ts similarity index 71% rename from tests/legacy-cli/e2e/tests/build/library/lib-consumption-partial-jit.ts rename to tests/e2e/tests/build/library/lib-consumption-partial-jit.ts index 9abb8a7e7b2f..503c09e525e9 100644 --- a/tests/legacy-cli/e2e/tests/build/library/lib-consumption-partial-jit.ts +++ b/tests/e2e/tests/build/library/lib-consumption-partial-jit.ts @@ -1,7 +1,7 @@ -import { setTimeout } from 'node:timers/promises'; import { updateJsonFile } from '../../../utils/project'; import { ng } from '../../../utils/process'; -import { libraryConsumptionSetup } from './setup'; +import { executeBrowserTest } from '../../../utils/puppeteer'; +import { browserCheck, libraryConsumptionSetup } from './setup'; import { getGlobalVariable } from '../../../utils/env'; export default async function () { @@ -22,7 +22,6 @@ export default async function () { }); // Check that the e2e succeeds prod and non prod mode - await ng('e2e', '--configuration=production'); - await setTimeout(500); - await ng('e2e', '--configuration=development'); + await executeBrowserTest({ configuration: 'production', checkFn: browserCheck }); + await executeBrowserTest({ configuration: 'development', checkFn: browserCheck }); } diff --git a/tests/legacy-cli/e2e/tests/build/library/lib-consumption-sourcemaps.ts b/tests/e2e/tests/build/library/lib-consumption-sourcemaps.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/build/library/lib-consumption-sourcemaps.ts rename to tests/e2e/tests/build/library/lib-consumption-sourcemaps.ts diff --git a/tests/legacy-cli/e2e/tests/build/library/lib-unused-decorated-class-treeshake.ts b/tests/e2e/tests/build/library/lib-unused-decorated-class-treeshake.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/build/library/lib-unused-decorated-class-treeshake.ts rename to tests/e2e/tests/build/library/lib-unused-decorated-class-treeshake.ts diff --git a/tests/legacy-cli/e2e/tests/build/library/setup.ts b/tests/e2e/tests/build/library/setup.ts similarity index 55% rename from tests/legacy-cli/e2e/tests/build/library/setup.ts rename to tests/e2e/tests/build/library/setup.ts index 9e05cab551a4..621b740cf4bc 100644 --- a/tests/legacy-cli/e2e/tests/build/library/setup.ts +++ b/tests/e2e/tests/build/library/setup.ts @@ -1,3 +1,4 @@ +import type { Page } from 'puppeteer'; import { writeMultipleFiles } from '../../../utils/fs'; import { silentNg } from '../../../utils/process'; @@ -30,30 +31,15 @@ export async function libraryConsumptionSetup(): Promise { } } `, - 'e2e/src/app.e2e-spec.ts': ` - import { browser, logging, element, by } from 'protractor'; - import { AppPage } from './app.po'; - - describe('workspace-project App', () => { - let page: AppPage; - - beforeEach(() => { - page = new AppPage(); - }); - - it('should display text from library component', async () => { - await page.navigateTo(); - expect(await element(by.css('lib-my-lib p')).getText()).toEqual('my-lib works!'); - }); - - afterEach(async () => { - // Assert that there are no errors emitted from the browser - const logs = await browser.manage().logs().get(logging.Type.BROWSER); - expect(logs).not.toContain(jasmine.objectContaining({ - level: logging.Level.SEVERE, - })); - }); - }); -`, }); } + +export async function browserCheck(page: Page): Promise { + await page.waitForFunction( + () => + !!(globalThis as any).document + .querySelector('lib-my-lib p') + ?.textContent?.includes('my-lib works!'), + { timeout: 10000 }, + ); +} diff --git a/tests/legacy-cli/e2e/tests/build/material.ts b/tests/e2e/tests/build/material.ts similarity index 92% rename from tests/legacy-cli/e2e/tests/build/material.ts rename to tests/e2e/tests/build/material.ts index 3fd41f712a30..bc4862f735fc 100644 --- a/tests/legacy-cli/e2e/tests/build/material.ts +++ b/tests/e2e/tests/build/material.ts @@ -1,5 +1,4 @@ -import assert from 'node:assert/strict'; -import { appendFile, readdir } from 'node:fs/promises'; +import { appendFile } from 'node:fs/promises'; import { getGlobalVariable } from '../../utils/env'; import { readFile, replaceInFile } from '../../utils/fs'; import { @@ -7,8 +6,9 @@ import { installPackage, installWorkspacePackages, } from '../../utils/packages'; -import { execWithEnv, ng } from '../../utils/process'; +import { ng } from '../../utils/process'; import { isPrereleaseCli, updateJsonFile } from '../../utils/project'; +import { executeBrowserTest } from '../../utils/puppeteer'; const snapshots = require('../../ng-snapshot/package.json'); @@ -85,5 +85,5 @@ export default async function () { }`, ); - await ng('e2e', '--configuration=production'); + await executeBrowserTest({ configuration: 'production' }); } diff --git a/tests/legacy-cli/e2e/tests/build/multiple-configs.ts b/tests/e2e/tests/build/multiple-configs.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/build/multiple-configs.ts rename to tests/e2e/tests/build/multiple-configs.ts diff --git a/tests/legacy-cli/e2e/tests/build/output-dir.ts b/tests/e2e/tests/build/output-dir.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/build/output-dir.ts rename to tests/e2e/tests/build/output-dir.ts diff --git a/tests/legacy-cli/e2e/tests/build/poll.ts b/tests/e2e/tests/build/poll.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/build/poll.ts rename to tests/e2e/tests/build/poll.ts diff --git a/tests/legacy-cli/e2e/tests/build/prerender/discover-routes-ngmodule.ts b/tests/e2e/tests/build/prerender/discover-routes-ngmodule.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/build/prerender/discover-routes-ngmodule.ts rename to tests/e2e/tests/build/prerender/discover-routes-ngmodule.ts diff --git a/tests/legacy-cli/e2e/tests/build/prerender/discover-routes-standalone.ts b/tests/e2e/tests/build/prerender/discover-routes-standalone.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/build/prerender/discover-routes-standalone.ts rename to tests/e2e/tests/build/prerender/discover-routes-standalone.ts diff --git a/tests/legacy-cli/e2e/tests/build/prerender/error-with-sourcemaps.ts b/tests/e2e/tests/build/prerender/error-with-sourcemaps.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/build/prerender/error-with-sourcemaps.ts rename to tests/e2e/tests/build/prerender/error-with-sourcemaps.ts diff --git a/tests/legacy-cli/e2e/tests/build/prerender/http-requests-assets.ts b/tests/e2e/tests/build/prerender/http-requests-assets.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/build/prerender/http-requests-assets.ts rename to tests/e2e/tests/build/prerender/http-requests-assets.ts diff --git a/tests/legacy-cli/e2e/tests/build/prod-build.ts b/tests/e2e/tests/build/prod-build.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/build/prod-build.ts rename to tests/e2e/tests/build/prod-build.ts diff --git a/tests/legacy-cli/e2e/tests/build/progress-and-stats.ts b/tests/e2e/tests/build/progress-and-stats.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/build/progress-and-stats.ts rename to tests/e2e/tests/build/progress-and-stats.ts diff --git a/tests/legacy-cli/e2e/tests/build/project-name.ts b/tests/e2e/tests/build/project-name.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/build/project-name.ts rename to tests/e2e/tests/build/project-name.ts diff --git a/tests/legacy-cli/e2e/tests/build/rebuild-deps-type-check.ts b/tests/e2e/tests/build/rebuild-deps-type-check.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/build/rebuild-deps-type-check.ts rename to tests/e2e/tests/build/rebuild-deps-type-check.ts diff --git a/tests/legacy-cli/e2e/tests/build/rebuild-dot-dirname.ts b/tests/e2e/tests/build/rebuild-dot-dirname.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/build/rebuild-dot-dirname.ts rename to tests/e2e/tests/build/rebuild-dot-dirname.ts diff --git a/tests/legacy-cli/e2e/tests/build/rebuild-replacements.ts b/tests/e2e/tests/build/rebuild-replacements.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/build/rebuild-replacements.ts rename to tests/e2e/tests/build/rebuild-replacements.ts diff --git a/tests/legacy-cli/e2e/tests/build/rebuild-symlink.ts b/tests/e2e/tests/build/rebuild-symlink.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/build/rebuild-symlink.ts rename to tests/e2e/tests/build/rebuild-symlink.ts diff --git a/tests/legacy-cli/e2e/tests/build/relative-sourcemap.ts b/tests/e2e/tests/build/relative-sourcemap.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/build/relative-sourcemap.ts rename to tests/e2e/tests/build/relative-sourcemap.ts diff --git a/tests/legacy-cli/e2e/tests/build/scripts-output-hashing.ts b/tests/e2e/tests/build/scripts-output-hashing.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/build/scripts-output-hashing.ts rename to tests/e2e/tests/build/scripts-output-hashing.ts diff --git a/tests/legacy-cli/e2e/tests/build/server-rendering/express-engine-csp-nonce.ts b/tests/e2e/tests/build/server-rendering/express-engine-csp-nonce.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/build/server-rendering/express-engine-csp-nonce.ts rename to tests/e2e/tests/build/server-rendering/express-engine-csp-nonce.ts diff --git a/tests/legacy-cli/e2e/tests/build/server-rendering/express-engine-ngmodule.ts b/tests/e2e/tests/build/server-rendering/express-engine-ngmodule.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/build/server-rendering/express-engine-ngmodule.ts rename to tests/e2e/tests/build/server-rendering/express-engine-ngmodule.ts diff --git a/tests/legacy-cli/e2e/tests/build/server-rendering/express-engine-standalone.ts b/tests/e2e/tests/build/server-rendering/express-engine-standalone.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/build/server-rendering/express-engine-standalone.ts rename to tests/e2e/tests/build/server-rendering/express-engine-standalone.ts diff --git a/tests/legacy-cli/e2e/tests/build/server-rendering/server-routes-output-mode-server-external-dependencies.ts b/tests/e2e/tests/build/server-rendering/server-routes-output-mode-server-external-dependencies.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/build/server-rendering/server-routes-output-mode-server-external-dependencies.ts rename to tests/e2e/tests/build/server-rendering/server-routes-output-mode-server-external-dependencies.ts diff --git a/tests/legacy-cli/e2e/tests/build/server-rendering/server-routes-output-mode-server-i18n-base-href.ts b/tests/e2e/tests/build/server-rendering/server-routes-output-mode-server-i18n-base-href.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/build/server-rendering/server-routes-output-mode-server-i18n-base-href.ts rename to tests/e2e/tests/build/server-rendering/server-routes-output-mode-server-i18n-base-href.ts diff --git a/tests/legacy-cli/e2e/tests/build/server-rendering/server-routes-output-mode-server-i18n-sub-path.ts b/tests/e2e/tests/build/server-rendering/server-routes-output-mode-server-i18n-sub-path.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/build/server-rendering/server-routes-output-mode-server-i18n-sub-path.ts rename to tests/e2e/tests/build/server-rendering/server-routes-output-mode-server-i18n-sub-path.ts diff --git a/tests/legacy-cli/e2e/tests/build/server-rendering/server-routes-output-mode-server-i18n.ts b/tests/e2e/tests/build/server-rendering/server-routes-output-mode-server-i18n.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/build/server-rendering/server-routes-output-mode-server-i18n.ts rename to tests/e2e/tests/build/server-rendering/server-routes-output-mode-server-i18n.ts diff --git a/tests/legacy-cli/e2e/tests/build/server-rendering/server-routes-output-mode-server-platform-neutral.ts b/tests/e2e/tests/build/server-rendering/server-routes-output-mode-server-platform-neutral.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/build/server-rendering/server-routes-output-mode-server-platform-neutral.ts rename to tests/e2e/tests/build/server-rendering/server-routes-output-mode-server-platform-neutral.ts diff --git a/tests/legacy-cli/e2e/tests/build/server-rendering/server-routes-output-mode-server.ts b/tests/e2e/tests/build/server-rendering/server-routes-output-mode-server.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/build/server-rendering/server-routes-output-mode-server.ts rename to tests/e2e/tests/build/server-rendering/server-routes-output-mode-server.ts diff --git a/tests/legacy-cli/e2e/tests/build/server-rendering/server-routes-output-mode-static-http-calls.ts b/tests/e2e/tests/build/server-rendering/server-routes-output-mode-static-http-calls.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/build/server-rendering/server-routes-output-mode-static-http-calls.ts rename to tests/e2e/tests/build/server-rendering/server-routes-output-mode-static-http-calls.ts diff --git a/tests/legacy-cli/e2e/tests/build/server-rendering/server-routes-output-mode-static-i18n_APP_BASE_HREF.ts b/tests/e2e/tests/build/server-rendering/server-routes-output-mode-static-i18n_APP_BASE_HREF.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/build/server-rendering/server-routes-output-mode-static-i18n_APP_BASE_HREF.ts rename to tests/e2e/tests/build/server-rendering/server-routes-output-mode-static-i18n_APP_BASE_HREF.ts diff --git a/tests/legacy-cli/e2e/tests/build/server-rendering/server-routes-output-mode-static.ts b/tests/e2e/tests/build/server-rendering/server-routes-output-mode-static.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/build/server-rendering/server-routes-output-mode-static.ts rename to tests/e2e/tests/build/server-rendering/server-routes-output-mode-static.ts diff --git a/tests/legacy-cli/e2e/tests/build/server-rendering/server-routes-preload-links.ts b/tests/e2e/tests/build/server-rendering/server-routes-preload-links.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/build/server-rendering/server-routes-preload-links.ts rename to tests/e2e/tests/build/server-rendering/server-routes-preload-links.ts diff --git a/tests/legacy-cli/e2e/tests/build/sourcemap.ts b/tests/e2e/tests/build/sourcemap.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/build/sourcemap.ts rename to tests/e2e/tests/build/sourcemap.ts diff --git a/tests/legacy-cli/e2e/tests/build/styles/bootstrap.ts b/tests/e2e/tests/build/styles/bootstrap.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/build/styles/bootstrap.ts rename to tests/e2e/tests/build/styles/bootstrap.ts diff --git a/tests/legacy-cli/e2e/tests/build/styles/include-paths.ts b/tests/e2e/tests/build/styles/include-paths.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/build/styles/include-paths.ts rename to tests/e2e/tests/build/styles/include-paths.ts diff --git a/tests/legacy-cli/e2e/tests/build/styles/less.ts b/tests/e2e/tests/build/styles/less.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/build/styles/less.ts rename to tests/e2e/tests/build/styles/less.ts diff --git a/tests/legacy-cli/e2e/tests/build/styles/loaders.ts b/tests/e2e/tests/build/styles/loaders.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/build/styles/loaders.ts rename to tests/e2e/tests/build/styles/loaders.ts diff --git a/tests/legacy-cli/e2e/tests/build/styles/sass-pkg-importer.ts b/tests/e2e/tests/build/styles/sass-pkg-importer.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/build/styles/sass-pkg-importer.ts rename to tests/e2e/tests/build/styles/sass-pkg-importer.ts diff --git a/tests/legacy-cli/e2e/tests/build/styles/sass.ts b/tests/e2e/tests/build/styles/sass.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/build/styles/sass.ts rename to tests/e2e/tests/build/styles/sass.ts diff --git a/tests/legacy-cli/e2e/tests/build/styles/scss-partial-resolution.ts b/tests/e2e/tests/build/styles/scss-partial-resolution.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/build/styles/scss-partial-resolution.ts rename to tests/e2e/tests/build/styles/scss-partial-resolution.ts diff --git a/tests/legacy-cli/e2e/tests/build/styles/scss.ts b/tests/e2e/tests/build/styles/scss.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/build/styles/scss.ts rename to tests/e2e/tests/build/styles/scss.ts diff --git a/tests/legacy-cli/e2e/tests/build/styles/symlinked-global.ts b/tests/e2e/tests/build/styles/symlinked-global.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/build/styles/symlinked-global.ts rename to tests/e2e/tests/build/styles/symlinked-global.ts diff --git a/tests/legacy-cli/e2e/tests/build/styles/tailwind-v2.ts b/tests/e2e/tests/build/styles/tailwind-v2.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/build/styles/tailwind-v2.ts rename to tests/e2e/tests/build/styles/tailwind-v2.ts diff --git a/tests/legacy-cli/e2e/tests/build/styles/tailwind-v3-cjs.ts b/tests/e2e/tests/build/styles/tailwind-v3-cjs.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/build/styles/tailwind-v3-cjs.ts rename to tests/e2e/tests/build/styles/tailwind-v3-cjs.ts diff --git a/tests/legacy-cli/e2e/tests/build/styles/tailwind-v3.ts b/tests/e2e/tests/build/styles/tailwind-v3.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/build/styles/tailwind-v3.ts rename to tests/e2e/tests/build/styles/tailwind-v3.ts diff --git a/tests/legacy-cli/e2e/tests/build/ts-paths.ts b/tests/e2e/tests/build/ts-paths.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/build/ts-paths.ts rename to tests/e2e/tests/build/ts-paths.ts diff --git a/tests/legacy-cli/e2e/tests/build/ts-standard-decorators.ts b/tests/e2e/tests/build/ts-standard-decorators.ts similarity index 88% rename from tests/legacy-cli/e2e/tests/build/ts-standard-decorators.ts rename to tests/e2e/tests/build/ts-standard-decorators.ts index 07bf93e3b545..05d056675d3b 100644 --- a/tests/legacy-cli/e2e/tests/build/ts-standard-decorators.ts +++ b/tests/e2e/tests/build/ts-standard-decorators.ts @@ -1,6 +1,7 @@ import { getGlobalVariable } from '../../utils/env'; import { ng } from '../../utils/process'; import { updateJsonFile, updateTsConfig } from '../../utils/project'; +import { executeBrowserTest } from '../../utils/puppeteer'; export default async function () { // Update project to disable experimental decorators @@ -31,6 +32,6 @@ export default async function () { // Unit tests (JIT only) await ng('test', '--no-watch'); - // E2E tests to ensure application functions in a browser - await ng('e2e'); + // Ensure application functions in a browser + await executeBrowserTest(); } diff --git a/tests/legacy-cli/e2e/tests/build/wasm-esm.ts b/tests/e2e/tests/build/wasm-esm.ts similarity index 86% rename from tests/legacy-cli/e2e/tests/build/wasm-esm.ts rename to tests/e2e/tests/build/wasm-esm.ts index 8a9735172f1b..70633a1021c1 100644 --- a/tests/legacy-cli/e2e/tests/build/wasm-esm.ts +++ b/tests/e2e/tests/build/wasm-esm.ts @@ -11,6 +11,7 @@ import { ng } from '../../utils/process'; import { prependToFile, replaceInFile } from '../../utils/fs'; import { updateJsonFile, useSha } from '../../utils/project'; import { installWorkspacePackages } from '../../utils/packages'; +import { executeBrowserTest } from '../../utils/puppeteer'; /** * Compiled and base64 encoded WASM file for the following WAT: @@ -66,22 +67,7 @@ export default async function () { await ng('build'); // Update E2E test to check for WASM execution - await writeFile( - 'e2e/src/app.e2e-spec.ts', - ` - import { AppPage } from './app.po'; - import { browser, logging } from 'protractor'; - describe('WASM execution', () => { - it('should log WASM result messages', async () => { - const page = new AppPage(); - await page.navigateTo(); - expect(await page.getTitleText()).toEqual('Hello, 32'); - }); - }); - `, - ); - - await ng('e2e'); + await executeBrowserTest({ expectedTitleText: 'Hello, 32' }); // Setup prerendering and build to test Node.js functionality await ng('add', '@angular/ssr', '--skip-confirmation', '--skip-install'); diff --git a/tests/legacy-cli/e2e/tests/build/worker.ts b/tests/e2e/tests/build/worker.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/build/worker.ts rename to tests/e2e/tests/build/worker.ts diff --git a/tests/legacy-cli/e2e/tests/commands/add/add-material.ts b/tests/e2e/tests/commands/add/add-material.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/commands/add/add-material.ts rename to tests/e2e/tests/commands/add/add-material.ts diff --git a/tests/legacy-cli/e2e/tests/commands/add/add-pwa.ts b/tests/e2e/tests/commands/add/add-pwa.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/commands/add/add-pwa.ts rename to tests/e2e/tests/commands/add/add-pwa.ts diff --git a/tests/legacy-cli/e2e/tests/commands/add/add-tailwindcss.ts b/tests/e2e/tests/commands/add/add-tailwindcss.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/commands/add/add-tailwindcss.ts rename to tests/e2e/tests/commands/add/add-tailwindcss.ts diff --git a/tests/legacy-cli/e2e/tests/commands/add/add-version.ts b/tests/e2e/tests/commands/add/add-version.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/commands/add/add-version.ts rename to tests/e2e/tests/commands/add/add-version.ts diff --git a/tests/legacy-cli/e2e/tests/commands/add/add.ts b/tests/e2e/tests/commands/add/add.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/commands/add/add.ts rename to tests/e2e/tests/commands/add/add.ts diff --git a/tests/legacy-cli/e2e/tests/commands/add/base.ts b/tests/e2e/tests/commands/add/base.ts similarity index 86% rename from tests/legacy-cli/e2e/tests/commands/add/base.ts rename to tests/e2e/tests/commands/add/base.ts index f4e7048df6ac..d31210c6c242 100644 --- a/tests/legacy-cli/e2e/tests/commands/add/base.ts +++ b/tests/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/e2e/tests/commands/add/dir.ts b/tests/e2e/tests/commands/add/dir.ts new file mode 100644 index 000000000000..7cb00704cc8e --- /dev/null +++ b/tests/e2e/tests/commands/add/dir.ts @@ -0,0 +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 () { + 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/file.ts b/tests/e2e/tests/commands/add/file.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/commands/add/file.ts rename to tests/e2e/tests/commands/add/file.ts diff --git a/tests/legacy-cli/e2e/tests/commands/add/npm-config.ts b/tests/e2e/tests/commands/add/npm-config.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/commands/add/npm-config.ts rename to tests/e2e/tests/commands/add/npm-config.ts diff --git a/tests/e2e/tests/commands/add/peer.ts b/tests/e2e/tests/commands/add/peer.ts new file mode 100644 index 000000000000..143542e4533c --- /dev/null +++ b/tests/e2e/tests/commands/add/peer.ts @@ -0,0 +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'; + +export default async function (): Promise { + const warning = /Adding the package may not succeed/; + + 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.`, + ); + + const stdout2 = await runNgAdd('add-collection-dir'); + assert.doesNotMatch( + stdout2, + warning, + `Peer warning should NOT be shown for add-collection-dir but was.`, + ); + + 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.`, + ); +} + +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/registry-option.ts b/tests/e2e/tests/commands/add/registry-option.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/commands/add/registry-option.ts rename to tests/e2e/tests/commands/add/registry-option.ts diff --git a/tests/e2e/tests/commands/add/secure-registry.ts b/tests/e2e/tests/commands/add/secure-registry.ts new file mode 100644 index 000000000000..4a640607f8be --- /dev/null +++ b/tests/e2e/tests/commands/add/secure-registry.ts @@ -0,0 +1,50 @@ +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 () { + 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 === 'yarn'; + const command = ['add', '@angular/pwa', '--skip-confirmation']; + + // 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 expectFileNotToExist('public/manifest.webmanifest'); + + 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'); + + // 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)); + } + + await createNpmConfigForAuthentication(true, true); + await expectToFail(() => ng(...command)); + } finally { + process.env['NPM_CONFIG_REGISTRY'] = originalNpmConfigRegistry; + await git('clean', '-dxf'); + await installWorkspacePackages(); + } +} diff --git a/tests/legacy-cli/e2e/tests/commands/add/version-specifier.ts b/tests/e2e/tests/commands/add/version-specifier.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/commands/add/version-specifier.ts rename to tests/e2e/tests/commands/add/version-specifier.ts diff --git a/tests/legacy-cli/e2e/tests/commands/add/yarn-env-vars.ts b/tests/e2e/tests/commands/add/yarn-env-vars.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/commands/add/yarn-env-vars.ts rename to tests/e2e/tests/commands/add/yarn-env-vars.ts diff --git a/tests/legacy-cli/e2e/tests/commands/additional-properties.ts b/tests/e2e/tests/commands/additional-properties.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/commands/additional-properties.ts rename to tests/e2e/tests/commands/additional-properties.ts diff --git a/tests/legacy-cli/e2e/tests/commands/analytics/analytics-enable-disable.ts b/tests/e2e/tests/commands/analytics/analytics-enable-disable.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/commands/analytics/analytics-enable-disable.ts rename to tests/e2e/tests/commands/analytics/analytics-enable-disable.ts diff --git a/tests/legacy-cli/e2e/tests/commands/analytics/analytics-info.ts b/tests/e2e/tests/commands/analytics/analytics-info.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/commands/analytics/analytics-info.ts rename to tests/e2e/tests/commands/analytics/analytics-info.ts diff --git a/tests/legacy-cli/e2e/tests/commands/analytics/ask-analytics-command.ts b/tests/e2e/tests/commands/analytics/ask-analytics-command.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/commands/analytics/ask-analytics-command.ts rename to tests/e2e/tests/commands/analytics/ask-analytics-command.ts diff --git a/tests/legacy-cli/e2e/tests/commands/builder-not-found.ts b/tests/e2e/tests/commands/builder-not-found.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/commands/builder-not-found.ts rename to tests/e2e/tests/commands/builder-not-found.ts diff --git a/tests/legacy-cli/e2e/tests/commands/builder-project-by-cwd.ts b/tests/e2e/tests/commands/builder-project-by-cwd.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/commands/builder-project-by-cwd.ts rename to tests/e2e/tests/commands/builder-project-by-cwd.ts diff --git a/tests/legacy-cli/e2e/tests/commands/cache/cache-clean.ts b/tests/e2e/tests/commands/cache/cache-clean.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/commands/cache/cache-clean.ts rename to tests/e2e/tests/commands/cache/cache-clean.ts diff --git a/tests/legacy-cli/e2e/tests/commands/cache/cache-enable-disable.ts b/tests/e2e/tests/commands/cache/cache-enable-disable.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/commands/cache/cache-enable-disable.ts rename to tests/e2e/tests/commands/cache/cache-enable-disable.ts diff --git a/tests/legacy-cli/e2e/tests/commands/cache/cache-info.ts b/tests/e2e/tests/commands/cache/cache-info.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/commands/cache/cache-info.ts rename to tests/e2e/tests/commands/cache/cache-info.ts diff --git a/tests/legacy-cli/e2e/tests/commands/completion/completion-prompt.ts b/tests/e2e/tests/commands/completion/completion-prompt.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/commands/completion/completion-prompt.ts rename to tests/e2e/tests/commands/completion/completion-prompt.ts diff --git a/tests/legacy-cli/e2e/tests/commands/completion/completion-script.ts b/tests/e2e/tests/commands/completion/completion-script.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/commands/completion/completion-script.ts rename to tests/e2e/tests/commands/completion/completion-script.ts diff --git a/tests/legacy-cli/e2e/tests/commands/completion/completion.ts b/tests/e2e/tests/commands/completion/completion.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/commands/completion/completion.ts rename to tests/e2e/tests/commands/completion/completion.ts diff --git a/tests/legacy-cli/e2e/tests/commands/config/config-get.ts b/tests/e2e/tests/commands/config/config-get.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/commands/config/config-get.ts rename to tests/e2e/tests/commands/config/config-get.ts diff --git a/tests/legacy-cli/e2e/tests/commands/config/config-global-validation.ts b/tests/e2e/tests/commands/config/config-global-validation.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/commands/config/config-global-validation.ts rename to tests/e2e/tests/commands/config/config-global-validation.ts diff --git a/tests/legacy-cli/e2e/tests/commands/config/config-global.ts b/tests/e2e/tests/commands/config/config-global.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/commands/config/config-global.ts rename to tests/e2e/tests/commands/config/config-global.ts diff --git a/tests/legacy-cli/e2e/tests/commands/config/config-set-enum-check.ts b/tests/e2e/tests/commands/config/config-set-enum-check.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/commands/config/config-set-enum-check.ts rename to tests/e2e/tests/commands/config/config-set-enum-check.ts diff --git a/tests/legacy-cli/e2e/tests/commands/config/config-set-prefix.ts b/tests/e2e/tests/commands/config/config-set-prefix.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/commands/config/config-set-prefix.ts rename to tests/e2e/tests/commands/config/config-set-prefix.ts diff --git a/tests/legacy-cli/e2e/tests/commands/config/config-set-serve-port.ts b/tests/e2e/tests/commands/config/config-set-serve-port.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/commands/config/config-set-serve-port.ts rename to tests/e2e/tests/commands/config/config-set-serve-port.ts diff --git a/tests/legacy-cli/e2e/tests/commands/config/config-set.ts b/tests/e2e/tests/commands/config/config-set.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/commands/config/config-set.ts rename to tests/e2e/tests/commands/config/config-set.ts diff --git a/tests/legacy-cli/e2e/tests/commands/help/help-hidden.ts b/tests/e2e/tests/commands/help/help-hidden.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/commands/help/help-hidden.ts rename to tests/e2e/tests/commands/help/help-hidden.ts diff --git a/tests/legacy-cli/e2e/tests/commands/help/help-json.ts b/tests/e2e/tests/commands/help/help-json.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/commands/help/help-json.ts rename to tests/e2e/tests/commands/help/help-json.ts diff --git a/tests/legacy-cli/e2e/tests/commands/ng-new-collection.ts b/tests/e2e/tests/commands/ng-new-collection.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/commands/ng-new-collection.ts rename to tests/e2e/tests/commands/ng-new-collection.ts diff --git a/tests/legacy-cli/e2e/tests/commands/project-cannot-be-determined-by-cwd.ts b/tests/e2e/tests/commands/project-cannot-be-determined-by-cwd.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/commands/project-cannot-be-determined-by-cwd.ts rename to tests/e2e/tests/commands/project-cannot-be-determined-by-cwd.ts diff --git a/tests/legacy-cli/e2e/tests/commands/run-configuration-option.ts b/tests/e2e/tests/commands/run-configuration-option.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/commands/run-configuration-option.ts rename to tests/e2e/tests/commands/run-configuration-option.ts diff --git a/tests/legacy-cli/e2e/tests/commands/serve/assets.ts b/tests/e2e/tests/commands/serve/assets.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/commands/serve/assets.ts rename to tests/e2e/tests/commands/serve/assets.ts diff --git a/tests/legacy-cli/e2e/tests/commands/serve/head-request.ts b/tests/e2e/tests/commands/serve/head-request.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/commands/serve/head-request.ts rename to tests/e2e/tests/commands/serve/head-request.ts diff --git a/tests/legacy-cli/e2e/tests/commands/serve/preflight-request.ts b/tests/e2e/tests/commands/serve/preflight-request.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/commands/serve/preflight-request.ts rename to tests/e2e/tests/commands/serve/preflight-request.ts diff --git a/tests/legacy-cli/e2e/tests/commands/serve/reload-shims.ts b/tests/e2e/tests/commands/serve/reload-shims.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/commands/serve/reload-shims.ts rename to tests/e2e/tests/commands/serve/reload-shims.ts diff --git a/tests/legacy-cli/e2e/tests/commands/serve/serve-path.ts b/tests/e2e/tests/commands/serve/serve-path.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/commands/serve/serve-path.ts rename to tests/e2e/tests/commands/serve/serve-path.ts diff --git a/tests/legacy-cli/e2e/tests/commands/serve/ssr-http-requests-assets.ts b/tests/e2e/tests/commands/serve/ssr-http-requests-assets.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/commands/serve/ssr-http-requests-assets.ts rename to tests/e2e/tests/commands/serve/ssr-http-requests-assets.ts diff --git a/tests/legacy-cli/e2e/tests/commands/unknown-configuration.ts b/tests/e2e/tests/commands/unknown-configuration.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/commands/unknown-configuration.ts rename to tests/e2e/tests/commands/unknown-configuration.ts diff --git a/tests/legacy-cli/e2e/tests/commands/unknown-option.ts b/tests/e2e/tests/commands/unknown-option.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/commands/unknown-option.ts rename to tests/e2e/tests/commands/unknown-option.ts diff --git a/tests/legacy-cli/e2e/tests/generate/application/application-no-zoneless-ng-module.ts b/tests/e2e/tests/generate/application/application-no-zoneless-ng-module.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/generate/application/application-no-zoneless-ng-module.ts rename to tests/e2e/tests/generate/application/application-no-zoneless-ng-module.ts diff --git a/tests/legacy-cli/e2e/tests/generate/application/application-no-zoneless-standalone.ts b/tests/e2e/tests/generate/application/application-no-zoneless-standalone.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/generate/application/application-no-zoneless-standalone.ts rename to tests/e2e/tests/generate/application/application-no-zoneless-standalone.ts diff --git a/tests/legacy-cli/e2e/tests/generate/application/application-zoneless-ng-module.ts b/tests/e2e/tests/generate/application/application-zoneless-ng-module.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/generate/application/application-zoneless-ng-module.ts rename to tests/e2e/tests/generate/application/application-zoneless-ng-module.ts diff --git a/tests/legacy-cli/e2e/tests/generate/application/application-zoneless-standalone.ts b/tests/e2e/tests/generate/application/application-zoneless-standalone.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/generate/application/application-zoneless-standalone.ts rename to tests/e2e/tests/generate/application/application-zoneless-standalone.ts diff --git a/tests/legacy-cli/e2e/tests/generate/component/component-basic.ts b/tests/e2e/tests/generate/component/component-basic.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/generate/component/component-basic.ts rename to tests/e2e/tests/generate/component/component-basic.ts diff --git a/tests/legacy-cli/e2e/tests/generate/component/component-child-dir.ts b/tests/e2e/tests/generate/component/component-child-dir.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/generate/component/component-child-dir.ts rename to tests/e2e/tests/generate/component/component-child-dir.ts diff --git a/tests/legacy-cli/e2e/tests/generate/component/component-flat.ts b/tests/e2e/tests/generate/component/component-flat.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/generate/component/component-flat.ts rename to tests/e2e/tests/generate/component/component-flat.ts diff --git a/tests/legacy-cli/e2e/tests/generate/component/component-inline-template.ts b/tests/e2e/tests/generate/component/component-inline-template.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/generate/component/component-inline-template.ts rename to tests/e2e/tests/generate/component/component-inline-template.ts diff --git a/tests/legacy-cli/e2e/tests/generate/component/component-not-flat.ts b/tests/e2e/tests/generate/component/component-not-flat.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/generate/component/component-not-flat.ts rename to tests/e2e/tests/generate/component/component-not-flat.ts diff --git a/tests/legacy-cli/e2e/tests/generate/component/component-path-case.ts b/tests/e2e/tests/generate/component/component-path-case.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/generate/component/component-path-case.ts rename to tests/e2e/tests/generate/component/component-path-case.ts diff --git a/tests/legacy-cli/e2e/tests/generate/component/component-prefix.ts b/tests/e2e/tests/generate/component/component-prefix.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/generate/component/component-prefix.ts rename to tests/e2e/tests/generate/component/component-prefix.ts diff --git a/tests/legacy-cli/e2e/tests/generate/config/type-browserslist.ts b/tests/e2e/tests/generate/config/type-browserslist.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/generate/config/type-browserslist.ts rename to tests/e2e/tests/generate/config/type-browserslist.ts diff --git a/tests/legacy-cli/e2e/tests/generate/config/type-karma.ts b/tests/e2e/tests/generate/config/type-karma.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/generate/config/type-karma.ts rename to tests/e2e/tests/generate/config/type-karma.ts diff --git a/tests/legacy-cli/e2e/tests/generate/directive/directive-basic.ts b/tests/e2e/tests/generate/directive/directive-basic.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/generate/directive/directive-basic.ts rename to tests/e2e/tests/generate/directive/directive-basic.ts diff --git a/tests/legacy-cli/e2e/tests/generate/directive/directive-prefix.ts b/tests/e2e/tests/generate/directive/directive-prefix.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/generate/directive/directive-prefix.ts rename to tests/e2e/tests/generate/directive/directive-prefix.ts diff --git a/tests/legacy-cli/e2e/tests/generate/generate-error.ts b/tests/e2e/tests/generate/generate-error.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/generate/generate-error.ts rename to tests/e2e/tests/generate/generate-error.ts diff --git a/tests/legacy-cli/e2e/tests/generate/generate-name-check.ts b/tests/e2e/tests/generate/generate-name-check.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/generate/generate-name-check.ts rename to tests/e2e/tests/generate/generate-name-check.ts diff --git a/tests/legacy-cli/e2e/tests/generate/guard/guard-basic.ts b/tests/e2e/tests/generate/guard/guard-basic.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/generate/guard/guard-basic.ts rename to tests/e2e/tests/generate/guard/guard-basic.ts diff --git a/tests/legacy-cli/e2e/tests/generate/guard/guard-implements.ts b/tests/e2e/tests/generate/guard/guard-implements.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/generate/guard/guard-implements.ts rename to tests/e2e/tests/generate/guard/guard-implements.ts diff --git a/tests/legacy-cli/e2e/tests/generate/guard/guard-multiple-implements.ts b/tests/e2e/tests/generate/guard/guard-multiple-implements.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/generate/guard/guard-multiple-implements.ts rename to tests/e2e/tests/generate/guard/guard-multiple-implements.ts diff --git a/tests/legacy-cli/e2e/tests/generate/help-output-no-duplicates.ts b/tests/e2e/tests/generate/help-output-no-duplicates.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/generate/help-output-no-duplicates.ts rename to tests/e2e/tests/generate/help-output-no-duplicates.ts diff --git a/tests/legacy-cli/e2e/tests/generate/help-output.ts b/tests/e2e/tests/generate/help-output.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/generate/help-output.ts rename to tests/e2e/tests/generate/help-output.ts diff --git a/tests/legacy-cli/e2e/tests/generate/install-allow-scripts.ts b/tests/e2e/tests/generate/install-allow-scripts.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/generate/install-allow-scripts.ts rename to tests/e2e/tests/generate/install-allow-scripts.ts diff --git a/tests/legacy-cli/e2e/tests/generate/interceptor/interceptor-basic.ts b/tests/e2e/tests/generate/interceptor/interceptor-basic.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/generate/interceptor/interceptor-basic.ts rename to tests/e2e/tests/generate/interceptor/interceptor-basic.ts diff --git a/tests/legacy-cli/e2e/tests/generate/library/library-basic.ts b/tests/e2e/tests/generate/library/library-basic.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/generate/library/library-basic.ts rename to tests/e2e/tests/generate/library/library-basic.ts diff --git a/tests/legacy-cli/e2e/tests/generate/library/library-standalone.ts b/tests/e2e/tests/generate/library/library-standalone.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/generate/library/library-standalone.ts rename to tests/e2e/tests/generate/library/library-standalone.ts diff --git a/tests/legacy-cli/e2e/tests/generate/module/module-basic.ts b/tests/e2e/tests/generate/module/module-basic.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/generate/module/module-basic.ts rename to tests/e2e/tests/generate/module/module-basic.ts diff --git a/tests/legacy-cli/e2e/tests/generate/module/module-import.ts b/tests/e2e/tests/generate/module/module-import.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/generate/module/module-import.ts rename to tests/e2e/tests/generate/module/module-import.ts diff --git a/tests/legacy-cli/e2e/tests/generate/module/module-routing-child-folder.ts b/tests/e2e/tests/generate/module/module-routing-child-folder.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/generate/module/module-routing-child-folder.ts rename to tests/e2e/tests/generate/module/module-routing-child-folder.ts diff --git a/tests/legacy-cli/e2e/tests/generate/pipe/pipe-basic.ts b/tests/e2e/tests/generate/pipe/pipe-basic.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/generate/pipe/pipe-basic.ts rename to tests/e2e/tests/generate/pipe/pipe-basic.ts diff --git a/tests/legacy-cli/e2e/tests/generate/schematic-aliases.ts b/tests/e2e/tests/generate/schematic-aliases.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/generate/schematic-aliases.ts rename to tests/e2e/tests/generate/schematic-aliases.ts diff --git a/tests/legacy-cli/e2e/tests/generate/schematic-defaults.ts b/tests/e2e/tests/generate/schematic-defaults.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/generate/schematic-defaults.ts rename to tests/e2e/tests/generate/schematic-defaults.ts diff --git a/tests/legacy-cli/e2e/tests/generate/schematic-force-override.ts b/tests/e2e/tests/generate/schematic-force-override.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/generate/schematic-force-override.ts rename to tests/e2e/tests/generate/schematic-force-override.ts diff --git a/tests/legacy-cli/e2e/tests/generate/schematics-collections-relative.ts b/tests/e2e/tests/generate/schematics-collections-relative.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/generate/schematics-collections-relative.ts rename to tests/e2e/tests/generate/schematics-collections-relative.ts diff --git a/tests/legacy-cli/e2e/tests/generate/schematics-collections.ts b/tests/e2e/tests/generate/schematics-collections.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/generate/schematics-collections.ts rename to tests/e2e/tests/generate/schematics-collections.ts diff --git a/tests/legacy-cli/e2e/tests/generate/service/service-basic.ts b/tests/e2e/tests/generate/service/service-basic.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/generate/service/service-basic.ts rename to tests/e2e/tests/generate/service/service-basic.ts diff --git a/tests/legacy-cli/e2e/tests/i18n/extract-ivy-disk-cache.ts b/tests/e2e/tests/i18n/extract-ivy-disk-cache.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/i18n/extract-ivy-disk-cache.ts rename to tests/e2e/tests/i18n/extract-ivy-disk-cache.ts diff --git a/tests/legacy-cli/e2e/tests/i18n/extract-ivy-libraries.ts b/tests/e2e/tests/i18n/extract-ivy-libraries.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/i18n/extract-ivy-libraries.ts rename to tests/e2e/tests/i18n/extract-ivy-libraries.ts diff --git a/tests/legacy-cli/e2e/tests/i18n/extract-ivy.ts b/tests/e2e/tests/i18n/extract-ivy.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/i18n/extract-ivy.ts rename to tests/e2e/tests/i18n/extract-ivy.ts diff --git a/tests/legacy-cli/e2e/tests/i18n/ivy-localize-app-shell-service-worker.ts b/tests/e2e/tests/i18n/ivy-localize-app-shell-service-worker.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/i18n/ivy-localize-app-shell-service-worker.ts rename to tests/e2e/tests/i18n/ivy-localize-app-shell-service-worker.ts diff --git a/tests/legacy-cli/e2e/tests/i18n/ivy-localize-app-shell.ts b/tests/e2e/tests/i18n/ivy-localize-app-shell.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/i18n/ivy-localize-app-shell.ts rename to tests/e2e/tests/i18n/ivy-localize-app-shell.ts diff --git a/tests/legacy-cli/e2e/tests/i18n/ivy-localize-basehref-absolute.ts b/tests/e2e/tests/i18n/ivy-localize-basehref-absolute.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/i18n/ivy-localize-basehref-absolute.ts rename to tests/e2e/tests/i18n/ivy-localize-basehref-absolute.ts diff --git a/tests/legacy-cli/e2e/tests/i18n/ivy-localize-basehref.ts b/tests/e2e/tests/i18n/ivy-localize-basehref.ts similarity index 77% rename from tests/legacy-cli/e2e/tests/i18n/ivy-localize-basehref.ts rename to tests/e2e/tests/i18n/ivy-localize-basehref.ts index d14c14307ab1..4bbd7dece3ee 100644 --- a/tests/legacy-cli/e2e/tests/i18n/ivy-localize-basehref.ts +++ b/tests/e2e/tests/i18n/ivy-localize-basehref.ts @@ -9,7 +9,14 @@ import { expectFileToMatch } from '../../utils/fs'; import { ng } from '../../utils/process'; import { updateJsonFile } from '../../utils/project'; -import { baseHrefs, externalServer, langTranslations, setupI18nConfig } from './setup'; +import { executeBrowserTest } from '../../utils/puppeteer'; +import { + baseHrefs, + browserCheck, + externalServer, + langTranslations, + setupI18nConfig, +} from './setup'; export default async function () { // Setup i18n tests and config. @@ -50,18 +57,12 @@ export default async function () { await expectFileToMatch(`${outputPath}/index.html`, `href="${baseHrefs[lang] || '/'}"`); // Execute Application E2E tests for a production build without dev server - const { server, port, url } = await externalServer( - outputPath, - (baseHrefs[lang] as string) || '/', - ); + const { server, url } = await externalServer(outputPath, (baseHrefs[lang] as string) || '/'); try { - await ng( - 'e2e', - `--port=${port}`, - `--configuration=${lang}`, - '--dev-server-target=', - `--base-url=${url}`, - ); + await executeBrowserTest({ + baseUrl: url, + checkFn: (page) => browserCheck(page, lang), + }); } finally { server.close(); } @@ -81,18 +82,12 @@ export default async function () { await expectFileToMatch(`${outputPath}/index.html`, `href="/test${baseHrefs[lang] || '/'}"`); // Execute Application E2E tests for a production build without dev server - const { server, port, url } = await externalServer( - outputPath, - '/test' + (baseHrefs[lang] || '/'), - ); + const { server, url } = await externalServer(outputPath, '/test' + (baseHrefs[lang] || '/')); try { - await ng( - 'e2e', - `--port=${port}`, - `--configuration=${lang}`, - '--dev-server-target=', - `--base-url=${url}`, - ); + await executeBrowserTest({ + baseUrl: url, + checkFn: (page) => browserCheck(page, lang), + }); } finally { server.close(); } diff --git a/tests/e2e/tests/i18n/ivy-localize-es2015-e2e.ts b/tests/e2e/tests/i18n/ivy-localize-es2015-e2e.ts new file mode 100644 index 000000000000..d1f7ac8e28b8 --- /dev/null +++ b/tests/e2e/tests/i18n/ivy-localize-es2015-e2e.ts @@ -0,0 +1,14 @@ +import { executeBrowserTest } from '../../utils/puppeteer'; +import { browserCheck, langTranslations, setupI18nConfig } from './setup'; + +export default async function () { + // Setup i18n tests and config. + await setupI18nConfig(); + + for (const { lang } of langTranslations) { + await executeBrowserTest({ + configuration: lang, + checkFn: (page) => browserCheck(page, lang), + }); + } +} diff --git a/tests/legacy-cli/e2e/tests/i18n/ivy-localize-es2015.ts b/tests/e2e/tests/i18n/ivy-localize-es2015.ts similarity index 80% rename from tests/legacy-cli/e2e/tests/i18n/ivy-localize-es2015.ts rename to tests/e2e/tests/i18n/ivy-localize-es2015.ts index b8425795cab4..cea87a75f2b8 100644 --- a/tests/legacy-cli/e2e/tests/i18n/ivy-localize-es2015.ts +++ b/tests/e2e/tests/i18n/ivy-localize-es2015.ts @@ -1,8 +1,9 @@ import { getGlobalVariable } from '../../utils/env'; import { expectFileToMatch } from '../../utils/fs'; import { ng } from '../../utils/process'; +import { executeBrowserTest } from '../../utils/puppeteer'; import { expectToFail } from '../../utils/utils'; -import { externalServer, langTranslations, setupI18nConfig } from './setup'; +import { browserCheck, externalServer, langTranslations, setupI18nConfig } from './setup'; export default async function () { // Setup i18n tests and config. @@ -32,15 +33,12 @@ export default async function () { await expectFileToMatch(`${outputPath}/index.html`, `lang="${lang}"`); // Execute Application E2E tests for a production build without dev server - const { server, port, url } = await externalServer(outputPath, `/${lang}/`); + const { server, url } = await externalServer(outputPath, `/${lang}/`); try { - await ng( - 'e2e', - `--port=${port}`, - `--configuration=${lang}`, - '--dev-server-target=', - `--base-url=${url}`, - ); + await executeBrowserTest({ + baseUrl: url, + checkFn: (page) => browserCheck(page, lang), + }); } finally { server.close(); } diff --git a/tests/legacy-cli/e2e/tests/i18n/ivy-localize-hashes.ts b/tests/e2e/tests/i18n/ivy-localize-hashes.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/i18n/ivy-localize-hashes.ts rename to tests/e2e/tests/i18n/ivy-localize-hashes.ts diff --git a/tests/legacy-cli/e2e/tests/i18n/ivy-localize-locale-data-augment.ts b/tests/e2e/tests/i18n/ivy-localize-locale-data-augment.ts similarity index 57% rename from tests/legacy-cli/e2e/tests/i18n/ivy-localize-locale-data-augment.ts rename to tests/e2e/tests/i18n/ivy-localize-locale-data-augment.ts index e2c408a74c69..b4c0ae72ced4 100644 --- a/tests/legacy-cli/e2e/tests/i18n/ivy-localize-locale-data-augment.ts +++ b/tests/e2e/tests/i18n/ivy-localize-locale-data-augment.ts @@ -1,14 +1,9 @@ import { getGlobalVariable } from '../../utils/env'; -import { - expectFileToMatch, - prependToFile, - readFile, - replaceInFile, - writeFile, -} from '../../utils/fs'; +import { expectFileToMatch, prependToFile, readFile, writeFile } from '../../utils/fs'; import { ng } from '../../utils/process'; import { updateJsonFile } from '../../utils/project'; -import { langTranslations, setupI18nConfig } from './setup'; +import { executeBrowserTest } from '../../utils/puppeteer'; +import { browserCheck, langTranslations, setupI18nConfig } from './setup'; export default async function () { // Setup i18n tests and config. @@ -20,9 +15,6 @@ export default async function () { appProject.architect['build'].options.localize = ['fr']; }); - // Update E2E test to look for augmented locale data - await replaceInFile('./e2e/src/app.fr.e2e-spec.ts', 'janvier', 'changed-janvier'); - // Augment the locale data and import into the main application file const localeData = await readFile('node_modules/@angular/common/locales/global/fr.js'); await writeFile('src/fr-changed.js', localeData.replace('janvier', 'changed-janvier')); @@ -45,6 +37,27 @@ export default async function () { } // Execute Application E2E tests with dev server - await ng('e2e', `--configuration=${lang}`, '--port=0'); + await executeBrowserTest({ + configuration: lang, + checkFn: async (page) => { + // Run standard checks but expect failure on date + try { + await browserCheck(page, lang); + throw new Error('Expected browserCheck to fail due to modified locale data'); + } catch (e) { + if (!(e instanceof Error) || !e.message.includes("Expected 'date' to be")) { + throw e; + } + } + + // Verify the modified date + const getParagraph = async (id: string) => + page.$eval(`p#${id}`, (el) => el.textContent?.trim()); + const date = await getParagraph('date'); + if (date !== 'changed-janvier') { + throw new Error(`Expected 'date' to be 'changed-janvier', but got '${date}'.`); + } + }, + }); } } diff --git a/tests/legacy-cli/e2e/tests/i18n/ivy-localize-locale-data.ts b/tests/e2e/tests/i18n/ivy-localize-locale-data.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/i18n/ivy-localize-locale-data.ts rename to tests/e2e/tests/i18n/ivy-localize-locale-data.ts diff --git a/tests/legacy-cli/e2e/tests/i18n/ivy-localize-merging.ts b/tests/e2e/tests/i18n/ivy-localize-merging.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/i18n/ivy-localize-merging.ts rename to tests/e2e/tests/i18n/ivy-localize-merging.ts diff --git a/tests/legacy-cli/e2e/tests/i18n/ivy-localize-sourcelocale.ts b/tests/e2e/tests/i18n/ivy-localize-sourcelocale.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/i18n/ivy-localize-sourcelocale.ts rename to tests/e2e/tests/i18n/ivy-localize-sourcelocale.ts diff --git a/tests/legacy-cli/e2e/tests/i18n/ivy-localize-sourcemaps.ts b/tests/e2e/tests/i18n/ivy-localize-sourcemaps.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/i18n/ivy-localize-sourcemaps.ts rename to tests/e2e/tests/i18n/ivy-localize-sourcemaps.ts diff --git a/tests/legacy-cli/e2e/tests/i18n/ivy-localize-ssr.ts b/tests/e2e/tests/i18n/ivy-localize-ssr.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/i18n/ivy-localize-ssr.ts rename to tests/e2e/tests/i18n/ivy-localize-ssr.ts diff --git a/tests/legacy-cli/e2e/tests/i18n/ivy-localize-sub-path.ts b/tests/e2e/tests/i18n/ivy-localize-sub-path.ts similarity index 83% rename from tests/legacy-cli/e2e/tests/i18n/ivy-localize-sub-path.ts rename to tests/e2e/tests/i18n/ivy-localize-sub-path.ts index d6640534c45f..6116c438a49f 100644 --- a/tests/legacy-cli/e2e/tests/i18n/ivy-localize-sub-path.ts +++ b/tests/e2e/tests/i18n/ivy-localize-sub-path.ts @@ -10,7 +10,8 @@ import { join } from 'node:path'; import { expectFileToMatch } from '../../utils/fs'; import { ng } from '../../utils/process'; import { updateJsonFile } from '../../utils/project'; -import { baseDir, baseHrefs, externalServer, langTranslations, setupI18nConfig } from './setup'; +import { executeBrowserTest } from '../../utils/puppeteer'; +import { baseDir, browserCheck, externalServer, langTranslations, setupI18nConfig } from './setup'; export default async function () { // Setup i18n tests and config. @@ -56,16 +57,13 @@ export default async function () { await expectFileToMatch(`${outputPath}/index.html`, `href="${baseHref}"`); // Execute Application E2E tests for a production build without dev server - const { server, port, url } = await externalServer(outputPath, baseHref); + const { server, url } = await externalServer(outputPath, baseHref); try { - await ng( - 'e2e', - `--port=${port}`, - `--configuration=${lang}`, - '--dev-server-target=', - `--base-url=${url}`, - ); + await executeBrowserTest({ + baseUrl: url, + checkFn: (page) => browserCheck(page, lang), + }); } finally { server.close(); } diff --git a/tests/legacy-cli/e2e/tests/i18n/setup.ts b/tests/e2e/tests/i18n/setup.ts similarity index 84% rename from tests/legacy-cli/e2e/tests/i18n/setup.ts rename to tests/e2e/tests/i18n/setup.ts index 99d71f9bbe4b..8eade4e2783c 100644 --- a/tests/legacy-cli/e2e/tests/i18n/setup.ts +++ b/tests/e2e/tests/i18n/setup.ts @@ -7,6 +7,7 @@ import { updateJsonFile } from '../../utils/project'; import { readNgVersion } from '../../utils/version'; import { Server } from 'node:http'; import { AddressInfo } from 'node:net'; +import type { Page } from 'puppeteer'; // Configurations for each locale. const translationFile = 'src/locale/messages.xlf'; @@ -61,6 +62,35 @@ export const langTranslations = [ ]; export const sourceLocale = langTranslations[0].lang; +export async function browserCheck(page: Page, lang: string) { + const translation = langTranslations.find((t) => t.lang === lang)?.translation; + if (!translation) { + throw new Error(`Could not find translation for language '${lang}'`); + } + + const getParagraph = async (id: string) => page.$eval(`p#${id}`, (el) => el.textContent?.trim()); + + const hello = await getParagraph('hello'); + if (hello !== translation.hello) { + throw new Error(`Expected 'hello' to be '${translation.hello}', but got '${hello}'.`); + } + + const locale = await getParagraph('locale'); + if (locale !== lang) { + throw new Error(`Expected 'locale' to be '${lang}', but got '${locale}'.`); + } + + const date = await getParagraph('date'); + if (date !== translation.date) { + throw new Error(`Expected 'date' to be '${translation.date}', but got '${date}'.`); + } + + const plural = await getParagraph('plural'); + if (plural !== translation.plural) { + throw new Error(`Expected 'plural' to be '${translation.plural}', but got '${plural}'.`); + } +} + export interface ExternalServer { readonly server: Server; readonly port: number; @@ -173,47 +203,12 @@ export async function setupI18nConfig() { `, ); - // Add e2e specs for each lang. - for (const { lang, translation } of langTranslations) { - await writeFile( - `./e2e/src/app.${lang}.e2e-spec.ts`, - ` - import { browser, logging, element, by } from 'protractor'; - - describe('workspace-project App', () => { - const getParagraph = async (name: string) => element(by.css('app-root p#' + name)).getText(); - beforeEach(() => browser.get(browser.baseUrl)); - afterEach(async () => { - // Assert that there are no errors emitted from the browser - const logs = await browser.manage().logs().get(logging.Type.BROWSER); - expect(logs).not.toContain(jasmine.objectContaining({ - level: logging.Level.SEVERE, - } as logging.Entry)); - }); - - it('should display welcome message', async () => - expect(await getParagraph('hello')).toEqual('${translation.hello}')); - - it('should display locale', async () => - expect(await getParagraph('locale')).toEqual('${lang}')); - - it('should display localized date', async () => - expect(await getParagraph('date')).toEqual('${translation.date}')); - - it('should display pluralized message', async () => - expect(await getParagraph('plural')).toEqual('${translation.plural}')); - }); - `, - ); - } - // Update angular.json to build, serve, and test each locale. await updateJsonFile('angular.json', (workspaceJson) => { const appProject = workspaceJson.projects['test-project']; const appArchitect = workspaceJson.projects['test-project'].architect; const buildConfigs = appArchitect['build'].configurations; const serveConfigs = appArchitect['serve'].configurations; - const e2eConfigs = appArchitect['e2e'].configurations; appArchitect['build'].defaultConfiguration = undefined; @@ -245,10 +240,6 @@ export async function setupI18nConfig() { buildConfigs[lang] = { localize: [lang] }; serveConfigs[lang] = { buildTarget: `test-project:build:${lang}` }; - e2eConfigs[lang] = { - specs: [`./src/app.${lang}.e2e-spec.ts`], - devServerTarget: `test-project:serve:${lang}`, - }; } }); diff --git a/tests/legacy-cli/e2e/tests/jest/aot.ts b/tests/e2e/tests/jest/aot.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/jest/aot.ts rename to tests/e2e/tests/jest/aot.ts diff --git a/tests/legacy-cli/e2e/tests/jest/basic.ts b/tests/e2e/tests/jest/basic.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/jest/basic.ts rename to tests/e2e/tests/jest/basic.ts diff --git a/tests/legacy-cli/e2e/tests/jest/custom-config.ts b/tests/e2e/tests/jest/custom-config.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/jest/custom-config.ts rename to tests/e2e/tests/jest/custom-config.ts diff --git a/tests/legacy-cli/e2e/tests/jest/no-zoneless.ts b/tests/e2e/tests/jest/no-zoneless.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/jest/no-zoneless.ts rename to tests/e2e/tests/jest/no-zoneless.ts diff --git a/tests/legacy-cli/e2e/tests/mcp/ai-tutor.ts b/tests/e2e/tests/mcp/ai-tutor.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/mcp/ai-tutor.ts rename to tests/e2e/tests/mcp/ai-tutor.ts diff --git a/tests/legacy-cli/e2e/tests/mcp/best-practices.ts b/tests/e2e/tests/mcp/best-practices.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/mcp/best-practices.ts rename to tests/e2e/tests/mcp/best-practices.ts diff --git a/tests/e2e/tests/mcp/find-examples-basic.ts b/tests/e2e/tests/mcp/find-examples-basic.ts new file mode 100644 index 000000000000..b7f42045076c --- /dev/null +++ b/tests/e2e/tests/mcp/find-examples-basic.ts @@ -0,0 +1,48 @@ +import { exec, ProcessOutput, silentNpm } from '../../utils/process'; +import assert from 'node:assert/strict'; + +const MCP_INSPECTOR_PACKAGE_NAME = '@modelcontextprotocol/inspector-cli'; +const MCP_INSPECTOR_PACKAGE_VERSION = '0.16.2'; +const MCP_INSPECTOR_COMMAND_NAME = 'mcp-inspector-cli'; + +async function runInspector(...args: string[]): Promise { + const result = await exec( + MCP_INSPECTOR_COMMAND_NAME, + '--cli', + 'npx', + '--no', + '@angular/cli', + 'mcp', + ...args, + ); + + return result; +} + +export default async function () { + const [nodeMajor, nodeMinor] = process.versions.node.split('.', 2).map(Number); + if (nodeMajor < 22 || (nodeMajor === 22 && nodeMinor < 16)) { + console.log('Test bypassed: find_examples tool requires Node.js 22.16 or higher.'); + + return; + } + + await silentNpm( + 'install', + '--ignore-scripts', + '-g', + `${MCP_INSPECTOR_PACKAGE_NAME}@${MCP_INSPECTOR_PACKAGE_VERSION}`, + ); + + // Ensure `get_best_practices` returns the markdown content + const { stdout: stdoutInsideWorkspace } = await runInspector( + '--method', + 'tools/call', + '--tool-name', + 'find_examples', + '--tool-arg', + 'query=if', + ); + + assert.match(stdoutInsideWorkspace, /Using the @if Built-in Control Flow Block/); +} diff --git a/tests/legacy-cli/e2e/tests/mcp/registers-tools.ts b/tests/e2e/tests/mcp/registers-tools.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/mcp/registers-tools.ts rename to tests/e2e/tests/mcp/registers-tools.ts diff --git a/tests/legacy-cli/e2e/tests/misc/ask-missing-builder.ts b/tests/e2e/tests/misc/ask-missing-builder.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/misc/ask-missing-builder.ts rename to tests/e2e/tests/misc/ask-missing-builder.ts diff --git a/tests/legacy-cli/e2e/tests/misc/browsers.ts b/tests/e2e/tests/misc/browsers.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/misc/browsers.ts rename to tests/e2e/tests/misc/browsers.ts diff --git a/tests/legacy-cli/e2e/tests/misc/check-postinstalls.ts b/tests/e2e/tests/misc/check-postinstalls.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/misc/check-postinstalls.ts rename to tests/e2e/tests/misc/check-postinstalls.ts diff --git a/tests/legacy-cli/e2e/tests/misc/create-angular.ts b/tests/e2e/tests/misc/create-angular.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/misc/create-angular.ts rename to tests/e2e/tests/misc/create-angular.ts diff --git a/tests/legacy-cli/e2e/tests/misc/dedupe-duplicate-modules.ts b/tests/e2e/tests/misc/dedupe-duplicate-modules.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/misc/dedupe-duplicate-modules.ts rename to tests/e2e/tests/misc/dedupe-duplicate-modules.ts diff --git a/tests/legacy-cli/e2e/tests/misc/duplicate-command-line-option.ts b/tests/e2e/tests/misc/duplicate-command-line-option.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/misc/duplicate-command-line-option.ts rename to tests/e2e/tests/misc/duplicate-command-line-option.ts diff --git a/tests/legacy-cli/e2e/tests/misc/es2015-nometa.ts b/tests/e2e/tests/misc/es2015-nometa.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/misc/es2015-nometa.ts rename to tests/e2e/tests/misc/es2015-nometa.ts diff --git a/tests/legacy-cli/e2e/tests/misc/forwardref-es2015.ts b/tests/e2e/tests/misc/forwardref-es2015.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/misc/forwardref-es2015.ts rename to tests/e2e/tests/misc/forwardref-es2015.ts diff --git a/tests/legacy-cli/e2e/tests/misc/invalid-schematic-dependencies.ts b/tests/e2e/tests/misc/invalid-schematic-dependencies.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/misc/invalid-schematic-dependencies.ts rename to tests/e2e/tests/misc/invalid-schematic-dependencies.ts diff --git a/tests/legacy-cli/e2e/tests/misc/loaders-resolution.ts b/tests/e2e/tests/misc/loaders-resolution.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/misc/loaders-resolution.ts rename to tests/e2e/tests/misc/loaders-resolution.ts diff --git a/tests/legacy-cli/e2e/tests/misc/module-resolution/module-resolution-core-mapping.ts b/tests/e2e/tests/misc/module-resolution/module-resolution-core-mapping.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/misc/module-resolution/module-resolution-core-mapping.ts rename to tests/e2e/tests/misc/module-resolution/module-resolution-core-mapping.ts diff --git a/tests/legacy-cli/e2e/tests/misc/multiple-targets.ts b/tests/e2e/tests/misc/multiple-targets.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/misc/multiple-targets.ts rename to tests/e2e/tests/misc/multiple-targets.ts diff --git a/tests/legacy-cli/e2e/tests/misc/negated-boolean-options.ts b/tests/e2e/tests/misc/negated-boolean-options.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/misc/negated-boolean-options.ts rename to tests/e2e/tests/misc/negated-boolean-options.ts diff --git a/tests/legacy-cli/e2e/tests/misc/nested-schematic-packages.ts b/tests/e2e/tests/misc/nested-schematic-packages.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/misc/nested-schematic-packages.ts rename to tests/e2e/tests/misc/nested-schematic-packages.ts diff --git a/tests/legacy-cli/e2e/tests/misc/supported-angular.ts b/tests/e2e/tests/misc/supported-angular.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/misc/supported-angular.ts rename to tests/e2e/tests/misc/supported-angular.ts diff --git a/tests/legacy-cli/e2e/tests/misc/target-default-configuration.ts b/tests/e2e/tests/misc/target-default-configuration.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/misc/target-default-configuration.ts rename to tests/e2e/tests/misc/target-default-configuration.ts diff --git a/tests/legacy-cli/e2e/tests/misc/trace-resolution.ts b/tests/e2e/tests/misc/trace-resolution.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/misc/trace-resolution.ts rename to tests/e2e/tests/misc/trace-resolution.ts diff --git a/tests/legacy-cli/e2e/tests/misc/trusted-types.ts b/tests/e2e/tests/misc/trusted-types.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/misc/trusted-types.ts rename to tests/e2e/tests/misc/trusted-types.ts diff --git a/tests/legacy-cli/e2e/tests/misc/update-git-clean-subdirectory.ts b/tests/e2e/tests/misc/update-git-clean-subdirectory.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/misc/update-git-clean-subdirectory.ts rename to tests/e2e/tests/misc/update-git-clean-subdirectory.ts diff --git a/tests/legacy-cli/e2e/tests/misc/update-git-clean.ts b/tests/e2e/tests/misc/update-git-clean.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/misc/update-git-clean.ts rename to tests/e2e/tests/misc/update-git-clean.ts diff --git a/tests/legacy-cli/e2e/tests/misc/version.ts b/tests/e2e/tests/misc/version.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/misc/version.ts rename to tests/e2e/tests/misc/version.ts diff --git a/tests/legacy-cli/e2e/tests/misc/workspace-verification.ts b/tests/e2e/tests/misc/workspace-verification.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/misc/workspace-verification.ts rename to tests/e2e/tests/misc/workspace-verification.ts diff --git a/tests/legacy-cli/e2e/tests/protractor/test-fails.ts b/tests/e2e/tests/protractor/test-fails.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/protractor/test-fails.ts rename to tests/e2e/tests/protractor/test-fails.ts diff --git a/tests/legacy-cli/e2e/tests/schematics_cli/basic.ts b/tests/e2e/tests/schematics_cli/basic.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/schematics_cli/basic.ts rename to tests/e2e/tests/schematics_cli/basic.ts diff --git a/tests/legacy-cli/e2e/tests/schematics_cli/blank-test.ts b/tests/e2e/tests/schematics_cli/blank-test.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/schematics_cli/blank-test.ts rename to tests/e2e/tests/schematics_cli/blank-test.ts diff --git a/tests/legacy-cli/e2e/tests/schematics_cli/schematic-test.ts b/tests/e2e/tests/schematics_cli/schematic-test.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/schematics_cli/schematic-test.ts rename to tests/e2e/tests/schematics_cli/schematic-test.ts diff --git a/tests/legacy-cli/e2e/tests/test/karma-junit-output.ts b/tests/e2e/tests/test/karma-junit-output.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/test/karma-junit-output.ts rename to tests/e2e/tests/test/karma-junit-output.ts diff --git a/tests/legacy-cli/e2e/tests/test/test-code-coverage-exclude.ts b/tests/e2e/tests/test/test-code-coverage-exclude.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/test/test-code-coverage-exclude.ts rename to tests/e2e/tests/test/test-code-coverage-exclude.ts diff --git a/tests/legacy-cli/e2e/tests/test/test-environment.ts b/tests/e2e/tests/test/test-environment.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/test/test-environment.ts rename to tests/e2e/tests/test/test-environment.ts diff --git a/tests/legacy-cli/e2e/tests/test/test-fail-single-run.ts b/tests/e2e/tests/test/test-fail-single-run.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/test/test-fail-single-run.ts rename to tests/e2e/tests/test/test-fail-single-run.ts diff --git a/tests/legacy-cli/e2e/tests/test/test-include-glob.ts b/tests/e2e/tests/test/test-include-glob.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/test/test-include-glob.ts rename to tests/e2e/tests/test/test-include-glob.ts diff --git a/tests/legacy-cli/e2e/tests/test/test-jasmine-clock.ts b/tests/e2e/tests/test/test-jasmine-clock.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/test/test-jasmine-clock.ts rename to tests/e2e/tests/test/test-jasmine-clock.ts diff --git a/tests/legacy-cli/e2e/tests/test/test-scripts.ts b/tests/e2e/tests/test/test-scripts.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/test/test-scripts.ts rename to tests/e2e/tests/test/test-scripts.ts diff --git a/tests/legacy-cli/e2e/tests/test/test-sourcemap.ts b/tests/e2e/tests/test/test-sourcemap.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/test/test-sourcemap.ts rename to tests/e2e/tests/test/test-sourcemap.ts diff --git a/tests/legacy-cli/e2e/tests/update/update-application-builder.ts b/tests/e2e/tests/update/update-application-builder.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/update/update-application-builder.ts rename to tests/e2e/tests/update/update-application-builder.ts diff --git a/tests/legacy-cli/e2e/tests/update/update-multiple-versions.ts b/tests/e2e/tests/update/update-multiple-versions.ts similarity index 96% rename from tests/legacy-cli/e2e/tests/update/update-multiple-versions.ts rename to tests/e2e/tests/update/update-multiple-versions.ts index ce343df77d29..6fecb7b15b58 100644 --- a/tests/legacy-cli/e2e/tests/update/update-multiple-versions.ts +++ b/tests/e2e/tests/update/update-multiple-versions.ts @@ -7,7 +7,7 @@ import { expectToFail } from '../../utils/utils'; export default async function () { let restoreRegistry: (() => Promise) | undefined; try { - restoreRegistry = await createProjectFromAsset('18.0-project', true); + restoreRegistry = await createProjectFromAsset('19.0-project', true); await setRegistry(true); const extraArgs = ['--force']; diff --git a/tests/legacy-cli/e2e/tests/update/update-secure-registry.ts b/tests/e2e/tests/update/update-secure-registry.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/update/update-secure-registry.ts rename to tests/e2e/tests/update/update-secure-registry.ts diff --git a/tests/legacy-cli/e2e/tests/update/update.ts b/tests/e2e/tests/update/update.ts similarity index 87% rename from tests/legacy-cli/e2e/tests/update/update.ts rename to tests/e2e/tests/update/update.ts index 3ab99bb0d7b6..ae941762d6ca 100644 --- a/tests/legacy-cli/e2e/tests/update/update.ts +++ b/tests/e2e/tests/update/update.ts @@ -4,6 +4,7 @@ import { expectFileMatchToExist } from '../../utils/fs'; import { getActivePackageManager } from '../../utils/packages'; import { ng, noSilentNg } from '../../utils/process'; import { isPrereleaseCli, useCIChrome, useCIDefaults, getNgCLIVersion } from '../../utils/project'; +import { executeBrowserTest } from '../../utils/puppeteer'; export default async function () { let restoreRegistry: (() => Promise) | undefined; @@ -11,10 +12,10 @@ export default async function () { try { // We need to use the public registry because in the local NPM server we don't have // older versions @angular/cli packages which would cause `npm install` during `ng update` to fail. - restoreRegistry = await createProjectFromAsset('18.0-project', true); + restoreRegistry = await createProjectFromAsset('19.0-project', true); // CLI project version - const cliMajorProjectVersion = 18; + const cliMajorProjectVersion = 19; // If using npm, enable legacy peer deps mode to avoid defects in npm 7+'s peer dependency resolution // Example error where 11.2.14 satisfies the SemVer range ^11.0.0 but still fails: @@ -70,22 +71,24 @@ export default async function () { await ng('update', '@angular/cli', ...extraUpdateArgs); - // Generate E2E setup - await ng('generate', 'private-e2e', '--related-app-name=eighteen-project'); - // Setup testing to use CI Chrome. - await useCIChrome('eighteen-project', './'); - await useCIChrome('eighteen-project', './e2e/'); - await useCIDefaults('eighteen-project'); + await useCIChrome('nineteen-project', './'); + await useCIDefaults('nineteen-project'); // Run CLI commands. await ng('generate', 'component', 'my-comp'); await ng('test', '--watch=false'); - await ng('e2e'); - await ng('e2e', '--configuration=production'); + await executeBrowserTest({ + configuration: 'production', + expectedTitleText: 'Hello, nineteen-project', + }); + await executeBrowserTest({ + configuration: 'development', + expectedTitleText: 'Hello, nineteen-project', + }); // Verify project now creates bundles await noSilentNg('build', '--configuration=production'); - await expectFileMatchToExist('dist/eighteen-project/browser', /main-[a-zA-Z0-9]{8}\.js/); + await expectFileMatchToExist('dist/nineteen-project/browser', /main-[a-zA-Z0-9]{8}\.js/); } diff --git a/tests/legacy-cli/e2e/tests/vite/reuse-dep-optimization-cache.ts b/tests/e2e/tests/vite/reuse-dep-optimization-cache.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/vite/reuse-dep-optimization-cache.ts rename to tests/e2e/tests/vite/reuse-dep-optimization-cache.ts diff --git a/tests/e2e/tests/vite/ssr-base-href.ts b/tests/e2e/tests/vite/ssr-base-href.ts new file mode 100644 index 000000000000..140f2582689a --- /dev/null +++ b/tests/e2e/tests/vite/ssr-base-href.ts @@ -0,0 +1,48 @@ +import assert from 'node:assert'; +import { ng } from '../../utils/process'; +import { replaceInFile } from '../../utils/fs'; +import { installWorkspacePackages, uninstallPackage } from '../../utils/packages'; +import { ngServe, updateJsonFile, useSha } from '../../utils/project'; +import { getGlobalVariable } from '../../utils/env'; + +export default async function () { + assert( + getGlobalVariable('argv')['esbuild'], + 'This test should not be called in the Webpack suite.', + ); + + await uninstallPackage('@angular/ssr'); + await ng('add', '@angular/ssr', '--skip-confirmation', '--skip-install'); + await useSha(); + await installWorkspacePackages(); + + await updateJsonFile('angular.json', (json) => { + json.projects['test-project'].architect.build.options['baseHref'] = '/base'; + }); + + await replaceInFile( + 'src/server.ts', + /express\(\);/, + `express(); + + app.use('/ping', (req, res) => { + return res.json({ pong: true }); + });`, + ); + + const port = await ngServe(); + + // Angular application and bundled should be affected by baseHref + await matchResponse(`http://localhost:${port}/base`, /ng-server-context/); + await matchResponse(`http://localhost:${port}/base/main.js`, /App/); + + // Server endpoint should not be affected by baseHref + await matchResponse(`http://localhost:${port}/ping`, /pong/); +} + +async function matchResponse(url: string, match: RegExp): Promise { + const response = await fetch(url); + const text = await response.text(); + + assert.match(text, match); +} diff --git a/tests/legacy-cli/e2e/tests/vite/ssr-default.ts b/tests/e2e/tests/vite/ssr-default.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/vite/ssr-default.ts rename to tests/e2e/tests/vite/ssr-default.ts diff --git a/tests/legacy-cli/e2e/tests/vite/ssr-entry-express.ts b/tests/e2e/tests/vite/ssr-entry-express.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/vite/ssr-entry-express.ts rename to tests/e2e/tests/vite/ssr-entry-express.ts diff --git a/tests/legacy-cli/e2e/tests/vite/ssr-entry-fastify.ts b/tests/e2e/tests/vite/ssr-entry-fastify.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/vite/ssr-entry-fastify.ts rename to tests/e2e/tests/vite/ssr-entry-fastify.ts diff --git a/tests/legacy-cli/e2e/tests/vite/ssr-entry-h3.ts b/tests/e2e/tests/vite/ssr-entry-h3.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/vite/ssr-entry-h3.ts rename to tests/e2e/tests/vite/ssr-entry-h3.ts diff --git a/tests/legacy-cli/e2e/tests/vite/ssr-entry-hono.ts b/tests/e2e/tests/vite/ssr-entry-hono.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/vite/ssr-entry-hono.ts rename to tests/e2e/tests/vite/ssr-entry-hono.ts diff --git a/tests/legacy-cli/e2e/tests/vite/ssr-error-stack.ts b/tests/e2e/tests/vite/ssr-error-stack.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/vite/ssr-error-stack.ts rename to tests/e2e/tests/vite/ssr-error-stack.ts diff --git a/tests/legacy-cli/e2e/tests/vite/ssr-new-dep-optimization.ts b/tests/e2e/tests/vite/ssr-new-dep-optimization.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/vite/ssr-new-dep-optimization.ts rename to tests/e2e/tests/vite/ssr-new-dep-optimization.ts diff --git a/tests/legacy-cli/e2e/tests/vite/ssr-with-ssl.ts b/tests/e2e/tests/vite/ssr-with-ssl.ts similarity index 80% rename from tests/legacy-cli/e2e/tests/vite/ssr-with-ssl.ts rename to tests/e2e/tests/vite/ssr-with-ssl.ts index 5bb8d9105cf8..90518080f8f3 100644 --- a/tests/legacy-cli/e2e/tests/vite/ssr-with-ssl.ts +++ b/tests/e2e/tests/vite/ssr-with-ssl.ts @@ -42,14 +42,23 @@ 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/); + // http 2 + await validateResponse('/main.js', /bootstrapApplication/, true); + await validateResponse('/home', /home works/, true); - async function validateResponse(pathname: string, match: RegExp): Promise { + // 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, }, }), diff --git a/tests/legacy-cli/e2e/tests/vitest/browser-no-globals.ts b/tests/e2e/tests/vitest/browser-no-globals.ts similarity index 86% rename from tests/legacy-cli/e2e/tests/vitest/browser-no-globals.ts rename to tests/e2e/tests/vitest/browser-no-globals.ts index 21135ff42f83..b25f8168c5f7 100644 --- a/tests/legacy-cli/e2e/tests/vitest/browser-no-globals.ts +++ b/tests/e2e/tests/vitest/browser-no-globals.ts @@ -1,7 +1,7 @@ import assert from 'node:assert/strict'; import { writeFile } from '../../utils/fs'; import { installPackage } from '../../utils/packages'; -import { exec, ng } from '../../utils/process'; +import { ng } from '../../utils/process'; import { applyVitestBuilder } from '../../utils/vitest'; /** @@ -14,8 +14,6 @@ export default async function (): Promise { await installPackage('playwright@1'); await installPackage('@vitest/browser-playwright@4'); - await exec('npx', 'playwright', 'install', 'chromium', '--only-shell'); - await writeFile( 'src/app/app.spec.ts', ` diff --git a/tests/legacy-cli/e2e/tests/vitest/browser-playwright.ts b/tests/e2e/tests/vitest/browser-playwright.ts similarity index 86% rename from tests/legacy-cli/e2e/tests/vitest/browser-playwright.ts rename to tests/e2e/tests/vitest/browser-playwright.ts index 9b22b81736e4..fa9ec43aabf3 100644 --- a/tests/legacy-cli/e2e/tests/vitest/browser-playwright.ts +++ b/tests/e2e/tests/vitest/browser-playwright.ts @@ -1,6 +1,6 @@ import assert from 'node:assert/strict'; import { applyVitestBuilder } from '../../utils/vitest'; -import { exec, ng } from '../../utils/process'; +import { ng } from '../../utils/process'; import { installPackage } from '../../utils/packages'; import { writeFile } from '../../utils/fs'; @@ -8,8 +8,6 @@ export default async function (): Promise { await applyVitestBuilder(); await installPackage('playwright@1'); await installPackage('@vitest/browser-playwright@4'); - await exec('npx', 'playwright', 'install', 'chromium', '--only-shell'); - await ng('generate', 'component', 'my-comp'); await writeFile( diff --git a/tests/legacy-cli/e2e/tests/vitest/browser-webdriverio.ts b/tests/e2e/tests/vitest/browser-webdriverio.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/vitest/browser-webdriverio.ts rename to tests/e2e/tests/vitest/browser-webdriverio.ts diff --git a/tests/legacy-cli/e2e/tests/vitest/component.ts b/tests/e2e/tests/vitest/component.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/vitest/component.ts rename to tests/e2e/tests/vitest/component.ts diff --git a/tests/e2e/tests/vitest/include.ts b/tests/e2e/tests/vitest/include.ts new file mode 100644 index 000000000000..4585194ef3c2 --- /dev/null +++ b/tests/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/e2e/tests/vitest/larger-project-coverage.ts similarity index 74% rename from tests/legacy-cli/e2e/tests/vitest/larger-project-coverage.ts rename to tests/e2e/tests/vitest/larger-project-coverage.ts index 17e642014d8d..3594bdc7dfee 100644 --- a/tests/legacy-cli/e2e/tests/vitest/larger-project-coverage.ts +++ b/tests/e2e/tests/vitest/larger-project-coverage.ts @@ -2,7 +2,6 @@ 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'; import { updateJsonFile } from '../../utils/project'; import { readFile } from '../../utils/fs'; @@ -27,33 +26,7 @@ export default async function () { const artifactCount = 100; const initialTestCount = 1; - const generatedFiles: string[] = []; - - // Generate a mix of components, services, and pipes - for (let i = 0; i < artifactCount; i++) { - const type = i % 3; - const name = `test-artifact${i}`; - let generateType; - let fileSuffix; - - switch (type) { - case 0: - generateType = 'component'; - fileSuffix = '.ts'; - break; - case 1: - generateType = 'service'; - fileSuffix = '.ts'; - break; - default: - generateType = 'pipe'; - fileSuffix = '-pipe.ts'; - break; - } - - await ng('generate', generateType, name, '--skip-tests=false'); - generatedFiles.push(`${name}${fileSuffix}`); - } + const generatedFiles = await generateArtifactsInBatches(artifactCount); const totalTests = initialTestCount + artifactCount; const expectedMessage = new RegExp(`${totalTests} passed`); @@ -63,22 +36,17 @@ export default async function () { const { stdout: jsdomStdout } = await ng('test', '--no-watch', '--coverage'); assert.match(jsdomStdout, expectedMessage, `Expected ${totalTests} tests to pass in JSDOM mode.`); - // TODO: Investigate why coverage-final.json is empty on Windows in JSDOM mode. - // For now, skip the coverage report check on Windows. - if (process.platform !== 'win32') { - // 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.`); - } + // 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'); - await exec('npx', 'playwright', 'install', 'chromium', '--only-shell'); // Run tests in browser mode with coverage const { stdout: browserStdout } = await ng( @@ -102,3 +70,42 @@ export default async function () { 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/e2e/tests/vitest/larger-project.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/vitest/larger-project.ts rename to tests/e2e/tests/vitest/larger-project.ts diff --git a/tests/legacy-cli/e2e/tests/vitest/runner-config-path.ts b/tests/e2e/tests/vitest/runner-config-path.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/vitest/runner-config-path.ts rename to tests/e2e/tests/vitest/runner-config-path.ts diff --git a/tests/legacy-cli/e2e/tests/vitest/snapshot.ts b/tests/e2e/tests/vitest/snapshot.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/vitest/snapshot.ts rename to tests/e2e/tests/vitest/snapshot.ts diff --git a/tests/legacy-cli/e2e/tests/vitest/tslib-resolution.ts b/tests/e2e/tests/vitest/tslib-resolution.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/vitest/tslib-resolution.ts rename to tests/e2e/tests/vitest/tslib-resolution.ts diff --git a/tests/legacy-cli/e2e/tests/web-test-runner/basic.ts b/tests/e2e/tests/web-test-runner/basic.ts similarity index 100% rename from tests/legacy-cli/e2e/tests/web-test-runner/basic.ts rename to tests/e2e/tests/web-test-runner/basic.ts diff --git a/tests/legacy-cli/e2e/utils/BUILD.bazel b/tests/e2e/utils/BUILD.bazel similarity index 77% rename from tests/legacy-cli/e2e/utils/BUILD.bazel rename to tests/e2e/utils/BUILD.bazel index b73eec76471a..8082ab9d97c4 100644 --- a/tests/legacy-cli/e2e/utils/BUILD.bazel +++ b/tests/e2e/utils/BUILD.bazel @@ -7,20 +7,20 @@ ts_project( testonly = True, srcs = glob(["**/*.ts"]), data = [ - "//tests/legacy-cli/e2e/ng-snapshot", + "//tests/e2e/ng-snapshot", ], deps = [ "//:node_modules/@types/jasmine", "//:node_modules/@types/node", "//:node_modules/@types/semver", - "//:node_modules/ansi-colors", "//:node_modules/fast-glob", "//:node_modules/protractor", + "//:node_modules/puppeteer", "//:node_modules/semver", - "//:node_modules/tar", "//:node_modules/verdaccio", "//:node_modules/verdaccio-auth-memory", - "//tests:node_modules/rxjs", + "//tests:node_modules/@types/tar-stream", + "//tests:node_modules/tar-stream", "//tests:node_modules/tree-kill", ], ) diff --git a/tests/legacy-cli/e2e/utils/assets.ts b/tests/e2e/utils/assets.ts similarity index 83% rename from tests/legacy-cli/e2e/utils/assets.ts rename to tests/e2e/utils/assets.ts index d421086c1b9e..153fdaccc6de 100644 --- a/tests/legacy-cli/e2e/utils/assets.ts +++ b/tests/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/env.ts b/tests/e2e/utils/env.ts similarity index 100% rename from tests/legacy-cli/e2e/utils/env.ts rename to tests/e2e/utils/env.ts diff --git a/tests/legacy-cli/e2e/utils/fs.ts b/tests/e2e/utils/fs.ts similarity index 100% rename from tests/legacy-cli/e2e/utils/fs.ts rename to tests/e2e/utils/fs.ts diff --git a/tests/legacy-cli/e2e/utils/git.ts b/tests/e2e/utils/git.ts similarity index 100% rename from tests/legacy-cli/e2e/utils/git.ts rename to tests/e2e/utils/git.ts diff --git a/tests/legacy-cli/e2e/utils/jest.ts b/tests/e2e/utils/jest.ts similarity index 100% rename from tests/legacy-cli/e2e/utils/jest.ts rename to tests/e2e/utils/jest.ts diff --git a/tests/e2e/utils/network.ts b/tests/e2e/utils/network.ts new file mode 100644 index 000000000000..48602f0000d2 --- /dev/null +++ b/tests/e2e/utils/network.ts @@ -0,0 +1,30 @@ +import { createServer } from 'node:net'; + +/** + * Finds an available network port on the loopback interface (127.0.0.1). + * This is useful for tests that need to bind to a free port to avoid conflicts. + * Explicitly binds to IPv4 localhost to avoid firewall prompts, IPv6 binding issues, and ensure consistency. + * + * @returns A promise that resolves with an available port number. + */ +export function findFreePort(): Promise { + return new Promise((resolve, reject) => { + const srv = createServer(); + srv.once('listening', () => { + const address = srv.address(); + if (!address || typeof address === 'string') { + // Should not happen with TCP, but good for type safety + srv.close(() => reject(new Error('Failed to get server address'))); + return; + } + const port = address.port; + srv.close((e) => (e ? reject(e) : resolve(port))); + }); + + // If an error happens (e.g. during bind), the server is not listening, + // so we should not call close(). + srv.once('error', (e) => reject(e)); + // Explicitly listen on IPv4 localhost to avoid firewall prompts, IPv6 binding issues, and ensure consistency. + srv.listen(0, '127.0.0.1'); + }); +} diff --git a/tests/legacy-cli/e2e/utils/packages.ts b/tests/e2e/utils/packages.ts similarity index 100% rename from tests/legacy-cli/e2e/utils/packages.ts rename to tests/e2e/utils/packages.ts diff --git a/tests/legacy-cli/e2e/utils/process.ts b/tests/e2e/utils/process.ts similarity index 94% rename from tests/legacy-cli/e2e/utils/process.ts rename to tests/e2e/utils/process.ts index cb542ff1ea0f..91216843086a 100644 --- a/tests/legacy-cli/e2e/utils/process.ts +++ b/tests/e2e/utils/process.ts @@ -1,11 +1,9 @@ -import * as ansiColors from 'ansi-colors'; import { spawn, SpawnOptions } from 'node:child_process'; import * as child_process from 'node:child_process'; -import { concat, defer, EMPTY, from, lastValueFrom, catchError, repeat } from 'rxjs'; import { getGlobalVariable, getGlobalVariablesEnv } from './env'; import treeKill from 'tree-kill'; import { delimiter, join, resolve } from 'node:path'; -import { stripVTControlCharacters } from 'node:util'; +import { stripVTControlCharacters, styleText } from 'node:util'; interface ExecOptions { silent?: boolean; @@ -30,9 +28,6 @@ export type ProcessOutput = { }; function _exec(options: ExecOptions, cmd: string, args: string[]): Promise { - // Create a separate instance to prevent unintended global changes to the color configuration - const colors = ansiColors.create(); - const cwd = options.cwd ?? process.cwd(); const env = options.env ?? process.env; @@ -57,8 +52,10 @@ function _exec(options: ExecOptions, cmd: string, args: string[]): Promise `"${x}"`).join(' ')}\`${flags}...`)); - console.log(colors.blue(`CWD: ${cwd}`)); + console.log( + styleText(['blue'], `Running \`${cmd} ${args.map((x) => `"${x}"`).join(' ')}\`${flags}...`), + ); + console.log(styleText(['blue'], `CWD: ${cwd}`)); const spawnOptions: SpawnOptions = { cwd, @@ -123,7 +120,7 @@ function _exec(options: ExecOptions, cmd: string, args: string[]): Promise line !== '') - .forEach((line) => console.error(colors.yellow(' ' + line))); + .forEach((line) => console.error(styleText(['yellow'], ' ' + line))); }); childProcess.on('close', (code) => { @@ -312,7 +309,7 @@ export async function execAndCaptureError( } } -export function execAndWaitForOutputToMatch( +export async function execAndWaitForOutputToMatch( cmd: string, args: string[], match: RegExp, @@ -324,15 +321,19 @@ export function execAndWaitForOutputToMatch( // happened just before the build (e.g. `git clean`). // This seems to be due to host file system differences, see // https://nodejs.org/docs/latest/api/fs.html#fs_caveats - return lastValueFrom( - concat( - from(_exec({ waitForMatch: match, env }, cmd, args)), - defer(() => waitForAnyProcessOutputToMatch(match, 2500)).pipe( - repeat(20), - catchError(() => EMPTY), - ), - ), - ); + const maxRetries = 20; + let lastResult = await _exec({ waitForMatch: match, env }, cmd, args); + + for (let i = 0; i < maxRetries; i++) { + try { + lastResult = await waitForAnyProcessOutputToMatch(match, 2500); + } catch { + // If we timeout (no new match found), we assume the process is stable. + break; + } + } + + return lastResult; } else { return _exec({ waitForMatch: match, env }, cmd, args); } diff --git a/tests/legacy-cli/e2e/utils/project.ts b/tests/e2e/utils/project.ts similarity index 97% rename from tests/legacy-cli/e2e/utils/project.ts rename to tests/e2e/utils/project.ts index c84b7fce8e1d..58e249d19895 100644 --- a/tests/legacy-cli/e2e/utils/project.ts +++ b/tests/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) => { @@ -118,7 +119,7 @@ export async function useSha(): Promise { if (missingSnapshots.length > 0) { throw new Error( 'e2e test with --ng-snapshots requires all angular packages be ' + - 'listed in tests/legacy-cli/e2e/ng-snapshot/package.json.\nErrors:\n' + + 'listed in tests/e2e/ng-snapshot/package.json.\nErrors:\n' + missingSnapshots.join('\n '), ); } @@ -270,3 +271,7 @@ export function updateServerFileForEsbuild(filepath: string): Promise { `, ); } + +export function getTestProjectDir(): string { + return join(getGlobalVariable('projects-root'), 'test-project'); +} diff --git a/tests/e2e/utils/puppeteer.ts b/tests/e2e/utils/puppeteer.ts new file mode 100644 index 000000000000..8cab9f2ddef6 --- /dev/null +++ b/tests/e2e/utils/puppeteer.ts @@ -0,0 +1,90 @@ +import { type Page, launch } from 'puppeteer'; +import { execAndWaitForOutputToMatch, killAllProcesses } from './process'; +import { stripVTControlCharacters } from 'node:util'; + +export interface BrowserTestOptions { + project?: string; + configuration?: string; + baseUrl?: string; + checkFn?: (page: Page) => Promise; + expectedTitleText?: string; +} + +export async function executeBrowserTest(options: BrowserTestOptions = {}) { + let url = options.baseUrl; + let hasStartedServer = false; + + try { + if (!url) { + // Start serving and find address (1 - Webpack; 2 - Vite) + const match = /(?:open your browser on|Local:)\s+(http:\/\/localhost:\d+\/)/; + const serveArgs = ['serve', '--port=0']; + if (options.project) { + serveArgs.push(options.project); + } + if (options.configuration) { + serveArgs.push(`--configuration=${options.configuration}`); + } + + const { stdout } = await execAndWaitForOutputToMatch('ng', serveArgs, match, { + ...process.env, + 'NO_COLOR': '1', + }); + url = stripVTControlCharacters(stdout).match(match)?.[1]; + if (!url) { + throw new Error('Could not find serving URL'); + } + hasStartedServer = true; + } + + const browser = await launch({ + executablePath: process.env['CHROME_BIN'], + headless: true, + args: ['--no-sandbox'], + }); + try { + const page = await browser.newPage(); + + // Capture errors + const errors: string[] = []; + page.on('console', (msg) => { + if (msg.type() === 'error') { + errors.push(msg.text()); + } + }); + page.on('pageerror', (err) => { + errors.push(err.toString()); + }); + + await page.goto(url); + + if (options.checkFn) { + await options.checkFn(page); + } else { + // Default check: verify h1 content and no browser errors + const expectedText = options.expectedTitleText || 'Hello, test-project'; + + // Wait for the h1 element to appear and contain the expected text + await page.waitForFunction( + (selector: string, text: string) => { + const doc = (globalThis as any).document; + return doc.querySelector(selector)?.textContent?.includes(text); + }, + { timeout: 10000 }, // Max 10 seconds wait time + 'h1', + expectedText, + ); + } + + if (errors.length > 0) { + throw new Error(`Browser console errors detected:\n${errors.join('\n')}`); + } + } finally { + await browser.close(); + } + } finally { + if (hasStartedServer) { + await killAllProcesses(); + } + } +} diff --git a/tests/legacy-cli/e2e/utils/registry.ts b/tests/e2e/utils/registry.ts similarity index 72% rename from tests/legacy-cli/e2e/utils/registry.ts rename to tests/e2e/utils/registry.ts index 1bd3084d4f48..fd557c116120 100644 --- a/tests/legacy-cli/e2e/utils/registry.ts +++ b/tests/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]); @@ -44,7 +49,7 @@ export async function createNpmRegistry( // Token was generated using `echo -n 'testing:s3cret' | openssl base64`. const VALID_TOKEN = `dGVzdGluZzpzM2NyZXQ=`; -export function createNpmConfigForAuthentication( +export async function createNpmConfigForAuthentication( /** * When true, the authentication token will be scoped to the registry URL. * @example @@ -65,17 +70,30 @@ export function createNpmConfigForAuthentication( const token = invalidToken ? `invalid=` : VALID_TOKEN; const registry = (getGlobalVariable('package-secure-registry') as string).replace(/^\w+:/, ''); - return writeFile( + await writeFile( '.npmrc', scopedAuthentication ? ` - ${registry}:_auth="${token}" - registry=http:${registry} - ` +${registry}/:_auth="${token}" +registry=http:${registry} +` + : ` +_auth="${token}" +registry=http:${registry} +`, + ); + + await writeFile( + '.yarnrc', + scopedAuthentication + ? ` +${registry}/:_auth "${token}" +registry http:${registry} +` : ` - _auth="${token}" - registry=http:${registry} - `, +_auth "${token}" +registry http:${registry} +`, ); } diff --git a/tests/legacy-cli/e2e/utils/tar.ts b/tests/e2e/utils/tar.ts similarity index 50% rename from tests/legacy-cli/e2e/utils/tar.ts rename to tests/e2e/utils/tar.ts index 77239c74eb0f..d78f47762337 100644 --- a/tests/legacy-cli/e2e/utils/tar.ts +++ b/tests/e2e/utils/tar.ts @@ -8,7 +8,8 @@ import { createReadStream } from 'node:fs'; import { normalize } from 'node:path'; -import { Parser } from 'tar'; +import { createGunzip } from 'node:zlib'; +import { extract } from 'tar-stream'; /** * Extract and return the contents of a single file out of a tar file. @@ -21,20 +22,27 @@ export function extractFile(tarball: string, filePath: string): Promise const normalizedFilePath = normalize(filePath); return new Promise((resolve, reject) => { - createReadStream(tarball) - .pipe( - new Parser({ - strict: true, - filter: (p) => normalize(p) === normalizedFilePath, - onReadEntry: (entry) => { - const chunks: Buffer[] = []; - - entry.on('data', (chunk) => chunks.push(chunk)); - entry.on('error', reject); - entry.on('finish', () => resolve(Buffer.concat(chunks))); - }, - }), - ) - .on('close', () => reject(`${tarball} does not contain ${filePath}`)); + const extractor = extract(); + + extractor.on('entry', (header, stream, next) => { + if (normalize(header.name) !== normalizedFilePath) { + stream.resume(); + next(); + + return; + } + + const chunks: Buffer[] = []; + stream.on('data', (chunk) => chunks.push(chunk)); + stream.on('error', reject); + stream.on('end', () => { + resolve(Buffer.concat(chunks)); + next(); + }); + }); + + extractor.on('finish', () => reject(new Error(`'${filePath}' not found in '${tarball}'.`))); + + createReadStream(tarball).pipe(createGunzip()).pipe(extractor).on('error', reject); }); } diff --git a/tests/legacy-cli/e2e/utils/test_process.ts b/tests/e2e/utils/test_process.ts similarity index 100% rename from tests/legacy-cli/e2e/utils/test_process.ts rename to tests/e2e/utils/test_process.ts diff --git a/tests/legacy-cli/e2e/utils/utils.ts b/tests/e2e/utils/utils.ts similarity index 100% rename from tests/legacy-cli/e2e/utils/utils.ts rename to tests/e2e/utils/utils.ts diff --git a/tests/legacy-cli/e2e/utils/version.ts b/tests/e2e/utils/version.ts similarity index 100% rename from tests/legacy-cli/e2e/utils/version.ts rename to tests/e2e/utils/version.ts diff --git a/tests/legacy-cli/e2e/utils/vitest.ts b/tests/e2e/utils/vitest.ts similarity index 100% rename from tests/legacy-cli/e2e/utils/vitest.ts rename to tests/e2e/utils/vitest.ts diff --git a/tests/legacy-cli/e2e/utils/web-test-runner.ts b/tests/e2e/utils/web-test-runner.ts similarity index 100% rename from tests/legacy-cli/e2e/utils/web-test-runner.ts rename to tests/e2e/utils/web-test-runner.ts diff --git a/tests/legacy-cli/e2e_runner.ts b/tests/e2e_runner.ts similarity index 76% rename from tests/legacy-cli/e2e_runner.ts rename to tests/e2e_runner.ts index 5d7031f20489..c7a672161b7a 100644 --- a/tests/legacy-cli/e2e_runner.ts +++ b/tests/e2e_runner.ts @@ -1,9 +1,9 @@ -import { parseArgs } from 'node:util'; -import { createConsoleLogger } from '../../packages/angular_devkit/core/node'; -import colors from 'ansi-colors'; +import { parseArgs, styleText } from 'node:util'; +import { createConsoleLogger } from '../packages/angular_devkit/core/node'; 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 +13,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 +31,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 +54,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' }, @@ -118,14 +113,14 @@ if (process.env.CHROME_BIN) { const logger = createConsoleLogger(argv.verbose, process.stdout, process.stderr, { info: (s) => s, debug: (s) => s, - warn: (s) => colors.bold.yellow(s), - error: (s) => colors.bold.red(s), - fatal: (s) => colors.bold.red(s), + warn: (s) => styleText(['bold', 'yellow'], s), + error: (s) => styleText(['bold', 'red'], s), + fatal: (s) => styleText(['bold', 'red'], s), }); 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 +221,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(styleText(['green'], `Done shard ${shardId} of ${nbShards}.`)); + } else { + console.log(styleText(['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(styleText(['red'], err.message)); + if (err.stack) { + console.error(styleText(['red'], err.stack)); } + } else { + console.error(styleText(['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(styleText(['red'], `Unkown Error: ${err}`)); + process.exitCode = 1; +}); async function runSteps( run: (name: string) => Promise | void, @@ -303,7 +309,7 @@ async function runSteps( await run(absoluteName); } catch (e) { console.log('\n'); - console.error(colors.red(`${capsType} "${name}" failed...`)); + console.error(styleText(['red'], `${capsType} "${name}" failed...`)); throw e; } finally { @@ -334,7 +340,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 +349,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. @@ -356,16 +362,22 @@ function printHeader( count: number, type: 'setup' | 'initializer' | 'test', ) { - const text = `${testIndex + 1} of ${count}`; + const text = `(${testIndex + 1} of ${count})`; const fullIndex = testIndex * nbShards + (shardId ?? 0) + 1; const shard = shardId === null || type !== 'test' ? '' - : colors.yellow(` [${shardId}:${nbShards}]` + colors.bold(` (${fullIndex}/${tests.length})`)); + : styleText( + ['yellow'], + ` [${shardId}:${nbShards}]` + styleText(['bold'], ` (${fullIndex}/${tests.length})`), + ); console.log( - colors.green( - `Running ${type} "${colors.bold.blue(testName)}" (${colors.bold.white(text)}${shard})...`, - ), + styleText(['green'], `Running ${type} "`) + + styleText(['bold', 'blue'], testName) + + styleText(['green'], '" ') + + styleText(['bold', 'white'], text) + + shard + + styleText(['green'], '...'), ); } @@ -375,9 +387,11 @@ function printFooter(testName: string, type: 'setup' | 'initializer' | 'test', s // Round to hundredth of a second. const t = Math.round((Date.now() - startTime) / 10) / 100; console.log( - colors.green(`${capsType} "${colors.bold.blue(testName)}" took `) + - colors.bold.blue('' + t) + - colors.green('s...'), + styleText(['green'], `${capsType} "`) + + styleText(['bold', 'blue'], testName) + + styleText(['green'], '" took ') + + styleText(['bold', 'blue'], t.toFixed(2)) + + styleText(['green'], 's...'), ); console.log(''); } @@ -389,7 +403,7 @@ async function findPackageTars(): Promise<{ [pkg: string]: PkgInfo }> { ); const pkgJsons = await Promise.all( - pkgs.map((pkg) => realpathSync(pkg)).map((pkg) => extractFile(pkg, './package.json')), + pkgs.map((pkg) => realpathSync(pkg)).map((pkg) => extractFile(pkg, 'npm_package/package.json')), ); return pkgs.reduce( @@ -406,3 +420,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/BUILD.bazel b/tests/legacy-cli/BUILD.bazel deleted file mode 100644 index 713bc31b5c82..000000000000 --- a/tests/legacy-cli/BUILD.bazel +++ /dev/null @@ -1,74 +0,0 @@ -load("@aspect_bazel_lib//lib:directory_path.bzl", "directory_path") -load("@npm//:rollup/package_json.bzl", rollup = "bin") -load("//tools:defaults.bzl", "ts_project") -load(":e2e.bzl", "e2e_suites") - -package(default_visibility = ["//visibility:public"]) - -ts_project( - name = "runner", - testonly = True, - srcs = [ - "e2e_runner.ts", - ], - deps = [ - "//:node_modules/@types/node", - "//:node_modules/ansi-colors", - "//:node_modules/fast-glob", - "//packages/angular_devkit/core", - "//packages/angular_devkit/core/node", - "//tests/legacy-cli/e2e/utils", - ], -) - -rollup.rollup( - name = "runner_bundled", - testonly = True, - srcs = [ - "rollup.config.mjs", - ":runner", - "//:node_modules/@rollup/plugin-alias", - "//:node_modules/@rollup/plugin-commonjs", - "//:node_modules/@rollup/plugin-json", - "//:node_modules/@rollup/plugin-node-resolve", - "//:node_modules/fast-glob", - "//tests/legacy-cli/e2e/initialize", - "//tests/legacy-cli/e2e/ng-snapshot", - "//tests/legacy-cli/e2e/setup", - "//tests/legacy-cli/e2e/tests", - ], - args = [ - "--format=cjs", - "--config=./rollup.config.mjs", - ], - chdir = package_name(), - out_dirs = ["runner_bundled_out"], - progress_message = "Bundling e2e test runner", -) - -directory_path( - name = "runner_entrypoint", - testonly = True, - directory = ":runner_bundled", - path = "./e2e_runner.js", -) - -e2e_suites( - name = "e2e", - data = [ - ":runner_bundled", - "verdaccio.yaml", - "verdaccio_auth.yaml", - - # Dynamically loaded. - "//tests/legacy-cli/e2e/assets", - "//:node_modules/verdaccio", - "//:node_modules/verdaccio-auth-memory", - - # 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/assets/18.0-project/README.md b/tests/legacy-cli/e2e/assets/18.0-project/README.md deleted file mode 100644 index 60097ece05c4..000000000000 --- a/tests/legacy-cli/e2e/assets/18.0-project/README.md +++ /dev/null @@ -1,27 +0,0 @@ -# EighteenProject - -This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 18.2.20. - -## Development server - -Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The application will automatically reload if you change any of the source files. - -## Code scaffolding - -Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`. - -## Build - -Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. - -## Running unit tests - -Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io). - -## Running end-to-end tests - -Run `ng e2e` to execute the end-to-end tests via a platform of your choice. To use this command, you need to first add a package that implements end-to-end testing capabilities. - -## Further help - -To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI Overview and Command Reference](https://angular.dev/tools/cli) page. diff --git a/tests/legacy-cli/e2e/assets/18.0-project/package.json b/tests/legacy-cli/e2e/assets/18.0-project/package.json deleted file mode 100644 index 8b05bf229739..000000000000 --- a/tests/legacy-cli/e2e/assets/18.0-project/package.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "name": "eighteen-project", - "version": "0.0.0", - "scripts": { - "ng": "ng", - "start": "ng serve", - "build": "ng build", - "watch": "ng build --watch --configuration development", - "test": "ng test" - }, - "private": true, - "dependencies": { - "@angular/animations": "^18.2.0", - "@angular/common": "^18.2.0", - "@angular/compiler": "^18.2.0", - "@angular/core": "^18.2.0", - "@angular/forms": "^18.2.0", - "@angular/platform-browser": "^18.2.0", - "@angular/platform-browser-dynamic": "^18.2.0", - "@angular/router": "^18.2.0", - "rxjs": "~7.8.0", - "tslib": "^2.3.0", - "zone.js": "~0.14.10" - }, - "devDependencies": { - "@angular-devkit/build-angular": "^18.2.20", - "@angular/cli": "^18.2.20", - "@angular/compiler-cli": "^18.2.0", - "@types/jasmine": "~5.1.0", - "jasmine-core": "~5.2.0", - "karma": "~6.4.0", - "karma-chrome-launcher": "~3.2.0", - "karma-coverage": "~2.2.0", - "karma-jasmine": "~5.1.0", - "karma-jasmine-html-reporter": "~2.1.0", - "typescript": "~5.5.2" - } -} diff --git a/tests/legacy-cli/e2e/assets/18.0-project/src/index.html b/tests/legacy-cli/e2e/assets/18.0-project/src/index.html deleted file mode 100644 index ff4948e77fd2..000000000000 --- a/tests/legacy-cli/e2e/assets/18.0-project/src/index.html +++ /dev/null @@ -1,13 +0,0 @@ - - - - - EighteenProject - - - - - - - - diff --git a/tests/legacy-cli/e2e/assets/add-collection-peer-bad/index.js b/tests/legacy-cli/e2e/assets/add-collection-peer-bad/index.js deleted file mode 100644 index 867b3a4eb6fd..000000000000 --- a/tests/legacy-cli/e2e/assets/add-collection-peer-bad/index.js +++ /dev/null @@ -1 +0,0 @@ -exports.default = (options) => tree => tree.create(options.name || 'empty-file-peer-bad', ''); diff --git a/tests/legacy-cli/e2e/assets/add-collection-peer-good/index.js b/tests/legacy-cli/e2e/assets/add-collection-peer-good/index.js deleted file mode 100644 index 4d5dbdca2f69..000000000000 --- a/tests/legacy-cli/e2e/assets/add-collection-peer-good/index.js +++ /dev/null @@ -1 +0,0 @@ -exports.default = (options) => tree => tree.create(options.name || 'empty-file-peer-good', ''); diff --git a/tests/legacy-cli/e2e/assets/add-collection/index.js b/tests/legacy-cli/e2e/assets/add-collection/index.js deleted file mode 100644 index cf404a768650..000000000000 --- a/tests/legacy-cli/e2e/assets/add-collection/index.js +++ /dev/null @@ -1 +0,0 @@ -exports.default = (options) => tree => tree.create(options.name || 'empty-file', ''); diff --git a/tests/legacy-cli/e2e/assets/nested-schematic-dependency/index.js b/tests/legacy-cli/e2e/assets/nested-schematic-dependency/index.js deleted file mode 100644 index cf404a768650..000000000000 --- a/tests/legacy-cli/e2e/assets/nested-schematic-dependency/index.js +++ /dev/null @@ -1 +0,0 @@ -exports.default = (options) => tree => tree.create(options.name || 'empty-file', ''); diff --git a/tests/legacy-cli/e2e/assets/nested-schematic-main/index.js b/tests/legacy-cli/e2e/assets/nested-schematic-main/index.js deleted file mode 100644 index 9763f96fbb58..000000000000 --- a/tests/legacy-cli/e2e/assets/nested-schematic-main/index.js +++ /dev/null @@ -1 +0,0 @@ -exports.default = (options) => require("@angular-devkit/schematics").externalSchematic('empty-app-nested', 'nested', {}); diff --git a/tests/legacy-cli/e2e/assets/schematic-allow-scripts/index.js b/tests/legacy-cli/e2e/assets/schematic-allow-scripts/index.js deleted file mode 100644 index 20fb52c65137..000000000000 --- a/tests/legacy-cli/e2e/assets/schematic-allow-scripts/index.js +++ /dev/null @@ -1,16 +0,0 @@ -const tasks = require("@angular-devkit/schematics/tasks"); - -exports.default = ({ allowScripts, ignoreScripts = false }) => { - return (tree, context) => { - tree.create('/install-test/package.json', JSON.stringify({ - name: 'install-test', - version: '0.0.0', - scripts: { - postinstall: `node run-post.js`, - } - })); - tree.create('/install-test/.npmrc', `ignore-scripts=${ignoreScripts}`); - tree.create('/install-test/run-post.js', 'require("fs").writeFileSync(__dirname + "/post-script-ran", "12345");') - context.addTask(new tasks.NodePackageInstallTask({ workingDirectory: 'install-test', allowScripts })); - }; -}; diff --git a/tests/legacy-cli/e2e/assets/schematic-allow-scripts/schema.json b/tests/legacy-cli/e2e/assets/schematic-allow-scripts/schema.json deleted file mode 100644 index 099432e4063a..000000000000 --- a/tests/legacy-cli/e2e/assets/schematic-allow-scripts/schema.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-07/schema", - "type": "object", - "additionalProperties": false, - "properties": { - "allowScripts": { - "type": "boolean" - }, - "ignoreScripts": { - "type": "boolean" - } - } -} - \ No newline at end of file diff --git a/tests/legacy-cli/e2e/assets/ssr-project-webpack/src/app/app.component.html b/tests/legacy-cli/e2e/assets/ssr-project-webpack/src/app/app.component.html deleted file mode 100644 index a1ab9650d463..000000000000 --- a/tests/legacy-cli/e2e/assets/ssr-project-webpack/src/app/app.component.html +++ /dev/null @@ -1,483 +0,0 @@ - - - - - - - - - - - - - - -
- - -
- - - Rocket Ship - - - - - - - - - - {{ title }} app is running! - - - Rocket Ship Smoke - - - -
- - -

Resources

-

Here are some links to help you get started:

- - - - -

Next Steps

-

What do you want to do next with your app?

- - - -
- - - - - - - - - - - -
- - -
-
ng generate component xyz
-
ng add @angular/material
-
ng add @angular/pwa
-
ng add _____
-
ng test
-
ng build
-
- - - - - - - - - Gray Clouds Background - - - -
- - - - - - - - - - diff --git a/tests/legacy-cli/e2e/assets/ssr-project-webpack/src/index.html b/tests/legacy-cli/e2e/assets/ssr-project-webpack/src/index.html deleted file mode 100644 index ca14f8bfb86f..000000000000 --- a/tests/legacy-cli/e2e/assets/ssr-project-webpack/src/index.html +++ /dev/null @@ -1,13 +0,0 @@ - - - - - 17SsrProjectWebpack - - - - - - - - diff --git a/tests/legacy-cli/e2e/ng-snapshot/package.json b/tests/legacy-cli/e2e/ng-snapshot/package.json deleted file mode 100644 index f2359c3ae218..000000000000 --- a/tests/legacy-cli/e2e/ng-snapshot/package.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "description": "snapshot versions of Angular for e2e testing", - "private": true, - "dependencies": { - "@angular/animations": "github:angular/animations-builds#11097c79350fe45c91d735ff56e4a65033eff2d0", - "@angular/cdk": "github:angular/cdk-builds#fcb6a60ccdf562ea603fa1e69124fb88ea2d0b87", - "@angular/common": "github:angular/common-builds#68a879555e986eadce2c2e491ec5d5a19e07ea69", - "@angular/compiler": "github:angular/compiler-builds#3b84f54190d722d1b9146ec1f103c738693d68eb", - "@angular/compiler-cli": "github:angular/compiler-cli-builds#5835bcbb2511c85162c1ed48a9231f7127ca03d2", - "@angular/core": "github:angular/core-builds#67db87f334964b99f490a8bdb30b3eb068e78db2", - "@angular/forms": "github:angular/forms-builds#1e1465e423f4815000630b320267245949d8f09b", - "@angular/language-service": "github:angular/language-service-builds#bae19c16444af108601fe2a1f4ffbf6d8d1a57da", - "@angular/localize": "github:angular/localize-builds#e7a995f8f170eea18bbb22a865574c638e45104a", - "@angular/material": "github:angular/material-builds#c8dc810b80c0830111d20539873d14445a5927eb", - "@angular/material-moment-adapter": "github:angular/material-moment-adapter-builds#8feb74c86d59345c8f7051fd0992e9a709045e4d", - "@angular/platform-browser": "github:angular/platform-browser-builds#32a7e1cb1eafeb19942d36944231a5cb54f7fdec", - "@angular/platform-browser-dynamic": "github:angular/platform-browser-dynamic-builds#c3ca0dc2c4e17c4926ef6959bb488f7cd5c7ad77", - "@angular/platform-server": "github:angular/platform-server-builds#100517daedde70a9c9086e7d3cbc9ab0f61b752b", - "@angular/router": "github:angular/router-builds#305d28f496501736d694bd26fcb25675ac0b43d3", - "@angular/service-worker": "github:angular/service-worker-builds#87b2fdd7b79302df8af1e8749ac0eef0c6057f55" - } -} 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/tests/build/library/lib-consumption-full-aot.ts b/tests/legacy-cli/e2e/tests/build/library/lib-consumption-full-aot.ts deleted file mode 100644 index 88c80ac61629..000000000000 --- a/tests/legacy-cli/e2e/tests/build/library/lib-consumption-full-aot.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { setTimeout } from 'node:timers/promises'; -import { ng } from '../../../utils/process'; -import { libraryConsumptionSetup } from './setup'; - -export default async function () { - await libraryConsumptionSetup(); - - // Build library in full mode (development) - await ng('build', 'my-lib', '--configuration=development'); - - // Check that the e2e succeeds prod and non prod mode - await ng('e2e', '--configuration=production'); - await setTimeout(500); - await ng('e2e', '--configuration=development'); -} diff --git a/tests/legacy-cli/e2e/tests/build/library/lib-consumption-partial-aot.ts b/tests/legacy-cli/e2e/tests/build/library/lib-consumption-partial-aot.ts deleted file mode 100644 index 5b45a8372f68..000000000000 --- a/tests/legacy-cli/e2e/tests/build/library/lib-consumption-partial-aot.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { setTimeout } from 'node:timers/promises'; -import { ng } from '../../../utils/process'; -import { libraryConsumptionSetup } from './setup'; - -export default async function () { - await libraryConsumptionSetup(); - - // Build library in partial mode (production) - await ng('build', 'my-lib', '--configuration=production'); - - // Check that the e2e succeeds prod and non prod mode - await ng('e2e', '--configuration=production'); - await setTimeout(500); - await ng('e2e', '--configuration=development'); -} diff --git a/tests/legacy-cli/e2e/tests/commands/add/dir.ts b/tests/legacy-cli/e2e/tests/commands/add/dir.ts deleted file mode 100644 index f5fadc486b3d..000000000000 --- a/tests/legacy-cli/e2e/tests/commands/add/dir.ts +++ /dev/null @@ -1,8 +0,0 @@ -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'); - 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 deleted file mode 100644 index d085efadf080..000000000000 --- a/tests/legacy-cli/e2e/tests/commands/add/peer.ts +++ /dev/null @@ -1,24 +0,0 @@ -import assert from 'node:assert/strict'; -import { assetDir } from '../../../utils/assets'; -import { ng } from '../../../utils/process'; - -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', - ); - 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 { stdout: good } = await ng( - 'add', - assetDir('add-collection-peer-good'), - '--skip-confirmation', - ); - assert.doesNotMatch(good, new RegExp(warning), 'peer warning shown on good package'); -} diff --git a/tests/legacy-cli/e2e/tests/commands/add/secure-registry.ts b/tests/legacy-cli/e2e/tests/commands/add/secure-registry.ts deleted file mode 100644 index a27ba708637d..000000000000 --- a/tests/legacy-cli/e2e/tests/commands/add/secure-registry.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { expectFileNotToExist, expectFileToExist } 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 command = ['add', '@angular/pwa', '--skip-confirmation']; - await expectFileNotToExist('public/manifest.webmanifest'); - - // Works with unscoped registry authentication details - if (!isNpm) { - // NPM no longer support unscoped. - 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'); - - // Invalid authentication token - if (isNpm) { - // NPM no longer support unscoped. - await createNpmConfigForAuthentication(false, true); - await expectToFail(() => ng(...command)); - } - - await createNpmConfigForAuthentication(true, true); - await expectToFail(() => ng(...command)); - - await git('clean', '-dxf'); - await installWorkspacePackages(); -} diff --git a/tests/legacy-cli/e2e/tests/i18n/ivy-localize-es2015-e2e.ts b/tests/legacy-cli/e2e/tests/i18n/ivy-localize-es2015-e2e.ts deleted file mode 100644 index 3fe337c4c90f..000000000000 --- a/tests/legacy-cli/e2e/tests/i18n/ivy-localize-es2015-e2e.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { getGlobalVariable } from '../../utils/env'; -import { ng } from '../../utils/process'; -import { langTranslations, setupI18nConfig } from './setup'; - -export default async function () { - // Setup i18n tests and config. - await setupI18nConfig(); - - for (const { lang } of langTranslations) { - // Execute Application E2E tests with dev server - await ng('e2e', `--configuration=${lang}`, '--port=0'); - } -} diff --git a/tests/legacy-cli/e2e/tests/vitest/absolute-include.ts b/tests/legacy-cli/e2e/tests/vitest/absolute-include.ts deleted file mode 100644 index 787fe942edbd..000000000000 --- a/tests/legacy-cli/e2e/tests/vitest/absolute-include.ts +++ /dev/null @@ -1,12 +0,0 @@ -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 } = await ng('test', '--include', path.resolve('src/app/app.spec.ts')); - - assert.match(stdout, /1 passed/, 'Expected 1 test to pass.'); -} diff --git a/tests/legacy-cli/e2e/utils/network.ts b/tests/legacy-cli/e2e/utils/network.ts deleted file mode 100644 index a869d9289ad2..000000000000 --- a/tests/legacy-cli/e2e/utils/network.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { AddressInfo, createServer } from 'node:net'; - -export function findFreePort(): Promise { - return new Promise((resolve, reject) => { - const srv = createServer(); - srv.once('listening', () => { - const port = (srv.address() as AddressInfo).port; - srv.close((e) => (e ? reject(e) : resolve(port))); - }); - srv.once('error', (e) => srv.close(() => reject(e))); - srv.listen(); - }); -} diff --git a/tests/package.json b/tests/package.json index 4aec9553824b..17660ff2192e 100644 --- a/tests/package.json +++ b/tests/package.json @@ -1,7 +1,8 @@ { "devDependencies": { + "@types/tar-stream": "3.1.4", "@angular-devkit/schematics": "workspace:*", - "rxjs": "7.8.2", + "tar-stream": "3.1.7", "tree-kill": "1.2.2" } } diff --git a/tests/legacy-cli/rollup.config.mjs b/tests/rollup.config.mjs similarity index 87% rename from tests/legacy-cli/rollup.config.mjs rename to tests/rollup.config.mjs index 208e4cc78c42..c48299094fef 100644 --- a/tests/legacy-cli/rollup.config.mjs +++ b/tests/rollup.config.mjs @@ -25,7 +25,7 @@ for (const file of testFiles) { export default { input: chunks, - 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 + external: ['undici', 'puppeteer'], // 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/tests/legacy-cli/tsconfig.json b/tests/tsconfig.json similarity index 64% rename from tests/legacy-cli/tsconfig.json rename to tests/tsconfig.json index 235d85b0bb7c..5070cc5b6927 100644 --- a/tests/legacy-cli/tsconfig.json +++ b/tests/tsconfig.json @@ -1,5 +1,5 @@ { - "extends": "../../tsconfig-test.json", + "extends": "../tsconfig-test.json", "compilerOptions": { "paths": {} }, "exclude": ["e2e/assets/**"] } diff --git a/tests/legacy-cli/verdaccio.yaml b/tests/verdaccio.yaml similarity index 100% rename from tests/legacy-cli/verdaccio.yaml rename to tests/verdaccio.yaml diff --git a/tests/legacy-cli/verdaccio_auth.yaml b/tests/verdaccio_auth.yaml similarity index 100% rename from tests/legacy-cli/verdaccio_auth.yaml rename to tests/verdaccio_auth.yaml diff --git a/tools/BUILD.bazel b/tools/BUILD.bazel index 59c817e3aa06..4804a9ee59a6 100644 --- a/tools/BUILD.bazel +++ b/tools/BUILD.bazel @@ -30,12 +30,3 @@ js_binary( ], entry_point = "quicktype_runner.js", ) - -js_binary( - name = "ng_example_db", - data = [ - "example_db_generator.js", - "//:node_modules/zod", - ], - entry_point = "example_db_generator.js", -) diff --git a/tools/bazel/npm_package.bzl b/tools/bazel/npm_package.bzl index d38cebef4579..c6ce650d3971 100644 --- a/tools/bazel/npm_package.bzl +++ b/tools/bazel/npm_package.bzl @@ -119,6 +119,7 @@ def npm_package( name = "npm_package_archive", srcs = [":pkg"], extension = "tgz", - strip_prefix = "./npm_package", + # should not be built unless it is a dependency of another rule + tags = ["manual"], visibility = visibility, ) diff --git a/tools/defaults.bzl b/tools/defaults.bzl index dd4238243f16..d301591a32ba 100644 --- a/tools/defaults.bzl +++ b/tools/defaults.bzl @@ -2,6 +2,7 @@ load("@aspect_bazel_lib//lib:copy_to_bin.bzl", _copy_to_bin = "copy_to_bin") load("@aspect_rules_jasmine//jasmine:defs.bzl", _jasmine_test = "jasmine_test") load("@aspect_rules_js//js:defs.bzl", _js_binary = "js_binary") load("@devinfra//bazel/ts_project:index.bzl", "strict_deps_test") +load("@rules_angular//src/ng_examples_db:index.bzl", _ng_examples_db = "ng_examples_db") load("@rules_angular//src/ng_package:index.bzl", _ng_package = "ng_package") load("@rules_angular//src/ts_project:index.bzl", _ts_project = "ts_project") load("//tools:substitutions.bzl", "substitutions") @@ -77,3 +78,6 @@ def jasmine_test(data = [], args = [], **kwargs): data = data + ["//:node_modules/source-map-support"], **kwargs ) + +def ng_examples_db(**kwargs): + _ng_examples_db(**kwargs) diff --git a/tools/example_db_generator.bzl b/tools/example_db_generator.bzl deleted file mode 100644 index 7a899e3d8de4..000000000000 --- a/tools/example_db_generator.bzl +++ /dev/null @@ -1,12 +0,0 @@ -load("@aspect_rules_js//js:defs.bzl", "js_run_binary") - -def cli_example_db(name, srcs, path, out, data = []): - js_run_binary( - name = name, - outs = [out], - srcs = srcs + data, - tool = "//tools:ng_example_db", - progress_message = "Generating code example database from %s" % path, - mnemonic = "NgExampleSqliteDb", - args = [path, "$(rootpath %s)" % out], - ) 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/toolchains/BUILD.bazel b/tools/toolchains/BUILD.bazel new file mode 100644 index 000000000000..1abb54e5faa7 --- /dev/null +++ b/tools/toolchains/BUILD.bazel @@ -0,0 +1,44 @@ +load("@rules_cc//cc:defs.bzl", "cc_toolchain") +load(":dummy_cc_toolchain.bzl", "dummy_cc_toolchain_config") + +# This is needed following https://github.com/bazel-contrib/rules_nodejs/pull/3859 +toolchain( + name = "node22_windows_no_exec_toolchain", + exec_compatible_with = [], + target_compatible_with = [ + "@platforms//os:windows", + "@platforms//cpu:x86_64", + ], + toolchain = "@node22_windows_amd64//:toolchain", + toolchain_type = "@rules_nodejs//nodejs:toolchain_type", +) + +# This defines a dummy C++ toolchain for Windows. +# Without this, the build fails with "Unable to find a CC toolchain using toolchain resolution". +dummy_cc_toolchain_config(name = "dummy_cc_toolchain_config") + +filegroup(name = "empty") + +cc_toolchain( + name = "dummy_cc_toolchain", + all_files = ":empty", + compiler_files = ":empty", + dwp_files = ":empty", + linker_files = ":empty", + objcopy_files = ":empty", + strip_files = ":empty", + supports_param_files = 0, + toolchain_config = ":dummy_cc_toolchain_config", + toolchain_identifier = "dummy_cc_toolchain", +) + +toolchain( + name = "dummy_cc_windows_no_exec_toolchain", + exec_compatible_with = [], + target_compatible_with = [ + "@platforms//os:windows", + "@platforms//cpu:x86_64", + ], + toolchain = ":dummy_cc_toolchain", + toolchain_type = "@rules_cc//cc:toolchain_type", +) diff --git a/tools/toolchains/dummy_cc_toolchain.bzl b/tools/toolchains/dummy_cc_toolchain.bzl new file mode 100644 index 000000000000..a18a9c780915 --- /dev/null +++ b/tools/toolchains/dummy_cc_toolchain.bzl @@ -0,0 +1,28 @@ +""" +This file defines a dummy C++ toolchain for Windows. +It is needed to satisfy Bazel's toolchain resolution when cross-compiling for Windows on Linux. +Some rules (e.g. rules_nodejs, js_test) or their dependencies may trigger C++ toolchain resolution +even if no actual C++ compilation is performed for the target platform. +Without this, the build fails with "Unable to find a CC toolchain using toolchain resolution". +""" + +load("@rules_cc//cc:defs.bzl", "cc_common") + +def _impl(ctx): + return cc_common.create_cc_toolchain_config_info( + ctx = ctx, + toolchain_identifier = "dummy-toolchain", + host_system_name = "local", + target_system_name = "local", + target_cpu = "x64_windows", + target_libc = "unknown", + compiler = "dummy", + abi_version = "unknown", + abi_libc_version = "unknown", + ) + +dummy_cc_toolchain_config = rule( + implementation = _impl, + attrs = {}, + provides = [CcToolchainConfigInfo], +) 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 ///