diff --git a/.github/workflows/assistant-to-the-branch-manager.yml b/.github/workflows/assistant-to-the-branch-manager.yml index 5431306096a7..f50263aad6ab 100644 --- a/.github/workflows/assistant-to-the-branch-manager.yml +++ b/.github/workflows/assistant-to-the-branch-manager.yml @@ -18,6 +18,6 @@ jobs: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false - - uses: angular/dev-infra/github-actions/branch-manager@b137f89f74478e0d2b3902372c538d5656795e9a # main + - uses: angular/dev-infra/github-actions/branch-manager@c09a8058c7eee99ed9cec6213ff967bad6d6b73c # main with: angular-robot-key: ${{ secrets.ANGULAR_ROBOT_PRIVATE_KEY }} diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ad97a6cf125e..6397ee443590 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -21,9 +21,9 @@ jobs: runs-on: ubuntu-latest steps: - name: Initialize environment - uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@b137f89f74478e0d2b3902372c538d5656795e9a # main + uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@c09a8058c7eee99ed9cec6213ff967bad6d6b73c # main - name: Setup Bazel - uses: angular/dev-infra/github-actions/bazel/setup@b137f89f74478e0d2b3902372c538d5656795e9a # main + uses: angular/dev-infra/github-actions/bazel/setup@c09a8058c7eee99ed9cec6213ff967bad6d6b73c # main - name: Install node modules run: pnpm install --frozen-lockfile - name: Generate JSON schema types @@ -44,11 +44,11 @@ jobs: runs-on: ubuntu-latest steps: - name: Initialize environment - uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@b137f89f74478e0d2b3902372c538d5656795e9a # main + uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@c09a8058c7eee99ed9cec6213ff967bad6d6b73c # main - name: Setup Bazel - uses: angular/dev-infra/github-actions/bazel/setup@b137f89f74478e0d2b3902372c538d5656795e9a # main + uses: angular/dev-infra/github-actions/bazel/setup@c09a8058c7eee99ed9cec6213ff967bad6d6b73c # main - name: Setup Bazel RBE - uses: angular/dev-infra/github-actions/bazel/configure-remote@b137f89f74478e0d2b3902372c538d5656795e9a # main + uses: angular/dev-infra/github-actions/bazel/configure-remote@c09a8058c7eee99ed9cec6213ff967bad6d6b73c # main with: google_credential: ${{ secrets.RBE_TRUSTED_BUILDS_USER }} - name: Install node modules @@ -61,11 +61,11 @@ jobs: runs-on: ubuntu-latest steps: - name: Initialize environment - uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@b137f89f74478e0d2b3902372c538d5656795e9a # main + uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@c09a8058c7eee99ed9cec6213ff967bad6d6b73c # main - name: Setup Bazel - uses: angular/dev-infra/github-actions/bazel/setup@b137f89f74478e0d2b3902372c538d5656795e9a # main + uses: angular/dev-infra/github-actions/bazel/setup@c09a8058c7eee99ed9cec6213ff967bad6d6b73c # main - name: Setup Bazel RBE - uses: angular/dev-infra/github-actions/bazel/configure-remote@b137f89f74478e0d2b3902372c538d5656795e9a # main + uses: angular/dev-infra/github-actions/bazel/configure-remote@c09a8058c7eee99ed9cec6213ff967bad6d6b73c # main with: google_credential: ${{ secrets.RBE_TRUSTED_BUILDS_USER }} - name: Install node modules @@ -84,13 +84,13 @@ jobs: runs-on: ubuntu-latest steps: - name: Initialize environment - uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@b137f89f74478e0d2b3902372c538d5656795e9a # main + uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@c09a8058c7eee99ed9cec6213ff967bad6d6b73c # main - name: Install node modules run: pnpm install --frozen-lockfile - name: Setup Bazel - uses: angular/dev-infra/github-actions/bazel/setup@b137f89f74478e0d2b3902372c538d5656795e9a # main + uses: angular/dev-infra/github-actions/bazel/setup@c09a8058c7eee99ed9cec6213ff967bad6d6b73c # main - name: Setup Bazel RBE - uses: angular/dev-infra/github-actions/bazel/configure-remote@b137f89f74478e0d2b3902372c538d5656795e9a # main + uses: angular/dev-infra/github-actions/bazel/configure-remote@c09a8058c7eee99ed9cec6213ff967bad6d6b73c # main with: google_credential: ${{ secrets.RBE_TRUSTED_BUILDS_USER }} - name: Run CLI E2E tests @@ -100,11 +100,11 @@ jobs: runs-on: ubuntu-latest steps: - name: Initialize environment - uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@b137f89f74478e0d2b3902372c538d5656795e9a # main + uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@c09a8058c7eee99ed9cec6213ff967bad6d6b73c # main - name: Setup Bazel - uses: angular/dev-infra/github-actions/bazel/setup@b137f89f74478e0d2b3902372c538d5656795e9a # main + uses: angular/dev-infra/github-actions/bazel/setup@c09a8058c7eee99ed9cec6213ff967bad6d6b73c # main - name: Setup Bazel RBE - uses: angular/dev-infra/github-actions/bazel/configure-remote@b137f89f74478e0d2b3902372c538d5656795e9a # main + uses: angular/dev-infra/github-actions/bazel/configure-remote@c09a8058c7eee99ed9cec6213ff967bad6d6b73c # main with: google_credential: ${{ secrets.RBE_TRUSTED_BUILDS_USER }} - name: Install node modules @@ -137,7 +137,7 @@ jobs: runs-on: windows-2025 steps: - name: Initialize environment - uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@b137f89f74478e0d2b3902372c538d5656795e9a # main + uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@c09a8058c7eee99ed9cec6213ff967bad6d6b73c # main - name: Install node modules run: pnpm install --frozen-lockfile - name: Download built Windows E2E tests @@ -164,13 +164,13 @@ jobs: runs-on: ubuntu-latest steps: - name: Initialize environment - uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@b137f89f74478e0d2b3902372c538d5656795e9a # main + uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@c09a8058c7eee99ed9cec6213ff967bad6d6b73c # main - name: Install node modules run: pnpm install --frozen-lockfile - name: Setup Bazel - uses: angular/dev-infra/github-actions/bazel/setup@b137f89f74478e0d2b3902372c538d5656795e9a # main + uses: angular/dev-infra/github-actions/bazel/setup@c09a8058c7eee99ed9cec6213ff967bad6d6b73c # main - name: Setup Bazel RBE - uses: angular/dev-infra/github-actions/bazel/configure-remote@b137f89f74478e0d2b3902372c538d5656795e9a # main + uses: angular/dev-infra/github-actions/bazel/configure-remote@c09a8058c7eee99ed9cec6213ff967bad6d6b73c # main with: google_credential: ${{ secrets.RBE_TRUSTED_BUILDS_USER }} - name: Run CLI E2E tests @@ -188,13 +188,13 @@ jobs: runs-on: ubuntu-latest steps: - name: Initialize environment - uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@b137f89f74478e0d2b3902372c538d5656795e9a # main + uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@c09a8058c7eee99ed9cec6213ff967bad6d6b73c # main - name: Install node modules run: pnpm install --frozen-lockfile - name: Setup Bazel - uses: angular/dev-infra/github-actions/bazel/setup@b137f89f74478e0d2b3902372c538d5656795e9a # main + uses: angular/dev-infra/github-actions/bazel/setup@c09a8058c7eee99ed9cec6213ff967bad6d6b73c # main - name: Setup Bazel RBE - uses: angular/dev-infra/github-actions/bazel/configure-remote@b137f89f74478e0d2b3902372c538d5656795e9a # main + uses: angular/dev-infra/github-actions/bazel/configure-remote@c09a8058c7eee99ed9cec6213ff967bad6d6b73c # main with: google_credential: ${{ secrets.RBE_TRUSTED_BUILDS_USER }} - name: Run CLI E2E tests @@ -208,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@b137f89f74478e0d2b3902372c538d5656795e9a # main + uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@c09a8058c7eee99ed9cec6213ff967bad6d6b73c # main - name: Install node modules run: pnpm install --frozen-lockfile - name: Setup Bazel - uses: angular/dev-infra/github-actions/bazel/setup@b137f89f74478e0d2b3902372c538d5656795e9a # main + uses: angular/dev-infra/github-actions/bazel/setup@c09a8058c7eee99ed9cec6213ff967bad6d6b73c # main - name: Setup Bazel RBE - uses: angular/dev-infra/github-actions/bazel/configure-remote@b137f89f74478e0d2b3902372c538d5656795e9a # main + uses: angular/dev-infra/github-actions/bazel/configure-remote@c09a8058c7eee99ed9cec6213ff967bad6d6b73c # main with: google_credential: ${{ secrets.RBE_TRUSTED_BUILDS_USER }} - name: Start Sauce Connect @@ -245,11 +245,11 @@ jobs: CIRCLE_BRANCH: ${{ github.ref_name }} steps: - name: Initialize environment - uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@b137f89f74478e0d2b3902372c538d5656795e9a # main + uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@c09a8058c7eee99ed9cec6213ff967bad6d6b73c # main - name: Install node modules run: pnpm install --frozen-lockfile - name: Setup Bazel - uses: angular/dev-infra/github-actions/bazel/setup@b137f89f74478e0d2b3902372c538d5656795e9a # main + uses: angular/dev-infra/github-actions/bazel/setup@c09a8058c7eee99ed9cec6213ff967bad6d6b73c # main - run: pnpm admin snapshots --verbose env: SNAPSHOT_BUILDS_GITHUB_TOKEN: ${{ secrets.SNAPSHOT_BUILDS_GITHUB_TOKEN }} diff --git a/.github/workflows/dev-infra.yml b/.github/workflows/dev-infra.yml index 98d8d726c99b..b85b240f82dc 100644 --- a/.github/workflows/dev-infra.yml +++ b/.github/workflows/dev-infra.yml @@ -16,21 +16,21 @@ jobs: if: github.event_name == 'pull_request_target' runs-on: ubuntu-latest steps: - - uses: angular/dev-infra/github-actions/labeling/pull-request@b137f89f74478e0d2b3902372c538d5656795e9a # main + - uses: angular/dev-infra/github-actions/labeling/pull-request@c09a8058c7eee99ed9cec6213ff967bad6d6b73c # main with: angular-robot-key: ${{ secrets.ANGULAR_ROBOT_PRIVATE_KEY }} post_approval_changes: if: github.event_name == 'pull_request_target' runs-on: ubuntu-latest steps: - - uses: angular/dev-infra/github-actions/post-approval-changes@b137f89f74478e0d2b3902372c538d5656795e9a # main + - uses: angular/dev-infra/github-actions/post-approval-changes@c09a8058c7eee99ed9cec6213ff967bad6d6b73c # main with: angular-robot-key: ${{ secrets.ANGULAR_ROBOT_PRIVATE_KEY }} issue_labels: if: github.event_name == 'issues' runs-on: ubuntu-latest steps: - - uses: angular/dev-infra/github-actions/labeling/issue@b137f89f74478e0d2b3902372c538d5656795e9a # main + - uses: angular/dev-infra/github-actions/labeling/issue@c09a8058c7eee99ed9cec6213ff967bad6d6b73c # main with: angular-robot-key: ${{ secrets.ANGULAR_ROBOT_PRIVATE_KEY }} google-generative-ai-key: ${{ secrets.GOOGLE_GENERATIVE_AI_KEY }} diff --git a/.github/workflows/perf.yml b/.github/workflows/perf.yml index e61e4d1bb083..21a061c26de5 100644 --- a/.github/workflows/perf.yml +++ b/.github/workflows/perf.yml @@ -22,7 +22,7 @@ jobs: workflows: ${{ steps.workflows.outputs.workflows }} steps: - name: Initialize environment - uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@b137f89f74478e0d2b3902372c538d5656795e9a # main + uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@c09a8058c7eee99ed9cec6213ff967bad6d6b73c # main - name: Install node modules run: pnpm install --frozen-lockfile - id: workflows @@ -40,9 +40,9 @@ jobs: workflow: ${{ fromJSON(needs.list.outputs.workflows) }} steps: - name: Initialize environment - uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@b137f89f74478e0d2b3902372c538d5656795e9a # main + uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@c09a8058c7eee99ed9cec6213ff967bad6d6b73c # main - name: Setup Bazel - uses: angular/dev-infra/github-actions/bazel/setup@b137f89f74478e0d2b3902372c538d5656795e9a # main + uses: angular/dev-infra/github-actions/bazel/setup@c09a8058c7eee99ed9cec6213ff967bad6d6b73c # main - 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 7cf337949011..f0eb6e091f47 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -34,9 +34,9 @@ jobs: runs-on: ubuntu-latest steps: - name: Initialize environment - uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@b137f89f74478e0d2b3902372c538d5656795e9a # main + uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@c09a8058c7eee99ed9cec6213ff967bad6d6b73c # main - name: Setup Bazel - uses: angular/dev-infra/github-actions/bazel/setup@b137f89f74478e0d2b3902372c538d5656795e9a # main + uses: angular/dev-infra/github-actions/bazel/setup@c09a8058c7eee99ed9cec6213ff967bad6d6b73c # main - name: Setup ESLint Caching uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 with: @@ -66,17 +66,17 @@ jobs: # 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@b137f89f74478e0d2b3902372c538d5656795e9a # main + uses: angular/dev-infra/github-actions/linting/licenses@c09a8058c7eee99ed9cec6213ff967bad6d6b73c # main build: runs-on: ubuntu-latest steps: - name: Initialize environment - uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@b137f89f74478e0d2b3902372c538d5656795e9a # main + uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@c09a8058c7eee99ed9cec6213ff967bad6d6b73c # main - name: Setup Bazel - uses: angular/dev-infra/github-actions/bazel/setup@b137f89f74478e0d2b3902372c538d5656795e9a # main + uses: angular/dev-infra/github-actions/bazel/setup@c09a8058c7eee99ed9cec6213ff967bad6d6b73c # main - name: Setup Bazel RBE - uses: angular/dev-infra/github-actions/bazel/configure-remote@b137f89f74478e0d2b3902372c538d5656795e9a # main + uses: angular/dev-infra/github-actions/bazel/configure-remote@c09a8058c7eee99ed9cec6213ff967bad6d6b73c # main - name: Install node modules run: pnpm install --frozen-lockfile - name: Build release targets @@ -93,11 +93,11 @@ jobs: runs-on: ubuntu-latest steps: - name: Initialize environment - uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@b137f89f74478e0d2b3902372c538d5656795e9a # main + uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@c09a8058c7eee99ed9cec6213ff967bad6d6b73c # main - name: Setup Bazel - uses: angular/dev-infra/github-actions/bazel/setup@b137f89f74478e0d2b3902372c538d5656795e9a # main + uses: angular/dev-infra/github-actions/bazel/setup@c09a8058c7eee99ed9cec6213ff967bad6d6b73c # main - name: Setup Bazel RBE - uses: angular/dev-infra/github-actions/bazel/configure-remote@b137f89f74478e0d2b3902372c538d5656795e9a # main + uses: angular/dev-infra/github-actions/bazel/configure-remote@c09a8058c7eee99ed9cec6213ff967bad6d6b73c # main - name: Install node modules run: pnpm install --frozen-lockfile - name: Run module and package tests @@ -114,13 +114,13 @@ jobs: runs-on: ubuntu-latest steps: - name: Initialize environment - uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@b137f89f74478e0d2b3902372c538d5656795e9a # main + uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@c09a8058c7eee99ed9cec6213ff967bad6d6b73c # main - name: Install node modules run: pnpm install --frozen-lockfile - name: Setup Bazel - uses: angular/dev-infra/github-actions/bazel/setup@b137f89f74478e0d2b3902372c538d5656795e9a # main + uses: angular/dev-infra/github-actions/bazel/setup@c09a8058c7eee99ed9cec6213ff967bad6d6b73c # main - name: Setup Bazel RBE - uses: angular/dev-infra/github-actions/bazel/configure-remote@b137f89f74478e0d2b3902372c538d5656795e9a # main + uses: angular/dev-infra/github-actions/bazel/configure-remote@c09a8058c7eee99ed9cec6213ff967bad6d6b73c # main - 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:e2e.${{ matrix.subset }}_node${{ matrix.node }} @@ -128,11 +128,11 @@ jobs: runs-on: ubuntu-latest steps: - name: Initialize environment - uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@b137f89f74478e0d2b3902372c538d5656795e9a # main + uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@c09a8058c7eee99ed9cec6213ff967bad6d6b73c # main - name: Setup Bazel - uses: angular/dev-infra/github-actions/bazel/setup@b137f89f74478e0d2b3902372c538d5656795e9a # main + uses: angular/dev-infra/github-actions/bazel/setup@c09a8058c7eee99ed9cec6213ff967bad6d6b73c # main - name: Setup Bazel RBE - uses: angular/dev-infra/github-actions/bazel/configure-remote@b137f89f74478e0d2b3902372c538d5656795e9a # main + uses: angular/dev-infra/github-actions/bazel/configure-remote@c09a8058c7eee99ed9cec6213ff967bad6d6b73c # main - name: Install node modules run: pnpm install --frozen-lockfile - name: Build E2E tests for Windows on Linux @@ -156,7 +156,7 @@ jobs: runs-on: windows-2025 steps: - name: Initialize environment - uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@b137f89f74478e0d2b3902372c538d5656795e9a # main + uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@c09a8058c7eee99ed9cec6213ff967bad6d6b73c # main - name: Install node modules run: pnpm install --frozen-lockfile - name: Download built Windows E2E tests @@ -183,13 +183,13 @@ jobs: runs-on: ubuntu-latest steps: - name: Initialize environment - uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@b137f89f74478e0d2b3902372c538d5656795e9a # main + uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@c09a8058c7eee99ed9cec6213ff967bad6d6b73c # main - name: Install node modules run: pnpm install --frozen-lockfile - name: Setup Bazel - uses: angular/dev-infra/github-actions/bazel/setup@b137f89f74478e0d2b3902372c538d5656795e9a # main + uses: angular/dev-infra/github-actions/bazel/setup@c09a8058c7eee99ed9cec6213ff967bad6d6b73c # main - name: Setup Bazel RBE - uses: angular/dev-infra/github-actions/bazel/configure-remote@b137f89f74478e0d2b3902372c538d5656795e9a # main + uses: angular/dev-infra/github-actions/bazel/configure-remote@c09a8058c7eee99ed9cec6213ff967bad6d6b73c # main - 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:e2e.${{ matrix.subset }}_node${{ matrix.node }} @@ -205,12 +205,12 @@ jobs: runs-on: ubuntu-latest steps: - name: Initialize environment - uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@b137f89f74478e0d2b3902372c538d5656795e9a # main + uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@c09a8058c7eee99ed9cec6213ff967bad6d6b73c # main - name: Install node modules run: pnpm install --frozen-lockfile - name: Setup Bazel - uses: angular/dev-infra/github-actions/bazel/setup@b137f89f74478e0d2b3902372c538d5656795e9a # main + uses: angular/dev-infra/github-actions/bazel/setup@c09a8058c7eee99ed9cec6213ff967bad6d6b73c # main - name: Setup Bazel RBE - uses: angular/dev-infra/github-actions/bazel/configure-remote@b137f89f74478e0d2b3902372c538d5656795e9a # main + uses: angular/dev-infra/github-actions/bazel/configure-remote@c09a8058c7eee99ed9cec6213ff967bad6d6b73c # main - 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:e2e.snapshots.${{ matrix.subset }}_node${{ matrix.node }} diff --git a/.nvmrc b/.nvmrc index df6ae3370f9f..a43a5b943d99 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -24.21.0 +24.22.0 diff --git a/CHANGELOG.md b/CHANGELOG.md index 1922874a08d9..a3c4fa9dd66e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,135 @@ + + +# 22.3.0-next.2 (2026-10-15) + +### @angular/cli + +| Commit | Type | Description | +| --------------------------------------------------------------------------------------------------- | ---- | ----------------------------------------------------------------- | +| [5c656152e](https://github.com/angular/angular-cli/commit/5c656152ef91e2731155f6027a845d8014b53e4f) | fix | accept multi-digit array indices in ng config | +| [2e0f48e3f](https://github.com/angular/angular-cli/commit/2e0f48e3ff40289725d3590fa8e1fc059fbaeb88) | fix | keep ng version working when the package manager is not installed | +| [a1cf33bc4](https://github.com/angular/angular-cli/commit/a1cf33bc45882fb74578cc7d1faeba8cecec7b73) | fix | resolve package name in npm workspace members | + +### @schematics/angular + +| Commit | Type | Description | +| --------------------------------------------------------------------------------------------------- | ---- | ------------------------------------------------------------------------- | +| [639d31f11](https://github.com/angular/angular-cli/commit/639d31f11b102a12776ae8dce6369d6edd1b6ff3) | feat | add use-library-builder migration to migrate from ng-packagr | +| [445cc767e](https://github.com/angular/angular-cli/commit/445cc767e62b72631dd53c0c5324b458fee76608) | fix | keep the workspace build version in the use-application-builder migration | + +### @angular-devkit/schematics + +| Commit | Type | Description | +| --------------------------------------------------------------------------------------------------- | ---- | --------------------------------------------- | +| [b39a5fcda](https://github.com/angular/angular-cli/commit/b39a5fcdac288e317f3b00468c09173a1d306f98) | fix | avoid stack overflow when chaining many rules | + +### @angular/build + +| Commit | Type | Description | +| --------------------------------------------------------------------------------------------------- | ---- | --------------------------------------------------------------------------------- | +| [b975da4b0](https://github.com/angular/angular-cli/commit/b975da4b016eaa107acc259eb8205135d9396f95) | fix | add CSP nonce to stylesheet and modulepreload link elements | +| [13923047f](https://github.com/angular/angular-cli/commit/13923047f64b6e1063f7f07029c70295b5c99d69) | fix | always use AOT when extracting i18n messages | +| [d2233e225](https://github.com/angular/angular-cli/commit/d2233e225aa5fd507e3ce7bfd35ae2268c036aa4) | fix | coalesce external watcher directories to avoid macOS FSEvents limit | +| [23d42fc80](https://github.com/angular/angular-cli/commit/23d42fc806f174c7da961c35e07a42aa1e00d1e7) | fix | conditionally declare tslib in built library package | +| [f0a74e8f0](https://github.com/angular/angular-cli/commit/f0a74e8f0ea04fd3f839be0120346067440b1350) | fix | disable Vitest fsModuleCache in unit-test builder | +| [2c518a6db](https://github.com/angular/angular-cli/commit/2c518a6dbe8734d247dfe4eb2c1c4af5e169fa94) | fix | enhance build logs and output location for library builder | +| [47c328d3e](https://github.com/angular/angular-cli/commit/47c328d3ef7b409af02f270a48f5e99a520fbe14) | fix | ensure Sass importers return global Promise instances | +| [432aea016](https://github.com/angular/angular-cli/commit/432aea0161840bc135704c040108e46092e61c27) | fix | filter Rolldown warnings and disable .d.ts annotation comments in library builder | +| [39d230e18](https://github.com/angular/angular-cli/commit/39d230e1862f2f626d3fba03bbf51873f3578153) | fix | keep Vite server file watcher disabled in Vitest watch mode | +| [93f238c97](https://github.com/angular/angular-cli/commit/93f238c9781897997b1c64d607f3bb3c91ba1fd1) | fix | rebuild library when transitive stylesheet dependencies change in watch mode | +| [7714a8537](https://github.com/angular/angular-cli/commit/7714a8537589afc6180443cc36acd5149a459c57) | fix | resolve unimported NgModule scopes when filtering unit-test files | +| [ad4845583](https://github.com/angular/angular-cli/commit/ad4845583b8d1b8de26ccc0e45b4e6e58d95b362) | fix | respect tsconfig declarationMap and compilationMode in library builder | +| [01afbf418](https://github.com/angular/angular-cli/commit/01afbf418cf6a7688f3f131988eeba3cdac2bde4) | fix | sort discovered unit-test files for deterministic builds | +| [b640b32e1](https://github.com/angular/angular-cli/commit/b640b32e148ab68f820b390b1d9f03ae9a809266) | fix | use module.registerHooks() for in-memory ESM loader | +| [0a077eeed](https://github.com/angular/angular-cli/commit/0a077eeed92b43fa195bf10f479d39a7aca2d3f7) | perf | avoid invalidating Angular diagnostic cache on stylesheet change | +| [2b5a9a620](https://github.com/angular/angular-cli/commit/2b5a9a620495b4c85bf5cfa3db39b632d88e6ca5) | perf | enable TypeScript incremental build cache in library builder | +| [07845f019](https://github.com/angular/angular-cli/commit/07845f019cf7d70c17f037d711b430a776f96031) | perf | optimize incremental watch rebuilds in AOT compilation | +| [fb7efc862](https://github.com/angular/angular-cli/commit/fb7efc86228164330f68ed86801f9d5bbe9ad5b0) | perf | resolve asset patterns concurrently with bounded concurrency | +| [dcf906a62](https://github.com/angular/angular-cli/commit/dcf906a6261a00f95cfbdb18cf9065e1c966e182) | perf | reuse extendedConfigCache in TypeScript compilation | + +### @angular/ssr + +| Commit | Type | Description | +| --------------------------------------------------------------------------------------------------- | ---- | ------------------------------------------------------ | +| [0b9a52cc9](https://github.com/angular/angular-cli/commit/0b9a52cc9516d28afa0d9ef0d07ec745967faf56) | fix | preserve preload metadata for nested empty-path routes | +| [f2cd8c8cf](https://github.com/angular/angular-cli/commit/f2cd8c8cf7c5aa7e7076c87e9c0c55b3bf880f4b) | fix | preserve query parameters on relative route redirects | +| [b3bbada1e](https://github.com/angular/angular-cli/commit/b3bbada1efd0d3a414cce8843b7284e9560f66fc) | fix | resolve relative redirects against the parent route | + + + + + +# 22.2.3 (2026-10-15) + +### @angular/cli + +| Commit | Type | Description | +| --------------------------------------------------------------------------------------------------- | ---- | ----------------------------------------------------------------- | +| [3158e865f](https://github.com/angular/angular-cli/commit/3158e865ffdd828711d645dff16cc17fd26bb58e) | fix | accept multi-digit array indices in ng config | +| [bcff0e76a](https://github.com/angular/angular-cli/commit/bcff0e76adf73cfa32b02c5256490189675cb132) | fix | keep ng version working when the package manager is not installed | +| [5570c91d2](https://github.com/angular/angular-cli/commit/5570c91d2f38764cd841fcce146f1d13fef49b0c) | fix | resolve package name in npm workspace members | + +### @schematics/angular + +| Commit | Type | Description | +| --------------------------------------------------------------------------------------------------- | ---- | ------------------------------------------------------------------------- | +| [881865f36](https://github.com/angular/angular-cli/commit/881865f364b33b36630aa6ab7a129f7ed665cfa7) | fix | keep the workspace build version in the use-application-builder migration | + +### @angular-devkit/schematics + +| Commit | Type | Description | +| --------------------------------------------------------------------------------------------------- | ---- | --------------------------------------------- | +| [cf1f3b9d4](https://github.com/angular/angular-cli/commit/cf1f3b9d43177e15cf9a00973e6da3baeaf4b2dd) | fix | avoid stack overflow when chaining many rules | + +### @angular/build + +| Commit | Type | Description | +| --------------------------------------------------------------------------------------------------- | ---- | ------------------------------------------------------------------- | +| [1d477d6bc](https://github.com/angular/angular-cli/commit/1d477d6bc27159cb0c78288e047fcadfe6fc31d4) | fix | add CSP nonce to stylesheet and modulepreload link elements | +| [fbe208f83](https://github.com/angular/angular-cli/commit/fbe208f8392ec0ce82d597e8b690f146370bdc81) | fix | always use AOT when extracting i18n messages | +| [c1c4216a6](https://github.com/angular/angular-cli/commit/c1c4216a680891b80670660a621d6736c4aecaf0) | fix | coalesce external watcher directories to avoid macOS FSEvents limit | +| [792990bc2](https://github.com/angular/angular-cli/commit/792990bc2eacdcc8ed4de6ef46e58ceeb5fe8741) | fix | disable Vitest fsModuleCache in unit-test builder | +| [82439399c](https://github.com/angular/angular-cli/commit/82439399cbfb4c1d21d1c80d976021efa9e4a39a) | fix | ensure Sass importers return global Promise instances | +| [a818de415](https://github.com/angular/angular-cli/commit/a818de415e933819e7a2e3e679cd0b2d2be734de) | fix | keep Vite server file watcher disabled in Vitest watch mode | +| [91bad469e](https://github.com/angular/angular-cli/commit/91bad469ec5ce7b2dfd07e394b482d2cef411407) | fix | sort discovered unit-test files for deterministic builds | +| [e84db4af7](https://github.com/angular/angular-cli/commit/e84db4af70ca9be490b462e6d8112524dc8eb6d4) | fix | use module.registerHooks() for in-memory ESM loader | +| [a428021b7](https://github.com/angular/angular-cli/commit/a428021b77dbccf404f39712f1c88bed9b8e4272) | perf | avoid invalidating Angular diagnostic cache on stylesheet change | +| [b0ac394a0](https://github.com/angular/angular-cli/commit/b0ac394a063f084d885df67660224b6a25e2c614) | perf | resolve asset patterns concurrently with bounded concurrency | + +### @angular/ssr + +| Commit | Type | Description | +| --------------------------------------------------------------------------------------------------- | ---- | ------------------------------------------------------ | +| [bd09b98fc](https://github.com/angular/angular-cli/commit/bd09b98fcaff4b76c7fd65999b51bfaa942d1858) | fix | preserve preload metadata for nested empty-path routes | +| [a92546892](https://github.com/angular/angular-cli/commit/a925468929c74c30a3e64b361a6dc5d6fdc1006a) | fix | preserve query parameters on relative route redirects | +| [a2b86cc94](https://github.com/angular/angular-cli/commit/a2b86cc94d408ba4c3d422224b58e26b7a68f692) | fix | resolve relative redirects against the parent route | + + + + + +# 21.2.27 (2026-10-14) + +### @angular/cli + +| Commit | Type | Description | +| --------------------------------------------------------------------------------------------------- | ---- | ------------------- | +| [2e1742adf](https://github.com/angular/angular-cli/commit/2e1742adf28645635bb6cff3cc08ad9c94c7d085) | fix | update zod to 4.6.6 | + + + + + +# 20.3.40 (2026-10-14) + +### @angular/cli + +| Commit | Type | Description | +| --------------------------------------------------------------------------------------------------- | ---- | ------------------- | +| [bfe1908f4](https://github.com/angular/angular-cli/commit/bfe1908f46f5799e839dcf0f162955df59a94cb0) | fix | update zod to 4.6.6 | + + + # 22.3.0-next.1 (2026-10-07) diff --git a/MODULE.bazel b/MODULE.bazel index 1a6f890d5188..69ddf30cdb73 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -19,36 +19,36 @@ bazel_dep(name = "aspect_rules_jasmine", version = "2.0.4") bazel_dep(name = "rules_angular") git_override( module_name = "rules_angular", - commit = "2d9142c529ac31dddf1bb4e7e5a61c696f97a8f4", + commit = "bfe6e98434982bad38c393f1bd472ec33f16cd32", remote = "https://github.com/angular/rules_angular.git", ) bazel_dep(name = "devinfra") git_override( module_name = "devinfra", - commit = "b137f89f74478e0d2b3902372c538d5656795e9a", + commit = "c09a8058c7eee99ed9cec6213ff967bad6d6b73c", remote = "https://github.com/angular/dev-infra.git", ) bazel_dep(name = "rules_browsers") git_override( module_name = "rules_browsers", - commit = "dba44a25ad8789113c37c2aace5574d794faa7ef", + commit = "48a956da3908e5c923f21e6ffd1c72c5605d61fe", remote = "https://github.com/angular/rules_browsers.git", ) node = use_extension("@rules_nodejs//nodejs:extensions.bzl", "node") node.toolchain( node_repositories = { - "24.21.0-darwin_arm64": ("node-v24.21.0-darwin-arm64.tar.gz", "node-v24.21.0-darwin-arm64", "bed7eea5325e1108f32ce5228ddd6a5f0f08a499ee42aa7442aea583702f6057"), - "24.21.0-darwin_amd64": ("node-v24.21.0-darwin-x64.tar.gz", "node-v24.21.0-darwin-x64", "1462cb3b3046b815cf8ea436d3da450ec1a9f11dac7e5a46b0ada5305d7e8097"), - "24.21.0-linux_arm64": ("node-v24.21.0-linux-arm64.tar.xz", "node-v24.21.0-linux-arm64", "6ad1325edbdb5649c379b75a237147a666c95d4f9ae8d340fef2d1575d289ad2"), - "24.21.0-linux_ppc64le": ("node-v24.21.0-linux-ppc64le.tar.xz", "node-v24.21.0-linux-ppc64le", "1936fd64623a2f98d1fb31b456686d10c30e92639899cfefdefa8835d22adccd"), - "24.21.0-linux_s390x": ("node-v24.21.0-linux-s390x.tar.xz", "node-v24.21.0-linux-s390x", "2ef7e2ecbf7a6c2f3d08d106f6b2f279419c6dac89fe91b8cad193af7890082c"), - "24.21.0-linux_amd64": ("node-v24.21.0-linux-x64.tar.xz", "node-v24.21.0-linux-x64", "fd8e59d5a511510f6a298afb548f18c7d2b1be404d8b4a27d94fbe49f56cb2d6"), - "24.21.0-windows_amd64": ("node-v24.21.0-win-x64.zip", "node-v24.21.0-win-x64", "158f7685b44de51f6c0df1d153526cbcd3e1bc739a8dfc607721cef75de9e541"), + "24.22.0-darwin_arm64": ("node-v24.22.0-darwin-arm64.tar.gz", "node-v24.22.0-darwin-arm64", "6a9724d9b50a5f22b4816cded441b5a8d9ddae546191a5e5b7efa5a5e2ee26d5"), + "24.22.0-darwin_amd64": ("node-v24.22.0-darwin-x64.tar.gz", "node-v24.22.0-darwin-x64", "495e3f273c645f3c4677d438304cdfc701729ea5dec5728f5c8e76a2dece4b91"), + "24.22.0-linux_arm64": ("node-v24.22.0-linux-arm64.tar.xz", "node-v24.22.0-linux-arm64", "fef00bb4d36e1c50104a9775a10359beee98f162b4104dd12472c75065c6a143"), + "24.22.0-linux_ppc64le": ("node-v24.22.0-linux-ppc64le.tar.xz", "node-v24.22.0-linux-ppc64le", "edfc950d84a74b4fb4fedc8d2a569976682f6bbbf182961a4e916b7aad856013"), + "24.22.0-linux_s390x": ("node-v24.22.0-linux-s390x.tar.xz", "node-v24.22.0-linux-s390x", "ee3a16f7700188d62cd87b50fde39f8cbd8ab82f25611114e5ed6ef00d914431"), + "24.22.0-linux_amd64": ("node-v24.22.0-linux-x64.tar.xz", "node-v24.22.0-linux-x64", "cc2f601cee82d9a5a233302fe307abc600727035fd7d2c3a3571755989e95008"), + "24.22.0-windows_amd64": ("node-v24.22.0-win-x64.zip", "node-v24.22.0-win-x64", "f9683a19877e12a9b167305c8db71040d812876359de21f0d2e97c3f321f80c3"), }, - node_version = "24.21.0", + node_version = "24.22.0", ) use_repo( node, @@ -131,8 +131,8 @@ use_repo( pnpm = use_extension("@aspect_rules_js//npm:extensions.bzl", "pnpm") pnpm.pnpm( name = "pnpm", - pnpm_version = "12.3.4", - pnpm_version_integrity = "sha512-lhqkH7B32joEpEHZ+OFevAyW2o73ELLrZ7+e58sGEOq9SPH9hfUc/+c4RnhfoPh8VqOocqHYk/hEZ0G1zORUVw==", + pnpm_version = "12.11.2", + pnpm_version_integrity = "sha512-JaUWUqGWeFr18ovabf6NxYLr76SsHRgCIc4TkCMcX7dmN4IupLbkYzznSs3LCnf3Ja8RutOBd/3t2u6BipVcuQ==", ) use_repo(pnpm, "pnpm") diff --git a/MODULE.bazel.lock b/MODULE.bazel.lock index c29e353e22a7..ec705b4eb497 100644 --- a/MODULE.bazel.lock +++ b/MODULE.bazel.lock @@ -93,7 +93,6 @@ "https://bcr.bazel.build/modules/hermetic_launcher/0.0.15/source.json": "aceb903a3bda5131b0a3132ccf77f36f32b96dcb70864049ec8cfdf9c0b0f068", "https://bcr.bazel.build/modules/jq.bzl/0.1.0/MODULE.bazel": "2ce69b1af49952cd4121a9c3055faa679e748ce774c7f1fda9657f936cae902f", "https://bcr.bazel.build/modules/jq.bzl/0.4.0/MODULE.bazel": "a7b39b37589f2b0dad53fd6c1ccaabbdb290330caa920d7ef3e6aad068cd4ab2", - "https://bcr.bazel.build/modules/jq.bzl/0.7.0/MODULE.bazel": "9d382531c42e7a7f6f2ea486aa8f27c605c54f6109e565f04056015e5764deca", "https://bcr.bazel.build/modules/jq.bzl/0.7.1/MODULE.bazel": "2dc4fd806df09b30aa7e0339f141a661f72df6af237f6db1fc4ca5c5045f1772", "https://bcr.bazel.build/modules/jq.bzl/0.7.1/source.json": "0dbd7febb9361e34247c952e749f21f258b5123126248624c0d921c4592eac96", "https://bcr.bazel.build/modules/jsoncpp/1.9.5/MODULE.bazel": "31271aedc59e815656f5736f282bb7509a97c7ecb43e927ac1a37966e0578075", @@ -181,7 +180,6 @@ "https://bcr.bazel.build/modules/rules_nodejs/6.2.0/MODULE.bazel": "ec27907f55eb34705adb4e8257952162a2d4c3ed0f0b3b4c3c1aad1fac7be35e", "https://bcr.bazel.build/modules/rules_nodejs/6.5.0/MODULE.bazel": "546d0cf79f36f9f6e080816045f97234b071c205f4542e3351bd4424282a8810", "https://bcr.bazel.build/modules/rules_nodejs/6.7.3/MODULE.bazel": "c22a48b2a0dbf05a9dc5f83837bbc24c226c1f6e618de3c3a610044c9f336056", - "https://bcr.bazel.build/modules/rules_nodejs/6.7.5/MODULE.bazel": "97e6794043821d23c013baa4a50fd1c599f2e6ae92b06e2c5f1cd7074fd83e7c", "https://bcr.bazel.build/modules/rules_nodejs/6.7.6/MODULE.bazel": "45f203b9c0d721bcb1a5932815f218091fa2169cede2af3d88a8a17a4fb7676a", "https://bcr.bazel.build/modules/rules_nodejs/6.7.6/source.json": "ad9987b7979be1ce93d10ffee9b6af4f84f8835cbc7def27937d632da2e4750e", "https://bcr.bazel.build/modules/rules_pkg/0.7.0/MODULE.bazel": "df99f03fc7934a4737122518bb87e667e62d780b610910f0447665a7e2be62dc", @@ -386,7 +384,7 @@ "aspect_rules_jasmine": "2.0.4", "aspect_tools_telemetry": "0.5.2" }, - "last_notice": 3 + "last_notice": 0 } } }, @@ -447,16 +445,16 @@ }, "@@rules_browsers+//browsers:extensions.bzl%browsers": { "general": { - "bzlTransitiveDigest": "v0vEY6SvVg9HBAENk4KWY5NR6epcg4ucdSnkhcCNv8E=", + "bzlTransitiveDigest": "OZ8V6/fUTaz9GXcrxaCB7fqf29DyphGiugzTRl7yPR8=", "usagesDigest": "FmXYJVoVJlnfUU8x8gObSvu4qWcco/9Faw61aC/wBF0=", "recordedInputs": [], "generatedRepoSpecs": { "rules_browsers_chrome_linux": { "repoRuleId": "@@rules_browsers+//browsers/private:browser_repo.bzl%browser_repo", "attributes": { - "sha256": "272b05d40a44082c82d09beb35c7cb7116564a6a85ff2f7a48bc161129fc5772", + "sha256": "e3f19706550899e76efdadb1ad172c3af2bb89c137a1350fe7f72333d2a4d0a4", "urls": [ - "https://storage.googleapis.com/chrome-for-testing-public/157.0.8089.0/linux64/chrome-headless-shell-linux64.zip" + "https://storage.googleapis.com/chrome-for-testing-public/158.0.8106.0/linux64/chrome-headless-shell-linux64.zip" ], "named_files": { "CHROME-HEADLESS-SHELL": "chrome-headless-shell-linux64/chrome-headless-shell" @@ -472,9 +470,9 @@ "rules_browsers_chrome_mac": { "repoRuleId": "@@rules_browsers+//browsers/private:browser_repo.bzl%browser_repo", "attributes": { - "sha256": "9c28150b8c24fa99ac1e9e39fa07247b61d7f6f15f1c25ca9273c1b4273e2afd", + "sha256": "1f98187b068d2c631d4465a437ce7c4d83f7bde9b6a0e19faae1c9de04eb4a4d", "urls": [ - "https://storage.googleapis.com/chrome-for-testing-public/157.0.8089.0/mac-x64/chrome-headless-shell-mac-x64.zip" + "https://storage.googleapis.com/chrome-for-testing-public/158.0.8106.0/mac-x64/chrome-headless-shell-mac-x64.zip" ], "named_files": { "CHROME-HEADLESS-SHELL": "chrome-headless-shell-mac-x64/chrome-headless-shell" @@ -490,9 +488,9 @@ "rules_browsers_chrome_mac_arm": { "repoRuleId": "@@rules_browsers+//browsers/private:browser_repo.bzl%browser_repo", "attributes": { - "sha256": "a6471f52b4eeb52af88d2f45e16125a9327517fd8b43512fc5561a3318487640", + "sha256": "a564d6b8c498a1c8624e9e14f83ece2d9903db1ec2b491e9fa8fdc7805187dce", "urls": [ - "https://storage.googleapis.com/chrome-for-testing-public/157.0.8089.0/mac-arm64/chrome-headless-shell-mac-arm64.zip" + "https://storage.googleapis.com/chrome-for-testing-public/158.0.8106.0/mac-arm64/chrome-headless-shell-mac-arm64.zip" ], "named_files": { "CHROME-HEADLESS-SHELL": "chrome-headless-shell-mac-arm64/chrome-headless-shell" @@ -508,9 +506,9 @@ "rules_browsers_chrome_win64": { "repoRuleId": "@@rules_browsers+//browsers/private:browser_repo.bzl%browser_repo", "attributes": { - "sha256": "64332865f3d250a66f704e90d37a3d57f9800d6ea774138ede35ea37d1af7f2c", + "sha256": "3a874f0d0d36a84a89c4fedf7e0dfdcad7e4818bbaf22842ac06762305256b87", "urls": [ - "https://storage.googleapis.com/chrome-for-testing-public/157.0.8089.0/win64/chrome-headless-shell-win64.zip" + "https://storage.googleapis.com/chrome-for-testing-public/158.0.8106.0/win64/chrome-headless-shell-win64.zip" ], "named_files": { "CHROME-HEADLESS-SHELL": "chrome-headless-shell-win64/chrome-headless-shell.exe" @@ -526,9 +524,9 @@ "rules_browsers_chromedriver_linux": { "repoRuleId": "@@rules_browsers+//browsers/private:browser_repo.bzl%browser_repo", "attributes": { - "sha256": "0901026cbac4cce608a95bcbdcc87166e7736c9cc0431f5d3466058c9e570aec", + "sha256": "51b3afc67a5765403601420074ad7091e083133ca8f82e878a49b783614256df", "urls": [ - "https://storage.googleapis.com/chrome-for-testing-public/157.0.8089.0/linux64/chromedriver-linux64.zip" + "https://storage.googleapis.com/chrome-for-testing-public/158.0.8106.0/linux64/chromedriver-linux64.zip" ], "named_files": { "CHROMEDRIVER": "chromedriver-linux64/chromedriver" @@ -542,9 +540,9 @@ "rules_browsers_chromedriver_mac": { "repoRuleId": "@@rules_browsers+//browsers/private:browser_repo.bzl%browser_repo", "attributes": { - "sha256": "69b0982bfe954c969cbc9357a39efb2f1861447f4168017831ffafec62448d90", + "sha256": "944924ff8ba2ca15407e788f1d36918e4979f9f5329d0bdf8689c9a96c1818fc", "urls": [ - "https://storage.googleapis.com/chrome-for-testing-public/157.0.8089.0/mac-x64/chromedriver-mac-x64.zip" + "https://storage.googleapis.com/chrome-for-testing-public/158.0.8106.0/mac-x64/chromedriver-mac-x64.zip" ], "named_files": { "CHROMEDRIVER": "chromedriver-mac-x64/chromedriver" @@ -558,9 +556,9 @@ "rules_browsers_chromedriver_mac_arm": { "repoRuleId": "@@rules_browsers+//browsers/private:browser_repo.bzl%browser_repo", "attributes": { - "sha256": "18d30af4843f5535d56ab615702749650f1fc9c6b67ec3fd419ed5a9d4fcbf58", + "sha256": "2b46fd5f3838e6a2ea73e29e67537c97f43a23eeddbf5522ac15cd27f05a4987", "urls": [ - "https://storage.googleapis.com/chrome-for-testing-public/157.0.8089.0/mac-arm64/chromedriver-mac-arm64.zip" + "https://storage.googleapis.com/chrome-for-testing-public/158.0.8106.0/mac-arm64/chromedriver-mac-arm64.zip" ], "named_files": { "CHROMEDRIVER": "chromedriver-mac-arm64/chromedriver" @@ -574,9 +572,9 @@ "rules_browsers_chromedriver_win64": { "repoRuleId": "@@rules_browsers+//browsers/private:browser_repo.bzl%browser_repo", "attributes": { - "sha256": "9b79fc95eed9212d5888a1a7de456eba68df54d65b6fb490ce34d55efdc5cd48", + "sha256": "5c38e2e1d06b51e34885fb0474bf74a5bb36e4a6a517327df8d74f49d7cf9dd8", "urls": [ - "https://storage.googleapis.com/chrome-for-testing-public/157.0.8089.0/win64/chromedriver-win64.zip" + "https://storage.googleapis.com/chrome-for-testing-public/158.0.8106.0/win64/chromedriver-win64.zip" ], "named_files": { "CHROMEDRIVER": "chromedriver-win64/chromedriver.exe" @@ -590,9 +588,9 @@ "rules_browsers_firefox_linux": { "repoRuleId": "@@rules_browsers+//browsers/private:browser_repo.bzl%browser_repo", "attributes": { - "sha256": "42f2c62a562316982ef5a796738c57602bf84a984f5c616e80bcff4627f78fff", + "sha256": "9549b054ec4cf85f50f6a19d8c6fb79580312e71ebc4ec2cb217ec9af09dc12a", "urls": [ - "https://archive.mozilla.org/pub/firefox/releases/157.0/linux-x86_64/en-US/firefox-157.0.tar.xz" + "https://archive.mozilla.org/pub/firefox/releases/158.0/linux-x86_64/en-US/firefox-158.0.tar.xz" ], "named_files": { "FIREFOX": "firefox/firefox" @@ -606,9 +604,9 @@ "rules_browsers_firefox_mac": { "repoRuleId": "@@rules_browsers+//browsers/private:browser_repo.bzl%browser_repo", "attributes": { - "sha256": "df6d8c79b6126bcf24cb70ebab9371916a662028e2d706e462d432e78fdf396c", + "sha256": "dfe8508ee8880cec1578253eb02b9154b5971a55e832d97396f463cd07b5ef67", "urls": [ - "https://archive.mozilla.org/pub/firefox/releases/157.0/mac/en-US/Firefox%20157.0.dmg" + "https://archive.mozilla.org/pub/firefox/releases/158.0/mac/en-US/Firefox%20158.0.dmg" ], "named_files": { "FIREFOX": "Firefox.app/Contents/MacOS/firefox" @@ -622,9 +620,9 @@ "rules_browsers_firefox_mac_arm": { "repoRuleId": "@@rules_browsers+//browsers/private:browser_repo.bzl%browser_repo", "attributes": { - "sha256": "df6d8c79b6126bcf24cb70ebab9371916a662028e2d706e462d432e78fdf396c", + "sha256": "dfe8508ee8880cec1578253eb02b9154b5971a55e832d97396f463cd07b5ef67", "urls": [ - "https://archive.mozilla.org/pub/firefox/releases/157.0/mac/en-US/Firefox%20157.0.dmg" + "https://archive.mozilla.org/pub/firefox/releases/158.0/mac/en-US/Firefox%20158.0.dmg" ], "named_files": { "FIREFOX": "Firefox.app/Contents/MacOS/firefox" @@ -638,9 +636,9 @@ "rules_browsers_firefox_win64": { "repoRuleId": "@@rules_browsers+//browsers/private:browser_repo.bzl%browser_repo", "attributes": { - "sha256": "58c90afab6e4b9a6b34d2958fe06d1a143dd9226ea8834377e5cd45e815936d7", + "sha256": "450ab1c772e02f0c9ef02f489b153d86c45bd9311b202d10dc233f3c0206f8d1", "urls": [ - "https://archive.mozilla.org/pub/firefox/releases/157.0/win64/en-US/Firefox%20Setup%20157.0.exe" + "https://archive.mozilla.org/pub/firefox/releases/158.0/win64/en-US/Firefox%20Setup%20158.0.exe" ], "named_files": { "FIREFOX": "core/firefox.exe" @@ -1071,6 +1069,9 @@ } }, "facts": { + "@@aspect_rules_js+//npm:extensions.bzl%pnpm": { + "exe:12.11.2": "{\"darwin-arm64\":\"sha512-n9M4On66y8FDxUAKgMO1FXt0qihyG4e+56p3/F0uo8Bx9KplW8BwKVnXigzCJ8aNM6sVVPkOE6M9XXbeVIxnZA==\",\"darwin-x64\":\"sha512-oSW7n3sBT0c0cyk4kj10T2Y6Mpm+OFEiCAWozgX039tdoI6yqJF1uaFD3sw7ugLdu6VV3+uY0pa1TUZIPdxDWA==\",\"linux-arm64\":\"sha512-6e8ZAQUwFXAWHHHv5FesWqf/kEK04wuitBv5f/gxzkC0DiBfaixr+f+6JQede8SF4Ji5LcMwVFJbzAtffpWxEg==\",\"linux-arm64-musl\":\"sha512-uMEYDwKRktGwANubQ+kYCwmhNwg3Ik6qK89ahsbQ5cP30R/5FVJb/T99vr9XRUBC/A7mZI6eeT92oyVas9ACyg==\",\"linux-x64\":\"sha512-FK7QKe24AYgppJwO3mpooYptduuvIGqKJPi5ac7MdnWkjUy46JBfYhCW0grnkWKC8YOf0x+vGviuQqJEj3SpQQ==\",\"linux-x64-musl\":\"sha512-7eIxQR05fvJxCujUSKp5KTtehJ7QqrgqP/HD1+BcB/SaV2Be4XI1PEHiOgA1zxsiMIeMvTF6E4G4dYHrFjeXRg==\",\"win32-arm64\":\"sha512-PCTjVDdJb6VvraZxrEeFv6EqEA0hhIsAySx28e95J2Tqpi88hLpgOXgNqV0vpxq9A+b8GLQfyk/D+1UlJPv/YQ==\",\"win32-x64\":\"sha512-8sgX86OvRjJ3yZs/TvGR2L31wH9ScPZ+f+geKI3ikuB0RRiK3i4LlTRO8OXN6pdRqj8epMde4312MCjj7JsJdA==\"}" + }, "@@aspect_tools_telemetry+//:extension.bzl%telemetry": { "notice_version": "3" } diff --git a/modules/testing/builder/package.json b/modules/testing/builder/package.json index 8823cbf0c68e..398a200cdf8a 100644 --- a/modules/testing/builder/package.json +++ b/modules/testing/builder/package.json @@ -4,12 +4,12 @@ "@angular-devkit/build-angular": "workspace:*", "@angular-devkit/core": "workspace:*", "@angular/ssr": "workspace:*", - "@vitest/coverage-v8": "5.0.3", + "@vitest/coverage-v8": "5.0.4", "browser-sync": "3.0.4", "istanbul-lib-instrument": "6.0.3", "jsdom": "30.1.2", "ng-packagr": "22.3.0-next.2", "rxjs": "7.8.2", - "vitest": "5.0.3" + "vitest": "5.0.4" } } diff --git a/modules/testing/builder/projects/hello-world-lib/projects/lib/tsconfig.lib.json b/modules/testing/builder/projects/hello-world-lib/projects/lib/tsconfig.lib.json index fe12470400e2..23e851ac9c8b 100644 --- a/modules/testing/builder/projects/hello-world-lib/projects/lib/tsconfig.lib.json +++ b/modules/testing/builder/projects/hello-world-lib/projects/lib/tsconfig.lib.json @@ -2,7 +2,6 @@ "extends": "../../tsconfig.json", "compilerOptions": { "declaration": true, - "declarationMap": true, "inlineSources": true, "types": [] }, diff --git a/package.json b/package.json index 1037a8bfa7c6..911eea20e9f6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@angular/devkit-repo", - "version": "22.3.0-next.1", + "version": "22.3.0-next.2", "private": true, "description": "Software Development Kit for Angular", "keywords": [ @@ -29,12 +29,12 @@ "type": "git", "url": "git+https://github.com/angular/angular-cli.git" }, - "packageManager": "pnpm@12.10.1", + "packageManager": "pnpm@12.11.2", "engines": { "node": "^22.22.3 || ^24.15.0 || >=26.0.0", "npm": "Please use pnpm instead of NPM to install dependencies", "yarn": "Please use pnpm instead of Yarn to install dependencies", - "pnpm": "12.10.1" + "pnpm": "12.11.2" }, "author": "Angular Authors", "license": "MIT", @@ -43,23 +43,23 @@ }, "homepage": "https://github.com/angular/angular-cli", "dependencies": { - "@angular/compiler-cli": "22.3.0-next.1", + "@angular/compiler-cli": "22.3.0-next.2", "typescript": "6.0.3" }, "devDependencies": { - "@angular/animations": "22.3.0-next.1", - "@angular/cdk": "22.3.0-next.1", - "@angular/common": "22.3.0-next.1", - "@angular/compiler": "22.3.0-next.1", - "@angular/core": "22.3.0-next.1", - "@angular/forms": "22.3.0-next.1", - "@angular/localize": "22.3.0-next.1", - "@angular/material": "22.3.0-next.1", - "@angular/ng-dev": "https://github.com/angular/dev-infra-private-ng-dev-builds.git#89b8c002e5d47ba1cbc4483d3ec06b0ae0aaccb7", - "@angular/platform-browser": "22.3.0-next.1", - "@angular/platform-server": "22.3.0-next.1", - "@angular/router": "22.3.0-next.1", - "@angular/service-worker": "22.3.0-next.1", + "@angular/animations": "22.3.0-next.2", + "@angular/cdk": "22.3.0-next.2", + "@angular/common": "22.3.0-next.2", + "@angular/compiler": "22.3.0-next.2", + "@angular/core": "22.3.0-next.2", + "@angular/forms": "22.3.0-next.2", + "@angular/localize": "22.3.0-next.2", + "@angular/material": "22.3.0-next.2", + "@angular/ng-dev": "https://github.com/angular/dev-infra-private-ng-dev-builds.git#940c0beef0de4cb128e90962d47ff80e352a1cd8", + "@angular/platform-browser": "22.3.0-next.2", + "@angular/platform-server": "22.3.0-next.2", + "@angular/router": "22.3.0-next.2", + "@angular/service-worker": "22.3.0-next.2", "@babel/core": "8.0.7", "@bazel/bazelisk": "1.28.1", "@bazel/buildifier": "8.2.1", @@ -70,7 +70,7 @@ "@rollup/plugin-commonjs": "^29.0.0", "@rollup/plugin-json": "^6.1.0", "@rollup/plugin-node-resolve": "16.0.3", - "@rollup/wasm-node": "4.64.1", + "@rollup/wasm-node": "4.64.6", "@stylistic/eslint-plugin": "^5.0.0", "@tony.ganchev/eslint-plugin-header": "~3.4.0", "@types/babel__core": "7.20.5", @@ -91,8 +91,8 @@ "@types/semver": "^7.3.12", "@types/yargs": "^17.0.20", "@types/yargs-parser": "^21.0.0", - "@typescript-eslint/eslint-plugin": "8.71.1", - "@typescript-eslint/parser": "8.71.1", + "@typescript-eslint/eslint-plugin": "8.71.2", + "@typescript-eslint/parser": "8.71.2", "ajv": "8.20.0", "buffer": "6.0.3", "esbuild": "0.28.2", @@ -100,7 +100,7 @@ "eslint": "10.12.0", "eslint-config-prettier": "10.1.8", "eslint-plugin-import": "2.32.0", - "express": "5.2.1", + "express": "5.3.0", "fast-glob": "3.3.3", "globals": "17.13.0", "http-proxy": "^1.18.1", @@ -119,15 +119,15 @@ "lodash": "^4.17.21", "magic-string": "1.4.3", "prettier": "^3.0.0", - "puppeteer": "25.12.0", + "puppeteer": "25.14.0", "quicktype-core": "26.0.0", - "rollup": "4.64.1", + "rollup": "4.64.6", "rollup-plugin-dts": "6.5.1", "rollup-plugin-sourcemaps2": "0.5.9", "semver": "7.8.5", "source-map-support": "0.5.21", "tslib": "2.8.1", - "undici": "8.11.2", + "undici": "8.12.1", "verdaccio": "6.10.5", "verdaccio-auth-memory": "^13.0.0", "zone.js": "^0.16.0" diff --git a/packages/angular/build/BUILD.bazel b/packages/angular/build/BUILD.bazel index bf6c034292df..d0ab55750e98 100644 --- a/packages/angular/build/BUILD.bazel +++ b/packages/angular/build/BUILD.bazel @@ -153,6 +153,7 @@ ts_project( ":build", ":node_modules/@angular-devkit/core", ":node_modules/@babel/core", + ":node_modules/@parcel/watcher", ":node_modules/oxc-parser", "//:node_modules/@angular/compiler-cli", "//:node_modules/@angular/router", diff --git a/packages/angular/build/package.json b/packages/angular/build/package.json index 2aa20f0c6b8f..711ab5893f75 100644 --- a/packages/angular/build/package.json +++ b/packages/angular/build/package.json @@ -24,27 +24,27 @@ "@inquirer/confirm": "6.3.3", "@parcel/watcher": "2.6.0", "@vitejs/plugin-basic-ssl": "2.3.0", - "beasties": "0.5.4", + "beasties": "0.5.5", "browserslist": "^4.26.0", "chokidar": "5.0.0", "esbuild": "0.28.2", "https-proxy-agent": "9.1.0", "jsonc-parser": "3.3.1", - "listr2": "11.1.1", + "listr2": "11.2.0", "magic-string": "1.4.3", "mrmime": "2.0.1", - "oxc-parser": "0.153.0", + "oxc-parser": "0.154.0", "parse5-html-rewriting-stream": "8.0.1", "picomatch": "4.0.7", "piscina": "5.3.2", - "rolldown": "1.2.12", - "rolldown-plugin-dts": "0.28.6", + "rolldown": "1.2.14", + "rolldown-plugin-dts": "0.28.8", "sass": "1.105.1", "sass-embedded": "1.105.1", "semver": "7.8.5", "source-map-support": "0.5.21", "tinyglobby": "0.2.17", - "vite": "8.3.3", + "vite": "8.3.4", "xxhash-wasm": "1.1.0" }, "optionalDependencies": { @@ -53,16 +53,16 @@ "devDependencies": { "@angular-devkit/core": "workspace:*", "@angular/ssr": "workspace:*", - "@oxc-project/types": "0.153.0", - "@vitest/coverage-v8": "5.0.3", + "@oxc-project/types": "0.154.0", + "@vitest/coverage-v8": "5.0.4", "istanbul-lib-instrument": "6.0.3", "jsdom": "30.1.2", "less": "4.9.1", "ng-packagr": "22.3.0-next.2", - "postcss": "8.5.29", - "rollup": "4.64.1", + "postcss": "8.5.30", + "rollup": "4.64.6", "rxjs": "7.8.2", - "vitest": "5.0.3" + "vitest": "5.0.4" }, "peerDependencies": { "@angular/compiler": "0.0.0-ANGULAR-FW-PEER-DEP", diff --git a/packages/angular/build/src/builders/application/build-action.ts b/packages/angular/build/src/builders/application/build-action.ts index b728ab9a8df5..eaae50848a48 100644 --- a/packages/angular/build/src/builders/application/build-action.ts +++ b/packages/angular/build/src/builders/application/build-action.ts @@ -333,7 +333,7 @@ function* emitOutputResults( incrementalResult.files.push({ path: destination, type: BuildOutputFileType.Browser, - inputPath: source, + source, origin: 'disk', }); } @@ -406,7 +406,7 @@ function createFullResult( ...assetFiles.map(({ source, destination }) => ({ path: destination, type: BuildOutputFileType.Browser, - inputPath: source, + source, origin: 'disk' as const, })), ...outputFiles.map((file) => ({ diff --git a/packages/angular/build/src/builders/application/index.ts b/packages/angular/build/src/builders/application/index.ts index 59ded1cd8182..db2cee237896 100644 --- a/packages/angular/build/src/builders/application/index.ts +++ b/packages/angular/build/src/builders/application/index.ts @@ -8,12 +8,12 @@ import { Builder, BuilderContext, BuilderOutput, createBuilder } from '@angular-devkit/architect'; import assert from 'node:assert'; -import fs from 'node:fs/promises'; import path from 'node:path'; import { BuildOutputFileType } from '../../tools/esbuild/bundler-files'; -import { createJsonBuildManifest, emitFilesToDisk } from '../../tools/esbuild/utils'; +import { createJsonBuildManifest } from '../../tools/esbuild/utils'; import { colors as ansiColors } from '../../utils/color'; import { deleteOutputDir } from '../../utils/delete-output-dir'; +import { deleteFilesFromDisk, emitFilesToDisk } from '../../utils/disk-files'; import { bazelEsbuildPluginPath, useJSONBuildLogs } from '../../utils/environment-options'; import { isSubDirectory } from '../../utils/path'; import { purgeStaleBuildCache } from '../../utils/purge-cache'; @@ -197,37 +197,14 @@ export async function* buildApplication( } // Writes the output files to disk and ensures the containing directories are present - const directoryExists = new Set(); try { - await emitFilesToDisk(result.files, async (file) => { - if ( - outputOptions.ignoreServer && - (file.type === BuildOutputFileType.ServerApplication || - file.type === BuildOutputFileType.ServerRoot) - ) { - return; - } - - const fullFilePath = generateFullPath(file.path, file.type, outputOptions); - - // Ensure output subdirectories exist - const fileBasePath = path.dirname(fullFilePath); - if (fileBasePath && !directoryExists.has(fileBasePath)) { - await fs.mkdir(fileBasePath, { recursive: true }); - directoryExists.add(fileBasePath); - } - - if (file.origin === 'memory') { - // Write file contents - await fs.writeFile(fullFilePath, file.contents); - } else { - // Copy file contents - await fs.cp(file.inputPath, fullFilePath, { - mode: fs.constants.COPYFILE_FICLONE, - preserveTimestamps: true, - }); - } - }); + await emitFilesToDisk(result.files, (file) => + outputOptions.ignoreServer && + (file.type === BuildOutputFileType.ServerApplication || + file.type === BuildOutputFileType.ServerRoot) + ? undefined + : generateFullPath(file.path, file.type, outputOptions), + ); } catch (error) { context.logger.error(error instanceof Error ? error.message : String(error)); yield { success: false }; @@ -237,13 +214,10 @@ export async function* buildApplication( // Delete any removed files if incremental if (result.kind === ResultKind.Incremental && result.removed?.length) { try { - await Promise.all( - result.removed.map((file) => { - const fullFilePath = generateFullPath(file.path, file.type, outputOptions); - - return fs.rm(fullFilePath, { force: true, maxRetries: 3 }); - }), + const filesToDelete = result.removed.map((file) => + generateFullPath(file.path, file.type, outputOptions), ); + await deleteFilesFromDisk(filesToDelete); } catch (error) { context.logger.error(error instanceof Error ? error.message : String(error)); yield { success: false }; diff --git a/packages/angular/build/src/builders/application/options.ts b/packages/angular/build/src/builders/application/options.ts index 322c2dbef897..0e225be24045 100644 --- a/packages/angular/build/src/builders/application/options.ts +++ b/packages/angular/build/src/builders/application/options.ts @@ -287,7 +287,7 @@ export async function normalizeOptions( // Skip tailwind configuration if postcss is customized const tailwindConfiguration = postcssConfiguration ? undefined - : await getTailwindConfig(searchDirectories, workspaceRoot, context.logger); + : getTailwindConfig(searchDirectories, workspaceRoot, context.logger); let serverEntryPoint: string | undefined; if (typeof options.server === 'string') { diff --git a/packages/angular/build/src/builders/application/results.ts b/packages/angular/build/src/builders/application/results.ts index 8e95176639f5..e9bd786a3fe6 100644 --- a/packages/angular/build/src/builders/application/results.ts +++ b/packages/angular/build/src/builders/application/results.ts @@ -53,7 +53,7 @@ export interface BaseResultFile { export interface DiskFile extends BaseResultFile { origin: 'disk'; - inputPath: string; + source: string; } export interface MemoryFile extends BaseResultFile { diff --git a/packages/angular/build/src/builders/application/tests/behavior/rebuild-errors_spec.ts b/packages/angular/build/src/builders/application/tests/behavior/rebuild-errors_spec.ts index de53c7223fff..5e7bdeb3fd9c 100644 --- a/packages/angular/build/src/builders/application/tests/behavior/rebuild-errors_spec.ts +++ b/packages/angular/build/src/builders/application/tests/behavior/rebuild-errors_spec.ts @@ -15,12 +15,6 @@ import { expectNoLog, } from '../setup'; -/** - * Maximum time in milliseconds for single build/rebuild - * This accounts for CI variability. - */ -export const BUILD_TIMEOUT = 30_000; - describeBuilder(buildApplication, APPLICATION_BUILDER_INFO, (harness) => { describe('Behavior: "Rebuild Error Detection"', () => { it('detects template errors with no AOT codegen or TS emit differences', async () => { @@ -128,6 +122,235 @@ describeBuilder(buildApplication, APPLICATION_BUILDER_INFO, (harness) => { ); }); + it('detects template errors across components on watch rebuild when isolatedModules is enabled', async () => { + harness.useTarget('build', { + ...BASE_OPTIONS, + watch: true, + }); + + await harness.modifyFile('tsconfig.json', (content) => { + const tsconfig = JSON.parse(content); + tsconfig.compilerOptions = { + ...tsconfig.compilerOptions, + isolatedModules: true, + }; + + return JSON.stringify(tsconfig); + }); + + const goodChildComponentContents = ` + import { Component, Input } from '@angular/core'; + @Component({ + selector: 'child', + standalone: true, + template: '

{{ value }}

', + }) + export class ChildComponent { + @Input() value!: number; + } + `; + + const typeErrorText = `Type 'number' is not assignable to type 'string'.`; + + await harness.writeFiles({ + 'src/app/child.component.ts': goodChildComponentContents, + 'src/app/app.module.ts': ` + import { NgModule } from '@angular/core'; + import { BrowserModule } from '@angular/platform-browser'; + import { AppComponent } from './app.component'; + @NgModule({ + imports: [ + BrowserModule, + AppComponent, + ], + providers: [], + bootstrap: [AppComponent] + }) + export class AppModule { } + `, + 'src/app/app.component.ts': ` + import { Component } from '@angular/core'; + import { ChildComponent } from './child.component'; + @Component({ + selector: 'app-root', + standalone: true, + imports: [ChildComponent], + template: '', + }) + export class AppComponent {} + `, + }); + + await harness.executeWithCases( + [ + async ({ result }) => { + expect(result?.success).toBeTrue(); + + // Update child component to change input type from number to string + await harness.writeFile( + 'src/app/child.component.ts', + ` + import { Component, Input } from '@angular/core'; + @Component({ + selector: 'child', + standalone: true, + template: '

{{ value }}

', + }) + export class ChildComponent { + @Input() value!: string; + } + `, + ); + }, + async ({ result, logs }) => { + expect(result?.success).toBeFalse(); + expectLog(logs, typeErrorText); + + // Make an unrelated change to verify error persists + await harness.modifyFile('src/main.ts', (content) => content + '\n'); + }, + async ({ result, logs }) => { + expect(result?.success).toBeFalse(); + expectLog(logs, typeErrorText); + + // Revert back to number + await harness.writeFile('src/app/child.component.ts', goodChildComponentContents); + }, + async ({ result, logs }) => { + expect(result?.success).toBeTrue(); + expectNoLog(logs, typeErrorText); + + // Make an unrelated change to verify error cache cleared + await harness.modifyFile('src/main.ts', (content) => content + '\n'); + }, + ({ result, logs }) => { + expect(result?.success).toBeTrue(); + expectNoLog(logs, typeErrorText); + }, + ], + { outputLogsOnFailure: false }, + ); + }); + + it('detects template errors when an input becomes required or a selector is renamed on watch rebuild', async () => { + harness.useTarget('build', { + ...BASE_OPTIONS, + watch: true, + }); + + await harness.modifyFile('tsconfig.json', (content) => { + const tsconfig = JSON.parse(content); + tsconfig.compilerOptions = { + ...tsconfig.compilerOptions, + isolatedModules: true, + }; + + return JSON.stringify(tsconfig); + }); + + const goodChildComponentContents = ` + import { Component, input } from '@angular/core'; + @Component({ + selector: 'app-child', + standalone: true, + template: '

{{ label() }}

', + }) + export class ChildComponent { + label = input(''); + } + `; + + const requiredInputErrorText = `Required input 'label' from component ChildComponent must be specified.`; + const unknownElementErrorText = `'app-child' is not a known element`; + + await harness.writeFiles({ + 'src/app/child.component.ts': goodChildComponentContents, + 'src/app/app.module.ts': ` + import { NgModule } from '@angular/core'; + import { BrowserModule } from '@angular/platform-browser'; + import { AppComponent } from './app.component'; + @NgModule({ + imports: [ + BrowserModule, + AppComponent, + ], + providers: [], + bootstrap: [AppComponent] + }) + export class AppModule { } + `, + 'src/app/app.component.ts': ` + import { Component } from '@angular/core'; + import { ChildComponent } from './child.component'; + @Component({ + selector: 'app-root', + standalone: true, + imports: [ChildComponent], + template: '', + }) + export class AppComponent {} + `, + }); + + await harness.executeWithCases( + [ + async ({ result }) => { + expect(result?.success).toBeTrue(); + + // Make input required using signal input.required() (which has the same TS .d.ts type InputSignal) + await harness.writeFile( + 'src/app/child.component.ts', + ` + import { Component, input } from '@angular/core'; + @Component({ + selector: 'app-child', + standalone: true, + template: '

{{ label() }}

', + }) + export class ChildComponent { + label = input.required(); + } + `, + ); + }, + async ({ result, logs }) => { + expect(result?.success).toBeFalse(); + expectLog(logs, requiredInputErrorText); + + // Revert required input and rename selector instead + await harness.writeFile( + 'src/app/child.component.ts', + ` + import { Component, input } from '@angular/core'; + @Component({ + selector: 'app-child-renamed', + standalone: true, + template: '

{{ label() }}

', + }) + export class ChildComponent { + label = input(''); + } + `, + ); + }, + async ({ result, logs }) => { + expect(result?.success).toBeFalse(); + expectNoLog(logs, requiredInputErrorText); + expectLog(logs, unknownElementErrorText); + + // Revert back to valid child component + await harness.writeFile('src/app/child.component.ts', goodChildComponentContents); + }, + ({ result, logs }) => { + expect(result?.success).toBeTrue(); + expectNoLog(logs, requiredInputErrorText); + expectNoLog(logs, unknownElementErrorText); + }, + ], + { outputLogsOnFailure: false }, + ); + }); + it('detects cumulative block syntax errors', async () => { harness.useTarget('build', { ...BASE_OPTIONS, @@ -234,7 +457,7 @@ describeBuilder(buildApplication, APPLICATION_BUILDER_INFO, (harness) => { await harness.appendToFile('src/app/app.component.html', '
Guten Tag
'); }, ({ logs }) => { - expectNoLog(logs, 'invalid-css-content'); + expectNoLog(logs, 'Unexpected character "EOF"'); harness.expectFile('dist/browser/main.js').content.toContain('Hello, world!'); harness.expectFile('dist/browser/main.js').content.toContain('Guten Tag'); diff --git a/packages/angular/build/src/builders/dev-server/schema.json b/packages/angular/build/src/builders/dev-server/schema.json index 023478ff7e52..3517d3098887 100644 --- a/packages/angular/build/src/builders/dev-server/schema.json +++ b/packages/angular/build/src/builders/dev-server/schema.json @@ -91,7 +91,7 @@ }, "hmr": { "type": "boolean", - "description": "Enable hot module replacement. Defaults to the value of 'liveReload'. Currently, only global and component stylesheets are supported." + "description": "Enable hot module replacement. Defaults to the value of 'liveReload'. Currently, global stylesheets, component stylesheets, and component templates are supported." }, "watch": { "type": "boolean", diff --git a/packages/angular/build/src/builders/dev-server/vite/utils.ts b/packages/angular/build/src/builders/dev-server/vite/utils.ts index bc2d4a513b5a..4a0b85b9324a 100644 --- a/packages/angular/build/src/builders/dev-server/vite/utils.ts +++ b/packages/angular/build/src/builders/dev-server/vite/utils.ts @@ -40,7 +40,7 @@ export function updateResultRecord( ): void { if (file.origin === 'disk') { assetFiles.set('/' + normalizePath(file.path), { - source: normalizePath(file.inputPath), + source: normalizePath(file.source), updated: !initial, }); diff --git a/packages/angular/build/src/builders/extract-i18n/application-extraction.ts b/packages/angular/build/src/builders/extract-i18n/application-extraction.ts index ff223ba0bd16..02233fade493 100644 --- a/packages/angular/build/src/builders/extract-i18n/application-extraction.ts +++ b/packages/angular/build/src/builders/extract-i18n/application-extraction.ts @@ -39,6 +39,7 @@ export async function extractMessages( await context.getTargetOptions(options.buildTarget), builderName, )) as unknown as ApplicationBuilderInternalOptions; + buildOptions.aot = true; buildOptions.optimization = false; buildOptions.sourceMap = { scripts: true, vendor: true, styles: false }; buildOptions.localize = false; @@ -106,7 +107,7 @@ function setupLocalizeExtractor( if (file?.origin === 'memory') { content = textDecoder.decode(file.contents); } else if (file?.origin === 'disk') { - content = readFileSync(file.inputPath, 'utf-8'); + content = readFileSync(file.source, 'utf-8'); } if (content === undefined) { throw new Error('Unknown file requested: ' + requestedPath); diff --git a/packages/angular/build/src/builders/karma/assets-middleware.ts b/packages/angular/build/src/builders/karma/assets-middleware.ts index 45483cb4ec41..6f04faffae46 100644 --- a/packages/angular/build/src/builders/karma/assets-middleware.ts +++ b/packages/angular/build/src/builders/karma/assets-middleware.ts @@ -60,7 +60,7 @@ export class AngularAssetsMiddleware { // https://github.com/karma-runner/karma/blob/84f85e7016efc2266fa6b3465f494a3fa151c85c/lib/middleware/common.js#L10 switch (file.origin) { case 'disk': - this.serveFile(file.inputPath, undefined, res, undefined, undefined, /* doNotCache */ true); + this.serveFile(file.source, undefined, res, undefined, undefined, /* doNotCache */ true); break; case 'memory': // Include pathname to help with Content-Type headers. diff --git a/packages/angular/build/src/builders/library/builder-state.ts b/packages/angular/build/src/builders/library/builder-state.ts new file mode 100644 index 000000000000..81b4e21e778d --- /dev/null +++ b/packages/angular/build/src/builders/library/builder-state.ts @@ -0,0 +1,333 @@ +/** + * @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 { toPosixPath } from '../../utils/path'; +import { ExecutionResult } from './execution-result'; +import type { + BundleResult, + LibraryCompilationOutput, + NormalizedLibraryOptions, + SingleProgramCache, +} from './types'; + +/** + * Encapsulates incremental compilation state, transitive resource reference tracking, + * watched compilation file tracking, and execution results across watch-mode rebuilds. + */ +export class BuilderState { + readonly #referencingFiles = new Map>(); + readonly #referencedByFile = new Map>(); + readonly #watchedCompilationFiles = new Set(); + readonly #pendingCompilationFiles = new Set(); + + #executionResult = new ExecutionResult(); + #singleProgramCache?: SingleProgramCache; + #compilationMode?: 'partial' | 'full'; + + /** Map of entry point names to their previous bundle results. */ + readonly previousBundleResults = new Map(); + + /** Set of changed POSIX ESM file paths pending bundling. */ + readonly pendingChangedEsmFiles = new Set(); + + /** Set of changed POSIX TypeScript declaration file paths pending bundling. */ + readonly pendingChangedDtsFiles = new Set(); + + /** Whether the previous build iteration encountered a compilation or diagnostic error. */ + hasCompilationError = false; + + /** Whether package.json and secondary entry point manifests have been emitted. */ + hasEmittedManifests = false; + + /** Whether the last emitted root package.json included a tslib dependency. */ + hasEmittedTslib = false; + + /** Whether static assets have been emitted in a prior iteration. */ + hasEmittedAssets = false; + + /** Whether entry points in package.json changed since the last compilation. */ + hasEntryPointsChanges = false; + + constructor( + options?: Partial< + Pick + >, + ) { + if (options?.tsConfigPath) { + this.#watchedCompilationFiles.add(options.tsConfigPath); + } + if (options?.packageJsonPath) { + this.#watchedCompilationFiles.add(options.packageJsonPath); + } + if (options?.entryPoints) { + for (const entryPoint of options.entryPoints.values()) { + this.#watchedCompilationFiles.add(entryPoint.entryFilePath); + } + } + } + + /** Cached unified TypeScript and Angular compilation state. */ + get singleProgramCache(): SingleProgramCache | undefined { + return this.#singleProgramCache; + } + + /** Effective compilation mode resolved from builder options and tsconfig. */ + get compilationMode(): 'partial' | 'full' | undefined { + return this.#compilationMode; + } + + /** Whether any active entry point bundle imports `tslib`. */ + get hasTslibImport(): boolean { + for (const result of this.previousBundleResults.values()) { + if (result.hasTslibImport) { + return true; + } + } + + return false; + } + + /** + * Set of POSIX file paths tracked for compilation and configuration + * (including `tsconfig.json`, `package.json`, entry points, and referenced source, + * template, and stylesheet files). + */ + get watchedCompilationFiles(): Set { + return this.#watchedCompilationFiles; + } + + /** + * Execution result holding errors, warnings, and logs for the current build iteration. + */ + get executionResult(): ExecutionResult { + return this.#executionResult; + } + + /** + * Resets the execution result for a new build iteration. + * + * @returns The fresh `ExecutionResult` instance for the current build iteration. + */ + resetExecutionResult(): ExecutionResult { + this.#executionResult = new ExecutionResult(); + + return this.#executionResult; + } + + /** + * Records transitive resource dependencies (such as Sass partials) for a containing + * component or stylesheet file and registers them in `watchedCompilationFiles`. + * Multiple calls for the same containing file accumulate references additively. + * + * @param containingFile Path of the referencing component or stylesheet file. + * @param referencedFiles Paths of the referenced files. + */ + addFileReferences(containingFile: string, referencedFiles: Iterable): void { + const posixContainingFile = toPosixPath(containingFile); + let forwardReferences = this.#referencedByFile.get(posixContainingFile); + + for (const file of referencedFiles) { + const posixFile = toPosixPath(file); + this.#watchedCompilationFiles.add(posixFile); + if (posixFile === posixContainingFile) { + continue; + } + + if (!forwardReferences) { + forwardReferences = new Set(); + this.#referencedByFile.set(posixContainingFile, forwardReferences); + } + forwardReferences.add(posixFile); + + let referencing = this.#referencingFiles.get(posixFile); + if (!referencing) { + referencing = new Set(); + this.#referencingFiles.set(posixFile, referencing); + } + referencing.add(posixContainingFile); + } + } + + /** + * Clears previously recorded outgoing resource references for the given containing files + * before they are recompiled. + * + * @param containingFiles Paths of the containing or stylesheet files being recompiled. + */ + clearFileReferences(containingFiles: Iterable): void { + for (const containingFile of containingFiles) { + const posixContainingFile = toPosixPath(containingFile); + const forwardReferences = this.#referencedByFile.get(posixContainingFile); + if (!forwardReferences) { + continue; + } + + for (const prevRef of forwardReferences) { + const referencing = this.#referencingFiles.get(prevRef); + if (referencing) { + referencing.delete(posixContainingFile); + if (referencing.size === 0) { + this.#referencingFiles.delete(prevRef); + } + } + } + + this.#referencedByFile.delete(posixContainingFile); + } + } + + /** + * Returns the set of all files affected by the given changed files, including the + * changed files themselves and all transitively referencing files + * (e.g., partial `.scss` -> component `.scss` -> component `.ts`). + * + * @param files Initial changed file paths. + * @returns Set of POSIX paths including all transitively referencing files. + */ + getAffectedFiles(files: Iterable): Set { + const affected = new Set(); + for (const file of files) { + affected.add(toPosixPath(file)); + } + + for (const file of affected) { + const referencing = this.#referencingFiles.get(file); + if (referencing) { + for (const refFile of referencing) { + affected.add(refFile); + } + } + } + + return affected; + } + + /** + * Determines whether compilation and bundling need to run for the current build iteration. + * Also records incoming modified files so they are retained if the iteration aborts early. + * + * @param packageJsonPath Normalized POSIX path to the library's `package.json`. + * @param modifiedFiles Optional set of modified POSIX file paths in watch mode. + * @returns `true` if entry points should be compiled; otherwise `false`. + */ + shouldCompileEntryPoints(packageJsonPath: string, modifiedFiles?: ReadonlySet): boolean { + if (modifiedFiles) { + for (const file of modifiedFiles) { + if (file !== packageJsonPath) { + this.#pendingCompilationFiles.add(file); + } + } + } + + if ( + !modifiedFiles || + !this.#singleProgramCache || + this.hasCompilationError || + this.hasEntryPointsChanges || + this.pendingChangedEsmFiles.size > 0 || + this.pendingChangedDtsFiles.size > 0 + ) { + return true; + } + + for (const file of this.#pendingCompilationFiles) { + if (this.#watchedCompilationFiles.has(file)) { + return true; + } + } + + this.#pendingCompilationFiles.clear(); + + return false; + } + + /** + * Returns the effective set of modified POSIX file paths for compilation, + * merging any pending compilation files from earlier aborted iterations. + * + * @param modifiedFiles Optional set of modified POSIX file paths from the current iteration. + * @returns Merged set of modified POSIX file paths, or `undefined` for a full compilation. + */ + getCompilationModifiedFiles(modifiedFiles?: ReadonlySet): Set | undefined { + if (!modifiedFiles) { + this.#pendingCompilationFiles.clear(); + + return undefined; + } + + const files = new Set(modifiedFiles); + for (const file of this.#pendingCompilationFiles) { + files.add(file); + } + + return files; + } + + /** + * Records compilation output into the build state, updating the program cache, + * watched compilation files, and pending changed ESM/DTS file sets. + * + * @param output The compilation output produced by `compileLibrary`. + */ + recordCompilationOutput(output: LibraryCompilationOutput): void { + this.#pendingCompilationFiles.clear(); + this.#singleProgramCache = output.cache; + if (this.#compilationMode !== output.compilationMode) { + this.#compilationMode = output.compilationMode; + this.hasEmittedManifests = false; + } + + for (const file of output.referencedFiles) { + this.#watchedCompilationFiles.add(file); + } + for (const file of output.changedEsmFiles) { + this.pendingChangedEsmFiles.add(file); + } + for (const file of output.changedDtsFiles) { + this.pendingChangedDtsFiles.add(file); + } + } + + /** + * Records active bundle results across entry points after compilation and bundling succeed, + * clearing pending compilation/bundle flags. + * + * @param bundleResults Newly bundled results keyed by entry point name. + */ + recordBundleOutput(bundleResults: ReadonlyMap): void { + this.hasCompilationError = false; + this.hasEntryPointsChanges = false; + this.pendingChangedEsmFiles.clear(); + this.pendingChangedDtsFiles.clear(); + + for (const [name, bundleResult] of bundleResults) { + this.previousBundleResults.set(name, bundleResult); + } + } + + /** + * Disposes active compilation resources and clears cached state. + */ + async dispose(): Promise { + await this.#singleProgramCache?.compilationInstance.close?.(); + this.#singleProgramCache = undefined; + this.#compilationMode = undefined; + this.hasEmittedTslib = false; + this.hasEmittedAssets = false; + this.hasCompilationError = false; + this.hasEmittedManifests = false; + this.hasEntryPointsChanges = false; + this.previousBundleResults.clear(); + this.pendingChangedEsmFiles.clear(); + this.pendingChangedDtsFiles.clear(); + this.#pendingCompilationFiles.clear(); + this.#referencingFiles.clear(); + this.#referencedByFile.clear(); + this.#watchedCompilationFiles.clear(); + } +} diff --git a/packages/angular/build/src/builders/library/builder-state_spec.ts b/packages/angular/build/src/builders/library/builder-state_spec.ts new file mode 100644 index 000000000000..ae84bbaa7331 --- /dev/null +++ b/packages/angular/build/src/builders/library/builder-state_spec.ts @@ -0,0 +1,207 @@ +/** + * @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 { BuilderState } from './builder-state'; +import type { LibraryCompilationOutput, SingleProgramCache } from './types'; + +describe('BuilderState', () => { + const packageJsonPath = '/workspace/projects/my-lib/package.json'; + const tsConfigPath = '/workspace/projects/my-lib/tsconfig.lib.json'; + const entryFilePath = '/workspace/projects/my-lib/src/public-api.ts'; + + function createMockCache(closeSpy?: jasmine.Spy): SingleProgramCache { + return { + compilationInstance: { + close: closeSpy, + } as unknown as SingleProgramCache['compilationInstance'], + esmFiles: new Map(), + dtsFiles: new Map(), + failedFiles: new Set(), + }; + } + + function createMockCompilationOutput( + overrides: Partial = {}, + ): LibraryCompilationOutput { + return { + esmFiles: new Map(), + dtsFiles: new Map(), + changedEsmFiles: new Set(), + changedDtsFiles: new Set(), + referencedFiles: new Set(), + cache: createMockCache(), + hasStylesheetErrors: false, + diagnosePromise: Promise.resolve(), + declarationMap: false, + compilationMode: 'partial', + ...overrides, + }; + } + + describe('ExecutionResult', () => { + it('should track logs, warnings, and errors and reset per iteration', () => { + const state = new BuilderState(); + expect(state.executionResult.hasErrors).toBeFalse(); + expect(state.executionResult.output).toEqual({ success: true }); + + state.executionResult.addLog('Bundling my-lib'); + state.executionResult.addWarning('Unused import'); + state.executionResult.addWarnings(['Deprecated API']); + state.executionResult.addError('Type error'); + state.executionResult.addErrors(['Syntax error']); + + expect(state.executionResult.logs).toEqual(['Bundling my-lib']); + expect(state.executionResult.warnings).toEqual(['Unused import', 'Deprecated API']); + expect(state.executionResult.errors).toEqual(['Type error', 'Syntax error']); + expect(state.executionResult.hasErrors).toBeTrue(); + expect(state.executionResult.output).toEqual({ success: false }); + + const freshResult = state.resetExecutionResult(); + expect(freshResult).toBe(state.executionResult); + expect(freshResult.hasErrors).toBeFalse(); + expect(freshResult.logs).toEqual([]); + expect(freshResult.warnings).toEqual([]); + expect(freshResult.errors).toEqual([]); + }); + }); + + describe('file reference tracking', () => { + it('should expand transitive multi-hop references and ignore self-references', () => { + const state = new BuilderState(); + const compTs = '/workspace/projects/my-lib/src/lib/comp.ts'; + const compScss = '/workspace/projects/my-lib/src/lib/comp.scss'; + const varsScss = '/workspace/projects/my-lib/src/lib/_vars.scss'; + + state.addFileReferences(compTs, [compTs, compScss, varsScss]); + state.addFileReferences(compScss, [compScss, varsScss]); + + expect(state.watchedCompilationFiles.has(compTs)).toBeTrue(); + expect(state.watchedCompilationFiles.has(compScss)).toBeTrue(); + expect(state.watchedCompilationFiles.has(varsScss)).toBeTrue(); + + const affected = state.getAffectedFiles([varsScss]); + expect(affected).toEqual(new Set([varsScss, compTs, compScss])); + + // Subsequent getAffectedFiles calls preserve mappings when not yet overwritten + const affectedAgain = state.getAffectedFiles([varsScss]); + expect(affectedAgain).toEqual(new Set([varsScss, compTs, compScss])); + }); + + it('should accumulate references across multiple stylesheets for the same containing file', () => { + const state = new BuilderState(); + const compTs = '/workspace/projects/my-lib/src/lib/comp.ts'; + const firstPartial = '/workspace/projects/my-lib/src/lib/_first.scss'; + const secondPartial = '/workspace/projects/my-lib/src/lib/_second.scss'; + + state.addFileReferences(compTs, [compTs, firstPartial]); + state.addFileReferences(compTs, [compTs, secondPartial]); + + expect(state.getAffectedFiles([firstPartial])).toEqual(new Set([firstPartial, compTs])); + expect(state.getAffectedFiles([secondPartial])).toEqual(new Set([secondPartial, compTs])); + }); + + it('should prune stale references when a containing file is cleared and re-recorded', () => { + const state = new BuilderState(); + const compTs = '/workspace/projects/my-lib/src/lib/comp.ts'; + const oldPartial = '/workspace/projects/my-lib/src/lib/_old.scss'; + const newPartial = '/workspace/projects/my-lib/src/lib/_new.scss'; + + state.addFileReferences(compTs, [compTs, oldPartial]); + expect(state.getAffectedFiles([oldPartial])).toEqual(new Set([oldPartial, compTs])); + + // Clear and re-record references for compTs without oldPartial + state.clearFileReferences([compTs]); + state.addFileReferences(compTs, [compTs, newPartial]); + expect(state.getAffectedFiles([oldPartial])).toEqual(new Set([oldPartial])); + expect(state.getAffectedFiles([newPartial])).toEqual(new Set([newPartial, compTs])); + }); + }); + + describe('shouldCompileEntryPoints & getCompilationModifiedFiles', () => { + it('should preserve pending compilation files across aborted iterations', () => { + const state = new BuilderState({ + packageJsonPath, + tsConfigPath, + entryPoints: new Map([ + [ + '.', + { + subpath: '.', + name: '.', + displayName: 'my-lib', + bundleName: 'my-lib', + entryFilePath, + isPrimary: true, + }, + ], + ]), + }); + + state.recordCompilationOutput( + createMockCompilationOutput({ + referencedFiles: new Set([entryFilePath]), + }), + ); + + // Iteration 1: entryFilePath changes, but build aborts before recordCompilationOutput + expect( + state.shouldCompileEntryPoints(packageJsonPath, new Set([entryFilePath, packageJsonPath])), + ).toBeTrue(); + + // Iteration 2: only package.json changes on recovery + expect( + state.shouldCompileEntryPoints(packageJsonPath, new Set([packageJsonPath])), + ).toBeTrue(); + expect(state.getCompilationModifiedFiles(new Set([packageJsonPath]))).toEqual( + new Set([packageJsonPath, entryFilePath]), + ); + + // Once compilation output is recorded, pending compilation files are cleared + state.recordCompilationOutput(createMockCompilationOutput()); + expect( + state.shouldCompileEntryPoints(packageJsonPath, new Set([packageJsonPath])), + ).toBeFalse(); + }); + + it('should invalidate hasEmittedManifests when compilationMode changes', () => { + const state = new BuilderState(); + state.hasEmittedManifests = true; + state.recordCompilationOutput(createMockCompilationOutput({ compilationMode: 'partial' })); + expect(state.hasEmittedManifests).toBeFalse(); + + state.hasEmittedManifests = true; + state.recordCompilationOutput(createMockCompilationOutput({ compilationMode: 'partial' })); + expect(state.hasEmittedManifests).toBeTrue(); + + state.recordCompilationOutput(createMockCompilationOutput({ compilationMode: 'full' })); + expect(state.hasEmittedManifests).toBeFalse(); + }); + }); + + describe('dispose', () => { + it('should close compilationInstance and clear cached state on dispose', async () => { + const closeSpy = jasmine.createSpy('close').and.resolveTo(); + const state = new BuilderState({ packageJsonPath }); + state.recordCompilationOutput( + createMockCompilationOutput({ + cache: createMockCache(closeSpy), + changedEsmFiles: new Set(['/a.js']), + changedDtsFiles: new Set(['/a.d.ts']), + }), + ); + + await state.dispose(); + + expect(closeSpy).toHaveBeenCalledTimes(1); + expect(state.singleProgramCache).toBeUndefined(); + expect(state.pendingChangedEsmFiles).toHaveSize(0); + expect(state.pendingChangedDtsFiles).toHaveSize(0); + expect(state.watchedCompilationFiles).toHaveSize(0); + }); + }); +}); diff --git a/packages/angular/build/src/builders/library/builder.ts b/packages/angular/build/src/builders/library/builder.ts index 33df771fc6ab..42be7f99b211 100644 --- a/packages/angular/build/src/builders/library/builder.ts +++ b/packages/angular/build/src/builders/library/builder.ts @@ -8,6 +8,7 @@ import type { BuilderContext, BuilderOutput } from '@angular-devkit/architect'; import type { logging } from '@angular-devkit/core'; +import type { ComponentStylesheetBundler } from '../../tools/esbuild/angular/component-stylesheets'; import { resetSassWorkerPoolCaches, shutdownSassWorkerPool, @@ -23,12 +24,14 @@ import { purgeStaleBuildCache } from '../../utils/purge-cache'; import { getSupportedBrowsers } from '../../utils/supported-browsers'; import { assertCompatibleAngularVersion } from '../../utils/version'; import type { BuildWatcher } from '../../utils/watcher'; +import { BuilderState } from './builder-state'; +import type { ExecutionResult } from './execution-result'; import { normalizeLibraryOptions } from './options'; +import type { BuildActionContext } from './pipeline/build-action'; import { updateWatchedEntryPoints } from './pipeline/entry-points'; -import type { createComponentStylesheetBundlerForLibrary } from './pipeline/stylesheet-bundler'; import { loadPackageJson } from './pipeline/utils'; import type { Schema as LibraryBuilderOptions } from './schema'; -import type { NormalizedLibraryOptions, SingleBuildState } from './types'; +import type { NormalizedLibraryOptions } from './types'; /** * Executes the library builder to compile, bundle, and package an Angular library into the Angular Package Format (APF). @@ -42,10 +45,6 @@ export async function* executeLibraryBuilder( context: BuilderContext & { signal?: AbortSignal }, ): AsyncIterableIterator { assertCompatibleAngularVersion(context.workspaceRoot); - await initializeHash(); - - // Purge old build disk cache - await purgeStaleBuildCache(context); const projectName = context.target?.project; if (!projectName) { @@ -54,14 +53,18 @@ export async function* executeLibraryBuilder( return; } - const normalizedOptions = await normalizeLibraryOptions(context, projectName, options); + const [normalizedOptions] = await Promise.all([ + normalizeLibraryOptions(context, projectName, options), + initializeHash(), + // Purge old build disk cache + purgeStaleBuildCache(context), + ]); + const { workspaceRoot, projectRoot, outputPath, deleteOutputPath, - packageJsonPath, - tsConfigPath, watch: isWatchMode, poll, cacheOptions, @@ -86,17 +89,14 @@ export async function* executeLibraryBuilder( } // Dynamically lazy-loaded to prevent importing dependencies at the top level. - const [ - { buildAction, createSingleBuildState, hasModifiedWatchedFile }, - { createComponentStylesheetBundlerForLibrary }, - ] = await Promise.all([ + const [{ buildAction }, { createComponentStylesheetBundlerForLibrary }] = await Promise.all([ import('./pipeline/build-action'), import('./pipeline/stylesheet-bundler'), ]); - let stylesheetBundler: ReturnType | undefined; + let stylesheetBundler: ComponentStylesheetBundler | undefined; let watcher: BuildWatcher | undefined; - const buildState = createSingleBuildState(); + const buildState = new BuilderState(normalizedOptions); try { const browsers = getSupportedBrowsers(projectRoot, logger); @@ -107,21 +107,7 @@ export async function* executeLibraryBuilder( target, ); - // Track all referenced compilation files for watch mode - const watchedCompilationFiles = new Set([ - toPosixPath(tsConfigPath), - toPosixPath(packageJsonPath), - ]); - - for (const entryPoint of normalizedOptions.entryPoints.values()) { - watchedCompilationFiles.add(toPosixPath(entryPoint.entryFilePath)); - } - if (isWatchMode) { - if (progress) { - logger.info('Watch mode enabled. Watching for file changes...'); - } - const { setupWatcher } = await import('../../utils/watcher'); watcher = await setupWatcher({ workspaceRoot, @@ -131,7 +117,10 @@ export async function* executeLibraryBuilder( poll, preserveSymlinks, signal, - watchFiles: watchedCompilationFiles, + watchFiles: [ + ...buildState.watchedCompilationFiles, + ...normalizedOptions.assets.map((asset) => asset.input), + ], }); context.addTeardown?.(() => void watcher?.close()); @@ -143,15 +132,19 @@ export async function* executeLibraryBuilder( { options: normalizedOptions, stylesheetBundler, - watchedCompilationFiles, isWatchMode, - context, buildState, }, + logger, withProgress, watcher, buildAction, ); + + if (isWatchMode && progress) { + logger.info('Watch mode enabled. Watching for file changes...'); + } + yield initialResult; if (!isWatchMode || !watcher) { @@ -162,12 +155,10 @@ export async function* executeLibraryBuilder( watcher, normalizedOptions, stylesheetBundler, - watchedCompilationFiles, - context, + logger, withProgress, buildState, buildAction, - hasModifiedWatchedFile, signal, ); } finally { @@ -177,37 +168,53 @@ export async function* executeLibraryBuilder( await Promise.allSettled([ watcher?.close(), stylesheetBundler?.dispose(), - buildState.singleProgramCache?.compilationInstance.close?.(), + buildState.dispose(), ]); } } +/** + * Executes a single build iteration with progress reporting, logs any resulting + * messages, and registers newly discovered files with the watcher. + * + * @param message Progress spinner message to display while building. + * @param actionContext The build action context and state. + * @param logger The Architect logger API. + * @param withProgress Progress wrapper function (`withSpinner` or `withNoProgress`). + * @param watcher Optional file watcher for watch mode. + * @param buildAction The library build action function to execute. + * @returns A promise resolving to the Architect `BuilderOutput`. + */ async function executeBuild( message: string, - actionContext: import('./pipeline/build-action').BuildActionContext, + actionContext: BuildActionContext, + logger: logging.LoggerApi, withProgress: typeof withSpinner, watcher: BuildWatcher | undefined, buildAction: typeof import('./pipeline/build-action').buildAction, ): Promise { const startTime = process.hrtime.bigint(); - const { context, watchedCompilationFiles, isWatchMode } = actionContext; + const { buildState, isWatchMode, options } = actionContext; try { - await withProgress(message, () => buildAction(actionContext)); - logBuildResult(context.logger, startTime, true); - if (isWatchMode) { + const result = await withProgress(message, () => buildAction(actionContext)); + logMessages(logger, result); + + const { output } = result; + logBuildResult(logger, startTime, output.success, options.outputPath); + if (output.success && isWatchMode) { logCumulativeDurations(); } - return { success: true }; + return output; } catch (error) { assertIsError(error); - logBuildResult(context.logger, startTime, false); + logBuildResult(logger, startTime, false); return { success: false, error: error.message }; } finally { if (watcher) { - watcher.add(Array.from(watchedCompilationFiles)); + watcher.add(Array.from(buildState.watchedCompilationFiles)); const { assetsToEmit } = actionContext; if (assetsToEmit?.length) { @@ -219,26 +226,33 @@ async function executeBuild( /** * Runs the watch loop, rebuilding the library as watched files are modified. + * + * @param watcher The file watcher instance. + * @param options The normalized library builder options with POSIX paths. + * @param stylesheetBundler The component stylesheet bundler instance. + * @param logger The Architect logger API. + * @param withProgress Progress wrapper function (`withSpinner` or `withNoProgress`). + * @param buildState The state preserved across incremental builds. + * @param buildAction The library build action function to execute. + * @param signal Abort signal to terminate the watch loop. + * @returns An async iterator yielding builder output results. */ async function* runWatchLoop( watcher: BuildWatcher, options: NormalizedLibraryOptions, - stylesheetBundler: ReturnType, - watchedCompilationFiles: Set, - context: BuilderContext, + stylesheetBundler: ComponentStylesheetBundler, + logger: logging.LoggerApi, withProgress: typeof withSpinner, - buildState: SingleBuildState, + buildState: BuilderState, buildAction: typeof import('./pipeline/build-action').buildAction, - hasModifiedWatchedFile: typeof import('./pipeline/build-action').hasModifiedWatchedFile, - signal?: AbortSignal, + signal: AbortSignal, ): AsyncIterableIterator { const { collectAssetsToEmit } = await import('./pipeline/assets'); const { workspaceRoot, packageJsonPath, assets, clearScreen } = options; - const posixPackageJsonPath = toPosixPath(packageJsonPath); for await (const changes of watcher) { - if (signal?.aborted) { + if (signal.aborted) { break; } @@ -247,56 +261,30 @@ async function* runWatchLoop( console.clear(); } - const changedFiles = new Set(); - let hasStyleChanges = false; - let hasSassChanges = false; - for (const file of changes.all) { - const posixFile = toPosixPath(file); - changedFiles.add(posixFile); - if (/\.(?:scss|sass)$/i.test(posixFile)) { - hasStyleChanges = true; - hasSassChanges = true; - } else if (/\.(?:less|css)$/i.test(posixFile)) { - hasStyleChanges = true; - } - } + const rawChangedFiles = changes.all.map(toPosixPath); + stylesheetBundler.invalidate(rawChangedFiles); + const changedFiles = buildState.getAffectedFiles(rawChangedFiles); - if (hasStyleChanges) { - if (hasSassChanges) { + for (const posixFile of changedFiles) { + if (/\.(?:scss|sass)$/i.test(posixFile)) { resetSassWorkerPoolCaches(); - } - const invalidatedStyles = stylesheetBundler.invalidate(changedFiles); - if (invalidatedStyles) { - for (const styleFile of invalidatedStyles) { - changedFiles.add(toPosixPath(styleFile)); - } + break; } } // Check if package.json was modified - let hasPackageJsonChanges = false; - if (changedFiles.has(posixPackageJsonPath)) { + if (changedFiles.has(packageJsonPath)) { + buildState.hasEmittedManifests = false; try { const packageJson = await loadPackageJson(packageJsonPath); - options.packageJson = packageJson; - if (!packageJson.name) { - throw new Error(`The package.json at '${packageJsonPath}' must contain a 'name'.`); - } - options.packageName = packageJson.name; - - updateWatchedEntryPoints( - packageJson, - options, - buildState, - watchedCompilationFiles, - packageJsonPath, - ); - - hasPackageJsonChanges = true; + updateWatchedEntryPoints(packageJson, options, buildState); } catch (error) { assertIsError(error); await buildState.singleProgramCache?.compilationInstance.update?.(changedFiles); - buildState.hasEmittedManifests = false; + if (buildState.shouldCompileEntryPoints(packageJsonPath, changedFiles)) { + buildState.hasCompilationError = true; + } + yield { success: false, error: `Failed to reload 'package.json': ${error.message}`, @@ -305,11 +293,10 @@ async function* runWatchLoop( } } - const hasSourceChanges = - !buildState.singleProgramCache || - buildState.hasCompilationError || - buildState.hasEntryPointsChanges || - hasModifiedWatchedFile(changedFiles, watchedCompilationFiles, posixPackageJsonPath); + const hasSourceChanges = buildState.shouldCompileEntryPoints(packageJsonPath, changedFiles); + if (!hasSourceChanges) { + await buildState.singleProgramCache?.compilationInstance.update?.(changedFiles); + } const assetsToEmit = await collectAssetsToEmit( assets, @@ -317,7 +304,7 @@ async function* runWatchLoop( buildState.hasEmittedAssets ? changedFiles : undefined, ); - if (!hasSourceChanges && !hasPackageJsonChanges && assetsToEmit.length === 0) { + if (!hasSourceChanges && buildState.hasEmittedManifests && assetsToEmit.length === 0) { continue; } @@ -326,13 +313,12 @@ async function* runWatchLoop( { options, stylesheetBundler, - watchedCompilationFiles, isWatchMode: true, - context, buildState, modifiedFiles: changedFiles, assetsToEmit, }, + logger, withProgress, watcher, buildAction, @@ -340,17 +326,51 @@ async function* runWatchLoop( } } +/** + * Logs informational, warning, and error messages produced during a build iteration. + * + * @param logger The Architect logger API. + * @param result The build execution result containing logs, warnings, and errors. + */ +function logMessages(logger: logging.LoggerApi, { logs, warnings, errors }: ExecutionResult): void { + if (logs.length) { + logger.info(logs.join('\n')); + } + + if (warnings.length) { + logger.warn(warnings.join('\n')); + } + + if (errors.length) { + logger.error(errors.join('\n')); + } +} + /** * Logs the build completion time and status. + * + * @param logger The Architect logger API. + * @param startTime High-resolution timestamp recorded when the build iteration started. + * @param success Whether the build iteration succeeded without errors. + * @param outputPath Optional POSIX output directory path to log on successful builds. */ -function logBuildResult(logger: logging.LoggerApi, startTime: bigint, success: boolean): void { - const durationMs = Number(process.hrtime.bigint() - startTime) / 1_000_000; - const durationSec = (durationMs / 1000).toFixed(2); +function logBuildResult( + logger: logging.LoggerApi, + startTime: bigint, + success: boolean, + outputPath?: string, +): void { + const buildTime = Number(process.hrtime.bigint() - startTime) / 10 ** 9; + const message = + `Library bundle generation ${success ? 'complete' : 'failed'}.` + + ` [${buildTime.toFixed(3)} seconds] - ${new Date().toISOString()}`; if (success) { - logger.info(`Build at: ${new Date().toISOString()} - Time: ${durationMs.toFixed(0)}ms`); - logger.info(`Built Angular library in ${durationSec}s.`); + logger.info(message); + if (outputPath) { + logger.info(`Output location: ${outputPath}\n`); + } } else { - logger.error(`Build failed after ${durationSec}s.`); + logger.error(message); } } diff --git a/packages/angular/build/src/builders/library/execution-result.ts b/packages/angular/build/src/builders/library/execution-result.ts new file mode 100644 index 000000000000..4f9826360503 --- /dev/null +++ b/packages/angular/build/src/builders/library/execution-result.ts @@ -0,0 +1,75 @@ +/** + * @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 { BuilderOutput } from '@angular-devkit/architect'; + +/** + * Encapsulates the diagnostic messages (`errors`, `warnings`, and `logs`) and + * builder output status for a single library build iteration. + */ +export class ExecutionResult { + readonly #errors: string[] = []; + readonly #warnings: string[] = []; + readonly #logs: string[] = []; + + /** Formatted error messages produced during the build iteration. */ + get errors(): readonly string[] { + return this.#errors; + } + + /** Formatted warning messages produced during the build iteration. */ + get warnings(): readonly string[] { + return this.#warnings; + } + + /** Informational log messages produced during the build iteration. */ + get logs(): readonly string[] { + return this.#logs; + } + + /** Whether any errors have been recorded in the build iteration. */ + get hasErrors(): boolean { + return this.#errors.length > 0; + } + + /** Architect builder output status for the build iteration. */ + get output(): BuilderOutput { + return { + success: !this.hasErrors, + }; + } + + /** Records an informational log message. */ + addLog(value: string): void { + this.#logs.push(value); + } + + /** Records a formatted error message. */ + addError(error: string): void { + this.#errors.push(error); + } + + /** Records multiple formatted error messages. */ + addErrors(errors: Iterable): void { + for (const error of errors) { + this.#errors.push(error); + } + } + + /** Records a formatted warning message. */ + addWarning(warning: string): void { + this.#warnings.push(warning); + } + + /** Records multiple formatted warning messages. */ + addWarnings(warnings: Iterable): void { + for (const warning of warnings) { + this.#warnings.push(warning); + } + } +} diff --git a/packages/angular/build/src/builders/library/index.ts b/packages/angular/build/src/builders/library/index.ts index 6a6ddf7a1ce7..da1f5d9385a3 100644 --- a/packages/angular/build/src/builders/library/index.ts +++ b/packages/angular/build/src/builders/library/index.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.dev/license */ -import { Builder, createBuilder } from '@angular-devkit/architect'; +import { type Builder, createBuilder } from '@angular-devkit/architect'; import { executeLibraryBuilder } from './builder'; import type { Schema as LibraryBuilderOptions } from './schema'; diff --git a/packages/angular/build/src/builders/library/options.ts b/packages/angular/build/src/builders/library/options.ts index 450dce36cab5..23d18d6f4d8e 100644 --- a/packages/angular/build/src/builders/library/options.ts +++ b/packages/angular/build/src/builders/library/options.ts @@ -9,10 +9,11 @@ import type { BuilderContext } from '@angular-devkit/architect'; import path from 'node:path'; import type { StylesheetPluginsass } from '../../tools/esbuild/stylesheets/stylesheet-plugin-factory'; -import { normalizeAssetPatterns } from '../../utils'; import { supportColor } from '../../utils/color'; import { assertIsError } from '../../utils/error'; +import { normalizeAssetPatterns } from '../../utils/normalize-asset-patterns'; import { normalizeCacheOptions } from '../../utils/normalize-cache'; +import { toPosixPath } from '../../utils/path'; import { generateSearchDirectories, getTailwindConfig, @@ -24,22 +25,33 @@ import { loadPackageJson } from './pipeline/utils'; import type { Schema as LibraryBuilderOptions } from './schema'; import type { NormalizedLibraryOptions, PackageJsonData } from './types'; +/** + * Normalizes raw library builder options into {@link NormalizedLibraryOptions} with all + * file and directory paths normalized to POSIX format. + * + * @param context The Architect builder context. + * @param projectName The name of the library project. + * @param options The raw library builder schema options. + * @returns A promise resolving to the normalized library options. + */ export async function normalizeLibraryOptions( context: BuilderContext, projectName: string, options: LibraryBuilderOptions, ): Promise { - const { workspaceRoot } = context; + const workspaceRoot = toPosixPath(context.workspaceRoot); const projectMetadata = await context.getProjectMetadata(projectName); - const { projectRoot, projectSourceRoot } = getProjectRootPaths(workspaceRoot, projectMetadata); + const projectRootPaths = getProjectRootPaths(workspaceRoot, projectMetadata); + const projectRoot = toPosixPath(projectRootPaths.projectRoot); + const projectSourceRoot = toPosixPath(projectRootPaths.projectSourceRoot); const { tsConfig, assets: rawAssets, stylePreprocessorOptions, inlineStyleLanguage = 'css', - compilationMode = 'partial', - declarationMap = false, + compilationMode, + declarationMap, allowedNonPeerDependencies: rawAllowedNonPeerDependencies = [], keepLifecycleScripts = false, watch = false, @@ -50,8 +62,8 @@ export async function normalizeLibraryOptions( clearScreen, } = options; - const resolvedTsConfigPath = path.resolve(workspaceRoot, tsConfig); - const packageJsonPath = path.join(projectRoot, 'package.json'); + const resolvedTsConfigPath = toPosixPath(path.resolve(workspaceRoot, tsConfig)); + const packageJsonPath = path.posix.join(projectRoot, 'package.json'); let packageJson: PackageJsonData; try { @@ -99,7 +111,7 @@ export async function normalizeLibraryOptions( } defaultAssets.push({ glob: 'README.md', - input: path.dirname(entryPoint.entryFilePath), + input: path.posix.dirname(entryPoint.entryFilePath), output: entryPoint.name, }); } @@ -109,7 +121,11 @@ export async function normalizeLibraryOptions( workspaceRoot, projectRoot, projectSourceRoot, - ); + ).map((asset) => ({ + ...asset, + input: toPosixPath(path.resolve(workspaceRoot, asset.input)), + output: toPosixPath(asset.output), + })); const cacheOptions = normalizeCacheOptions( projectMetadata, @@ -119,21 +135,23 @@ export async function normalizeLibraryOptions( ); const styleIncludePaths = (stylePreprocessorOptions?.includePaths ?? []).map((p: string) => - path.resolve(workspaceRoot, p), + toPosixPath(path.resolve(workspaceRoot, p)), ); const searchDirectories = await generateSearchDirectories([projectRoot, workspaceRoot]); const postcssConfiguration = await loadPostcssConfiguration(searchDirectories); const tailwindConfiguration = postcssConfiguration ? undefined - : await getTailwindConfig(searchDirectories, workspaceRoot, context.logger); + : getTailwindConfig(searchDirectories, workspaceRoot, context.logger); return { workspaceRoot, projectRoot, packageName, packageJson, - outputPath: path.resolve(workspaceRoot, options.outputPath ?? path.join('dist', projectName)), + outputPath: toPosixPath( + path.resolve(workspaceRoot, options.outputPath ?? path.posix.join('dist', projectName)), + ), deleteOutputPath, packageJsonPath, tsConfigPath: resolvedTsConfigPath, diff --git a/packages/angular/build/src/builders/library/pipeline/assets.ts b/packages/angular/build/src/builders/library/pipeline/assets.ts index e1e46f4b9f08..7b8329a7865a 100644 --- a/packages/angular/build/src/builders/library/pipeline/assets.ts +++ b/packages/angular/build/src/builders/library/pipeline/assets.ts @@ -9,7 +9,6 @@ import { statSync } from 'node:fs'; import path from 'node:path'; import picomatch from 'picomatch'; -import { toPosixPath } from '../../../utils/path'; import { DEFAULT_ASSET_IGNORE, resolveAssets } from '../../../utils/resolve-assets'; import type { NormalizedLibraryOptions } from '../types'; import { type DiskOutputFile, createDiskOutputFile } from './utils'; @@ -17,9 +16,9 @@ import { type DiskOutputFile, createDiskOutputFile } from './utils'; /** * Resolves and collects configured library assets to be emitted to disk. * - * @param assets The normalized asset patterns. - * @param workspaceRoot The workspace root directory path. - * @param modifiedFiles Optional set of modified file paths for incremental copying in watch mode. + * @param assets The normalized asset patterns with POSIX paths. + * @param workspaceRoot The normalized POSIX workspace root directory path. + * @param modifiedFiles Optional set of modified POSIX file paths for incremental copying in watch mode. * @returns An array of disk file emission descriptors. */ export async function collectAssetsToEmit( @@ -32,25 +31,22 @@ export async function collectAssetsToEmit( } if (modifiedFiles) { - const matchers = createAssetMatchers(assets, workspaceRoot); + const matchers = createAssetMatchers(assets); const filesToEmit: DiskOutputFile[] = []; for (const file of modifiedFiles) { - const resolvedFile = path.isAbsolute(file) ? file : path.resolve(workspaceRoot, file); - const posixFile = toPosixPath(resolvedFile); - - for (const { asset, posixInputPrefix, isMatch } of matchers) { - if (!posixFile.startsWith(posixInputPrefix)) { + for (const { asset, prefix, isMatch } of matchers) { + if (!file.startsWith(prefix)) { continue; } - const relative = posixFile.slice(posixInputPrefix.length); + const relative = file.slice(prefix.length); if (!isMatch(relative)) { continue; } - if (statSync(resolvedFile, { throwIfNoEntry: false })?.isFile()) { - filesToEmit.push(createDiskOutputFile(resolvedFile, path.join(asset.output, relative))); + if (statSync(file, { throwIfNoEntry: false })?.isFile()) { + filesToEmit.push(createDiskOutputFile(file, path.posix.join(asset.output, relative))); } } } @@ -63,15 +59,14 @@ export async function collectAssetsToEmit( return resolvedAssets.map(({ source, destination }) => createDiskOutputFile(source, destination)); } -function createAssetMatchers(assets: NormalizedLibraryOptions['assets'], workspaceRoot: string) { +function createAssetMatchers(assets: NormalizedLibraryOptions['assets']) { return assets.map((asset) => { - const absInput = path.resolve(workspaceRoot, asset.input); - const posixInput = toPosixPath(absInput).replace(/\/+$/, ''); const isMatch = picomatch(asset.glob, { dot: true, ignore: [...DEFAULT_ASSET_IGNORE, ...(asset.ignore ?? [])], }); + const prefix = asset.input.endsWith('/') ? asset.input : `${asset.input}/`; - return { asset, posixInputPrefix: `${posixInput}/`, isMatch }; + return { asset, prefix, isMatch }; }); } diff --git a/packages/angular/build/src/builders/library/pipeline/build-action.ts b/packages/angular/build/src/builders/library/pipeline/build-action.ts index 19718a793deb..734ea23c544e 100644 --- a/packages/angular/build/src/builders/library/pipeline/build-action.ts +++ b/packages/angular/build/src/builders/library/pipeline/build-action.ts @@ -6,67 +6,44 @@ * found in the LICENSE file at https://angular.dev/license */ -import type { BuilderContext } from '@angular-devkit/architect'; -import { constants, copyFile, mkdir, writeFile } from 'node:fs/promises'; import path from 'node:path'; import type { ComponentStylesheetBundler } from '../../../tools/esbuild/angular/component-stylesheets'; -import { emitFilesToDisk } from '../../../tools/esbuild/utils'; -import { toPosixPath } from '../../../utils/path'; -import type { NormalizedLibraryOptions, SingleBuildState } from '../types'; +import { emitFilesToDisk } from '../../../utils/disk-files'; +import { assertIsError } from '../../../utils/error'; +import type { BuilderState } from '../builder-state'; +import type { ExecutionResult } from '../execution-result'; +import type { NormalizedLibraryOptions } from '../types'; import { collectAssetsToEmit } from './assets'; -import { type BundleEntryPointInput, bundleEntryPoints } from './bundler'; +import { type BundleEntryPointsOutput, bundleEntryPoints } from './bundler'; import { compileLibrary } from './compilation'; +import { collectEntryPointsToBundle } from './entry-points'; import { generatePackageManifests } from './package-manifests'; import type { DiskOutputFile, OutputFile } from './utils'; -/** - * Creates a fresh {@link SingleBuildState} instance. - */ -export function createSingleBuildState(): SingleBuildState { - return { - previousBundleResults: new Map(), - pendingChangedEsmFiles: new Set(), - pendingChangedDtsFiles: new Set(), - directoryExists: new Set(), - }; -} - /** * Context required to execute a single library build iteration. */ export interface BuildActionContext { - /** Normalized options for the library build. */ + /** Normalized options for the library build with POSIX paths. */ options: NormalizedLibraryOptions; - /** The Architect builder context. */ - context: BuilderContext; - /** Bundler instance used to process component stylesheets. */ stylesheetBundler: ComponentStylesheetBundler; /** Whether the builder is running in watch mode. */ isWatchMode: boolean; - /** - * Set of file paths tracked for compilation and configuration - * (including `tsconfig.json`, `package.json`, entry points, and referenced source, - * template, and stylesheet files). Updated during compilation and used to determine - * whether file changes require recompiling entry points, excluding asset files so - * asset-only changes do not trigger code compilation. - */ - watchedCompilationFiles: Set; - - /** State preserved across incremental builds in watch mode. */ - buildState: SingleBuildState; + /** Build state tracking compilation caches and emitted outputs. */ + buildState: BuilderState; - /** Set of file paths modified since the last build iteration. */ + /** Set of POSIX file paths modified since the last build iteration. */ modifiedFiles?: Set; /** - * Asset files to emit to disk for the current build iteration. + * Asset files to emit for the current build iteration. * Pre-collected in the watch loop to avoid redundant asset matching, or resolved and * populated by `buildAction` when omitted (such as during the initial build) so the - * caller can register their source paths with the file watcher. + * caller can register their POSIX source paths with the file watcher. */ assetsToEmit?: DiskOutputFile[]; } @@ -77,205 +54,145 @@ export interface BuildActionContext { * manifest generation, and asset copying. * * @param actionContext The build action state and configuration. + * @returns A promise resolving to the build iteration's `ExecutionResult`. */ -export async function buildAction(actionContext: BuildActionContext): Promise { - const { - options, - context, - stylesheetBundler, - isWatchMode, - watchedCompilationFiles, - buildState, - modifiedFiles, - } = actionContext; - - const posixPackageJsonPath = toPosixPath(options.packageJsonPath); - const { pendingChangedEsmFiles, pendingChangedDtsFiles, directoryExists } = buildState; - - if (!modifiedFiles || modifiedFiles.has(posixPackageJsonPath)) { - buildState.hasEmittedManifests = false; - } +export async function buildAction(actionContext: BuildActionContext): Promise { + const { options, stylesheetBundler, isWatchMode, buildState, modifiedFiles } = actionContext; + const result = buildState.resetExecutionResult(); - const shouldCompileEntryPoints = - !modifiedFiles || - !buildState.singleProgramCache || - buildState.hasCompilationError || - buildState.hasEntryPointsChanges || - pendingChangedEsmFiles.size > 0 || - pendingChangedDtsFiles.size > 0 || - hasModifiedWatchedFile(modifiedFiles, watchedCompilationFiles, posixPackageJsonPath); - const shouldGenerateManifests = !buildState.hasEmittedManifests; + const { packageJsonPath } = options; - if (shouldGenerateManifests) { - verifyAllowedDependencies(options); - } - - const filesToEmit: OutputFile[] = []; - - if (shouldCompileEntryPoints) { - buildState.hasCompilationError = true; - buildState.hasEntryPointsChanges = false; - - const { - esmFiles, - dtsFiles, - changedEsmFiles, - changedDtsFiles, - referencedFiles, - cache, - diagnosePromise, - } = await compileLibrary( - options.entryPoints.values(), - options, - stylesheetBundler, - buildState.singleProgramCache, - modifiedFiles, - ); - buildState.singleProgramCache = cache; + const shouldCompileEntryPoints = buildState.shouldCompileEntryPoints( + packageJsonPath, + modifiedFiles, + ); - for (const file of referencedFiles) { - watchedCompilationFiles.add(file); + try { + if (!buildState.hasEmittedManifests && !verifyAllowedDependencies(options, result)) { + return result; } - for (const file of changedEsmFiles) { - pendingChangedEsmFiles.add(file); - } - for (const file of changedDtsFiles) { - pendingChangedDtsFiles.add(file); - } + const filesToEmit: OutputFile[] = []; - const itemsToBundle: BundleEntryPointInput[] = []; + if (shouldCompileEntryPoints) { + buildState.hasCompilationError = true; - for (const entryPoint of options.entryPoints.values()) { - const previousBundleResult = buildState.previousBundleResults.get(entryPoint.name); - const hasEsmChanges = - !previousBundleResult || - hasEntryPointChanges(previousBundleResult.esmModuleIds, pendingChangedEsmFiles); - const hasDtsChanges = - !previousBundleResult || - hasEntryPointChanges(previousBundleResult.dtsModuleIds, pendingChangedDtsFiles); - - if (hasEsmChanges || hasDtsChanges) { - context.logger.info(`Compiling ${entryPoint.displayName}...`); - itemsToBundle.push({ - entryPoint, - hasEsmChanges, - hasDtsChanges, - previousBundleResult, - }); - } - } + const compilationOutput = await compileLibrary( + options, + stylesheetBundler, + buildState, + buildState.getCompilationModifiedFiles(modifiedFiles), + ); + buildState.recordCompilationOutput(compilationOutput); - let bundleOutput: Awaited>; - let warnings: string[]; - try { - [bundleOutput, warnings] = await Promise.all([ - bundleEntryPoints(itemsToBundle, esmFiles, dtsFiles, options), + const { + esmFiles, + dtsFiles, + hasStylesheetErrors, diagnosePromise, - ]); - } catch (error) { - // Prioritize TypeScript/Angular diagnostic errors over secondary bundler failures. - await diagnosePromise; - throw error; - } - - buildState.hasCompilationError = false; - pendingChangedEsmFiles.clear(); - pendingChangedDtsFiles.clear(); + declarationMap, + compilationMode, + } = compilationOutput; - for (const warning of [...warnings, ...bundleOutput.warnings]) { - context.logger.warn(warning); - } - - filesToEmit.push(...bundleOutput.filesToEmit); - for (const [name, bundleResult] of bundleOutput.bundleResults) { - buildState.previousBundleResults.set(name, bundleResult); - } - } + if (hasStylesheetErrors) { + await diagnosePromise; - let hasTslibImport = false; - for (const entryPoint of options.entryPoints.values()) { - if (buildState.previousBundleResults.get(entryPoint.name)?.hasTslibImport) { - hasTslibImport = true; - break; - } - } + return result; + } - if (shouldGenerateManifests || buildState.hasEmittedTslib !== hasTslibImport) { - filesToEmit.push(...(await generatePackageManifests(options, isWatchMode, hasTslibImport))); - } + const itemsToBundle = collectEntryPointsToBundle(options, buildState); + let bundleOutput: BundleEntryPointsOutput; + try { + [bundleOutput] = await Promise.all([ + bundleEntryPoints(itemsToBundle, esmFiles, dtsFiles, { + ...options, + declarationMap, + compilationMode, + }), + diagnosePromise, + ]); + } catch (error) { + assertIsError(error); + // Prioritize TypeScript/Angular diagnostic errors over secondary bundler failures. + await diagnosePromise; + if (!result.hasErrors) { + result.addError(error.message); + } + + return result; + } - const resolvedAssetsToEmit = (actionContext.assetsToEmit ??= await collectAssetsToEmit( - options.assets, - options.workspaceRoot, - buildState.hasEmittedAssets ? modifiedFiles : undefined, - )); - filesToEmit.push(...resolvedAssetsToEmit); + result.addWarnings(bundleOutput.warnings); + if (result.hasErrors) { + return result; + } - await emitFilesToDisk(filesToEmit, async (file) => { - const fullFilePath = path.join(options.outputPath, file.path); - const fileBasePath = path.dirname(fullFilePath); - if (fileBasePath && !directoryExists.has(fileBasePath)) { - await mkdir(fileBasePath, { recursive: true }); - directoryExists.add(fileBasePath); + filesToEmit.push(...bundleOutput.filesToEmit); + buildState.recordBundleOutput(bundleOutput.bundleResults); } - if (file.type === 'memory') { - await writeFile(fullFilePath, file.contents); - } else { - await copyFile(file.source, fullFilePath, constants.COPYFILE_FICLONE); + const { hasTslibImport } = buildState; + if (!buildState.hasEmittedManifests || buildState.hasEmittedTslib !== hasTslibImport) { + filesToEmit.push( + ...(await generatePackageManifests( + { + ...options, + compilationMode: buildState.compilationMode ?? options.compilationMode ?? 'partial', + }, + isWatchMode, + hasTslibImport, + )), + ); } - }); - - buildState.hasEmittedManifests = true; - buildState.hasEmittedTslib = hasTslibImport; - buildState.hasEmittedAssets = true; -} -export function hasModifiedWatchedFile( - modifiedFiles: ReadonlySet, - watchedCompilationFiles: ReadonlySet, - posixPackageJsonPath: string, -): boolean { - for (const file of modifiedFiles) { - if (file !== posixPackageJsonPath && watchedCompilationFiles.has(file)) { - return true; - } + const resolvedAssetsToEmit = (actionContext.assetsToEmit ??= await collectAssetsToEmit( + options.assets, + options.workspaceRoot, + buildState.hasEmittedAssets ? modifiedFiles : undefined, + )); + filesToEmit.push(...resolvedAssetsToEmit); + + await emitFilesToDisk(filesToEmit, (file) => path.posix.join(options.outputPath, file.path)); + + buildState.hasEmittedManifests = true; + buildState.hasEmittedTslib = hasTslibImport; + buildState.hasEmittedAssets = true; + } catch (error) { + assertIsError(error); + result.addError(error.message); } - return false; + return result; } -function hasEntryPointChanges( - moduleIds: ReadonlySet, - changedFiles: ReadonlySet, +/** + * Validates that all direct and optional dependencies in `package.json` are + * permitted by `allowedNonPeerDependencies`. + * + * @param options The normalized library builder options. + * @param result The execution result to record any disallowed dependency errors into. + * @returns `true` if all dependencies are allowed; otherwise `false`. + */ +function verifyAllowedDependencies( + options: NormalizedLibraryOptions, + result: ExecutionResult, ): boolean { - if (changedFiles.size === 0) { - return false; - } - - for (const file of changedFiles) { - if (moduleIds.has(file)) { - return true; - } - } - - return false; -} - -function verifyAllowedDependencies(options: NormalizedLibraryOptions): void { const { packageJson, allowedNonPeerDependencies } = options; const dependencies = { ...(packageJson.dependencies ?? {}), ...(packageJson.optionalDependencies ?? {}), }; + let isValid = true; for (const dep of Object.keys(dependencies)) { if (!allowedNonPeerDependencies.some((regex) => regex.test(dep))) { - throw new Error( + result.addError( `Dependency '${dep}' must be explicitly allowed using the 'allowedNonPeerDependencies' option, ` + `or moved to 'peerDependencies' in 'package.json'.`, ); + isValid = false; } } + + return isValid; } diff --git a/packages/angular/build/src/builders/library/pipeline/bundler.ts b/packages/angular/build/src/builders/library/pipeline/bundler.ts index d05f8b7e814f..c48942885083 100644 --- a/packages/angular/build/src/builders/library/pipeline/bundler.ts +++ b/packages/angular/build/src/builders/library/pipeline/bundler.ts @@ -60,7 +60,7 @@ interface MultiBundleOutput { /** In-memory files generated by the bundle invocation. */ filesToEmit: MemoryOutputFile[]; - /** Map of bundle entry names to the set of virtual module IDs included in the bundle. */ + /** Map of bundle entry names to the set of virtual POSIX module IDs included in the bundle. */ moduleIdsByBundle: Map>; /** Set of bundle entry names that import 'tslib'. */ @@ -75,9 +75,9 @@ interface MultiBundleOutput { * using at most 2 Rolldown instances total (1 for all .mjs bundles, 1 for all .d.ts bundles). * * @param items The collection of entry point inputs with change statuses. - * @param esmFiles Map of virtual ESM output file paths to their content. - * @param dtsFiles Map of virtual TypeScript declaration file paths to their content. - * @param options The normalized library builder options. + * @param esmFiles Map of virtual POSIX ESM output file paths to their content. + * @param dtsFiles Map of virtual POSIX TypeScript declaration file paths to their content. + * @param options The normalized library builder options with POSIX paths. * @returns A promise resolving to the bundled output files and bundle cache results. */ export async function bundleEntryPoints( @@ -135,7 +135,7 @@ export async function bundleEntryPoints( * * @param entryPoints The entry points to include in the bundle input map. * @param dtsMode Whether the input map is for declaration files (.d.ts) rather than ESM files (.js). - * @returns A record mapping bundle names to virtual entry file paths. + * @returns A record mapping bundle names to virtual POSIX entry file paths. */ function resolveEntryInputMap( entryPoints: readonly NormalizedEntryPoint[], @@ -143,20 +143,18 @@ function resolveEntryInputMap( ): Record { const input: Record = {}; for (const { bundleName, entryFilePath } of entryPoints) { - const posixPath = toPosixPath(entryFilePath); input[bundleName] = dtsMode - ? posixPath.replace(/\.([cm]?ts)$/, '.d.$1') - : posixPath.replace(/\.([cm]?)ts$/, '.$1js'); + ? entryFilePath.replace(/\.([cm]?ts)$/, '.d.$1') + : entryFilePath.replace(/\.([cm]?)ts$/, '.$1js'); } return input; } /** - * Creates a Rolldown plugin that loads modules from an in-memory file map - * and prevents illegal cross-entry-point sibling imports. + * Creates a Rolldown plugin that resolves and loads modules from an in-memory file map. * - * @param files Map of virtual file paths to their content. + * @param files Map of virtual POSIX file paths to their content. * @param extensions Candidate extensions to probe when resolving extensionless module specifiers. * @param includeMap Whether source map files should also be loaded from the memory map. * @returns A Rolldown plugin instance. @@ -183,12 +181,7 @@ function createMemoryFileLoaderPlugin( return { id, external: true }; } - const posixId = toPosixPath(id); - const importerPosix = toPosixPath(importer); - const resolved = - posixId[0] === '.' - ? path.posix.join(path.posix.dirname(importerPosix), posixId) - : posixId; + const resolved = id[0] === '.' ? path.posix.join(path.posix.dirname(importer), id) : id; let resolvedCandidate: string | undefined; if (files.has(resolved)) { @@ -230,7 +223,7 @@ function createMemoryFileLoaderPlugin( * the transitive set of module IDs belonging to each entry point bundle. * * @param output The array of output chunks and assets from Rolldown. - * @param dir The destination output directory prefix. + * @param dir The destination POSIX output directory prefix. * @returns The processed multi-bundle output. */ function processRolldownOutput( @@ -300,13 +293,22 @@ function processRolldownOutput( return { filesToEmit, moduleIdsByBundle, tslibBundles }; } +/** + * Set of Rolldown warning codes that should be ignored. + */ +const IGNORED_ROLLDOWN_WARNING_CODES: ReadonlySet = new Set([ + 'EMPTY_BUNDLE', + 'UNUSED_EXTERNAL_IMPORT', + 'THIS_IS_UNDEFINED', +]); + /** * Executes a Rolldown bundle build across all provided entry inputs. * - * @param input Map of bundle names to virtual entry file paths. + * @param input Map of bundle names to virtual POSIX entry file paths. * @param plugins Rolldown plugins to use during bundling. * @param extension Output file extension ('mjs' or 'd.ts'). - * @param options The normalized library options. + * @param options The normalized library options with POSIX paths. * @returns A promise resolving to the multi-bundle output. */ async function executeMultiBundle( @@ -323,7 +325,7 @@ async function executeMultiBundle( if (isDts) { dir = TYPES_OUTPUT_DIR; - sourcemap = declarationMap; + sourcemap = !!declarationMap; } else { dir = FESM_OUTPUT_DIR; // FESM (.mjs) sourcemaps are always enabled. @@ -341,7 +343,12 @@ async function executeMultiBundle( resolve: { symlinks: !preserveSymlinks }, checks: { circularDependency: false }, onLog(level, log) { + // Ignore specified warning codes if (level === 'warn') { + if (log.code && IGNORED_ROLLDOWN_WARNING_CODES.has(log.code)) { + return; + } + warnings.push(log.message); } }, @@ -353,12 +360,12 @@ async function executeMultiBundle( try { const { output } = await bundle.generate({ format: 'es', - dir: path.join(outputPath, dir), + dir: path.posix.join(outputPath, dir), entryFileNames: `[name].${extension}`, chunkFileNames: `[name]-[hash].${extension}`, sourcemap, hoistTransitiveImports: false, - comments: { jsdoc: isDts, legal: true, annotation: true }, + comments: { jsdoc: isDts, legal: true, annotation: !isDts }, }); return { @@ -374,8 +381,8 @@ async function executeMultiBundle( * Bundles JavaScript ESM output files for all provided entry points. * * @param entryPoints The entry points to bundle. - * @param esmFiles Map of virtual ESM output file paths to contents. - * @param options The normalized library options. + * @param esmFiles Map of virtual POSIX ESM output file paths to contents. + * @param options The normalized library options with POSIX paths. * @returns A promise resolving to the multi-bundle output. */ async function bundleAllEsm( @@ -404,8 +411,8 @@ async function bundleAllEsm( * Bundles TypeScript declaration (.d.ts) output files for all provided entry points. * * @param entryPoints The entry points to bundle. - * @param dtsFiles Map of virtual declaration output file paths to contents. - * @param options The normalized library options. + * @param dtsFiles Map of virtual POSIX declaration output file paths to contents. + * @param options The normalized library options with POSIX paths. * @returns A promise resolving to the multi-bundle output. */ async function bundleAllDts( @@ -425,9 +432,12 @@ async function bundleAllDts( // Ensure Rolldown's native binding is initialized via `loadRolldown()` before evaluating // `rolldown-plugin-dts`, which has top-level imports of `rolldown/experimental`. await loadRolldown(); - const { dts } = await import('rolldown-plugin-dts'); + const { dts } = (await import('rolldown-plugin-dts' as string)) as typeof import( + 'rolldown-plugin-dts', + { with: { 'resolution-mode': 'import' } } + ); - const dtsSourcemap = options.declarationMap; + const dtsSourcemap = !!options.declarationMap; // Filter out `rolldown-plugin-dts:resolver` because all `.d.ts` files are already emitted // in-memory by the Angular/TypeScript compilation and resolved via `createMemoryFileLoaderPlugin`. // The default `rolldown-plugin-dts:resolver` plugin performs filesystem resolution (`oxc-resolver`) diff --git a/packages/angular/build/src/builders/library/pipeline/compilation.ts b/packages/angular/build/src/builders/library/pipeline/compilation.ts index b8b89fc41e24..1c026c5e9fec 100644 --- a/packages/angular/build/src/builders/library/pipeline/compilation.ts +++ b/packages/angular/build/src/builders/library/pipeline/compilation.ts @@ -15,59 +15,41 @@ import { import type { ComponentStylesheetBundler } from '../../../tools/esbuild/angular/component-stylesheets'; import { useTypeChecking } from '../../../utils/environment-options'; import { toPosixPath } from '../../../utils/path'; -import type { NormalizedEntryPoint, NormalizedLibraryOptions, SingleProgramCache } from '../types'; +import type { BuilderState } from '../builder-state'; +import type { LibraryCompilationOutput, NormalizedLibraryOptions } from '../types'; import { isDeclarationFile, isDeclarationSourceMapFile } from './utils'; const EMITTED_EXTENSIONS = ['.js', '.mjs', '.cjs', '.d.ts', '.d.mts', '.d.cts']; -/** - * Output of the unified library compilation step. - */ -export interface LibraryCompilationOutput { - /** Read-only map of emitted JavaScript files. */ - readonly esmFiles: ReadonlyMap; - - /** Read-only map of emitted TypeScript declaration files. */ - readonly dtsFiles: ReadonlyMap; - - /** Set of JavaScript file paths that were modified or newly emitted in this iteration. */ - readonly changedEsmFiles: ReadonlySet; - - /** Set of declaration file paths that were modified or newly emitted in this iteration. */ - readonly changedDtsFiles: ReadonlySet; - - /** Set of all source and dependency file paths referenced during compilation. */ - readonly referencedFiles: ReadonlySet; - - /** Cached compilation state for subsequent incremental builds. */ - readonly cache: SingleProgramCache; - - /** Promise that resolves to formatted compilation warnings or rejects on diagnostic errors. */ - readonly diagnosePromise: Promise; -} - /** * Compiles all library entry points in a single TypeScript and Angular compilation pass. * - * @param entryPoints The collection of normalized library entry points to compile. - * @param options The normalized library builder options. + * @param options The normalized library builder options with POSIX paths. * @param stylesheetBundler The component stylesheet bundler instance. - * @param cached Optional cached compilation state from a previous build iteration. - * @param modifiedFiles Optional set of modified file paths for incremental compilation. - * @returns A promise resolving to the compilation output containing emitted files and diagnostics. + * @param buildState The build state tracking compilation caches, file references, and diagnostics. + * @param modifiedFiles Optional set of modified POSIX file paths for incremental compilation. + * @returns A promise resolving to the compilation output containing emitted files and cache state. */ export async function compileLibrary( - entryPoints: Iterable, options: NormalizedLibraryOptions, stylesheetBundler: ComponentStylesheetBundler, - cached?: SingleProgramCache, + buildState: BuilderState, modifiedFiles?: Set, ): Promise { - const { tsConfigPath, compilationMode, preserveSymlinks, colors, declarationMap } = options; + const { + tsConfigPath, + entryPoints, + compilationMode, + preserveSymlinks, + colors, + declarationMap, + cacheOptions, + } = options; + const cached = buildState.singleProgramCache; const entryPathsMap: Record = {}; const rootFiles: string[] = []; - for (const ep of entryPoints) { + for (const ep of entryPoints.values()) { entryPathsMap[ep.displayName] = [ep.entryFilePath]; rootFiles.push(ep.entryFilePath); } @@ -77,19 +59,20 @@ export async function compileLibrary( try { let effectiveModifiedFiles = modifiedFiles; - if (cached?.failedFiles?.size) { + if (cached && cached.failedFiles.size > 0) { stylesheetBundler.invalidate(cached.failedFiles); - effectiveModifiedFiles = new Set(modifiedFiles); + const merged = new Set(modifiedFiles); for (const file of cached.failedFiles) { - effectiveModifiedFiles.add(file); + merged.add(file); } + effectiveModifiedFiles = merged; } if (effectiveModifiedFiles && effectiveModifiedFiles.size > 0) { await compilationInstance.update?.(effectiveModifiedFiles); + buildState.clearFileReferences(effectiveModifiedFiles); } - const allReferencedFiles = new Set(); const stylesheetWarnings: PartialMessage[] = []; const stylesheetErrors: PartialMessage[] = []; const failedFiles = new Set(); @@ -105,7 +88,13 @@ export async function compileLibrary( ? await stylesheetBundler.bundleFile(stylesheetFile) : await stylesheetBundler.bundleInline(data, containingFile); - result.referencedFiles?.forEach((f) => allReferencedFiles.add(toPosixPath(f))); + if (result.referencedFiles) { + buildState.addFileReferences(containingFile, result.referencedFiles); + if (stylesheetFile) { + buildState.addFileReferences(stylesheetFile, result.referencedFiles); + } + } + if (result.warnings.length > 0) { stylesheetWarnings.push(...result.warnings); } @@ -125,7 +114,7 @@ export async function compileLibrary( processWebWorker: () => '', }; - const { referencedFiles } = await compilationInstance.initialize( + const { referencedFiles, compilerOptions } = await compilationInstance.initialize( tsConfigPath, hostOptions, { @@ -135,40 +124,35 @@ export async function compileLibrary( declarationMap, compilationMode, paths: entryPathsMap, + cachePath: cacheOptions.enabled ? cacheOptions.path : undefined, }, 'library', ); - const emittedFiles = - stylesheetErrors.length === 0 ? await compilationInstance.emitAffectedFiles() : []; + const hasStylesheetErrors = stylesheetErrors.length > 0; + const emittedFiles = hasStylesheetErrors ? [] : await compilationInstance.emitAffectedFiles(); const diagnosePromise = runDiagnosticsAndFormat( compilationInstance, stylesheetErrors, stylesheetWarnings, colors, + buildState, ); - // Prevent unhandled promise rejection if an error occurs before diagnosePromise is awaited. - diagnosePromise.catch(() => {}); - const esmFiles = cached?.esmFiles ?? new Map(); const dtsFiles = cached?.dtsFiles ?? new Map(); const changedEsmFiles = new Set(); const changedDtsFiles = new Set(); + const posixReferencedFiles = new Set(referencedFiles.map(toPosixPath)); - for (const ref of referencedFiles) { - allReferencedFiles.add(toPosixPath(ref)); - } - - if (effectiveModifiedFiles) { + if (cached && effectiveModifiedFiles) { for (const modifiedFile of effectiveModifiedFiles) { - const posixModified = toPosixPath(modifiedFile); - if (allReferencedFiles.has(posixModified)) { + if (posixReferencedFiles.has(modifiedFile)) { continue; } - const basePathWithoutExt = posixModified.replace(/(?:\.d\.[cm]?ts|\.[cm]?[jt]sx?)$/i, ''); - if (basePathWithoutExt === posixModified || existsSync(posixModified)) { + const basePathWithoutExt = modifiedFile.replace(/(?:\.d\.[cm]?ts|\.[cm]?[jt]sx?)$/i, ''); + if (basePathWithoutExt === modifiedFile || existsSync(modifiedFile)) { continue; } @@ -189,21 +173,15 @@ export async function compileLibrary( for (const { filename, contents } of emittedFiles) { const normalized = toPosixPath(filename); - if (normalized.endsWith('.map')) { - const isDtsMap = isDeclarationSourceMapFile(normalized); - const targetMap = isDtsMap ? dtsFiles : esmFiles; - if (targetMap.get(normalized) !== contents) { - targetMap.set(normalized, contents); - (isDtsMap ? changedDtsFiles : changedEsmFiles).add(normalized.slice(0, -4)); - } - } else if (isDeclarationFile(normalized)) { - if (dtsFiles.get(normalized) !== contents) { - changedDtsFiles.add(normalized); - dtsFiles.set(normalized, contents); - } - } else if (esmFiles.get(normalized) !== contents) { - changedEsmFiles.add(normalized); - esmFiles.set(normalized, contents); + const isMap = normalized.endsWith('.map'); + const isDts = isMap ? isDeclarationSourceMapFile(normalized) : isDeclarationFile(normalized); + const targetFiles = isDts ? dtsFiles : esmFiles; + + if (targetFiles.get(normalized) !== contents) { + targetFiles.set(normalized, contents); + (isDts ? changedDtsFiles : changedEsmFiles).add( + isMap ? normalized.slice(0, -4) : normalized, + ); } } @@ -212,14 +190,17 @@ export async function compileLibrary( dtsFiles, changedEsmFiles, changedDtsFiles, - referencedFiles: allReferencedFiles, + referencedFiles: posixReferencedFiles, cache: { compilationInstance, esmFiles, dtsFiles, - failedFiles: failedFiles.size > 0 ? failedFiles : undefined, + failedFiles, }, + hasStylesheetErrors, diagnosePromise, + declarationMap: !!compilerOptions.declarationMap, + compilationMode: compilerOptions.compilationMode === 'full' ? 'full' : 'partial', }; } catch (error) { if (!cached) { @@ -230,49 +211,43 @@ export async function compileLibrary( } /** - * Validates stylesheet bundling results and runs TypeScript/Angular diagnostics. + * Formats stylesheet bundling diagnostics and TypeScript/Angular diagnostics + * and records them on the build state's execution result. * * @param compilationInstance The active Angular compilation instance. * @param stylesheetErrors List of stylesheet bundling errors. * @param stylesheetWarnings List of stylesheet bundling warnings. * @param colors Whether diagnostic messages should be formatted with ANSI colors. - * @returns A promise resolving to formatted warning messages. - * @throws If stylesheet bundling errors or compilation errors occur. + * @param buildState The build state whose execution result receives the formatted diagnostics. */ async function runDiagnosticsAndFormat( compilationInstance: AngularCompilation, stylesheetErrors: PartialMessage[], stylesheetWarnings: PartialMessage[], colors: boolean, -): Promise { - if (stylesheetErrors.length > 0) { - const formatted = await formatMessages(stylesheetErrors, { kind: 'error', color: colors }); - - throw new Error(`Failed to bundle stylesheet:\n${formatted.join('\n')}`); - } - - const warningsOut: string[] = []; - - if (useTypeChecking) { - const { errors, warnings } = await compilationInstance.diagnoseFiles(); - if (errors?.length) { - const errorMessages = await formatMessages(errors, { kind: 'error', color: colors }); - throw new Error(`Compilation failed with errors:\n${errorMessages.join('\n')}`); + buildState: BuilderState, +): Promise { + const { executionResult } = buildState; + const errors = [...stylesheetErrors]; + const warnings: PartialMessage[] = []; + + if (errors.length === 0 && useTypeChecking) { + const diagnostics = await compilationInstance.diagnoseFiles(); + if (diagnostics.errors?.length) { + errors.push(...diagnostics.errors); } - - if (warnings?.length) { - const formatted = await formatMessages(warnings, { kind: 'warning', color: colors }); - warningsOut.push(...formatted); + if (diagnostics.warnings?.length) { + warnings.push(...diagnostics.warnings); } } - if (stylesheetWarnings.length > 0) { - const formattedStyleWarnings = await formatMessages(stylesheetWarnings, { - kind: 'warning', - color: colors, - }); - warningsOut.push(...formattedStyleWarnings); + warnings.push(...stylesheetWarnings); + + if (errors.length > 0) { + executionResult.addErrors(await formatMessages(errors, { kind: 'error', color: colors })); } - return warningsOut; + if (warnings.length > 0) { + executionResult.addWarnings(await formatMessages(warnings, { kind: 'warning', color: colors })); + } } diff --git a/packages/angular/build/src/builders/library/pipeline/entry-points.ts b/packages/angular/build/src/builders/library/pipeline/entry-points.ts index 93a96307d9e4..f50824c5d9f3 100644 --- a/packages/angular/build/src/builders/library/pipeline/entry-points.ts +++ b/packages/angular/build/src/builders/library/pipeline/entry-points.ts @@ -8,12 +8,9 @@ import path from 'node:path'; import { toPosixPath } from '../../../utils/path'; -import type { - NormalizedEntryPoint, - NormalizedLibraryOptions, - PackageJsonData, - SingleBuildState, -} from '../types'; +import type { BuilderState } from '../builder-state'; +import type { NormalizedEntryPoint, NormalizedLibraryOptions, PackageJsonData } from '../types'; +import type { BundleEntryPointInput } from './bundler'; /** * Computes the base bundle file name for an entry point. @@ -23,7 +20,7 @@ import type { * @returns The sanitized bundle base name. */ export function getEntryPointBundleName(packageName: string, entryPointName = '.'): string { - const isPrimary = !entryPointName || entryPointName === '.'; + const isPrimary = entryPointName === '.'; const pkgName = packageName[0] === '@' ? packageName.slice(1) : packageName; const epName = isPrimary ? pkgName : `${pkgName}-${entryPointName}`; @@ -37,9 +34,9 @@ export function getEntryPointBundleName(packageName: string, entryPointName = '. * @param posixKey Normalized POSIX key without trailing slashes. * @param isPrimary Whether this is the primary entry point. * @param targetPath The relative file path string from exports. - * @param projectRoot The library project root directory. + * @param projectRoot The library project root POSIX directory path. * @param packageName The root package name (e.g. `@my/lib`). - * @returns The normalized entry point descriptor. + * @returns The normalized entry point descriptor with POSIX paths. */ function normalizeEntryPoint( key: string, @@ -49,11 +46,7 @@ function normalizeEntryPoint( projectRoot: string, packageName: string, ): NormalizedEntryPoint { - const name = isPrimary - ? '.' - : posixKey[0] === '.' && posixKey[1] === '/' - ? posixKey.slice(2) - : posixKey; + const name = isPrimary ? '.' : posixKey.startsWith('./') ? posixKey.slice(2) : posixKey; if (name !== '.' && (path.posix.isAbsolute(name) || name.includes('..'))) { throw new Error( @@ -64,7 +57,7 @@ function normalizeEntryPoint( const subpath = isPrimary ? '.' : `./${name}`; const displayName = (isPrimary ? packageName : `${packageName}/${name}`).toLowerCase(); const bundleName = getEntryPointBundleName(packageName, name); - const entryFilePath = path.resolve(projectRoot, targetPath); + const entryFilePath = toPosixPath(path.resolve(projectRoot, targetPath)); if (!/(? k.startsWith('.')); + const hasSubpathKeys = Object.keys(rawExports).some((k) => k.startsWith('.')); exportsRecord = hasSubpathKeys ? (rawExports as Record) : { '.': rawExports }; } @@ -189,10 +182,7 @@ export function normalizeEntryPoints( usedBundleNames.set(entryPoint.bundleName, key); entryPoints.set(entryPoint.name, entryPoint); - - if (entryPoint.isPrimary) { - hasPrimary = true; - } + hasPrimary ||= isPrimary; } if (!hasPrimary) { @@ -205,7 +195,12 @@ export function normalizeEntryPoints( } /** - * Determines whether two sets of normalized entry points differ in keys, paths, or subpaths. + * Determines whether two sets of normalized entry points differ in keys, POSIX entry paths, + * subpaths, bundle names, or display names. + * + * @param oldEntryPoints The previous map of normalized entry points. + * @param newEntryPoints The new map of normalized entry points. + * @returns `true` if any entry point was added, removed, or modified; otherwise `false`. */ export function haveEntryPointsChanged( oldEntryPoints: ReadonlyMap, @@ -233,27 +228,40 @@ export function haveEntryPointsChanged( /** * Updates watched files, entry points in options, and cached bundle results when package.json entry points change. + * + * @param packageJson The reloaded `package.json` data. + * @param options The normalized library builder options. + * @param buildState The state preserved across incremental builds. */ export function updateWatchedEntryPoints( packageJson: PackageJsonData, options: NormalizedLibraryOptions, - buildState: SingleBuildState, - watchedCompilationFiles: Set, - packageJsonPath: string, + buildState: BuilderState, ): void { + const { packageJsonPath } = options; + if (!packageJson.name) { + throw new Error(`The package.json at '${packageJsonPath}' must contain a 'name'.`); + } + const newEntryPoints = normalizeEntryPoints( packageJson.exports, options.projectRoot, packageJsonPath, - options.packageName, + packageJson.name, ); + const { watchedCompilationFiles, previousBundleResults } = buildState; + if (haveEntryPointsChanged(options.entryPoints, newEntryPoints)) { + for (const oldEntryPoint of options.entryPoints.values()) { + watchedCompilationFiles.delete(oldEntryPoint.entryFilePath); + } + for (const entryPoint of newEntryPoints.values()) { - watchedCompilationFiles.add(toPosixPath(entryPoint.entryFilePath)); + watchedCompilationFiles.add(entryPoint.entryFilePath); } - for (const name of buildState.previousBundleResults.keys()) { + for (const name of previousBundleResults.keys()) { const oldEp = options.entryPoints.get(name); const newEp = newEntryPoints.get(name); if ( @@ -262,11 +270,77 @@ export function updateWatchedEntryPoints( newEp.entryFilePath !== oldEp.entryFilePath || newEp.bundleName !== oldEp.bundleName ) { - buildState.previousBundleResults.delete(name); + previousBundleResults.delete(name); } } options.entryPoints = newEntryPoints; buildState.hasEntryPointsChanges = true; } + + options.packageJson = packageJson; + options.packageName = packageJson.name; +} + +/** + * Determines which entry points have modified JavaScript or declaration files + * and collects them for bundling. + * + * @param options The normalized library builder options. + * @param buildState The state preserved across incremental builds. + * @returns The list of entry points that require bundling in the current build iteration. + */ +export function collectEntryPointsToBundle( + options: NormalizedLibraryOptions, + buildState: BuilderState, +): BundleEntryPointInput[] { + const { pendingChangedEsmFiles, pendingChangedDtsFiles, previousBundleResults, executionResult } = + buildState; + const itemsToBundle: BundleEntryPointInput[] = []; + + for (const entryPoint of options.entryPoints.values()) { + const previousBundleResult = previousBundleResults.get(entryPoint.name); + const hasEsmChanges = + !previousBundleResult || + hasEntryPointChanges(previousBundleResult.esmModuleIds, pendingChangedEsmFiles); + const hasDtsChanges = + !previousBundleResult || + hasEntryPointChanges(previousBundleResult.dtsModuleIds, pendingChangedDtsFiles); + + if (hasEsmChanges || hasDtsChanges) { + executionResult.addLog(`Compiling ${entryPoint.displayName}...`); + itemsToBundle.push({ + entryPoint, + hasEsmChanges, + hasDtsChanges, + previousBundleResult, + }); + } + } + + return itemsToBundle; +} + +/** + * Checks whether any file in `changedFiles` belongs to the given entry point's `moduleIds`. + * + * @param moduleIds Set of virtual POSIX module IDs referenced by the entry point's previous bundle. + * @param changedFiles Set of POSIX file paths that changed during compilation. + * @returns `true` if at least one changed file is part of `moduleIds`; otherwise `false`. + */ +function hasEntryPointChanges( + moduleIds: ReadonlySet, + changedFiles: ReadonlySet, +): boolean { + if (changedFiles.size === 0) { + return false; + } + + for (const file of changedFiles) { + if (moduleIds.has(file)) { + return true; + } + } + + return false; } diff --git a/packages/angular/build/src/builders/library/pipeline/entry-points_spec.ts b/packages/angular/build/src/builders/library/pipeline/entry-points_spec.ts index 834b7aec6835..e4653ef56e2b 100644 --- a/packages/angular/build/src/builders/library/pipeline/entry-points_spec.ts +++ b/packages/angular/build/src/builders/library/pipeline/entry-points_spec.ts @@ -6,16 +6,16 @@ * found in the LICENSE file at https://angular.dev/license */ -import { join, resolve } from 'node:path'; -import { toPosixPath } from '../../../utils/path'; +import { join } from 'node:path'; +import { BuilderState } from '../builder-state'; import type { BundleResult, NormalizedEntryPoint, NormalizedLibraryOptions, PackageJsonData, - SingleBuildState, } from '../types'; import { + collectEntryPointsToBundle, getEntryPointBundleName, haveEntryPointsChanged, normalizeEntryPoints, @@ -23,10 +23,19 @@ import { } from './entry-points'; describe('entry-points pipeline', () => { - const projectRoot = resolve('/workspace/projects/my-lib'); + const projectRoot = '/workspace/projects/my-lib'; const packageJsonPath = join(projectRoot, 'package.json'); const packageName = '@my-scope/my-lib'; + function createBundleResult(overrides: Partial = {}): BundleResult { + return { + esmModuleIds: new Set(), + dtsModuleIds: new Set(), + hasTslibImport: false, + ...overrides, + }; + } + describe('getEntryPointBundleName', () => { it('should compute bundle name for primary entry point without scope', () => { expect(getEntryPointBundleName('my-lib')).toBe('my-lib'); @@ -505,6 +514,8 @@ describe('entry-points pipeline', () => { }); describe('updateWatchedEntryPoints', () => { + const tsConfigPath = join(projectRoot, 'tsconfig.lib.json'); + it('should update options, watched files, build state, and prune removed bundles when entry points change', () => { const initialEntryPoints = normalizeEntryPoints( { @@ -520,17 +531,14 @@ describe('entry-points pipeline', () => { entryPoints: initialEntryPoints, projectRoot, packageName, + packageJsonPath, + tsConfigPath, + assets: [], } as unknown as NormalizedLibraryOptions; - const buildState = { - previousBundleResults: new Map([ - ['.', { esmModuleIds: new Set(), dtsModuleIds: new Set() }], - ['old-feature', { esmModuleIds: new Set(), dtsModuleIds: new Set() }], - ]), - hasEntryPointsChanges: false, - } as unknown as SingleBuildState; - - const watchedCompilationFiles = new Set(); + const buildState = new BuilderState(options); + buildState.previousBundleResults.set('.', createBundleResult()); + buildState.previousBundleResults.set('old-feature', createBundleResult()); const newPackageJson: PackageJsonData = { name: packageName, @@ -540,13 +548,7 @@ describe('entry-points pipeline', () => { }, }; - updateWatchedEntryPoints( - newPackageJson, - options, - buildState, - watchedCompilationFiles, - packageJsonPath, - ); + updateWatchedEntryPoints(newPackageJson, options, buildState); expect(buildState.hasEntryPointsChanges).toBeTrue(); expect(options.entryPoints.has('new-feature')).toBeTrue(); @@ -554,16 +556,17 @@ describe('entry-points pipeline', () => { expect(buildState.previousBundleResults.has('old-feature')).toBeFalse(); expect(buildState.previousBundleResults.has('.')).toBeTrue(); expect( - watchedCompilationFiles.has(toPosixPath(join(projectRoot, 'src/public-api.ts'))), + buildState.watchedCompilationFiles.has(join(projectRoot, 'src/public-api.ts')), ).toBeTrue(); expect( - watchedCompilationFiles.has( - toPosixPath(join(projectRoot, 'new-feature/src/public-api.ts')), - ), + buildState.watchedCompilationFiles.has(join(projectRoot, 'old-feature/src/public-api.ts')), + ).toBeFalse(); + expect( + buildState.watchedCompilationFiles.has(join(projectRoot, 'new-feature/src/public-api.ts')), ).toBeTrue(); }); - it('should not mutate buildState or options when entry points do not change', () => { + it('should not mutate buildState or options.entryPoints when entry points do not change', () => { const initialEntryPoints = normalizeEntryPoints( { '.': './src/public-api.ts', @@ -578,17 +581,14 @@ describe('entry-points pipeline', () => { entryPoints: initialEntryPoints, projectRoot, packageName, + packageJsonPath, + tsConfigPath, + assets: [], } as unknown as NormalizedLibraryOptions; - const buildState = { - previousBundleResults: new Map([ - ['.', { esmModuleIds: new Set(), dtsModuleIds: new Set() }], - ['feature', { esmModuleIds: new Set(), dtsModuleIds: new Set() }], - ]), - hasEntryPointsChanges: false, - } as unknown as SingleBuildState; - - const watchedCompilationFiles = new Set(); + const buildState = new BuilderState(options); + buildState.previousBundleResults.set('.', createBundleResult()); + buildState.previousBundleResults.set('feature', createBundleResult()); const samePackageJson: PackageJsonData = { name: packageName, @@ -598,17 +598,10 @@ describe('entry-points pipeline', () => { }, }; - updateWatchedEntryPoints( - samePackageJson, - options, - buildState, - watchedCompilationFiles, - packageJsonPath, - ); + updateWatchedEntryPoints(samePackageJson, options, buildState); expect(buildState.hasEntryPointsChanges).toBeFalse(); expect(options.entryPoints).toBe(initialEntryPoints); - expect(watchedCompilationFiles).toHaveSize(0); expect(buildState.previousBundleResults).toHaveSize(2); }); @@ -626,16 +619,13 @@ describe('entry-points pipeline', () => { entryPoints: initialEntryPoints, projectRoot, packageName, + packageJsonPath, + tsConfigPath, + assets: [], } as unknown as NormalizedLibraryOptions; - const buildState = { - previousBundleResults: new Map([ - ['.', { esmModuleIds: new Set(), dtsModuleIds: new Set() }], - ]), - hasEntryPointsChanges: false, - } as unknown as SingleBuildState; - - const watchedCompilationFiles = new Set(); + const buildState = new BuilderState(options); + buildState.previousBundleResults.set('.', createBundleResult()); const updatedPackageJson: PackageJsonData = { name: packageName, @@ -644,13 +634,7 @@ describe('entry-points pipeline', () => { }, }; - updateWatchedEntryPoints( - updatedPackageJson, - options, - buildState, - watchedCompilationFiles, - packageJsonPath, - ); + updateWatchedEntryPoints(updatedPackageJson, options, buildState); expect(buildState.hasEntryPointsChanges).toBeTrue(); expect(buildState.previousBundleResults.has('.')).toBeFalse(); @@ -658,7 +642,7 @@ describe('entry-points pipeline', () => { join(projectRoot, 'src/other-api.ts'), ); expect( - watchedCompilationFiles.has(toPosixPath(join(projectRoot, 'src/other-api.ts'))), + buildState.watchedCompilationFiles.has(join(projectRoot, 'src/other-api.ts')), ).toBeTrue(); }); @@ -676,16 +660,13 @@ describe('entry-points pipeline', () => { entryPoints: initialEntryPoints, projectRoot, packageName, + packageJsonPath, + tsConfigPath, + assets: [], } as unknown as NormalizedLibraryOptions; - const buildState = { - previousBundleResults: new Map([ - ['.', { esmModuleIds: new Set(), dtsModuleIds: new Set() }], - ]), - hasEntryPointsChanges: false, - } as unknown as SingleBuildState; - - const watchedCompilationFiles = new Set(); + const buildState = new BuilderState(options); + buildState.previousBundleResults.set('.', createBundleResult()); const updatedPackageJson: PackageJsonData = { name: '@renamed-scope/my-lib', @@ -694,19 +675,108 @@ describe('entry-points pipeline', () => { }, }; - options.packageName = updatedPackageJson.name; - - updateWatchedEntryPoints( - updatedPackageJson, - options, - buildState, - watchedCompilationFiles, - packageJsonPath, - ); + updateWatchedEntryPoints(updatedPackageJson, options, buildState); expect(buildState.hasEntryPointsChanges).toBeTrue(); expect(buildState.previousBundleResults.has('.')).toBeFalse(); + expect(options.packageName).toBe('@renamed-scope/my-lib'); expect(options.entryPoints.get('.')?.bundleName).toBe('renamed-scope-my-lib'); }); + + it('should throw when updated packageJson is missing a name field', () => { + const options = { + entryPoints: new Map(), + projectRoot, + packageName, + packageJsonPath, + assets: [], + } as unknown as NormalizedLibraryOptions; + const buildState = new BuilderState(options); + + expect(() => + updateWatchedEntryPoints({} as PackageJsonData, options, buildState), + ).toThrowError(/must contain a 'name'/); + }); + }); + + describe('collectEntryPointsToBundle', () => { + it('should return all entry points with ESM and DTS changes on initial build', () => { + const entryPoints = normalizeEntryPoints( + { + '.': './src/public-api.ts', + './testing': './testing/src/public-api.ts', + }, + projectRoot, + packageJsonPath, + packageName, + ); + const options = { entryPoints } as unknown as NormalizedLibraryOptions; + const buildState = new BuilderState(options); + + const items = collectEntryPointsToBundle(options, buildState); + + expect(items).toHaveSize(2); + expect(items[0]).toEqual({ + entryPoint: jasmine.objectContaining({ name: '.' }), + previousBundleResult: undefined, + hasEsmChanges: true, + hasDtsChanges: true, + }); + expect(items[1]).toEqual({ + entryPoint: jasmine.objectContaining({ name: 'testing' }), + previousBundleResult: undefined, + hasEsmChanges: true, + hasDtsChanges: true, + }); + expect(buildState.executionResult.logs).toEqual([ + 'Compiling @my-scope/my-lib...', + 'Compiling @my-scope/my-lib/testing...', + ]); + }); + + it('should only return entry points whose ESM or DTS module IDs intersect pending changes', () => { + const entryPoints = normalizeEntryPoints( + { + '.': './src/public-api.ts', + './testing': './testing/src/public-api.ts', + }, + projectRoot, + packageJsonPath, + packageName, + ); + const options = { entryPoints } as unknown as NormalizedLibraryOptions; + const buildState = new BuilderState(options); + + const primaryEntryJs = join(projectRoot, 'src/public-api.js'); + const primaryEntryDts = join(projectRoot, 'src/public-api.d.ts'); + const testingEntryJs = join(projectRoot, 'testing/src/public-api.js'); + const testingEntryDts = join(projectRoot, 'testing/src/public-api.d.ts'); + + buildState.previousBundleResults.set( + '.', + createBundleResult({ + esmModuleIds: new Set([primaryEntryJs]), + dtsModuleIds: new Set([primaryEntryDts]), + }), + ); + buildState.previousBundleResults.set( + 'testing', + createBundleResult({ + esmModuleIds: new Set([testingEntryJs]), + dtsModuleIds: new Set([testingEntryDts]), + }), + ); + + buildState.pendingChangedEsmFiles.add(testingEntryJs); + + const items = collectEntryPointsToBundle(options, buildState); + + expect(items).toHaveSize(1); + expect(items[0]?.entryPoint.name).toBe('testing'); + expect(items[0]?.hasEsmChanges).toBeTrue(); + expect(items[0]?.hasDtsChanges).toBeFalse(); + expect(items[0]?.previousBundleResult).toBe(buildState.previousBundleResults.get('testing')); + expect(buildState.executionResult.logs).toEqual(['Compiling @my-scope/my-lib/testing...']); + }); }); }); diff --git a/packages/angular/build/src/builders/library/pipeline/package-manifests.ts b/packages/angular/build/src/builders/library/pipeline/package-manifests.ts index 90e5d5bbd94a..5238ccca76c2 100644 --- a/packages/angular/build/src/builders/library/pipeline/package-manifests.ts +++ b/packages/angular/build/src/builders/library/pipeline/package-manifests.ts @@ -78,13 +78,10 @@ export async function generatePackageManifests( }; if (hasTslibImport && !rawPackageJson.dependencies?.['tslib']) { - const tslibVersion = await getAngularTslibRange(options.workspaceRoot); - if (tslibVersion) { - distPackageJson.dependencies = { - ...rawPackageJson.dependencies, - tslib: tslibVersion, - }; - } + distPackageJson.dependencies = { + ...rawPackageJson.dependencies, + tslib: await getAngularTslibRange(options.workspaceRoot), + }; } // Retain scripts if keepLifecycleScripts is set @@ -159,6 +156,11 @@ export async function generatePackageManifests( /** * Creates or updates export conditions for an entry point, preserving custom user-defined conditions. + * + * @param existingConditions Existing export conditions from the user's `package.json`. + * @param dtsPath Relative POSIX path to the bundled `.d.ts` file. + * @param fesmPath Relative POSIX path to the bundled FESM `.mjs` file. + * @returns The merged export conditions object. */ function createExportConditions( existingConditions: unknown, @@ -188,7 +190,7 @@ let cachedTslibRange: string | undefined; /** * Get the tslib range used by the Angular compiler. - * @param workspaceRoot path to the workspace root + * @param workspaceRoot path to the workspace root. * @returns tslib range */ async function getAngularTslibRange(workspaceRoot: string): Promise { diff --git a/packages/angular/build/src/builders/library/pipeline/stylesheet-bundler.ts b/packages/angular/build/src/builders/library/pipeline/stylesheet-bundler.ts index 74c44f3a1491..971741d7a2e2 100644 --- a/packages/angular/build/src/builders/library/pipeline/stylesheet-bundler.ts +++ b/packages/angular/build/src/builders/library/pipeline/stylesheet-bundler.ts @@ -10,6 +10,9 @@ import { ComponentStylesheetBundler } from '../../../tools/esbuild/angular/compo import type { BundleStylesheetOptions } from '../../../tools/esbuild/stylesheets/bundle-options'; import type { NormalizedLibraryOptions } from '../types'; +/** + * Subset of normalized library options required to configure the component stylesheet bundler. + */ export type LibraryStylesheetBundlerOptions = Pick< NormalizedLibraryOptions, | 'workspaceRoot' @@ -25,7 +28,7 @@ export type LibraryStylesheetBundlerOptions = Pick< /** * Creates a stylesheet bundler instance configured for library compilation. * - * @param options The normalized library builder options. + * @param options The normalized library builder options with POSIX paths. * @param incremental Whether incremental watch mode is enabled. * @param target The esbuild target environments derived from browserslist. * @returns A new ComponentStylesheetBundler instance. diff --git a/packages/angular/build/src/builders/library/pipeline/types.d.ts b/packages/angular/build/src/builders/library/pipeline/types.d.ts deleted file mode 100644 index 100c539f098c..000000000000 --- a/packages/angular/build/src/builders/library/pipeline/types.d.ts +++ /dev/null @@ -1,54 +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 - */ - -declare module 'rolldown-plugin-dts' { - import type { Plugin } from 'rolldown'; - import type { IsolatedDeclarationsOptions } from 'rolldown/experimental'; - - interface Logger { - info: (...args: unknown[]) => void; - warn: (...args: unknown[]) => void; - error: (...args: unknown[]) => void; - } - interface GeneralOptions { - generator?: 'tsc' | 'oxc' | 'tsgo'; - entry?: string | string[]; - cwd?: string; - dtsInput?: boolean; - emitDtsOnly?: boolean; - tsconfig?: string | boolean; - tsconfigRaw?: unknown; - compilerOptions?: unknown; - sourcemap?: boolean; - resolver?: 'oxc' | 'tsc'; - cjsDefault?: boolean; - sideEffects?: boolean; - logger?: Logger; - } - - interface TscOptions { - build?: boolean; - incremental?: boolean; - parallel?: boolean; - eager?: boolean; - newContext?: boolean; - emitJs?: boolean; - } - - interface Options extends GeneralOptions, TscOptions { - oxc?: Omit; - tsgo?: TsgoOptions; - customLanguages?: unknown[]; - } - - interface TsgoOptions { - path?: string; - } - - export declare function dts(options?: Options): Plugin[]; -} diff --git a/packages/angular/build/src/builders/library/pipeline/utils.ts b/packages/angular/build/src/builders/library/pipeline/utils.ts index 8258c8874e42..8ef8cf7fe9f3 100644 --- a/packages/angular/build/src/builders/library/pipeline/utils.ts +++ b/packages/angular/build/src/builders/library/pipeline/utils.ts @@ -7,6 +7,7 @@ */ import fs from 'node:fs/promises'; +import { toPosixPath } from '../../../utils/path'; import type { PackageJsonData } from '../types'; const IS_DTS_FILE_REGEXP = /\.d\.[cm]?ts$/i; @@ -26,9 +27,9 @@ export const TYPES_OUTPUT_DIR = 'types'; * Represents an in-memory file to be emitted to disk. */ export interface MemoryOutputFile { - type: 'memory'; + origin: 'memory'; - /** The destination path where the file should be written. */ + /** The relative POSIX destination path where the file should be written. */ path: string; /** The contents of the file as either a string or byte array. */ @@ -39,12 +40,12 @@ export interface MemoryOutputFile { * Represents an existing file on disk to be copied to a destination path. */ export interface DiskOutputFile { - type: 'disk'; + origin: 'disk'; - /** The path to the source file on disk. */ + /** The POSIX path to the source file on disk. */ source: string; - /** The destination path where the file should be copied. */ + /** The relative POSIX destination path where the file should be copied. */ path: string; } @@ -54,34 +55,34 @@ export interface DiskOutputFile { export type OutputFile = MemoryOutputFile | DiskOutputFile; /** - * Creates an output file descriptor for an existing file on disk. + * Creates an output file descriptor for an existing file on disk with POSIX paths. * * @param source The path to the source file on disk. - * @param path The destination path where the file should be copied. - * @returns A {@link DiskOutputFile} descriptor. + * @param path The relative destination path where the file should be copied. + * @returns A {@link DiskOutputFile} descriptor with POSIX `source` and `path`. */ export function createDiskOutputFile(source: string, path: string): DiskOutputFile { return { - type: 'disk', - source, - path, + origin: 'disk', + source: toPosixPath(source), + path: toPosixPath(path), }; } /** - * Creates an output file descriptor for an in-memory file. + * Creates an output file descriptor for an in-memory file with a POSIX destination path. * - * @param path The destination path where the file should be written. + * @param path The relative destination path where the file should be written. * @param contents The contents of the file as either a string, byte array, or JSON object. - * @returns A {@link MemoryOutputFile} descriptor. + * @returns A {@link MemoryOutputFile} descriptor with a POSIX `path`. */ export function createMemoryOutputFile( path: string, contents: string | Uint8Array | Record, ): MemoryOutputFile { return { - type: 'memory', - path, + origin: 'memory', + path: toPosixPath(path), contents: typeof contents === 'string' || contents instanceof Uint8Array ? contents diff --git a/packages/angular/build/src/builders/library/schema.json b/packages/angular/build/src/builders/library/schema.json index bac9944091f6..ee34936bdd62 100644 --- a/packages/angular/build/src/builders/library/schema.json +++ b/packages/angular/build/src/builders/library/schema.json @@ -71,14 +71,12 @@ }, "declarationMap": { "type": "boolean", - "description": "Generates a sourcemap for each corresponding '.d.ts' file.", - "default": false + "description": "Generates a sourcemap for each corresponding '.d.ts' file." }, "compilationMode": { "type": "string", "description": "Angular compilation mode. Use 'partial' when publishing to npm (APF requirement). Use 'full' only during development or for private, internal monorepo packages that are never published.", - "enum": ["partial", "full"], - "default": "partial" + "enum": ["partial", "full"] }, "allowedNonPeerDependencies": { "description": "A list of package names allowed in the 'dependencies' and 'optionalDependencies' sections of package.json. Values can be regular expression patterns.", diff --git a/packages/angular/build/src/builders/library/tests/behavior/build_spec.ts b/packages/angular/build/src/builders/library/tests/behavior/build_spec.ts index 74150e57acb7..0b6de2a26c7f 100644 --- a/packages/angular/build/src/builders/library/tests/behavior/build_spec.ts +++ b/packages/angular/build/src/builders/library/tests/behavior/build_spec.ts @@ -74,6 +74,47 @@ describeLibraryBuilder(executeLibraryBuilder, LIBRARY_BUILDER_INFO, (harness) => ); }); + it('should not log EMPTY_BUNDLE warnings for type-only entry points', async () => { + await harness.writeFile( + 'projects/lib/src/public-api.ts', + `export interface OnlyType { id: string; }`, + ); + + harness.useTarget('build', { + ...BASE_OPTIONS, + }); + + const { result, logs } = await harness.executeOnce(); + expect(result?.success).toBeTrue(); + expect(logs.some((l) => l.level === 'warn')).toBeFalse(); + }); + + it('should preserve JSDoc comments in .d.ts and annotation comments in .mjs bundles', async () => { + await harness.writeFile( + 'projects/lib/src/public-api.ts', + ` + function identity(x: T): T { return x; } + /** Public API JSDoc comment */ + export const VALUE = /* @__PURE__ */ identity('test'); + `, + ); + + harness.useTarget('build', { + ...BASE_OPTIONS, + }); + + const { result } = await harness.executeOnce(); + expect(result?.success).toBeTrue(); + + const fesmContent = harness.readFile('dist/lib/fesm2022/lib.mjs'); + expect(fesmContent).toContain('@__PURE__'); + expect(fesmContent).not.toContain('Public API JSDoc comment'); + + const dtsContent = harness.readFile('dist/lib/types/lib.d.ts'); + expect(dtsContent).toContain('Public API JSDoc comment'); + expect(dtsContent).not.toContain('@__PURE__'); + }); + it('should automatically add tslib to dependencies when output chunks import tslib', async () => { await harness.writeFile( 'projects/lib/src/public-api.ts', @@ -139,5 +180,103 @@ describeLibraryBuilder(executeLibraryBuilder, LIBRARY_BUILDER_INFO, (harness) => tslib: '^2.0.0', }); }); + + it('should log completion and output location', async () => { + harness.useTarget('build', { + ...BASE_OPTIONS, + }); + + const { result, logs } = await harness.executeOnce(); + expect(result?.success).toBeTrue(); + expect(logs).toContain( + jasmine.objectContaining({ + level: 'info', + message: jasmine.stringMatching( + /Library bundle generation complete\. \[\d+\.\d{3} seconds\] - \d{4}-\d{2}-\d{2}T/, + ), + }), + ); + expect(logs).toContain( + jasmine.objectContaining({ + level: 'info', + message: jasmine.stringMatching(/Output location: .*dist[/\\]lib/), + }), + ); + }); + + it('should log failure on build error', async () => { + await harness.writeFile( + 'projects/lib/src/public-api.ts', + `export const title: number = 'invalid type';`, + ); + + harness.useTarget('build', { + ...BASE_OPTIONS, + }); + + const { result, logs } = await harness.executeOnce({ outputLogsOnFailure: false }); + expect(result?.success).toBeFalse(); + expect(logs).toContain( + jasmine.objectContaining({ + level: 'error', + message: jasmine.stringContaining('TS2322'), + }), + ); + expect(logs).toContain( + jasmine.objectContaining({ + level: 'error', + message: jasmine.stringMatching( + /Library bundle generation failed\. \[\d+\.\d{3} seconds\] - \d{4}-\d{2}-\d{2}T/, + ), + }), + ); + expect(logs).not.toContain( + jasmine.objectContaining({ + message: jasmine.stringMatching(/Output location:/), + }), + ); + }); + + it('should write and reuse .tsbuildinfo in the persistent cache directory when cache is enabled', async () => { + harness.useProject('lib', { + root: 'projects/lib', + sourceRoot: 'projects/lib/src', + cli: { + cache: { + enabled: true, + environment: 'all', + path: '.angular/cache', + }, + }, + }); + + harness.useTarget('build', { + ...BASE_OPTIONS, + }); + + const firstBuild = await harness.executeOnce(); + expect(firstBuild.result?.success).toBeTrue(); + harness + .expectFile('.angular/cache/0.0.0-PLACEHOLDER/lib/angular-build-library/.tsbuildinfo') + .toExist(); + + await harness.writeFile( + 'projects/lib/src/public-api.ts', + ` + export * from './lib/lib.service'; + export * from './lib/lib.component'; + export const CACHED_BUILD = true; + `, + ); + + const secondBuild = await harness.executeOnce(); + expect(secondBuild.result?.success).toBeTrue(); + const fesmContent = harness.readFile('dist/lib/fesm2022/lib.mjs'); + expect(fesmContent).toContain('LibComponent'); + expect(fesmContent).toContain('CACHED_BUILD'); + const dtsContent = harness.readFile('dist/lib/types/lib.d.ts'); + expect(dtsContent).toContain('LibComponent'); + expect(dtsContent).toContain('CACHED_BUILD'); + }); }); }); diff --git a/packages/angular/build/src/builders/library/tests/behavior/watch_spec.ts b/packages/angular/build/src/builders/library/tests/behavior/watch_spec.ts index 2533da417909..e9b7e57ddd9f 100644 --- a/packages/angular/build/src/builders/library/tests/behavior/watch_spec.ts +++ b/packages/angular/build/src/builders/library/tests/behavior/watch_spec.ts @@ -6,7 +6,9 @@ * found in the LICENSE file at https://angular.dev/license */ +import { rm } from 'node:fs/promises'; import { executeLibraryBuilder } from '../../builder'; +import { InlineStyleLanguage } from '../../schema'; import { BASE_OPTIONS, LIBRARY_BUILDER_INFO, describeLibraryBuilder } from '../setup'; describeLibraryBuilder(executeLibraryBuilder, LIBRARY_BUILDER_INFO, (harness) => { @@ -563,5 +565,89 @@ describeLibraryBuilder(executeLibraryBuilder, LIBRARY_BUILDER_INFO, (harness) => }, ]); }); + + it('should recreate deleted output directories on subsequent watch rebuilds', async () => { + harness.useTarget('build', { + ...BASE_OPTIONS, + watch: true, + }); + + await harness.executeWithCases([ + async ({ result }) => { + expect(result?.success).toBeTrue(); + expect(harness.hasFile('dist/lib/fesm2022/lib.mjs')).toBeTrue(); + + // Remove output subdirectory externally between rebuilds + await rm(harness.resolvePath('dist/lib/fesm2022'), { recursive: true, force: true }); + expect(harness.hasFile('dist/lib/fesm2022/lib.mjs')).toBeFalse(); + + // Trigger a rebuild + await harness.writeFile( + 'projects/lib/src/lib/lib.component.ts', + ` + import { Component } from '@angular/core'; + + @Component({ + selector: 'lib-recreated', + template: 'Recreated', + }) + export class LibComponent {} + `, + ); + }, + ({ result }) => { + expect(result?.success).toBeTrue(); + const content = harness.readFile('dist/lib/fesm2022/lib.mjs'); + expect(content).toContain('lib-recreated'); + }, + ]); + }); + + it('should rebuild when a Sass partial imported by an inline component stylesheet is modified in watch mode', async () => { + await harness.writeFiles({ + 'projects/lib/src/lib/_vars.scss': '$primary-color: rgb(10, 20, 30);', + 'projects/lib/src/lib/_spacing.scss': '$pad: 12px;', + 'projects/lib/src/lib/lib.component.ts': ` + import { Component } from '@angular/core'; + + @Component({ + selector: 'lib-inline-sass', + template: 'Inline Sass', + styles: [ + '@use "./vars"; :host { color: vars.$primary-color; }', + '@use "./spacing"; :host { padding: spacing.$pad; }', + ], + }) + export class LibComponent {} + `, + }); + + harness.useTarget('build', { + ...BASE_OPTIONS, + inlineStyleLanguage: InlineStyleLanguage.Scss, + watch: true, + }); + + await harness.executeWithCases([ + async ({ result }) => { + expect(result?.success).toBeTrue(); + const content = harness.readFile('dist/lib/fesm2022/lib.mjs'); + expect(content).toMatch(/color:\s*(?:#0a141e|rgb\(10,\s*20,\s*30\))/); + expect(content).toMatch(/padding:\s*12px/); + + // Modify the Sass partial referenced by the first inline component style + await harness.writeFile( + 'projects/lib/src/lib/_vars.scss', + '$primary-color: rgb(200, 100, 50);', + ); + }, + ({ result }) => { + expect(result?.success).toBeTrue(); + const content = harness.readFile('dist/lib/fesm2022/lib.mjs'); + expect(content).toMatch(/color:\s*(?:#c86432|rgb\(200,\s*100,\s*50\))/); + expect(content).toMatch(/padding:\s*12px/); + }, + ]); + }); }); }); diff --git a/packages/angular/build/src/builders/library/tests/options/allowed-non-peer-dependencies_spec.ts b/packages/angular/build/src/builders/library/tests/options/allowed-non-peer-dependencies_spec.ts index caca7c594f26..c75699d068a3 100644 --- a/packages/angular/build/src/builders/library/tests/options/allowed-non-peer-dependencies_spec.ts +++ b/packages/angular/build/src/builders/library/tests/options/allowed-non-peer-dependencies_spec.ts @@ -24,10 +24,14 @@ describeLibraryBuilder(executeLibraryBuilder, LIBRARY_BUILDER_INFO, (harness) => ...BASE_OPTIONS, }); - const { result } = await harness.executeOnce(); + const { result, logs } = await harness.executeOnce({ outputLogsOnFailure: false }); expect(result?.success).toBeFalse(); - expect(result?.error).toContain('allowedNonPeerDependencies'); - expect(result?.error).toContain('lodash-es'); + expect(logs).toContain( + jasmine.objectContaining({ + level: 'error', + message: jasmine.stringMatching(/lodash-es.*allowedNonPeerDependencies/), + }), + ); }); it('should succeed build when dependency matches allowedNonPeerDependencies pattern', async () => { diff --git a/packages/angular/build/src/builders/library/tests/options/assets_spec.ts b/packages/angular/build/src/builders/library/tests/options/assets_spec.ts index ded7743675ff..a596cbfe4e62 100644 --- a/packages/angular/build/src/builders/library/tests/options/assets_spec.ts +++ b/packages/angular/build/src/builders/library/tests/options/assets_spec.ts @@ -15,6 +15,7 @@ describeLibraryBuilder(executeLibraryBuilder, LIBRARY_BUILDER_INFO, (harness) => await harness.writeFiles({ 'projects/lib/assets-dir/file-a.png': 'PNG_A', 'projects/lib/assets-dir/file-b.png': 'PNG_B', + 'projects/lib/assets-dir/ignored.png': 'PNG_IGNORED', 'projects/lib/assets-dir/file-c.svg': 'SVG_C', }); @@ -25,6 +26,7 @@ describeLibraryBuilder(executeLibraryBuilder, LIBRARY_BUILDER_INFO, (harness) => glob: '**/*.png', input: 'projects/lib/assets-dir', output: 'assets', + ignore: ['ignored.png'], }, ], }); @@ -34,6 +36,7 @@ describeLibraryBuilder(executeLibraryBuilder, LIBRARY_BUILDER_INFO, (harness) => expect(harness.readFile('dist/lib/assets/file-a.png')).toBe('PNG_A'); expect(harness.readFile('dist/lib/assets/file-b.png')).toBe('PNG_B'); + expect(harness.hasFile('dist/lib/assets/ignored.png')).toBeFalse(); expect(harness.hasFile('dist/lib/assets/file-c.svg')).toBeFalse(); }); diff --git a/packages/angular/build/src/builders/library/tests/options/compilation-mode_spec.ts b/packages/angular/build/src/builders/library/tests/options/compilation-mode_spec.ts index cd794206960d..0efedef5708f 100644 --- a/packages/angular/build/src/builders/library/tests/options/compilation-mode_spec.ts +++ b/packages/angular/build/src/builders/library/tests/options/compilation-mode_spec.ts @@ -37,5 +37,53 @@ describeLibraryBuilder(executeLibraryBuilder, LIBRARY_BUILDER_INFO, (harness) => const fesm = harness.readFile('dist/lib/fesm2022/lib.mjs'); expect(fesm).toContain('ɵɵdefineComponent'); }); + + it('should respect angularCompilerOptions.compilationMode from tsconfig when not set in builder options', async () => { + await harness.modifyFile('projects/lib/tsconfig.lib.json', (content) => { + const tsconfig = JSON.parse(content); + tsconfig.angularCompilerOptions = { + ...tsconfig.angularCompilerOptions, + compilationMode: 'full', + }; + + return JSON.stringify(tsconfig, null, 2); + }); + + harness.useTarget('build', { + ...BASE_OPTIONS, + }); + + const { result } = await harness.executeOnce(); + expect(result?.success).toBeTrue(); + + const fesm = harness.readFile('dist/lib/fesm2022/lib.mjs'); + expect(fesm).toContain('ɵɵdefineComponent'); + + const pkgJson = JSON.parse(harness.readFile('dist/lib/package.json')); + expect(pkgJson.scripts?.prepublishOnly).toContain('full compilation mode'); + }); + + it('should override angularCompilerOptions.compilationMode from tsconfig when set in builder options', async () => { + await harness.modifyFile('projects/lib/tsconfig.lib.json', (content) => { + const tsconfig = JSON.parse(content); + tsconfig.angularCompilerOptions = { + ...tsconfig.angularCompilerOptions, + compilationMode: 'full', + }; + + return JSON.stringify(tsconfig, null, 2); + }); + + harness.useTarget('build', { + ...BASE_OPTIONS, + compilationMode: CompilationMode.Partial, + }); + + const { result } = await harness.executeOnce(); + expect(result?.success).toBeTrue(); + + const fesm = harness.readFile('dist/lib/fesm2022/lib.mjs'); + expect(fesm).toContain('ɵɵngDeclareComponent'); + }); }); }); diff --git a/packages/angular/build/src/builders/library/tests/options/declaration-map_spec.ts b/packages/angular/build/src/builders/library/tests/options/declaration-map_spec.ts index a37e2f0dd255..2e30109d6a03 100644 --- a/packages/angular/build/src/builders/library/tests/options/declaration-map_spec.ts +++ b/packages/angular/build/src/builders/library/tests/options/declaration-map_spec.ts @@ -44,5 +44,43 @@ describeLibraryBuilder(executeLibraryBuilder, LIBRARY_BUILDER_INFO, (harness) => expect(dtsMap.sources).toContain('../../../projects/lib/src/lib/lib.component.ts'); harness.expectFile(join('dist/lib/types', dtsMap.sources[0])).toExist(); }); + + it('should respect declarationMap from tsconfig when not set in builder options', async () => { + await harness.modifyFile('projects/lib/tsconfig.lib.json', (content) => { + const tsconfig = JSON.parse(content); + tsconfig.compilerOptions.declarationMap = true; + + return JSON.stringify(tsconfig, null, 2); + }); + + harness.useTarget('build', { + ...BASE_OPTIONS, + }); + + const { result } = await harness.executeOnce(); + expect(result?.success).toBeTrue(); + + harness.expectFile('dist/lib/types/lib.d.ts.map').toExist(); + }); + + it('should override declarationMap from tsconfig when declarationMap is false', async () => { + await harness.modifyFile('projects/lib/tsconfig.lib.json', (content) => { + const tsconfig = JSON.parse(content); + tsconfig.compilerOptions.declarationMap = true; + + return JSON.stringify(tsconfig, null, 2); + }); + + harness.useTarget('build', { + ...BASE_OPTIONS, + declarationMap: false, + }); + + const { result } = await harness.executeOnce(); + expect(result?.success).toBeTrue(); + + harness.expectFile('dist/lib/fesm2022/lib.mjs.map').toExist(); + harness.expectFile('dist/lib/types/lib.d.ts.map').toNotExist(); + }); }); }); diff --git a/packages/angular/build/src/builders/library/types.ts b/packages/angular/build/src/builders/library/types.ts index dfe91c36f3c5..2d2d9f61891d 100644 --- a/packages/angular/build/src/builders/library/types.ts +++ b/packages/angular/build/src/builders/library/types.ts @@ -9,7 +9,7 @@ import type { AngularCompilation } from '../../tools/angular/compilation'; import type { StylesheetPluginsass } from '../../tools/esbuild/stylesheets/stylesheet-plugin-factory'; import type { normalizeAssetPatterns } from '../../utils'; -import type { normalizeCacheOptions } from '../../utils/normalize-cache'; +import type { NormalizedCachedOptions } from '../../utils/normalize-cache'; import type { PostcssConfiguration } from '../../utils/postcss-configuration'; /** @@ -28,7 +28,7 @@ export interface NormalizedEntryPoint { /** Base name of the output bundle (e.g. 'my-lib' or 'my-lib-testing'). */ bundleName: string; - /** Absolute path to entry file. */ + /** Absolute POSIX path to the entry file. */ entryFilePath: string; /** Is this the primary entry point ('.')? */ @@ -59,33 +59,85 @@ export interface PackageJsonData { /** * Normalized options for the library builder. + * All file and directory paths are normalized to POSIX format. */ export interface NormalizedLibraryOptions { + /** Absolute POSIX path to the workspace root directory. */ workspaceRoot: string; + + /** Absolute POSIX path to the library project root directory. */ projectRoot: string; + + /** Name of the npm package from the library's `package.json`. */ packageName: string; + + /** Parsed contents of the library's `package.json`. */ packageJson: PackageJsonData; + + /** Absolute POSIX path to the build output directory. */ outputPath: string; + + /** Whether to delete the output directory before building. */ deleteOutputPath: boolean; + + /** Absolute POSIX path to the library's `package.json`. */ packageJsonPath: string; + + /** Absolute POSIX path to the TypeScript configuration file. */ tsConfigPath: string; + + /** Map of normalized entry points keyed by entry point name. */ entryPoints: Map; + + /** Default stylesheet language for inline component styles. */ inlineStyleLanguage: 'css' | 'less' | 'sass' | 'scss'; + + /** Absolute POSIX paths to style preprocessor include directories. */ styleIncludePaths: string[]; + + /** Optional Sass preprocessor configuration. */ sass?: StylesheetPluginsass; + + /** Normalized asset patterns with POSIX `input` and `output` paths. */ assets: ReturnType; - compilationMode: 'partial' | 'full'; - declarationMap: boolean; + + /** Angular compilation mode (`'partial'` or `'full'`). */ + compilationMode?: 'partial' | 'full'; + + /** Whether to generate source maps for TypeScript declaration files. */ + declarationMap?: boolean; + + /** Regular expressions matching allowed non-peer dependencies. */ allowedNonPeerDependencies: RegExp[]; + + /** Whether to preserve lifecycle scripts in the emitted `package.json`. */ keepLifecycleScripts: boolean; + + /** Whether to run the build in watch mode. */ watch: boolean; + + /** Optional file watching polling interval in milliseconds. */ poll?: number; + + /** Whether to preserve symbolic links during resolution and bundling. */ preserveSymlinks: boolean; + + /** Whether to display build progress in the terminal. */ progress: boolean; + + /** Whether to clear the terminal screen before watch rebuilds. */ clearScreen?: boolean; - cacheOptions: ReturnType; + + /** Normalized build cache configuration. */ + cacheOptions: NormalizedCachedOptions; + + /** Resolved PostCSS configuration, if present in the workspace. */ postcssConfiguration?: { config: PostcssConfiguration; configPath: string }; + + /** Resolved Tailwind CSS configuration, if applicable. */ tailwindConfiguration?: { file: string; package: string }; + + /** Whether terminal color output is enabled. */ colors: boolean; } @@ -93,10 +145,10 @@ export interface NormalizedLibraryOptions { * Cached module ID sets for a bundled entry point. */ export interface BundleResult { - /** Exact set of virtual ESM module IDs bundled into this entry point. */ + /** Exact set of virtual POSIX ESM module IDs bundled into this entry point. */ esmModuleIds: ReadonlySet; - /** Exact set of virtual DTS module IDs bundled into this entry point. */ + /** Exact set of virtual POSIX DTS module IDs bundled into this entry point. */ dtsModuleIds: ReadonlySet; /** Whether this entry point bundle imports 'tslib'. */ @@ -110,47 +162,47 @@ export interface SingleProgramCache { /** The active Angular compilation instance. */ readonly compilationInstance: AngularCompilation; - /** In-memory map of emitted JavaScript files keyed by relative output path. */ + /** In-memory map of emitted JavaScript files keyed by POSIX path. */ readonly esmFiles: Map; - /** In-memory map of emitted TypeScript declaration files keyed by relative output path. */ + /** In-memory map of emitted TypeScript declaration files keyed by POSIX path. */ readonly dtsFiles: Map; - /** Set of file paths that failed during stylesheet bundling or compilation. */ - readonly failedFiles?: ReadonlySet; + /** Set of POSIX file paths that failed during stylesheet bundling. */ + readonly failedFiles: ReadonlySet; } /** - * State preserved across incremental builds in watch mode. + * Output of the unified library compilation step. */ -export interface SingleBuildState { - /** Cached unified TypeScript and Angular compilation state. */ - singleProgramCache?: SingleProgramCache; +export interface LibraryCompilationOutput { + /** Read-only map of emitted JavaScript files keyed by POSIX path. */ + readonly esmFiles: ReadonlyMap; - /** Map of entry point names to their previous bundle results. */ - previousBundleResults: Map; + /** Read-only map of emitted TypeScript declaration files keyed by POSIX path. */ + readonly dtsFiles: ReadonlyMap; - /** Set of changed ESM file paths pending bundling. */ - pendingChangedEsmFiles: Set; + /** Set of POSIX JavaScript file paths that were added, modified, or deleted in this iteration. */ + readonly changedEsmFiles: ReadonlySet; - /** Set of changed TypeScript declaration file paths pending bundling. */ - pendingChangedDtsFiles: Set; + /** Set of POSIX declaration file paths that were added, modified, or deleted in this iteration. */ + readonly changedDtsFiles: ReadonlySet; - /** Whether the previous build iteration encountered a compilation or diagnostic error. */ - hasCompilationError?: boolean; + /** Set of all POSIX source and dependency file paths referenced during compilation. */ + readonly referencedFiles: ReadonlySet; - /** Whether package.json and secondary entry point manifests have been emitted. */ - hasEmittedManifests?: boolean; + /** Cached compilation state for subsequent incremental builds. */ + readonly cache: SingleProgramCache; - /** Whether the last emitted root package.json included a tslib dependency. */ - hasEmittedTslib?: boolean; + /** Whether any component stylesheet bundling errors occurred. */ + readonly hasStylesheetErrors: boolean; - /** Whether static assets have been emitted in a prior iteration. */ - hasEmittedAssets?: boolean; + /** Promise that resolves when compilation diagnostics have been formatted and recorded. */ + readonly diagnosePromise: Promise; - /** Whether entry points in package.json changed since the last compilation. */ - hasEntryPointsChanges?: boolean; + /** Effective declarationMap setting resolved from builder options and tsconfig. */ + readonly declarationMap: boolean; - /** Cache of output directory paths known to exist on disk. */ - directoryExists: Set; + /** Effective compilationMode setting resolved from builder options and tsconfig. */ + readonly compilationMode: 'partial' | 'full'; } 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 aa201e24c578..b5c7841c799b 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 @@ -398,11 +398,6 @@ export class VitestExecutor implements TestExecutor { const vitestServerConfig = { // Note `.vitest` is auto appended to the path. cacheDir: cacheOptions.path, - server: { - // Disable the actual file watcher. The boolean watch option above should still - // be enabled as it controls other internal behavior related to rerunning tests. - watch: null, - }, plugins: [ await createVitestConfigPlugin({ browser: browserOptions.browser, 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 6be4c6e8ebe3..72308b6b57d6 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 @@ -164,6 +164,16 @@ export async function createVitestConfigPlugin( return { name: 'angular:vitest-configuration', + enforce: 'post', + configResolved: { + order: 'post', + handler(config: Vite.ResolvedConfig) { + // Vitest's internal post-configResolved hook resets `server.watch` to `{}` when + // `watch` is enabled (`server.watch ??= {}`). Force it back to `null` so Vite uses + // a no-op watcher and relies solely on the Angular CLI build watcher. + config.server.watch = null; + }, + }, async config(config: UserWorkspaceConfig & Vite.UserConfig) { const testConfig = config.test; @@ -271,6 +281,7 @@ export async function createVitestConfigPlugin( test: { name: projectName, include, + fsModuleCache: false, // CLI provider browser options override, if present ...(browser ? { browser } : {}), // Only override if the user explicitly set it via CLI @@ -290,6 +301,7 @@ export async function createVitestConfigPlugin( return { test: { + fsModuleCache: false, coverage: await generateCoverageOption( options.coverage, testConfig?.coverage, @@ -310,7 +322,7 @@ async function loadResultFile(file: ResultFile): Promise { return textDecoder.decode(file.contents); } - return readFile(file.inputPath, 'utf-8'); + return readFile(file.source, 'utf-8'); } export function createVitestPlugins(pluginOptions: PluginOptions): Vite.Plugin[] { 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 f2fc8c221646..2452675a9abf 100644 --- a/packages/angular/build/src/builders/unit-test/test-discovery.ts +++ b/packages/angular/build/src/builders/unit-test/test-discovery.ts @@ -74,8 +74,10 @@ export async function findTests( } } - // 3. Combine and de-duplicate results - return [...resolvedTestFiles]; + // 3. Combine, de-duplicate and sort the results. The glob crawls directories asynchronously and + // returns matches in a varying order; the order feeds the build entry points, so an unsorted list + // makes the output (chunk contents and names) differ between two builds of the same sources. + return [...resolvedTestFiles].sort(); } interface TestEntrypointsOptions { 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 index 764924d9552b..91c33fd0120a 100644 --- a/packages/angular/build/src/builders/unit-test/test-discovery_spec.ts +++ b/packages/angular/build/src/builders/unit-test/test-discovery_spec.ts @@ -6,8 +6,40 @@ * found in the LICENSE file at https://angular.dev/license */ +import assert from 'node:assert'; +import { promises as fs } from 'node:fs'; +import { join } from 'node:path'; import { initializeHash } from '../../utils/hash'; -import { generateNameFromPath, getTestEntrypoints } from './test-discovery'; +import { toPosixPath } from '../../utils/path'; +import { findTests, generateNameFromPath, getTestEntrypoints } from './test-discovery'; + +describe('findTests', () => { + let projectSourceRoot: string; + + beforeEach(async () => { + const baseTmpDir = process.env['TEST_TMPDIR']; + assert(baseTmpDir, 'TEST_TMPDIR is not set'); + projectSourceRoot = await fs.mkdtemp(join(baseTmpDir, 'angular-find-tests-')); + }); + + afterEach(async () => { + await fs.rm(projectSourceRoot, { recursive: true, force: true }); + }); + + it('should return the test files sorted, regardless of the crawl order', async () => { + const files = ['z/z.spec.ts', 'a/b/c.spec.ts', 'm.spec.ts', 'a/a.spec.ts', 'b/x/y.spec.ts']; + for (const file of files) { + const path = join(projectSourceRoot, file); + await fs.mkdir(join(path, '..'), { recursive: true }); + await fs.writeFile(path, ''); + } + + const result = await findTests(['**/*.spec.ts'], [], projectSourceRoot, projectSourceRoot); + + const expected = files.map((file) => toPosixPath(join(projectSourceRoot, file))).sort(); + expect(result).toEqual(expected); + }); +}); describe('getTestEntrypoints', () => { beforeAll(async () => { 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 95107d1baec2..53039adf53e8 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 @@ -614,5 +614,43 @@ describeBuilder(execute, UNIT_TEST_BUILDER_INFO, (harness) => { const { result } = await harness.executeOnce(); expect(result?.success).toBeTrue(); }); + + it('should not serve stale cached modules across runs when an imported file changes', async () => { + harness.useTarget('test', { + ...BASE_OPTIONS, + runnerConfig: 'vitest.config.mts', + }); + + await harness.writeFiles({ + 'vitest.config.mts': ` + import { defineConfig } from 'vitest/config'; + export default defineConfig({ + test: { + fsModuleCache: true, + fsModuleCachePath: './.vitest-cache', + }, + }); + `, + 'src/app/value.ts': `export const VALUE = 'initial';`, + 'src/app/app.component.spec.ts': ` + import { test, expect } from 'vitest'; + import { VALUE } from './value'; + + test('should use the latest value from imported module', () => { + expect(VALUE).toBe('updated'); + }); + `, + }); + + const firstRun = await harness.executeOnce(); + expect(firstRun.result?.success).toBeFalse(); + + // Update the imported application file without modifying the .spec.ts file on disk + await harness.writeFile('src/app/value.ts', `export const VALUE = 'updated';`); + + const secondRun = await harness.executeOnce(); + expect(secondRun.result?.success).toBeTrue(); + harness.expectDirectory('.vitest-cache').toNotExist(); + }); }); }); 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 index 6ff753c7eac7..cab5afd48a2c 100644 --- 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 @@ -28,11 +28,11 @@ describeBuilder(execute, UNIT_TEST_BUILDER_INFO, (harness) => { await harness.executeWithCases([ // 1. Initial success - ({ result }) => { + async ({ result }) => { expect(result?.success).toBeTrue(); // 2. Introduce compilation error - harness.writeFiles({ + await harness.writeFiles({ 'src/app/app.component.spec.ts': ` import { describe, expect, test } from 'vitest' describe('AppComponent', () => { @@ -44,11 +44,11 @@ describeBuilder(execute, UNIT_TEST_BUILDER_INFO, (harness) => { }); }, // 3. Expect compilation error - ({ result }) => { + async ({ result }) => { expect(result?.success).toBeFalse(); // 4. Fix compilation error BUT introduce test failure - harness.writeFiles({ + await harness.writeFiles({ 'src/app/app.component.spec.ts': ` import { describe, expect, test } from 'vitest' describe('AppComponent', () => { diff --git a/packages/angular/build/src/builders/unit-test/tests/options/include_spec.ts b/packages/angular/build/src/builders/unit-test/tests/options/include_spec.ts index d3d4e19a30f9..4ba7c9eaa051 100644 --- a/packages/angular/build/src/builders/unit-test/tests/options/include_spec.ts +++ b/packages/angular/build/src/builders/unit-test/tests/options/include_spec.ts @@ -110,5 +110,172 @@ describeBuilder(execute, UNIT_TEST_BUILDER_INFO, (harness) => { const { result } = await harness.executeOnce(); expect(result?.success).toBeTrue(); }); + + it('should not compile unimported application files when testing standalone code', async () => { + await harness.writeFiles({ + 'src/tsconfig.spec.json': JSON.stringify({ + extends: '../tsconfig.json', + compilerOptions: { + outDir: '../out-tsc/spec', + types: ['jasmine', '@angular/localize'], + }, + files: ['polyfills.ts'], + include: ['**/*.ts', '**/*.d.ts'], + }), + 'src/app/services/test.service.spec.ts': ` + describe('TestService', () => { + it('should succeed', () => { + expect(true).toBe(true); + }); + });`, + 'src/app/unused-broken.ts': ` + // This unimported file has a TypeScript error and should not be compiled for standalone tests + export const invalidNumber: number = 'not a number'; + `, + }); + + harness.useTarget('test', { + ...BASE_OPTIONS, + include: ['src/app/services/test.service.spec.ts'], + }); + + const { result } = await harness.executeOnce(); + expect(result?.success).toBeTrue(); + }); + + it('should resolve NgModule scope when testing a non-standalone component without importing the NgModule', async () => { + await harness.writeFiles({ + 'src/tsconfig.spec.json': JSON.stringify({ + extends: '../tsconfig.json', + compilerOptions: { + outDir: '../out-tsc/spec', + types: ['jasmine', '@angular/localize'], + }, + files: ['polyfills.ts'], + include: ['**/*.ts', '**/*.d.ts'], + }), + 'src/app/app.ts': ` + import { Component } from '@angular/core'; + + @Component({ + selector: 'app-root', + template: '

Hello, {{ title }}

', + standalone: false, + }) + export class App { + title = 'test'; + } + `, + 'src/app/app-module.ts': ` + import { NgModule } from '@angular/core'; + import { BrowserModule } from '@angular/platform-browser'; + import { RouterModule } from '@angular/router'; + import { App } from './app'; + + @NgModule({ + declarations: [App], + imports: [BrowserModule, RouterModule.forRoot([])], + bootstrap: [App], + }) + export class AppModule {} + `, + 'src/app/app.spec.ts': ` + import { TestBed } from '@angular/core/testing'; + import { RouterModule } from '@angular/router'; + import { App } from './app'; + + describe('App', () => { + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [RouterModule.forRoot([])], + declarations: [App], + }).compileComponents(); + }); + + it('should create the app', () => { + const fixture = TestBed.createComponent(App); + expect(fixture.componentInstance).toBeTruthy(); + }); + }); + `, + }); + + harness.useTarget('test', { + ...BASE_OPTIONS, + include: ['src/app/app.spec.ts'], + }); + + const { result } = await harness.executeOnce(); + expect(result?.success).toBeTrue(); + }); + + it('should not compile unimported files when a non-standalone component NgModule is imported by the test', async () => { + await harness.writeFiles({ + 'src/tsconfig.spec.json': JSON.stringify({ + extends: '../tsconfig.json', + compilerOptions: { + outDir: '../out-tsc/spec', + types: ['jasmine', '@angular/localize'], + }, + files: ['polyfills.ts'], + include: ['**/*.ts', '**/*.d.ts'], + }), + 'src/app/app.ts': ` + import { Component } from '@angular/core'; + + @Component({ + selector: 'app-root', + template: '

Hello, {{ title }}

', + standalone: false, + }) + export class App { + title = 'test'; + } + `, + 'src/app/app-module.ts': ` + import { NgModule } from '@angular/core'; + import { BrowserModule } from '@angular/platform-browser'; + import { RouterModule } from '@angular/router'; + import { App } from './app'; + + @NgModule({ + declarations: [App], + imports: [BrowserModule, RouterModule.forRoot([])], + bootstrap: [App], + }) + export class AppModule {} + `, + 'src/app/app.spec.ts': ` + import { TestBed } from '@angular/core/testing'; + import { App } from './app'; + import { AppModule } from './app-module'; + + describe('App', () => { + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [AppModule], + }).compileComponents(); + }); + + it('should create the app', () => { + const fixture = TestBed.createComponent(App); + expect(fixture.componentInstance).toBeTruthy(); + }); + }); + `, + 'src/app/unused-broken.ts': ` + // This unimported file has a TypeScript error and should not be compiled when AppModule is already imported + export const invalidNumber: number = 'not a number'; + `, + }); + + harness.useTarget('test', { + ...BASE_OPTIONS, + include: ['src/app/app.spec.ts'], + }); + + const { result } = await harness.executeOnce(); + expect(result?.success).toBeTrue(); + }); }); }); diff --git a/packages/angular/build/src/builders/unit-test/tests/options/watch_spec.ts b/packages/angular/build/src/builders/unit-test/tests/options/watch_spec.ts index d1840beb4a96..79e25ca84b27 100644 --- a/packages/angular/build/src/builders/unit-test/tests/options/watch_spec.ts +++ b/packages/angular/build/src/builders/unit-test/tests/options/watch_spec.ts @@ -55,5 +55,38 @@ describeBuilder(execute, UNIT_TEST_BUILDER_INFO, (harness) => { }, ]); }); + + it('should disable the Vite server file watcher when watch is true', async () => { + harness.useTarget('test', { + ...BASE_OPTIONS, + watch: true, + runnerConfig: 'vitest.config.mts', + }); + + await harness.writeFile( + 'vitest.config.mts', + ` + import { defineConfig } from 'vitest/config'; + export default defineConfig({ + plugins: [ + { + name: 'assert-watch-disabled', + configureVitest({ vitest, project }) { + if (vitest.vite.config.server.watch !== null || project.vite.config.server.watch !== null) { + throw new Error('Expected Vite server.watch to be null'); + } + }, + }, + ], + }); + `, + ); + + await harness.executeWithCases([ + ({ result }) => { + expect(result?.success).toBeTrue(); + }, + ]); + }); }); }); diff --git a/packages/angular/build/src/private.ts b/packages/angular/build/src/private.ts index 9db709640b57..e95ad26af3fc 100644 --- a/packages/angular/build/src/private.ts +++ b/packages/angular/build/src/private.ts @@ -34,7 +34,6 @@ export { // Tools export type { ExternalResultMetadata } from './tools/esbuild/bundler-execution-result'; -export { emitFilesToDisk } from './tools/esbuild/utils'; export { transformSupportedBrowsersToTargets } from './tools/esbuild/target'; export { SassCompiler } from './tools/sass/sass-service'; @@ -73,6 +72,7 @@ export { ComponentStylesheetBundler } from './tools/esbuild/angular/component-st export * from './utils/bundle-calculator'; export { checkPort } from './utils/check-port'; export { deleteOutputDir } from './utils/delete-output-dir'; +export { emitFilesToDisk } from './utils/disk-files'; export { type I18nOptions, createI18nOptions, loadTranslations } from './utils/i18n-options'; export { IndexHtmlGenerator, diff --git a/packages/angular/build/src/tools/angular/angular-host.ts b/packages/angular/build/src/tools/angular/angular-host.ts index 9322b5683dc5..f30df5bab64d 100644 --- a/packages/angular/build/src/tools/angular/angular-host.ts +++ b/packages/angular/build/src/tools/angular/angular-host.ts @@ -35,7 +35,10 @@ export interface AngularHostOptions { * field. The `version` field is required when used with a TypeScript BuilderProgram. * @param program The TypeScript Program instance to patch. */ -export function ensureSourceFileVersions(program: ts.Program): void { +export function ensureSourceFileVersions( + program: ts.Program, + previousProgram?: ts.BuilderProgram, +): void { const baseGetSourceFiles = program.getSourceFiles; // TODO: Update Angular compiler to add versions to all internal files and remove this program.getSourceFiles = function (...parameters) { @@ -45,7 +48,11 @@ export function ensureSourceFileVersions(program: ts.Program): void { for (const file of files) { if (file.version === undefined) { - file.version = calculateHash(file.text); + const previousFile: (ts.SourceFile & { version?: string }) | undefined = + previousProgram && !file.fileName.endsWith('.ngtypecheck.ts') + ? previousProgram.getSourceFile(file.fileName) + : undefined; + file.version = previousFile?.version ?? calculateHash(file.text); } } 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 d766680d1674..3da78cdf0275 100644 --- a/packages/angular/build/src/tools/angular/compilation/angular-compilation.ts +++ b/packages/angular/build/src/tools/angular/compilation/angular-compilation.ts @@ -23,6 +23,7 @@ export interface FileTransformResult { export interface AngularCompilationOptions { allowJs?: boolean; + compilationMode?: 'full' | 'partial' | 'experimental-local'; declarationMap?: boolean; isolatedModules?: boolean; sourceMap?: boolean; diff --git a/packages/angular/build/src/tools/angular/compilation/angular-compilation_spec.ts b/packages/angular/build/src/tools/angular/compilation/angular-compilation_spec.ts index 2d001dfeff7c..3c09b87f69d7 100644 --- a/packages/angular/build/src/tools/angular/compilation/angular-compilation_spec.ts +++ b/packages/angular/build/src/tools/angular/compilation/angular-compilation_spec.ts @@ -91,8 +91,12 @@ describe('AngularCompilation', () => { return this.sourceFiles; } - public async testLoadConfiguration(tsconfig: string, overrides?: CompilerOptionOverrides) { - return this.loadConfiguration(tsconfig, overrides); + public async testLoadConfiguration( + tsconfig: string, + overrides?: CompilerOptionOverrides, + buildType?: 'application' | 'library', + ) { + return this.loadConfiguration(tsconfig, overrides, buildType); } } @@ -162,12 +166,83 @@ describe('AngularCompilation', () => { jasmine.any(Map), ); expect(result.rootNames).toEqual(['/src/main.ts']); + expect(result.fallbackRootNames).toBeUndefined(); expect(result.compilerOptions.target).toBe(ts.ScriptTarget.ES2022); expect(result.compilerOptions.inlineSources).toBe(true); expect(result.warnings.length).toBeGreaterThan(0); expect(result.tsConfigFiles).toEqual(['tsconfig.json']); }); + it('filters rootNames to rootFiles while retaining ambient declaration files and setting fallbackRootNames', async () => { + const compilation = new MockTypeScriptCompilation(); + const mockReadConfig = jasmine.createSpy('readConfiguration').and.returnValue({ + options: { target: ts.ScriptTarget.ES2022 }, + rootNames: [ + '/src/app/included.spec.ts', + '/src/app/excluded.spec.ts', + '/src/app/app-module.ts', + '/src/env.d.ts', + '/src/types.d.mts', + '/src/legacy.d.cts', + ], + errors: [], + }); + spyOn(TypeScriptCompilation, 'loadCompilerCli').and.resolveTo({ + readConfiguration: mockReadConfig, + } as unknown as typeof import('@angular/compiler-cli')); + + const result = await compilation.testLoadConfiguration('tsconfig.spec.json', { + rootFiles: ['/src/app/included.spec.ts'], + }); + + expect(result.rootNames).toEqual([ + '/src/app/included.spec.ts', + '/src/env.d.ts', + '/src/types.d.mts', + '/src/legacy.d.cts', + ]); + expect(result.fallbackRootNames).toEqual([ + '/src/app/included.spec.ts', + '/src/app/excluded.spec.ts', + '/src/app/app-module.ts', + '/src/env.d.ts', + '/src/types.d.mts', + '/src/legacy.d.cts', + ]); + }); + + it('omits fallbackRootNames when buildType is "library" or when rootFiles covers all tsconfig files', async () => { + const compilation = new MockTypeScriptCompilation(); + const mockReadConfig = jasmine.createSpy('readConfiguration').and.returnValue({ + options: { target: ts.ScriptTarget.ES2022 }, + rootNames: ['/src/public-api.ts', '/src/internal.ts', '/src/env.d.ts'], + errors: [], + }); + spyOn(TypeScriptCompilation, 'loadCompilerCli').and.resolveTo({ + readConfiguration: mockReadConfig, + } as unknown as typeof import('@angular/compiler-cli')); + + const libraryResult = await compilation.testLoadConfiguration( + 'tsconfig.lib.json', + { rootFiles: ['/src/public-api.ts'] }, + 'library', + ); + expect(libraryResult.rootNames).toEqual(['/src/public-api.ts', '/src/env.d.ts']); + expect(libraryResult.fallbackRootNames).toBeUndefined(); + + const fullSetResult = await compilation.testLoadConfiguration( + 'tsconfig.app.json', + { rootFiles: ['/src/public-api.ts', '/src/internal.ts'] }, + 'application', + ); + expect(fullSetResult.rootNames).toEqual([ + '/src/public-api.ts', + '/src/internal.ts', + '/src/env.d.ts', + ]); + expect(fullSetResult.fallbackRootNames).toBeUndefined(); + }); + it('passes extendedConfigCache to readConfiguration and invalidates entries on config change', async () => { const compilation = new MockTypeScriptCompilation(); let passedCache: Map | undefined; 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 6f45199d1421..4ca1480f646c 100644 --- a/packages/angular/build/src/tools/angular/compilation/aot-compilation.ts +++ b/packages/angular/build/src/tools/angular/compilation/aot-compilation.ts @@ -37,16 +37,42 @@ import { printSourceFileWithMap } from './typescript-printer'; */ const HMR_MODIFIED_FILE_LIMIT = 32; +/** + * Extension for Angular type-checking files. + */ +const TTC_EXTENSION = '.ngtypecheck.ts'; + +/** + * Length of the `.ngtypecheck.ts` extension. + */ +const TTC_EXTENSION_LENGTH = TTC_EXTENSION.length; + +/** + * Checks if a file is an Angular type-checking shim file. + */ +function isNgTypeCheckFile(fileName: string): boolean { + return fileName.endsWith(TTC_EXTENSION); +} + +/** + * Returns the original filename for a type-checking shim file. + */ +function getOriginalFilename(fileName: string): string { + return fileName.slice(0, -TTC_EXTENSION_LENGTH) + '.ts'; +} + class AngularCompilationState { constructor( public readonly angularProgram: ng.NgtscProgram, public readonly compilerHost: ng.CompilerHost, - public readonly typeScriptProgram: ts.EmitAndSemanticDiagnosticsBuilderProgram, + public typeScriptProgram: ts.EmitAndSemanticDiagnosticsBuilderProgram, public readonly affectedFiles: ReadonlySet, public readonly templateDiagnosticsOptimization: ng.OptimizeFor, public readonly webWorkerTransform: ts.TransformerFactory, public readonly useTypeScriptTranspilation: boolean, public readonly diagnosticCache = new WeakMap(), + public readonly filesWithoutDtsSignature = new Set(), + public readonly useFallbackRootNames = false, ) {} get angularCompiler() { @@ -75,6 +101,7 @@ export class AotCompilation extends TypeScriptCompilation { const { compilerOptions, rootNames, + fallbackRootNames, errors: configurationDiagnostics, warnings, tsConfigFiles, @@ -103,8 +130,8 @@ export class AotCompilation extends TypeScriptCompilation { if (hostOptions.modifiedFiles) { this.invalidateFiles(hostOptions.modifiedFiles); - for (const modifiedFile of hostOptions.modifiedFiles) { - if (this.#state) { + if (this.#state && hostOptions.modifiedFiles.size > 0) { + for (const modifiedFile of hostOptions.modifiedFiles) { // Clear package.json cache if a node modules file was modified if (!clearPackageJsonCache && modifiedFile.includes('node_modules')) { clearPackageJsonCache = true; @@ -120,6 +147,14 @@ export class AotCompilation extends TypeScriptCompilation { } } } + + if (useTypeChecking || useTypeScriptTranspilation) { + populatePreviousDtsSignatures( + this.#state.typeScriptProgram, + hostOptions.modifiedFiles, + this.#state.filesWithoutDtsSignature, + ); + } } } @@ -132,13 +167,48 @@ export class AotCompilation extends TypeScriptCompilation { this.sourceFiles, ); + // If a previous watch rebuild already fell back to the full tsconfig root file set, + // start directly with fallbackRootNames to avoid repeating the two-step check. + let useFallbackRootNames = this.#state?.useFallbackRootNames ?? false; + // Create the Angular specific program that contains the Angular compiler - const angularProgram = profileSync( + let angularProgram = profileSync( 'NG_CREATE_PROGRAM', - () => new NgtscProgram(rootNames, compilerOptions, host, this.#state?.angularProgram), + () => + new NgtscProgram( + (useFallbackRootNames && fallbackRootNames) || rootNames, + compilerOptions, + host, + this.#state?.angularProgram, + ), ); - const angularCompiler = angularProgram.compiler; - const angularTypeScriptProgram = angularProgram.getTsProgram(); + let angularCompiler = angularProgram.compiler; + + await profileAsync('NG_ANALYZE_PROGRAM', () => angularCompiler.analyzeAsync()); + + let angularTypeScriptProgram = angularProgram.getTsProgram(); + + // When rootFiles filtering is active and a non-standalone component without a resolved + // NgModule scope is encountered, its declaring NgModule may not be directly imported by + // the test entry points. Fall back to the full tsconfig root file set if it contains + // additional uncompiled files so the Angular compiler can discover all NgModules and + // resolve component compilation scopes. + if ( + !useFallbackRootNames && + fallbackRootNames?.some((file) => !angularTypeScriptProgram.getSourceFile(file)) && + hasUnscopedComponent(angularCompiler) + ) { + useFallbackRootNames = true; + angularProgram = profileSync( + 'NG_CREATE_PROGRAM_FALLBACK', + () => new NgtscProgram(fallbackRootNames, compilerOptions, host, angularProgram), + ); + angularCompiler = angularProgram.compiler; + + await profileAsync('NG_ANALYZE_PROGRAM_FALLBACK', () => angularCompiler.analyzeAsync()); + angularTypeScriptProgram = angularProgram.getTsProgram(); + } + ensureSourceFileVersions(angularTypeScriptProgram); let oldProgram = this.#state?.typeScriptProgram; @@ -155,7 +225,17 @@ export class AotCompilation extends TypeScriptCompilation { configurationDiagnostics, ); - await profileAsync('NG_ANALYZE_PROGRAM', () => angularCompiler.analyzeAsync()); + const filesWithoutDtsSignature = this.#state?.filesWithoutDtsSignature ?? new Set(); + if (!this.#state) { + for (const sourceFile of typeScriptProgram.getSourceFiles()) { + if ( + !sourceFile.isDeclarationFile && + !angularCompiler.ignoreForDiagnostics.has(sourceFile) + ) { + filesWithoutDtsSignature.add(sourceFile.fileName); + } + } + } let templateUpdates; if (useHmr && hostOptions.modifiedFiles && this.#state) { @@ -203,38 +283,62 @@ export class AotCompilation extends TypeScriptCompilation { const affectedFiles = useTypeChecking || useTypeScriptTranspilation ? profileSync('NG_FIND_AFFECTED', () => - findAffectedFiles(typeScriptProgram, angularCompiler, usingBuildInfo), + findAffectedFiles( + typeScriptProgram, + angularCompiler, + usingBuildInfo, + this.#state?.typeScriptProgram, + filesWithoutDtsSignature, + ), ) : new Set(); const componentResourcesDependencies = new Map(); // Get all files referenced in the TypeScript/Angular program including component resources - const referencedFiles = [ - ...tsConfigFiles, - ...typeScriptProgram - .getSourceFiles() - .filter((sourceFile) => !angularCompiler.ignoreForEmit.has(sourceFile)) - .flatMap((sourceFile) => { - const resourceDependencies = angularCompiler.getResourceDependencies(sourceFile); - componentResourcesDependencies.set(sourceFile.fileName, resourceDependencies); - // Also invalidate Angular diagnostics for a source file if component resources are modified - if (this.#state && hostOptions.modifiedFiles?.size) { - for (const resourceDependency of resourceDependencies) { - if ( - hostOptions.modifiedFiles.has(resourceDependency) && - !/\.(?:css|scss|sass|less)$/i.test(resourceDependency) - ) { - this.#state.diagnosticCache.delete(sourceFile); - // Also mark as affected in case changed template affects diagnostics - affectedFiles.add(sourceFile); - } - } + const referencedFiles = [...tsConfigFiles]; + for (const sourceFile of typeScriptProgram.getSourceFiles()) { + if (angularCompiler.ignoreForEmit.has(sourceFile)) { + continue; + } + + referencedFiles.push(sourceFile.fileName); + if (sourceFile.isDeclarationFile) { + continue; + } + + const resourceDependencies = angularCompiler.getResourceDependencies(sourceFile); + componentResourcesDependencies.set(sourceFile.fileName, resourceDependencies); + + if (resourceDependencies.length === 0) { + continue; + } + + referencedFiles.push(...resourceDependencies); + + // Also invalidate Angular diagnostics for a source file if component resources are modified + if (this.#state && hostOptions.modifiedFiles?.size) { + for (const resourceDependency of resourceDependencies) { + if ( + !hostOptions.modifiedFiles.has(resourceDependency) || + /\.(?:css|scss|sass|less)$/i.test(resourceDependency) + ) { + continue; + } + + this.#state.diagnosticCache.delete(sourceFile); + + const oldSourceFile = this.#state.typeScriptProgram.getSourceFile(sourceFile.fileName); + if (oldSourceFile) { + this.#state.diagnosticCache.delete(oldSourceFile); } - return [sourceFile.fileName, ...resourceDependencies]; - }), - ]; + // Also mark as affected in case changed template affects diagnostics + affectedFiles.add(sourceFile); + break; + } + } + } this.#state = new AngularCompilationState( angularProgram, @@ -245,6 +349,8 @@ export class AotCompilation extends TypeScriptCompilation { createWorkerTransformer(hostOptions.processWebWorker.bind(hostOptions)), useTypeScriptTranspilation, this.#state?.diagnosticCache, + filesWithoutDtsSignature, + useFallbackRootNames, ); return { @@ -270,61 +376,92 @@ export class AotCompilation extends TypeScriptCompilation { const syntactic = modes & DiagnosticModes.Syntactic; const semantic = modes & DiagnosticModes.Semantic; - // Collect program level diagnostics - if (modes & DiagnosticModes.Option) { - yield* typeScriptProgram.getConfigFileParsingDiagnostics(); - yield* angularCompiler.getOptionDiagnostics(); - yield* typeScriptProgram.getOptionsDiagnostics(); - } - if (syntactic) { - yield* typeScriptProgram.getGlobalDiagnostics(); - } - - // Collect source file specific diagnostics - for (const sourceFile of typeScriptProgram.getSourceFiles()) { - if (angularCompiler.ignoreForDiagnostics.has(sourceFile)) { - continue; + try { + // Collect program level diagnostics + if (modes & DiagnosticModes.Option) { + yield* typeScriptProgram.getConfigFileParsingDiagnostics(); + yield* angularCompiler.getOptionDiagnostics(); + yield* typeScriptProgram.getOptionsDiagnostics(); } - if (syntactic) { - // TypeScript will use cached diagnostics for files that have not been - // changed or affected for this build when using incremental building. - yield* profileSync( - 'NG_DIAGNOSTICS_SYNTACTIC', - () => typeScriptProgram.getSyntacticDiagnostics(sourceFile), - true, - ); + yield* typeScriptProgram.getGlobalDiagnostics(); } - if (!semantic) { - continue; - } + // Collect source file specific diagnostics + for (const sourceFile of typeScriptProgram.getSourceFiles()) { + if (angularCompiler.ignoreForDiagnostics.has(sourceFile)) { + continue; + } - yield* profileSync( - 'NG_DIAGNOSTICS_SEMANTIC', - () => typeScriptProgram.getSemanticDiagnostics(sourceFile), - true, - ); + if (syntactic) { + // TypeScript will use cached diagnostics for files that have not been + // changed or affected for this build when using incremental building. + yield* profileSync( + 'NG_DIAGNOSTICS_SYNTACTIC', + () => typeScriptProgram.getSyntacticDiagnostics(sourceFile), + true, + ); + } - // Declaration files cannot have template diagnostics - if (sourceFile.isDeclarationFile) { - continue; - } + if (!semantic) { + continue; + } - // Only request Angular template diagnostics for affected files to avoid - // overhead of template diagnostics for unchanged files. - if (affectedFiles.has(sourceFile)) { - const angularDiagnostics = profileSync( - 'NG_DIAGNOSTICS_TEMPLATE', - () => angularCompiler.getDiagnosticsForFile(sourceFile, templateDiagnosticsOptimization), + yield* profileSync( + 'NG_DIAGNOSTICS_SEMANTIC', + () => typeScriptProgram.getSemanticDiagnostics(sourceFile), true, ); - diagnosticCache.set(sourceFile, angularDiagnostics); - yield* angularDiagnostics; - } else { - const angularDiagnostics = diagnosticCache.get(sourceFile); - if (angularDiagnostics) { + + // Declaration files cannot have template diagnostics + if (sourceFile.isDeclarationFile) { + continue; + } + + // Only request Angular template diagnostics for affected files to avoid + // overhead of template diagnostics for unchanged files. + if (affectedFiles.has(sourceFile)) { + const angularDiagnostics = profileSync( + 'NG_DIAGNOSTICS_TEMPLATE', + () => + angularCompiler.getDiagnosticsForFile(sourceFile, templateDiagnosticsOptimization), + true, + ); + diagnosticCache.set(sourceFile, angularDiagnostics); yield* angularDiagnostics; + } else { + const angularDiagnostics = diagnosticCache.get(sourceFile); + if (angularDiagnostics) { + yield* angularDiagnostics; + } + } + } + } finally { + // Angular's template typechecker lazily creates `.ngtypecheck.ts` shims in a new `ts.Program` + // during `getDiagnosticsForFile`. Sync the builder program with `angularCompiler.getCurrentProgram()` + // so the next incremental build does not treat all `.ngtypecheck.ts` files as newly added files. + const currentTsProgram = angularCompiler.getCurrentProgram(); + if (currentTsProgram !== typeScriptProgram.getProgram()) { + ensureSourceFileVersions(currentTsProgram, typeScriptProgram); + this.#state.typeScriptProgram = ts.createEmitAndSemanticDiagnosticsBuilderProgram( + currentTsProgram, + this.#state.compilerHost, + typeScriptProgram, + ); + + const baseEmit = currentTsProgram.emit; + currentTsProgram.emit = () => ({ emitSkipped: true, diagnostics: [] }); + try { + while ( + this.#state.typeScriptProgram.getSemanticDiagnosticsOfNextAffectedFile( + undefined, + () => true, + ) + ) { + /* empty */ + } + } finally { + currentTsProgram.emit = baseEmit; } } } @@ -419,7 +556,7 @@ export class AotCompilation extends TypeScriptCompilation { if ( angularCompiler.incrementalCompilation.safeToSkipEmit(sourceFile) && - !affectedFiles.has(sourceFile) + (!useTypeScriptTranspilation || !affectedFiles.has(sourceFile)) ) { continue; } @@ -483,40 +620,138 @@ export class AotCompilation extends TypeScriptCompilation { function findAffectedFiles( builder: ts.EmitAndSemanticDiagnosticsBuilderProgram, - { ignoreForDiagnostics }: ng.NgtscProgram['compiler'], + { ignoreForDiagnostics, incrementalCompilation }: ng.NgtscProgram['compiler'], includeTTC: boolean, + oldBuilder: ts.EmitAndSemanticDiagnosticsBuilderProgram | undefined, + filesWithoutDtsSignature: Set, ): Set { const affectedFiles = new Set(); - // eslint-disable-next-line no-constant-condition - while (true) { - const result = builder.getSemanticDiagnosticsOfNextAffectedFile(undefined, (sourceFile) => { - // If the affected file is a TTC shim, add the shim's original source file. - // This ensures that changes that affect TTC are typechecked even when the changes - // are otherwise unrelated from a TS perspective and do not result in Ivy codegen changes. - // For example, changing @Input property types of a directive used in another component's - // template. - // A TTC shim is a file that has been ignored for diagnostics and has a filename ending in `.ngtypecheck.ts`. - if (ignoreForDiagnostics.has(sourceFile) && sourceFile.fileName.endsWith('.ngtypecheck.ts')) { - // This file name conversion relies on internal compiler logic and should be converted - // to an official method when available. 15 is length of `.ngtypecheck.ts` - const originalFilename = sourceFile.fileName.slice(0, -15) + '.ts'; - const originalSourceFile = builder.getSourceFile(originalFilename); - if (originalSourceFile) { - affectedFiles.add(originalSourceFile); + // Temporarily disable `isolatedModules` and `verbatimModuleSyntax` while walking affected + // files so TypeScript's `getFilesAffectedByUpdatedShapeWhenModuleEmit` yields dependent + // `.ngtypecheck.ts` shims to the `ignoreSourceFile` callback when a file's `.d.ts` shape + // changes, instead of returning only the modified file itself. + const compilerOptions = builder.getCompilerOptions(); + const prevIsolatedModules = compilerOptions.isolatedModules; + const prevVerbatimModuleSyntax = compilerOptions.verbatimModuleSyntax; + compilerOptions.isolatedModules = false; + compilerOptions.verbatimModuleSyntax = false; + + const program = builder.getProgram(); + const baseEmit = program.emit; + const oldProgram = oldBuilder?.getProgram(); + program.emit = ( + targetSourceFile, + writeFile, + cancellationToken, + emitOnly, + customTransformers, + ...rest + ) => { + if (targetSourceFile) { + if (isNgTypeCheckFile(targetSourceFile.fileName)) { + return { emitSkipped: true, diagnostics: [] }; + } + + if (oldProgram && filesWithoutDtsSignature.has(targetSourceFile.fileName)) { + const oldSourceFile = oldProgram.getSourceFile(targetSourceFile.fileName); + if (oldSourceFile) { + let oldDtsText: string | undefined; + let oldHasDiagnostics = false; + oldProgram.emit( + oldSourceFile, + (_fileName, text, _bom, _onError, _sourceFiles, data) => { + oldDtsText = text; + oldHasDiagnostics = !!(data as { diagnostics?: unknown[] } | undefined)?.diagnostics + ?.length; + }, + cancellationToken, + emitOnly, + customTransformers, + ...(rest as []), + ); + + return baseEmit.call( + program, + targetSourceFile, + (fileName, text, writeByteOrderMark, onError, sourceFiles, data) => { + const newHasDiagnostics = !!(data as { diagnostics?: unknown[] } | undefined) + ?.diagnostics?.length; + if (text !== oldDtsText || newHasDiagnostics !== oldHasDiagnostics) { + filesWithoutDtsSignature.delete(targetSourceFile.fileName); + writeFile?.(fileName, text, writeByteOrderMark, onError, sourceFiles, data); + } + }, + cancellationToken, + emitOnly, + customTransformers, + ...(rest as []), + ); } - return true; + filesWithoutDtsSignature.delete(targetSourceFile.fileName); } + } - return false; - }); + return baseEmit.call( + program, + targetSourceFile, + writeFile, + cancellationToken, + emitOnly, + customTransformers, + ...(rest as []), + ); + }; + + try { + while ( + builder.getSemanticDiagnosticsOfNextAffectedFile(undefined, (sourceFile) => { + if (ignoreForDiagnostics.has(sourceFile)) { + // If the affected file is a TTC shim, add the shim's original source file. + // This ensures that changes that affect TTC are typechecked even when the changes + // are otherwise unrelated from a TS perspective and do not result in Ivy codegen changes. + // For example, changing @Input property types of a directive used in another component's + // template. + // A TTC shim is a file that has been ignored for diagnostics and has a filename ending in `.ngtypecheck.ts`. + if (isNgTypeCheckFile(sourceFile.fileName)) { + // This file name conversion relies on internal compiler logic and should be converted + // to an official method when available. 15 is length of `.ngtypecheck.ts` + const originalFilename = getOriginalFilename(sourceFile.fileName); + const originalSourceFile = builder.getSourceFile(originalFilename); + if (originalSourceFile) { + affectedFiles.add(originalSourceFile); + } + } + } else { + affectedFiles.add(sourceFile); + } + + return true; + }) + ) { + /* empty */ + } + } finally { + program.emit = baseEmit; + compilerOptions.isolatedModules = prevIsolatedModules; + compilerOptions.verbatimModuleSyntax = prevVerbatimModuleSyntax; + } - if (!result) { - break; + // Also include files identified by Angular's incremental compilation graph as needing + // re-emission or template re-typechecking (such as selector renames or required input changes + // that only affect Angular metadata without altering standard TypeScript `.d.ts` signatures). + for (const sourceFile of builder.getSourceFiles()) { + if (sourceFile.isDeclarationFile || ignoreForDiagnostics.has(sourceFile)) { + continue; } - affectedFiles.add(result.affected as ts.SourceFile); + if ( + !incrementalCompilation.safeToSkipEmit(sourceFile) || + incrementalCompilation.priorTypeCheckingResultsFor(sourceFile) === null + ) { + affectedFiles.add(sourceFile); + } } // Add all files with associated template type checking files. @@ -525,10 +760,10 @@ function findAffectedFiles( // info is present. if (includeTTC) { for (const sourceFile of builder.getSourceFiles()) { - if (ignoreForDiagnostics.has(sourceFile) && sourceFile.fileName.endsWith('.ngtypecheck.ts')) { + if (ignoreForDiagnostics.has(sourceFile) && isNgTypeCheckFile(sourceFile.fileName)) { // This file name conversion relies on internal compiler logic and should be converted // to an official method when available. 15 is length of `.ngtypecheck.ts` - const originalFilename = sourceFile.fileName.slice(0, -15) + '.ts'; + const originalFilename = getOriginalFilename(sourceFile.fileName); const originalSourceFile = builder.getSourceFile(originalFilename); if (originalSourceFile) { affectedFiles.add(originalSourceFile); @@ -539,3 +774,129 @@ function findAffectedFiles( return affectedFiles; } + +/** + * Ensures `.d.ts` shape signatures are computed on the previous `BuilderProgram` for modified + * files before the next `BuilderProgram` is created. + * + * On the initial build (`oldProgram === undefined`), TypeScript initializes each file's shape + * signature to its `.ts` source version hash rather than emitting `.d.ts` declarations for every + * file. When a file is modified on a watch rebuild, TypeScript computes its new `.d.ts` signature + * and compares it against the previous program's stored signature. Emitting the `.d.ts` signature + * on `oldProgram` for the modified file replaces its initial `.ts` version hash with its true + * `.d.ts` signature prior to diffing, avoiding false-positive `.d.ts` cascades across importers. + */ +function populatePreviousDtsSignatures( + oldProgram: ts.EmitAndSemanticDiagnosticsBuilderProgram, + modifiedFiles: ReadonlySet, + filesWithoutDtsSignature: Set, +): void { + const filesToUpdate: ts.SourceFile[] = []; + for (const modifiedFile of modifiedFiles) { + const sf = oldProgram.getSourceFile(modifiedFile); + if (sf && !sf.isDeclarationFile && filesWithoutDtsSignature.delete(sf.fileName)) { + filesToUpdate.push(sf); + } + } + + if (filesToUpdate.length === 0) { + return; + } + + const compilerOptions = oldProgram.getCompilerOptions(); + const prevDeclaration = compilerOptions.declaration; + const prevComposite = compilerOptions.composite; + const prevNoEmit = compilerOptions.noEmit; + const prevNoEmitOnError = compilerOptions.noEmitOnError; + + compilerOptions.declaration = true; + compilerOptions.composite = false; + compilerOptions.noEmit = false; + compilerOptions.noEmitOnError = false; + + const program = oldProgram.getProgram(); + const baseEmit = program.emit as ( + targetSourceFile?: ts.SourceFile, + writeFile?: ts.WriteFileCallback, + cancellationToken?: ts.CancellationToken, + emitOnlyDtsFiles?: boolean | number, + customTransformers?: ts.CustomTransformers, + forceDtsEmit?: boolean, + ) => ts.EmitResult; + program.emit = (targetSourceFile, writeFile, cancellationToken) => + baseEmit.call( + program, + targetSourceFile, + writeFile, + cancellationToken, + 2, // ts.EmitOnly.BuilderSignature + undefined, + true, // forceDtsEmit + ); + + try { + for (const sf of filesToUpdate) { + oldProgram.emit(sf, () => {}, undefined, true); + } + } finally { + program.emit = baseEmit; + compilerOptions.declaration = prevDeclaration; + compilerOptions.composite = prevComposite; + compilerOptions.noEmit = prevNoEmit; + compilerOptions.noEmitOnError = prevNoEmitOnError; + } +} + +/** + * Checks whether the analyzed Angular program contains any non-standalone `@Component` + * declarations in user source files that lack an owning `NgModule` scope. + */ +function hasUnscopedComponent(angularCompiler: ng.NgCompiler): boolean { + // `incrementalCompilation.state` is typed as the `IncrementalState` union + // (`AnalyzedIncrementalState | DeltaIncrementalState | FreshIncrementalState`), where only + // `AnalyzedIncrementalState` (populated once `analyzeAsync()` completes) contains + // `priorAnalysis`. Because the `IncrementalStateKind` discriminant enum is not re-exported + // from `@angular/compiler-cli`, narrow the union via `'priorAnalysis' in state`. + const state = angularCompiler.incrementalCompilation.state; + if (!('priorAnalysis' in state)) { + return false; + } + + let templateTypeChecker: ReturnType | undefined; + + for (const [sourceFile, records] of state.priorAnalysis) { + if (records.length === 0 || angularCompiler.ignoreForEmit.has(sourceFile)) { + continue; + } + + for (const { node } of records) { + if (!ts.isClassDeclaration(node)) { + continue; + } + + const meta = angularCompiler.getMeta(node); + if (meta && 'isComponent' in meta && meta.isComponent && !meta.isStandalone) { + if (!templateTypeChecker) { + // `NgtscProgram` initializes `NgCompiler` with `enableTemplateTypeChecker = false`, which + // guards the public `getTemplateTypeChecker()` getter. Temporarily enable the flag after + // `analyzeAsync()` has already constructed `TemplateTypeCheckerImpl` so we can query + // `getOwningNgModule()` without altering `typeCheckingConfig.enableTemplateTypeChecker`. + const compiler = angularCompiler as { enableTemplateTypeChecker: boolean }; + const previous = compiler.enableTemplateTypeChecker; + try { + compiler.enableTemplateTypeChecker = true; + templateTypeChecker = angularCompiler.getTemplateTypeChecker(); + } finally { + compiler.enableTemplateTypeChecker = previous; + } + } + + if (templateTypeChecker.getOwningNgModule(node) === null) { + return true; + } + } + } + } + + return false; +} diff --git a/packages/angular/build/src/tools/angular/compilation/compiler-options.ts b/packages/angular/build/src/tools/angular/compilation/compiler-options.ts index 1720a8bb0516..b2c83d616f08 100644 --- a/packages/angular/build/src/tools/angular/compilation/compiler-options.ts +++ b/packages/angular/build/src/tools/angular/compilation/compiler-options.ts @@ -71,6 +71,8 @@ export function transformCompilerOptions( compilerOptions.compilationMode = 'full'; } else if (overrides?.compilationMode) { compilerOptions.compilationMode = overrides.compilationMode; + } else if (isLibrary) { + compilerOptions.compilationMode ??= 'partial'; } // Enable incremental compilation by default if caching is enabled and incremental is not explicitly disabled @@ -114,7 +116,9 @@ export function transformCompilerOptions( compilerOptions.moduleResolution = typeScript.ModuleResolutionKind.Bundler; compilerOptions.importHelpers = true; compilerOptions.declaration = true; - compilerOptions.declarationMap = overrides?.declarationMap; + compilerOptions.declarationMap = !!( + overrides?.declarationMap ?? compilerOptions.declarationMap + ); compilerOptions.declarationDir = undefined; } diff --git a/packages/angular/build/src/tools/angular/compilation/parallel-worker.ts b/packages/angular/build/src/tools/angular/compilation/parallel-worker.ts index a74c032a1b70..475592bd478b 100644 --- a/packages/angular/build/src/tools/angular/compilation/parallel-worker.ts +++ b/packages/angular/build/src/tools/angular/compilation/parallel-worker.ts @@ -125,6 +125,7 @@ export async function initialize(request: InitRequest): Promise canonicalizePath(toPosixPath(file))), ); + const fallbackFilesSet = new Set(rootFilesSet); for (const file of originalRootNames) { + const canonicalFile = canonicalizePath(toPosixPath(file)); + // Ambient declaration files (.d.ts) are not imported directly, so they must always be + // retained from the tsconfig to preserve global type definitions. if (/\.d\.[cm]?ts$/i.test(file)) { - rootFilesSet.add(canonicalizePath(toPosixPath(file))); + rootFilesSet.add(canonicalFile); } + fallbackFilesSet.add(canonicalFile); } rootNames = [...rootFilesSet]; + // For application builds (including unit tests), preserve the full set of tsconfig root + // files as a fallback in case the AOT compilation encounters a non-standalone component + // whose declaring NgModule is not directly imported by the entry points. Note that when a + // test tsconfig uses broad globs (e.g., `src/**/*.ts`), this fallback set will also include + // non-included spec files; this is an acceptable trade-off to keep compilation agnostic of + // test-file naming conventions and only occurs for legacy non-standalone components lacking + // explicit NgModule imports. Library builds are excluded because all public NgModules must + // be reachable from the library entry points and should never resolve compilation scope from + // unexported tsconfig files. + if (buildType === 'application' && fallbackFilesSet.size > rootFilesSet.size) { + fallbackRootNames = [...fallbackFilesSet]; + } } const { compilerOptions, warnings } = transformCompilerOptions( @@ -94,6 +120,7 @@ export abstract class TypeScriptCompilation extends AngularCompilation { return { compilerOptions, rootNames, + fallbackRootNames, errors, warnings, tsConfigFiles, diff --git a/packages/angular/build/src/tools/esbuild/bundler-context.ts b/packages/angular/build/src/tools/esbuild/bundler-context.ts index a4228caa5782..202ab514cbfe 100644 --- a/packages/angular/build/src/tools/esbuild/bundler-context.ts +++ b/packages/angular/build/src/tools/esbuild/bundler-context.ts @@ -328,7 +328,11 @@ export class BundlerContext { const initialFiles = new Map(); for (const outputFile of result.outputFiles) { // Entries in the metafile are relative to the `absWorkingDir` option which is set to the workspaceRoot - const relativeFilePath = relative(this.workspaceRoot, outputFile.path); + // `outputFile.path` may already be relative if the `OutputFile` instance was cached in `additionalResults` + // across an incremental rebuild. + const relativeFilePath = isAbsolute(outputFile.path) + ? relative(this.workspaceRoot, outputFile.path) + : outputFile.path; const entryPoint = result.metafile.outputs[relativeFilePath]?.entryPoint; outputFile.path = relativeFilePath; diff --git a/packages/angular/build/src/tools/esbuild/persistent-load-result-cache.ts b/packages/angular/build/src/tools/esbuild/persistent-load-result-cache.ts index c5dcee90ccf9..7eaf11806f1d 100644 --- a/packages/angular/build/src/tools/esbuild/persistent-load-result-cache.ts +++ b/packages/angular/build/src/tools/esbuild/persistent-load-result-cache.ts @@ -32,7 +32,7 @@ import { readFile, stat } from 'node:fs/promises'; import { isAbsolute } from 'node:path'; import { fileURLToPath } from 'node:url'; import type { Cache as PersistentCacheStore } from '../../utils/cache'; -import { mapConcurrent, runConcurrent } from '../../utils/concurrency'; +import { MAX_CONCURRENT_READS, mapConcurrent, runConcurrent } from '../../utils/concurrency'; import { calculateHash, createContentHash } from '../../utils/hash'; import { LoadResultCache, MemoryLoadResultCache } from './load-result-cache'; @@ -112,9 +112,6 @@ export function extractDiskFilePath(path: string): string | undefined { return isAbsolute(path) ? path : undefined; } -/** Maximum number of concurrent file system read/stat operations to prevent OS file descriptor exhaustion. */ -const MAX_CONCURRENT_READS = 16; - /** * Validates that all imported watch files exist on disk and their contents match. * Performs a fast-path metadata check (mtime + size) first, falling back to content hashing. diff --git a/packages/angular/build/src/tools/esbuild/stylesheets/sass-language_spec.ts b/packages/angular/build/src/tools/esbuild/stylesheets/sass-language_spec.ts index cdd4d7e7c8db..216a2d18fc47 100644 --- a/packages/angular/build/src/tools/esbuild/stylesheets/sass-language_spec.ts +++ b/packages/angular/build/src/tools/esbuild/stylesheets/sass-language_spec.ts @@ -286,5 +286,69 @@ describe('sass-language', () => { pathToFileURL(join(projectRoot, 'node_modules', 'theme', '_index.scss')).href, ); }); + + it('should resolve imports when globalThis.Promise is replaced (e.g., by Zone.js)', async () => { + const originalPromise = globalThis.Promise; + + // Simulate Zone.js replacing globalThis.Promise with a constructor where + // native V8 async function return values fail `instanceof globalThis.Promise`. + class ZoneLikePromise { + #inner: Promise; + + constructor( + executor: ( + resolve: (value: T | PromiseLike) => void, + reject: (reason?: unknown) => void, + ) => void, + ) { + this.#inner = new originalPromise(executor); + } + + then( + onfulfilled?: ((value: T) => TResult1 | PromiseLike) | null, + onrejected?: ((reason: unknown) => TResult2 | PromiseLike) | null, + ): ZoneLikePromise { + return new ZoneLikePromise((resolve, reject) => { + this.#inner.then(onfulfilled, onrejected).then(resolve, reject); + }); + } + + catch( + onrejected?: ((reason: unknown) => TResult | PromiseLike) | null, + ): ZoneLikePromise { + return this.then(undefined, onrejected); + } + + finally(onfinally?: (() => void) | null): ZoneLikePromise { + return new ZoneLikePromise((resolve, reject) => { + this.#inner.finally(onfinally).then(resolve, reject); + }); + } + + static resolve(value?: U | PromiseLike): ZoneLikePromise> { + if (value instanceof ZoneLikePromise) { + return value; + } + + return new ZoneLikePromise((resolve) => resolve(value as Awaited)); + } + + static reject(reason?: unknown): ZoneLikePromise { + return new ZoneLikePromise((_, reject) => reject(reason)); + } + + static all(values: Iterable): ZoneLikePromise { + return ZoneLikePromise.resolve(originalPromise.all(values)); + } + } + + globalThis.Promise = ZoneLikePromise as unknown as PromiseConstructor; + try { + const application = await compile(buttonStylesheet); + expect(application).toContain('content: "project";'); + } finally { + globalThis.Promise = originalPromise; + } + }); }); }); diff --git a/packages/angular/build/src/tools/esbuild/utils.ts b/packages/angular/build/src/tools/esbuild/utils.ts index 3bbaabdd2400..e93bb295b439 100644 --- a/packages/angular/build/src/tools/esbuild/utils.ts +++ b/packages/angular/build/src/tools/esbuild/utils.ts @@ -17,11 +17,7 @@ import { OutputMode } from '../../builders/application/schema'; import { BudgetCalculatorResult } from '../../utils/bundle-calculator'; import { BundleStats, generateEsbuildBuildStatsTable } from '../../utils/stats-table'; -import { - BuildOutputAsset, - ExecutionResult, - PrerenderedRoutesRecord, -} from './bundler-execution-result'; +import { ExecutionResult, PrerenderedRoutesRecord } from './bundler-execution-result'; import { type BuildOutputFile, BuildOutputFileType, type InitialFileRecord } from './bundler-files'; export function logBuildStats( @@ -212,24 +208,6 @@ export function getFeatureSupport(nativeAsyncAwait: boolean): BuildOptions['supp }; } -const MAX_CONCURRENT_WRITES = 64; -export async function emitFilesToDisk( - files: readonly T[], - writeFileCallback: (file: T) => Promise, -): Promise { - // Write files in groups of MAX_CONCURRENT_WRITES to avoid too many open files - for (let fileIndex = 0; fileIndex < files.length;) { - const groupMax = Math.min(fileIndex + MAX_CONCURRENT_WRITES, files.length); - - const actions = []; - while (fileIndex < groupMax) { - actions.push(writeFileCallback(files[fileIndex++])); - } - - await Promise.all(actions); - } -} - interface BuildManifest { errors: string[]; warnings: string[]; diff --git a/packages/angular/build/src/tools/sass/rebasing-importer.ts b/packages/angular/build/src/tools/sass/rebasing-importer.ts index 7198d8b74f1e..889d26ef567e 100644 --- a/packages/angular/build/src/tools/sass/rebasing-importer.ts +++ b/packages/angular/build/src/tools/sass/rebasing-importer.ts @@ -344,14 +344,18 @@ export class AsyncModuleUrlRebasingImporter implements Importer<'async'> { this.load = this.load.bind(this); } - async canonicalize(url: string, options: CanonicalizeContext): Promise { + canonicalize(url: string, options: CanonicalizeContext): Promise | URL | null { if (url.startsWith('file://')) { return this.relativeImporter.canonicalize(url, options); } - const result = await this.finder(url, options); - - return result ? this.relativeImporter.canonicalize(result.href, options) : null; + // Use `Promise.resolve` instead of an `async` method so the returned promise is an + // instance of the current `globalThis.Promise` (e.g., `ZoneAwarePromise` when Zone.js + // is loaded during SSR). `sass-embedded` checks `result instanceof Promise`, which fails + // for native `async` function return values when `globalThis.Promise` has been replaced. + return Promise.resolve(this.finder(url, options)).then((result) => + result ? this.relativeImporter.canonicalize(result.href, options) : null, + ); } load(canonicalUrl: URL): ImporterResult | null { 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 3c38d000b30c..a1aa19361c5c 100644 --- a/packages/angular/build/src/tools/vite/middlewares/assets-middleware.ts +++ b/packages/angular/build/src/tools/vite/middlewares/assets-middleware.ts @@ -236,7 +236,7 @@ export function createBuildAssetsMiddleware( const outputFile = buildResultFiles.get(pathname.slice(1)); if (outputFile) { const contents = - outputFile.origin === 'memory' ? outputFile.contents : readHandler(outputFile.inputPath); + outputFile.origin === 'memory' ? outputFile.contents : readHandler(outputFile.source); const etag = `W/${calculateHash(contents)}`; if (checkAndHandleEtag(req, res, etag)) { diff --git a/packages/angular/build/src/utils/concurrency.ts b/packages/angular/build/src/utils/concurrency.ts index a38a9a57fa36..39f2b70c95df 100644 --- a/packages/angular/build/src/utils/concurrency.ts +++ b/packages/angular/build/src/utils/concurrency.ts @@ -6,6 +6,11 @@ * found in the LICENSE file at https://angular.dev/license */ +/** + * Maximum number of concurrent file system read/stat operations to prevent OS file descriptor exhaustion. + */ +export const MAX_CONCURRENT_READS = 16; + /** * Executes an asynchronous function for each item in an array concurrently up to a specified limit. * diff --git a/packages/angular/build/src/utils/delete-output-dir.ts b/packages/angular/build/src/utils/delete-output-dir.ts index 45084760793d..1f676e77b759 100644 --- a/packages/angular/build/src/utils/delete-output-dir.ts +++ b/packages/angular/build/src/utils/delete-output-dir.ts @@ -17,8 +17,9 @@ export async function deleteOutputDir( outputPath: string, emptyOnlyDirectories?: string[], ): Promise { - const resolvedOutputPath = resolve(root, outputPath); - if (resolvedOutputPath === root) { + const resolvedRoot = resolve(root); + const resolvedOutputPath = resolve(resolvedRoot, outputPath); + if (resolvedOutputPath === resolvedRoot) { throw new Error('Output path MUST not be project root directory!'); } diff --git a/packages/angular/build/src/utils/disk-files.ts b/packages/angular/build/src/utils/disk-files.ts new file mode 100644 index 000000000000..dbbe34ee919e --- /dev/null +++ b/packages/angular/build/src/utils/disk-files.ts @@ -0,0 +1,83 @@ +/** + * @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 { constants, cp, mkdir, rm, writeFile } from 'node:fs/promises'; +import { dirname } from 'node:path'; +import { runConcurrent } from './concurrency'; + +/** + * Maximum number of concurrent file system write/copy operations to avoid open file descriptor exhaustion. + */ +const MAX_CONCURRENT_OPS = 64; + +/** + * Represents a file that can be emitted to disk from memory or copied from an existing path on disk. + */ +export type EmittableFile = + { origin: 'memory'; contents: string | Uint8Array } | { origin: 'disk'; source: string }; + +/** + * Writes or copies a collection of files to disk with bounded concurrency, + * ensuring parent directories are created once per invocation. + * + * @param files The collection of files to emit. + * @param resolveDestinationPath Callback returning the absolute destination path for a file, or `undefined` to skip it. + */ +export async function emitFilesToDisk( + files: readonly T[], + resolveDestinationPath: (file: T) => string | undefined, +): Promise { + if (files.length === 0) { + return; + } + + const directoryCreationPromises = new Map>(); + + await runConcurrent(files, MAX_CONCURRENT_OPS, async (file) => { + const fullFilePath = resolveDestinationPath(file); + if (!fullFilePath) { + return; + } + + // Ensure output subdirectories exist + const fileBasePath = dirname(fullFilePath); + if (fileBasePath) { + let mkdirPromise = directoryCreationPromises.get(fileBasePath); + if (!mkdirPromise) { + mkdirPromise = mkdir(fileBasePath, { recursive: true }); + directoryCreationPromises.set(fileBasePath, mkdirPromise); + } + await mkdirPromise; + } + + if (file.origin === 'memory') { + // Write file contents + await writeFile(fullFilePath, file.contents); + + return; + } + + // Copy file contents + await cp(file.source, fullFilePath, { + mode: constants.COPYFILE_FICLONE, + preserveTimestamps: true, + }); + }); +} + +/** + * Deletes a collection of files from disk with bounded concurrency. + * @param files The collection of files to delete. + */ +export async function deleteFilesFromDisk(files: readonly string[]): Promise { + if (files.length === 0) { + return; + } + + await runConcurrent(files, MAX_CONCURRENT_OPS, (file) => rm(file, { force: true })); +} diff --git a/packages/angular/build/src/utils/disk-files_spec.ts b/packages/angular/build/src/utils/disk-files_spec.ts new file mode 100644 index 000000000000..4a9422bd4373 --- /dev/null +++ b/packages/angular/build/src/utils/disk-files_spec.ts @@ -0,0 +1,75 @@ +/** + * @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 assert from 'node:assert'; +import { existsSync } from 'node:fs'; +import { mkdtemp, readFile, rm, writeFile } from 'node:fs/promises'; +import { join } from 'node:path'; +import { type EmittableFile, deleteFilesFromDisk, emitFilesToDisk } from './disk-files'; + +describe('disk-files', () => { + let tempRoot: string; + + beforeEach(async () => { + const baseTmpDir = process.env['TEST_TMPDIR']; + assert(baseTmpDir, 'TEST_TMPDIR is not set'); + tempRoot = await mkdtemp(join(baseTmpDir, 'angular-cli-disk-files-test-')); + }); + + afterEach(async () => { + if (tempRoot) { + await rm(tempRoot, { recursive: true, force: true }); + } + }); + + describe('emitFilesToDisk', () => { + it('should write memory files and copy disk files while creating nested directories', async () => { + const sourceFile = join(tempRoot, 'source-asset.txt'); + await writeFile(sourceFile, 'from-disk'); + + const files: (EmittableFile & { path: string })[] = [ + { + origin: 'memory', + path: 'nested/a/mem1.txt', + contents: new TextEncoder().encode('mem-1'), + }, + { origin: 'memory', path: 'nested/a/mem2.txt', contents: 'mem-2' }, + { origin: 'disk', path: 'nested/b/disk1.txt', source: sourceFile }, + { origin: 'disk', path: 'nested/c/disk2.txt', source: sourceFile }, + { origin: 'memory', path: 'skipped/file.txt', contents: 'should-not-exist' }, + ]; + + const outDir = join(tempRoot, 'out'); + await emitFilesToDisk(files, (file) => + file.path.startsWith('skipped/') ? undefined : join(outDir, file.path), + ); + + expect(await readFile(join(outDir, 'nested/a/mem1.txt'), 'utf-8')).toBe('mem-1'); + expect(await readFile(join(outDir, 'nested/a/mem2.txt'), 'utf-8')).toBe('mem-2'); + expect(await readFile(join(outDir, 'nested/b/disk1.txt'), 'utf-8')).toBe('from-disk'); + expect(await readFile(join(outDir, 'nested/c/disk2.txt'), 'utf-8')).toBe('from-disk'); + expect(existsSync(join(outDir, 'skipped/file.txt'))).toBeFalse(); + }); + }); + + describe('deleteFilesFromDisk', () => { + it('should delete existing files and ignore non-existent files', async () => { + const fileA = join(tempRoot, 'a.txt'); + const fileB = join(tempRoot, 'b.txt'); + const nonExistent = join(tempRoot, 'missing.txt'); + + await writeFile(fileA, 'a'); + await writeFile(fileB, 'b'); + + await deleteFilesFromDisk([fileA, nonExistent]); + + expect(existsSync(fileA)).toBeFalse(); + expect(existsSync(fileB)).toBeTrue(); + }); + }); +}); diff --git a/packages/angular/build/src/utils/normalize-asset-patterns.ts b/packages/angular/build/src/utils/normalize-asset-patterns.ts index d97b6e605fa4..defa6511172f 100644 --- a/packages/angular/build/src/utils/normalize-asset-patterns.ts +++ b/packages/angular/build/src/utils/normalize-asset-patterns.ts @@ -6,32 +6,52 @@ * found in the LICENSE file at https://angular.dev/license */ -import assert from 'node:assert'; import { statSync } from 'node:fs'; -import * as path from 'node:path'; -import { AssetPattern, AssetPatternClass } from '../builders/application/schema'; +import path from 'node:path'; +import type { AssetPattern, AssetPatternClass } from '../builders/application/schema'; import { isSubDirectory } from './path'; +/** + * Asset pattern configuration normalized into its object form with a resolved `output` directory. + */ +export interface NormalizedAssetsPattern extends AssetPatternClass { + /** Relative output directory within the build output path. */ + output: string; +} + +/** + * Normalizes raw string and object asset patterns into {@link NormalizedAssetsPattern} objects + * and validates that all asset inputs and outputs stay within the workspace and output directories. + * + * @param assetPatterns Raw string or object asset patterns from builder options. + * @param workspaceRoot Workspace root directory path. + * @param projectRoot Project root directory path. + * @param projectSourceRoot Optional project source root directory path. + * @returns An array of normalized asset pattern configurations. + */ export function normalizeAssetPatterns( assetPatterns: AssetPattern[], workspaceRoot: string, projectRoot: string, projectSourceRoot: string | undefined, -): (AssetPatternClass & { output: string })[] { +): NormalizedAssetsPattern[] { if (assetPatterns.length === 0) { return []; } // When sourceRoot is not available, we default to ${projectRoot}/src. const sourceRoot = projectSourceRoot || path.join(projectRoot, 'src'); - const resolvedSourceRoot = path.resolve(workspaceRoot, sourceRoot); - const resolvedProjectRoot = path.resolve(workspaceRoot, projectRoot); + const resolvedWorkspaceRoot = path.resolve(workspaceRoot); + const resolvedSourceRoot = path.resolve(resolvedWorkspaceRoot, sourceRoot); + const resolvedProjectRoot = path.resolve(resolvedWorkspaceRoot, projectRoot); return assetPatterns.map((assetPattern) => { + let normalized: NormalizedAssetsPattern; + // Normalize string asset patterns to objects. if (typeof assetPattern === 'string') { const assetPath = path.normalize(assetPattern); - const resolvedAssetPath = path.resolve(workspaceRoot, assetPath); + const resolvedAssetPath = path.resolve(resolvedWorkspaceRoot, assetPath); let root: string; // Check if the string asset is within sourceRoot. @@ -39,51 +59,38 @@ export function normalizeAssetPatterns( root = resolvedSourceRoot; } else if (resolvedAssetPath.startsWith(resolvedProjectRoot)) { root = resolvedProjectRoot; - } else if (resolvedAssetPath.startsWith(workspaceRoot)) { - root = workspaceRoot; + } else if (resolvedAssetPath.startsWith(resolvedWorkspaceRoot)) { + root = resolvedWorkspaceRoot; } else { throw new Error(`The ${assetPattern} asset path must be within the workspace root.`); } - let glob: string, input: string; - let isDirectory: boolean; - + let isDirectory = true; try { - isDirectory = statSync(resolvedAssetPath).isDirectory(); - } catch { - isDirectory = true; - } - - if (isDirectory) { - // Folders get a recursive star glob. - glob = '**/*'; - // Input directory is their original path. - input = assetPath; - } else { - // Files are their own glob. - glob = path.basename(assetPath); - // Input directory is their original dirname. - input = path.dirname(assetPath); - } + isDirectory = statSync(resolvedAssetPath, { throwIfNoEntry: false })?.isDirectory() ?? true; + } catch {} + const glob = isDirectory ? '**/*' : path.basename(assetPath); + const input = isDirectory ? assetPath : path.dirname(assetPath); // Output directory for both is the relative path from the root to input. - const output = path.relative(root, path.resolve(workspaceRoot, input)); + const output = path.relative(root, path.resolve(resolvedWorkspaceRoot, input)); - assetPattern = { glob, input, output }; + normalized = { glob, input, output }; } else { - if (!isSubDirectory(workspaceRoot, assetPattern.input)) { + if (!isSubDirectory(resolvedWorkspaceRoot, assetPattern.input)) { throw new Error(`The ${assetPattern.input} asset path must be within the workspace root.`); } - assetPattern.output = path.join('.', assetPattern.output ?? ''); + normalized = { + ...assetPattern, + output: path.join('.', assetPattern.output ?? ''), + }; } - assert(assetPattern.output !== undefined); - - if (assetPattern.output.startsWith('..')) { + if (normalized.output.startsWith('..')) { throw new Error('An asset cannot be written to a location outside of the output path.'); } - return assetPattern as AssetPatternClass & { output: string }; + return normalized; }); } diff --git a/packages/angular/build/src/utils/normalize-cache.ts b/packages/angular/build/src/utils/normalize-cache.ts index e24eaf8627c8..bb24f774f79b 100644 --- a/packages/angular/build/src/utils/normalize-cache.ts +++ b/packages/angular/build/src/utils/normalize-cache.ts @@ -140,7 +140,7 @@ export function normalizeCacheOptions( const cacheBasePath = getCacheBasePath(workspaceRoot, path); const localCacheBasePath = isAbsolute(path) ? path : resolve(workspaceRoot, path); - const normalizedBuilderName = builderName.replaceAll(':', '-'); + const normalizedBuilderName = builderName.replace('@', '').replaceAll(/[:/]/g, '-'); return { enabled: cacheEnabled, diff --git a/packages/angular/build/src/utils/normalize-cache_spec.ts b/packages/angular/build/src/utils/normalize-cache_spec.ts index 18e75403756d..1a0520c96a96 100644 --- a/packages/angular/build/src/utils/normalize-cache_spec.ts +++ b/packages/angular/build/src/utils/normalize-cache_spec.ts @@ -283,10 +283,10 @@ describe('normalizeCacheOptions', () => { const options = normalizeCacheOptions({}, workspaceRoot, 'my-app', '@angular/build:unit-test'); expect(options.path).toBe( - resolve(workspaceRoot, '.angular/cache/0.0.0-PLACEHOLDER/my-app/@angular/build-unit-test'), + resolve(workspaceRoot, '.angular/cache/0.0.0-PLACEHOLDER/my-app/angular-build-unit-test'), ); expect(options.localPath).toBe( - resolve(workspaceRoot, '.angular/cache/0.0.0-PLACEHOLDER/my-app/@angular/build-unit-test'), + resolve(workspaceRoot, '.angular/cache/0.0.0-PLACEHOLDER/my-app/angular-build-unit-test'), ); }); }); diff --git a/packages/angular/build/src/utils/postcss-configuration.ts b/packages/angular/build/src/utils/postcss-configuration.ts index abde0e632b30..e5a4076232a0 100644 --- a/packages/angular/build/src/utils/postcss-configuration.ts +++ b/packages/angular/build/src/utils/postcss-configuration.ts @@ -31,8 +31,8 @@ export interface SearchDirectory { files: Set; } -export async function generateSearchDirectories(roots: string[]): Promise { - return await Promise.all( +export function generateSearchDirectories(roots: string[]): Promise { + return Promise.all( roots.map((root) => readdir(root, { withFileTypes: true }).then((entries) => ({ root, @@ -63,11 +63,11 @@ export function findTailwindConfiguration( return findFile(searchDirectories, tailwindConfigFiles); } -export async function getTailwindConfig( +export function getTailwindConfig( searchDirectories: SearchDirectory[], workspaceRoot: string, logger?: { warn(message: string): void }, -): Promise<{ file: string; package: string } | undefined> { +): { file: string; package: string } | undefined { const tailwindConfigurationPath = findTailwindConfiguration(searchDirectories); if (!tailwindConfigurationPath) { return undefined; diff --git a/packages/angular/build/src/utils/resolve-assets.ts b/packages/angular/build/src/utils/resolve-assets.ts index 79ddd8548266..06a6c6c157e9 100644 --- a/packages/angular/build/src/utils/resolve-assets.ts +++ b/packages/angular/build/src/utils/resolve-assets.ts @@ -6,8 +6,10 @@ * found in the LICENSE file at https://angular.dev/license */ +import { lstat, stat } from 'node:fs/promises'; import path from 'node:path'; -import { glob } from 'tinyglobby'; +import { glob, isDynamicPattern } from 'tinyglobby'; +import { MAX_CONCURRENT_READS, mapConcurrent } from './concurrency'; import { isSubDirectory } from './path'; /** @@ -26,15 +28,34 @@ export async function resolveAssets( }[], root: string, ): Promise<{ source: string; destination: string }[]> { - const outputFiles: { source: string; destination: string }[] = []; - - for (const entry of entries) { + const resolvedEntries = await mapConcurrent(entries, MAX_CONCURRENT_READS, async (entry) => { if (!isSubDirectory(root, entry.input)) { throw new Error(`The ${entry.input} asset path must be within the workspace root.`); } const cwd = path.resolve(root, entry.input); + if (!entry.ignore?.length && !isDynamicPattern(entry.glob)) { + const src = path.join(cwd, entry.glob); + if (isSubDirectory(cwd, src)) { + try { + const stats = await (entry.followSymlinks ? stat(src) : lstat(src)); + if (stats.isFile()) { + const filePath = entry.flatten ? path.basename(entry.glob) : entry.glob; + + return [{ source: src, destination: path.join(entry.output, filePath) }]; + } + + if (!stats.isSymbolicLink()) { + return []; + } + } catch { + // File does not exist or cannot be accessed. + return []; + } + } + } + const files = await glob(entry.glob, { cwd, dot: true, @@ -42,13 +63,11 @@ export async function resolveAssets( followSymbolicLinks: entry.followSymlinks ?? false, }); - for (const file of files) { - const src = path.join(cwd, file); - const filePath = entry.flatten ? path.basename(file) : file; - - outputFiles.push({ source: src, destination: path.join(entry.output, filePath) }); - } - } + return files.map((file) => ({ + source: path.join(cwd, file), + destination: path.join(entry.output, entry.flatten ? path.basename(file) : file), + })); + }); - return outputFiles; + return resolvedEntries.flat(); } diff --git a/packages/angular/build/src/utils/server-rendering/esm-in-memory-loader/loader-hooks.ts b/packages/angular/build/src/utils/server-rendering/esm-in-memory-loader/loader-hooks.ts index 6f21bfbc739b..6ff4c8f8191c 100644 --- a/packages/angular/build/src/utils/server-rendering/esm-in-memory-loader/loader-hooks.ts +++ b/packages/angular/build/src/utils/server-rendering/esm-in-memory-loader/loader-hooks.ts @@ -8,7 +8,7 @@ import assert from 'node:assert'; import { randomUUID } from 'node:crypto'; -import { readFile } from 'node:fs/promises'; +import { readFileSync } from 'node:fs'; import { join } from 'node:path'; import { fileURLToPath, pathToFileURL } from 'node:url'; @@ -110,7 +110,7 @@ export function resolve( }); } -export async function load(url: string, context: { format?: string | null }, nextLoad: Function) { +export function load(url: string, context: { format?: string | null }, nextLoad: Function) { const { format } = context; // Load the file from memory if the URL is based in the virtual root @@ -137,7 +137,7 @@ export async function load(url: string, context: { format?: string | null }, nex } const filePath = fileURLToPath(url); - const fileBytes = await readFile(filePath); + const fileBytes = readFileSync(filePath); const source = Buffer.concat([NG_SERVER_MODE_INIT_BYTES, fileBytes]); return { diff --git a/packages/angular/build/src/utils/server-rendering/esm-in-memory-loader/loader-hooks_spec.ts b/packages/angular/build/src/utils/server-rendering/esm-in-memory-loader/loader-hooks_spec.ts index 2b7622145378..0b32c2248b10 100644 --- a/packages/angular/build/src/utils/server-rendering/esm-in-memory-loader/loader-hooks_spec.ts +++ b/packages/angular/build/src/utils/server-rendering/esm-in-memory-loader/loader-hooks_spec.ts @@ -6,6 +6,10 @@ * found in the LICENSE file at https://angular.dev/license */ +import assert from 'node:assert'; +import { mkdir, mkdtemp, rm, writeFile } from 'node:fs/promises'; +import { join } from 'node:path'; +import { pathToFileURL } from 'node:url'; import { initialize, load, resolve } from './loader-hooks'; import { createSharedServerFiles } from './utils'; @@ -124,12 +128,12 @@ describe('esm-in-memory-loader loader-hooks', () => { }); describe('load', () => { - it('should load in-memory file source from SharedArrayBuffer backed Uint8Array', async () => { + it('should load in-memory file source from SharedArrayBuffer backed Uint8Array', () => { const memoryUrl = new URL('./main.server.mjs', 'memory://').href; const rootResolve = resolve(memoryUrl, { parentURL: undefined }, () => {}); const nextLoad = jasmine.createSpy('nextLoad'); - const result = await load(rootResolve.url, { format: 'module' }, nextLoad); + const result = load(rootResolve.url, { format: 'module' }, nextLoad); expect(nextLoad).not.toHaveBeenCalled(); expect(result.format).toBe('module'); @@ -137,12 +141,12 @@ describe('esm-in-memory-loader loader-hooks', () => { expect(result.source).toBe('export const main = true;'); }); - it('should load in-memory file with multi-byte UTF-8 characters', async () => { + it('should load in-memory file with multi-byte UTF-8 characters', () => { const memoryUrl = new URL('./utf8.mjs', 'memory://').href; const rootResolve = resolve(memoryUrl, { parentURL: undefined }, () => {}); const nextLoad = jasmine.createSpy('nextLoad'); - const result = await load(rootResolve.url, { format: 'module' }, nextLoad); + const result = load(rootResolve.url, { format: 'module' }, nextLoad); expect(nextLoad).not.toHaveBeenCalled(); expect(result.format).toBe('module'); @@ -150,12 +154,12 @@ describe('esm-in-memory-loader loader-hooks', () => { expect(result.source).toBe('export const text = "🔥 UTF-8 🚀";'); }); - it('should load in-memory file with empty content', async () => { + it('should load in-memory file with empty content', () => { const memoryUrl = new URL('./empty.mjs', 'memory://').href; const rootResolve = resolve(memoryUrl, { parentURL: undefined }, () => {}); const nextLoad = jasmine.createSpy('nextLoad'); - const result = await load(rootResolve.url, { format: 'module' }, nextLoad); + const result = load(rootResolve.url, { format: 'module' }, nextLoad); expect(nextLoad).not.toHaveBeenCalled(); expect(result.format).toBe('module'); @@ -163,7 +167,7 @@ describe('esm-in-memory-loader loader-hooks', () => { expect(result.source).toBe(''); }); - it('should load in-memory file source with non-zero byteOffset in Uint8Array', async () => { + it('should load in-memory file source with non-zero byteOffset in Uint8Array', () => { const target = 'export const sliced = 42;'; const fullBuffer = Buffer.from(`__PADDING__${target}__MORE__`); const offset = Buffer.byteLength('__PADDING__', 'utf-8'); @@ -181,7 +185,7 @@ describe('esm-in-memory-loader loader-hooks', () => { const rootResolve = resolve(memoryUrl, { parentURL: undefined }, () => {}); const nextLoad = jasmine.createSpy('nextLoad'); - const result = await load(rootResolve.url, { format: 'module' }, nextLoad); + const result = load(rootResolve.url, { format: 'module' }, nextLoad); expect(nextLoad).not.toHaveBeenCalled(); expect(result.format).toBe('module'); @@ -189,7 +193,7 @@ describe('esm-in-memory-loader loader-hooks', () => { expect(result.source).toBe(target); }); - it('should load in-memory file source when outputFiles contain string values', async () => { + it('should load in-memory file source when outputFiles contain string values', () => { initialize({ workspaceRoot, outputFiles: { @@ -201,7 +205,7 @@ describe('esm-in-memory-loader loader-hooks', () => { const rootResolve = resolve(memoryUrl, { parentURL: undefined }, () => {}); const nextLoad = jasmine.createSpy('nextLoad'); - const result = await load(rootResolve.url, { format: 'module' }, nextLoad); + const result = load(rootResolve.url, { format: 'module' }, nextLoad); expect(nextLoad).not.toHaveBeenCalled(); expect(result.format).toBe('module'); @@ -209,21 +213,46 @@ describe('esm-in-memory-loader loader-hooks', () => { expect(result.source).toBe('export const fromString = 1;'); }); - it('should reject when in-memory file does not exist in outputFiles', async () => { + it('should throw when in-memory file does not exist in outputFiles', () => { const memoryUrl = new URL('./main.server.mjs', 'memory://').href; const rootResolve = resolve(memoryUrl, { parentURL: undefined }, () => {}); const nonExistentVirtualUrl = rootResolve.url.replace('main.server.mjs', 'missing.mjs'); const nextLoad = jasmine.createSpy('nextLoad'); - await expectAsync( - load(nonExistentVirtualUrl, { format: 'module' }, nextLoad), - ).toBeRejectedWithError(/Resolved in-memory ESM file should always exist/); + expect(() => load(nonExistentVirtualUrl, { format: 'module' }, nextLoad)).toThrowError( + /Resolved in-memory ESM file should always exist/, + ); expect(nextLoad).not.toHaveBeenCalled(); }); - it('should delegate to nextLoad for non-angular file URLs', async () => { - const nextLoad = jasmine.createSpy('nextLoad').and.resolveTo({ format: 'module' }); - const result = await load( + it('should prepend ngServerMode initialization for @angular/ file URLs', async () => { + const baseTmpDir = process.env['TEST_TMPDIR']; + assert(baseTmpDir, 'TEST_TMPDIR is not set'); + const tmpDir = await mkdtemp(join(baseTmpDir, 'loader-hooks-test-')); + + try { + const angularDir = join(tmpDir, '@angular/core'); + await mkdir(angularDir, { recursive: true }); + const filePath = join(angularDir, 'index.mjs'); + await writeFile(filePath, 'export const VERSION = "1.0.0";'); + + const nextLoad = jasmine.createSpy('nextLoad'); + const result = load(pathToFileURL(filePath).href, { format: 'module' }, nextLoad); + + expect(nextLoad).not.toHaveBeenCalled(); + expect(result.format).toBe('module'); + expect(result.shortCircuit).toBeTrue(); + expect(new TextDecoder().decode(result.source)).toBe( + 'var ngServerMode=true;export const VERSION = "1.0.0";', + ); + } finally { + await rm(tmpDir, { recursive: true, force: true }); + } + }); + + it('should delegate to nextLoad for non-angular file URLs', () => { + const nextLoad = jasmine.createSpy('nextLoad').and.returnValue({ format: 'module' }); + const result = load( 'file:///workspace/node_modules/rxjs/index.js', { format: 'module' }, nextLoad, @@ -235,9 +264,9 @@ describe('esm-in-memory-loader loader-hooks', () => { expect(result.format).toBe('module'); }); - it('should delegate to nextLoad for non-memory non-file URLs', async () => { - const nextLoad = jasmine.createSpy('nextLoad').and.resolveTo({ format: 'builtin' }); - const result = await load('node:fs', { format: 'builtin' }, nextLoad); + it('should delegate to nextLoad for non-memory non-file URLs', () => { + const nextLoad = jasmine.createSpy('nextLoad').and.returnValue({ format: 'builtin' }); + const result = load('node:fs', { format: 'builtin' }, nextLoad); expect(nextLoad).toHaveBeenCalledWith('node:fs'); expect(result.format).toBe('builtin'); diff --git a/packages/angular/build/src/utils/server-rendering/esm-in-memory-loader/register-hooks.ts b/packages/angular/build/src/utils/server-rendering/esm-in-memory-loader/register-hooks.ts index b23fe297bc19..56747e17bd03 100644 --- a/packages/angular/build/src/utils/server-rendering/esm-in-memory-loader/register-hooks.ts +++ b/packages/angular/build/src/utils/server-rendering/esm-in-memory-loader/register-hooks.ts @@ -6,8 +6,9 @@ * found in the LICENSE file at https://angular.dev/license */ -import { register } from 'node:module'; -import { pathToFileURL } from 'node:url'; +import { registerHooks } from 'node:module'; import { workerData } from 'node:worker_threads'; +import { initialize, load, resolve } from './loader-hooks'; -register('./loader-hooks.js', { parentURL: pathToFileURL(__filename), data: workerData }); +initialize(workerData); +registerHooks({ resolve, load }); diff --git a/packages/angular/build/src/utils/test-files.ts b/packages/angular/build/src/utils/test-files.ts index 6790d007e220..763c762ac19f 100644 --- a/packages/angular/build/src/utils/test-files.ts +++ b/packages/angular/build/src/utils/test-files.ts @@ -6,11 +6,10 @@ * found in the LICENSE file at https://angular.dev/license */ -import * as fs from 'node:fs/promises'; import path from 'node:path'; import { ResultFile } from '../builders/application/results'; import { BuildOutputFileType } from '../tools/esbuild/bundler-files'; -import { emitFilesToDisk } from '../tools/esbuild/utils'; +import { emitFilesToDisk } from './disk-files'; /** * Writes a collection of build result files to a specified directory. @@ -20,29 +19,11 @@ import { emitFilesToDisk } from '../tools/esbuild/utils'; * @param files A collection of `ResultFile` objects, representing the build output. * @param testDir The absolute path to the directory where the files should be written. */ -export async function writeTestFiles(files: readonly ResultFile[], testDir: string): Promise { - const directoryExists = new Set(); +export function writeTestFiles(files: readonly ResultFile[], testDir: string): Promise { // Writes the test related output files to disk and ensures the containing directories are present - await emitFilesToDisk(files, async (file) => { - if (file.type !== BuildOutputFileType.Browser && file.type !== BuildOutputFileType.Media) { - return; - } - - const fullFilePath = path.join(testDir, file.path); - - // Ensure output subdirectories exist - const fileBasePath = path.dirname(fullFilePath); - if (fileBasePath && !directoryExists.has(fileBasePath)) { - await fs.mkdir(fileBasePath, { recursive: true }); - directoryExists.add(fileBasePath); - } - - if (file.origin === 'memory') { - // Write file contents - await fs.writeFile(fullFilePath, file.contents); - } else { - // Copy file contents - await fs.copyFile(file.inputPath, fullFilePath, fs.constants.COPYFILE_FICLONE); - } - }); + return emitFilesToDisk(files, (file) => + file.type === BuildOutputFileType.Browser || file.type === BuildOutputFileType.Media + ? path.join(testDir, file.path) + : undefined, + ); } diff --git a/packages/angular/build/src/utils/watcher.ts b/packages/angular/build/src/utils/watcher.ts index 610051c5dcc7..fd865ee80b56 100644 --- a/packages/angular/build/src/utils/watcher.ts +++ b/packages/angular/build/src/utils/watcher.ts @@ -8,7 +8,6 @@ import type * as ParcelWatcher from '@parcel/watcher'; import type * as Chokidar from 'chokidar'; -import { once } from 'node:events'; import * as fs from 'node:fs'; import * as path from 'node:path'; import picomatch from 'picomatch'; @@ -419,89 +418,364 @@ export function extractNodeModulesPackageDir(posixPath: string): string | undefi return `${nodeModulesPath}/${segments[0]}`; } -class ParcelExternalManager { - private readonly extraSubscriptions = new Map(); - private readonly pendingSubscriptions = new Map< - string, - Promise - >(); - private readonly externalDirFiles = new Map }>(); +/** + * Maximum number of concurrent external directory subscriptions before coalescing + * sibling/cousin directories into common ancestor directories. + * Kept well below macOS's ~512 per-process FSEvents stream limit. + */ +const DEFAULT_MAX_EXTERNAL_SUBSCRIPTIONS = 256; + +/** + * Node in a directory prefix tree used to subsume nested directories + * and coalesce sibling/cousin directories into common ancestors. + */ +interface DirTrieNode { + /** Case-normalized POSIX path key for set/map lookups. */ + dirKey: string; + + /** Original-case POSIX directory path passed to `@parcel/watcher`. */ + dirPath: string; + + /** Depth of this directory node from the filesystem root (0 at root). */ + depth: number; + + /** Parent directory node, or `undefined` if this node is a filesystem root. */ + parent: DirTrieNode | undefined; + + /** Child directory nodes keyed by their `dirKey`. */ + children: Map; + + /** Whether this directory is selected as an active subscription target. */ + isTarget: boolean; + + /** Number of active target leaf directories within this node's subtree. */ + leafCount: number; +} + +/** + * Checks whether any strict ancestor of the given trie node is already marked as a target. + */ +function hasTargetAncestor(node: DirTrieNode): boolean { + let current = node.parent; + while (current) { + if (current.isTarget) { + return true; + } + current = current.parent; + } + + return false; +} + +/** + * Manages `@parcel/watcher` directory subscriptions for watched files located outside the + * primary `workspaceRoot`. + * + * Because `@parcel/watcher` watches directories recursively and macOS limits each process to + * ~512 concurrent `FSEvents` streams, this manager: + * - Normalizes external `node_modules` file paths to their package root directory. + * - Batches synchronous `watchFile` and `removeFile` calls within a microtask before subscribing. + * - Uses a directory prefix tree to subsume nested child directories and iteratively coalesce + * deepest branching subdirectories into common ancestors when the subscription budget is exceeded. + * - Dynamically lowers its subscription budget and coalesces further if `subscribe()` fails on + * an existing directory due to OS stream limits. + */ +export class ParcelExternalManager { + /** Active `@parcel/watcher` subscriptions keyed by subscribed directory lookup key. */ + readonly #extraSubscriptions = new Map(); + + /** Reference-counted watched files grouped by their target external directory lookup key. */ + readonly #externalDirFiles = new Map }>(); + + /** Reverse lookup mapping each watched file lookup key to its target directory lookup key. */ + readonly #fileToDirKey = new Map(); + + /** The `@parcel/watcher` module instance. */ + readonly #parcelWatcher: typeof ParcelWatcher; + + /** Watcher configuration options including ignore patterns. */ + readonly #options: WatcherOptions | undefined; + + /** Normalized POSIX path of the primary watched workspace root. */ + readonly #rootDirPosix: string; + /** Case-normalized lookup key of the primary watched workspace root. */ + readonly #rootDirLookupKey: string; + + /** Whether the underlying filesystem is case-sensitive. */ + readonly #isCaseSensitive: boolean; + + /** Callback invoked when `@parcel/watcher` emits filesystem events. */ + readonly #handleEvents: (events: ParcelWatcher.Event[]) => void; + + /** Current maximum number of concurrent external directory subscriptions allowed before coalescing. */ + #maxSubscriptions: number; + + /** Whether external directory state has changed and requires a reconciliation pass. */ + #needsReconcile = false; + + /** Whether obsolete subscriptions should be unsubscribed before creating new ones (used after stream limit errors). */ + #shrinkBeforeSubscribe = false; + + /** In-flight promise for the currently scheduled or running reconciliation loop. */ + #reconcilePromise: Promise | undefined; + + /** Whether this manager has been closed. */ + #isClosed = false; + + /** + * Creates a new external directory subscription manager for `@parcel/watcher`. + * + * @param parcelWatcher The `@parcel/watcher` module. + * @param options Watcher configuration options. + * @param rootDirPosix Normalized POSIX path of the primary workspace root. + * @param rootDirLookupKey Case-normalized lookup key of the primary workspace root. + * @param isCaseSensitive Whether the filesystem at the workspace root is case-sensitive. + * @param handleEvents Callback to process incoming `@parcel/watcher` events. + * @param maxSubscriptions Maximum number of concurrent external directory subscriptions before coalescing. + */ constructor( - private readonly parcelWatcher: typeof ParcelWatcher, - private readonly options: WatcherOptions | undefined, - private readonly rootDirLookupKey: string, - private readonly handleEvents: (events: ParcelWatcher.Event[]) => void, - ) {} + parcelWatcher: typeof ParcelWatcher, + options: WatcherOptions | undefined, + rootDirPosix: string, + rootDirLookupKey: string, + isCaseSensitive: boolean, + handleEvents: (events: ParcelWatcher.Event[]) => void, + maxSubscriptions = DEFAULT_MAX_EXTERNAL_SUBSCRIPTIONS, + ) { + this.#parcelWatcher = parcelWatcher; + this.#options = options; + this.#rootDirPosix = rootDirPosix; + this.#rootDirLookupKey = rootDirLookupKey; + this.#isCaseSensitive = isCaseSensitive; + this.#handleEvents = handleEvents; + this.#maxSubscriptions = Math.max(1, maxSubscriptions); + } - async ensureWatched(posixPath: string, lookupKey: string): Promise { - if (isPathInside(lookupKey, this.rootDirLookupKey) || lookupKey === this.rootDirLookupKey) { + /** + * Registers a file path to be watched if it resides outside the primary workspace root, + * and schedules an asynchronous subscription reconciliation pass if needed. + * + * @param posixPath Normalized POSIX file path preserving original casing. + * @param lookupKey Case-normalized lookup key for the file path. + */ + watchFile(posixPath: string, lookupKey: string): void { + if ( + this.#isClosed || + isPathInside(lookupKey, this.#rootDirLookupKey) || + lookupKey === this.#rootDirLookupKey + ) { return; } - const dirPath = getDirectoryPath(posixPath); - const dirKey = getDirectoryPath(lookupKey); - let dirEntry = this.externalDirFiles.get(dirKey); + const pkgDir = extractNodeModulesPackageDir(posixPath); + const dirPath = pkgDir ?? getDirectoryPath(posixPath); + const dirKey = pkgDir + ? toLookupKey(pkgDir, this.#isCaseSensitive) + : getDirectoryPath(lookupKey); + + this.#fileToDirKey.set(lookupKey, dirKey); + + let dirEntry = this.#externalDirFiles.get(dirKey); + const isNewDir = !dirEntry; if (!dirEntry) { dirEntry = { dirPath, files: new Set() }; - this.externalDirFiles.set(dirKey, dirEntry); + this.#externalDirFiles.set(dirKey, dirEntry); } dirEntry.files.add(lookupKey); - await this.ensureDirWatched(dirPath, dirKey); + if (isNewDir || !this.#isCoveredByActiveSubscription(dirKey)) { + this.#scheduleReconcile(); + } } + /** + * Unregisters a previously watched external file and schedules a reconciliation pass + * if it was the last watched file in its target directory. + * + * @param lookupKey Case-normalized lookup key for the file path to remove. + */ removeFile(lookupKey: string): void { - if (isPathInside(lookupKey, this.rootDirLookupKey) || lookupKey === this.rootDirLookupKey) { + const dirKey = this.#fileToDirKey.get(lookupKey); + if (!dirKey) { return; } - const dirKey = getDirectoryPath(lookupKey); - const dirEntry = this.externalDirFiles.get(dirKey); + this.#fileToDirKey.delete(lookupKey); + const dirEntry = this.#externalDirFiles.get(dirKey); if (dirEntry) { dirEntry.files.delete(lookupKey); if (dirEntry.files.size === 0) { - this.externalDirFiles.delete(dirKey); - const sub = this.extraSubscriptions.get(dirKey); - if (sub) { - this.extraSubscriptions.delete(dirKey); - sub.unsubscribe().catch(() => {}); - - for (const [remainingDirKey, remainingDirEntry] of this.externalDirFiles.entries()) { - if (!this.isCoveredByExistingExternal(remainingDirKey)) { - this.ensureDirWatched(remainingDirEntry.dirPath, remainingDirKey).catch(() => {}); - } - } - } + this.#externalDirFiles.delete(dirKey); + this.#scheduleReconcile(); } } } + /** + * Waits for any scheduled or in-progress subscription reconciliation pass to complete. + */ + async flush(): Promise { + if (this.#reconcilePromise) { + await this.#reconcilePromise; + } + } + + /** + * Closes the manager, awaiting any pending reconciliation and unsubscribing all active + * external directory subscriptions. + */ async close(): Promise { + this.#isClosed = true; + this.#needsReconcile = false; try { - if (this.pendingSubscriptions.size > 0) { - await Promise.allSettled(Array.from(this.pendingSubscriptions.values())); + if (this.#reconcilePromise) { + await this.#reconcilePromise.catch(() => {}); } - if (this.extraSubscriptions.size > 0) { + if (this.#extraSubscriptions.size > 0) { await Promise.allSettled( - Array.from(this.extraSubscriptions.values()).map((sub) => sub.unsubscribe()), + Array.from(this.#extraSubscriptions.values(), (sub) => sub.unsubscribe()), ); } } finally { - this.extraSubscriptions.clear(); - this.pendingSubscriptions.clear(); - this.externalDirFiles.clear(); + this.#extraSubscriptions.clear(); + this.#externalDirFiles.clear(); + this.#fileToDirKey.clear(); } } - private isCoveredByExistingExternal(dirLookupKey: string): boolean { - for (const existingDir of this.extraSubscriptions.keys()) { - if (dirLookupKey === existingDir || isPathInside(dirLookupKey, existingDir)) { - return true; + /** + * Schedules a microtask to reconcile active `@parcel/watcher` subscriptions with the + * current set of external directories. + */ + #scheduleReconcile(): void { + if (this.#isClosed) { + return; + } + + this.#needsReconcile = true; + this.#reconcilePromise ??= Promise.resolve() + .then(() => this.#runReconcileLoop()) + .finally(() => { + this.#reconcilePromise = undefined; + }); + } + + /** + * Continuously executes reconciliation passes until no further changes are pending. + */ + async #runReconcileLoop(): Promise { + while (this.#needsReconcile && !this.#isClosed) { + this.#needsReconcile = false; + await this.#reconcileOnce(); + } + } + + /** + * Performs a single reconciliation pass: computes the desired coalesced directory set, + * subscribes to newly required directories, unsubscribes obsolete directories, and + * triggers fallback coalescing if OS stream limits are encountered. + */ + async #reconcileOnce(): Promise { + const desiredDirs = this.#computeDesiredDirectories(); + + if (this.#shrinkBeforeSubscribe) { + this.#shrinkBeforeSubscribe = false; + const toUnsubscribe: Promise[] = []; + for (const [existingKey, sub] of this.#extraSubscriptions.entries()) { + if (!desiredDirs.has(existingKey)) { + this.#extraSubscriptions.delete(existingKey); + toUnsubscribe.push(sub.unsubscribe().catch(() => {})); + } + } + if (toUnsubscribe.length > 0) { + await Promise.allSettled(toUnsubscribe); + } + } + + if (this.#isClosed) { + return; + } + + const toSubscribe: [string, string][] = []; + for (const [dirKey, dirPath] of desiredDirs.entries()) { + if (!this.#extraSubscriptions.has(dirKey)) { + toSubscribe.push([dirKey, dirPath]); + } + } + + let hadExistingDirFailure = false; + if (toSubscribe.length > 0) { + await Promise.allSettled( + toSubscribe.map(async ([dirKey, dirPath]) => { + const ignore = isPathInside(this.#rootDirLookupKey, dirKey) + ? [...(this.#options?.ignored ?? []), this.#rootDirPosix, `${this.#rootDirPosix}/**`] + : this.#options?.ignored; + + try { + const sub = await this.#parcelWatcher.subscribe( + dirPath, + (err, events) => { + if (!err) { + this.#handleEvents(events); + } + }, + { ignore }, + ); + + if (this.#isClosed) { + await sub.unsubscribe().catch(() => {}); + + return; + } + + this.#extraSubscriptions.set(dirKey, sub); + } catch { + // Ignore errors for missing directories; if the directory exists on disk, + // mark failure so the manager can coalesce further if over OS stream limits. + if (!this.#isClosed && fs.existsSync(dirPath)) { + hadExistingDirFailure = true; + } + } + }), + ); + } + + if (this.#isClosed) { + return; + } + + // Unsubscribe any previous subscriptions that are no longer desired and whose + // remaining watched files (if any) are now covered by another active subscription. + for (const [existingKey, sub] of this.#extraSubscriptions.entries()) { + if (!desiredDirs.has(existingKey) && this.#canRemoveSubscription(existingKey)) { + this.#extraSubscriptions.delete(existingKey); + sub.unsubscribe().catch(() => {}); } } - for (const pendingDir of this.pendingSubscriptions.keys()) { - if (dirLookupKey === pendingDir || isPathInside(dirLookupKey, pendingDir)) { + + // If subscribing to an existing directory failed (e.g. macOS FSEvents per-process limit), + // dynamically lower the subscription budget and re-reconcile to coalesce into ancestors. + if (hadExistingDirFailure && desiredDirs.size > 1 && this.#maxSubscriptions > 1) { + const nextMax = + this.#extraSubscriptions.size > 0 + ? Math.max(1, Math.floor(this.#extraSubscriptions.size / 2)) + : Math.max(1, Math.floor(Math.min(this.#maxSubscriptions, desiredDirs.size) / 2)); + + if (nextMax < desiredDirs.size && nextMax < this.#maxSubscriptions) { + this.#maxSubscriptions = nextMax; + this.#shrinkBeforeSubscribe = true; + this.#needsReconcile = true; + } + } + } + + /** + * Checks whether a directory lookup key is already covered by an active subscription. + */ + #isCoveredByActiveSubscription(dirLookupKey: string): boolean { + for (const existingDir of this.#extraSubscriptions.keys()) { + if (dirLookupKey === existingDir || isPathInside(dirLookupKey, existingDir)) { return true; } } @@ -509,47 +783,196 @@ class ParcelExternalManager { return false; } - private async ensureDirWatched(dirPath: string, dirKey: string): Promise { - if (this.isCoveredByExistingExternal(dirKey)) { - return; + /** + * Determines whether an existing subscription can be safely removed without leaving + * any remaining watched target directories uncovered. + */ + #canRemoveSubscription(subKey: string): boolean { + for (const targetDirKey of this.#externalDirFiles.keys()) { + if (targetDirKey === subKey || isPathInside(targetDirKey, subKey)) { + let coveredByOther = false; + for (const otherSubKey of this.#extraSubscriptions.keys()) { + if ( + otherSubKey !== subKey && + (targetDirKey === otherSubKey || isPathInside(targetDirKey, otherSubKey)) + ) { + coveredByOther = true; + break; + } + } + if (!coveredByOther) { + return false; + } + } } - const subPromise = this.parcelWatcher.subscribe( - dirPath, - (err, events) => { - if (!err) { - this.handleEvents(events); + return true; + } + + /** + * Computes the minimal set of external directories to subscribe to (mapped from `dirKey` to `dirPath`), + * subsuming nested directories and coalescing branching ancestors to stay within `#maxSubscriptions`. + */ + #computeDesiredDirectories(): Map { + if (this.#externalDirFiles.size === 0) { + return new Map(); + } + + const nodesByKey = new Map(); + const roots: DirTrieNode[] = []; + + for (const [dirKey, { dirPath }] of this.#externalDirFiles.entries()) { + const chain: { dirKey: string; dirPath: string }[] = []; + let currentKey = dirKey; + let currentPath = dirPath; + while (true) { + chain.push({ dirKey: currentKey, dirPath: currentPath }); + const parentKey = getDirectoryPath(currentKey); + if (parentKey === currentKey) { + break; } - }, - { - ignore: this.options?.ignored, - }, - ); + currentKey = parentKey; + currentPath = getDirectoryPath(currentPath); + } - this.pendingSubscriptions.set(dirKey, subPromise); + let parentNode: DirTrieNode | undefined; + for (let i = chain.length - 1; i >= 0; i--) { + const { dirKey: nodeKey, dirPath: nodePath } = chain[i]; + const depth = chain.length - 1 - i; + let node = nodesByKey.get(nodeKey); + if (!node) { + node = { + dirKey: nodeKey, + dirPath: nodePath, + depth, + parent: parentNode, + children: new Map(), + isTarget: false, + leafCount: 0, + }; + nodesByKey.set(nodeKey, node); + if (parentNode) { + parentNode.children.set(nodeKey, node); + } else { + roots.push(node); + } + } + parentNode = node; + } - try { - const sub = await subPromise; - this.pendingSubscriptions.delete(dirKey); + if (parentNode) { + parentNode.isTarget = true; + } + } + + const initSubtree = (node: DirTrieNode): number => { + if (node.isTarget) { + node.children.clear(); + node.leafCount = 1; + + return 1; + } + + let count = 0; + for (const child of node.children.values()) { + count += initSubtree(child); + } + node.leafCount = count; - if (this.externalDirFiles.has(dirKey) && !this.isCoveredByExistingExternal(dirKey)) { - this.extraSubscriptions.set(dirKey, sub); + return count; + }; - // Subsume any nested child subscriptions that are now covered by this parent subscription - for (const [childDir, childSub] of this.extraSubscriptions.entries()) { - if (childDir !== dirKey && isPathInside(childDir, dirKey)) { - this.extraSubscriptions.delete(childDir); - childSub.unsubscribe().catch(() => {}); + let totalLeaves = 0; + for (const root of roots) { + totalLeaves += initSubtree(root); + } + + if (totalLeaves > this.#maxSubscriptions) { + // Tier 1: non-ancestors of workspaceRoot (e.g., /libs, /node_modules) + // Tier 2: ancestors of workspaceRoot (e.g., when sibling apps/libs are directly under it) + const tier1ByDepth = new Map(); + const tier2ByDepth = new Map(); + + const collectInternalNodes = (node: DirTrieNode) => { + if (node.isTarget) { + return; + } + if (node.parent !== undefined && node.leafCount > 1) { + const isWorkspaceAncestor = isPathInside(this.#rootDirLookupKey, node.dirKey); + const bucketMap = isWorkspaceAncestor ? tier2ByDepth : tier1ByDepth; + let bucket = bucketMap.get(node.depth); + if (!bucket) { + bucket = []; + bucketMap.set(node.depth, bucket); } + bucket.push(node); } - } else { - sub.unsubscribe().catch(() => {}); + for (const child of node.children.values()) { + collectInternalNodes(child); + } + }; + + for (const root of roots) { + collectInternalNodes(root); } - } catch { - // Ignore subscription errors for missing or restricted external directories - } finally { - this.pendingSubscriptions.delete(dirKey); + + const collapseTier = (byDepth: Map) => { + const sortedLevels = Array.from(byDepth.entries()).sort(([a], [b]) => b - a); + for (const [, nodes] of sortedLevels) { + if (totalLeaves <= this.#maxSubscriptions) { + return; + } + + const candidates = nodes + .filter((n) => n.leafCount > 1 && !hasTargetAncestor(n)) + .sort((a, b) => b.leafCount - a.leafCount || a.dirKey.localeCompare(b.dirKey)); + + for (const candidate of candidates) { + if (totalLeaves <= this.#maxSubscriptions) { + return; + } + if (candidate.leafCount <= 1 || hasTargetAncestor(candidate)) { + continue; + } + + const reduction = candidate.leafCount - 1; + candidate.isTarget = true; + candidate.children.clear(); + candidate.leafCount = 1; + totalLeaves -= reduction; + + let ancestor = candidate.parent; + while (ancestor) { + ancestor.leafCount -= reduction; + ancestor = ancestor.parent; + } + } + } + }; + + collapseTier(tier1ByDepth); + if (totalLeaves > this.#maxSubscriptions) { + collapseTier(tier2ByDepth); + } + } + + const desired = new Map(); + const collectTargets = (node: DirTrieNode) => { + if (node.isTarget) { + desired.set(node.dirKey, node.dirPath); + + return; + } + for (const child of node.children.values()) { + collectTargets(child); + } + }; + + for (const root of roots) { + collectTargets(root); } + + return desired; } } @@ -609,7 +1032,9 @@ async function createParcelWatcher( const externalManager = new ParcelExternalManager( parcelWatcher, options, + rootDirPosix, rootDirLookupKey, + isCaseSensitive, handleEvents, ); @@ -629,7 +1054,7 @@ async function createParcelWatcher( const lookupKey = toLookupKey(posixPath, isCaseSensitive); if (!watchedFiles.has(lookupKey)) { watchedFiles.add(lookupKey); - void externalManager.ensureWatched(posixPath, lookupKey); + externalManager.watchFile(posixPath, lookupKey); } } }, @@ -660,92 +1085,94 @@ async function createParcelWatcher( return buildWatcher; } -/** - * Manages selective node_modules watching for Chokidar. - * - * During Chokidar's initial scan, the allowed packages set is empty, causing Chokidar to prune the entire - * node_modules directory immediately and avoid blocking the 'ready' event for minutes. - * Packages are dynamically allowed and watched at the package root level as files are added via `watcher.add`. - * Tracking files per package allows reference-counting so that package directories are unwatched when no - * longer needed. - */ -class ChokidarNodeModulesManager { - private readonly allowedPackages = new Map>(); - - constructor(private readonly isCaseSensitive: boolean) {} - - registerPackage( - posixPath: string, - lookupKey: string, - ): { isPackage: boolean; newPkgDir?: string } { - const packageDir = extractNodeModulesPackageDir(posixPath); - if (!packageDir) { - return { isPackage: false }; - } - - const packageKey = toLookupKey(packageDir, this.isCaseSensitive); - let watchedInPkg = this.allowedPackages.get(packageKey); - if (!watchedInPkg) { - watchedInPkg = new Set(); - this.allowedPackages.set(packageKey, watchedInPkg); - watchedInPkg.add(lookupKey); - - return { isPackage: true, newPkgDir: packageDir }; - } - - watchedInPkg.add(lookupKey); +function createChokidarIgnored( + ignoredPatterns: string[] | undefined, + isCaseSensitive: boolean, + watchedFiles: ReadonlySet, +): Chokidar.Matcher[] { + // Strip static node_modules ignore patterns (e.g. `**/node_modules/**` from setupWatcher). + // If left in place, picomatch would unconditionally ignore all node_modules files and prevent + // watching explicitly added files inside node_modules. + const customIgnored = + ignoredPatterns + ?.filter((pattern) => { + const base = toPosixPathNormalized(pattern).replace(/\/\*\*$/, ''); - return { isPackage: true }; - } + return base !== NODE_MODULES && !base.endsWith(`/${NODE_MODULES}`); + }) + .map((pattern) => { + if (/[*?[\]{}()]/.test(pattern)) { + const isMatch = picomatch(pattern, { dot: true }); - removePackageFile( - posixPath: string, - lookupKey: string, - ): { isPackage: boolean; unwatchPkgDir?: string } { - const packageDir = extractNodeModulesPackageDir(posixPath); - if (!packageDir) { - return { isPackage: false }; - } + return (filePath: string) => isMatch(toPosixPathNormalized(filePath)); + } - const packageKey = toLookupKey(packageDir, this.isCaseSensitive); - const watchedInPkg = this.allowedPackages.get(packageKey); - if (watchedInPkg) { - watchedInPkg.delete(lookupKey); - if (watchedInPkg.size === 0) { - this.allowedPackages.delete(packageKey); + return { path: toPosixPathNormalized(pattern), recursive: true }; + }) ?? []; - return { isPackage: true, unwatchPkgDir: packageDir }; + // When watching a directory (e.g. projectRoot via NG_BUILD_WATCH_ROOT), prune `node_modules` + // directories unless explicitly added to `watchedFiles`. Explicitly added files inside + // `node_modules` do not end with `/node_modules` and remain watchable. + return [ + ...customIgnored, + (filePath: string) => { + if (!filePath.includes(NODE_MODULES)) { + return false; } - } - - return { isPackage: true }; - } - - isIgnored(filePath: string): boolean { - // Fast path: avoid expensive normalization for non-node_modules files - if (!filePath.includes(NODE_MODULES)) { - return false; - } - const posixPath = toPosixPathNormalized(filePath); - const lookupKey = toLookupKey(posixPath, this.isCaseSensitive); + const posixPath = toPosixPathNormalized(filePath); + const lookupKey = toLookupKey(posixPath, isCaseSensitive); - if (!`${lookupKey}/`.includes(NODE_MODULES_SEGMENT)) { - return false; - } + return ( + (lookupKey === NODE_MODULES || lookupKey.endsWith(`/${NODE_MODULES}`)) && + !watchedFiles.has(lookupKey) + ); + }, + ]; +} - if (this.allowedPackages.has(lookupKey)) { - return false; - } +interface ChokidarBaselineEntry { + posixPath: string; + mtimeMs?: number; + size?: number; +} - for (const pkg of this.allowedPackages.keys()) { - if (isPathInside(lookupKey, pkg)) { - return false; +// Chokidar initializes file watchers asynchronously (`await stat(path)` followed by +// `fs.watch` or `fs.watchFile`). If a file is modified or recreated immediately after +// `watcher.add()` is called, Chokidar may capture the post-modification state as its initial +// baseline (or suppress the initial `'add'` event due to `ignoreInitial: true`) and miss the +// change. Re-statting after a short delay catches any modifications or recreations that occurred +// during asynchronous watcher registration, while `fileStats` deduplicates if Chokidar also fired. +async function checkChokidarBaselines( + baselineStats: ReadonlyMap, + watchedFiles: ReadonlySet, + fileStats: ReadonlyMap, + watcher: Chokidar.FSWatcher, + handleEvent: (type: 'added' | 'modified' | 'removed', rawPath: string) => void, +): Promise { + await Promise.all( + Array.from(baselineStats, async ([lookupKey, baseline]) => { + if (!watchedFiles.has(lookupKey)) { + return; + } + const currentStat = await fs.promises.stat(baseline.posixPath).catch(() => null); + if (!watchedFiles.has(lookupKey) || !currentStat?.isFile()) { + return; } - } - return true; - } + if (baseline.mtimeMs === undefined || baseline.size === undefined) { + const recorded = fileStats.get(lookupKey); + if (!recorded) { + watcher.add(baseline.posixPath); + handleEvent('added', path.normalize(baseline.posixPath)); + } else if (currentStat.mtimeMs !== recorded.mtimeMs || currentStat.size !== recorded.size) { + handleEvent('modified', path.normalize(baseline.posixPath)); + } + } else if (currentStat.mtimeMs !== baseline.mtimeMs || currentStat.size !== baseline.size) { + handleEvent('modified', path.normalize(baseline.posixPath)); + } + }), + ); } async function createChokidarWatcher( @@ -755,43 +1182,19 @@ async function createChokidarWatcher( const chokidar = chokidarModule ?? (await import('chokidar')); const watchedFiles = new Set(); const fileStats = new Map(); + const pendingTimers = new Set>(); - let queue: WatcherQueue; - if (options?.polling) { - const pollingInterval = options.interval ?? 100; - const debounceMs = Math.min(250, Math.max(100, Math.ceil(pollingInterval * 1.5))); - const maxWaitMs = Math.max(500, debounceMs * 3); - queue = new WatcherQueue(debounceMs, maxWaitMs); - } else { - queue = new WatcherQueue(); - } + const pollingInterval = options?.interval ?? 100; + const debounceMs = Math.min(250, Math.max(100, Math.ceil(pollingInterval * 1.5))); + const queue = options?.polling + ? new WatcherQueue(debounceMs, Math.max(500, debounceMs * 3)) + : new WatcherQueue(); const rootDir = options?.cwd ?? process.cwd(); const isCaseSensitive = isFileSystemCaseSensitive(rootDir); - const rootDirPosix = toPosixPathNormalized(rootDir); - const rootDirLookupKey = toLookupKey(rootDirPosix, isCaseSensitive); - - const nodeModulesManager = new ChokidarNodeModulesManager(isCaseSensitive); - - // Strip static node_modules ignore patterns (e.g. `**/node_modules/**` from setupWatcher). - // If left in place, picomatch would unconditionally ignore all node_modules files and override - // our dynamic package allowlist above. - const customIgnored = - options?.ignored - ?.filter((pattern) => !pattern.includes(NODE_MODULES)) - .map((pattern) => { - if (/[*?[\]{}()]/.test(pattern)) { - const isMatch = picomatch(pattern, { dot: true }); + const ignored = createChokidarIgnored(options?.ignored, isCaseSensitive, watchedFiles); - return (filePath: string) => isMatch(toPosixPathNormalized(filePath)); - } - - return { path: toPosixPathNormalized(pattern), recursive: true }; - }) ?? []; - - const ignored = [...customIgnored, (filePath: string) => nodeModulesManager.isIgnored(filePath)]; - - const watcher = chokidar.watch(rootDir, { + const watcher = chokidar.watch([], { ignoreInitial: true, ignored, followSymlinks: options?.followSymlinks, @@ -799,13 +1202,10 @@ async function createChokidarWatcher( interval: options?.interval, }); - const initTime = Date.now(); + // Prevent unhandled watcher errors (such as EMFILE or EACCES) from crashing the process. + watcher.on('error', () => {}); - // Wait for the watcher to complete its initial filesystem scan before returning. - // With `ignoreInitial: true`, any file visited during the initial scan is treated as the initial baseline - // and will not emit 'add' or 'change' events. Awaiting 'ready' ensures that rapid file modifications - // made right after watcher setup (e.g. in rebuild tests) are not swallowed as initial files. - await once(watcher, 'ready'); + const initTime = Date.now(); const handleEvent = (type: 'added' | 'modified' | 'removed', rawPath: string) => { const posixPath = toPosixPathNormalized(rawPath); @@ -822,14 +1222,10 @@ async function createChokidarWatcher( } const stat = fs.statSync(rawPath, { throwIfNoEntry: false }); - if (!stat) { - return; - } - // Ignore historical events from before watcher initialization, but allow a 1000 ms window // to account for coarse filesystem timestamp resolution (e.g., ext4/overlayfs integer second // mtime truncation on Linux) where files modified during startup may have truncated .000 ms mtimes. - if (stat.mtimeMs < initTime - 1000) { + if (!stat || stat.mtimeMs < initTime - 1000) { return; } @@ -847,11 +1243,55 @@ async function createChokidarWatcher( queue.addChange(type, rawPath); }; + const scheduleBaselineCheck = (baselineStats: ReadonlyMap) => { + if (baselineStats.size === 0) { + return; + } + + const timer = setTimeout(() => { + pendingTimers.delete(timer); + void checkChokidarBaselines(baselineStats, watchedFiles, fileStats, watcher, handleEvent); + }, 50); + timer.unref(); + pendingTimers.add(timer); + }; + watcher.on('add', (path) => handleEvent('added', path)); watcher.on('change', (path) => handleEvent('modified', path)); - watcher.on('unlink', (path) => handleEvent('removed', path)); + watcher.on('unlink', (rawPath) => { + const posixPath = toPosixPathNormalized(rawPath); + const lookupKey = toLookupKey(posixPath, isCaseSensitive); + if (!isPathWatched(lookupKey, watchedFiles)) { + return; + } - const buildWatcher: BuildWatcher = { + // When Chokidar processes a file removal, it closes the underlying file watcher unless only a + // single directory is tracked (`_watched.size === 1`). Re-adding a directly watched path + // ensures Chokidar continues watching either the recreated file (if replaced within the atomic + // window) or its parent directory for future recreation. + if (watchedFiles.has(lookupKey)) { + fileStats.delete(lookupKey); + watcher.add(posixPath); + const stat = fs.statSync(rawPath, { throwIfNoEntry: false }); + if (stat?.isFile()) { + handleEvent('modified', rawPath); + scheduleBaselineCheck( + new Map([[lookupKey, { posixPath, mtimeMs: stat.mtimeMs, size: stat.size }]]), + ); + + return; + } + + handleEvent('removed', rawPath); + scheduleBaselineCheck(new Map([[lookupKey, { posixPath }]])); + + return; + } + + handleEvent('removed', rawPath); + }); + + return { [Symbol.asyncIterator]() { return this; }, @@ -878,28 +1318,23 @@ async function createChokidarWatcher( add(paths) { const targets = typeof paths === 'string' ? [paths] : paths; const newPaths: string[] = []; + const baselineStats = new Map(); + for (const p of targets) { const posixPath = toPosixPathNormalized(p); const lookupKey = toLookupKey(posixPath, isCaseSensitive); if (!watchedFiles.has(lookupKey)) { watchedFiles.add(lookupKey); - - // For files inside node_modules, register their package directory. Only newly encountered - // package directories need to be added to Chokidar. - const { isPackage, newPkgDir } = nodeModulesManager.registerPackage(posixPath, lookupKey); - if (newPkgDir) { - newPaths.push(newPkgDir); - } else if ( - !isPackage && - !isPathInside(lookupKey, rootDirLookupKey) && - lookupKey !== rootDirLookupKey - ) { - newPaths.push(posixPath); + newPaths.push(posixPath); + const stat = fs.statSync(posixPath, { throwIfNoEntry: false }); + if (stat?.isFile()) { + baselineStats.set(lookupKey, { posixPath, mtimeMs: stat.mtimeMs, size: stat.size }); } } } if (newPaths.length > 0) { watcher.add(newPaths); + scheduleBaselineCheck(baselineStats); } }, @@ -912,21 +1347,7 @@ async function createChokidarWatcher( if (watchedFiles.has(lookupKey)) { watchedFiles.delete(lookupKey); fileStats.delete(lookupKey); - - // When the last watched file in a package is removed, unwatch the package directory. - const { isPackage, unwatchPkgDir } = nodeModulesManager.removePackageFile( - posixPath, - lookupKey, - ); - if (unwatchPkgDir) { - removePaths.push(unwatchPkgDir); - } else if ( - !isPackage && - !isPathInside(lookupKey, rootDirLookupKey) && - lookupKey !== rootDirLookupKey - ) { - removePaths.push(posixPath); - } + removePaths.push(posixPath); } } if (removePaths.length > 0) { @@ -935,13 +1356,15 @@ async function createChokidarWatcher( }, async close() { + for (const timer of pendingTimers) { + clearTimeout(timer); + } + pendingTimers.clear(); try { await watcher.close(); } finally { queue.close(); } }, - }; - - return buildWatcher; + } satisfies BuildWatcher; } diff --git a/packages/angular/build/src/utils/watcher_spec.ts b/packages/angular/build/src/utils/watcher_spec.ts index 547ad2695f90..ef7a14d831d2 100644 --- a/packages/angular/build/src/utils/watcher_spec.ts +++ b/packages/angular/build/src/utils/watcher_spec.ts @@ -6,6 +6,7 @@ * found in the LICENSE file at https://angular.dev/license */ +import type * as ParcelWatcher from '@parcel/watcher'; import assert from 'node:assert'; import * as fs from 'node:fs'; import * as path from 'node:path'; @@ -13,6 +14,7 @@ import { setTimeout } from 'node:timers/promises'; import { type BuildWatcher, ChangedFiles, + ParcelExternalManager, createWatcher, extractNodeModulesPackageDir, getDirectoryPath, @@ -773,7 +775,7 @@ describe('Watcher', () => { } }, 10000); - it('should unwatch linked package directory when all its files are removed via watcher.remove', async () => { + it('should unwatch linked package file when removed via watcher.remove', async () => { const externalDir = fs.mkdtempSync(path.join(TMP_DIR, 'watcher-remove-npm-link-')); let watcher: BuildWatcher | undefined; @@ -811,7 +813,7 @@ describe('Watcher', () => { // Remove the linked file from the watcher watcher.remove(linkedFile); - // Allow a brief moment for Chokidar to asynchronously complete unwatching the package directory + // Allow a brief moment for Chokidar to asynchronously complete unwatching the file await setTimeout(250); // Modify both the removed linked file and the regular file @@ -829,5 +831,456 @@ describe('Watcher', () => { fs.rmSync(externalDir, { recursive: true, force: true }); } }, 10000); + + it('should detect changes across multiple external directories when using @parcel/watcher', async () => { + const externalRoot = fs.mkdtempSync(path.join(TMP_DIR, 'parcel-external-spec-')); + let watcher: BuildWatcher | undefined; + + try { + const lib1Dir = path.join(externalRoot, 'libs', 'lib-1', 'src'); + const lib2Dir = path.join(externalRoot, 'libs', 'lib-2', 'src'); + fs.mkdirSync(lib1Dir, { recursive: true }); + fs.mkdirSync(lib2Dir, { recursive: true }); + + const file1 = path.join(lib1Dir, 'index.ts'); + const file2 = path.join(lib2Dir, 'index.ts'); + fs.writeFileSync(file1, 'export const a = 1;'); + fs.writeFileSync(file2, 'export const b = 1;'); + + // Backdate initial file timestamps to earlier than `initTime - 1000` so that any + // delayed initial FSEvents replay from file creation is ignored by the watcher. + const pastTime = new Date(Date.now() - 5000); + fs.utimesSync(file1, pastTime, pastTime); + fs.utimesSync(file2, pastTime, pastTime); + + watcher = await createWatcher({ cwd: tempDir }); + watcher.add([file1, file2]); + + await setTimeout(150); + + const iterator = watcher[Symbol.asyncIterator](); + + let nextPromise = iterator.next(); + fs.writeFileSync(file1, 'export const a = 2;'); + let result = await nextPromise; + expect(result.done).toBeFalsy(); + expect(result.value?.all.some((f: string) => f.includes('lib-1'))).toBeTrue(); + + nextPromise = iterator.next(); + fs.writeFileSync(file2, 'export const b = 2;'); + result = await nextPromise; + expect(result.done).toBeFalsy(); + expect(result.value?.all.some((f: string) => f.includes('lib-2'))).toBeTrue(); + } finally { + await watcher?.close(); + fs.rmSync(externalRoot, { recursive: true, force: true }); + } + }, 10000); + + it('should detect file deletion and recreation when watching multiple files with followSymlinks: true', async () => { + const subDirA = path.join(tempDir, 'dir-a'); + const subDirB = path.join(tempDir, 'dir-b'); + fs.mkdirSync(subDirA); + fs.mkdirSync(subDirB); + + const fileA = path.join(subDirA, 'a.ts'); + const fileB = path.join(subDirB, 'b.ts'); + fs.writeFileSync(fileA, 'export const a = 1;'); + fs.writeFileSync(fileB, 'export const b = 1;'); + + const watcher = await createWatcher({ + followSymlinks: true, + cwd: tempDir, + }); + + try { + watcher.add([fileA, fileB]); + await setTimeout(250); + + const iterator = watcher[Symbol.asyncIterator](); + + // Delete fileA and wait past Chokidar's atomic write window + fs.rmSync(fileA); + let result = await iterator.next(); + expect(result.done).toBeFalsy(); + expect(result.value?.removed.size).toBeGreaterThan(0); + + await setTimeout(150); + + // Recreate fileA and verify the watcher still detects it + fs.writeFileSync(fileA, 'export const a = 2;'); + result = await iterator.next(); + expect(result.done).toBeFalsy(); + expect(result.value?.all.some((f: string) => f.includes('a.ts'))).toBeTrue(); + + // Modify recreated fileA again to ensure ongoing changes are tracked + await setTimeout(150); + fs.writeFileSync(fileA, 'export const a = 3;'); + result = await iterator.next(); + expect(result.done).toBeFalsy(); + expect(result.value?.all.some((f: string) => f.includes('a.ts'))).toBeTrue(); + } finally { + await watcher.close(); + } + }, 10000); + + it('should prune node_modules when watching root directory while watching added package files', async () => { + const nodeModulesDir = path.join(tempDir, 'node_modules'); + const pkgDir = path.join(nodeModulesDir, 'some-pkg'); + fs.mkdirSync(pkgDir, { recursive: true }); + + const watchedPkgFile = path.join(pkgDir, 'index.js'); + const unwatchedPkgFile = path.join(pkgDir, 'other.js'); + const rootFile = path.join(tempDir, 'main.ts'); + fs.writeFileSync(watchedPkgFile, 'export const idx = 1;'); + fs.writeFileSync(unwatchedPkgFile, 'export const other = 1;'); + fs.writeFileSync(rootFile, 'export const main = 1;'); + + const pastTime = new Date(Date.now() - 5000); + fs.utimesSync(watchedPkgFile, pastTime, pastTime); + fs.utimesSync(unwatchedPkgFile, pastTime, pastTime); + fs.utimesSync(rootFile, pastTime, pastTime); + + const watcher = await createWatcher({ + followSymlinks: true, + cwd: tempDir, + }); + + try { + // Watch both the root directory and a specific file in node_modules + watcher.add([tempDir, watchedPkgFile]); + await setTimeout(250); + + const iterator = watcher[Symbol.asyncIterator](); + const nextPromise = iterator.next(); + + // Modify unwatched file in node_modules (should be ignored even though tempDir is watched) + // and watched file in node_modules + fs.writeFileSync(unwatchedPkgFile, 'export const other = 2;'); + fs.writeFileSync(watchedPkgFile, 'export const idx = 2;'); + + const result = await nextPromise; + expect(result.done).toBeFalsy(); + const changed = result.value?.all ?? []; + expect(changed.some((f: string) => f.includes('index.js'))).toBeTrue(); + expect(changed.some((f: string) => f.includes('other.js'))).toBeFalse(); + } finally { + await watcher.close(); + } + }, 10000); + }); + + describe('ParcelExternalManager', () => { + function createMockParcelWatcher(options?: { + failAfter?: number; + onSubscribe?: (dir: string, opts?: ParcelWatcher.Options) => void; + }) { + const activeDirs = new Set(); + const subscribeHistory: { dir: string; options?: ParcelWatcher.Options }[] = []; + let totalCalls = 0; + + const mockWatcher = { + subscribe: jasmine + .createSpy('subscribe') + .and.callFake( + async ( + dir: string, + _cb: ParcelWatcher.SubscribeCallback, + opts?: ParcelWatcher.Options, + ): Promise => { + totalCalls++; + options?.onSubscribe?.(dir, opts); + if (options?.failAfter !== undefined && activeDirs.size >= options.failAfter) { + throw new Error('Error starting FSEvents stream'); + } + activeDirs.add(dir); + subscribeHistory.push({ dir, options: opts }); + + return { + unsubscribe: async () => { + activeDirs.delete(dir); + }, + }; + }, + ), + } as unknown as typeof ParcelWatcher; + + return { + mockWatcher, + activeDirs, + subscribeHistory, + getTotalCalls: () => totalCalls, + }; + } + + it('should batch synchronous watchFile calls and subsume child directories added before parent', async () => { + const { mockWatcher, activeDirs, getTotalCalls } = createMockParcelWatcher(); + const manager = new ParcelExternalManager( + mockWatcher, + undefined, + '/monorepo/apps/app', + '/monorepo/apps/app', + true, + () => {}, + ); + + // Add deeper child directories first, then their parent directory in the same synchronous turn + manager.watchFile( + '/monorepo/libs/lib-1/src/components/button/button.ts', + '/monorepo/libs/lib-1/src/components/button/button.ts', + ); + manager.watchFile( + '/monorepo/libs/lib-1/src/components/card/card.ts', + '/monorepo/libs/lib-1/src/components/card/card.ts', + ); + manager.watchFile('/monorepo/libs/lib-1/src/index.ts', '/monorepo/libs/lib-1/src/index.ts'); + + await manager.flush(); + + // Should only ever have subscribed once to the parent '/monorepo/libs/lib-1/src' + expect(getTotalCalls()).toBe(1); + expect(Array.from(activeDirs)).toEqual(['/monorepo/libs/lib-1/src']); + + await manager.close(); + }); + + it('should normalize external node_modules paths to their package root directory', async () => { + const { mockWatcher, activeDirs, getTotalCalls } = createMockParcelWatcher(); + const manager = new ParcelExternalManager( + mockWatcher, + undefined, + '/monorepo/apps/app', + '/monorepo/apps/app', + true, + () => {}, + ); + + manager.watchFile( + '/monorepo/node_modules/my-pkg/fesm2022/my-pkg.mjs', + '/monorepo/node_modules/my-pkg/fesm2022/my-pkg.mjs', + ); + manager.watchFile( + '/monorepo/node_modules/my-pkg/esm2022/sub/helper.mjs', + '/monorepo/node_modules/my-pkg/esm2022/sub/helper.mjs', + ); + manager.watchFile( + '/monorepo/node_modules/@scope/scoped-pkg/fesm2022/index.mjs', + '/monorepo/node_modules/@scope/scoped-pkg/fesm2022/index.mjs', + ); + manager.watchFile( + '/monorepo/node_modules/@scope/scoped-pkg/package.json', + '/monorepo/node_modules/@scope/scoped-pkg/package.json', + ); + + await manager.flush(); + + expect(getTotalCalls()).toBe(2); + expect(Array.from(activeDirs).sort()).toEqual([ + '/monorepo/node_modules/@scope/scoped-pkg', + '/monorepo/node_modules/my-pkg', + ]); + + // Removing one file from my-pkg should keep the package subscription active + manager.removeFile('/monorepo/node_modules/my-pkg/fesm2022/my-pkg.mjs'); + await manager.flush(); + expect(activeDirs.has('/monorepo/node_modules/my-pkg')).toBeTrue(); + + // Removing the last file from my-pkg should unsubscribe the package directory + manager.removeFile('/monorepo/node_modules/my-pkg/esm2022/sub/helper.mjs'); + await manager.flush(); + expect(activeDirs.has('/monorepo/node_modules/my-pkg')).toBeFalse(); + expect(Array.from(activeDirs)).toEqual(['/monorepo/node_modules/@scope/scoped-pkg']); + + await manager.close(); + }); + + it('should coalesce large numbers of external library and node_modules directories into common ancestors within budget', async () => { + const { mockWatcher, activeDirs, getTotalCalls } = createMockParcelWatcher(); + const manager = new ParcelExternalManager( + mockWatcher, + undefined, + '/monorepo/apps/app', + '/monorepo/apps/app', + true, + () => {}, + 64, + ); + + // Simulate 600 sibling libraries and 200 hoisted node_modules packages (800 external dirs) + for (let i = 0; i < 600; i++) { + const file = `/monorepo/libs/lib-${i}/src/index.ts`; + manager.watchFile(file, file); + } + for (let i = 0; i < 200; i++) { + const file = `/monorepo/node_modules/pkg-${i}/dist/index.mjs`; + manager.watchFile(file, file); + } + + await manager.flush(); + + // All 600 libs coalesce into '/monorepo/libs' and all 200 packages coalesce into '/monorepo/node_modules' + expect(getTotalCalls()).toBe(2); + expect(Array.from(activeDirs).sort()).toEqual(['/monorepo/libs', '/monorepo/node_modules']); + + await manager.close(); + }); + + it('should prioritize collapsing deeper branching subdirectories before broader ancestor directories', async () => { + const { mockWatcher, activeDirs } = createMockParcelWatcher(); + const manager = new ParcelExternalManager( + mockWatcher, + undefined, + '/monorepo/apps/app', + '/monorepo/apps/app', + true, + () => {}, + 3, // Budget of 3 subscriptions + ); + + // lib-1 has 4 component subdirectories, lib-2 and lib-3 have 1 each (total = 6 leaf dirs) + for (let c = 1; c <= 4; c++) { + const file = `/monorepo/libs/lib-1/src/comp-${c}/comp.ts`; + manager.watchFile(file, file); + } + manager.watchFile('/monorepo/libs/lib-2/src/index.ts', '/monorepo/libs/lib-2/src/index.ts'); + manager.watchFile('/monorepo/libs/lib-3/src/index.ts', '/monorepo/libs/lib-3/src/index.ts'); + + await manager.flush(); + + // Collapsing lib-1/src reduces total leaves from 6 to 3, meeting the budget of 3 + // without needing to collapse all of '/monorepo/libs' + expect(Array.from(activeDirs).sort()).toEqual([ + '/monorepo/libs/lib-1/src', + '/monorepo/libs/lib-2/src', + '/monorepo/libs/lib-3/src', + ]); + + await manager.close(); + }); + + it('should ignore workspaceRoot when coalescing into an ancestor of workspaceRoot (Tier 2)', async () => { + const { mockWatcher, activeDirs, subscribeHistory } = createMockParcelWatcher(); + const manager = new ParcelExternalManager( + mockWatcher, + { ignored: ['/monorepo/apps/app/dist'] }, + '/monorepo/apps/app', + '/monorepo/apps/app', + true, + () => {}, + 2, // Budget of 2 subscriptions + ); + + // Sibling apps/packages directly under /monorepo/apps (which is an ancestor of /monorepo/apps/app) + manager.watchFile( + '/monorepo/apps/shared-1/src/index.ts', + '/monorepo/apps/shared-1/src/index.ts', + ); + manager.watchFile( + '/monorepo/apps/shared-2/src/index.ts', + '/monorepo/apps/shared-2/src/index.ts', + ); + manager.watchFile( + '/monorepo/apps/shared-3/src/index.ts', + '/monorepo/apps/shared-3/src/index.ts', + ); + + await manager.flush(); + + expect(Array.from(activeDirs)).toEqual(['/monorepo/apps']); + expect(subscribeHistory.length).toBe(1); + expect(subscribeHistory[0].options?.ignore).toEqual([ + '/monorepo/apps/app/dist', + '/monorepo/apps/app', + '/monorepo/apps/app/**', + ]); + + await manager.close(); + }); + + it('should dynamically lower budget and coalesce into ancestor when subscribe fails with FSEvents stream limit error', async () => { + const externalRoot = fs.mkdtempSync(path.join(TMP_DIR, 'parcel-fallback-spec-')); + + try { + const rootPosix = toPosixPathNormalized(path.join(externalRoot, 'apps', 'app')); + const libFiles: string[] = []; + for (let i = 0; i < 6; i++) { + const libDir = path.join(externalRoot, 'libs', `lib-${i}`, 'src'); + fs.mkdirSync(libDir, { recursive: true }); + const file = toPosixPathNormalized(path.join(libDir, 'index.ts')); + fs.writeFileSync(file, 'export const x = 1;'); + libFiles.push(file); + } + + // Allow at most 2 active FSEvents streams even though manager budget starts at 64 + const { mockWatcher, activeDirs } = createMockParcelWatcher({ failAfter: 2 }); + const manager = new ParcelExternalManager( + mockWatcher, + undefined, + rootPosix, + rootPosix, + true, + () => {}, + 64, + ); + + for (const file of libFiles) { + manager.watchFile(file, file); + } + + await manager.flush(); + + // Should have shrunk and coalesced the 6 libraries into the single '/libs' ancestor directory + const expectedLibsDir = toPosixPathNormalized(path.join(externalRoot, 'libs')); + expect(Array.from(activeDirs)).toEqual([expectedLibsDir]); + + await manager.close(); + } finally { + fs.rmSync(externalRoot, { recursive: true, force: true }); + } + }); + + it('should uncoalesce and subscribe to remaining leaf directories when removals bring count back within budget', async () => { + const { mockWatcher, activeDirs } = createMockParcelWatcher(); + const manager = new ParcelExternalManager( + mockWatcher, + undefined, + '/monorepo/apps/app', + '/monorepo/apps/app', + true, + () => {}, + 2, // Budget of 2 subscriptions + ); + + const file1 = '/monorepo/libs/lib-1/src/index.ts'; + const file2 = '/monorepo/libs/lib-2/src/index.ts'; + const file3 = '/monorepo/libs/lib-3/src/index.ts'; + + manager.watchFile(file1, file1); + manager.watchFile(file2, file2); + manager.watchFile(file3, file3); + + await manager.flush(); + + // 3 directories exceed budget of 2, so they coalesce into '/monorepo/libs' + expect(Array.from(activeDirs)).toEqual(['/monorepo/libs']); + + // Removing file3 brings remaining directories to 2, which fits within budget + manager.removeFile(file3); + await manager.flush(); + + expect(Array.from(activeDirs).sort()).toEqual([ + '/monorepo/libs/lib-1/src', + '/monorepo/libs/lib-2/src', + ]); + + // Removing the remaining files unsubscribes everything + manager.removeFile(file1); + manager.removeFile(file2); + await manager.flush(); + + expect(activeDirs.size).toBe(0); + + await manager.close(); + }); }); }); diff --git a/packages/angular/cli/package.json b/packages/angular/cli/package.json index ef6e3407b670..f558e7a6293d 100644 --- a/packages/angular/cli/package.json +++ b/packages/angular/cli/package.json @@ -21,16 +21,16 @@ "@angular-devkit/core": "workspace:0.0.0-PLACEHOLDER", "@angular-devkit/schematics": "workspace:0.0.0-PLACEHOLDER", "@inquirer/prompts": "8.7.3", - "@listr2/prompt-adapter-inquirer": "4.2.7", + "@listr2/prompt-adapter-inquirer": "4.3.1", "@modelcontextprotocol/server": "2.3.1", "@schematics/angular": "workspace:0.0.0-PLACEHOLDER", "jsonc-parser": "3.3.1", - "listr2": "11.1.1", + "listr2": "11.2.0", "npm-package-arg": "14.0.0", "parse5-html-rewriting-stream": "8.0.1", "semver": "7.8.5", "yargs": "18.2.0", - "zod": "4.6.5" + "zod": "4.6.7" }, "ng-update": { "migrations": "@schematics/angular/migrations/migration-collection.json", diff --git a/packages/angular/cli/src/commands/config/cli.ts b/packages/angular/cli/src/commands/config/cli.ts index ae25f3839e22..ef7cfd95d5a5 100644 --- a/packages/angular/cli/src/commands/config/cli.ts +++ b/packages/angular/cli/src/commands/config/cli.ts @@ -133,9 +133,8 @@ export default class ConfigCommandModule * ["a", 3, "foo", "bar", 2]. * @param path The JSON string to parse. * @returns {(string|number)[]} The fragments for the string. - * @private */ -function parseJsonPath(path: string): (string | number)[] { +export function parseJsonPath(path: string): (string | number)[] { const fragments = (path || '').split(/\./g); const result: (string | number)[] = []; @@ -155,7 +154,7 @@ function parseJsonPath(path: string): (string | number)[] { const indices = match[2] .slice(1, -1) .split('][') - .map((x) => (/^\d$/.test(x) ? +x : x.replace(/"|'/g, ''))); + .map((x) => (/^\d+$/.test(x) ? +x : x.replace(/"|'/g, ''))); result.push(...indices); } } diff --git a/packages/angular/cli/src/commands/config/cli_spec.ts b/packages/angular/cli/src/commands/config/cli_spec.ts new file mode 100644 index 000000000000..dd8fe4790e16 --- /dev/null +++ b/packages/angular/cli/src/commands/config/cli_spec.ts @@ -0,0 +1,37 @@ +/** + * @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 { parseJsonPath } from './cli'; + +describe('parseJsonPath', () => { + it('should parse single digit array indices', () => { + expect(parseJsonPath('a[3].foo.bar[2]')).toEqual(['a', 3, 'foo', 'bar', 2]); + }); + + it('should parse multi digit array indices', () => { + expect(parseJsonPath('projects.app.architect.build.options.styles[10]')).toEqual([ + 'projects', + 'app', + 'architect', + 'build', + 'options', + 'styles', + 10, + ]); + expect(parseJsonPath('a[2][13].b')).toEqual(['a', 2, 13, 'b']); + }); + + it('should keep quoted keys as strings', () => { + expect(parseJsonPath('projects["test-project"].root')).toEqual([ + 'projects', + 'test-project', + 'root', + ]); + expect(parseJsonPath(`a['10']`)).toEqual(['a', '10']); + }); +}); diff --git a/packages/angular/cli/src/commands/version/version-info.ts b/packages/angular/cli/src/commands/version/version-info.ts index 128cc8ef5449..9709567e382b 100644 --- a/packages/angular/cli/src/commands/version/version-info.ts +++ b/packages/angular/cli/src/commands/version/version-info.ts @@ -104,6 +104,13 @@ export async function gatherVersionInfo(context: CommandContext): Promise { + let tempRoot: string; + + beforeEach(async () => { + const baseTmpDir = process.env['TEST_TMPDIR']; + assert(baseTmpDir, 'TEST_TMPDIR is not set'); + tempRoot = await mkdtemp(join(baseTmpDir, 'angular-cli-version-info-test-')); + }); + + afterEach(async () => { + await rm(tempRoot, { recursive: true, force: true }); + }); + + function createContext(packageManager: PackageManager): CommandContext { + return { root: tempRoot, packageManager } as unknown as CommandContext; + } + + it('should report the package manager version when it is available', async () => { + const packageManager = new PackageManager( + new MockHost(), + tempRoot, + SUPPORTED_PACKAGE_MANAGERS['npm'], + { version: '10.9.0' }, + ); + + const info = await gatherVersionInfo(createContext(packageManager)); + + expect(info.system.packageManager).toEqual({ name: 'npm', version: '10.9.0' }); + }); + + it('should not fail when the package manager is not installed', async () => { + const packageManager = new PackageManager( + new MockHost(), + tempRoot, + SUPPORTED_PACKAGE_MANAGERS['bun'], + { + initializationError: new Error( + `The project is configured to use 'bun', but it is not installed.`, + ), + }, + ); + + const info = await gatherVersionInfo(createContext(packageManager)); + + expect(info.system.packageManager).toEqual({ name: 'bun', version: undefined }); + }); +}); diff --git a/packages/angular/cli/src/package-managers/package-manager.ts b/packages/angular/cli/src/package-managers/package-manager.ts index 234b54851232..272454c21cbd 100644 --- a/packages/angular/cli/src/package-managers/package-manager.ts +++ b/packages/angular/cli/src/package-managers/package-manager.ts @@ -393,25 +393,39 @@ export class PackageManager { * Gets the name of the package in the current project. */ async getCurrentPackageName(): Promise { + try { + const content = await this.host.readFile(join(this.cwd, 'package.json')); + const { name } = JSON.parse(content) as { name?: unknown }; + if (typeof name === 'string') { + return name; + } + } catch { + // Fall back to package manager command if reading file fails + } + if (this.descriptor.getPackageNameCommand) { try { const { stdout } = await this.#run(this.descriptor.getPackageNameCommand); if (stdout) { - return JSON.parse(stdout); + const name = JSON.parse(stdout); + if (typeof name === 'string') { + return name; + } + + // In a workspace member npm prints an object keyed by workspace name instead of a string. + if (name && typeof name === 'object') { + const values = Object.values(name); + if (values.length === 1 && typeof values[0] === 'string') { + return values[0]; + } + } } } catch { - // Fall back to reading file if command fails + // Ignore command failure } } - try { - const content = await this.host.readFile(join(this.cwd, 'package.json')); - const pkgJson = JSON.parse(content); - - return pkgJson.name; - } catch { - return undefined; - } + return undefined; } /** diff --git a/packages/angular/cli/src/package-managers/package-manager_spec.ts b/packages/angular/cli/src/package-managers/package-manager_spec.ts index d500a435d0df..37e0d8aae113 100644 --- a/packages/angular/cli/src/package-managers/package-manager_spec.ts +++ b/packages/angular/cli/src/package-managers/package-manager_spec.ts @@ -378,6 +378,75 @@ describe('PackageManager', () => { }); }); + describe('getCurrentPackageName', () => { + it('should return the name from package.json without running the package manager command', async () => { + const pm = new PackageManager(host, '/repo/app', descriptor); + const readFileSpy = spyOn(host, 'readFile').and.resolveTo('{"name": "app"}'); + + expect(await pm.getCurrentPackageName()).toBe('app'); + expect(readFileSpy).toHaveBeenCalledWith(jasmine.stringMatching(/package\.json$/)); + expect(runCommandSpy).not.toHaveBeenCalled(); + }); + + it('should fall back to the package manager command when reading package.json fails', async () => { + const pm = new PackageManager(host, '/repo/app/src', descriptor); + spyOn(host, 'readFile').and.rejectWith(new Error('ENOENT')); + runCommandSpy.and.resolveTo({ stdout: '"app"', stderr: '' }); + + expect(await pm.getCurrentPackageName()).toBe('app'); + expect(runCommandSpy).toHaveBeenCalledWith( + descriptor.binary, + descriptor.getPackageNameCommand, + jasmine.objectContaining({ cwd: '/repo/app/src' }), + ); + }); + + it('should extract the package name when npm prints a single-entry workspace object', async () => { + const pm = new PackageManager(host, '/repo/apps/app/src', descriptor); + spyOn(host, 'readFile').and.rejectWith(new Error('ENOENT')); + // Inside a workspace member, `npm pkg get name` prints `{ "app": "app" }` instead of `"app"`. + runCommandSpy.and.resolveTo({ stdout: '{\n "app": "app"\n}', stderr: '' }); + + expect(await pm.getCurrentPackageName()).toBe('app'); + }); + + it('should return undefined when npm prints multiple workspace entries and package.json is unavailable', async () => { + const pm = new PackageManager(host, '/repo', descriptor); + spyOn(host, 'readFile').and.rejectWith(new Error('ENOENT')); + runCommandSpy.and.resolveTo({ + stdout: '{\n "app-a": "app-a",\n "app-b": "app-b"\n}', + stderr: '', + }); + + expect(await pm.getCurrentPackageName()).toBeUndefined(); + }); + }); + + describe('getProjectDependencies', () => { + it('should list the dependencies of an npm workspace member', async () => { + const pm = new PackageManager(host, '/repo/apps/app', descriptor); + runCommandSpy.and.resolveTo({ + stdout: JSON.stringify({ + name: 'monorepo', + dependencies: { + app: { + version: '0.0.0', + resolved: 'file:../apps/app', + dependencies: { '@angular/core': { version: '22.0.0' } }, + }, + }, + }), + stderr: '', + }); + spyOn(host, 'readFile').and.resolveTo('{"name": "app"}'); + + const dependencies = await pm.getProjectDependencies(); + + expect([...dependencies.keys()]).toEqual(['@angular/core']); + expect(dependencies.get('@angular/core')?.version).toBe('22.0.0'); + }); + }); + describe('initializationError', () => { it('should throw initializationError when running commands', async () => { const error = new Error('Not installed'); diff --git a/packages/angular/ssr/package.json b/packages/angular/ssr/package.json index 6e3596ca6aae..a3049daf662d 100644 --- a/packages/angular/ssr/package.json +++ b/packages/angular/ssr/package.json @@ -22,7 +22,7 @@ "save": "dependencies" }, "dependencies": { - "beasties": "0.5.4", + "beasties": "0.5.5", "tslib": "^2.3.0" }, "peerDependencies": { @@ -38,12 +38,12 @@ }, "devDependencies": { "@angular-devkit/schematics": "workspace:*", - "@angular/common": "22.3.0-next.1", - "@angular/compiler": "22.3.0-next.1", - "@angular/core": "22.3.0-next.1", - "@angular/platform-browser": "22.3.0-next.1", - "@angular/platform-server": "22.3.0-next.1", - "@angular/router": "22.3.0-next.1", + "@angular/common": "22.3.0-next.2", + "@angular/compiler": "22.3.0-next.2", + "@angular/core": "22.3.0-next.2", + "@angular/platform-browser": "22.3.0-next.2", + "@angular/platform-server": "22.3.0-next.2", + "@angular/router": "22.3.0-next.2", "@schematics/angular": "workspace:*" }, "sideEffects": false, diff --git a/packages/angular/ssr/src/app.ts b/packages/angular/ssr/src/app.ts index 6c0202a8f9b3..517cf1e31727 100644 --- a/packages/angular/ssr/src/app.ts +++ b/packages/angular/ssr/src/app.ts @@ -163,14 +163,17 @@ export class AngularServerApp { return null; } - const { redirectTo, status, renderMode, headers } = matchedRoute; + const { redirectTo, relativeRedirect, status, renderMode, headers } = matchedRoute; if (redirectTo !== undefined) { + const location = joinUrlParts( + request.headers.get('X-Forwarded-Prefix') ?? '', + buildPathWithParams(redirectTo, url.pathname), + ); + + // Match Angular router behavior: relative redirects keep the query string. return createRedirectResponse( - joinUrlParts( - request.headers.get('X-Forwarded-Prefix') ?? '', - buildPathWithParams(redirectTo, url.pathname), - ), + relativeRedirect ? location + url.search : location, status, headers, ); diff --git a/packages/angular/ssr/src/routes/ng-routes.ts b/packages/angular/ssr/src/routes/ng-routes.ts index ac83aae8cd4f..816155c4df01 100644 --- a/packages/angular/ssr/src/routes/ng-routes.ts +++ b/packages/angular/ssr/src/routes/ng-routes.ts @@ -31,7 +31,7 @@ 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 { joinUrlParts, stripLeadingSlash } from '../utils/url'; import { PrerenderFallback, RenderMode, @@ -146,6 +146,7 @@ type EntryPointToBrowserMapping = AngularAppManifest['entryPointToBrowserMapping async function* handleRoute(options: { metadata: ServerConfigRouteTreeNodeMetadata; currentRoutePath: string; + parentRoute: string; route: Route; compiler: Compiler; parentInjector: Injector; @@ -158,6 +159,7 @@ async function* handleRoute(options: { const { metadata, currentRoutePath, + parentRoute, route, compiler, parentInjector, @@ -168,19 +170,28 @@ async function* handleRoute(options: { } = options; const { redirectTo, loadChildren, loadComponent, children, ɵentryName } = route; - if (ɵentryName && loadComponent) { + if (ɵentryName && (loadComponent || loadChildren)) { appendPreloadToMetadata(ɵentryName, entryPointToBrowserMapping, metadata); } + const parentRoutes = new Map(); + if (metadata.renderMode === RenderMode.Prerender) { - yield* handleSSGRoute( + for await (const result of handleSSGRoute( serverConfigRouteTree, typeof redirectTo === 'string' ? redirectTo : undefined, + parentRoute, metadata, parentInjector, invokeGetPrerenderParams, includePrerenderFallbackRoutes, - ); + )) { + if ('error' in result) { + yield result; + } else { + parentRoutes.set(result.route, result); + } + } } else if (redirectTo !== undefined) { if (metadata.status && !isValidRedirectResponseCode(metadata.status)) { yield { @@ -189,33 +200,23 @@ async function* handleRoute(options: { `Please use one of the following redirect response codes: ${[...VALID_REDIRECT_RESPONSE_CODES.values()].join(', ')}.`, }; } else if (typeof redirectTo === 'string') { - yield { + parentRoutes.set(metadata.route, { ...metadata, - redirectTo: resolveRedirectTo(metadata.route, redirectTo), - }; + redirectTo: resolveRedirectTo(parentRoute, redirectTo), + relativeRedirect: redirectTo[0] !== '/' ? true : undefined, + }); } else { - yield metadata; + parentRoutes.set(metadata.route, metadata); } } else { - yield metadata; + parentRoutes.set(metadata.route, metadata); } - // Recursively process child routes - if (children?.length) { - yield* traverseRoutesConfig({ - ...options, - routes: children, - parentRoute: currentRoutePath, - parentPreloads: metadata.preload, - }); - } - - // Load and process lazy-loaded child routes - if (loadChildren) { - if (ɵentryName) { - appendPreloadToMetadata(ɵentryName, entryPointToBrowserMapping, metadata); - } + // Recursively process child and lazy-loaded routes + let childRoutes: Route[] | undefined = children; + let childInjector = parentInjector; + if (!childRoutes?.length && loadChildren) { const routeInjector = route.providers ? createEnvironmentInjector( route.providers, @@ -226,16 +227,48 @@ async function* handleRoute(options: { const loadedChildRoutes = await loadChildrenHelper(route, compiler, routeInjector); if (loadedChildRoutes) { - const { routes: childRoutes, injector = routeInjector } = loadedChildRoutes; - yield* traverseRoutesConfig({ - ...options, - routes: childRoutes, - parentInjector: injector, - parentRoute: currentRoutePath, - parentPreloads: metadata.preload, - }); + childRoutes = loadedChildRoutes.routes; + childInjector = loadedChildRoutes.injector ?? routeInjector; + } + } + + if (!childRoutes?.length) { + yield* parentRoutes.values(); + + return; + } + + const childResults: (RouteTreeNodeMetadata | { error: string })[] = []; + const overriddenParentRoutes = new Set(); + const traverseChildRoutes = traverseRoutesConfig({ + ...options, + routes: childRoutes, + parentInjector: childInjector, + parentRoute: currentRoutePath, + parentPreloads: metadata.preload, + }); + + for await (const result of traverseChildRoutes) { + if ('error' in result) { + childResults.push(result); + continue; + } + + // If a child route resolves to the exact same path as the parent (i.e., an empty-path child), + // let the first matching child override the parent's metadata so accumulated preloads and + // redirects from the activated descendant chain are preserved. + if (parentRoutes.has(result.route)) { + if (!overriddenParentRoutes.has(result.route)) { + overriddenParentRoutes.add(result.route); + parentRoutes.set(result.route, result); + } + } else { + childResults.push(result); } } + + yield* parentRoutes.values(); + yield* childResults; } catch (error) { yield { error: `Error in handleRoute for '${options.currentRoutePath}': ${(error as Error).message}`, @@ -340,10 +373,7 @@ async function* traverseRoutesConfig(options: { renderMode: RenderMode.Prerender, ...matchedMetaData, preload: parentPreloads, - // Match Angular router behavior - // ['one', 'two', ''] -> 'one/two/' - // ['one', 'two', 'three'] -> 'one/two/three' - route: path === '' ? addTrailingSlash(currentRoutePath) : currentRoutePath, + route: currentRoutePath, presentInClientRouter: undefined, }, currentRoutePath, @@ -392,6 +422,7 @@ function appendPreloadToMetadata( * * @param serverConfigRouteTree - The tree representing the server's routing setup. * @param redirectTo - Optional path to redirect to, if specified. + * @param parentRoute - The path of the parent route. * @param metadata - The metadata associated with the route tree node. * @param parentInjector - The dependency injection container for the parent route. * @param invokeGetPrerenderParams - A flag indicating whether to invoke the `getPrerenderParams` function. @@ -401,6 +432,7 @@ function appendPreloadToMetadata( async function* handleSSGRoute( serverConfigRouteTree: RouteTree | undefined, redirectTo: string | undefined, + parentRoute: string, metadata: ServerConfigRouteTreeNodeMetadata, parentInjector: Injector, invokeGetPrerenderParams: boolean, @@ -420,7 +452,8 @@ async function* handleSSGRoute( } if (redirectTo !== undefined) { - meta.redirectTo = resolveRedirectTo(currentRoutePath, redirectTo); + meta.redirectTo = resolveRedirectTo(parentRoute, redirectTo); + meta.relativeRedirect = redirectTo[0] !== '/' ? true : undefined; } const isCatchAllRoute = CATCH_ALL_REGEXP.test(currentRoutePath); @@ -478,7 +511,10 @@ async function* handleSSGRoute( redirectTo: redirectTo === undefined ? undefined - : resolveRedirectTo(routeWithResolvedParams, redirectTo), + : resolveRedirectTo( + parentRoute.replace(URL_PARAMETER_GLOBAL_REGEXP, replacer), + redirectTo, + ), }; } } catch (error) { @@ -533,24 +569,24 @@ function handlePrerenderParamsReplacement( * Resolves the `redirectTo` property for a given route. * * This function processes the `redirectTo` property to ensure that it correctly - * resolves relative to the current route path. If `redirectTo` is an absolute path, - * it is returned as is. If it is a relative path, it is resolved based on the current route path. + * resolves relative to the parent route path. If `redirectTo` is an absolute path, + * it is returned as is. If it is a relative path, it is resolved based on the parent route path, + * and, like in the Angular router, its query and fragment are ignored. * - * @param routePath - The current route path. + * @param parentRoute - The path of the parent route. * @param redirectTo - The target path for redirection. * @returns The resolved redirect path as a string. */ -function resolveRedirectTo(routePath: string, redirectTo: string): string { +function resolveRedirectTo(parentRoute: string, redirectTo: string): string { if (redirectTo[0] === '/') { // If the redirectTo path is absolute, return it as is. return redirectTo; } - // Resolve relative redirectTo based on the current route path. - const segments = routePath.replace(URL_PARAMETER_GLOBAL_REGEXP, '*').split('/'); - segments.pop(); // Remove the last segment to make it relative. + // Match Angular router behavior: replace all segments matched by the route. + const [path] = redirectTo.split(/[?#]/, 1); - return joinUrlParts(...segments, redirectTo); + return joinUrlParts(parentRoute.replace(URL_PARAMETER_GLOBAL_REGEXP, '*'), path); } /** diff --git a/packages/angular/ssr/src/routes/route-tree.ts b/packages/angular/ssr/src/routes/route-tree.ts index ff689a3c90f5..253783a5f13c 100644 --- a/packages/angular/ssr/src/routes/route-tree.ts +++ b/packages/angular/ssr/src/routes/route-tree.ts @@ -31,6 +31,9 @@ export interface RouteTreeNodeMetadata { */ redirectTo?: string; + /** Indicates if `redirectTo` was defined as a relative path. */ + relativeRedirect?: boolean; + /** * The route path for this node. * diff --git a/packages/angular/ssr/test/app_spec.ts b/packages/angular/ssr/test/app_spec.ts index b317f99f1d3a..8d8ee9add00b 100644 --- a/packages/angular/ssr/test/app_spec.ts +++ b/packages/angular/ssr/test/app_spec.ts @@ -88,6 +88,8 @@ describe('AngularServerApp', () => { { path: 'redirect/relative', redirectTo: 'home' }, { path: 'redirect/:param/relative', redirectTo: 'home' }, { path: 'redirect/absolute', redirectTo: '/home' }, + { path: 'redirect-with-query', redirectTo: 'home?param=value' }, + { path: 'redirect-nested/:param', children: [{ path: 'old', redirectTo: 'new' }] }, { path: 'redirect-to-function', redirectTo: () => 'home', @@ -204,24 +206,52 @@ describe('AngularServerApp', () => { expect(response?.status).toBe(302); }); - it('should correctly handle relative nested redirects', async () => { + it('should correctly handle relative redirects on multi-segment routes', async () => { const response = await app.handle(new Request('http://localhost/redirect/relative')); - expect(response?.headers.get('location')).toBe('/redirect/home'); + expect(response?.headers.get('location')).toBe('/home'); expect(response?.status).toBe(302); }); - it('should correctly handle relative nested redirects with parameter', async () => { + it('should correctly handle relative redirects on multi-segment routes with parameter', async () => { const response = await app.handle(new Request('http://localhost/redirect/param/relative')); - expect(response?.headers.get('location')).toBe('/redirect/param/home'); + expect(response?.headers.get('location')).toBe('/home'); expect(response?.status).toBe(302); }); - it('should correctly handle absolute nested redirects', async () => { + it('should correctly handle relative redirects of parameterized parent routes', async () => { + const response = await app.handle(new Request('http://localhost/redirect-nested/x/old')); + expect(response?.headers.get('location')).toBe('/redirect-nested/x/new'); + expect(response?.status).toBe(302); + }); + + it('should correctly handle absolute redirects on multi-segment routes', async () => { const response = await app.handle(new Request('http://localhost/redirect/absolute')); expect(response?.headers.get('location')).toBe('/home'); expect(response?.status).toBe(302); }); + it('should preserve query parameters on relative redirects', async () => { + const response = await app.handle(new Request('http://localhost/redirect?filter=test')); + expect(response?.headers.get('location')).toBe('/home?filter=test'); + expect(response?.status).toBe(302); + }); + + it('should ignore query parameters of relative redirect targets', async () => { + const response = await app.handle( + new Request('http://localhost/redirect-with-query?filter=test'), + ); + expect(response?.headers.get('location')).toBe('/home?filter=test'); + expect(response?.status).toBe(302); + }); + + it('should not preserve query parameters on absolute redirects', async () => { + const response = await app.handle( + new Request('http://localhost/redirect/absolute?filter=test'), + ); + expect(response?.headers.get('location')).toBe('/home'); + expect(response?.status).toBe(302); + }); + it('should handle request abortion gracefully', async () => { const controller = new AbortController(); const request = new Request('http://localhost/home', { signal: controller.signal }); @@ -498,6 +528,16 @@ describe('AngularServerApp', () => { expect(response?.headers.get('location')).toBe('/base/home'); expect(response?.status).toBe(302); }); + + it('returns a 302 status and keeps the query string for relative redirects', async () => { + const response = await app.handle( + new Request('http://localhost/redirect?filter=test', { + headers, + }), + ); + expect(response?.headers.get('location')).toBe('/base/home?filter=test'); + expect(response?.status).toBe(302); + }); }); describe('Critical CSS inlining', () => { diff --git a/packages/angular/ssr/test/routes/ng-routes_spec.ts b/packages/angular/ssr/test/routes/ng-routes_spec.ts index e214c31eab71..93b8e446ccb4 100644 --- a/packages/angular/ssr/test/routes/ng-routes_spec.ts +++ b/packages/angular/ssr/test/routes/ng-routes_spec.ts @@ -51,7 +51,13 @@ describe('extractRoutesAndCreateRouteTree', () => { expect(routeTree.toObject()).toEqual([ { route: '/', renderMode: RenderMode.Server }, { route: '/home', renderMode: RenderMode.Client }, - { route: '/redirect', renderMode: RenderMode.Server, status: 301, redirectTo: '/home' }, + { + route: '/redirect', + renderMode: RenderMode.Server, + status: 301, + redirectTo: '/home', + relativeRedirect: true, + }, { route: '/user/*', renderMode: RenderMode.Server }, ]); }); @@ -371,13 +377,118 @@ describe('extractRoutesAndCreateRouteTree', () => { expect(errors).toHaveSize(0); expect(routeTree.toObject()).toEqual([ - { route: '/', renderMode: RenderMode.Prerender, redirectTo: '/some' }, - { route: '/some', renderMode: RenderMode.Prerender, redirectTo: '/some/thing' }, + { + route: '/', + renderMode: RenderMode.Prerender, + redirectTo: '/some', + relativeRedirect: true, + }, + { + route: '/some', + renderMode: RenderMode.Prerender, + redirectTo: '/some/thing', + relativeRedirect: true, + }, { route: '/some/thing', renderMode: RenderMode.Prerender }, - { redirectTo: '/*/thing', route: '/*', renderMode: RenderMode.Server }, + { + redirectTo: '/*/thing', + relativeRedirect: true, + route: '/*', + renderMode: RenderMode.Server, + }, { route: '/*/thing', renderMode: RenderMode.Server }, ]); }); + + it('should resolve multi-segment relative redirects of prerendered routes', async () => { + setAngularAppTestingManifest( + [ + { + path: ':param', + children: [ + { path: 'old/page', redirectTo: 'new/page' }, + { path: 'new/page', component: DummyComponent }, + ], + }, + ], + [ + { + path: ':param/old/page', + renderMode: RenderMode.Prerender, + async getPrerenderParams() { + return [{ param: 'some' }]; + }, + }, + { path: '**', renderMode: RenderMode.Server }, + ], + ); + + const { routeTree, errors } = await extractRoutesAndCreateRouteTree({ + url, + invokeGetPrerenderParams: true, + includePrerenderFallbackRoutes: true, + }); + + expect(errors).toHaveSize(0); + expect(routeTree.toObject()).toEqual([ + { route: '/*', renderMode: RenderMode.Server }, + { + route: '/*/old/page', + renderMode: RenderMode.Server, + redirectTo: '/*/new/page', + relativeRedirect: true, + }, + { route: '/*/new/page', renderMode: RenderMode.Server }, + { + route: '/some/old/page', + renderMode: RenderMode.Prerender, + redirectTo: '/some/new/page', + relativeRedirect: true, + }, + ]); + }); + + it('should resolve relative redirects of prerendered catch-all routes', async () => { + setAngularAppTestingManifest( + [ + { path: 'docs/**', redirectTo: 'new' }, + { path: 'new', component: DummyComponent }, + ], + [ + { + path: 'docs/**', + renderMode: RenderMode.Prerender, + async getPrerenderParams() { + return [{ '**': 'a/b' }]; + }, + }, + { path: '**', renderMode: RenderMode.Server }, + ], + ); + + const { routeTree, errors } = await extractRoutesAndCreateRouteTree({ + url, + invokeGetPrerenderParams: true, + includePrerenderFallbackRoutes: true, + }); + + expect(errors).toHaveSize(0); + expect(routeTree.toObject()).toEqual([ + { + route: '/docs/a/b', + renderMode: RenderMode.Prerender, + redirectTo: '/new', + relativeRedirect: true, + }, + { + route: '/docs/**', + renderMode: RenderMode.Server, + redirectTo: '/new', + relativeRedirect: true, + }, + { route: '/new', renderMode: RenderMode.Server }, + ]); + }); }); it('should extract routes with a route level matcher', async () => { @@ -496,8 +607,13 @@ describe('extractRoutesAndCreateRouteTree', () => { const { routeTree, errors } = await extractRoutesAndCreateRouteTree({ url }); expect(errors).toHaveSize(0); expect(routeTree.toObject()).toEqual([ - { route: '/', renderMode: RenderMode.Server, redirectTo: '/some' }, - { route: '/*', renderMode: RenderMode.Server, redirectTo: '/*/thing' }, + { route: '/', renderMode: RenderMode.Server, redirectTo: '/some', relativeRedirect: true }, + { + route: '/*', + renderMode: RenderMode.Server, + redirectTo: '/*/thing', + relativeRedirect: true, + }, { route: '/*/thing', renderMode: RenderMode.Server }, ]); }); @@ -526,11 +642,98 @@ describe('extractRoutesAndCreateRouteTree', () => { const { routeTree, errors } = await extractRoutesAndCreateRouteTree({ url }); expect(errors).toHaveSize(0); expect(routeTree.toObject()).toEqual([ - { route: '/*/*', renderMode: RenderMode.Server, redirectTo: '/*/*/thing' }, + { + route: '/*/*', + renderMode: RenderMode.Server, + redirectTo: '/*/*/thing', + relativeRedirect: true, + }, { route: '/*/*/thing', renderMode: RenderMode.Server }, ]); }); + it('should resolve multi-segment relative redirects against the parent route', async () => { + setAngularAppTestingManifest( + [ + { + path: ':param', + children: [ + { path: 'old/page', redirectTo: 'new/page' }, + { path: 'new/page', component: DummyComponent }, + ], + }, + ], + [{ path: '**', renderMode: RenderMode.Server }], + ); + + const { routeTree, errors } = await extractRoutesAndCreateRouteTree({ url }); + expect(errors).toHaveSize(0); + expect(routeTree.toObject()).toEqual([ + { route: '/*', renderMode: RenderMode.Server }, + { + route: '/*/old/page', + renderMode: RenderMode.Server, + redirectTo: '/*/new/page', + relativeRedirect: true, + }, + { route: '/*/new/page', renderMode: RenderMode.Server }, + ]); + }); + + it('should not mark absolute redirects as relative', async () => { + setAngularAppTestingManifest( + [ + { path: 'server', redirectTo: '/home' }, + { path: 'prerender', redirectTo: '/home' }, + { path: 'home', component: DummyComponent }, + ], + [ + { path: 'prerender', renderMode: RenderMode.Prerender }, + { path: '**', renderMode: RenderMode.Server }, + ], + ); + + const { routeTree, errors } = await extractRoutesAndCreateRouteTree({ url }); + expect(errors).toHaveSize(0); + expect(routeTree.toObject()).toEqual([ + { route: '/server', renderMode: RenderMode.Server, redirectTo: '/home' }, + { route: '/prerender', renderMode: RenderMode.Prerender, redirectTo: '/home' }, + { route: '/home', renderMode: RenderMode.Server }, + ]); + }); + + it('should ignore the query and fragment of relative redirect targets', async () => { + setAngularAppTestingManifest( + [ + { path: 'server', redirectTo: 'home?param=value#fragment' }, + { path: 'prerender', redirectTo: 'home?param=value#fragment' }, + { path: 'home', component: DummyComponent }, + ], + [ + { path: 'prerender', renderMode: RenderMode.Prerender }, + { path: '**', renderMode: RenderMode.Server }, + ], + ); + + const { routeTree, errors } = await extractRoutesAndCreateRouteTree({ url }); + expect(errors).toHaveSize(0); + expect(routeTree.toObject()).toEqual([ + { + route: '/server', + renderMode: RenderMode.Server, + redirectTo: '/home', + relativeRedirect: true, + }, + { + route: '/prerender', + renderMode: RenderMode.Prerender, + redirectTo: '/home', + relativeRedirect: true, + }, + { route: '/home', renderMode: RenderMode.Server }, + ]); + }); + it('should not resolve parameterized routes for SSG when `invokeGetPrerenderParams` is false', async () => { setAngularAppTestingManifest( [ @@ -841,4 +1044,95 @@ describe('extractRoutesAndCreateRouteTree', () => { expect(isDiscoveringRoutes).toBeTrue(); }); + + it('should preserve preload metadata for routes under nested empty-path routes', async () => { + setAngularAppTestingManifest( + [ + { + path: '', + children: [ + { + path: '', + pathMatch: 'full', + loadChildren: () => [ + { + path: '', + loadComponent: () => DummyComponent, + ɵentryName: 'home-page', + } as Route, + ], + ɵentryName: 'home-routes', + } as Route, + { + path: '', + children: [ + { + path: 'pricing', + loadChildren: () => [ + { + path: '', + loadComponent: () => DummyComponent, + ɵentryName: 'pricing-page', + } as Route, + ], + ɵentryName: 'pricing-routes', + } as Route, + ], + }, + ], + }, + { + path: 'es', + children: [ + { + path: '', + pathMatch: 'full', + loadChildren: () => [ + { + path: '', + loadComponent: () => DummyComponent, + ɵentryName: 'home-page', + } as Route, + ], + ɵentryName: 'home-routes', + } as Route, + ], + }, + ], + [{ path: '**', renderMode: RenderMode.Prerender }], + undefined, + undefined, + undefined, + undefined, + undefined, + undefined, + undefined, + { + 'home-routes': ['chunk-home-routes.js'], + 'home-page': ['chunk-home-page.js'], + 'pricing-routes': ['chunk-pricing-routes.js'], + 'pricing-page': ['chunk-pricing-page.js'], + }, + ); + + const { routeTree, errors } = await extractRoutesAndCreateRouteTree({ url }); + expect(errors).toHaveSize(0); + expect(routeTree.toObject()).toEqual([ + { + route: '/', + renderMode: RenderMode.Prerender, + preload: ['chunk-home-routes.js', 'chunk-home-page.js'], + }, + { + route: '/pricing', + renderMode: RenderMode.Prerender, + preload: ['chunk-pricing-routes.js', 'chunk-pricing-page.js'], + }, + { + route: '/es', + renderMode: RenderMode.Prerender, + preload: ['chunk-home-routes.js', 'chunk-home-page.js'], + }, + ]); + }); }); diff --git a/packages/angular/ssr/test/routes/router_spec.ts b/packages/angular/ssr/test/routes/router_spec.ts index 380bc659549a..95006eda5f5b 100644 --- a/packages/angular/ssr/test/routes/router_spec.ts +++ b/packages/angular/ssr/test/routes/router_spec.ts @@ -55,6 +55,7 @@ describe('ServerRouter', () => { }); expect(router.match(new URL('http://localhost/redirect'))).toEqual({ redirectTo: '/home', + relativeRedirect: true, route: '/redirect', renderMode: RenderMode.Server, status: 301, @@ -89,6 +90,7 @@ describe('ServerRouter', () => { }); expect(redirectMetadata).toEqual({ redirectTo: '/home', + relativeRedirect: true, route: '/redirect', status: 301, renderMode: RenderMode.Server, @@ -110,6 +112,7 @@ describe('ServerRouter', () => { }); expect(redirectMetadata).toEqual({ redirectTo: '/home', + relativeRedirect: true, route: '/redirect', status: 301, renderMode: RenderMode.Server, diff --git a/packages/angular/ssr/test/testing-utils.ts b/packages/angular/ssr/test/testing-utils.ts index 6f825507c7a3..dad622d2e802 100644 --- a/packages/angular/ssr/test/testing-utils.ts +++ b/packages/angular/ssr/test/testing-utils.ts @@ -17,7 +17,7 @@ import { bootstrapApplication } from '@angular/platform-browser'; import { RouterOutlet, Routes, provideRouter } from '@angular/router'; import { type CompactPlan, compileSheet, encodePlan } from 'beasties/compiler'; import { destroyAngularServerApp } from '../src/app'; -import { ServerAsset, setAngularAppManifest } from '../src/manifest'; +import { AngularAppManifest, ServerAsset, setAngularAppManifest } from '../src/manifest'; import { ServerRoute, provideServerRendering, withRoutes } from '../src/routes/route-config'; @Component({ @@ -44,6 +44,7 @@ class AppComponent {} * root component and all its children. * @param inlineCriticalCss - An optional record of stylesheet paths to CSS content to be compiled into critical CSS plans. * @param nonce - An optional Content Security Policy (CSP) nonce to be used for inlined critical CSS. + * @param entryPointToBrowserMapping - An optional mapping of entry-point names to their associated browser bundles. */ export function setAngularAppTestingManifest( routes: Routes, @@ -55,6 +56,7 @@ export function setAngularAppTestingManifest( extraProviders: Array = [], inlineCriticalCss?: Record, nonce?: string, + entryPointToBrowserMapping?: AngularAppManifest['entryPointToBrowserMapping'], ): void { destroyAngularServerApp(); @@ -70,6 +72,7 @@ export function setAngularAppTestingManifest( setAngularAppManifest({ criticalCssPlans, nonce, + entryPointToBrowserMapping, baseHref, locale, assets: { diff --git a/packages/angular_devkit/build_angular/package.json b/packages/angular_devkit/build_angular/package.json index 6976448a01c0..54a44f134b16 100644 --- a/packages/angular_devkit/build_angular/package.json +++ b/packages/angular_devkit/build_angular/package.json @@ -25,7 +25,7 @@ "ansi-colors": "4.1.3", "autoprefixer": "10.6.1", "babel-loader": "10.1.1", - "beasties": "0.5.4", + "beasties": "0.5.5", "browserslist": "^4.26.0", "copy-webpack-plugin": "14.0.0", "css-loader": "7.1.5", @@ -43,7 +43,7 @@ "ora": "9.4.1", "picomatch": "4.0.7", "piscina": "5.3.2", - "postcss": "8.5.29", + "postcss": "8.5.30", "postcss-loader": "8.2.1", "resolve-url-loader": "5.0.0", "rxjs": "7.8.2", @@ -68,7 +68,7 @@ "browser-sync": "3.0.4", "ng-packagr": "22.3.0-next.2", "sass": "1.105.1", - "undici": "8.11.2" + "undici": "8.12.1" }, "peerDependencies": { "@angular/compiler-cli": "0.0.0-ANGULAR-FW-PEER-DEP", diff --git a/packages/angular_devkit/build_angular/src/builders/extract-i18n/application-extraction.ts b/packages/angular_devkit/build_angular/src/builders/extract-i18n/application-extraction.ts index 8de8b6863aaf..508b27e2d833 100644 --- a/packages/angular_devkit/build_angular/src/builders/extract-i18n/application-extraction.ts +++ b/packages/angular_devkit/build_angular/src/builders/extract-i18n/application-extraction.ts @@ -45,6 +45,7 @@ export async function extractMessages( ); } + buildOptions.aot = true; buildOptions.optimization = false; buildOptions.sourceMap = { scripts: true, vendor: true, styles: false }; buildOptions.localize = false; @@ -111,7 +112,7 @@ function setupLocalizeExtractor( if (file?.origin === 'memory') { content = textDecoder.decode(file.contents); } else if (file?.origin === 'disk') { - content = readFileSync(file.inputPath, 'utf-8'); + content = readFileSync(file.source, 'utf-8'); } if (content === undefined) { throw new Error('Unknown file requested: ' + requestedPath); diff --git a/packages/angular_devkit/schematics/src/rules/base.ts b/packages/angular_devkit/schematics/src/rules/base.ts index ec380f2aeb0d..1f5d3023c28e 100644 --- a/packages/angular_devkit/schematics/src/rules/base.ts +++ b/packages/angular_devkit/schematics/src/rules/base.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.dev/license */ -import { Observable, concat, map, mapTo, toArray } from 'rxjs'; +import { concat, lastValueFrom, map, mapTo, toArray } from 'rxjs'; import { FileOperator, Rule, Source } from '../engine/interface'; import { SchematicsException } from '../exception/exception'; import { FilterHostTree, HostTree } from '../tree/host-tree'; @@ -34,18 +34,18 @@ export function empty(): Source { */ export function chain(rules: Iterable | AsyncIterable): Rule { return async (initialTree, context) => { - let intermediateTree: Observable | undefined; + let intermediateTree = initialTree; if (Symbol.asyncIterator in rules) { for await (const rule of rules) { - intermediateTree = callRule(rule, intermediateTree ?? initialTree, context); + intermediateTree = await lastValueFrom(callRule(rule, intermediateTree, context)); } } else { for (const rule of rules) { - intermediateTree = callRule(rule, intermediateTree ?? initialTree, context); + intermediateTree = await lastValueFrom(callRule(rule, intermediateTree, context)); } } - return () => intermediateTree; + return intermediateTree; }; } diff --git a/packages/angular_devkit/schematics/src/rules/base_spec.ts b/packages/angular_devkit/schematics/src/rules/base_spec.ts index b3578ca8c00a..6eb299ef9890 100644 --- a/packages/angular_devkit/schematics/src/rules/base_spec.ts +++ b/packages/angular_devkit/schematics/src/rules/base_spec.ts @@ -140,6 +140,19 @@ describe('chain', () => { expect(rulesCalled[2]).toBe(tree2); expect(result).toBe(tree3); }); + + it('works with a large number of rules', async () => { + const tree0 = empty(); + let callCount = 0; + const rules: Rule[] = Array.from({ length: 20_000 }, () => () => { + callCount++; + }); + + const result = await lastValueFrom(callRule(chain(rules), tree0, context)); + + expect(result).toBe(tree0); + expect(callCount).toBe(20_000); + }); }); describe('apply', () => { diff --git a/packages/ngtools/webpack/package.json b/packages/ngtools/webpack/package.json index a7488b0d9316..13cea96fc202 100644 --- a/packages/ngtools/webpack/package.json +++ b/packages/ngtools/webpack/package.json @@ -17,8 +17,8 @@ }, "devDependencies": { "@angular-devkit/core": "workspace:0.0.0-PLACEHOLDER", - "@angular/compiler": "22.3.0-next.1", - "@angular/compiler-cli": "22.3.0-next.1", + "@angular/compiler": "22.3.0-next.2", + "@angular/compiler-cli": "22.3.0-next.2", "typescript": "6.0.3", "webpack": "5.111.1" } diff --git a/packages/schematics/angular/library/schema.json b/packages/schematics/angular/library/schema.json index bb3d227e5245..02c6c6f8acaa 100644 --- a/packages/schematics/angular/library/schema.json +++ b/packages/schematics/angular/library/schema.json @@ -19,7 +19,7 @@ "entryFile": { "type": "string", "format": "path", - "description": "The path to the library's public API file, relative to the workspace root. This file defines what parts of the library are accessible to applications that import it.", + "description": "The name of the library's public API file, without the `.ts` extension, created in the library's `src` directory. This file defines what parts of the library are accessible to applications that import it.", "default": "public-api" }, "prefix": { diff --git a/packages/schematics/angular/migrations/migration-collection.json b/packages/schematics/angular/migrations/migration-collection.json index db1b95f987ef..9147e229b97c 100644 --- a/packages/schematics/angular/migrations/migration-collection.json +++ b/packages/schematics/angular/migrations/migration-collection.json @@ -29,6 +29,13 @@ "version": "22.0.0", "factory": "./update-workspace-config/migration", "description": "Update the angular workspace configuration." + }, + "use-library-builder": { + "version": "22.3.0", + "factory": "./use-library-builder/migration", + "description": "Migrate library projects from 'ng-packagr' to the new '@angular/build:library' builder.", + "optional": true, + "recommended": false } } } diff --git a/packages/schematics/angular/migrations/use-application-builder/migration.ts b/packages/schematics/angular/migrations/use-application-builder/migration.ts index b481c4f30034..dcf0526a3945 100644 --- a/packages/schematics/angular/migrations/use-application-builder/migration.ts +++ b/packages/schematics/angular/migrations/use-application-builder/migration.ts @@ -19,6 +19,7 @@ import { DependencyType, ExistingBehavior, addDependency, + getDependency, removeDependency, } from '../../utility/dependency'; import { JSONFile } from '../../utility/json-file'; @@ -270,10 +271,14 @@ function updateProjects(tree: Tree, context: SchematicContext) { } // Add direct @angular/build dependencies and remove @angular-devkit/build-angular + // Keep the workspace version since this migration can run from a newer temporary CLI + const buildAngularVersion = + getDependency(tree, '@angular-devkit/build-angular')?.version ?? + latestVersions.AngularBuild; rules.push( - addDependency('@angular/build', latestVersions.DevkitBuildAngular, { + addDependency('@angular/build', buildAngularVersion, { type: DependencyType.Dev, - existing: ExistingBehavior.Replace, + existing: ExistingBehavior.Skip, }), removeDependency('@angular-devkit/build-angular'), ); 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 fd10352c6eac..05259962dc5d 100644 --- a/packages/schematics/angular/migrations/use-application-builder/migration_spec.ts +++ b/packages/schematics/angular/migrations/use-application-builder/migration_spec.ts @@ -9,6 +9,7 @@ import { JsonObject } from '@angular-devkit/core'; import { EmptyTree } from '@angular-devkit/schematics'; import { SchematicTestRunner, UnitTestTree } from '@angular-devkit/schematics/testing'; +import { latestVersions } from '../../utility/latest-versions'; import { Builders, ProjectType, WorkspaceSchema } from '../../utility/workspace-models'; function createWorkSpaceConfig(tree: UnitTestTree) { @@ -450,6 +451,47 @@ describe(`Migration to use the application builder`, () => { expect(devDependencies['postcss']).toBeUndefined(); }); + describe('"@angular/build" version', () => { + async function migrateWith( + manifest: object, + ): Promise<{ devDependencies: Record }> { + tree.overwrite('/package.json', JSON.stringify(manifest)); + const newTree = await schematicRunner.runSchematic(schematicName, {}, tree); + + return JSON.parse(newTree.readContent('/package.json')); + } + + it('should follow the "@angular-devkit/build-angular" range of the workspace', async () => { + const { devDependencies } = await migrateWith({ + devDependencies: { '@angular-devkit/build-angular': '~19.1.4' }, + }); + + expect(devDependencies['@angular/build']).toBe('~19.1.4'); + }); + + it('should read "@angular-devkit/build-angular" from "dependencies" as well', async () => { + const { devDependencies } = await migrateWith({ + dependencies: { '@angular-devkit/build-angular': '18.2.1' }, + }); + + expect(devDependencies['@angular/build']).toBe('18.2.1'); + }); + + it('should keep an "@angular/build" that is already installed', async () => { + const { devDependencies } = await migrateWith({ + devDependencies: { '@angular-devkit/build-angular': '^19.2.0', '@angular/build': '19.0.3' }, + }); + + expect(devDependencies['@angular/build']).toBe('19.0.3'); + }); + + it('should fall back to the latest "@angular/build" without "@angular-devkit/build-angular"', async () => { + const { devDependencies } = await migrateWith({}); + + expect(devDependencies['@angular/build']).toBe(latestVersions.AngularBuild); + }); + }); + it('it should not add esModuleInterop and moduleResolution when module is preserve', async () => { tree.overwrite( 'tsconfig.json', diff --git a/packages/schematics/angular/migrations/use-library-builder/migration.ts b/packages/schematics/angular/migrations/use-library-builder/migration.ts new file mode 100644 index 000000000000..68f9e8738e25 --- /dev/null +++ b/packages/schematics/angular/migrations/use-library-builder/migration.ts @@ -0,0 +1,587 @@ +/** + * @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 JsonObject, type JsonValue, isJsonObject } from '@angular-devkit/core'; +import { + type Rule, + type SchematicContext, + SchematicsException, + type Tree, + chain, +} from '@angular-devkit/schematics'; +import { dirname, join, normalize, relative } from 'node:path/posix'; +import { + DependencyType, + ExistingBehavior, + addDependency, + removeDependency, +} from '../../utility/dependency'; +import { JSONFile } from '../../utility/json-file'; +import { latestVersions } from '../../utility/latest-versions'; +import { + type TargetDefinition, + allTargetOptions, + allWorkspaceTargets, + updateWorkspace, +} from '../../utility/workspace'; +import { Builders, ProjectType } from '../../utility/workspace-models'; + +/** + * Represents the configuration structure of an `ng-package.json` file or + * the `ngPackage` section within a `package.json` file. + */ +interface NgPackageConfig { + dest?: string; + assets?: (string | { glob: string; input: string; output: string })[]; + allowedNonPeerDependencies?: string[]; + inlineStyleLanguage?: string; + keepLifecycleScripts?: boolean; + deleteDestPath?: boolean; + lib?: { + entryFile?: string; + styleIncludePaths?: string[]; + sass?: { + fatalDeprecations?: string[]; + silenceDeprecations?: string[]; + futureDeprecations?: string[]; + }; + }; +} + +/** + * Metadata describing a discovered secondary entry point in a library project. + */ +interface SecondaryEntryPoint { + subpath: string; + entryPoint: string; + configFile: string; +} + +/** + * Normalizes a workspace-relative POSIX path, converting `'.'` or `'/'` to `''`. + * + * @param path The path string to normalize. + * @returns The normalized workspace-relative path. + */ +function normalizeRelativePath(path: string): string { + const normalized = normalize(path).replace(/^\/+|\/+$/g, ''); + + return normalized === '.' ? '' : normalized; +} + +/** + * Reads and parses an `ng-packagr` configuration from either an `ng-package.json` + * file or the `ngPackage` property of a `package.json` file. + * + * @param tree The virtual file system tree. + * @param filePath The workspace-relative path to the configuration file. + * @returns The parsed `NgPackageConfig`, or `undefined` if the file does not exist or cannot be parsed. + */ +function parseNgPackageFile(tree: Tree, filePath: string): NgPackageConfig | undefined { + if (!tree.exists(filePath)) { + return undefined; + } + + try { + const json = new JSONFile(tree, filePath); + if (filePath.endsWith('/package.json') || filePath === 'package.json') { + const ngPackage = json.get(['ngPackage']) as JsonValue; + + return isJsonObject(ngPackage) ? (ngPackage as NgPackageConfig) : undefined; + } + + const content = json.get([]) as JsonValue; + + return isJsonObject(content) ? (content as NgPackageConfig) : undefined; + } catch { + return undefined; + } +} + +/** + * Resolves the TypeScript entry file path for a library entry point directory. + * Uses the explicitly configured `entryFile` when provided, or checks common + * default locations. + * + * @param tree The virtual file system tree. + * @param dir The directory of the primary or secondary entry point. + * @param configuredEntry The optional `lib.entryFile` path from `ng-package.json`. + * @returns The resolved entry file path relative to `dir`. + */ +function resolveEntryFile(tree: Tree, dir: string, configuredEntry?: string): string { + if (configuredEntry) { + return configuredEntry; + } + + const candidates = [ + 'src/public-api.ts', + 'src/public_api.ts', + 'public-api.ts', + 'public_api.ts', + 'src/index.ts', + 'index.ts', + ]; + + for (const candidate of candidates) { + if (tree.exists(join(dir, candidate))) { + return candidate; + } + } + + throw new SchematicsException( + `Could not find an entry file for library entry point at "${dir || '.'}".`, + ); +} + +/** + * Traverses a library project directory to discover all secondary entry points + * defined by nested `ng-package.json` files or `package.json` files containing an `ngPackage` field. + * + * @param tree The virtual file system tree. + * @param projectRoot The root directory of the library project. + * @param primaryProjectFile The workspace-relative path to the primary `ng-packagr` configuration file. + * @returns An array of discovered secondary entry points. + */ +function findSecondaryEntryPoints( + tree: Tree, + projectRoot: string, + primaryProjectFile: string, +): SecondaryEntryPoint[] { + const secondaryEntryPoints: SecondaryEntryPoint[] = []; + const normalizedRoot = normalizeRelativePath(projectRoot); + const normalizedPrimary = primaryProjectFile ? normalizeRelativePath(primaryProjectFile) : ''; + const rootDir = tree.getDir(normalizedRoot); + const directories = [rootDir]; + + for (const current of directories) { + const currentPath = normalizeRelativePath(current.path); + + if (currentPath !== normalizedRoot) { + const ngPackageJsonPath = join(currentPath, 'ng-package.json'); + const packageJsonPath = join(currentPath, 'package.json'); + + if ( + current.subfiles.some((f) => f === 'ng-package.json') && + ngPackageJsonPath !== normalizedPrimary + ) { + const config = parseNgPackageFile(tree, ngPackageJsonPath); + const subpath = relative(normalizedRoot, currentPath); + const entryFile = resolveEntryFile(tree, currentPath, config?.lib?.entryFile); + secondaryEntryPoints.push({ + subpath, + entryPoint: join(currentPath, entryFile), + configFile: ngPackageJsonPath, + }); + } else if ( + current.subfiles.some((f) => f === 'package.json') && + packageJsonPath !== normalizedPrimary + ) { + const config = parseNgPackageFile(tree, packageJsonPath); + if (config) { + const subpath = relative(normalizedRoot, currentPath); + const entryFile = resolveEntryFile(tree, currentPath, config.lib?.entryFile); + secondaryEntryPoints.push({ + subpath, + entryPoint: join(currentPath, entryFile), + configFile: packageJsonPath, + }); + } + } + } + + for (const subdir of current.subdirs) { + if ( + subdir === 'node_modules' || + subdir === 'dist' || + subdir === 'out-tsc' || + subdir.startsWith('.') + ) { + continue; + } + directories.push(current.dir(subdir)); + } + } + + return secondaryEntryPoints; +} + +/** + * Transfers `ng-packagr` configuration options onto the library's `build` target in `angular.json`. + * + * @param projectRoot The normalized root directory of the library project. + * @param projectDir The directory containing the primary `ng-packagr` configuration file. + * @param buildTarget The library's `build` target definition in `angular.json`. + * @param ngPackageConfig The parsed `NgPackageConfig`, if available. + * @param tree The virtual file system tree. + */ +function migrateTargetOptions( + projectRoot: string, + projectDir: string, + buildTarget: TargetDefinition, + ngPackageConfig: NgPackageConfig | undefined, + tree: Tree, +): void { + buildTarget.builder = Builders.BuildLibrary; + buildTarget.options ??= {}; + + const devTsConfig = buildTarget.configurations?.['development']?.['tsConfig']; + if (typeof buildTarget.options['tsConfig'] !== 'string') { + if (typeof devTsConfig === 'string') { + buildTarget.options['tsConfig'] = devTsConfig; + delete buildTarget.configurations?.['development']?.['tsConfig']; + } else if (tree.exists(join(projectRoot, 'tsconfig.lib.json'))) { + buildTarget.options['tsConfig'] = join(projectRoot, 'tsconfig.lib.json'); + } + } else if (devTsConfig === buildTarget.options['tsConfig']) { + delete buildTarget.configurations?.['development']?.['tsConfig']; + } + + if (ngPackageConfig?.dest && typeof buildTarget.options['outputPath'] !== 'string') { + buildTarget.options['outputPath'] = join(projectDir, ngPackageConfig.dest); + } + + if (ngPackageConfig?.assets?.length) { + const existingAssets = Array.isArray(buildTarget.options['assets']) + ? buildTarget.options['assets'] + : []; + const migratedAssets = ngPackageConfig.assets.map((asset) => { + if (typeof asset === 'string') { + return join(projectDir, asset); + } + if (isJsonObject(asset) && typeof asset['input'] === 'string') { + return { + ...asset, + input: join(projectDir, asset['input']), + }; + } + + return asset; + }); + buildTarget.options['assets'] = [...existingAssets, ...migratedAssets]; + } + + if (ngPackageConfig?.lib?.styleIncludePaths?.length || ngPackageConfig?.lib?.sass) { + const stylePreprocessorOptions = buildTarget.options['stylePreprocessorOptions']; + const existingPreprocessorOptions: JsonObject = + stylePreprocessorOptions && isJsonObject(stylePreprocessorOptions) + ? { ...stylePreprocessorOptions } + : {}; + + if (ngPackageConfig.lib.styleIncludePaths?.length) { + const existingIncludePaths = Array.isArray(existingPreprocessorOptions['includePaths']) + ? (existingPreprocessorOptions['includePaths'] as string[]) + : []; + const migratedIncludePaths = ngPackageConfig.lib.styleIncludePaths.map((p) => + join(projectDir, p), + ); + existingPreprocessorOptions['includePaths'] = [ + ...existingIncludePaths, + ...migratedIncludePaths, + ]; + } + if (ngPackageConfig.lib.sass) { + existingPreprocessorOptions['sass'] = ngPackageConfig.lib.sass; + } + + buildTarget.options['stylePreprocessorOptions'] = existingPreprocessorOptions; + } + + if (ngPackageConfig?.allowedNonPeerDependencies?.length) { + buildTarget.options['allowedNonPeerDependencies'] = ngPackageConfig.allowedNonPeerDependencies; + } + if (ngPackageConfig?.inlineStyleLanguage) { + buildTarget.options['inlineStyleLanguage'] = ngPackageConfig.inlineStyleLanguage; + } + if (ngPackageConfig?.keepLifecycleScripts !== undefined) { + buildTarget.options['keepLifecycleScripts'] = ngPackageConfig.keepLifecycleScripts; + } + if (ngPackageConfig?.deleteDestPath !== undefined) { + buildTarget.options['deleteOutputPath'] = ngPackageConfig.deleteDestPath; + } + + for (const [, options] of allTargetOptions(buildTarget, false)) { + delete options['project']; + } + + // Configure development configuration with full compilation mode unless already specified in tsconfig + buildTarget.configurations ??= {}; + buildTarget.configurations['development'] ??= {}; + + const effectiveDevTsConfig = + typeof buildTarget.configurations['development']['tsConfig'] === 'string' + ? buildTarget.configurations['development']['tsConfig'] + : typeof buildTarget.options['tsConfig'] === 'string' + ? buildTarget.options['tsConfig'] + : undefined; + + let hasDevTsConfigCompilationMode = false; + if (effectiveDevTsConfig && tree.exists(effectiveDevTsConfig)) { + try { + const tsConfigJson = new JSONFile(tree, effectiveDevTsConfig); + hasDevTsConfigCompilationMode = + tsConfigJson.get(['angularCompilerOptions', 'compilationMode']) !== undefined; + } catch { + // Ignore parse errors + } + } + + if (!hasDevTsConfigCompilationMode) { + buildTarget.configurations['development']['compilationMode'] ??= 'full'; + } +} + +/** + * Migrates a single library project's `build` target from `ng-packagr` to `@angular/build:library`, + * transferring configuration options to `angular.json`, writing entry points to `package.json#exports`, + * and cleaning up obsolete `ng-packagr` configuration files. + * + * @param projectRoot The root directory of the library project. + * @param buildTarget The library's `build` target definition in `angular.json`. + * @param tree The virtual file system tree. + * @param context The schematic execution context. + * @returns `true` if the library target was migrated, or `false` if it was skipped. + */ +function updateLibraryTarget( + projectRoot: string, + buildTarget: TargetDefinition, + tree: Tree, + context: SchematicContext, +): boolean { + const normalizedProjectRoot = normalizeRelativePath(projectRoot); + let primaryProjectFile = + typeof buildTarget.options?.['project'] === 'string' + ? normalizeRelativePath(buildTarget.options['project']) + : ''; + if (!primaryProjectFile) { + for (const [, options] of allTargetOptions(buildTarget, true)) { + if (typeof options['project'] === 'string') { + primaryProjectFile = normalizeRelativePath(options['project']); + break; + } + } + } + if (!primaryProjectFile) { + const defaultNgPackageJson = join(normalizedProjectRoot, 'ng-package.json'); + const defaultPackageJson = join(normalizedProjectRoot, 'package.json'); + if (tree.exists(defaultNgPackageJson)) { + primaryProjectFile = defaultNgPackageJson; + } else if (tree.exists(defaultPackageJson)) { + primaryProjectFile = defaultPackageJson; + } + } + + if (/\.[cm]?js$/.test(primaryProjectFile)) { + context.logger.warn( + `Skipping project at "${projectRoot}" because JavaScript ng-packagr ` + + `configuration files ("${primaryProjectFile}") cannot be automatically migrated.`, + ); + + return false; + } + + const ngPackageConfig = primaryProjectFile + ? parseNgPackageFile(tree, primaryProjectFile) + : undefined; + + const projectDir = primaryProjectFile + ? normalizeRelativePath(dirname(primaryProjectFile)) + : normalizedProjectRoot; + const primaryEntryFile = resolveEntryFile(tree, projectDir, ngPackageConfig?.lib?.entryFile); + const primaryEntryFilePath = join(projectDir, primaryEntryFile); + const secondaryEntryPoints = findSecondaryEntryPoints( + tree, + normalizedProjectRoot, + primaryProjectFile, + ); + + const toRelativeExportPath = (filePath: string): string => { + const rel = relative(normalizedProjectRoot, filePath); + + return rel.startsWith('./') || rel.startsWith('../') ? rel : `./${rel}`; + }; + + const entryPointExports: Record = { + '.': toRelativeExportPath(primaryEntryFilePath), + }; + for (const secondary of secondaryEntryPoints) { + const exportKey = + secondary.subpath.startsWith('./') || secondary.subpath.startsWith('../') + ? secondary.subpath + : `./${secondary.subpath}`; + entryPointExports[exportKey] = toRelativeExportPath(secondary.entryPoint); + } + + migrateTargetOptions(normalizedProjectRoot, projectDir, buildTarget, ngPackageConfig, tree); + + const packageJsonPath = join(normalizedProjectRoot, 'package.json'); + + // Delete primary ng-package.json if it exists + if ( + primaryProjectFile && + primaryProjectFile.endsWith('ng-package.json') && + tree.exists(primaryProjectFile) + ) { + tree.delete(primaryProjectFile); + } else if ( + primaryProjectFile && + primaryProjectFile !== packageJsonPath && + (primaryProjectFile.endsWith('/package.json') || primaryProjectFile === 'package.json') && + tree.exists(primaryProjectFile) + ) { + const json = new JSONFile(tree, primaryProjectFile); + json.remove(['ngPackage']); + } + + // Update library package.json with exports and remove ngPackage + if (tree.exists(packageJsonPath)) { + updateLibraryPackageJson(tree, packageJsonPath, entryPointExports); + } + + // Delete secondary configuration files + for (const secondary of secondaryEntryPoints) { + if (secondary.configFile.endsWith('ng-package.json') && tree.exists(secondary.configFile)) { + tree.delete(secondary.configFile); + } else if ( + (secondary.configFile.endsWith('/package.json') || secondary.configFile === 'package.json') && + tree.exists(secondary.configFile) + ) { + const json = new JSONFile(tree, secondary.configFile); + json.remove(['ngPackage']); + const remaining = json.get([]) as JsonValue; + if ( + isJsonObject(remaining) && + Object.keys(remaining).filter((k) => k !== '$schema').length === 0 + ) { + tree.delete(secondary.configFile); + } + } + } + + context.logger.info( + `Updated project at "${projectRoot}" to use the new '@angular/build:library' builder with ${ + Object.keys(entryPointExports).length + } entry point(s).`, + ); + + return true; +} + +/** + * Updates the library's `package.json` file by removing the legacy `ngPackage` configuration + * and `tslib` dependency, and merging the discovered entry points into the `exports` map. + * + * @param tree The virtual file system tree. + * @param packageJsonPath The workspace-relative path to the library's `package.json`. + * @param entryPointExports Map of export subpaths to relative entry file paths. + */ +function updateLibraryPackageJson( + tree: Tree, + packageJsonPath: string, + entryPointExports: Record, +): void { + const json = new JSONFile(tree, packageJsonPath); + json.remove(['ngPackage']); + + for (const depType of [ + 'dependencies', + 'peerDependencies', + 'devDependencies', + 'optionalDependencies', + ]) { + json.remove([depType, 'tslib']); + const deps = json.get([depType]) as JsonValue; + if (isJsonObject(deps) && Object.keys(deps).length === 0) { + json.remove([depType]); + } + } + + const existingExports = json.get(['exports']) as JsonValue; + let existingExportsObj: Record = {}; + if (isJsonObject(existingExports)) { + const keys = Object.keys(existingExports); + if (keys.some((k) => k.startsWith('.'))) { + existingExportsObj = existingExports; + } else if (keys.length > 0) { + existingExportsObj = { '.': existingExports }; + } + } + + const updatedExports: Record = { + ...existingExportsObj, + }; + + for (const [subpath, targetPath] of Object.entries(entryPointExports)) { + const current = updatedExports[subpath]; + if (current && isJsonObject(current)) { + updatedExports[subpath] = { + ...current, + default: targetPath, + }; + } else { + updatedExports[subpath] = targetPath; + } + } + + json.modify(['exports'], updatedExports); +} + +/** + * Migration main entrypoint for use-library-builder. + */ +export default function (): Rule { + return (tree, context) => + updateWorkspace((workspace) => { + let hasMigratedLibrary = false; + + for (const [, project] of workspace.projects) { + if (project.extensions.projectType !== ProjectType.Library) { + continue; + } + + const buildTarget = project.targets.get('build'); + if ( + !buildTarget || + buildTarget.builder === Builders.BuildLibrary || + (buildTarget.builder !== Builders.NgPackagr && + buildTarget.builder !== Builders.BuildNgPackagr) + ) { + continue; + } + + if (updateLibraryTarget(project.root, buildTarget, tree, context)) { + hasMigratedLibrary = true; + } + } + + if (!hasMigratedLibrary) { + return; + } + + const rules: Rule[] = [ + addDependency('@angular/build', latestVersions.AngularBuild, { + type: DependencyType.Dev, + existing: ExistingBehavior.Skip, + }), + ]; + + // Check if any targets still use ng-packagr across the workspace + let hasNgPackagrUsage = false; + for (const [, target] of allWorkspaceTargets(workspace)) { + if (target.builder === Builders.NgPackagr || target.builder === Builders.BuildNgPackagr) { + hasNgPackagrUsage = true; + break; + } + } + + if (!hasNgPackagrUsage) { + rules.push(removeDependency('ng-packagr')); + } + + return chain(rules); + }); +} diff --git a/packages/schematics/angular/migrations/use-library-builder/migration_spec.ts b/packages/schematics/angular/migrations/use-library-builder/migration_spec.ts new file mode 100644 index 000000000000..11192f8e5897 --- /dev/null +++ b/packages/schematics/angular/migrations/use-library-builder/migration_spec.ts @@ -0,0 +1,430 @@ +/** + * @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 { EmptyTree } from '@angular-devkit/schematics'; +import { SchematicTestRunner, UnitTestTree } from '@angular-devkit/schematics/testing'; +import { Builders, ProjectType, type WorkspaceSchema } from '../../utility/workspace-models'; + +function createWorkspaceConfig(tree: UnitTestTree) { + const angularConfig: WorkspaceSchema = { + version: 1, + projects: { + 'my-lib': { + root: 'projects/my-lib', + sourceRoot: 'projects/my-lib/src', + projectType: ProjectType.Library, + prefix: 'lib', + architect: { + build: { + builder: Builders.NgPackagr, + options: { + project: 'projects/my-lib/ng-package.json', + tsConfig: 'projects/my-lib/tsconfig.lib.json', + }, + configurations: { + production: { + tsConfig: 'projects/my-lib/tsconfig.lib.prod.json', + }, + }, + }, + }, + }, + }, + }; + + tree.create('/angular.json', JSON.stringify(angularConfig, undefined, 2)); + tree.create( + '/package.json', + JSON.stringify( + { + devDependencies: { + 'ng-packagr': '^18.0.0', + }, + }, + undefined, + 2, + ), + ); + tree.create( + '/projects/my-lib/package.json', + JSON.stringify( + { + name: 'my-lib', + version: '0.0.1', + dependencies: { + tslib: '^2.3.0', + }, + }, + undefined, + 2, + ), + ); + tree.create( + '/projects/my-lib/ng-package.json', + JSON.stringify( + { + dest: '../../dist/my-lib', + lib: { + entryFile: 'src/public-api.ts', + }, + }, + undefined, + 2, + ), + ); +} + +describe('Migration to use the library builder', () => { + const schematicName = 'use-library-builder'; + const schematicRunner = new SchematicTestRunner( + 'migrations', + require.resolve('../migration-collection.json'), + ); + + let tree: UnitTestTree; + beforeEach(() => { + tree = new UnitTestTree(new EmptyTree()); + createWorkspaceConfig(tree); + }); + + it('should migrate library project using ng-packagr to @angular/build:library', async () => { + const newTree = await schematicRunner.runSchematic(schematicName, {}, tree); + const { + projects: { 'my-lib': lib }, + // eslint-disable-next-line @typescript-eslint/no-explicit-any + } = newTree.readJson('/angular.json') as any; + + const { builder, options, configurations } = lib.architect['build']; + expect(builder).toBe(Builders.BuildLibrary); + expect(options.project).toBeUndefined(); + expect(options.entryPoints).toBeUndefined(); + expect(options.tsConfig).toBe('projects/my-lib/tsconfig.lib.json'); + expect(options.outputPath).toBe('dist/my-lib'); + expect(configurations.development).toEqual({ + compilationMode: 'full', + }); + + const libPkg = newTree.readJson('/projects/my-lib/package.json') as { + dependencies: unknown; + exports: unknown; + }; + expect(libPkg.dependencies).toBeUndefined(); + expect(libPkg.exports).toEqual({ + '.': './src/public-api.ts', + }); + + expect(newTree.exists('/projects/my-lib/ng-package.json')).toBe(false); + }); + + it('should migrate @angular/build:ng-packagr and promote tsConfig from development configuration', async () => { + const angularJson = JSON.parse(tree.readContent('/angular.json')); + angularJson.projects['my-lib'].architect.build = { + builder: Builders.BuildNgPackagr, + configurations: { + production: { + tsConfig: 'projects/my-lib/tsconfig.lib.prod.json', + }, + development: { + tsConfig: 'projects/my-lib/tsconfig.lib.json', + }, + }, + }; + tree.overwrite('/angular.json', JSON.stringify(angularJson, undefined, 2)); + + const newTree = await schematicRunner.runSchematic(schematicName, {}, tree); + const { + projects: { 'my-lib': lib }, + // eslint-disable-next-line @typescript-eslint/no-explicit-any + } = newTree.readJson('/angular.json') as any; + + const { builder, options, configurations } = lib.architect['build']; + expect(builder).toBe(Builders.BuildLibrary); + expect(options.tsConfig).toBe('projects/my-lib/tsconfig.lib.json'); + expect(configurations.development).toEqual({ + compilationMode: 'full', + }); + }); + + it('should not set compilationMode in development configuration if already defined in tsconfig.lib.json', async () => { + tree.create( + '/projects/my-lib/tsconfig.lib.json', + JSON.stringify( + { + compilerOptions: { + declaration: true, + declarationMap: true, + }, + angularCompilerOptions: { + compilationMode: 'partial', + }, + }, + undefined, + 2, + ), + ); + + const newTree = await schematicRunner.runSchematic(schematicName, {}, tree); + const { + projects: { 'my-lib': lib }, + // eslint-disable-next-line @typescript-eslint/no-explicit-any + } = newTree.readJson('/angular.json') as any; + + const { configurations } = lib.architect['build']; + expect(configurations.development).toEqual({}); + }); + + it('should discover and migrate secondary entry points into package.json exports', async () => { + tree.create( + '/projects/my-lib/testing/ng-package.json', + JSON.stringify( + { + lib: { + entryFile: 'src/public-api.ts', + }, + }, + undefined, + 2, + ), + ); + tree.create( + '/projects/my-lib/schematics/package.json', + JSON.stringify( + { + ngPackage: { + lib: { + entryFile: 'index.ts', + }, + }, + }, + undefined, + 2, + ), + ); + + const newTree = await schematicRunner.runSchematic(schematicName, {}, tree); + const { + projects: { 'my-lib': lib }, + // eslint-disable-next-line @typescript-eslint/no-explicit-any + } = newTree.readJson('/angular.json') as any; + + const { options } = lib.architect['build']; + expect(options.entryPoints).toBeUndefined(); + + const libPkg = newTree.readJson('/projects/my-lib/package.json') as { exports: unknown }; + expect(libPkg.exports).toEqual({ + '.': './src/public-api.ts', + './schematics': './schematics/index.ts', + './testing': './testing/src/public-api.ts', + }); + + expect(newTree.exists('/projects/my-lib/testing/ng-package.json')).toBe(false); + expect(newTree.exists('/projects/my-lib/schematics/package.json')).toBe(false); + }); + + it('should preserve existing custom exports in package.json when adding entry points', async () => { + tree.overwrite( + '/projects/my-lib/package.json', + JSON.stringify( + { + name: 'my-lib', + version: '0.0.1', + exports: { + './theming': { + sass: './src/_theming.scss', + }, + }, + }, + undefined, + 2, + ), + ); + + const newTree = await schematicRunner.runSchematic(schematicName, {}, tree); + const libPkg = newTree.readJson('/projects/my-lib/package.json') as { exports: unknown }; + expect(libPkg.exports).toEqual({ + './theming': { + sass: './src/_theming.scss', + }, + '.': './src/public-api.ts', + }); + }); + + it('should preserve root condition-only exports in package.json', async () => { + tree.overwrite( + '/projects/my-lib/package.json', + JSON.stringify( + { + name: 'my-lib', + version: '0.0.1', + exports: { + sass: './src/_index.scss', + }, + }, + undefined, + 2, + ), + ); + + const newTree = await schematicRunner.runSchematic(schematicName, {}, tree); + const libPkg = newTree.readJson('/projects/my-lib/package.json') as { exports: unknown }; + expect(libPkg.exports).toEqual({ + '.': { + sass: './src/_index.scss', + default: './src/public-api.ts', + }, + }); + }); + + it('should migrate assets, styleIncludePaths, and sass options from ng-package.json', async () => { + tree.overwrite( + '/projects/my-lib/ng-package.json', + JSON.stringify( + { + dest: '../../dist/my-lib', + assets: ['CHANGELOG.md'], + allowedNonPeerDependencies: ['lodash-es'], + inlineStyleLanguage: 'scss', + keepLifecycleScripts: true, + deleteDestPath: false, + lib: { + entryFile: 'src/public-api.ts', + styleIncludePaths: ['../theme'], + sass: { + silenceDeprecations: ['import'], + }, + }, + }, + undefined, + 2, + ), + ); + + const newTree = await schematicRunner.runSchematic(schematicName, {}, tree); + const { + projects: { 'my-lib': lib }, + // eslint-disable-next-line @typescript-eslint/no-explicit-any + } = newTree.readJson('/angular.json') as any; + + const { options } = lib.architect['build']; + expect(options.assets).toEqual(['projects/my-lib/CHANGELOG.md']); + expect(options.allowedNonPeerDependencies).toEqual(['lodash-es']); + expect(options.inlineStyleLanguage).toBe('scss'); + expect(options.keepLifecycleScripts).toBe(true); + expect(options.deleteOutputPath).toBe(false); + expect(options.stylePreprocessorOptions).toEqual({ + includePaths: ['projects/theme'], + sass: { + silenceDeprecations: ['import'], + }, + }); + }); + + it('should preserve existing stylePreprocessorOptions.includePaths when migrating styleIncludePaths', async () => { + const angularJson = JSON.parse(tree.readContent('/angular.json')); + angularJson.projects['my-lib'].architect.build.options.stylePreprocessorOptions = { + includePaths: ['existing/styles'], + }; + tree.overwrite('/angular.json', JSON.stringify(angularJson, undefined, 2)); + + tree.overwrite( + '/projects/my-lib/ng-package.json', + JSON.stringify( + { + lib: { + entryFile: 'src/public-api.ts', + styleIncludePaths: ['../theme'], + }, + }, + undefined, + 2, + ), + ); + + const newTree = await schematicRunner.runSchematic(schematicName, {}, tree); + const { + projects: { 'my-lib': lib }, + // eslint-disable-next-line @typescript-eslint/no-explicit-any + } = newTree.readJson('/angular.json') as any; + + expect(lib.architect['build'].options.stylePreprocessorOptions).toEqual({ + includePaths: ['existing/styles', 'projects/theme'], + }); + }); + + it('should throw an error when entry file is not configured and no default candidate exists', async () => { + tree.overwrite('/projects/my-lib/ng-package.json', JSON.stringify({}, undefined, 2)); + + await expectAsync(schematicRunner.runSchematic(schematicName, {}, tree)).toBeRejectedWithError( + 'Could not find an entry file for library entry point at "projects/my-lib".', + ); + }); + + it('should migrate package.json with ngPackage configuration', async () => { + tree.overwrite( + '/projects/my-lib/package.json', + JSON.stringify( + { + name: 'my-lib', + ngPackage: { + lib: { + entryFile: 'src/public-api.ts', + }, + }, + }, + undefined, + 2, + ), + ); + + // Point angular.json to package.json + const angularJson = JSON.parse(tree.readContent('/angular.json')); + angularJson.projects['my-lib'].architect.build.options.project = 'projects/my-lib/package.json'; + tree.overwrite('/angular.json', JSON.stringify(angularJson, undefined, 2)); + + const newTree = await schematicRunner.runSchematic(schematicName, {}, tree); + const libPkg = newTree.readJson('/projects/my-lib/package.json') as { + ngPackage?: unknown; + name: string; + exports: unknown; + }; + expect(libPkg.ngPackage).toBeUndefined(); + expect(libPkg.name).toBe('my-lib'); + expect(libPkg.exports).toEqual({ + '.': './src/public-api.ts', + }); + }); + + it('should skip libraries with JavaScript ng-packagr config files and retain ng-packagr dependency', async () => { + const angularJson = JSON.parse(tree.readContent('/angular.json')); + angularJson.projects['my-lib'].architect.build.options.project = + 'projects/my-lib/ng-package.cjs'; + tree.overwrite('/angular.json', JSON.stringify(angularJson, undefined, 2)); + + const newTree = await schematicRunner.runSchematic(schematicName, {}, tree); + const { + projects: { 'my-lib': lib }, + // eslint-disable-next-line @typescript-eslint/no-explicit-any + } = newTree.readJson('/angular.json') as any; + + expect(lib.architect['build'].builder).toBe(Builders.NgPackagr); + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const rootPkg = newTree.readJson('/package.json') as any; + expect(rootPkg.devDependencies['ng-packagr']).toBeDefined(); + expect(rootPkg.devDependencies['@angular/build']).toBeUndefined(); + }); + + it('should remove ng-packagr and add @angular/build when no library uses ng-packagr', async () => { + const newTree = await schematicRunner.runSchematic(schematicName, {}, tree); + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const rootPkg = newTree.readJson('/package.json') as any; + + expect(rootPkg.devDependencies['ng-packagr']).toBeUndefined(); + expect(rootPkg.devDependencies['@angular/build']).toBeDefined(); + }); +}); diff --git a/packages/schematics/angular/ng-new/schema.json b/packages/schematics/angular/ng-new/schema.json index 257628fcda9e..2c69fa84aaad 100644 --- a/packages/schematics/angular/ng-new/schema.json +++ b/packages/schematics/angular/ng-new/schema.json @@ -8,7 +8,7 @@ "properties": { "directory": { "type": "string", - "description": "The directory where the new workspace and project should be created. If not specified, the workspace will be created in the current directory." + "description": "The directory where the new workspace and project should be created. If not specified, the workspace will be created in a subfolder within the current directory, using the workspace's name." }, "name": { "description": "The name for the new workspace and the initial project. This name will be used for the root directory and various identifiers throughout the project.", diff --git a/packages/schematics/angular/package.json b/packages/schematics/angular/package.json index 5d0cc7efef53..6025fad3bd99 100644 --- a/packages/schematics/angular/package.json +++ b/packages/schematics/angular/package.json @@ -20,10 +20,10 @@ "@angular-devkit/core": "workspace:0.0.0-PLACEHOLDER", "@angular-devkit/schematics": "workspace:0.0.0-PLACEHOLDER", "jsonc-parser": "3.3.1", - "oxc-parser": "0.153.0", + "oxc-parser": "0.154.0", "typescript": "6.0.3" }, "devDependencies": { - "@oxc-project/types": "0.153.0" + "@oxc-project/types": "0.154.0" } } diff --git a/packages/schematics/angular/utility/find-module.ts b/packages/schematics/angular/utility/find-module.ts index c98b52a0cbe2..e6ad0c5620e6 100644 --- a/packages/schematics/angular/utility/find-module.ts +++ b/packages/schematics/angular/utility/find-module.ts @@ -117,7 +117,7 @@ export function findModule( const errorMsg = foundRoutingModule ? 'Could not find a non Routing NgModule.' + - `\nModules with suffix '${routingModuleExt}' are strictly reserved for routing.` + + `\nModules with suffix '${routingModuleExtensions.join("' or '")}' are strictly reserved for routing.` + `\nUse the '--skip-import' option to skip importing in NgModule.` : `Could not find an NgModule. Use the '--skip-import' option to skip importing in NgModule.`; diff --git a/packages/schematics/angular/utility/find-module_spec.ts b/packages/schematics/angular/utility/find-module_spec.ts index 0db295ae384f..dcc8a7df057e 100644 --- a/packages/schematics/angular/utility/find-module_spec.ts +++ b/packages/schematics/angular/utility/find-module_spec.ts @@ -56,6 +56,9 @@ describe('find-module', () => { expect(() => findModule(host, 'foo/src/app/anything-routing')).toThrowError( /Could not find a non Routing NgModule/, ); + expect(() => findModule(host, 'foo/src/app/anything-routing')).toThrowError( + /Modules with suffix '-routing-module\.ts' or '-routing\.module\.ts' are strictly reserved/, + ); }); it('should throw if two modules found', () => { diff --git a/packages/schematics/angular/utility/workspace-models.ts b/packages/schematics/angular/utility/workspace-models.ts index d1ae1b146835..51c9c9dbdc9f 100644 --- a/packages/schematics/angular/utility/workspace-models.ts +++ b/packages/schematics/angular/utility/workspace-models.ts @@ -36,6 +36,7 @@ export enum Builders { ExtractI18n = '@angular-devkit/build-angular:extract-i18n', BuildExtractI18n = '@angular/build:extract-i18n', BuildApplication = '@angular/build:application', + BuildLibrary = '@angular/build:library', } export interface FileReplacements { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e6b9e456459e..81cbdf51aeb5 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -7,153 +7,153 @@ importers: configDependencies: {} packageManagerDependencies: pnpm: - specifier: 12.10.1 - version: 12.10.1 + specifier: 12.11.2 + version: 12.11.2 packages: - '@pnpm/exe.android-arm64@12.10.1': - resolution: {integrity: sha512-CVTlHPwNvadQCJD9bGSMzWIhFX8QpjtqC2c27dhdnn/9iKzEjZ3qL62o3gHpKbIuo6qn4WxTKRoaokgNdReBig==} + '@pnpm/exe.android-arm64@12.11.2': + resolution: {integrity: sha512-owLw13OfPkEoY+hLBNeeG+dCFhywr7q2d/EHPz4mNL2RhKnv8gNwMARkjkiDQu234aItVwFNDeGnmQXm+ikkAg==} cpu: [arm64] os: [android] - '@pnpm/exe.android-x64@12.10.1': - resolution: {integrity: sha512-NFiYLIaodY7da0XIebwcU3JoP7IK/lzF7zgKSSP+9H+QcFnQ1GDfBkuBKCYJzZ+Xu0WH54neIPLBBCaOjaaauA==} + '@pnpm/exe.android-x64@12.11.2': + resolution: {integrity: sha512-GYPkVebl4iokOA/4MPSpGoUVfFlsth7uqOCe1XiaiM23LAdvUOMJBObqHJijr89ZqjwHm9pO6vO45GRwJ2U2qA==} cpu: [x64] os: [android] - '@pnpm/exe.darwin-arm64@12.10.1': - resolution: {integrity: sha512-0O8tdQ8ijlPJudXspLMTcitMkdBEz6uUflazL5uQE0WMs85oQIp0U+5hfEktMFB64QqEhjqlvvujTwbzdr9oMg==} + '@pnpm/exe.darwin-arm64@12.11.2': + resolution: {integrity: sha512-n9M4On66y8FDxUAKgMO1FXt0qihyG4e+56p3/F0uo8Bx9KplW8BwKVnXigzCJ8aNM6sVVPkOE6M9XXbeVIxnZA==} cpu: [arm64] os: [darwin] - '@pnpm/exe.darwin-x64@12.10.1': - resolution: {integrity: sha512-ud+1/uZOKEo8P/AsPynOJc/LNwfI0j6WwCElibj71rU9l4nIBnsSn+wWTIPZPkZ4BBSH5+82azSnAARMyJ1lNA==} + '@pnpm/exe.darwin-x64@12.11.2': + resolution: {integrity: sha512-oSW7n3sBT0c0cyk4kj10T2Y6Mpm+OFEiCAWozgX039tdoI6yqJF1uaFD3sw7ugLdu6VV3+uY0pa1TUZIPdxDWA==} cpu: [x64] os: [darwin] - '@pnpm/exe.freebsd-x64@12.10.1': - resolution: {integrity: sha512-mj9PE5rf8uAfQQ2K66HSATMSut+lNiB4eSqFd5+azFVCMnxNSy/32xpZQJT21PhnN3Y4qbK+iKHe5nWAxSuRhw==} + '@pnpm/exe.freebsd-x64@12.11.2': + resolution: {integrity: sha512-qbzc3KoDpa/Lkf8vs3jnDe6dUUbso+6eLIyssYFMiliLpYNxn8dirk11YoNnOQcdHVB50v5RFls1Yid6ttT5pg==} cpu: [x64] os: [freebsd] - '@pnpm/exe.linux-arm64-musl@12.10.1': - resolution: {integrity: sha512-xba+00wZJ8Um6rBJJjWl88uJj0tydB8dtomvjlCLvi0uL32vpa9CRP8Vv26FIXvbiZ1bo2RCzYt/eChxJVV52w==} + '@pnpm/exe.linux-arm64-musl@12.11.2': + resolution: {integrity: sha512-uMEYDwKRktGwANubQ+kYCwmhNwg3Ik6qK89ahsbQ5cP30R/5FVJb/T99vr9XRUBC/A7mZI6eeT92oyVas9ACyg==} cpu: [arm64] os: [linux] libc: [musl] - '@pnpm/exe.linux-arm64@12.10.1': - resolution: {integrity: sha512-oBdMp69ScC9UnniG4qZAQVr2fBDSXjm4yk/72vkEVyrlwS23yMxcRQl7Qdo3sgXPAygY6TWnPRKm4rXzjoyW7w==} + '@pnpm/exe.linux-arm64@12.11.2': + resolution: {integrity: sha512-6e8ZAQUwFXAWHHHv5FesWqf/kEK04wuitBv5f/gxzkC0DiBfaixr+f+6JQede8SF4Ji5LcMwVFJbzAtffpWxEg==} cpu: [arm64] os: [linux] libc: [glibc] - '@pnpm/exe.linux-ppc64@12.10.1': - resolution: {integrity: sha512-Yyb3wu09h3c7mLme270uHFiVaAIUHC411Ru9LIU2BKUIstrGlXa9OqqVWoXaRgYRdFdOFknkwG7GNn6ygwLbnw==} + '@pnpm/exe.linux-ppc64@12.11.2': + resolution: {integrity: sha512-Ui8A4nepkVupx2VpuFfysEluMvWK6gTB5rgVts7Ss/+8dj0RD28x3F/seNeH/zSypgC6yKdV6qsKVQh/uffTuQ==} cpu: [ppc64] os: [linux] libc: [glibc] - '@pnpm/exe.linux-riscv64@12.10.1': - resolution: {integrity: sha512-PdkeUFFkybknnGkWP0H22eMpIiS4OZXL5BcQzF4lh+vmrW3IaC2HOkbANKgot/PKO0Kl77pzA/6PKFxgbgfXIQ==} + '@pnpm/exe.linux-riscv64@12.11.2': + resolution: {integrity: sha512-i1yLdQ5bu+wjQ+srtFoJAaY2gN/+oXHFdjul4llCeoDxHE0rTIG3R41NhHfCjyFPW4+CpbBT58933CL9sMOCnA==} cpu: [riscv64] os: [linux] libc: [glibc] - '@pnpm/exe.linux-s390x@12.10.1': - resolution: {integrity: sha512-1okKuHKJwJPAbofDxfhZWAUxEWtee4ZSf7Pah8kcvocaJkf2WxFJ5KVTiNIvDml5GLidDQsgf7Pcf/WHbCak7Q==} + '@pnpm/exe.linux-s390x@12.11.2': + resolution: {integrity: sha512-YS1/lhPjjTsN4QL50VaFZKRa3Uc/bDnhE8eKbJMFvuX06UuFS5YaiDg04APoZdfVSGqTfX27QAg7rfLMSWaqxA==} cpu: [s390x] os: [linux] libc: [glibc] - '@pnpm/exe.linux-x64-musl@12.10.1': - resolution: {integrity: sha512-e6IqzVNkPa7srFKJKnY23uWJ+qz0LaRWiTUo/sHfFXRRMRzB5uNOr6qWWip/jqhdGwU91n9J7LrEYZ6e0tRczw==} + '@pnpm/exe.linux-x64-musl@12.11.2': + resolution: {integrity: sha512-7eIxQR05fvJxCujUSKp5KTtehJ7QqrgqP/HD1+BcB/SaV2Be4XI1PEHiOgA1zxsiMIeMvTF6E4G4dYHrFjeXRg==} cpu: [x64] os: [linux] libc: [musl] - '@pnpm/exe.linux-x64@12.10.1': - resolution: {integrity: sha512-ZByV0DRgWzGaDSjT5ddRBASdwz+r/qPMYh7o6RGmPQjtRGNFbSl94xY2DcAM8mqCOZJUUDtAXQ+wpBXcdpKlkg==} + '@pnpm/exe.linux-x64@12.11.2': + resolution: {integrity: sha512-FK7QKe24AYgppJwO3mpooYptduuvIGqKJPi5ac7MdnWkjUy46JBfYhCW0grnkWKC8YOf0x+vGviuQqJEj3SpQQ==} cpu: [x64] os: [linux] libc: [glibc] - '@pnpm/exe.win32-arm64@12.10.1': - resolution: {integrity: sha512-I7QZHcjioG7orFeCRUFUHByJ1G0zFPnAlaGGyrUvb6MGkv5oeNwNPl7t+Gp+7zHfnMcgLGJ1a+Wfim0IDw3lOw==} + '@pnpm/exe.win32-arm64@12.11.2': + resolution: {integrity: sha512-PCTjVDdJb6VvraZxrEeFv6EqEA0hhIsAySx28e95J2Tqpi88hLpgOXgNqV0vpxq9A+b8GLQfyk/D+1UlJPv/YQ==} cpu: [arm64] os: [win32] - '@pnpm/exe.win32-x64@12.10.1': - resolution: {integrity: sha512-4WJFdp58EKlCj0zLobrXU0vpszSZIr4X/3y/uNbAQqGW0zE+VdruZB0gFc3L2kzsUzVwTYbaCn+XEhnCYSAlTg==} + '@pnpm/exe.win32-x64@12.11.2': + resolution: {integrity: sha512-8sgX86OvRjJ3yZs/TvGR2L31wH9ScPZ+f+geKI3ikuB0RRiK3i4LlTRO8OXN6pdRqj8epMde4312MCjj7JsJdA==} cpu: [x64] os: [win32] - pnpm@12.10.1: - resolution: {integrity: sha512-ukCjfrGjcNYP6oxM8dNkwTvMzvUY6YrSvO6VT1OdONo3RY5YJho/pV4GUjRQ6g//+fSXsTW+QfLBXBZCusrObA==} + pnpm@12.11.2: + resolution: {integrity: sha512-JaUWUqGWeFr18ovabf6NxYLr76SsHRgCIc4TkCMcX7dmN4IupLbkYzznSs3LCnf3Ja8RutOBd/3t2u6BipVcuQ==} engines: {node: '>=18.*'} hasBin: true snapshots: - '@pnpm/exe.android-arm64@12.10.1': + '@pnpm/exe.android-arm64@12.11.2': optional: true - '@pnpm/exe.android-x64@12.10.1': + '@pnpm/exe.android-x64@12.11.2': optional: true - '@pnpm/exe.darwin-arm64@12.10.1': + '@pnpm/exe.darwin-arm64@12.11.2': optional: true - '@pnpm/exe.darwin-x64@12.10.1': + '@pnpm/exe.darwin-x64@12.11.2': optional: true - '@pnpm/exe.freebsd-x64@12.10.1': + '@pnpm/exe.freebsd-x64@12.11.2': optional: true - '@pnpm/exe.linux-arm64-musl@12.10.1': + '@pnpm/exe.linux-arm64-musl@12.11.2': optional: true - '@pnpm/exe.linux-arm64@12.10.1': + '@pnpm/exe.linux-arm64@12.11.2': optional: true - '@pnpm/exe.linux-ppc64@12.10.1': + '@pnpm/exe.linux-ppc64@12.11.2': optional: true - '@pnpm/exe.linux-riscv64@12.10.1': + '@pnpm/exe.linux-riscv64@12.11.2': optional: true - '@pnpm/exe.linux-s390x@12.10.1': + '@pnpm/exe.linux-s390x@12.11.2': optional: true - '@pnpm/exe.linux-x64-musl@12.10.1': + '@pnpm/exe.linux-x64-musl@12.11.2': optional: true - '@pnpm/exe.linux-x64@12.10.1': + '@pnpm/exe.linux-x64@12.11.2': optional: true - '@pnpm/exe.win32-arm64@12.10.1': + '@pnpm/exe.win32-arm64@12.11.2': optional: true - '@pnpm/exe.win32-x64@12.10.1': + '@pnpm/exe.win32-x64@12.11.2': optional: true - pnpm@12.10.1: + pnpm@12.11.2: optionalDependencies: - '@pnpm/exe.android-arm64': 12.10.1 - '@pnpm/exe.android-x64': 12.10.1 - '@pnpm/exe.darwin-arm64': 12.10.1 - '@pnpm/exe.darwin-x64': 12.10.1 - '@pnpm/exe.freebsd-x64': 12.10.1 - '@pnpm/exe.linux-arm64': 12.10.1 - '@pnpm/exe.linux-arm64-musl': 12.10.1 - '@pnpm/exe.linux-ppc64': 12.10.1 - '@pnpm/exe.linux-riscv64': 12.10.1 - '@pnpm/exe.linux-s390x': 12.10.1 - '@pnpm/exe.linux-x64': 12.10.1 - '@pnpm/exe.linux-x64-musl': 12.10.1 - '@pnpm/exe.win32-arm64': 12.10.1 - '@pnpm/exe.win32-x64': 12.10.1 + '@pnpm/exe.android-arm64': 12.11.2 + '@pnpm/exe.android-x64': 12.11.2 + '@pnpm/exe.darwin-arm64': 12.11.2 + '@pnpm/exe.darwin-x64': 12.11.2 + '@pnpm/exe.freebsd-x64': 12.11.2 + '@pnpm/exe.linux-arm64': 12.11.2 + '@pnpm/exe.linux-arm64-musl': 12.11.2 + '@pnpm/exe.linux-ppc64': 12.11.2 + '@pnpm/exe.linux-riscv64': 12.11.2 + '@pnpm/exe.linux-s390x': 12.11.2 + '@pnpm/exe.linux-x64': 12.11.2 + '@pnpm/exe.linux-x64-musl': 12.11.2 + '@pnpm/exe.win32-arm64': 12.11.2 + '@pnpm/exe.win32-x64': 12.11.2 --- lockfileVersion: '9.0' @@ -172,8 +172,8 @@ importers: .: dependencies: '@angular/compiler-cli': - specifier: 22.3.0-next.1 - version: 22.3.0-next.1(@angular/compiler@22.3.0-next.1)(typescript@6.0.3) + specifier: 22.3.0-next.2 + version: 22.3.0-next.2(@angular/compiler@22.3.0-next.2)(typescript@6.0.3) typescript: specifier: 6.0.3 version: 6.0.3 @@ -184,44 +184,44 @@ importers: built: true devDependencies: '@angular/animations': - specifier: 22.3.0-next.1 - version: 22.3.0-next.1(@angular/core@22.3.0-next.1(@angular/compiler@22.3.0-next.1)(rxjs@7.8.2)(zone.js@0.16.3)) + specifier: 22.3.0-next.2 + version: 22.3.0-next.2(@angular/core@22.3.0-next.2(@angular/compiler@22.3.0-next.2)(rxjs@7.8.2)(zone.js@0.16.3)) '@angular/cdk': - specifier: 22.3.0-next.1 - version: 22.3.0-next.1(1e228ab395252fe57958c1335d3a301f) + specifier: 22.3.0-next.2 + version: 22.3.0-next.2(e02572097466d520d7eb144e95d6bbd2) '@angular/common': - specifier: 22.3.0-next.1 - version: 22.3.0-next.1(@angular/core@22.3.0-next.1(@angular/compiler@22.3.0-next.1)(rxjs@7.8.2)(zone.js@0.16.3))(rxjs@7.8.2) + specifier: 22.3.0-next.2 + version: 22.3.0-next.2(@angular/core@22.3.0-next.2(@angular/compiler@22.3.0-next.2)(rxjs@7.8.2)(zone.js@0.16.3))(rxjs@7.8.2) '@angular/compiler': - specifier: 22.3.0-next.1 - version: 22.3.0-next.1 + specifier: 22.3.0-next.2 + version: 22.3.0-next.2 '@angular/core': - specifier: 22.3.0-next.1 - version: 22.3.0-next.1(@angular/compiler@22.3.0-next.1)(rxjs@7.8.2)(zone.js@0.16.3) + specifier: 22.3.0-next.2 + version: 22.3.0-next.2(@angular/compiler@22.3.0-next.2)(rxjs@7.8.2)(zone.js@0.16.3) '@angular/forms': - specifier: 22.3.0-next.1 - version: 22.3.0-next.1(@angular/common@22.3.0-next.1(@angular/core@22.3.0-next.1(@angular/compiler@22.3.0-next.1)(rxjs@7.8.2)(zone.js@0.16.3))(rxjs@7.8.2))(@angular/core@22.3.0-next.1(@angular/compiler@22.3.0-next.1)(rxjs@7.8.2)(zone.js@0.16.3))(@angular/platform-browser@22.3.0-next.1(@angular/animations@22.3.0-next.1(@angular/core@22.3.0-next.1(@angular/compiler@22.3.0-next.1)(rxjs@7.8.2)(zone.js@0.16.3)))(@angular/common@22.3.0-next.1(@angular/core@22.3.0-next.1(@angular/compiler@22.3.0-next.1)(rxjs@7.8.2)(zone.js@0.16.3))(rxjs@7.8.2))(@angular/core@22.3.0-next.1(@angular/compiler@22.3.0-next.1)(rxjs@7.8.2)(zone.js@0.16.3)))(rxjs@7.8.2) + specifier: 22.3.0-next.2 + version: 22.3.0-next.2(@angular/common@22.3.0-next.2(@angular/core@22.3.0-next.2(@angular/compiler@22.3.0-next.2)(rxjs@7.8.2)(zone.js@0.16.3))(rxjs@7.8.2))(@angular/core@22.3.0-next.2(@angular/compiler@22.3.0-next.2)(rxjs@7.8.2)(zone.js@0.16.3))(@angular/platform-browser@22.3.0-next.2(@angular/animations@22.3.0-next.2(@angular/core@22.3.0-next.2(@angular/compiler@22.3.0-next.2)(rxjs@7.8.2)(zone.js@0.16.3)))(@angular/common@22.3.0-next.2(@angular/core@22.3.0-next.2(@angular/compiler@22.3.0-next.2)(rxjs@7.8.2)(zone.js@0.16.3))(rxjs@7.8.2))(@angular/core@22.3.0-next.2(@angular/compiler@22.3.0-next.2)(rxjs@7.8.2)(zone.js@0.16.3)))(rxjs@7.8.2) '@angular/localize': - specifier: 22.3.0-next.1 - version: 22.3.0-next.1(@angular/compiler-cli@22.3.0-next.1(@angular/compiler@22.3.0-next.1)(typescript@6.0.3))(@angular/compiler@22.3.0-next.1) + specifier: 22.3.0-next.2 + version: 22.3.0-next.2(@angular/compiler-cli@22.3.0-next.2(@angular/compiler@22.3.0-next.2)(typescript@6.0.3))(@angular/compiler@22.3.0-next.2) '@angular/material': - specifier: 22.3.0-next.1 - version: 22.3.0-next.1(837417520965053888cf2615dc986a27) + specifier: 22.3.0-next.2 + version: 22.3.0-next.2(4016851894464b4783f4f873310c276b) '@angular/ng-dev': - specifier: https://github.com/angular/dev-infra-private-ng-dev-builds.git#89b8c002e5d47ba1cbc4483d3ec06b0ae0aaccb7 - version: https://codeload.github.com/angular/dev-infra-private-ng-dev-builds/tar.gz/89b8c002e5d47ba1cbc4483d3ec06b0ae0aaccb7 + specifier: https://github.com/angular/dev-infra-private-ng-dev-builds.git#940c0beef0de4cb128e90962d47ff80e352a1cd8 + version: https://codeload.github.com/angular/dev-infra-private-ng-dev-builds/tar.gz/940c0beef0de4cb128e90962d47ff80e352a1cd8 '@angular/platform-browser': - specifier: 22.3.0-next.1 - version: 22.3.0-next.1(@angular/animations@22.3.0-next.1(@angular/core@22.3.0-next.1(@angular/compiler@22.3.0-next.1)(rxjs@7.8.2)(zone.js@0.16.3)))(@angular/common@22.3.0-next.1(@angular/core@22.3.0-next.1(@angular/compiler@22.3.0-next.1)(rxjs@7.8.2)(zone.js@0.16.3))(rxjs@7.8.2))(@angular/core@22.3.0-next.1(@angular/compiler@22.3.0-next.1)(rxjs@7.8.2)(zone.js@0.16.3)) + specifier: 22.3.0-next.2 + version: 22.3.0-next.2(@angular/animations@22.3.0-next.2(@angular/core@22.3.0-next.2(@angular/compiler@22.3.0-next.2)(rxjs@7.8.2)(zone.js@0.16.3)))(@angular/common@22.3.0-next.2(@angular/core@22.3.0-next.2(@angular/compiler@22.3.0-next.2)(rxjs@7.8.2)(zone.js@0.16.3))(rxjs@7.8.2))(@angular/core@22.3.0-next.2(@angular/compiler@22.3.0-next.2)(rxjs@7.8.2)(zone.js@0.16.3)) '@angular/platform-server': - specifier: 22.3.0-next.1 - version: 22.3.0-next.1(@angular/common@22.3.0-next.1(@angular/core@22.3.0-next.1(@angular/compiler@22.3.0-next.1)(rxjs@7.8.2)(zone.js@0.16.3))(rxjs@7.8.2))(@angular/compiler@22.3.0-next.1)(@angular/core@22.3.0-next.1(@angular/compiler@22.3.0-next.1)(rxjs@7.8.2)(zone.js@0.16.3))(@angular/platform-browser@22.3.0-next.1(@angular/animations@22.3.0-next.1(@angular/core@22.3.0-next.1(@angular/compiler@22.3.0-next.1)(rxjs@7.8.2)(zone.js@0.16.3)))(@angular/common@22.3.0-next.1(@angular/core@22.3.0-next.1(@angular/compiler@22.3.0-next.1)(rxjs@7.8.2)(zone.js@0.16.3))(rxjs@7.8.2))(@angular/core@22.3.0-next.1(@angular/compiler@22.3.0-next.1)(rxjs@7.8.2)(zone.js@0.16.3)))(rxjs@7.8.2) + specifier: 22.3.0-next.2 + version: 22.3.0-next.2(@angular/common@22.3.0-next.2(@angular/core@22.3.0-next.2(@angular/compiler@22.3.0-next.2)(rxjs@7.8.2)(zone.js@0.16.3))(rxjs@7.8.2))(@angular/compiler@22.3.0-next.2)(@angular/core@22.3.0-next.2(@angular/compiler@22.3.0-next.2)(rxjs@7.8.2)(zone.js@0.16.3))(@angular/platform-browser@22.3.0-next.2(@angular/animations@22.3.0-next.2(@angular/core@22.3.0-next.2(@angular/compiler@22.3.0-next.2)(rxjs@7.8.2)(zone.js@0.16.3)))(@angular/common@22.3.0-next.2(@angular/core@22.3.0-next.2(@angular/compiler@22.3.0-next.2)(rxjs@7.8.2)(zone.js@0.16.3))(rxjs@7.8.2))(@angular/core@22.3.0-next.2(@angular/compiler@22.3.0-next.2)(rxjs@7.8.2)(zone.js@0.16.3)))(rxjs@7.8.2) '@angular/router': - specifier: 22.3.0-next.1 - version: 22.3.0-next.1(@angular/common@22.3.0-next.1(@angular/core@22.3.0-next.1(@angular/compiler@22.3.0-next.1)(rxjs@7.8.2)(zone.js@0.16.3))(rxjs@7.8.2))(@angular/core@22.3.0-next.1(@angular/compiler@22.3.0-next.1)(rxjs@7.8.2)(zone.js@0.16.3))(@angular/platform-browser@22.3.0-next.1(@angular/animations@22.3.0-next.1(@angular/core@22.3.0-next.1(@angular/compiler@22.3.0-next.1)(rxjs@7.8.2)(zone.js@0.16.3)))(@angular/common@22.3.0-next.1(@angular/core@22.3.0-next.1(@angular/compiler@22.3.0-next.1)(rxjs@7.8.2)(zone.js@0.16.3))(rxjs@7.8.2))(@angular/core@22.3.0-next.1(@angular/compiler@22.3.0-next.1)(rxjs@7.8.2)(zone.js@0.16.3)))(rxjs@7.8.2) + specifier: 22.3.0-next.2 + version: 22.3.0-next.2(@angular/common@22.3.0-next.2(@angular/core@22.3.0-next.2(@angular/compiler@22.3.0-next.2)(rxjs@7.8.2)(zone.js@0.16.3))(rxjs@7.8.2))(@angular/core@22.3.0-next.2(@angular/compiler@22.3.0-next.2)(rxjs@7.8.2)(zone.js@0.16.3))(@angular/platform-browser@22.3.0-next.2(@angular/animations@22.3.0-next.2(@angular/core@22.3.0-next.2(@angular/compiler@22.3.0-next.2)(rxjs@7.8.2)(zone.js@0.16.3)))(@angular/common@22.3.0-next.2(@angular/core@22.3.0-next.2(@angular/compiler@22.3.0-next.2)(rxjs@7.8.2)(zone.js@0.16.3))(rxjs@7.8.2))(@angular/core@22.3.0-next.2(@angular/compiler@22.3.0-next.2)(rxjs@7.8.2)(zone.js@0.16.3)))(rxjs@7.8.2) '@angular/service-worker': - specifier: 22.3.0-next.1 - version: 22.3.0-next.1(@angular/core@22.3.0-next.1(@angular/compiler@22.3.0-next.1)(rxjs@7.8.2)(zone.js@0.16.3))(rxjs@7.8.2) + specifier: 22.3.0-next.2 + version: 22.3.0-next.2(@angular/core@22.3.0-next.2(@angular/compiler@22.3.0-next.2)(rxjs@7.8.2)(zone.js@0.16.3))(rxjs@7.8.2) '@babel/core': specifier: 8.0.7 version: 8.0.7 @@ -245,16 +245,16 @@ importers: version: 10.0.1(eslint@10.12.0(jiti@2.7.0)(supports-color@11.0.0)) '@rollup/plugin-commonjs': specifier: ^29.0.0 - version: 29.0.3(rollup@4.64.1) + version: 29.0.3(rollup@4.64.6) '@rollup/plugin-json': specifier: ^6.1.0 - version: 6.1.0(rollup@4.64.1) + version: 6.1.0(rollup@4.64.6) '@rollup/plugin-node-resolve': specifier: 16.0.3 - version: 16.0.3(rollup@4.64.1) + version: 16.0.3(rollup@4.64.6) '@rollup/wasm-node': - specifier: 4.64.1 - version: 4.64.1 + specifier: 4.64.6 + version: 4.64.6 '@stylistic/eslint-plugin': specifier: ^5.0.0 version: 5.10.0(eslint@10.12.0(jiti@2.7.0)(supports-color@11.0.0)) @@ -293,7 +293,7 @@ importers: version: 3.0.0(esbuild@0.28.2)(lightningcss@1.33.0)(uglify-js@3.19.3) '@types/lodash': specifier: ^4.17.0 - version: 4.17.25 + version: 4.17.26 '@types/node': specifier: ^22.12.0 version: 22.20.5 @@ -316,11 +316,11 @@ importers: specifier: ^21.0.0 version: 21.0.3 '@typescript-eslint/eslint-plugin': - specifier: 8.71.1 - version: 8.71.1(@typescript-eslint/parser@8.71.1(eslint@10.12.0(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0)(typescript@6.0.3))(eslint@10.12.0(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0)(typescript@6.0.3) + specifier: 8.71.2 + version: 8.71.2(@typescript-eslint/parser@8.71.2(eslint@10.12.0(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0)(typescript@6.0.3))(eslint@10.12.0(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0)(typescript@6.0.3) '@typescript-eslint/parser': - specifier: 8.71.1 - version: 8.71.1(eslint@10.12.0(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0)(typescript@6.0.3) + specifier: 8.71.2 + version: 8.71.2(eslint@10.12.0(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0)(typescript@6.0.3) ajv: specifier: 8.20.0 version: 8.20.0 @@ -341,10 +341,10 @@ importers: version: 10.1.8(eslint@10.12.0(jiti@2.7.0)(supports-color@11.0.0)) eslint-plugin-import: specifier: 2.32.0 - version: 2.32.0(@typescript-eslint/parser@8.71.1(eslint@10.12.0(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0)(typescript@6.0.3))(eslint@10.12.0(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0) + version: 2.32.0(@typescript-eslint/parser@8.71.2(eslint@10.12.0(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0)(typescript@6.0.3))(eslint@10.12.0(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0) express: - specifier: 5.2.1 - version: 5.2.1(supports-color@11.0.0) + specifier: 5.3.0 + version: 5.3.0(supports-color@11.0.0) fast-glob: specifier: 3.3.3 version: 3.3.3 @@ -398,22 +398,22 @@ importers: version: 1.4.3 prettier: specifier: ^3.0.0 - version: 3.9.9 + version: 3.9.10 puppeteer: - specifier: 25.12.0 - version: 25.12.0(bufferutil@4.1.0)(utf-8-validate@6.0.6) + specifier: 25.14.0 + version: 25.14.0(bufferutil@4.1.0)(utf-8-validate@6.0.6) quicktype-core: specifier: 26.0.0 version: 26.0.0 rollup: - specifier: 4.64.1 - version: 4.64.1 + specifier: 4.64.6 + version: 4.64.6 rollup-plugin-dts: specifier: 6.5.1 - version: 6.5.1(rollup@4.64.1)(typescript@6.0.3) + version: 6.5.1(rollup@4.64.6)(typescript@6.0.3) rollup-plugin-sourcemaps2: specifier: 0.5.9 - version: 0.5.9(@types/node@22.20.5)(rollup@4.64.1) + version: 0.5.9(@types/node@22.20.5)(rollup@4.64.6) semver: specifier: 7.8.5 version: 7.8.5 @@ -424,14 +424,14 @@ importers: specifier: 2.8.1 version: 2.8.1 undici: - specifier: 8.11.2 - version: 8.11.2 + specifier: 8.12.1 + version: 8.12.1 verdaccio: specifier: 6.10.5 version: 6.10.5(encoding@0.1.13)(supports-color@11.0.0) verdaccio-auth-memory: specifier: ^13.0.0 - version: 13.1.5(supports-color@11.0.0) + version: 13.1.6(supports-color@11.0.0) zone.js: specifier: ^0.16.0 version: 0.16.3 @@ -451,8 +451,8 @@ importers: specifier: workspace:* version: link:../../../packages/angular/ssr '@vitest/coverage-v8': - specifier: 5.0.3 - version: 5.0.3(vitest@5.0.3) + specifier: 5.0.4 + version: 5.0.4(vitest@5.0.4) browser-sync: specifier: 3.0.4 version: 3.0.4(bufferutil@4.1.0)(debug@4.4.3(supports-color@11.0.0))(supports-color@11.0.0)(utf-8-validate@6.0.6) @@ -464,13 +464,13 @@ importers: version: 30.1.2(@noble/hashes@1.8.0) ng-packagr: specifier: 22.3.0-next.2 - version: 22.3.0-next.2(@angular/compiler-cli@22.3.0-next.1(@angular/compiler@22.3.0-next.1)(typescript@6.0.3))(supports-color@11.0.0)(tslib@2.8.1)(typescript@6.0.3) + version: 22.3.0-next.2(@angular/compiler-cli@22.3.0-next.2(@angular/compiler@22.3.0-next.2)(typescript@6.0.3))(supports-color@11.0.0)(tslib@2.8.1)(typescript@6.0.3) rxjs: specifier: 7.8.2 version: 7.8.2 vitest: - specifier: 5.0.3 - version: 5.0.3(@opentelemetry/api@1.9.1)(@types/node@22.20.5)(@vitest/coverage-v8@5.0.3)(jsdom@30.1.2(@noble/hashes@1.8.0))(vite@8.3.3(@types/node@22.20.5)(esbuild@0.28.2)(jiti@2.7.0)(less@4.9.1(supports-color@11.0.0))(sass-embedded@1.105.1)(sass@1.105.1)(terser@5.51.2)(tsx@4.23.15)(yaml@2.9.1)) + specifier: 5.0.4 + version: 5.0.4(@opentelemetry/api@1.9.1)(@types/node@22.20.5)(@vitest/coverage-v8@5.0.4)(jsdom@30.1.2(@noble/hashes@1.8.0))(vite@8.3.4(@types/node@22.20.5)(esbuild@0.28.2)(jiti@2.7.0)(less@4.9.1(supports-color@11.0.0))(sass-embedded@1.105.1)(sass@1.105.1)(terser@5.51.2)(tsx@4.23.15)(yaml@2.9.1)) packages/angular/build: dependencies: @@ -491,10 +491,10 @@ importers: version: 2.6.0 '@vitejs/plugin-basic-ssl': specifier: 2.3.0 - version: 2.3.0(vite@8.3.3(@types/node@22.20.5)(esbuild@0.28.2)(jiti@2.7.0)(less@4.9.1(supports-color@11.0.0))(sass-embedded@1.105.1)(sass@1.105.1)(terser@5.51.2)(tsx@4.23.15)(yaml@2.9.1)) + version: 2.3.0(vite@8.3.4(@types/node@22.20.5)(esbuild@0.28.2)(jiti@2.7.0)(less@4.9.1(supports-color@11.0.0))(sass-embedded@1.105.1)(sass@1.105.1)(terser@5.51.2)(tsx@4.23.15)(yaml@2.9.1)) beasties: - specifier: 0.5.4 - version: 0.5.4 + specifier: 0.5.5 + version: 0.5.5 browserslist: specifier: ^4.26.0 version: 4.29.3 @@ -511,8 +511,8 @@ importers: specifier: 3.3.1 version: 3.3.1 listr2: - specifier: 11.1.1 - version: 11.1.1 + specifier: 11.2.0 + version: 11.2.0 magic-string: specifier: 1.4.3 version: 1.4.3 @@ -520,8 +520,8 @@ importers: specifier: 2.0.1 version: 2.0.1 oxc-parser: - specifier: 0.153.0 - version: 0.153.0 + specifier: 0.154.0 + version: 0.154.0 parse5-html-rewriting-stream: specifier: 8.0.1 version: 8.0.1 @@ -532,11 +532,11 @@ importers: specifier: 5.3.2 version: 5.3.2 rolldown: - specifier: 1.2.12 - version: 1.2.12 + specifier: 1.2.14 + version: 1.2.14 rolldown-plugin-dts: - specifier: 0.28.6 - version: 0.28.6(rolldown@1.2.12)(typescript@6.0.3) + specifier: 0.28.8 + version: 0.28.8(rolldown@1.2.14)(typescript@6.0.3) sass: specifier: 1.105.1 version: 1.105.1 @@ -553,8 +553,8 @@ importers: specifier: 0.2.17 version: 0.2.17 vite: - specifier: 8.3.3 - version: 8.3.3(@types/node@22.20.5)(esbuild@0.28.2)(jiti@2.7.0)(less@4.9.1(supports-color@11.0.0))(sass-embedded@1.105.1)(sass@1.105.1)(terser@5.51.2)(tsx@4.23.15)(yaml@2.9.1) + specifier: 8.3.4 + version: 8.3.4(@types/node@22.20.5)(esbuild@0.28.2)(jiti@2.7.0)(less@4.9.1(supports-color@11.0.0))(sass-embedded@1.105.1)(sass@1.105.1)(terser@5.51.2)(tsx@4.23.15)(yaml@2.9.1) xxhash-wasm: specifier: 1.1.0 version: 1.1.0 @@ -566,11 +566,11 @@ importers: specifier: workspace:* version: link:../ssr '@oxc-project/types': - specifier: 0.153.0 - version: 0.153.0 + specifier: 0.154.0 + version: 0.154.0 '@vitest/coverage-v8': - specifier: 5.0.3 - version: 5.0.3(vitest@5.0.3) + specifier: 5.0.4 + version: 5.0.4(vitest@5.0.4) istanbul-lib-instrument: specifier: 6.0.3 version: 6.0.3(supports-color@11.0.0) @@ -582,19 +582,19 @@ importers: version: 4.9.1(supports-color@11.0.0) ng-packagr: specifier: 22.3.0-next.2 - version: 22.3.0-next.2(@angular/compiler-cli@22.3.0-next.1(@angular/compiler@22.3.0-next.1)(typescript@6.0.3))(supports-color@11.0.0)(tslib@2.8.1)(typescript@6.0.3) + version: 22.3.0-next.2(@angular/compiler-cli@22.3.0-next.2(@angular/compiler@22.3.0-next.2)(typescript@6.0.3))(supports-color@11.0.0)(tslib@2.8.1)(typescript@6.0.3) postcss: - specifier: 8.5.29 - version: 8.5.29 + specifier: 8.5.30 + version: 8.5.30 rollup: - specifier: 4.64.1 - version: 4.64.1 + specifier: 4.64.6 + version: 4.64.6 rxjs: specifier: 7.8.2 version: 7.8.2 vitest: - specifier: 5.0.3 - version: 5.0.3(@opentelemetry/api@1.9.1)(@types/node@22.20.5)(@vitest/coverage-v8@5.0.3)(jsdom@30.1.2(@noble/hashes@1.8.0))(vite@8.3.3(@types/node@22.20.5)(esbuild@0.28.2)(jiti@2.7.0)(less@4.9.1(supports-color@11.0.0))(sass-embedded@1.105.1)(sass@1.105.1)(terser@5.51.2)(tsx@4.23.15)(yaml@2.9.1)) + specifier: 5.0.4 + version: 5.0.4(@opentelemetry/api@1.9.1)(@types/node@22.20.5)(@vitest/coverage-v8@5.0.4)(jsdom@30.1.2(@noble/hashes@1.8.0))(vite@8.3.4(@types/node@22.20.5)(esbuild@0.28.2)(jiti@2.7.0)(less@4.9.1(supports-color@11.0.0))(sass-embedded@1.105.1)(sass@1.105.1)(terser@5.51.2)(tsx@4.23.15)(yaml@2.9.1)) optionalDependencies: lmdb: specifier: 3.5.7 @@ -615,8 +615,8 @@ importers: specifier: 8.7.3 version: 8.7.3(@types/node@22.20.5) '@listr2/prompt-adapter-inquirer': - specifier: 4.2.7 - version: 4.2.7(@inquirer/prompts@8.7.3(@types/node@22.20.5))(@types/node@22.20.5)(listr2@11.1.1) + specifier: 4.3.1 + version: 4.3.1(@inquirer/prompts@8.7.3(@types/node@22.20.5))(@types/node@22.20.5)(listr2@11.2.0) '@modelcontextprotocol/server': specifier: 2.3.1 version: 2.3.1 @@ -627,8 +627,8 @@ importers: specifier: 3.3.1 version: 3.3.1 listr2: - specifier: 11.1.1 - version: 11.1.1 + specifier: 11.2.0 + version: 11.2.0 npm-package-arg: specifier: 14.0.0 version: 14.0.0 @@ -642,8 +642,8 @@ importers: specifier: 18.2.0 version: 18.2.0 zod: - specifier: 4.6.5 - version: 4.6.5 + specifier: 4.6.7 + version: 4.6.7 packages/angular/pwa: dependencies: @@ -660,8 +660,8 @@ importers: packages/angular/ssr: dependencies: beasties: - specifier: 0.5.4 - version: 0.5.4 + specifier: 0.5.5 + version: 0.5.5 tslib: specifier: ^2.3.0 version: 2.8.1 @@ -670,23 +670,23 @@ importers: specifier: workspace:* version: link:../../angular_devkit/schematics '@angular/common': - specifier: 22.3.0-next.1 - version: 22.3.0-next.1(@angular/core@22.3.0-next.1(@angular/compiler@22.3.0-next.1)(rxjs@7.8.2)(zone.js@0.16.3))(rxjs@7.8.2) + specifier: 22.3.0-next.2 + version: 22.3.0-next.2(@angular/core@22.3.0-next.2(@angular/compiler@22.3.0-next.2)(rxjs@7.8.2)(zone.js@0.16.3))(rxjs@7.8.2) '@angular/compiler': - specifier: 22.3.0-next.1 - version: 22.3.0-next.1 + specifier: 22.3.0-next.2 + version: 22.3.0-next.2 '@angular/core': - specifier: 22.3.0-next.1 - version: 22.3.0-next.1(@angular/compiler@22.3.0-next.1)(rxjs@7.8.2)(zone.js@0.16.3) + specifier: 22.3.0-next.2 + version: 22.3.0-next.2(@angular/compiler@22.3.0-next.2)(rxjs@7.8.2)(zone.js@0.16.3) '@angular/platform-browser': - specifier: 22.3.0-next.1 - version: 22.3.0-next.1(@angular/animations@22.3.0-next.1(@angular/core@22.3.0-next.1(@angular/compiler@22.3.0-next.1)(rxjs@7.8.2)(zone.js@0.16.3)))(@angular/common@22.3.0-next.1(@angular/core@22.3.0-next.1(@angular/compiler@22.3.0-next.1)(rxjs@7.8.2)(zone.js@0.16.3))(rxjs@7.8.2))(@angular/core@22.3.0-next.1(@angular/compiler@22.3.0-next.1)(rxjs@7.8.2)(zone.js@0.16.3)) + specifier: 22.3.0-next.2 + version: 22.3.0-next.2(@angular/animations@22.3.0-next.2(@angular/core@22.3.0-next.2(@angular/compiler@22.3.0-next.2)(rxjs@7.8.2)(zone.js@0.16.3)))(@angular/common@22.3.0-next.2(@angular/core@22.3.0-next.2(@angular/compiler@22.3.0-next.2)(rxjs@7.8.2)(zone.js@0.16.3))(rxjs@7.8.2))(@angular/core@22.3.0-next.2(@angular/compiler@22.3.0-next.2)(rxjs@7.8.2)(zone.js@0.16.3)) '@angular/platform-server': - specifier: 22.3.0-next.1 - version: 22.3.0-next.1(@angular/common@22.3.0-next.1(@angular/core@22.3.0-next.1(@angular/compiler@22.3.0-next.1)(rxjs@7.8.2)(zone.js@0.16.3))(rxjs@7.8.2))(@angular/compiler@22.3.0-next.1)(@angular/core@22.3.0-next.1(@angular/compiler@22.3.0-next.1)(rxjs@7.8.2)(zone.js@0.16.3))(@angular/platform-browser@22.3.0-next.1(@angular/animations@22.3.0-next.1(@angular/core@22.3.0-next.1(@angular/compiler@22.3.0-next.1)(rxjs@7.8.2)(zone.js@0.16.3)))(@angular/common@22.3.0-next.1(@angular/core@22.3.0-next.1(@angular/compiler@22.3.0-next.1)(rxjs@7.8.2)(zone.js@0.16.3))(rxjs@7.8.2))(@angular/core@22.3.0-next.1(@angular/compiler@22.3.0-next.1)(rxjs@7.8.2)(zone.js@0.16.3)))(rxjs@7.8.2) + specifier: 22.3.0-next.2 + version: 22.3.0-next.2(@angular/common@22.3.0-next.2(@angular/core@22.3.0-next.2(@angular/compiler@22.3.0-next.2)(rxjs@7.8.2)(zone.js@0.16.3))(rxjs@7.8.2))(@angular/compiler@22.3.0-next.2)(@angular/core@22.3.0-next.2(@angular/compiler@22.3.0-next.2)(rxjs@7.8.2)(zone.js@0.16.3))(@angular/platform-browser@22.3.0-next.2(@angular/animations@22.3.0-next.2(@angular/core@22.3.0-next.2(@angular/compiler@22.3.0-next.2)(rxjs@7.8.2)(zone.js@0.16.3)))(@angular/common@22.3.0-next.2(@angular/core@22.3.0-next.2(@angular/compiler@22.3.0-next.2)(rxjs@7.8.2)(zone.js@0.16.3))(rxjs@7.8.2))(@angular/core@22.3.0-next.2(@angular/compiler@22.3.0-next.2)(rxjs@7.8.2)(zone.js@0.16.3)))(rxjs@7.8.2) '@angular/router': - specifier: 22.3.0-next.1 - version: 22.3.0-next.1(@angular/common@22.3.0-next.1(@angular/core@22.3.0-next.1(@angular/compiler@22.3.0-next.1)(rxjs@7.8.2)(zone.js@0.16.3))(rxjs@7.8.2))(@angular/core@22.3.0-next.1(@angular/compiler@22.3.0-next.1)(rxjs@7.8.2)(zone.js@0.16.3))(@angular/platform-browser@22.3.0-next.1(@angular/animations@22.3.0-next.1(@angular/core@22.3.0-next.1(@angular/compiler@22.3.0-next.1)(rxjs@7.8.2)(zone.js@0.16.3)))(@angular/common@22.3.0-next.1(@angular/core@22.3.0-next.1(@angular/compiler@22.3.0-next.1)(rxjs@7.8.2)(zone.js@0.16.3))(rxjs@7.8.2))(@angular/core@22.3.0-next.1(@angular/compiler@22.3.0-next.1)(rxjs@7.8.2)(zone.js@0.16.3)))(rxjs@7.8.2) + specifier: 22.3.0-next.2 + version: 22.3.0-next.2(@angular/common@22.3.0-next.2(@angular/core@22.3.0-next.2(@angular/compiler@22.3.0-next.2)(rxjs@7.8.2)(zone.js@0.16.3))(rxjs@7.8.2))(@angular/core@22.3.0-next.2(@angular/compiler@22.3.0-next.2)(rxjs@7.8.2)(zone.js@0.16.3))(@angular/platform-browser@22.3.0-next.2(@angular/animations@22.3.0-next.2(@angular/core@22.3.0-next.2(@angular/compiler@22.3.0-next.2)(rxjs@7.8.2)(zone.js@0.16.3)))(@angular/common@22.3.0-next.2(@angular/core@22.3.0-next.2(@angular/compiler@22.3.0-next.2)(rxjs@7.8.2)(zone.js@0.16.3))(rxjs@7.8.2))(@angular/core@22.3.0-next.2(@angular/compiler@22.3.0-next.2)(rxjs@7.8.2)(zone.js@0.16.3)))(rxjs@7.8.2) '@schematics/angular': specifier: workspace:* version: link:../../schematics/angular @@ -755,22 +755,22 @@ importers: version: 4.1.3 autoprefixer: specifier: 10.6.1 - version: 10.6.1(postcss@8.5.29) + version: 10.6.1(postcss@8.5.30) babel-loader: specifier: 10.1.1 - version: 10.1.1(@babel/core@8.0.7)(webpack@5.111.1(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.29)(uglify-js@3.19.3)) + version: 10.1.1(@babel/core@8.0.7)(webpack@5.111.1(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.30)(uglify-js@3.19.3)) beasties: - specifier: 0.5.4 - version: 0.5.4 + specifier: 0.5.5 + version: 0.5.5 browserslist: specifier: ^4.26.0 version: 4.29.3 copy-webpack-plugin: specifier: 14.0.0 - version: 14.0.0(webpack@5.111.1(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.29)(uglify-js@3.19.3)) + version: 14.0.0(webpack@5.111.1(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.30)(uglify-js@3.19.3)) css-loader: specifier: 7.1.5 - version: 7.1.5(webpack@5.111.1(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.29)(uglify-js@3.19.3)) + version: 7.1.5(webpack@5.111.1(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.30)(uglify-js@3.19.3)) esbuild-wasm: specifier: 0.28.2 version: 0.28.2 @@ -791,16 +791,16 @@ importers: version: 4.9.1(supports-color@11.0.0) less-loader: specifier: 13.0.0 - version: 13.0.0(less@4.9.1(supports-color@11.0.0))(webpack@5.111.1(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.29)(uglify-js@3.19.3)) + version: 13.0.0(less@4.9.1(supports-color@11.0.0))(webpack@5.111.1(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.30)(uglify-js@3.19.3)) license-webpack-plugin: specifier: 4.0.2 - version: 4.0.2(webpack@5.111.1(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.29)(uglify-js@3.19.3)) + version: 4.0.2(webpack@5.111.1(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.30)(uglify-js@3.19.3)) loader-utils: specifier: 3.3.1 version: 3.3.1 mini-css-extract-plugin: specifier: 2.10.2 - version: 2.10.2(webpack@5.111.1(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.29)(uglify-js@3.19.3)) + version: 2.10.2(webpack@5.111.1(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.30)(uglify-js@3.19.3)) open: specifier: 11.0.4 version: 11.0.4 @@ -814,11 +814,11 @@ importers: specifier: 5.3.2 version: 5.3.2 postcss: - specifier: 8.5.29 - version: 8.5.29 + specifier: 8.5.30 + version: 8.5.30 postcss-loader: specifier: 8.2.1 - version: 8.2.1(postcss@8.5.29)(typescript@6.0.3)(webpack@5.111.1(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.29)(uglify-js@3.19.3)) + version: 8.2.1(postcss@8.5.30)(typescript@6.0.3)(webpack@5.111.1(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.30)(uglify-js@3.19.3)) resolve-url-loader: specifier: 5.0.0 version: 5.0.0 @@ -827,13 +827,13 @@ importers: version: 7.8.2 sass-loader: specifier: 17.0.1 - version: 17.0.1(sass-embedded@1.105.1)(sass@1.105.1)(webpack@5.111.1(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.29)(uglify-js@3.19.3)) + version: 17.0.1(sass-embedded@1.105.1)(sass@1.105.1)(webpack@5.111.1(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.30)(uglify-js@3.19.3)) semver: specifier: 7.8.5 version: 7.8.5 source-map-loader: specifier: 5.0.0 - version: 5.0.0(webpack@5.111.1(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.29)(uglify-js@3.19.3)) + version: 5.0.0(webpack@5.111.1(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.30)(uglify-js@3.19.3)) source-map-support: specifier: 0.5.21 version: 0.5.21 @@ -848,19 +848,19 @@ importers: version: 2.8.1 webpack: specifier: 5.111.1 - version: 5.111.1(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.29)(uglify-js@3.19.3) + version: 5.111.1(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.30)(uglify-js@3.19.3) webpack-dev-middleware: specifier: 8.3.0 - version: 8.3.0(webpack@5.111.1(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.29)(uglify-js@3.19.3)) + version: 8.3.0(webpack@5.111.1(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.30)(uglify-js@3.19.3)) webpack-dev-server: specifier: 6.0.0 - version: 6.0.0(bufferutil@4.1.0)(supports-color@11.0.0)(utf-8-validate@6.0.6)(webpack@5.111.1(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.29)(uglify-js@3.19.3)) + version: 6.0.0(bufferutil@4.1.0)(supports-color@11.0.0)(utf-8-validate@6.0.6)(webpack@5.111.1(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.30)(uglify-js@3.19.3)) webpack-merge: specifier: 6.0.1 version: 6.0.1 webpack-subresource-integrity: specifier: 5.1.0 - version: 5.1.0(webpack@5.111.1(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.29)(uglify-js@3.19.3)) + version: 5.1.0(webpack@5.111.1(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.30)(uglify-js@3.19.3)) devDependencies: '@angular/ssr': specifier: workspace:* @@ -870,13 +870,13 @@ importers: version: 3.0.4(bufferutil@4.1.0)(debug@4.4.3(supports-color@11.0.0))(supports-color@11.0.0)(utf-8-validate@6.0.6) ng-packagr: specifier: 22.3.0-next.2 - version: 22.3.0-next.2(@angular/compiler-cli@22.3.0-next.1(@angular/compiler@22.3.0-next.1)(typescript@6.0.3))(supports-color@11.0.0)(tslib@2.8.1)(typescript@6.0.3) + version: 22.3.0-next.2(@angular/compiler-cli@22.3.0-next.2(@angular/compiler@22.3.0-next.2)(typescript@6.0.3))(supports-color@11.0.0)(tslib@2.8.1)(typescript@6.0.3) sass: specifier: 1.105.1 version: 1.105.1 undici: - specifier: 8.11.2 - version: 8.11.2 + specifier: 8.12.1 + version: 8.12.1 optionalDependencies: esbuild: specifier: 0.28.2 @@ -899,10 +899,10 @@ importers: version: link:../../ngtools/webpack webpack: specifier: 5.111.1 - version: 5.111.1(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.28)(uglify-js@3.19.3) + version: 5.111.1(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.29)(uglify-js@3.19.3) webpack-dev-server: specifier: 6.0.0 - version: 6.0.0(bufferutil@4.1.0)(supports-color@11.0.0)(utf-8-validate@6.0.6)(webpack@5.111.1(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.28)(uglify-js@3.19.3)) + version: 6.0.0(bufferutil@4.1.0)(supports-color@11.0.0)(utf-8-validate@6.0.6)(webpack@5.111.1(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.29)(uglify-js@3.19.3)) packages/angular_devkit/core: dependencies: @@ -965,17 +965,17 @@ importers: specifier: workspace:0.0.0-PLACEHOLDER version: link:../../angular_devkit/core '@angular/compiler': - specifier: 22.3.0-next.1 - version: 22.3.0-next.1 + specifier: 22.3.0-next.2 + version: 22.3.0-next.2 '@angular/compiler-cli': - specifier: 22.3.0-next.1 - version: 22.3.0-next.1(@angular/compiler@22.3.0-next.1)(typescript@6.0.3) + specifier: 22.3.0-next.2 + version: 22.3.0-next.2(@angular/compiler@22.3.0-next.2)(typescript@6.0.3) typescript: specifier: 6.0.3 version: 6.0.3 webpack: specifier: 5.111.1 - version: 5.111.1(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.28)(uglify-js@3.19.3) + version: 5.111.1(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.29)(uglify-js@3.19.3) packages/schematics/angular: dependencies: @@ -989,15 +989,15 @@ importers: specifier: 3.3.1 version: 3.3.1 oxc-parser: - specifier: 0.153.0 - version: 0.153.0 + specifier: 0.154.0 + version: 0.154.0 typescript: specifier: 6.0.3 version: 6.0.3 devDependencies: '@oxc-project/types': - specifier: 0.153.0 - version: 0.153.0 + specifier: 0.154.0 + version: 0.154.0 tests: devDependencies: @@ -1029,14 +1029,15 @@ packages: resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} engines: {node: '>=6.0.0'} - '@angular/animations@22.3.0-next.1': - resolution: {integrity: sha512-YdJE9xE7EO9xJtXNcWFXCatofYRXmkZm5oPOaKBCTHvX12Iw3SQsybxXr23Lq1gHscAZnqffEfhQ78TdnSl5wA==} + '@angular/animations@22.3.0-next.2': + resolution: {integrity: sha512-YJCMY2lUtiIQNfqTXnwbKjePHAEq4sION4i+66eH7Fhog55eust9BQ7zVCp8CtfPUF2kNwhiMyU6DbZQCxmN1w==} engines: {node: ^22.22.3 || ^24.15.0 || >=26.0.0} + deprecated: '@angular/animations is deprecated. Use `animate.enter` and `animate.leave` instead. For more information see: https://v22.angular.dev/guide/animations.' peerDependencies: - '@angular/core': 22.3.0-next.1 + '@angular/core': 22.3.0-next.2 - '@angular/cdk@22.3.0-next.1': - resolution: {integrity: sha512-WAOIXq+8KtLn59xdt7rRKgdxO3VUCGSlSzoUJQez4EM+/MFa8GPMTHZ392ewBA3R2XpEJNFbSIH61A0+yfNBMg==} + '@angular/cdk@22.3.0-next.2': + resolution: {integrity: sha512-UlleZDkYVds93nKOJ32g393Ca7XyrG+c8r5XoQG3aQHKe5L+lc5HjmPGAJ1Y80iotUQG3NFsgwLnweCGV12yWw==} peerDependencies: '@angular/common': ^22.0.0-0 || ^22.1.0-0 || ^22.2.0-0 || ^22.3.0-0 || ^23.0.0-0 '@angular/core': ^22.0.0-0 || ^22.1.0-0 || ^22.2.0-0 || ^22.3.0-0 || ^23.0.0-0 @@ -1044,33 +1045,33 @@ packages: '@angular/platform-browser': ^22.0.0-0 || ^22.1.0-0 || ^22.2.0-0 || ^22.3.0-0 || ^23.0.0-0 rxjs: ^6.5.3 || ^7.4.0 - '@angular/common@22.3.0-next.1': - resolution: {integrity: sha512-vJ4X+f43MIfOMKJc53ttkwxKSP6pfOFgfZ1m+W6fAIF5Ydy49/GnXIUbM2rtcbhsSYgh+95YMs3lhUrgyMXEUw==} + '@angular/common@22.3.0-next.2': + resolution: {integrity: sha512-w4oG/QGNTC5oY+vBf4WnODDr56vvPt8Y0TgLpDy6mW6ephWaIx8dAgvFdlpohU5R1cOYAABeLboQHtS6pdP8Fg==} engines: {node: ^22.22.3 || ^24.15.0 || >=26.0.0} peerDependencies: - '@angular/core': 22.3.0-next.1 + '@angular/core': 22.3.0-next.2 rxjs: ^6.5.3 || ^7.4.0 - '@angular/compiler-cli@22.3.0-next.1': - resolution: {integrity: sha512-yfJ4jfjdTjTJYhWT3A8aEWZsk4xwTTB/S0oVi9VWm7HAb2trn5c3F5B/nWUJSSX5CXQ8TAnzN27mca2ZhyUPLw==} + '@angular/compiler-cli@22.3.0-next.2': + resolution: {integrity: sha512-r0Cq0n88GBn/iOFAL/oLGC3uBAGMINAYtZDA0gvIG+WZPuyo0CPeUEmAndAFDzuiAPegfXVi8yPlfzLS6uP5mw==} engines: {node: ^22.22.3 || ^24.15.0 || >=26.0.0} hasBin: true peerDependencies: - '@angular/compiler': 22.3.0-next.1 + '@angular/compiler': 22.3.0-next.2 typescript: '>=6.0 <6.1' peerDependenciesMeta: typescript: optional: true - '@angular/compiler@22.3.0-next.1': - resolution: {integrity: sha512-58W3f6lwfMwAlmoKfK4qigYuRQkRF0ZGZiSjAEXFhU1sV48pgQaADiz/2cfNGD3pEcb99sW9853XzDA9zzcbqA==} + '@angular/compiler@22.3.0-next.2': + resolution: {integrity: sha512-ayaEJt1ZDS9qKmAvQHq47m3FSMt2Au95KL+ucElgBHGV0TIuXjLdc25smiRj6Jr47U9D1zbEnFoVgWhKBuCl7g==} engines: {node: ^22.22.3 || ^24.15.0 || >=26.0.0} - '@angular/core@22.3.0-next.1': - resolution: {integrity: sha512-6Y4bgGkZmje+7Ct8L3PARXfpm5ZrUYuWYKNz4Xtlr08Fsx1Fp2oroUQ8BsPgZGgfzmHaWyTBE/fvK7ZbybKcjw==} + '@angular/core@22.3.0-next.2': + resolution: {integrity: sha512-9onGgNA0v0q9UI31uVAk3K5Q2r/ye82qwKsQv5FesyZ1hnEyrMMc0kdTzHkgDDRS2xpwVHchZH8BTgJA6A+SGQ==} engines: {node: ^22.22.3 || ^24.15.0 || >=26.0.0} peerDependencies: - '@angular/compiler': 22.3.0-next.1 + '@angular/compiler': 22.3.0-next.2 rxjs: ^6.5.3 || ^7.4.0 zone.js: ~0.15.0 || ~0.16.0 peerDependenciesMeta: @@ -1079,78 +1080,78 @@ packages: zone.js: optional: true - '@angular/forms@22.3.0-next.1': - resolution: {integrity: sha512-dssi5/s/DekmNKDA8zFDgfDVLrK2akZxg0lFO+e/pDeSYBKoalbljtAssPFEdGCiDdfmz6pFasvRw50TYI1ErQ==} + '@angular/forms@22.3.0-next.2': + resolution: {integrity: sha512-1NF2giTkXbULCVTNV8fPooKa8HKYEozWdgzZdCHvtrgIF6C2aW+PN/4ZJTddQjFBjH/4XnQ2xfM7nFCm/SQJuw==} engines: {node: ^22.22.3 || ^24.15.0 || >=26.0.0} peerDependencies: - '@angular/common': 22.3.0-next.1 - '@angular/core': 22.3.0-next.1 - '@angular/platform-browser': 22.3.0-next.1 + '@angular/common': 22.3.0-next.2 + '@angular/core': 22.3.0-next.2 + '@angular/platform-browser': 22.3.0-next.2 rxjs: ^6.5.3 || ^7.4.0 - '@angular/localize@22.3.0-next.1': - resolution: {integrity: sha512-KPZuMWghal8+ZQn3N8s6shDZTyKTFSyw55ooxxhJNt+bOQ++mmjPJ7JCen39qAAILYpgI3IriLnswsbhGnGDNQ==} + '@angular/localize@22.3.0-next.2': + resolution: {integrity: sha512-Q0+i9GURr6A6jUIi5N4b/2ev8ZKDKfcZ1e1B9u6jT+wb1/mryB+8sJnFD3sT7a+E/2wRkNk5GCQuT+HjYzi+HQ==} engines: {node: ^22.22.3 || ^24.15.0 || >=26.0.0} hasBin: true peerDependencies: - '@angular/compiler': 22.3.0-next.1 - '@angular/compiler-cli': 22.3.0-next.1 + '@angular/compiler': 22.3.0-next.2 + '@angular/compiler-cli': 22.3.0-next.2 - '@angular/material@22.3.0-next.1': - resolution: {integrity: sha512-iQrlvYiHEwY9p7c8CIa9pPXfWDuE+4cU3c64X3w8i7ChDBSj/dzsGK31X4EstZ+PXozDzcLH+JXuHkWPg81CVQ==} + '@angular/material@22.3.0-next.2': + resolution: {integrity: sha512-4l6+DzUtwIH+SYrZdT54Lg7sNvFu6+MmLxlQQB8TtC5V5EijqOv1rjLmp+mFFgpVFmwlZwdGS8vJfRvkzKLY2w==} peerDependencies: - '@angular/cdk': 22.3.0-next.1 + '@angular/cdk': 22.3.0-next.2 '@angular/common': ^22.0.0-0 || ^22.1.0-0 || ^22.2.0-0 || ^22.3.0-0 || ^23.0.0-0 '@angular/core': ^22.0.0-0 || ^22.1.0-0 || ^22.2.0-0 || ^22.3.0-0 || ^23.0.0-0 '@angular/forms': ^22.0.0-0 || ^22.1.0-0 || ^22.2.0-0 || ^22.3.0-0 || ^23.0.0-0 '@angular/platform-browser': ^22.0.0-0 || ^22.1.0-0 || ^22.2.0-0 || ^22.3.0-0 || ^23.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/89b8c002e5d47ba1cbc4483d3ec06b0ae0aaccb7': - resolution: {gitHosted: true, integrity: sha512-vfMwQfoXYY5dX8IISR/9DpsLT+py7Zm2Nqj6CybryjFosxMxW4ajXKfJ3Zywu2k0wPfRVoQY+PMZ78vfPji+qQ==, tarball: https://codeload.github.com/angular/dev-infra-private-ng-dev-builds/tar.gz/89b8c002e5d47ba1cbc4483d3ec06b0ae0aaccb7} - version: 0.0.0-8b6cbbca278a26f2495275c5d9d0d8633573162d + '@angular/ng-dev@https://codeload.github.com/angular/dev-infra-private-ng-dev-builds/tar.gz/940c0beef0de4cb128e90962d47ff80e352a1cd8': + resolution: {gitHosted: true, integrity: sha512-6d8sBwbru5J4TsfMlHneqn1PD+K2p5l5HGKOba3Y/Ygl+bUEtxIJyM8aRgX88WWgVwjcaKSByQ6gClVxjqNznw==, tarball: https://codeload.github.com/angular/dev-infra-private-ng-dev-builds/tar.gz/940c0beef0de4cb128e90962d47ff80e352a1cd8} + version: 0.0.0-f8062d1d0f50e59fcb36a9375a596f416204ee28 hasBin: true - '@angular/platform-browser@22.3.0-next.1': - resolution: {integrity: sha512-2mhIqbG0U75M3m2zICdF1qInDsuwiS+asx/xHWXPNAC1Igsiiq2DrCo9lGfKAuYpORvRWJfppXSD88atBeiDJw==} + '@angular/platform-browser@22.3.0-next.2': + resolution: {integrity: sha512-/KrZjns+lbZx9pMpWLI1E7C5y8B5veA8D/5dWJnNuiUmbAWevaEGpsMl1xGnopuE8JrJvUEOI2aSU4oT729FIQ==} engines: {node: ^22.22.3 || ^24.15.0 || >=26.0.0} peerDependencies: - '@angular/animations': 22.3.0-next.1 - '@angular/common': 22.3.0-next.1 - '@angular/core': 22.3.0-next.1 + '@angular/animations': 22.3.0-next.2 + '@angular/common': 22.3.0-next.2 + '@angular/core': 22.3.0-next.2 peerDependenciesMeta: '@angular/animations': optional: true - '@angular/platform-server@22.3.0-next.1': - resolution: {integrity: sha512-sGX0TGZMoN2c5j3kC6egIoTaD8lNurzIlRbbOAlFhkv6I8NJuyStk74pXn9yTsi2co+i/b8eAQe7Jw0mRXa6gA==} + '@angular/platform-server@22.3.0-next.2': + resolution: {integrity: sha512-AyegJGA+qdjqUJGPX8IysYKqt5XUoLYnjmVzzBV3bYrN0kt0g9SRirm4QCExQB30v/g+AKiVI9Fib50VyU/O9Q==} engines: {node: ^22.22.3 || ^24.15.0 || >=26.0.0} peerDependencies: - '@angular/common': 22.3.0-next.1 - '@angular/compiler': 22.3.0-next.1 - '@angular/core': 22.3.0-next.1 - '@angular/platform-browser': 22.3.0-next.1 + '@angular/common': 22.3.0-next.2 + '@angular/compiler': 22.3.0-next.2 + '@angular/core': 22.3.0-next.2 + '@angular/platform-browser': 22.3.0-next.2 rxjs: ^6.5.3 || ^7.4.0 - '@angular/router@22.3.0-next.1': - resolution: {integrity: sha512-Syu0u+mWinMzwLYidzi1yy2wbXmljRucsLE1jwaaf97rZ6Lqqc8cgnfWBSwxVdZ1r8qhR5zlauvAN/+cbiMF3g==} + '@angular/router@22.3.0-next.2': + resolution: {integrity: sha512-b5Sn7sokZxL5rdN0M8Hf0FYexYo+l5vA7gcxpoJld00QPtv6Uq75mqjhp6MFIk8pWhANY3RoKEjHMdUoNgO4kA==} engines: {node: ^22.22.3 || ^24.15.0 || >=26.0.0} peerDependencies: - '@angular/common': 22.3.0-next.1 - '@angular/core': 22.3.0-next.1 - '@angular/platform-browser': 22.3.0-next.1 + '@angular/common': 22.3.0-next.2 + '@angular/core': 22.3.0-next.2 + '@angular/platform-browser': 22.3.0-next.2 rxjs: ^6.5.3 || ^7.4.0 - '@angular/service-worker@22.3.0-next.1': - resolution: {integrity: sha512-KStwvrt0vE8UAfXEE1GtIJm1b/oKu5XNSzCMqF/xE+VjSTrhnwNaKfyqXbK+7dVwr42AcUifTkOKJLq8zlz8fQ==} + '@angular/service-worker@22.3.0-next.2': + resolution: {integrity: sha512-L4a2at4NHToTNHwD03ir3w1ZXSrM/aARE2j4y7tcYBQXPQlSqwC7Bg4QHQecZ8Ua+eh8ssdVdpb8kPL4A+85pQ==} engines: {node: ^22.22.3 || ^24.15.0 || >=26.0.0} hasBin: true peerDependencies: - '@angular/core': 22.3.0-next.1 + '@angular/core': 22.3.0-next.2 rxjs: ^6.5.3 || ^7.4.0 - '@asamuzakjp/css-color@7.1.3': - resolution: {integrity: sha512-1t1U8Cm3RBl9vh3RAZhjkPaduzLSvQJRToRrxpGR/eDRW99mOREleuu+mWfiFa795bUzjwht7NgphCKTs2ianQ==} + '@asamuzakjp/css-color@7.1.5': + resolution: {integrity: sha512-jCpAvj8apa7SKHZYogk0rbXB3Kb0hLFrpkWF+Ap+vBA5rirItnpv9NKyAyEuDuFizvl0qwnj6JWR70RGJ0rOlw==} engines: {node: ^22.22.2 || ^24.15.0 || >=26.0.0} '@asamuzakjp/dom-selector@9.2.4': @@ -1177,10 +1178,6 @@ packages: resolution: {integrity: sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==} engines: {node: '>=6.9.0'} - '@babel/core@8.0.6': - resolution: {integrity: sha512-5zwYt1V4ji3mXKKRIAkohSJ29WepxNwxQi+7y7P5AcVPh1dcxnZBU2cfxHnPwS+u9+lZnXPsv30TQaF2xg+Tfg==} - engines: {node: ^22.18.0 || >=24.11.0} - '@babel/core@8.0.7': resolution: {integrity: sha512-ZIsZWBG6Y1oa+8fkQVp0xeCdjCHBkXFCCIWrmg9SvejVvfcVQgKKRmGrBDWfM7rilhvnRoEuaqa5dkK7KB3pHA==} engines: {node: ^22.18.0 || >=24.11.0} @@ -1201,22 +1198,18 @@ packages: resolution: {integrity: sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==} engines: {node: '>=6.9.0'} - '@babel/helper-compilation-targets@8.0.6': - resolution: {integrity: sha512-HTrtZ4+iA0+kzdbCgvdSk1U4pEl3BvpGnlUbualCGNbrVhhQvhgBzurTUmqpWyvyHbWYWaIfdx2mOlL9LJjsew==} - engines: {node: ^22.18.0 || >=24.11.0} - '@babel/helper-compilation-targets@8.0.7': resolution: {integrity: sha512-ilAZCdOOlOVA7dQbaFNw2f/59p6GW33NxTopc3DvEE3efp3ZXTrGX/yZScaKw8/vd9bq31Ag0EcXiLw32bAgJg==} engines: {node: ^22.18.0 || >=24.11.0} - '@babel/helper-create-class-features-plugin@8.0.6': - resolution: {integrity: sha512-Hy7wG2Pk1LnAkFMq0CbBk789XMTYVzYGxR7tKgk+TOpZckQRME3Tdou4mMT/8DFjJFxFZ8R6UsS4DR/duBKlVA==} + '@babel/helper-create-class-features-plugin@8.0.7': + resolution: {integrity: sha512-7ug3KrOhNVPPxFFLTel0/pMzimKzU/vTlNJKVL/dKguCpmNJZjjibyFbWg+Qs8QHmiIZPNuZ2UNnDGdHWGEpOg==} engines: {node: ^22.18.0 || >=24.11.0} peerDependencies: '@babel/core': ^8.0.0 - '@babel/helper-create-regexp-features-plugin@8.0.5': - resolution: {integrity: sha512-TWnIgw21pAv/FbBKB8ks26DMxQ+A3aBdi1HUBp+QhqSE8QdOqJgSK/FEvdMYPTZcHBBJPZ5pN6DDKmC5ZYPJbg==} + '@babel/helper-create-regexp-features-plugin@8.0.7': + resolution: {integrity: sha512-pyia4a5cQ9fmUuimPO4pl3p8aX9aGBBMqZyNUmwhJGO/e3bIbbt/26Zun6SE1CRvNaUWiTsfLuZeaLX6EIIUWQ==} engines: {node: ^22.18.0 || >=24.11.0} peerDependencies: '@babel/core': ^8.0.0 @@ -1317,8 +1310,8 @@ packages: resolution: {integrity: sha512-Qpm8+wi5xfDkBfollanwriCcKniFfBmMmaKB01GVM6VGzKXo1fdxosZp04qEr5HM+LKhwr3hG1yRy8+ORsficA==} engines: {node: ^22.18.0 || >=24.11.0} - '@babel/helpers@7.29.7': - resolution: {integrity: sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg==} + '@babel/helpers@7.29.10': + resolution: {integrity: sha512-EMCx3AGZ3WfAl6ht7AyQoPm2Lh+BGYEMdc4Q6Hf7uSCDOZFuLq44qNwL+iaim8VlyYhRAo4X43+weadK8ZB4Yg==} engines: {node: '>=6.9.0'} '@babel/helpers@8.0.7': @@ -1330,11 +1323,6 @@ packages: engines: {node: '>=6.0.0'} hasBin: true - '@babel/parser@8.0.6': - resolution: {integrity: sha512-LpGDIYJAzc3Y3PT9x+FxBrGYu2PlWxLmyN7SwPTzaX0LSwEmFJBsRnPBqmaXF2r1v+Zhz6lNiEJ+7yKtM5+Ugg==} - engines: {node: ^22.18.0 || >=24.11.0} - hasBin: true - '@babel/parser@8.0.7': resolution: {integrity: sha512-UkDwEbPO1px2JQSrdh4qIgaH1JgG7GO/pFeJkpxhDxIETkT/QvGpnigXIw75ViHYPtIgKITmVUJqLjiO+JuWjA==} engines: {node: ^22.18.0 || >=24.11.0} @@ -1710,14 +1698,10 @@ packages: resolution: {integrity: sha512-eAD0QW/AlbamBbw0FeGiwasbCVPq5ncW0HNVyLP3B9czqLyh4gvw+5JTSNt6le9+ziAU7mqDZsKTHf3jTb4chQ==} engines: {node: ^22.18.0 || >=24.11.0} - '@babel/traverse@7.29.8': - resolution: {integrity: sha512-I5z7H3bf/41ktsNVLtpN0wAa336HkqIHQ5BuPLEhTkt1jVSyZpeNKIzTgEWmlxjdg81R0IgUCcaE+Ok3NvrfZg==} + '@babel/traverse@7.29.10': + resolution: {integrity: sha512-B3lYAs2nkKskbgQ1nfy8Ma9ao9Q1rntTAQHrzzyDuj+TSO+X3le6PfWwqDvodsb+OumE10eMFfuMCTQSQ5iuIQ==} engines: {node: '>=6.9.0'} - '@babel/traverse@8.0.6': - resolution: {integrity: sha512-ilYV+qLStGzSKLipJlETdvdtpK3rAuwfCmCoGKv15MU4EoeIuLkespSNoNLFGOPSciEHx9TLWtN5mv0fz5w5mQ==} - engines: {node: ^22.18.0 || >=24.11.0} - '@babel/traverse@8.0.7': resolution: {integrity: sha512-bVV0brinsTWgc6ptSIoQo3GbITtl0VZAqEUJbHHcyfDPEu+q/fEvlYEa+6H85HoK8IdksKBFtKJbJFZ6Kff+zg==} engines: {node: ^22.18.0 || >=24.11.0} @@ -1746,8 +1730,8 @@ packages: resolution: {integrity: sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA==} engines: {node: '>=18'} - '@bramus/specificity@2.4.2': - resolution: {integrity: sha512-ctxtJ/eA+t+6q2++vj5j7FYX3nRu311q1wfYH3xjlLOsczhlhxAg2FWNUXhpGvAw3BWo1xBcvOV6/YLc2r5FJw==} + '@bramus/specificity@2.4.3': + resolution: {integrity: sha512-0GEz8qJWokkUVYQYRukmkSHIFNoBH8QTreeHrAFbvz1XFYc3Y+puhMs8PQ5x8GNugiC2l5jvDBXKLbBIByeSBw==} hasBin: true '@bufbuild/protobuf@2.16.0': @@ -1763,12 +1747,12 @@ packages: resolution: {integrity: sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==} engines: {node: '>=0.1.90'} - '@conventional-changelog/git-client@3.1.2': - resolution: {integrity: sha512-jZqwnJwf7nboIlAcw/mkOjVa6DexCcUOgT2oOQgkoi3z9vR8tGFkcMy2BFcYwjhL9sYcDDXkRQDayiDieCoW7A==} + '@conventional-changelog/git-client@3.2.0': + resolution: {integrity: sha512-sD/E0vzpkoDz/dqM1Hhv0ihC4KBw6RXjefeO8SG1XrX+wGkMxe1pKeedkKDPCtr2JjuhbuS9aJNgOvnT8hbeBw==} engines: {node: '>=22'} peerDependencies: conventional-commits-filter: ^6.0.1 - conventional-commits-parser: ^7.1.2 + conventional-commits-parser: ^7.1.3 peerDependenciesMeta: conventional-commits-filter: optional: true @@ -2036,9 +2020,9 @@ packages: '@noble/hashes': optional: true - '@firebase/ai@2.16.0': - resolution: {integrity: sha512-WRVxl58B61Orwf7st7949ZIW2L4huV80Gpaum1z8v2eH+m2xaxWtC8mOjQyVAOirr6O/yf89cBjlPvoyBpBfTg==} - engines: {node: '>=20.0.0'} + '@firebase/ai@3.0.0': + resolution: {integrity: sha512-66jJAi6JUmyXna89xaDONmfvupO1wNc3f2BegPwUImagbbpKdVeycE3kMqbU4jiDLL14N5PHzadovvxpaqD2hw==} + engines: {node: '>=24.12.0'} peerDependencies: '@firebase/app': 0.x '@firebase/app-types': 0.x @@ -2076,20 +2060,20 @@ packages: peerDependencies: '@firebase/app': 0.x - '@firebase/app-compat@0.5.18': - resolution: {integrity: sha512-77H57WuGEsgrv59HdtuhHG+eQdP8di6myz7O93yW6yTZlR/tLSmsCxCq5vHp6AWWDwSV0EVtKUvizXQiQZVBWA==} - engines: {node: '>=20.0.0'} + '@firebase/app-compat@0.5.19': + resolution: {integrity: sha512-pdT+THcv3h43LcawVJ+MUTzofL7umBaCvCgdbr01fnTXrbQU2RSHb0Po/YsF7w+tgjCycg84zCgZbd51NmoIaA==} + engines: {node: '>=24.12.0'} '@firebase/app-types@0.9.6': resolution: {integrity: sha512-yPLahy7Esfu2w/yme3msVK4xTkDXQqq6szfQn8yVOQpCKiT5GVFjqNpLbuz6NkX0WuTwUidkmPewW3r4xkvpeg==} - '@firebase/app@0.16.2': - resolution: {integrity: sha512-fyCLPahl3r0Zb7Wzm+JLWcOOH3SaDgAyJzJ8EWIDiv51aNiv+2Pjbpa6myEuP7iXxjmJqrcbVm0GUF7h229T1Q==} - engines: {node: '>=20.0.0'} + '@firebase/app@0.16.3': + resolution: {integrity: sha512-L2mYqVV2ilnek+W4JG7yasA97TsXCwx+f7/abweX5uSp3dkPa5Luiglt3s9YWfh1fBR3SWH/AkRgAwUpDGyCjg==} + engines: {node: '>=24.12.0'} - '@firebase/auth-compat@0.6.11': - resolution: {integrity: sha512-L+xp3DTJQrBV08Vt0UhcL/ofUOSjXom7JOJfbT6oas4o7gCrQnTYFKs8wfiuKLmHoo8BxMr5LdgjAs7o6aAsqQ==} - engines: {node: '>=20.0.0'} + '@firebase/auth-compat@0.6.12': + resolution: {integrity: sha512-Vspo2zx1RR6LB7zuM7TduD1clMLT/x3Wr97u2jf9s2JrhYArJAFKeEu8bRWF1DoX54z6W9N77SdPRbPAiLKPpg==} + engines: {node: '>=24.12.0'} peerDependencies: '@firebase/app': 0.x '@firebase/app-compat': 0.x @@ -2103,9 +2087,9 @@ packages: '@firebase/app-types': 0.x '@firebase/util': 1.x - '@firebase/auth@1.13.6': - resolution: {integrity: sha512-MKRLvF72HJ/irNn2fJR0Gv5FOZNtqv1IBeJHRwK8l8pZ/wi9vuUw8ecyCMvQDDNxfS6uW0MWACL5KC4Y8Pp+fA==} - engines: {node: '>=20.0.0'} + '@firebase/auth@1.13.7': + resolution: {integrity: sha512-00wn8cMrh1ZyEPF6RO5G081Xq1b57FN0QxChiAda0hy/D3Q5TrMk30qhWszRWCgXPRipw6yM6qtqUew3u3qEjA==} + engines: {node: '>=24.12.0'} peerDependencies: '@firebase/app': 0.x '@react-native-async-storage/async-storage': ^2.2.0 || ^3.0.0 @@ -2141,9 +2125,9 @@ packages: resolution: {integrity: sha512-/JGpvszLoNXNgzilRXocigGfFF4hbcPA9wN1i1kjJx6oKkXgkHteZYl3lQs1lJX3ETDf6bv7zI15lPMVUp4sAQ==} engines: {node: '>=20.0.0'} - '@firebase/firestore-compat@0.4.14': - resolution: {integrity: sha512-HJ2HBNgNPrRsWIJ4E+Kc0u0h1U7HlF7If8AtYjie9HZXlMUmwYSJaXBj9VFm6gkSj5dlKZNcOQ0Ou25CVVG6rA==} - engines: {node: '>=20.0.0'} + '@firebase/firestore-compat@0.4.15': + resolution: {integrity: sha512-/hWibRTNFsWkZZi1dbDS1hiJIMezYUMPrRjMmBXevel5b368XWY+NvfOaSxgy3+fDb6I+uZ7SKGFyL06gW3vTA==} + engines: {node: '>=24.12.0'} peerDependencies: '@firebase/app': 0.x '@firebase/app-compat': 0.x @@ -2154,9 +2138,9 @@ packages: '@firebase/app-types': 0.x '@firebase/util': 1.x - '@firebase/firestore@4.17.2': - resolution: {integrity: sha512-SiyQEK1dYPOjItFVA+kZGdvOoRAtMebDBvUaSxJPNZMkKcRwYYK2kzWo5UXTZZj523uMxJnkr+KKVySEMme3kQ==} - engines: {node: '>=20.0.0'} + '@firebase/firestore@4.18.0': + resolution: {integrity: sha512-28aKMUeM+L4fuxx5hurvQK31KLh/KRutaP1544avVWuR4mNTAC0FQoHg8gbkN5NoJj3yN9/ZFGSFSVPR8/x/BQ==} + engines: {node: '>=24.12.0'} peerDependencies: '@firebase/app': 0.x @@ -2224,8 +2208,8 @@ packages: peerDependencies: '@firebase/app': 0.x - '@firebase/remote-config-compat@0.2.29': - resolution: {integrity: sha512-mY7JtTISK6F4g4T0x3kVepr5cp0NXL7f5yjIUXXGaTrg4qUlFBWRbENaHaqZ78dwmNcLm24h/yPsOVRlDXEXhA==} + '@firebase/remote-config-compat@0.2.30': + resolution: {integrity: sha512-Q4sQKhQ8TPEMOcCUvUBfGeAP1DfcG7FGBfA9yFUn/XRr4vSToY0DTmK28Nh7lgLQYzs6D5Ng6knSoXH+aGn1Tg==} peerDependencies: '@firebase/app': 0.x '@firebase/app-compat': 0.x @@ -2233,14 +2217,14 @@ packages: '@firebase/remote-config-types@0.5.2': resolution: {integrity: sha512-i8k1omVfoAnaT1ZPv2FFjxMZrATYsO/GnFgHEj5+7fAJLzi8wLnGGv1WK06oEAD6ltrWXSO1HzeNyajn/NjMWw==} - '@firebase/remote-config@0.9.2': - resolution: {integrity: sha512-Rii93DkXjM+RE/ytHdYa7EIiQLJbdBr+W8EEiqd/vbLCOC+1FdkDuRiumJBp6t3yewHGbdqbpjB3fk3z0cZ+8g==} + '@firebase/remote-config@0.9.3': + resolution: {integrity: sha512-9DbuFNAFeUbVJQZmwLZfdoLRNFWV43V4UqaRpDkaSIdTmVWhzctPeISGkWe4sKTsqpoyldRumquS0gqWZg05fQ==} peerDependencies: '@firebase/app': 0.x - '@firebase/storage-compat@0.4.5': - resolution: {integrity: sha512-vO0tFPxXbKDKdlTu8tYT08S9t9ezUTJYEdLCULpWxWq2aq6zojwN1QmAB+1a50AI/g47GlWUJn1XPncm/PDZsw==} - engines: {node: '>=20.0.0'} + '@firebase/storage-compat@0.4.6': + resolution: {integrity: sha512-te0IsqX1bM+OZCJF7jNSKXcMqCA2yYmpshRHjX4axy0lI/P9ZP9GLjEZMVL8wrjUWF4e3cZxMZTbck3Zd5E1hA==} + engines: {node: '>=24.12.0'} peerDependencies: '@firebase/app': 0.x '@firebase/app-compat': 0.x @@ -2251,9 +2235,9 @@ packages: '@firebase/app-types': 0.x '@firebase/util': 1.x - '@firebase/storage@0.14.5': - resolution: {integrity: sha512-r2tozN/BlEewLi70tJNUQPzWwbex9GM7NgXZsamHKQrjKEfcR0i4jGgHBKAKA4hbwiPE9eNMb3hcxWnDpeJZwg==} - engines: {node: '>=20.0.0'} + '@firebase/storage@0.14.6': + resolution: {integrity: sha512-9WjohxpjbfcySjCp8kIbd1ZhtmCQFQLqPtDQ+wmZ5PVXimTDjDsSx/RZjwwK89MjCTXB9fCdmvZkJxplZn4W1A==} + engines: {node: '>=24.12.0'} peerDependencies: '@firebase/app': 0.x @@ -2295,8 +2279,8 @@ packages: resolution: {integrity: sha512-IJn+8A3QZJfe7FUtWqHVNo3xJs7KFpurCWGWCiCz3oEh+BkRymKZ1QxfAbU2yGMDzTytLGQ2IV6T2r3cuo75/w==} engines: {node: '>=18'} - '@google/genai@2.25.0': - resolution: {integrity: sha512-g1hyidWxahzIg+hh+lLqPeBJl+Fu6051we2QKhFXdWgE4yGYf8PWOS8S3mMAdpe3eLx3KCWmhQR7GWyd3LYT1A==} + '@google/genai@2.29.1': + resolution: {integrity: sha512-keU+JsY11fT4ykdFA6Q8iQHBgOAAVssFLxPtXRjCIEr5Zt2GzE75c22bBmq87XbhgtpuKaObnSgsrG60rj0ZMA==} engines: {node: '>=20.0.0'} peerDependencies: '@modelcontextprotocol/sdk': ^1.25.2 @@ -2304,8 +2288,8 @@ packages: '@modelcontextprotocol/sdk': optional: true - '@grpc/grpc-js@1.14.5': - resolution: {integrity: sha512-7VZM+SVdEcUUqSQeNI3zM8Qs/BhQKZndPo2h5VkYkAM8Iz0wJIa8mKV5ekQGqG8UUsnkQ0NMxIxwkIHYvj0qOw==} + '@grpc/grpc-js@1.14.6': + resolution: {integrity: sha512-SgC/B3/VZ8lTyOZ4Cwk6f1skWhmkNAaVfiX90qd5u1rRXGKk1//8FTUEN9GvOUq7rfWIqAYkA3CoyumYVpOS6g==} engines: {node: '>=12.10.0'} '@grpc/grpc-js@1.9.16': @@ -2358,15 +2342,6 @@ packages: '@types/node': optional: true - '@inquirer/confirm@6.3.2': - resolution: {integrity: sha512-Xvr/0HggjddPtGppuqVmxhTw+Hr8PvsZ/k0HmOEaAqQEt80OITNkFWnsdNmyT0/eM4Ab+iJLx2R8rctlEyfSVg==} - engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} - peerDependencies: - '@types/node': '>=18' - peerDependenciesMeta: - '@types/node': - optional: true - '@inquirer/confirm@6.3.3': resolution: {integrity: sha512-ImOBgpl/sJTFp8naeSkNu4S0Sf8F8pxv3/CUx+LoBAdQvCVqFz6rSB2A/oZYsJge3+yphbEDxEkfaRNYq1OU0A==} engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} @@ -2443,15 +2418,6 @@ packages: '@types/node': optional: true - '@inquirer/prompts@8.7.2': - resolution: {integrity: sha512-QoRB4wFIjgH5iOhSjoIKMkTvSHDuV+O3OITlIqAYO0oK5x364GJILXiMBvlPiE+klg7Xx9tq5XVqQHcGUDYYPA==} - engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} - peerDependencies: - '@types/node': '>=18' - peerDependenciesMeta: - '@types/node': - optional: true - '@inquirer/prompts@8.7.3': resolution: {integrity: sha512-DHK20kO+H5k/XcJQpHQUpVQbMYHHhZchlGWhZVMUV4WIuRuBW5744hipOtriE1vMqPLgnIyXFMHWKkIbTepa4Q==} engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} @@ -2488,15 +2454,6 @@ packages: '@types/node': optional: true - '@inquirer/type@4.1.1': - resolution: {integrity: sha512-yJoHYrMnxIsJZCY+0Vb66Dy3he3kL3e2wOBKhoSwWWAzZAY82emlxwgprCtp6yRixvNRNq9ztfRWQYPNr3Go7A==} - engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} - peerDependencies: - '@types/node': '>=18' - peerDependenciesMeta: - '@types/node': - optional: true - '@inquirer/type@4.2.0': resolution: {integrity: sha512-2rnHX8RPo4glJYqnFc/v3nQeIhDcjyRRyaT7MGcfL/8xv/8K03SXYoA2tX06WVSKywWOE/ncbG3dtuzLjR5TCQ==} engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} @@ -2674,12 +2631,12 @@ packages: '@leichtgewicht/ip-codec@2.0.5': resolution: {integrity: sha512-Vo+PSpZG2/fmgmiNzYK9qWRh8h/CHrwD0mo1h1DzL4yzHNSfWYujGTYsWGreD000gcgmZ7K4Ys6Tx9TxtsKdDw==} - '@listr2/prompt-adapter-inquirer@4.2.7': - resolution: {integrity: sha512-k180vxSv2KqmnDxoato6pCqEJsov4U5C0EVmldZkAdNeVBxY48A/LM6TCNPGIzDK/iZSfsZP0Mw+gbWc5hXLyA==} + '@listr2/prompt-adapter-inquirer@4.3.1': + resolution: {integrity: sha512-NnKK9IrkBPr3v9DxXz/MTxQ989bJEfH1Q6Qr+MA9XU0QRXEgAQJO4XjFs8OzPQI5WYScz0kXi9WNDkANr2bRVw==} engines: {node: '>=22.13.0'} peerDependencies: '@inquirer/prompts': '>= 3 < 9' - listr2: 11.1.0 + listr2: ^11.0.0 '@lmdb/lmdb-darwin-arm64@3.5.7': resolution: {integrity: sha512-Y0CUvoj3ToaPQGdiqtodR0yrbAToNV74ylGQGSsNzmACO6iqKIUdU2OyFhkB+AAYpyfCA6oYh8hxbtc7eZhklA==} @@ -2754,9 +2711,9 @@ packages: cpu: [x64] os: [win32] - '@mswjs/interceptors@0.41.9': - resolution: {integrity: sha512-VVPPgHyQ6ShqnrmDWuxjmUIsO9gWyOZFmuOfLd9LfBGQJwZfy0gvv9pbHSJuoFNIYC7ZDX9aoFwowjcdSC4E8w==} - engines: {node: '>=18'} + '@mswjs/interceptors@0.44.0': + resolution: {integrity: sha512-IYHevRStF4ijM20GI9c9Ndhw483NSAjCydQhUr4/zIB0BNLsTVw3vFAxTo6TlvX4QnV60wEHttUa9539TTAq+A==} + engines: {node: '>=22'} '@napi-rs/lzma-linux-x64-gnu@1.5.1': resolution: {integrity: sha512-oTXEIha4SsuXdTA4Iyskj0kpdx2yVXdhd75c2v3xGrHFfVMsbhTPZU/nMPL4sWKo4pBHm3aucLaqGlF696dTyQ==} @@ -2952,12 +2909,6 @@ packages: peerDependencies: '@octokit/core': '>=6' - '@octokit/plugin-paginate-rest@15.0.0': - resolution: {integrity: sha512-lw9A9YL5s4VPj+VEx8uMxaxQm2YTgrPDkrLEuZuu18R8TK3oPcXF4K6t52nx6xn1RcogZC9i188sAr/4XYJaQQ==} - engines: {node: '>= 20'} - peerDependencies: - '@octokit/core': '>=6' - '@octokit/plugin-request-log@6.0.0': resolution: {integrity: sha512-UkOzeEN3W91/eBq9sPZNQ7sUBvYCqYbrrD8gTbBuGtHEuycE4/awMXcYvx6sVYo7LypPhmQwwpUe4Yyu4QZN5Q==} engines: {node: '>= 20'} @@ -2997,27 +2948,21 @@ packages: '@octokit/types@18.0.0': resolution: {integrity: sha512-l6bAF43PNxkJp6g+W4PjoUSSkxHomXw2nOum5CTftJz1NlV3vu93NImgOYtLf6CbBUb5j+fiuzW0PPQ5JTSvZA==} - '@open-draft/deferred-promise@2.2.0': - resolution: {integrity: sha512-CecwLWx3rhxVQF6V4bAgPS5t+So2sTbPgAzafKkVizyi7tlwpcFpdFqq+wqF2OwNBmqFuu6tOyouTuxgpMfzmA==} - - '@open-draft/logger@0.3.0': - resolution: {integrity: sha512-X2g45fzhxH238HKO4xbSr7+wBS8Fvw6ixhTDuvLd5mqh6bJJCFAPwU9mPDxbcrRtfxv4u5IHCEH77BmxvXmmxQ==} - - '@open-draft/until@2.1.0': - resolution: {integrity: sha512-U69T3ItWHvLwGg5eJ0n3I62nWuE6ilHlmz7zM0npLBRvPRd7e6NYmg54vvRtP5mZG7kZqZCFVdsTWo7BPtBujg==} + '@open-draft/until@3.0.1': + resolution: {integrity: sha512-s7/9ELP4aP9YZtW7RJaa0Xf3RISaRH9+EFN18FtykJYRHGNrl8f9ymvoNXzjhrjmftFvkQudtuDU9RYhk0xs3A==} '@opentelemetry/api@1.9.1': resolution: {integrity: sha512-gLyJlPHPZYdAk1JENA9LeHejZe1Ti77/pTeFm/nMXmQH/HFZlcS/O2XJB+L8fkbrNSqhdtlvjBVjxwUYanNH5Q==} engines: {node: '>=8.0.0'} - '@opentelemetry/context-async-hooks@2.11.0': - resolution: {integrity: sha512-Tr79DyWI8itsBdg+jH+opjfrwLzX+erk1/ExkIwhWoAVjVrJIn2y5+cGjTC0Vy8fyNIA/y8wuJPZwr1T3xCZeQ==} + '@opentelemetry/context-async-hooks@2.12.0': + resolution: {integrity: sha512-iK/1nD7mmZVkuYjVjkdFBiCvzntJdFny1S3CYbUYyOq7V5enL6XX+gJ7eh965L1LnS11VA8mQ7Ej/ck04SB0MQ==} engines: {node: ^18.19.0 || >=20.6.0} peerDependencies: '@opentelemetry/api': '>=1.0.0 <1.10.0' - '@opentelemetry/core@2.11.0': - resolution: {integrity: sha512-7YP44XH0tV6+Mb54x2YGf84i7yi+31MBZlE8JwvozkxyTvXbSp10X7cI7YE49ChJ3shMJoBmCJF3+1QFBJctGA==} + '@opentelemetry/core@2.12.0': + resolution: {integrity: sha512-1HsSAuvT4/my0QrXWsyXFdWjKaPeCHsismKZgEEIb8NK13LP6DrMRlsUbvD/0F9mDWK3xDom9AMv3IscxgOnOQ==} engines: {node: ^18.19.0 || >=20.6.0} peerDependencies: '@opentelemetry/api': '>=1.0.0 <1.10.0' @@ -3026,136 +2971,133 @@ packages: resolution: {integrity: sha512-eSYWTm620tTk45EKSedaUL8MFYI8hW164hIXsgIHyxu3VobUB3fFCu5t0hQby6OoWRPsG1KkKUG2M5UadiLiVg==} engines: {node: '>=14'} - '@oxc-parser/binding-android-arm-eabi@0.153.0': - resolution: {integrity: sha512-BcWu25dMwMjHpaDCEidmpAi7rGg1qZoihJeUUH9bD55pVZ0z4Wa5CQgqSTo0R41H81oozisQKW3WqvAN2m77IQ==} + '@oxc-parser/binding-android-arm-eabi@0.154.0': + resolution: {integrity: sha512-sDdEtDl7vKdCvf54j72RJbowE3jCmyRz/+oOB6IAwQxAHZ0eI+Dcj4hvVnm0b9Dy31plUkdmfds9C+/Zt895Tg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [android] - '@oxc-parser/binding-android-arm64@0.153.0': - resolution: {integrity: sha512-9l5rdp2DhRqVriBHsNpu5cEf+hizZ4B58rt93HzyQEBUgZiC6qe2oFpaAAnXGCxHGnQExI1s7S2G0QhFacUZ6w==} + '@oxc-parser/binding-android-arm64@0.154.0': + resolution: {integrity: sha512-UHrDPELzvWZoVkIDdSycj4bzcTMCJG3IbN++mSUgVBaUtNtWHjQ98U6Vz5bpF/1Ic3FYJyYhMr74huO5tVvpZw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [android] - '@oxc-parser/binding-darwin-arm64@0.153.0': - resolution: {integrity: sha512-EcpsyRXHhENDpekpG4QNaSZj2F/BAz4Gh4o89zZwNss86R8KyRSQ6sXbAj74Xyeb4z3j/yBZZ6joQZhraIWyfw==} + '@oxc-parser/binding-darwin-arm64@0.154.0': + resolution: {integrity: sha512-mokYzajOSXxtSRDWCdHARGdntrKH+rZZ24Jzo8AtafyNzz0NARmpMm26Tc5B6qiGrr6UliWibHCOif1qRN2vaA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [darwin] - '@oxc-parser/binding-darwin-x64@0.153.0': - resolution: {integrity: sha512-hlj80HUQy07/2lYp6rmxH+3u+H2ik3rjLR52CUdPApxmZCNxASDUf/+ykTzAHS/fehftmVMATKvPaWmuLHuasg==} + '@oxc-parser/binding-darwin-x64@0.154.0': + resolution: {integrity: sha512-WbtUBrclOVItWfLRSvs+Z/WLtXxJ2Ctv50PyuX894HzDN5/e0qzxVpTNPIVMzOerBhgKtFsSMcfxlYIAbvdVgg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [darwin] - '@oxc-parser/binding-freebsd-x64@0.153.0': - resolution: {integrity: sha512-fWRcv2pMSh1qtez46MOu8vuUJ5fiaQZPHSphbWulbL7SHBMKfRgZl2PzjXZYQJbq7NEXyXbgIuIuUm+/Rmkv8w==} + '@oxc-parser/binding-freebsd-x64@0.154.0': + resolution: {integrity: sha512-YooHZSLawOe2fqm+lDdG8oAaQh2NYV/RMYTYzWRLy72stm2BgE9O/8Al6kFENjkmzyyBccDLLBirGxVtpzQfrw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [freebsd] - '@oxc-parser/binding-linux-arm-gnueabihf@0.153.0': - resolution: {integrity: sha512-paZeUK1GXShl/bOJQET1/8AQxayFeuN4SIWAkgTRxE7ZQwv9uYSB2n0648smygP22N7jcI3zxAm0Z+ALNiIKxg==} + '@oxc-parser/binding-linux-arm-gnueabihf@0.154.0': + resolution: {integrity: sha512-hKOwhZbrXdTwc4bGVfbLCXl9f2swl6zWdISocLQSNX9MgPmwHE3qGzOUkCrkuGpwdCVHPnkfOwsqTuYsrP5uNg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] - '@oxc-parser/binding-linux-arm-musleabihf@0.153.0': - resolution: {integrity: sha512-qFPp5NwMf4K1C0O+DCVGmcbOaYyYAq98Rvv9p4VSfb777iUwlJCqMUxf6QG9N5f5ls4TEX6LitOZHi+6GL6rYQ==} + '@oxc-parser/binding-linux-arm-musleabihf@0.154.0': + resolution: {integrity: sha512-tnjiooXEU013349O/yxKbF5Cusp1CfHWQ8kYR8tGeRzR/P4sOpQGQo21fsS71M0fMD6ESLq+lbvXFWi43PuKeQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] - '@oxc-parser/binding-linux-arm64-gnu@0.153.0': - resolution: {integrity: sha512-oufEhZCX7saeo27Logra8cvBR1jQ/yw2zUI2hHmWw3qA/XYqEW31FOvLQBv7DWN32NjVdlcaP11WBBZTJo4k9g==} + '@oxc-parser/binding-linux-arm64-gnu@0.154.0': + resolution: {integrity: sha512-Cao2kj+8d7BT6XJU1gV9eVQjV+Cr+e8vQm3tddDRZiM2hHrMDxwy3j6t+ofaSl0idyuPPCZ4AlpmQoj8yOT1QA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] libc: [glibc] - '@oxc-parser/binding-linux-arm64-musl@0.153.0': - resolution: {integrity: sha512-zXYzuAqng85h6+LI3iRm0zeMLX1/K2z41ZAMWxVRgt0D10VvZUaV5q+TEB46KDhoKBpdexoTLC6QoB/EiP2oqQ==} + '@oxc-parser/binding-linux-arm64-musl@0.154.0': + resolution: {integrity: sha512-uiwA0V3q8llWA7/8r+a9BBjk4j5YMDmi6wfOsyQfGOJMQSLHEiZ3HI8ulea766ykUa9wJFWk9XWEl4uJrZLQqw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] libc: [musl] - '@oxc-parser/binding-linux-ppc64-gnu@0.153.0': - resolution: {integrity: sha512-J7+NLwLEzdkKM9cKyPHH/AjXXstWOs98KYgLrPPnO4N9MO8EZRBfPNnBcS3S67uMOBkRpUmCl2FhRnWdQ5CbBA==} + '@oxc-parser/binding-linux-ppc64-gnu@0.154.0': + resolution: {integrity: sha512-LICfN0Xs4Zdko+bJy6eOAEQojDuIR26suF2JtOsMaXjgbCrpRSH7/NCvJhY13YvYBx12V6hnwrL8n0lk0rbNJQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ppc64] os: [linux] libc: [glibc] - '@oxc-parser/binding-linux-riscv64-gnu@0.153.0': - resolution: {integrity: sha512-sD0K11GQTLWylln54nPMMTtgmBbQrR5It/vMpZpy/fogiw8O2AvIE+9LuWUXddrWk9zvFIHGRcSgFn4IHOfoYg==} + '@oxc-parser/binding-linux-riscv64-gnu@0.154.0': + resolution: {integrity: sha512-gbbSYxLF8ssRbQ2qQPy+2EhGNwNH1bAoRdkk6cnWaKHqmCikpSIPtpXjFbCCtjTi0uojqOM3ghFQwQPbDQsU0Q==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [riscv64] os: [linux] libc: [glibc] - '@oxc-parser/binding-linux-riscv64-musl@0.153.0': - resolution: {integrity: sha512-7L66uql2BhvSz51LwfExjOdn6et/ydTiQxl23VApdKzJhTc3TczA1Qt6VR/UcvlQrL2ZbytIJb447vh7p5wmUw==} + '@oxc-parser/binding-linux-riscv64-musl@0.154.0': + resolution: {integrity: sha512-nSnRYYDLCcRVjSQzF3g6rKqCNYwo5o+aQgnn8BBXfe+ofCgMGlGqkfRyvXm6ptZvoxNkkWtLlqM8W7Xc3j6ZnQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [riscv64] os: [linux] libc: [musl] - '@oxc-parser/binding-linux-s390x-gnu@0.153.0': - resolution: {integrity: sha512-hXV3T7mNyrPR19rbPYkglkQJtoM/UNo/xwbZeeqdAxEg8PXgb4LXXuxPpGgI4bzRRCWwLUxdDSS58xIyC2u1TA==} + '@oxc-parser/binding-linux-s390x-gnu@0.154.0': + resolution: {integrity: sha512-BxBQoJKtzRQgZEKQIt6CDUm8RGKC5T8Qo4576yDa5/xgc10X3fBDItMbRabzLtbUBfTqKLl+8AVr/H6CF7W22g==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [s390x] os: [linux] libc: [glibc] - '@oxc-parser/binding-linux-x64-gnu@0.153.0': - resolution: {integrity: sha512-Da8Vqs1xdtoDYtEjmozD7wGgJeLBQkMbR0WUBECOSxodyX5DXy8ySlvJWbukyO2Q0+TM9EK+4LT6tbq0jCg4hQ==} + '@oxc-parser/binding-linux-x64-gnu@0.154.0': + resolution: {integrity: sha512-/whkust2vYy+GjRUVL5cWaJmEu6jkdFfo5mkxgckyeOX61eMwo4iBoiwzeJZeMWAdzb/XOtQyLApHhFht3sOWA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] libc: [glibc] - '@oxc-parser/binding-linux-x64-musl@0.153.0': - resolution: {integrity: sha512-zGAPxNTIIw6X5WyTRscIFE/oDjpdNtrrl32ing6NOXdGjzRucadnxBilexLdeYQ9YxixF93GujpXyQ0jw7ddSg==} + '@oxc-parser/binding-linux-x64-musl@0.154.0': + resolution: {integrity: sha512-EXURTyW9riw4B/kZnnE+58emAtw6+3VEszQfXUZkRqGaKIcmWDYkZtKB+xY/blHvuZ+Qy/fDclc31nmUCgtkSA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] libc: [musl] - '@oxc-parser/binding-openharmony-arm64@0.153.0': - resolution: {integrity: sha512-GoHzpqP5XyPIp9DHokoEdCp+K4UPl/h1neFj1uXnUB3pJYyOQ7NOhFVOttNUgCGcj2eywAvu5cRivTOSq4S10g==} + '@oxc-parser/binding-openharmony-arm64@0.154.0': + resolution: {integrity: sha512-ZRFooyH3qsR6vuR4jFGlG3XM36490SvvXtNaUahBK4zHpooMdTHNaOMtGGJI0EW38m5hi4rOco9kw7ZwCIp28w==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [openharmony] - '@oxc-parser/binding-win32-arm64-msvc@0.153.0': - resolution: {integrity: sha512-xGcy12tO9k7zFbeyQzEuilfWfQpyRq23/fLPfN3wjSWuWv58k46fQT1hsO1KFNykZuVGW9xSJE/z592TmAoSDA==} + '@oxc-parser/binding-win32-arm64-msvc@0.154.0': + resolution: {integrity: sha512-U0Vp+4lvwUa/PnOCJTZOjydj1rX2+VLHz4Y2vSFqlN8/7LTiURgD7z4uQ9yNv9O/NlU3mELBnPDlu7aaSYG1zQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [win32] - '@oxc-parser/binding-win32-ia32-msvc@0.153.0': - resolution: {integrity: sha512-o1ykUjWncvXX6N5a4KebgLjtD8zRui+f1llMhSGoSC9oP2KOqapRpdwmZ3NcNG0axFouwVKmhGBjd+IhA8BksA==} + '@oxc-parser/binding-win32-ia32-msvc@0.154.0': + resolution: {integrity: sha512-Hc8QgsFY5eUUP25we8TISUruibG2cDt0H4IcPG9RKdabb6odkCzIqjV6Gk9beGWGuOwMVCr81H/Su1q363LCCg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ia32] os: [win32] - '@oxc-parser/binding-win32-x64-msvc@0.153.0': - resolution: {integrity: sha512-gxQz7G96Up136fd+2zWrP+o6FYGs9NO4Dyv55IoeJFJUDwLFHcLYY11hhA5wQKs0HoM57Q8esQ3nEVhnCdsjAQ==} + '@oxc-parser/binding-win32-x64-msvc@0.154.0': + resolution: {integrity: sha512-hD7judkUMA/hZPoamvKADs6+an1smp8UTK0aMYRijkvi00K/1vl7vEtmyn2JyQ9S+vibtnfcNzxqTLGyJUZCRQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [win32] - '@oxc-project/types@0.151.0': - resolution: {integrity: sha512-J1yXrIlNDZVzE3ada310xeAw7nH8yCAyLPuUIsjKatFPmfn5bS1oW+cM+QsGOtVWd5nhSpbwZWx/rue+r5Z+PA==} - '@oxc-project/types@0.152.0': resolution: {integrity: sha512-oM/5rLBm2tPkg0iBgkH/FOeR3PCDpY19GTgAZjMFM8h9WI9VW7cLgzp6nwtarYKmovavIQZ+Fe/RKX/8C8O/Rw==} - '@oxc-project/types@0.153.0': - resolution: {integrity: sha512-jSNrRIyvPVLL5E1z/tS5zLAprosGz/FuI1PWwEOz59j3E4TTTzSpqKb/RDGqJhhW0QHYISGXxq582wGx6T11Tw==} + '@oxc-project/types@0.154.0': + resolution: {integrity: sha512-KYOUGQFlLWR+s4QMV2Q9ko38TZm7EdO87Qm7rvZLqk/webse/cDjtyxKhg/47l0byCbs/k46LT8qc4PKjAikzA==} '@parcel/watcher-android-arm64@2.6.0': resolution: {integrity: sha512-trgpLSCKRC/huFjXX/Smh+0sWe4+YtKfktIToiMl59ghz7z+qkH6kMvNnUbLyRs9N11t8l4svSCs1+5B3rOAhA==} @@ -3320,8 +3262,8 @@ packages: '@protobufjs/utf8@1.1.2': resolution: {integrity: sha512-b1UQwcEZ4yCnMCD8DAL1VlbvBJE9/IX4FTIp7BG1xYpf29SLazLSrqUkj4w7Y5y7cCVP6E5tcqqcI0xemPkHug==} - '@puppeteer/browsers@3.2.3': - resolution: {integrity: sha512-2Bt3m6dDAJqmZehn0wiXSYyQmuhyiHHZ7FjvrVaS1W2RoFGV6eCK71UvKYkNJjkwC1Q9nobjhvTMhLQztFVINA==} + '@puppeteer/browsers@3.2.5': + resolution: {integrity: sha512-UoCo3Pynbm37aLEEoydSiG+t24ui7sLD40+07CKxzxNy4PpSowJMkvlLIiV67XjP1lMVynhQVnuwlTABqBH8LQ==} engines: {node: '>=22.12.0'} hasBin: true peerDependencies: @@ -3333,22 +3275,16 @@ packages: yauzl: optional: true - '@rolldown/binding-android-arm-eabi@1.2.11': - resolution: {integrity: sha512-A5kXfGKvKWWZE0TtPrfsvT+q4Y5d1QG8gGUzpYjGydM+fARM9MuX90PrXYXe0XbsDVgyxxNzHo6giCj90bsFNw==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [arm] - os: [android] - '@rolldown/binding-android-arm-eabi@1.2.12': resolution: {integrity: sha512-dB/a1214qKfHMXCpgqR4OZT+jS4kTyEXbQGJPqzobt5EwH5rX080pxE37alt3RzvR1bf1Yz/yGqRfrYAxuPw0A==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [android] - '@rolldown/binding-android-arm64@1.2.11': - resolution: {integrity: sha512-z6cTycz+iJ4PVkuL4HHW4DfTfoeU/2nqYYuSOrTmH7yHK5Y0LCOnA03V4ZNxavyVaU1oOqUgIg2klN/s+USGOA==} + '@rolldown/binding-android-arm-eabi@1.2.14': + resolution: {integrity: sha512-aWR+XKH9+rW7m5fWq/5360J3mHycrx/o6xZM2DQFdJnTMdyP7/WzxPjhtLbVWdKo2y612/4U1bWqb3HQhiF7vg==} engines: {node: ^20.19.0 || >=22.12.0} - cpu: [arm64] + cpu: [arm] os: [android] '@rolldown/binding-android-arm64@1.2.12': @@ -3357,11 +3293,11 @@ packages: cpu: [arm64] os: [android] - '@rolldown/binding-darwin-arm64@1.2.11': - resolution: {integrity: sha512-jShvqNtP6vDC6/A5JOAzbVV+DkgHqhl/ScVCJEbt+TUY6QYz7YnXcrg3sLtFBniro0f/Ld50ZwCWA6f7KYD1nQ==} + '@rolldown/binding-android-arm64@1.2.14': + resolution: {integrity: sha512-QiVxHofl6A8lQU7WAtOUY4QMVZXohxwz7WJJoeh8NBEUyKfIkMRKhYyq8az/1RypNiIXzNTtiS6KwHz0erxYRQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] - os: [darwin] + os: [android] '@rolldown/binding-darwin-arm64@1.2.12': resolution: {integrity: sha512-3YIhqHD96nA5SaYNRBR16HnGv4oavZvXfD/ayHM+oYZ0WD/8lBAtf6zQua4kEyAvpqrluKXl0lnOBoiNby7x9w==} @@ -3369,10 +3305,10 @@ packages: cpu: [arm64] os: [darwin] - '@rolldown/binding-darwin-x64@1.2.11': - resolution: {integrity: sha512-f2i2xiNWq1Z1l2++q2fuhZRdLAT3aqxD6vRNm1RAxpUoBcdqNB3C0s1Bt+K+PbEx2F5F4gQp6hqKkphCY/xF9w==} + '@rolldown/binding-darwin-arm64@1.2.14': + resolution: {integrity: sha512-lTQOzHuBIooh3jIr5XINQkjPGEuGy+Dsy0meXDr6Sjty0b99kYgGh4uBC/8mJX1T0kC8d6S0aPP1Y+bHkh6aBw==} engines: {node: ^20.19.0 || >=22.12.0} - cpu: [x64] + cpu: [arm64] os: [darwin] '@rolldown/binding-darwin-x64@1.2.12': @@ -3381,11 +3317,11 @@ packages: cpu: [x64] os: [darwin] - '@rolldown/binding-freebsd-x64@1.2.11': - resolution: {integrity: sha512-4Ir5FSOKIAMr4r0kExpt1s3bMgzJU3rA45AYOHtQpls0oNeqcYBKrWMlckrYH4KCfGLfkfn1tN1dmZPMVsdXow==} + '@rolldown/binding-darwin-x64@1.2.14': + resolution: {integrity: sha512-hyMKEGatjmBYCs/Q4sbIcL/GqpFEqsX3wc+HYVQ/85A4rxc1o7rPm4DUI4GV4k/h0ITddwFSGpqQunI14GXjTA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] - os: [freebsd] + os: [darwin] '@rolldown/binding-freebsd-x64@1.2.12': resolution: {integrity: sha512-uMvssit0a4W+/7D8CbHUvG719mH3R2jwXAlh/XcPvuHTE0g++LymF88DCGNX0HM2rBOn0xrzgXktIB6fLSJBTQ==} @@ -3393,11 +3329,11 @@ packages: cpu: [x64] os: [freebsd] - '@rolldown/binding-linux-arm-gnueabihf@1.2.11': - resolution: {integrity: sha512-/gnRDM+39BROzAN/k1OZjDPnDMcZxB/0EUxKjONO5yVkNEvlsoMDrxGNKgZi/ttFriS2gwlDNzB65pvNbFOXIQ==} + '@rolldown/binding-freebsd-x64@1.2.14': + resolution: {integrity: sha512-u371GbXoVXrEgP20PwzoGNOdAOqAMhzo19JzzY+V4YmmnC+DTOoqdbMu+O+54emM59Mik/UdrtWgcuEDKWRPKA==} engines: {node: ^20.19.0 || >=22.12.0} - cpu: [arm] - os: [linux] + cpu: [x64] + os: [freebsd] '@rolldown/binding-linux-arm-gnueabihf@1.2.12': resolution: {integrity: sha512-XcFu0R0xWnwzSf4IQgFH1rJIckPN1pLy2R+4r9IDB7Yfu/ys9cVqfa4pBrMHj7a3gl8mIR4nRNPg0e5IvEVs6g==} @@ -3405,12 +3341,11 @@ packages: cpu: [arm] os: [linux] - '@rolldown/binding-linux-arm64-gnu@1.2.11': - resolution: {integrity: sha512-PFaK8HwvAHbaKbBcDNQihjMKYvFnA5hiENx/l5tphTDz1E0WFp32l0A7aq7lyUwGsRw/xSrNIy/gIK4thrSCrw==} + '@rolldown/binding-linux-arm-gnueabihf@1.2.14': + resolution: {integrity: sha512-BqwKsdZQf6EqVWr8fv2MrDYzAvOZ7+NYFSJESRGs87fkseEhs0TU9TXgRJTkZoRRkxTizyEg1LKUXfU3/78uqg==} engines: {node: ^20.19.0 || >=22.12.0} - cpu: [arm64] + cpu: [arm] os: [linux] - libc: [glibc] '@rolldown/binding-linux-arm64-gnu@1.2.12': resolution: {integrity: sha512-260UrKgn8tz39ak+SMDOirKzr7V04M9dWPw5llW00SwBivCZoWcRBKV1d8cXnRkUmSZA3BdiUmBHWk7734Ulpw==} @@ -3419,12 +3354,12 @@ packages: os: [linux] libc: [glibc] - '@rolldown/binding-linux-arm64-musl@1.2.11': - resolution: {integrity: sha512-AskzJUIKRLPxkruR1wLKewGbOw+EYfU/9lOrBFj4AFrEA8hPpKFnODWNu2WLaNs0QNkEb9QIJufmVZZIL/bJlg==} + '@rolldown/binding-linux-arm64-gnu@1.2.14': + resolution: {integrity: sha512-SD9kY8M009x/7s57Z1dgUVlP5+uYBgKQK0Tkr1WIWngrtUJmRCW1RJFMY7QDla2khXhmg2i5Io7MUjcmZNZ/KQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] - libc: [musl] + libc: [glibc] '@rolldown/binding-linux-arm64-musl@1.2.12': resolution: {integrity: sha512-5YK1I9SqDkbPgc1IA8BgDl34suqUS2q0KWnBrirm0E51YjOs6eo6dV6jbQfNE/argHRSvd0QUGgtpIoYx+WWpw==} @@ -3433,12 +3368,12 @@ packages: os: [linux] libc: [musl] - '@rolldown/binding-linux-ppc64-gnu@1.2.11': - resolution: {integrity: sha512-qlUGAheh2yh8afH7QBgx0PrRHN85hKnNd78x8MeMhXivuevgd8vgf6/CstOzmNKY/lLTHvNTrPy98cLnAugzJw==} + '@rolldown/binding-linux-arm64-musl@1.2.14': + resolution: {integrity: sha512-BJ2CL19M6uuWesMgnqJzgxoI+N+eV7b0A9O9vLV42y+gitC41fNa8KElau1tHVzuLWX5GzEuxMPhAO0tQXQMkg==} engines: {node: ^20.19.0 || >=22.12.0} - cpu: [ppc64] + cpu: [arm64] os: [linux] - libc: [glibc] + libc: [musl] '@rolldown/binding-linux-ppc64-gnu@1.2.12': resolution: {integrity: sha512-Rkcrmp7eFRg74yL5fXEU91JEWbdEPLevWwGtXpmhbjlD1StScbWTmO94Bhly+Mo+ketKYkdmM1vNUKeWSlx8cQ==} @@ -3447,10 +3382,10 @@ packages: os: [linux] libc: [glibc] - '@rolldown/binding-linux-s390x-gnu@1.2.11': - resolution: {integrity: sha512-secpEad+0vCbSfn8upFySkDskv+bGPk3THSDS9Y89yc4rb4kzqHp8Dmyd9BkQW4SnhNXBZCl/6CrO//hZahNJQ==} + '@rolldown/binding-linux-ppc64-gnu@1.2.14': + resolution: {integrity: sha512-4+Kl8daPV8NDHaD7mfCcc1pRM+Qg7ab8DyMm5FG5WoZgv4h2i3KFN1CybYBNbQqmuYm7ftJLvmMI1eHYJ22S/Q==} engines: {node: ^20.19.0 || >=22.12.0} - cpu: [s390x] + cpu: [ppc64] os: [linux] libc: [glibc] @@ -3461,10 +3396,10 @@ packages: os: [linux] libc: [glibc] - '@rolldown/binding-linux-x64-gnu@1.2.11': - resolution: {integrity: sha512-mOVBT3dPpkWm8XBWPmU4bf+U6dYDLeMo/9ojUmis4N0L5uu10qra5vOyngZ7/PSdoE4G9KvRt4bloRxNjLas7A==} + '@rolldown/binding-linux-s390x-gnu@1.2.14': + resolution: {integrity: sha512-V/h8ePq1fKDrmtFMz6xcCN3DKvuvMRrwwD1q0PkBeeo4DpK64biaKvqWGVxqCeYKqeRk7gE7/iKPxLCn08O97A==} engines: {node: ^20.19.0 || >=22.12.0} - cpu: [x64] + cpu: [s390x] os: [linux] libc: [glibc] @@ -3475,12 +3410,12 @@ packages: os: [linux] libc: [glibc] - '@rolldown/binding-linux-x64-musl@1.2.11': - resolution: {integrity: sha512-Is78i9A8Ui4SqcxUwFJ9uMmjDn58IbVTjFWYdQestFEgeuEmHMLGNriXnVJKkwG2YiZjw8cP0zCTyDMdDGtOOg==} + '@rolldown/binding-linux-x64-gnu@1.2.14': + resolution: {integrity: sha512-WxalXzgrLVpKC93xfpaJ3gCXUWzeCr+FRCbDVD2mUAy4L6Vo3lNVz0epUEUpxPWJkLNYUwM4OSIikv36OJ9mLg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] - libc: [musl] + libc: [glibc] '@rolldown/binding-linux-x64-musl@1.2.12': resolution: {integrity: sha512-3IBxWFMjbOZskDPKv8Lf9BCnahlKuHthWkYnyIxOH/QcJrFcS4EmcenthApkwr/5+nEqZlLzeYbxeMaX7A5u4g==} @@ -3489,11 +3424,12 @@ packages: os: [linux] libc: [musl] - '@rolldown/binding-openharmony-arm64@1.2.11': - resolution: {integrity: sha512-dUCXneZ87INUMyQ0D+C0HrEBNUPNXHaPmU5GTjyKTJEiussw9Kaj5Ln8UztPe4epV/ffvgNBEadksdYhmW6xJA==} + '@rolldown/binding-linux-x64-musl@1.2.14': + resolution: {integrity: sha512-3G3tnAmTpdk9jO3GQtyHXqeFD2SlecfTws/Y3M2mmj9nMBwwuLT4kur8sZKeu7ENUHSpVO3cIsm5A52LEWWwlg==} engines: {node: ^20.19.0 || >=22.12.0} - cpu: [arm64] - os: [openharmony] + cpu: [x64] + os: [linux] + libc: [musl] '@rolldown/binding-openharmony-arm64@1.2.12': resolution: {integrity: sha512-xtX61xg4LKPkPWilZU1ynKClz5Gj4bf74LML4r3eVLWumKnGjoEr1OSHQhMdbBDoYTi+yjrujvpZe2pUnqCrrA==} @@ -3501,11 +3437,11 @@ packages: cpu: [arm64] os: [openharmony] - '@rolldown/binding-win32-arm64-msvc@1.2.11': - resolution: {integrity: sha512-jByxb6qfd+bH1xUd0qnfFnb17i9sWBPY2tOavJ0l3tdr3OTu+Kvtm8cd/JV5nFt657b1VqGltxg9olOEfofXWw==} + '@rolldown/binding-openharmony-arm64@1.2.14': + resolution: {integrity: sha512-qyr5XiNGxm2ytx17vvtcJ9nXn6v1gENETi503bJx3OGwZ1tSonj7BosxbMa/xeAfSl5iS9X6KjEWYXLu6v1L7Q==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] - os: [win32] + os: [openharmony] '@rolldown/binding-win32-arm64-msvc@1.2.12': resolution: {integrity: sha512-At7fPB6PCaIjzgIhEZFxuT+BBFqiQibJDT4d3PhiR3f4E7bbMZF4aKblbFfEM3sETRDd1YiQx/+U/g/B/ou5Ew==} @@ -3513,10 +3449,10 @@ packages: cpu: [arm64] os: [win32] - '@rolldown/binding-win32-x64-msvc@1.2.11': - resolution: {integrity: sha512-/PzKqzAJ03i19oy2ItPvyvaVjOjBCNnfaJs8yvUdGBKmiESgnrJSQ2awd81QzFbbnAmu7YO9ZnJrDCb9VSJPRA==} + '@rolldown/binding-win32-arm64-msvc@1.2.14': + resolution: {integrity: sha512-oA4o5o0S2yB199YyYXDzKUv1Z56Rc+mgOjwm7Z/vJ9Z8igiDPYvoOuDyo6g7AK/ZaF09nPhDKVnGBcYHO11GXA==} engines: {node: ^20.19.0 || >=22.12.0} - cpu: [x64] + cpu: [arm64] os: [win32] '@rolldown/binding-win32-x64-msvc@1.2.12': @@ -3525,6 +3461,12 @@ packages: cpu: [x64] os: [win32] + '@rolldown/binding-win32-x64-msvc@1.2.14': + resolution: {integrity: sha512-9mE94bf2GZUaR/fUSXCMpBoWkctu421Mi0ORpm4zqfbkjKep7RxzPFa5etcGs5h+2KApoWBOA/hAJa/7saGgEQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [win32] + '@rolldown/pluginutils@1.0.1': resolution: {integrity: sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==} @@ -3564,146 +3506,146 @@ packages: rollup: optional: true - '@rollup/rollup-android-arm-eabi@4.64.1': - resolution: {integrity: sha512-QRU7UpoS9hd8Ij5I6fCVBxZ0UpdfAbZ5y7BWG5STFGMoM9jGzUP464yYAa3CzZxvG+jBwq1kI8L07xx0ReAdGA==} + '@rollup/rollup-android-arm-eabi@4.64.6': + resolution: {integrity: sha512-FkO19Fowv/OQeJZPVfdetJaI3/VpbZmBsnmAlR/rQ+b3QwfiGQiOpVegpSRH331kelvHTVw9hXYG3DwjAaRzyQ==} cpu: [arm] os: [android] - '@rollup/rollup-android-arm64@4.64.1': - resolution: {integrity: sha512-ttQF6Rp4MrNy9/2AOPmHmMXbNE8kIlNPYoGC3ksid9tonleMQALfrhTFJiQxobBvXYKG5Wcc8VGcHWTHR0ik8w==} + '@rollup/rollup-android-arm64@4.64.6': + resolution: {integrity: sha512-si5/Etm6MphPc2xYHnk455Shls/hoMjTy3mhXge4OPmvCw7TwC4KP4fKokAlKLV4XaQvLvgSHfafPcblLMYYdQ==} cpu: [arm64] os: [android] - '@rollup/rollup-darwin-arm64@4.64.1': - resolution: {integrity: sha512-MvzZ+qbBi5jrafURHsASUF1qW/58tOJFt/gFVJo2znQWrKX4CSf/Lt/qGONtoA4yHsNL5QzgdXMuITZxAs62Vg==} + '@rollup/rollup-darwin-arm64@4.64.6': + resolution: {integrity: sha512-PQCl3q7b0BgvVt72i2xrWM6I5KliwwVe4GpWW5QDGyUQawDS9aPZss/aGyFqBmxdRxlE8wDc9CaxiQ5DHRSp/Q==} cpu: [arm64] os: [darwin] - '@rollup/rollup-darwin-x64@4.64.1': - resolution: {integrity: sha512-9uj1VBuiPK5ij/+g0mCK17X+V/4SbErvRQMXIQjZAtbl4VcUhv63RhPZsFSWHrCRkTd3FKDtaVTrtT0+8bIhhQ==} + '@rollup/rollup-darwin-x64@4.64.6': + resolution: {integrity: sha512-l9DFl1Itb1YYdSrP1K3U8ih44AOHa/p+Q4WMMDfGvH5qVWp+ajfgS1DlYo4yCRh8wJ2ZJv0Fqccf+ULAmMFjmg==} cpu: [x64] os: [darwin] - '@rollup/rollup-freebsd-arm64@4.64.1': - resolution: {integrity: sha512-FCoXm836IqcXWhWPRkqlLx3Hjnv9S7c8tg5MFwsgHB3aFCz1JjUy6b1ssrxEjEbqaHpnvRmNjUUHxsXuU85NYA==} + '@rollup/rollup-freebsd-arm64@4.64.6': + resolution: {integrity: sha512-bR4GR3205N9+od0+l5Dd9/iZTDpJkIe6d6Ydp2qbcQZJLmKBdYQQRp8gbWpbMt92twKJY66o1vK4NkoKQODIrA==} cpu: [arm64] os: [freebsd] - '@rollup/rollup-freebsd-x64@4.64.1': - resolution: {integrity: sha512-Xa6Oxo86Ilx1hq1vNb69541NOuUyhGEH3Sc0g7eGkfsfRblWh4hAa4Lj2GWwZyfUVYL+Z4xK3TxhwIuZ3fNPLg==} + '@rollup/rollup-freebsd-x64@4.64.6': + resolution: {integrity: sha512-XTVNaDzcFl2TeBglvxdGgsCv9JGtAGRAoJENmGBR/XvDH2mA2K0IXc2aMfP0dR8mexSXSKifJ85FixoXX1wfTw==} cpu: [x64] os: [freebsd] - '@rollup/rollup-linux-arm-gnueabihf@4.64.1': - resolution: {integrity: sha512-Nkw7cVMu1yrczpzr42Mh1BZ5Ai5A4Fsz2Je/frbwHmazpQTJzP+qo7pNjdxHrxVj56ivKTJihNoN90OD1hGxBA==} + '@rollup/rollup-linux-arm-gnueabihf@4.64.6': + resolution: {integrity: sha512-qWc6qbc+25ugG9vqPR9P422KJBbVfZ05mjcMggoq8tsfWP+eRfC+/tiDdIlRF5VL2pDVZWN9zAhWsmu7R1+7Dw==} cpu: [arm] os: [linux] libc: [glibc] - '@rollup/rollup-linux-arm-musleabihf@4.64.1': - resolution: {integrity: sha512-JONaLM9mZPXbSetxMzYG3496qoJuWvG2ERCTrrVptfw/Fwe4UzrQoEx/mYEeZCRv/Xz0RldOLFeqDfqmH75/Zg==} + '@rollup/rollup-linux-arm-musleabihf@4.64.6': + resolution: {integrity: sha512-V4Nwkp8844X/Xnww3PI1LYS0YPei8XQEVG70R+5MPZGO7AhYoun4hCN7Svs4U3wbQLZbwiz+9xRFhUZZdRl8Ww==} cpu: [arm] os: [linux] libc: [musl] - '@rollup/rollup-linux-arm64-gnu@4.64.1': - resolution: {integrity: sha512-OfimHma+prmdoobHI3tfYud2enQ2LXYaMu+iMUQH7IjJkykJ6qUmAs1M/scdQaEKRWysiKwTctsDa2LXvaO0xw==} + '@rollup/rollup-linux-arm64-gnu@4.64.6': + resolution: {integrity: sha512-xb72A1aPaaIsp+a88VtRae/ovIEXislbUMyp7b9AQQU7ZkP2m/+ik6DW9TQcJyw5R8/RIailF9zExYwkjR++UA==} cpu: [arm64] os: [linux] libc: [glibc] - '@rollup/rollup-linux-arm64-musl@4.64.1': - resolution: {integrity: sha512-X5fwh2HM8EHbgfFwpf4m2x2pG/AHdQ2aPDPk+JCDwV4DPeM8yxYmKG1vvm123JSc9gBSu8tu9NECA99wHPztsw==} + '@rollup/rollup-linux-arm64-musl@4.64.6': + resolution: {integrity: sha512-pbcs1BVBwC43hZkjhjlF8Rx8m5G0KnhLZs9Azy/gqccP6UsNSzJr0hKGIAVPqzAH1O/NdPxxC9GVGb4msQg/aw==} cpu: [arm64] os: [linux] libc: [musl] - '@rollup/rollup-linux-loong64-gnu@4.64.1': - resolution: {integrity: sha512-8dp2TxKdjiX/4f7Da9gF7BR29hygxOQlHxKNa8qxJHoqZ13ytrogFTpMPhtVEFrgFu9MSOabAn006PeoygavjA==} + '@rollup/rollup-linux-loong64-gnu@4.64.6': + resolution: {integrity: sha512-EKOC8os6Vsg487ZLKUqzLF99Jsmnq6tu7O7mAELakNpcA+8KadktFf/JYdRuHe/DzCUXmN38pUKu4XBcKlqGbQ==} cpu: [loong64] os: [linux] libc: [glibc] - '@rollup/rollup-linux-loong64-musl@4.64.1': - resolution: {integrity: sha512-YuYDpRSZnU9qLf6sqyhiBdMRCw5ImECGLNqRjM2hZrJIEwAckameWkRJ3SUnn0oxSewZIib/4iLwyEXj40QqQQ==} + '@rollup/rollup-linux-loong64-musl@4.64.6': + resolution: {integrity: sha512-v/SQ68GnSlr/HyjB79sNXesGDwkOPu9JjbHfmWaymCiM+nZsQ9TstG1KfA4MuEvTr2d3PoY8PhFxj9bt0e32JA==} cpu: [loong64] os: [linux] libc: [musl] - '@rollup/rollup-linux-ppc64-gnu@4.64.1': - resolution: {integrity: sha512-gGj3v7YYkysy7IbfclMI4PDtHaa6YX4nU1D5NR1fyvXAGqM4TDk28ZWvrpOkkK92eoQRryAludWz6jNjsm77Qg==} + '@rollup/rollup-linux-ppc64-gnu@4.64.6': + resolution: {integrity: sha512-G3pmliMQ7r15zFFNwBlaT/xTdSLi4lfgI7CUrQueyKYheNVhrd8JrWCE6Ll8xf4AA1RZFqq+mNJYntiElsVOyA==} cpu: [ppc64] os: [linux] libc: [glibc] - '@rollup/rollup-linux-ppc64-musl@4.64.1': - resolution: {integrity: sha512-DTZVwvS94FUYpP0CjCfz2iT0XTmBfQGc/arx9IdwUbDImpe9h8dWuMT/oPUvcGiwLKMQoNmuFBx3AVajm+FNjA==} + '@rollup/rollup-linux-ppc64-musl@4.64.6': + resolution: {integrity: sha512-XQTB/IPoB/wG6x/wu9uomAt8YH61IAr5iZTubMiad/lu88sotq2ocz9hJd556+Jz2kIkF7ZQZIsUuWE3OWqtcA==} cpu: [ppc64] os: [linux] libc: [musl] - '@rollup/rollup-linux-riscv64-gnu@4.64.1': - resolution: {integrity: sha512-NWnyiq2moc8iEHGyTjIFadJTy8VCvCU/rUBfor+3l30JOQKAifpmKdxX4ZxRRPSPE/rLhFpLADdzNjS3Ldctdw==} + '@rollup/rollup-linux-riscv64-gnu@4.64.6': + resolution: {integrity: sha512-O5mpokth6+ydMuwiGtY8fIjvOBfH7u6SzmDNmZ3k6GK5C6iEAC2q2ZN/T5L9jYILx0IcCT0KnfMDihQ5yQFSBw==} cpu: [riscv64] os: [linux] libc: [glibc] - '@rollup/rollup-linux-riscv64-musl@4.64.1': - resolution: {integrity: sha512-yIz+7L37g7bVOGLaUQmGqiYADH262krL4dtf5zU6QGldPgOairLEgW1FTs5pP0UFkHq5uf2qxDttOFsVAX6kxg==} + '@rollup/rollup-linux-riscv64-musl@4.64.6': + resolution: {integrity: sha512-aIFxbAD9v3TvIaEDsKmukjcDg1JS4/tFXw8SHyQQgzOwzWCQ4cyDK7Ju1pQKqUXDFkEu/BZCFDCDATsKhQcB/Q==} cpu: [riscv64] os: [linux] libc: [musl] - '@rollup/rollup-linux-s390x-gnu@4.64.1': - resolution: {integrity: sha512-sRK+GWovJZV6ijyV/k1Ft3FL5+4DscF5OYsJolTbhVTTLFxKOPVjS0wxUUHakvL0vRw/cbFxZNeZLfM/EzuQhA==} + '@rollup/rollup-linux-s390x-gnu@4.64.6': + resolution: {integrity: sha512-fuNo1UwBc6BxnHDOxxguQL/InhheMN3RJcJMb0b4xP8r5Q3CNCrJbVg0eCIFuJ4e/zAfjR2gYbOfbzO/G7CIyw==} cpu: [s390x] os: [linux] libc: [glibc] - '@rollup/rollup-linux-x64-gnu@4.64.1': - resolution: {integrity: sha512-T2C0NXqqD063yJdm8UGdcoudBATDODdphEXWS7rEoh0jOITfoEBv7LHfRBYVsy+pI7wwxgScoZpkFiQOf40ryQ==} + '@rollup/rollup-linux-x64-gnu@4.64.6': + resolution: {integrity: sha512-05v6wFKDKJPZs9xlytVrrRu6IbRuoSvlH9xNwEOafXsKyjBFZPvAcZz2VIAB/M+lblYeKa9iOYC4RDVdfcAAaw==} cpu: [x64] os: [linux] libc: [glibc] - '@rollup/rollup-linux-x64-musl@4.64.1': - resolution: {integrity: sha512-05wl8uRZMkcobB6YZrzskr2SiM0VOdzlFw3bAxOjKKTt3M6o3O0X/hE/8xk15u8oQY17xUCdTKcNMMg45ZAFog==} + '@rollup/rollup-linux-x64-musl@4.64.6': + resolution: {integrity: sha512-wJXKcdMjvkc/FaavZXNP1yW4i0pfOmhZAIWRSLG2yJ0PB0TWi7dkLErhX0u0vptDcXKKO/m1Y/YTeuyvb2MXwg==} cpu: [x64] os: [linux] libc: [musl] - '@rollup/rollup-openbsd-x64@4.64.1': - resolution: {integrity: sha512-NoRZK1VclOgn9x3b9vPJ3sTxwvQ/F+Hf93xurRKSFJKiwNZyQZnLxVpqlCejhDvesMLMF8VNN+Y4jHTFddV/Xg==} + '@rollup/rollup-openbsd-x64@4.64.6': + resolution: {integrity: sha512-cwg7/h0ThvJwy8CBkmACdiHAxRWpbsCmIsjOt+Zzx0hTnIcSUmKYEV03bGg+hD827OnwUsbKh1ZZDW754xWyBA==} cpu: [x64] os: [openbsd] - '@rollup/rollup-openharmony-arm64@4.64.1': - resolution: {integrity: sha512-SDkX4OkZBePUPf0/XsXVJLwS+O61CmkDKuIyk5CeWddK3vkE8AZjmWK+p1ILRez4wnbS5rTK+U9YmcbRyx2dHw==} + '@rollup/rollup-openharmony-arm64@4.64.6': + resolution: {integrity: sha512-T0RkYIJbdYXJEQFw+tMBO09KDFdgmNxkHsof3wWAABrvnx15Yh8RytmrgYy9+q9cBeBNZF66MMQZC+5nzEz6vg==} cpu: [arm64] os: [openharmony] - '@rollup/rollup-win32-arm64-msvc@4.64.1': - resolution: {integrity: sha512-JQ7el/Plr3juYFxIVu9YXDPNl57XR2L8WIc87JX9wj0L3goV8fXQPQXb3DaHVXq61X/WCGWsTCQsQHo6/Mz5UQ==} + '@rollup/rollup-win32-arm64-msvc@4.64.6': + resolution: {integrity: sha512-KlfpmF1AAIdAWxt8QQNClbpLFlAUfYOzqIuJ7IXEStMHpFRuH78RG6FjqPb3Rqhh0K89F5npvG+/i3Fjl6IveQ==} cpu: [arm64] os: [win32] - '@rollup/rollup-win32-ia32-msvc@4.64.1': - resolution: {integrity: sha512-oZSlQ2H3ZDOz3ce1jEyuOZKuD0tPY/D2cKGMcMl0jhHQauBvkHHKAGR1OeRiMluvu3itzLRU2xCTUZptLayObA==} + '@rollup/rollup-win32-ia32-msvc@4.64.6': + resolution: {integrity: sha512-MrTLjd7Jlt9/ZavJZYG61KQv9Iy9bzbY+yXy73Xvwoa2uP66wvDShkyBr5Chz/yrE6lbD/zHMuTmKQVNQqBApg==} cpu: [ia32] os: [win32] - '@rollup/rollup-win32-x64-gnu@4.64.1': - resolution: {integrity: sha512-Do2m3jUeVAvUDpee/yVZYRqhO8Ecx4x5sX3EVGalqLx+OxecLDGx3sIwPEm1Nq+MFK2FV3pgdpwWP4624b85Og==} + '@rollup/rollup-win32-x64-gnu@4.64.6': + resolution: {integrity: sha512-8fWDH1CkPI8GdBKAxzHDza7UmgficsKo2+MVbvUgOGhB0629w5sHMg3Qu5y7TZsrYKHHWpz8fla4KvoT7WsO3Q==} cpu: [x64] os: [win32] - '@rollup/rollup-win32-x64-msvc@4.64.1': - resolution: {integrity: sha512-KpxCwRn1Qh3tmBHAqIAF4HtUCVKqZd0w57TsItWblx3QVeJLtXaSgeBFAj1WAvBSpBo+Mh9iZCjj6YW/HaBVMw==} + '@rollup/rollup-win32-x64-msvc@4.64.6': + resolution: {integrity: sha512-9/c9PWdr5GCIHkkxiHM1HjNBQMVdftP4O8QTcoUAaRza2FSqJ4axbf8QAnBuE6BIGQDl71SaRPeDdDYid5ecTw==} cpu: [x64] os: [win32] - '@rollup/wasm-node@4.64.1': - resolution: {integrity: sha512-NX7sXK/FT9RBgFxkMKB3jFcc3LCcUL/44PMJ8BBE0t0ljgXih0tZrUPUdZEN/JnN1N7i5axbGQtt5iPwlFJDQw==} + '@rollup/wasm-node@4.64.6': + resolution: {integrity: sha512-ufelhiGxNl8RuflC/Tie5ZVcI3MJUtd27nJ0jC2I4LroyZb7n4iwmajEmXXeTLxJtDiiWUwU8v2UjkACBHqQvw==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true @@ -3781,6 +3723,9 @@ packages: '@types/cors@2.8.19': resolution: {integrity: sha512-mFNylyeyqN93lfe/9CSxOGREz8cpzAhH+E93xJ4xWQf62V8sQ/24reV2nyzUWM6H6Xji+GGHpkbLe7pVoUEskg==} + '@types/debug@4.1.13': + resolution: {integrity: sha512-KSVgmQmzMwPlmtljOomayoR89W4FynCAi3E8PPs7vmDVPe84hT+vGPKkJfThkmXs0x0jAaa9U8uW8bbfyS2fWw==} + '@types/deep-eql@4.0.2': resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==} @@ -3844,17 +3789,20 @@ packages: '@types/loader-utils@3.0.0': resolution: {integrity: sha512-oOi4OGpiLUbb+Q/cN9FIkkDFgOpOGZ2cUAzb5i03wrGstnG6Syx1WDMhSiB5rcP10XX7cw7Uev8mv++/aplnNg==} - '@types/lodash@4.17.25': - resolution: {integrity: sha512-+K1NIO8I+F9/wNulfVvu23QYd0Pe9/OCqRrim4NoYIf1VoEDL90Ve4ClzpyqBLc7NpGGWRvYNCKZ1BE/Jpf8dQ==} + '@types/lodash@4.17.26': + resolution: {integrity: sha512-48NWfiY6oNcKOh47Z4h+sIvwcQR91h6KPhaYKgeMIrkT2jV82Gy90SErj60vW8jQbeMSOSbhIv3bZ7JoqPPVhA==} '@types/micromatch@2.3.35': resolution: {integrity: sha512-J749bHo/Zu56w0G0NI/IGHLQPiSsjx//0zJhfEVAN95K/xM5C8ZDmhkXtU3qns0sBOao7HuQzr8XV1/2o5LbXA==} + '@types/ms@2.1.0': + resolution: {integrity: sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==} + '@types/node@22.20.5': resolution: {integrity: sha512-U2+DNr+wSjpsTS/wZGYHq7GcwfuSmKiKvoPvK22zwTlRhU91yOniN4qRR5KhIjvif7ysw/dz/hKmfDH0Ris4aA==} - '@types/node@24.19.0': - resolution: {integrity: sha512-zY+5tKxXdhGh1PYI0ac+7juvEu4OI6vWtVVoj5i2m42jxAY1U+zHGt6QCyOFwykdP62sM3MJ9stoYYUw5aCWew==} + '@types/node@24.19.2': + resolution: {integrity: sha512-93d49wCWJkCyN87MxLpNN6KXPCwFw7bVUg79mGUV1OS40JGWwVCr8n61DXCSqz7CV6/VfPkb7xec7uP2M+QHWQ==} '@types/npm-package-arg@6.1.4': resolution: {integrity: sha512-vDgdbMy2QXHnAruzlv68pUtXCjmqUk3WrBAsRboRovsOmxbfn/WiYCjmecyKjGztnMps5dWp4Uq2prp+Ilo17Q==} @@ -3922,67 +3870,67 @@ packages: '@types/yarnpkg__lockfile@1.1.9': resolution: {integrity: sha512-GD4Fk15UoP5NLCNor51YdfL9MSdldKCqOC9EssrRw3HVfar9wUZ5y8Lfnp+qVD6hIinLr8ygklDYnmlnlQo12Q==} - '@typescript-eslint/eslint-plugin@8.71.1': - resolution: {integrity: sha512-nNlBf6HuotqMgZvSkWD5hNdemiBIxQ2NmrlgfcUIeZRs+BbakjscCQRq/Hb74EFVnFiSWWdL12JruqwBy3ERDw==} + '@typescript-eslint/eslint-plugin@8.71.2': + resolution: {integrity: sha512-K72/pvT7qpm8q3k8G5u93NCSN1RcLxX5h7Gw0bQpbMXviBHeY6Nx1WEwmk9wP+eKTSQ8ecxvcgt/ZX/rB938xQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - '@typescript-eslint/parser': ^8.71.1 + '@typescript-eslint/parser': ^8.71.2 eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/parser@8.71.1': - resolution: {integrity: sha512-oM/5sAqz/l1v/zYK1qTOPrn2+JIrBGC1V6uNYucRO5gh6eT9vWJ/RhPbGE59byvZEUQ4VP6XkLBaS5Jti5jpvw==} + '@typescript-eslint/parser@8.71.2': + resolution: {integrity: sha512-aTtmZjp/x3gsGHkDs04/1I8kNogBJkpQaeGaN0PfiG43NzDEmW2n+OP5im9wfsvyX0yP1UZWBrAGtDBfH9jEVw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/project-service@8.71.1': - resolution: {integrity: sha512-Oijc9RUsohHjncg6iGi7gCtjnEjYt9FWv9u6ygK8uKcHK5eLjLAkqERQ9BaytbpceOFdk6kifPCS/zAZE+A99g==} + '@typescript-eslint/project-service@8.71.2': + resolution: {integrity: sha512-Pd68GsLVh5ZXlcEccFPdhSOvtHZ/2WU0XsA8zmXOS3F158lVlcTLhfQ6qvtL1RzbMie0Kp8xtbVwAg4nejKPHw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/scope-manager@8.71.1': - resolution: {integrity: sha512-1YUDZXdTnXLlob03SFHBipdyMBsDVm7zoSz0ytTJ39dk5J1TgsTK03VlR/dLXG/RLLUrPcYbD4rp23BmHxHP0w==} + '@typescript-eslint/scope-manager@8.71.2': + resolution: {integrity: sha512-YF4wgg5iUX5Obywoac27B56FhozMNFhdJSbh11H5GBAwmBtfyRhmgTQqW9tRNNmHDvsZ2CX6UGzoz+YfYn2Leg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/tsconfig-utils@8.71.1': - resolution: {integrity: sha512-oTvkml5SxXhgg+WWKhod5qWQICDXtOB+/Hi9VdPKFAANJ2b7YOgMffgvzXuqog7R6A/JtrzRf128dsxummyWhg==} + '@typescript-eslint/tsconfig-utils@8.71.2': + resolution: {integrity: sha512-jz49TAWokm/7yWD9/o837nYax9DyALxIPOImdImvVzo6canBCZYzin8zJyqNR5k1j9K1DE0bVvr0XYB+99uAdg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/type-utils@8.71.1': - resolution: {integrity: sha512-kH3t3jZCYv2PZSBEWPKTariLrCaoF1CLoPitYFBJOhAGEwWnSRAtGEifrfrpBa4K9JlwWNZotgA5w3/wLSk/FQ==} + '@typescript-eslint/type-utils@8.71.2': + resolution: {integrity: sha512-cC8UaalCViJkb3rH/xLIvidEwoFRtMZMUqDjjJMGEKaTFwYRHYBcoczlEGHuvkHEO8svRDrgZ0yQgOArFmwang==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/types@8.71.0': - resolution: {integrity: sha512-cJ4OoxPGWvFnBTnSZyaU+qJzGTqPTGJY+gDchj6cRyLRdmIdt4rcsE4twj+zPfrNiWuVi38wijHzShL++Z9atQ==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/types@8.71.1': resolution: {integrity: sha512-ybYnPTUwg3VQQOFEiBPLs8Jxl1ry2vXdn1kVKguomBbvxpetdu3MKTNDqFNUrlG8ylP9MpZosmXMFYlGQHk1oQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/typescript-estree@8.71.1': - resolution: {integrity: sha512-pSKSEK1JJpHs6KiKVtABCE4iJK5d7FLFzDXQNBQ2oRY7LEd3UwOt3TZemTgu9syTIf56dJWlTn/pdmrO9oKUXQ==} + '@typescript-eslint/types@8.71.2': + resolution: {integrity: sha512-y5Ukl3oVp7e/VGxqIoEXoeb/Roj71PAJ4WG/N/RGj+eeDvKRLM15kGr9MijLbN0i03S52zxu31Z98E5gWChJKw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/typescript-estree@8.71.2': + resolution: {integrity: sha512-DgNOEsl4pdYzwSL+N/Cl33tX1Hlw3FvId73LnKXfq7jnrF2viOEn7X+P7RBkkSWCVm92gnWd28GIC5vJuLREeA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/utils@8.71.1': - resolution: {integrity: sha512-CBjT6gfAz3DW2j9Q4moFnqt208Aq+506sVGXfpRcyAeqn0EpayL0mjdWoCO2t3RC7GewNrFxdeehuEaPg6DMzQ==} + '@typescript-eslint/utils@8.71.2': + resolution: {integrity: sha512-RyKa1G1M9rKHDhrk+HJtiF1MvLD5Ux2JW3TSkzw+02pRALdpJb+QzG1Y5em+LvCgzGwgg8H7mBRFKljNz7ImPw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/visitor-keys@8.71.1': - resolution: {integrity: sha512-0GxiUGqMU0qXJt58cBsiG3DP8lUmBaFo2vChtUX+CT7PnR3v/VGLLrzVP4K9r1lNiZXjHYefMBbPunplr09v6w==} + '@typescript-eslint/visitor-keys@8.71.2': + resolution: {integrity: sha512-zozlN0UA32bSG2zkKAgQyRYSbEISVoyU6vvkKYo2aSny74D8Jh4e+POkJ6Lk0dlezZi5x7TtD+88Xb06IB8nhg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@verdaccio/auth@8.1.5': @@ -3997,6 +3945,10 @@ packages: resolution: {integrity: sha512-KRYepjgpSgrYYNHjmg77F9Mp7FMzQ0f9dcNc2AP3fmCmQMx0IWH6TYe4okvqkASoYhLnil5NRWqjO4Z4qSOHVg==} engines: {node: '>=22'} + '@verdaccio/core@8.3.3': + resolution: {integrity: sha512-Vhfpo9MIwUOIsNQCGmTKqYn2l73261YQ6NCmRS47NbpXi5YWuSGf7I6i4dIVMZVG7UUtz9633754WtzFFAgFvg==} + engines: {node: '>=22'} + '@verdaccio/file-locking@13.1.0': resolution: {integrity: sha512-rGFfdyCZdgpbkROJJfjOA01R5BFtzrnDAhNIkxxc/yWZ6Cir+MRHpEbN2weOEPAwYDke9Wms4JKhpsStu+iV8Q==} engines: {node: '>=22'} @@ -4062,11 +4014,11 @@ packages: peerDependencies: vite: ^6.0.0 || ^7.0.0 || ^8.0.0 - '@vitest/coverage-v8@5.0.3': - resolution: {integrity: sha512-+klsyz7BvT1vCU28Zkfzms1Ia78XD0V41U3FUtRaa3S+vOr/EXvK1O6BvVD7l1RzEjm6SONR2aybOvDDf9u0mQ==} + '@vitest/coverage-v8@5.0.4': + resolution: {integrity: sha512-P5YcQY8Y6RmoE9xRXI38rT8pssbcubEzqtYTM2XQoxOXMDn04nRaTI1SM8qCl9/NfW3ag9GlzdSXu7IwO5Ft8A==} peerDependencies: - '@vitest/browser': 5.0.3 - vitest: 5.0.3 + '@vitest/browser': 5.0.4 + vitest: 5.0.4 peerDependenciesMeta: '@vitest/browser': optional: true @@ -4079,10 +4031,10 @@ packages: resolution: {integrity: sha512-gUsfXZJbzPamoIY5TvHFiMMoXESBrUMo+xqaj+rYrWI69+EnvRlYBlP96ZnHPY4vX8kyUpgAnFUCg5wZG/HkDQ==} engines: {node: '>=22'} - '@vitest/mocker@5.0.3': - resolution: {integrity: sha512-T8sWAIbkSyAjkwTcaEc3Iu0o9A27X1/kdXrizhZkGuSKScRQtRzclfAMpOTcGdXCsqxeWlpGy3XjqaW8CpLORg==} + '@vitest/mocker@5.0.4': + resolution: {integrity: sha512-tqAlrYWce4iUxhr3ncNohejtCTOcHeE8XWrSNZY3OhPoDn01yeGv+gnaBy64ATp7o6pb03eIbBAjzmibpc9vIQ==} peerDependencies: - msw: ^2.4.9 + msw: ^2.4.9 || ^3.0.0 vite: ^6.0.0 || ^7.0.0 || ^8.0.0 peerDependenciesMeta: msw: @@ -4090,8 +4042,8 @@ packages: vite: optional: true - '@vitest/spy@5.0.3': - resolution: {integrity: sha512-XhFysQTB8AZ+P4gMi+Lpo99vg2AZi0qKpaB9yXQl37+CaMEAPO3iH/wGVnSyL5MPERiLezpqTVtrR6UZH5GCXg==} + '@vitest/spy@5.0.4': + resolution: {integrity: sha512-MZKk/gSkm2q+ICobMxCpH1mA+cCAWm8WV7UhxlqGjq/sJ3AGc1BqFOs5CRVXxH78wQtme9q/NAAgBquj4AcyMw==} '@webassemblyjs/ast@1.14.1': resolution: {integrity: sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==} @@ -4229,6 +4181,72 @@ packages: os: [win32] deprecated: yuku-codegen is pure JavaScript since 0.14 + '@yuku-core/binding-android-arm64@0.19.0': + resolution: {integrity: sha512-wdL2XmTiZixAOaHk9Z0JJZ8u9Qm8d333Zjpc6gaxXFX+UR5BlsfxZ7a2+2Rm71pamppsPYGUGFq2oVLbh6DISw==} + cpu: [arm64] + os: [android] + + '@yuku-core/binding-darwin-arm64@0.19.0': + resolution: {integrity: sha512-7sNJa6XJhyetnTy64jw3NEi4YgUcezHk1G5GH9FSvXAuC8wIlbaOwT9lgq5qo5sspjrZJQlBR1TWflP49uRpMw==} + cpu: [arm64] + os: [darwin] + + '@yuku-core/binding-darwin-x64@0.19.0': + resolution: {integrity: sha512-pTC7dYEc59isdyizc3nsuyFNBu2LU5wMYZ1be6UMr59PY/AJNtO1aBLWV0MgL/z7C1nv/2sX4TNjGgKLoeevJw==} + cpu: [x64] + os: [darwin] + + '@yuku-core/binding-freebsd-x64@0.19.0': + resolution: {integrity: sha512-xrZjF6XOEwyLOiDIfrwDdRCS6ghxcJeirNC1KivQhVcUwlRonaU79S3/Uiyx3lBylPnT8qKJ3/aMT+H52nGTJQ==} + cpu: [x64] + os: [freebsd] + + '@yuku-core/binding-linux-arm-gnu@0.19.0': + resolution: {integrity: sha512-0OyIH53C0I1CbADHn9FSdCfX5qbpeLwJhmOVhonFJNYheybOnkSJ50qGMfTJIb8y5zrwqvLs7FVBvz6Fk8maZg==} + cpu: [arm] + os: [linux] + libc: [glibc] + + '@yuku-core/binding-linux-arm-musl@0.19.0': + resolution: {integrity: sha512-z/iPfkCOCSXE34VXNt0uP1ORSW+4mEa9b5CiJcWZeqZH08g49bFU/jRM2T04f+Wpw5mJ6HIzNBiojxeAbrHjeg==} + cpu: [arm] + os: [linux] + libc: [musl] + + '@yuku-core/binding-linux-arm64-gnu@0.19.0': + resolution: {integrity: sha512-jLNTi+gzaRwBJ+gwhCJ+rDb5dmx/8hEBx93UWd7hjMSqPTL1D1wvVSvXVAldV5kALvju7b0/m51EHEhs4+I1fA==} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@yuku-core/binding-linux-arm64-musl@0.19.0': + resolution: {integrity: sha512-YwyPIdW/rN2Fmv0MTFWi47FaB3F9g+2b+sJISyAw83Yd60RWH4T3Nx6XkAHHVN+uKJNBAd6So7/BVihSiFYvuQ==} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@yuku-core/binding-linux-x64-gnu@0.19.0': + resolution: {integrity: sha512-/vskaAXVX3ESR43FiTdfiYS7z1b9uoQJwVQ/0RgUHgN2sWoAyCZs+aX4CgU7eKGIHTVfp9VFwv+c+2XchYiEtQ==} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@yuku-core/binding-linux-x64-musl@0.19.0': + resolution: {integrity: sha512-FuXL5igeSKib9Rye02TdH+D6SQezTyGxIATMu1Qsn/QywrzwpGl35OLpS2C1x5vpGLeVE2XgiLd5mVPFHmIglQ==} + cpu: [x64] + os: [linux] + libc: [musl] + + '@yuku-core/binding-win32-arm64@0.19.0': + resolution: {integrity: sha512-YuJAe7zEKT1Xa7LuHlhGJpPnYQeVtvo0rZvNjJRqkH0hQGOw4C8QeP30Tg5VKGzZfo2x8CjhjEhKt4TmLwfmdg==} + cpu: [arm64] + os: [win32] + + '@yuku-core/binding-win32-x64@0.19.0': + resolution: {integrity: sha512-o8iY/mXwlRkedp0LNTr4+TEfXTPQwoNZmv6k+1WIAd1ZXEn8xJJTBCtB0osaVvdZo/x/cNmV16UrWrrGpM5Mvw==} + cpu: [x64] + os: [win32] + '@yuku-parser/binding-android-arm64@0.10.2': resolution: {integrity: sha512-2VPBU9fRGRAQ2xPAvghnec3oou5Nrxm2Bezhf+13UMspAxQSkF/32r+ySKXSwIPA5/RivumDJDxAxc301Sgicw==} cpu: [arm64] @@ -4310,6 +4328,9 @@ packages: '@yuku-toolchain/types@0.10.2': resolution: {integrity: sha512-sSeo4SSSToiS+sSD+bwn/s94EEcaLJ7tG5LCp8gFYC1G5VzxzX7fqB6m9RU1yMD8KTpu6zdU/I1NlQf8hVBJ9Q==} + '@yuku-toolchain/types@0.19.0': + resolution: {integrity: sha512-tFkNbsP4WXX9UMUC5syJGdK9dPMLK0PPaIuXCcCYH75sZ8MrebfBZ2XvCILRTw8AgLivSSCfDa4R67iYBDa4Kg==} + JSONStream@1.3.5: resolution: {integrity: sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==} hasBin: true @@ -4331,8 +4352,8 @@ packages: peerDependencies: acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 - acorn@8.18.0: - resolution: {integrity: sha512-lGq+9yr1/GuAWaVYIHRjvvySG5/4VfKIvC8EWxStPdcDh/Ka7FG3twP6v4d5BkravUilhIAsG4Qj83t02LWUPQ==} + acorn@8.19.0: + resolution: {integrity: sha512-oJlA3XiRm7Cyk6qFD2Jn8ak9B7jSy0qy00ADO3+8dpT0LSjFihQYv4C02LFCSYJV3Q37xYwwRy5m+IpIiUzqWw==} engines: {node: '>=0.4.0'} hasBin: true @@ -4555,8 +4576,8 @@ packages: bare-abort-controller: optional: true - bare-fs@4.8.2: - resolution: {integrity: sha512-+ZI68KHMUvosXfKbg/UOHK0tbCdRnegbvPEdEcZ3Nd6TetieQsJPRXBRXPdLyy8+3VSEbPXtsumTpEtt78xv9w==} + bare-fs@4.8.4: + resolution: {integrity: sha512-MPC2eyrpbk3A873sK4y6rk+Iv4o2enxTjHZ8zH3HO0mjrpm5XjnguY2XmfidIsDV6vnjpbMBKUAJpV0ISD2SsQ==} engines: {bare: '>=1.28.0'} peerDependencies: bare-buffer: '*' @@ -4587,8 +4608,8 @@ packages: base64-js@1.5.1: resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} - baseline-browser-mapping@2.11.27: - resolution: {integrity: sha512-ElY12DaROGuan+lMmZ8Cvo/ZUbXPe7Enc/9VU/b1T3Kp4dwytRcNdR8DoSJN5SNJT/CuvcCA0DHDVmMOCePdRQ==} + baseline-browser-mapping@2.11.28: + resolution: {integrity: sha512-br0LOmjJqr6Rt4AEkadbcNf67Zpo7EPwKsWbjnRrJIa1QYFHQBqM4C7F2WTbV8ahTEcg1Eo+Y0aWjjORInYFyA==} engines: {node: '>=6.0.0'} hasBin: true @@ -4601,8 +4622,8 @@ packages: bcryptjs@2.4.3: resolution: {integrity: sha512-V/Hy/X9Vt7f3BbPJEi8BdVFMByHi+jNXrYkW3huaybV/kQ0KJg0Y6PkEMbn+zeT+i+SiKZ/HMqJGIIt4LZDqNQ==} - beasties@0.5.4: - resolution: {integrity: sha512-8/lOOjT+PElYCnaUbmhgd3Uad5R1VTQ4Wg0k4rK8bX04m5WEqHNmnlLy3Q/lJ7dk25XA+72gayzc/skTfKKpcA==} + beasties@0.5.5: + resolution: {integrity: sha512-4mUqdlIqPmHlX5qwkGmunWnhi406XcT5AfmwltoiDCToyGP2pSxS9ClfURL14IRwPYHq/JAIrtjNptbpSvUTPA==} engines: {node: '>=20.19.0'} before-after-hook@4.0.0: @@ -4735,8 +4756,8 @@ packages: resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} engines: {node: '>=6'} - caniuse-lite@1.0.30001814: - resolution: {integrity: sha512-/Uaf1lAzr59XcMpW0o96WoEfr+VXK2OX4U9AgFoiSHsVJ4HppnIFUjtYzsyDH2+tgANaQb2/oxYGwCPapN1FpA==} + caniuse-lite@1.0.30001815: + resolution: {integrity: sha512-uDybUEyfgbyJJ93J1vikTylBX31AkdVYeVPeffMQpf8XN7hSfr0oZOn4VFOIMNvtU5+rHvC5Udg2TgtJKHm23Q==} caseless@0.12.0: resolution: {integrity: sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==} @@ -4771,8 +4792,8 @@ packages: resolution: {integrity: sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==} engines: {node: '>=6.0'} - chromium-bidi@17.0.2: - resolution: {integrity: sha512-5v9GQFhTktFvotn/OFNJBmKLKRAb6n9r0bVCwf7sHgWc3/JryK0bj1nn93L3pHFrfgcsu6Be6EWsDi+1XHTGDg==} + chromium-bidi@157.0.8090-0: + resolution: {integrity: sha512-KJw7IQgqXwxpJgJlEIPg5mr2ZoqA+jkTRtc5EABI7sUJ3bu3cpI7NuEBoj4Gn1AsF78CaT9So5WQWspK3/wYag==} engines: {node: '>=20.19.0 <22.0.0 || >=22.12.0'} peerDependencies: devtools-protocol: '*' @@ -4895,6 +4916,10 @@ packages: resolution: {integrity: sha512-5jRCH9Z/+DRP7rkvY83B+yGIGX96OYdJmzngqnw2SBSxqCFPd0w2km3s5iawpGX8krnwSGmF0FW5Nhr0Hfai3g==} engines: {node: '>=18'} + content-disposition@2.0.1: + resolution: {integrity: sha512-e+H0ZXHSWYrENhQzw1LPuP4oF5MzVKmDU6d3hxlvaPEYLLg62MxtQNPRx4SYSuYJSBUgnQIG4HIN2tEtNv7Dog==} + engines: {node: '>=18'} + content-type@1.0.5: resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==} engines: {node: '>= 0.6'} @@ -4911,8 +4936,8 @@ packages: resolution: {integrity: sha512-cs+LadpH7Kpw0M3k8wurk+sOVVDAENA0iK4OBOrkL94j5lEVYRJ4j3zd2bhY9qgzyrPqthdcYT3axzRN7AliMg==} engines: {node: '>=22'} - conventional-commits-parser@7.1.2: - resolution: {integrity: sha512-O+x4N2yH+ijvqWlIyTHsXTAP+algNWgGbjY2duCe8w2vUMvUB95cLRslCPfTMQyLAKlet3bhZTdu6ozn4M+QJQ==} + conventional-commits-parser@7.1.3: + resolution: {integrity: sha512-Q9oWm1o9uy/oEsZQIraoFMZ4RuPD+79KgwWj6mJVP356tazolCB0piRLZ5Kz9te9p027uSEcUW+Gu44Eh+X79g==} engines: {node: '>=22'} hasBin: true @@ -5049,15 +5074,6 @@ packages: supports-color: optional: true - debug@4.4.0: - resolution: {integrity: sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==} - engines: {node: '>=6.0'} - peerDependencies: - supports-color: '*' - peerDependenciesMeta: - supports-color: - optional: true - debug@4.4.3: resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} engines: {node: '>=6.0'} @@ -5130,8 +5146,8 @@ packages: engines: {node: '>= 0.8.0'} hasBin: true - devtools-protocol@0.0.1687809: - resolution: {integrity: sha512-t7kSb+UKYdyxcmqy98U2P86ne6rCve3DIJ4+lp54i/hhClgFuG35JrLj23rO14Jw3sWDsp7i+22QhNU0+/qk8g==} + devtools-protocol@0.0.1696802: + resolution: {integrity: sha512-FO5/SENfvc2K8wRnWQflKcwuoTH4dBkXCWTQ9ebIWtuX8JM5+ZvCotaEmkfh21pO7TvIKGTM3hG4xFiq9WTwmg==} di@0.0.1: resolution: {integrity: sha512-uJaamHkagcZtHPqCIHZxnFrXlunQXgBOsZSUOWwFw31QJCAbyTBoHMW75YOTur5ZNx8pIeAKgf6GWIgaqqiLhA==} @@ -5211,8 +5227,8 @@ packages: engines: {node: '>=0.12.18'} hasBin: true - electron-to-chromium@1.5.444: - resolution: {integrity: sha512-5ss/uJfoDYDHT0lfJzT6FbcskIzROIOPf0BbbFkGcvDzoJU7i//9GDrwwIHQVmIsrAGiF3ihpADBRIsrEFt1rQ==} + electron-to-chromium@1.5.452: + resolution: {integrity: sha512-cVvk1YBSj6571VmV6hdJTqj94Y79xSu8cj6Y9t6ae4Y4431z8/7EJFvu3rVp90xhfuv9BpPKUmaLfTo4+FOGBg==} emoji-regex@10.6.0: resolution: {integrity: sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==} @@ -5476,6 +5492,10 @@ packages: resolution: {integrity: sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==} engines: {node: '>= 18'} + express@5.3.0: + resolution: {integrity: sha512-qCqy1xSoaugxOD4IVHb1ZWdkvCF2W3aE2okKouWj8VwJ+ztskSiXoBnCSJqfKT+cVPJhuJqe9YLbY425+Zh00g==} + engines: {node: '>= 18'} + extend@3.0.2: resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==} @@ -5565,8 +5585,8 @@ packages: resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} engines: {node: '>=10'} - firebase@12.19.0: - resolution: {integrity: sha512-kwXCLcI0ly2lkwygkbuRx6SsX3DSO96355YrWh9SWZ5ncsxK/y5cirn3sY6nZVSiBVE++2nL6Hchzu1EB8uohQ==} + firebase@13.0.0: + resolution: {integrity: sha512-4cChZajVtuOijT/t7Vw1GZRTuohCxw+aoc8DAwhF+CSbS+zhqHE+i5PzEVttIySw7oKerBDRZRyDt4FynrWecg==} flat-cache@6.1.23: resolution: {integrity: sha512-f++BY9pTk+983xK1FLzlLpmM0i0z+jHmx3QESGkURMXujQZz1k5wzwX6hjnQ8goaD0B+sYnDK1yZ6MTyZfUaqA==} @@ -5582,8 +5602,8 @@ packages: resolution: {integrity: sha512-kWyh8ADvHBFz6ua5xYOPnUroZTT/bwWfrCeL0Wj1dzG4/YOmOcfJ99W8dOVyyynJN35rZ9aCOtHChqQovV7yog==} engines: {node: '>=6'} - folder-hash@4.1.3: - resolution: {integrity: sha512-94fj+fXj1XHT8zGumUy/VlyFARc/yrslKJ2+vjrP/U6ftTdL7u68+gQhvSBjz9wrwTuty6BpZ7JsbEK5OU9RNw==} + folder-hash@4.1.4: + resolution: {integrity: sha512-BhHpJCYoTej4YH0C3HY4X49C2uOcWntjSD24T1GBYlfWcW/7mkHZov9HbvzCHQO+ZLbJ3VRE+cL1uEiaQvacLw==} engines: {node: '>=10.10.0'} hasBin: true @@ -6408,8 +6428,8 @@ packages: resolution: {integrity: sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==} engines: {node: '>=0.10.0'} - launch-editor@2.14.1: - resolution: {integrity: sha512-QWBrQsMpH7gPr965dsKD/3cKWiNoTjpATQf++Xq63N6sKRGMwlVXz41O1IZTMfZQgBctD/K5Zt06+/I6pP6+HA==} + launch-editor@2.14.2: + resolution: {integrity: sha512-NHJEb30MGBCkmZXLZwz25t37QR4aY03ZzPYUJXOJaOa92uWwExI9SBQUuIznBYc1xkD7cJNGX7oc3Nl3sq2kQQ==} less-loader@13.0.0: resolution: {integrity: sha512-TIa8d6znKH634Mg+7OU3jevZT6KeOhh0amW+YeMPD0GM9buUn5Y7HvtyCR5pUDdLaFfqLA8AX5PTSIHMNSexEA==} @@ -6525,8 +6545,8 @@ packages: lines-and-columns@1.2.4: resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} - listr2@11.1.1: - resolution: {integrity: sha512-zArGFXg2/nhA1vYBz789VpwzMQQXkGZiRxeguxFCsDrg4S+LOR22v4oQlSdTN7dR14XgOW+pW33UfvKf+i8nrQ==} + listr2@11.2.0: + resolution: {integrity: sha512-+H/hEo9EA7tyLY0ZihpDg76jHWolc0nCjbjnXFguSm4Epk+WYtL1w1tYs1kjEo229NmhgLmUrncyn81K1vBoRw==} engines: {node: '>=22.13.0'} lmdb@3.5.7: @@ -6631,9 +6651,6 @@ packages: magic-string@0.30.21: resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} - magic-string@1.4.2: - resolution: {integrity: sha512-vG+rjFRj1PqdIBozIxAGMjPlOhaVe+GXpbttY/iSK7rGcJRMlwNJO7dcUwmUqkymsFLJiNGI06t4D7Fr7yRC9g==} - magic-string@1.4.3: resolution: {integrity: sha512-z12OxmaPGE0F4xlpGdjuAUckipLpQlTAuAmyGhNoD7ODpYUzvDfvtxUbjM/jwznyP178oju/KDdyi220wNJ0RQ==} @@ -6858,8 +6875,8 @@ packages: resolution: {integrity: sha512-dkEJPVvun4FryqBmZ5KhDo0K9iDXAwn08tMLDinNdRBNPcYEDiWYysLcc6k3mjTMlbP9KyylvRpd4wFtwrT9rw==} engines: {node: ^20.17.0 || >=22.9.0} - nanoid@3.3.19: - resolution: {integrity: sha512-Y2tUNy4ouw6tq5oDSKeQYGOyhkUBhNOcGV/02KC+6kd9eDGqdZd++mjMiIDilrBYvjEnCYvVtsuHCuP+okSfug==} + nanoid@3.3.20: + resolution: {integrity: sha512-uKdg2G3GNCKQn9byYOpxbGqrT2fGO5KRt5J/8b3pok8rT6qxGWF6hxMyJiEYtAf+FVyYuD9hRaDqX5uPFYJ4ZQ==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true @@ -6904,9 +6921,9 @@ packages: tailwindcss: optional: true - nock@14.0.17: - resolution: {integrity: sha512-EjRr1weMa4ALQX35AgZTEnP+weJJjlW1KGDiNM2IQC2069YDHas4f4B4UUYR+TTLyKWxJvOz2wObDKQs/LNreA==} - engines: {node: '>=18.20.0 <20 || >=20.12.1'} + nock@15.0.1: + resolution: {integrity: sha512-Mjx5Xl4t9vP13eUZPWqwwpjjXNjvhUTo1qTdryVM3+7t6Z2w6WHZ9GVZk/zQ8Ym1vbmloWaglye3cloyHAJ8FA==} + engines: {node: '>=20.12.1'} node-addon-api@6.1.0: resolution: {integrity: sha512-+eawOlIgy680F0kBzPUNFhMZGtJ1YmqM6l4+Crf4IkImjYrO/mqPwRMh352g23uIaQKFItcQ64I7KMaJxHgAVA==} @@ -6944,8 +6961,8 @@ packages: resolution: {integrity: sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==} hasBin: true - node-releases@2.0.57: - resolution: {integrity: sha512-kQK9LGGFiHtrWiNhZtA7Qbw17AQz+dmsEKODRIVTXA9+e5MS/2gZEBhYJt13GrAz5/IOZKddH/0Z3TP/Zgo+yw==} + node-releases@2.0.58: + resolution: {integrity: sha512-BdiKAgbfRlvfMq/XzO/AF9NPmaA8a+YpLAyAFTVFBj3gARrAIfWvNq7Q7NE4se3KgFu0KkrvQMxTS0XVnsGzHg==} engines: {node: '>=18'} normalize-path@3.0.0: @@ -7000,10 +7017,6 @@ packages: resolution: {integrity: sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==} engines: {node: '>= 0.4'} - obug@2.2.1: - resolution: {integrity: sha512-XrsrhT5sybtKI6wakr2SPOlGZWWYbUXZ7a0jT8/QOeAPau+1X/bSegNe5YR75oJmEZQbKningirmGOEJCIk61Q==} - engines: {node: '>=12.20.0'} - obug@3.0.0: resolution: {integrity: sha512-5vvB5+W7ePv+p3uqxi+RcW1XAzLW0/hxt3/4X4Lc4qHudzOhmBiBwOY6DRob4WnanAEGvNcLjF+KNOufrUoEQw==} engines: {node: '>=12.20.0'} @@ -7057,8 +7070,8 @@ packages: resolution: {integrity: sha512-19YVAg7T+WTrxggPukVq7DjTv6+PJ867TmhCvBsYwmbFCsZd344rq2Ld1p0wo8f8Qrrhgp82c6FJRqdXWtSEhg==} engines: {node: '>= 0.4'} - oxc-parser@0.153.0: - resolution: {integrity: sha512-cEnpyyV1OJRXv++SFNw0z8i+2o/cr88rVLaOYJafELDoHM5yHvCAbwGRLvxuany6SNLcbblSMUyZaGzH+quSYg==} + oxc-parser@0.154.0: + resolution: {integrity: sha512-Gr/6fkjZwi937c4Tsz8y0iO4U0FLPkZI6JSVucLAIvQyqh379px7RS8ORIc0HOORbFjXZ6zyqWLl53qxhSeofw==} engines: {node: ^20.19.0 || >=22.12.0} p-finally@1.0.0: @@ -7264,14 +7277,14 @@ packages: postcss-value-parser@4.2.0: resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} - postcss@8.5.28: - resolution: {integrity: sha512-RRuzqDtt5Y9h3quz5hWhK+TPnsmVs6WwSU6LkJMeY4HstUEDuYTG8UJSdawMRzmzAtV+KEoG8N3Qg2qLy5vM/A==} - engines: {node: ^10 || ^12 || >=14} - postcss@8.5.29: resolution: {integrity: sha512-49cGhUbXj8Qenv0iTMxA1cFBzxXoctpC9Ujd77t1WcbJIr6nF/eI7g/8MgxrYldFRuAXvja7xQRwavoW7kgrxQ==} engines: {node: ^10 || ^12 || >=14} + postcss@8.5.30: + resolution: {integrity: sha512-zkd+e9S1AQHhmaBKye9zlFHWjOwVgqXl1q3ECYy8561K7qxR2cREf/STLskulBFd78k6AOFeA+ff1TtEzk9bAA==} + engines: {node: ^10 || ^12 || >=14} + powershell-utils@0.2.1: resolution: {integrity: sha512-C+y9x90UElAddDZmV4qOx9W53B61PO7cIqWz2dQsWlwswuq4mr8NEwytdGKboYbQlGZ3awrkTeNvcZiZNHnQ8A==} engines: {node: '>=20'} @@ -7280,8 +7293,8 @@ packages: resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} engines: {node: '>= 0.8.0'} - prettier@3.9.9: - resolution: {integrity: sha512-Z/CJHIkdujO/OtN7nXUii0Rf3VT5SRuhjBA82Xvu2XhBUgX3nhP67T0LHceBdQLex7OOFGTox+Q5Yg8Jk2Qivg==} + prettier@3.9.10: + resolution: {integrity: sha512-skGHTH1Yb9QvaTC4hWnRTzEYtCpApF0UMBPz+t/0ZPzP8ihefo/btKd+QA7pUE0fEwA1SbS/NmhyEcstdXOIdA==} engines: {node: '>=14'} hasBin: true @@ -7298,17 +7311,13 @@ packages: process-warning@1.0.0: resolution: {integrity: sha512-du4wfLyj4yCZq1VupnVSZmRsPJsNuxoDQFdCFHLaYiEbFBD7QE0a+I4D7hOxrVnh78QE/YipFAj9lXHiXocV+Q==} - process-warning@5.1.0: - resolution: {integrity: sha512-jQSaVHsPgtyw60e1rQ/A+/ArPEj/S8pS/vFnyGa/gYFXrKk/6RuDkoqVDQ5NI5MmS01698ltlAk0NoDBNLujRw==} + process-warning@5.1.1: + resolution: {integrity: sha512-oCkNVj4OKnKEcocVBcDfiIugN1/cE6rgfT3nCtzeqZPA8+rIxMaQ0xXzIkGP8Gj2qUaP1UR7uhJ1VD8lUCpzCg==} process@0.11.10: resolution: {integrity: sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==} engines: {node: '>= 0.6.0'} - propagate@2.0.1: - resolution: {integrity: sha512-vGrhOavPSTz4QVNuBNdcNXePNdNMaO1xj9yBeH1ScQPjk/rhg9sSlCXPhMkFuaNNW/syTvYqsnbIJxMBfRbbag==} - engines: {node: '>= 8'} - proto3-json-serializer@3.0.4: resolution: {integrity: sha512-E1sbAYg3aEbXrq0n1ojJkRHQJGE1kaE/O6GLA94y8rnJBfgvOPTOd1b9hOceQK1FFZI9qMh1vBERCyO2ifubcw==} engines: {node: '>=18'} @@ -7346,12 +7355,12 @@ packages: resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} engines: {node: '>=6'} - puppeteer-core@25.12.0: - resolution: {integrity: sha512-z6LQUt5SH7jwGtGA+nMtJuEjZIsqPiwQ+EFHCYuVDQrJpvifNmmGuyDvTzAKPyFzihxgHc5f3aMzu2kn8Ps37A==} + puppeteer-core@25.14.0: + resolution: {integrity: sha512-LQitnes3tBeVbdZ7V/4iwJPLZGgkl9uBVK0W7YYMh68j/3Y9eNuvj4yl+Fdo7zN0SuajOMrhd7aPj3DifK8k6A==} engines: {node: '>=22.12.0'} - puppeteer@25.12.0: - resolution: {integrity: sha512-agM9cYcxbvmU17w9qtF17s7/Vv7Dhbyig6AWwIgvQQLR2dKZdPqNvRPanac7xH4iMPcNYOCfyCkwqCnR4nVOcA==} + puppeteer@25.14.0: + resolution: {integrity: sha512-+s1Iqbl9DsGLOaQdgCQyhtgHZsF9hi4Iu8iuBqxzraxUXoRG1QkYQI0CA1/VsNK1gZWJfs50BL/oOb1L+Yz8Yg==} engines: {node: '>=22.12.0'} hasBin: true @@ -7419,8 +7428,8 @@ packages: resolution: {integrity: sha512-yjavECdqeZ3GLXNgRXgeQEdz9fvDDkNKyHnbHRFtOr7/LcfgBcmct7t/ET+HaCTqfh06OzoAxrkN/IfjJBVe+g==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - readable-stream@4.7.0: - resolution: {integrity: sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==} + readable-stream@4.8.0: + resolution: {integrity: sha512-2jhTTU3Eilp/2DMT71gbcoqC35eQHasMJH0RHkYxwlxIv3+FR6Y5xAFZIwEGLxKkjZYEFlsiMNIdwUqjK2lLHQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} readdirp@3.6.0: @@ -7456,15 +7465,15 @@ packages: resolution: {integrity: sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==} engines: {node: '>= 0.4'} - regexpu-core@6.5.2: - resolution: {integrity: sha512-zkmVH92DlzjSFc4xKGZp/2BXIFrDIdBQBC8gUXKCsMgKYb1bkCkxfSuoW1werDjaTEMTbULkHtKFHe1L+Abnaw==} + regexpu-core@6.5.3: + resolution: {integrity: sha512-E8VgUoSK+LMQrLboDxavm76MTBGrQhx9RiNuomRmYnZ10Uw12HrI8fkxOOncktb2hBpzIZ9SV/347euQHG5z+g==} engines: {node: '>=4'} regjsgen@0.8.0: resolution: {integrity: sha512-RvwtGe3d7LvWiDQXeQw8p5asZUmfU1G/l6WbUXeHta7Y2PEIvBTwH6E2EfmYUK8pxcxEdEmaomqyp0vZZ7C+3Q==} - regjsparser@0.13.3: - resolution: {integrity: sha512-ycwFAS14Jw4mppvmK4GR/J6u3WpWpjkEApehuHtLc/8VpPNpDMbQ4WjqwplXifGeyKOzHSFLmSPqzksDQE2Sfg==} + regjsparser@0.13.4: + resolution: {integrity: sha512-EWV80YgFaVgFrjGCAsV7oybp4GOxbls5RzBg8OB0nKEVXsBJR1JlV8Di1JhCROg3MzTfe1RI8yitIm1eOs68iA==} hasBin: true require-directory@2.1.1: @@ -7492,8 +7501,8 @@ packages: resolution: {integrity: sha512-uZtduh8/8srhBoMx//5bwqjQ+rfYOUq8zC9NrMUGtjBiGTtFJM42s58/36+hTqeqINcnYe08Nj3LkK9lW4N8Xg==} engines: {node: '>=12'} - resolve@1.22.12: - resolution: {integrity: sha512-TyeJ1zif53BPfHootBGwPRYT1RUt6oGWsaQr8UyZW/eAm9bKoijtvruSDEmZHm92CwS9nj7/fWttqPCgzep8CA==} + resolve@1.22.13: + resolution: {integrity: sha512-Dj/cW9zBV2aRj6XzCxuGIs3ESZKv02jixne3q6jFZYZ3/Tg09gHHviSMDL4kObh9pqSahdRHEnDoaP2gn8YLhA==} engines: {node: '>= 0.4'} hasBin: true @@ -7522,6 +7531,9 @@ packages: resolution: {integrity: sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==} engines: {node: '>= 4'} + rettime@0.11.12: + resolution: {integrity: sha512-5ExtR1ppiDBQkVoPw5NAqdbfH4Iq2VOmQKHMtUoaFxA9bo2krzFYBtyq6K40xxbYp+2w/YOr6D+1ToF6tTN53A==} + reusify@1.1.0: resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} engines: {iojs: '>=1.0.0', node: '>=0.10.0'} @@ -7560,16 +7572,38 @@ packages: vue-tsc: optional: true - rolldown@1.2.11: - resolution: {integrity: sha512-qpSwIyz0jHQq5qXBTNxFmE6664rJ7O+4TvPFOiOaBSrz8IOHc1koKKSqTM2H6u1UG1+TveuC6vaDHKXFOvb1Kw==} - engines: {node: ^20.19.0 || >=22.12.0} - hasBin: true + rolldown-plugin-dts@0.28.8: + resolution: {integrity: sha512-FVhfqxorCWcc20BVT5eVRfRjzLLl6mgzjcMR4gSBf0K4oQMXT0qoHhhzfXy0L7y83fQy7Ug5ZDNk90xM3/LiCQ==} + engines: {node: ^22.18.0 || ^24.11.0 || >=26.0.0} + peerDependencies: + '@typescript/native-preview': '*' + '@volar/typescript': ~2.4.0 + '@vue/language-core': ~3.2.0 || ~3.3.0 + rolldown: ^1.2.0 + typescript: ^5.0.0 || ^6.0.0 || ~7.0.0 + vue-tsc: ~3.2.0 || ~3.3.0 + peerDependenciesMeta: + '@typescript/native-preview': + optional: true + '@volar/typescript': + optional: true + '@vue/language-core': + optional: true + typescript: + optional: true + vue-tsc: + optional: true rolldown@1.2.12: resolution: {integrity: sha512-8wafseiaG80xmXSfqidUNqZcylTlhmPZZt+za2m+js2sFZ8dTNlhIOV2WcbIPx2hgwPBJpEUGFAMZ9bgBBLTSQ==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true + rolldown@1.2.14: + resolution: {integrity: sha512-eTBTTqMR6uHKLgcUqpJP8ntzDI6V4LYA8negAMBEI7gkgVvOdrx6VsvS2Ym+83KhOPdLCHWJu0w7UQFJZhHKNw==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + rollup-plugin-dts@6.5.1: resolution: {integrity: sha512-jODTXp3H7MK/Ur/ErtsrQ0G1GvaCmc3du+y5pNrdBMf6d7HlL2Nd/N6TkEr+f75CkUj01zEoEd7y2elH0eHi1Q==} engines: {node: '>=20'} @@ -7591,8 +7625,8 @@ packages: '@types/node': optional: true - rollup@4.64.1: - resolution: {integrity: sha512-7s4V+3J8ux75WNj9qwSJnrWmlCmZ2YHr2fojtHy5RiIMvhJv6DGe7HKKz7L6G1sq1Eg1fLC8SjYtAtDis1F4CQ==} + rollup@4.64.6: + resolution: {integrity: sha512-fX1Vy+UNKdXn+xKDnu/HBXbHVr1AY8cSdQj9BU4kLv5aFCMqGpvrVbyUMmjN3DdzWhSAuTbcX8PCNEVaumthYA==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true @@ -8002,9 +8036,6 @@ packages: streamx@2.28.1: resolution: {integrity: sha512-zEzXb0s5Cds7tqMH6rhZ05lcJydCWiQPEwiNngVqzsxCc962vLY4Uw+mW7od8kDH258k2Uz/JrOkdIAAhSh9VA==} - strict-event-emitter@0.5.1: - resolution: {integrity: sha512-vMgjE/GGEPEFnhFub6pa4FmJBRBVOLpIII2hvCZ8Kzb7K0hlHo7mQv6xYrBvCL2LtAIBwFUK8wvuJgTVSQ5MFQ==} - string-width@4.2.3: resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} engines: {node: '>=8'} @@ -8135,8 +8166,8 @@ packages: tiny-inflate@1.0.3: resolution: {integrity: sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw==} - tinybench@6.2.0: - resolution: {integrity: sha512-78U2TlB2CnVenajOFzf3BKSm0J6oz5L0NV7g32LCPccvYc0lbWvys4d3uUUCS2B1N8PAf2+aekR8i1KbC3HO7Q==} + tinybench@6.2.2: + resolution: {integrity: sha512-nIZ2N2dFpjBsg2hgitKu7mA+FvsIJuGJOwysRngmVRRmWgR/UyPjwJj8c/fT/dhm8EkHnF5EOogRf+0Z2qrimA==} engines: {node: '>=20.0.0'} tinyexec@1.3.1: @@ -8154,15 +8185,15 @@ packages: tldts-core@6.1.86: resolution: {integrity: sha512-Je6p7pkk+KMzMv2XXKmAE3McmolOQFdxkKw0R8EYNr7sELW46JqnNeTX8ybPiQgvg1ymCoF8LXs5fzFaZvJPTA==} - tldts-core@7.4.16: - resolution: {integrity: sha512-MDolfaSJtlSK5Y0A1xl3277ekubZwobpBjugknDizI9O5Rm60a1m8k4ICK+MRsCDzPygT81mp3BBf5RKDlFRfA==} + tldts-core@7.4.18: + resolution: {integrity: sha512-KI2PKvCmvQI/xD6ypPqkU0i7oxNXR/O58RLRaHR4svsgVUQ2JE/XC8BtMbG6PSehD27I8sZJRcC1ASnCy6lp3g==} tldts@6.1.86: resolution: {integrity: sha512-WMi/OQ2axVTf/ykqCQgXiIct+mSQDFdH2fkwhPwgEwvJ1kSzZRiinb0zF2Xb8u4+OqPChmyI6MEu4EezNJz+FQ==} hasBin: true - tldts@7.4.16: - resolution: {integrity: sha512-QwBER5KMR86IIjpIiO7H/Z3IMJPsZ1A6RKPAqzTTgOyUQUSt9FdnKcqhTaJmkY6HVrgouZHZR0ncK5QxvmnQeg==} + tldts@7.4.18: + resolution: {integrity: sha512-somxA8xKAUzlAxl0TX/jMSYIbRNFIVGnI96pVGJPeMc5BsHmfVXn6mOJT6U++ELnl04eGfzpfcEuZe8WiASkXg==} hasBin: true tmp@0.2.7: @@ -8323,6 +8354,10 @@ packages: resolution: {integrity: sha512-u4UB2/IrKdU6lFxumHmmo1a3fCQO5tzQllRorfoRS63txhrB7xTpSn1PftwC4qEHkOaqP95fCWW4lJzwErwzhQ==} engines: {node: '>=22.19.0'} + undici@8.12.1: + resolution: {integrity: sha512-usz3OVQBeh1LxNn0DzU0voLXMgn5CU/WUdpBsY6Ti5afNao+vTkmn4H/NxoXolc10cOqe2+YIG0Hspq2rsl64w==} + engines: {node: '>=22.19.0'} + unicode-canonical-property-names-ecmascript@2.0.1: resolution: {integrity: sha512-dA8WbNeb2a6oQzAQ55YlT5vQAWGV9WXOsi3SskE3bcCdM0P4SDd+24zS/OCacdRq5BkdsRj9q3Pg6YyQoxIGqg==} engines: {node: '>=4'} @@ -8362,8 +8397,8 @@ packages: resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==} engines: {node: '>= 0.8'} - update-browserslist-db@1.3.3: - resolution: {integrity: sha512-pJ2sYawQS0R/WI928Gj5GlPhTGzbMelq0+4INtSYNDV9ErKJcX6xjGWkoG/VnB3dpUm00zALaqkrUD77pO5TDQ==} + update-browserslist-db@1.3.4: + resolution: {integrity: sha512-t8wonIOdQyOjtPd2VktlBu6QjTVTks6v8bSi7kd0r2Vll8TK1JYDZtRRdbmCHOAMphkZgrzK+L+vXK4pyZ8t4A==} hasBin: true peerDependencies: browserslist: '>= 4.21.0' @@ -8407,8 +8442,8 @@ packages: resolution: {integrity: sha512-Q5dkR66lSUpzoR2xKWFiFCYXlvC8hJqV+ZMMLgt9/vm9355VnH25ueIJzrpunO/QjhhgcT5yBlR6wGQjPHeHkA==} engines: {node: '>=22'} - verdaccio-auth-memory@13.1.5: - resolution: {integrity: sha512-rfKDhm1DzZoFdHUjiSwpe8hlXkiKB4brtzagaZcBnZXyBEASwW6SK/mdr4yU4asMcf+Zc8MjUJYnjlI7ji1yaw==} + verdaccio-auth-memory@13.1.6: + resolution: {integrity: sha512-L9LNz1q/JW1I9mPLch+8LUGnma/amo4EpS/+hZA6ZJJRIN7G5QAOKU9hLLPV6WqxO/I8T0KPtwNhyav9Aopqtg==} engines: {node: '>=22'} verdaccio-htpasswd@13.1.5: @@ -8420,10 +8455,6 @@ packages: engines: {node: '>=22'} hasBin: true - verkit@0.3.2: - resolution: {integrity: sha512-zj/ob3UsvJGN0whEAKFp53REA5X66hvffVqoCtVQAakJKnKlH+/PcOfMoFwIG/o4rElqLv/ycAFlx8ZlXUorCg==} - engines: {node: '>=18.12.0'} - verkit@0.5.0: resolution: {integrity: sha512-u4J1hr6Y+C/hit34GwL3eIcUf3wngT8TlYUjK7tk0jxydKLAq3sYvifjdTDA6U3KCKRcQCQDGf/srjxfZF3l7g==} engines: {node: '>=18.12.0'} @@ -8431,8 +8462,8 @@ packages: verror@1.10.0: resolution: {integrity: sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==} - vite@8.3.3: - resolution: {integrity: sha512-cTAldKPImjg6c+gk48U19POPn3GCBzZwpdsN8ZMEEcbpes+6/wvfqUd0C2y3qYY4wsj8PwgFwrZ/1jBPVttMSg==} + vite@8.3.4: + resolution: {integrity: sha512-IftLE8phJhdNRIGNEV4JCCvfNScoZQVfcXaN8TQropWGJtOHcu+iAB9pHcuGvmEb/kddw0LBe/jS8ztdC6xdiQ==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true peerDependencies: @@ -8474,20 +8505,20 @@ packages: yaml: optional: true - vitest@5.0.3: - resolution: {integrity: sha512-xMw97S3rjdtj5dkVat7jCsqWBpvchs3RlpQctUqwJD0KkERk40vz2fJ77lDwW/Vzh/pk18eItYAzkodhSes3jQ==} + vitest@5.0.4: + resolution: {integrity: sha512-z0WxZRV2Sg48wF0FSgMq3TFG8JS38/Qr96vZVZdX6+Em9raQTvnXQvdTFJe2m3rfyJnEHeHQ/snceIdi8D1pVg==} engines: {node: ^22.12.0 || ^24.0.0 || >=26.0.0} hasBin: true peerDependencies: '@edge-runtime/vm': '*' '@opentelemetry/api': ^1.9.0 '@types/node': ^22.0.0 || >=24.0.0 - '@vitest/browser-playwright': 5.0.3 - '@vitest/browser-preview': 5.0.3 + '@vitest/browser-playwright': 5.0.4 + '@vitest/browser-preview': 5.0.4 '@vitest/browser-webdriverio': ^5.0.0-beta.5 || >=5.0.0 - '@vitest/coverage-istanbul': 5.0.3 - '@vitest/coverage-v8': 5.0.3 - '@vitest/ui': 5.0.3 + '@vitest/coverage-istanbul': 5.0.4 + '@vitest/coverage-v8': 5.0.4 + '@vitest/ui': 5.0.4 happy-dom: '*' jsdom: '*' vite: ^6.4.0 || ^7.0.0 || ^8.0.0 @@ -8537,8 +8568,8 @@ packages: web-vitals@4.2.4: resolution: {integrity: sha512-r4DIlprAGwJ7YM11VZp4R884m0Vmgr6EAKe3P+kO0PPj3Unqyvv59rczf6UiGcb9Z8QxZVcqKNwv/g0WNdWwsw==} - webdriver-bidi-protocol@0.4.3: - resolution: {integrity: sha512-uuN0goWfxP22B7J/uAgBpOYNPttC+XVseYE+rSY5+rQ+YBeVz/VORw8WbmLVcqW78zNg5A4qnjNXYUWR3il2ig==} + webdriver-bidi-protocol@1.0.1: + resolution: {integrity: sha512-WXWs/aXF2a6Al1Z2/V/7Gp2oPZOAAmHjJOTmv4KCXRJ122IMj3tSSMc6a4q3K4QOmhQbse/Ahr+oTVJOU2dUiw==} webidl-conversions@3.0.1: resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} @@ -8777,18 +8808,34 @@ packages: yuku-ast@0.10.2: resolution: {integrity: sha512-UnG9mA6giglCvSErft2/40TVFX750Sj1xgwddPLpG7J7rlr/P1wPADg9G2RK+d/1tNLtRVoLdMMOMVBB9561TQ==} + yuku-ast@0.19.0: + resolution: {integrity: sha512-PyCsiScaelhxBGzL8dyeJkLqvRZycoh78R31Vrt/WqYrm0hMFoKwIw/X7Dx5mBKLWAMlV9I9HEyBOHnj2gZVyw==} + engines: {node: '>=14.18.0'} + yuku-codegen@0.10.2: resolution: {integrity: sha512-hentl2dtrF6cPjiAirtkfxfFZBA6Hdm61UqRJ7cA0qrlDNMk9PZGOjw5w1mx3E0hu/6pHcJF4dJW+D0SXHPZOA==} + yuku-codegen@0.19.0: + resolution: {integrity: sha512-uymAKHjDuaP4BEpkXC/BSLz4DUEF5oArZ5AuD3888nb2miChjWSgpPdojEJxm3pjpXkgUaCRJQyNAbnMN33wHg==} + engines: {node: '>=14.18.0'} + + yuku-core@0.19.0: + resolution: {integrity: sha512-kYK9e1XdfirNhkipzW0wmMcSWcVF/CWnuugodfVYRCUwvoBefweL9HzB96OiKEhsx78V5WbUxbJqlizMX869Qg==} + engines: {node: '>=14.18.0'} + yuku-parser@0.10.2: resolution: {integrity: sha512-CgaU0/PPjCAIEZ3WQroosOxTY3eeKldAN3h+vk8pMNz4+jl1CZzBR4pW+K/rRPF112VooCL5FdjJoiMNjDrL2A==} - zod@3.25.76: - resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==} + yuku-parser@0.19.0: + resolution: {integrity: sha512-XrBh9ZiQK5mJVRxZBAvD6LwRyAYrK3MFtH3+onyuxFL5hFD44EC3QOmIC1FjTj6H2MMwq46/NAI+NZxLZPZgxg==} + engines: {node: '>=14.18.0'} zod@4.6.5: resolution: {integrity: sha512-v5l/aFXZQeai4awLbOpSoHecE9UiMrnfx75tEXLjNonXVARxQ5mOeipTjROUchszUNCqnE+hqAMujRsRHsut2Q==} + zod@4.6.7: + resolution: {integrity: sha512-g8W2D2kU6a8untc053fX1uVBKzM7yqAxW/3zwk83+6RyF2+SKOuV+c0wBDhUwzV1PaGapmZH+rmcUzPf+3CGwg==} + zone.js@0.16.3: resolution: {integrity: sha512-ihXL9+vhYyEhXz4TDNpHeAOZN9FVrbog0Il64OIEI28UP/n5AaI6gsccRuOHBfx+206agzyoK527bYIO0Foy6A==} @@ -8815,31 +8862,31 @@ snapshots: '@jridgewell/gen-mapping': 0.3.13 '@jridgewell/trace-mapping': 0.3.31 - '@angular/animations@22.3.0-next.1(@angular/core@22.3.0-next.1(@angular/compiler@22.3.0-next.1)(rxjs@7.8.2)(zone.js@0.16.3))': + '@angular/animations@22.3.0-next.2(@angular/core@22.3.0-next.2(@angular/compiler@22.3.0-next.2)(rxjs@7.8.2)(zone.js@0.16.3))': dependencies: - '@angular/core': 22.3.0-next.1(@angular/compiler@22.3.0-next.1)(rxjs@7.8.2)(zone.js@0.16.3) + '@angular/core': 22.3.0-next.2(@angular/compiler@22.3.0-next.2)(rxjs@7.8.2)(zone.js@0.16.3) tslib: 2.8.1 - '@angular/cdk@22.3.0-next.1(1e228ab395252fe57958c1335d3a301f)': + '@angular/cdk@22.3.0-next.2(e02572097466d520d7eb144e95d6bbd2)': dependencies: - '@angular/common': 22.3.0-next.1(@angular/core@22.3.0-next.1(@angular/compiler@22.3.0-next.1)(rxjs@7.8.2)(zone.js@0.16.3))(rxjs@7.8.2) - '@angular/core': 22.3.0-next.1(@angular/compiler@22.3.0-next.1)(rxjs@7.8.2)(zone.js@0.16.3) - '@angular/forms': 22.3.0-next.1(@angular/common@22.3.0-next.1(@angular/core@22.3.0-next.1(@angular/compiler@22.3.0-next.1)(rxjs@7.8.2)(zone.js@0.16.3))(rxjs@7.8.2))(@angular/core@22.3.0-next.1(@angular/compiler@22.3.0-next.1)(rxjs@7.8.2)(zone.js@0.16.3))(@angular/platform-browser@22.3.0-next.1(@angular/animations@22.3.0-next.1(@angular/core@22.3.0-next.1(@angular/compiler@22.3.0-next.1)(rxjs@7.8.2)(zone.js@0.16.3)))(@angular/common@22.3.0-next.1(@angular/core@22.3.0-next.1(@angular/compiler@22.3.0-next.1)(rxjs@7.8.2)(zone.js@0.16.3))(rxjs@7.8.2))(@angular/core@22.3.0-next.1(@angular/compiler@22.3.0-next.1)(rxjs@7.8.2)(zone.js@0.16.3)))(rxjs@7.8.2) - '@angular/platform-browser': 22.3.0-next.1(@angular/animations@22.3.0-next.1(@angular/core@22.3.0-next.1(@angular/compiler@22.3.0-next.1)(rxjs@7.8.2)(zone.js@0.16.3)))(@angular/common@22.3.0-next.1(@angular/core@22.3.0-next.1(@angular/compiler@22.3.0-next.1)(rxjs@7.8.2)(zone.js@0.16.3))(rxjs@7.8.2))(@angular/core@22.3.0-next.1(@angular/compiler@22.3.0-next.1)(rxjs@7.8.2)(zone.js@0.16.3)) + '@angular/common': 22.3.0-next.2(@angular/core@22.3.0-next.2(@angular/compiler@22.3.0-next.2)(rxjs@7.8.2)(zone.js@0.16.3))(rxjs@7.8.2) + '@angular/core': 22.3.0-next.2(@angular/compiler@22.3.0-next.2)(rxjs@7.8.2)(zone.js@0.16.3) + '@angular/forms': 22.3.0-next.2(@angular/common@22.3.0-next.2(@angular/core@22.3.0-next.2(@angular/compiler@22.3.0-next.2)(rxjs@7.8.2)(zone.js@0.16.3))(rxjs@7.8.2))(@angular/core@22.3.0-next.2(@angular/compiler@22.3.0-next.2)(rxjs@7.8.2)(zone.js@0.16.3))(@angular/platform-browser@22.3.0-next.2(@angular/animations@22.3.0-next.2(@angular/core@22.3.0-next.2(@angular/compiler@22.3.0-next.2)(rxjs@7.8.2)(zone.js@0.16.3)))(@angular/common@22.3.0-next.2(@angular/core@22.3.0-next.2(@angular/compiler@22.3.0-next.2)(rxjs@7.8.2)(zone.js@0.16.3))(rxjs@7.8.2))(@angular/core@22.3.0-next.2(@angular/compiler@22.3.0-next.2)(rxjs@7.8.2)(zone.js@0.16.3)))(rxjs@7.8.2) + '@angular/platform-browser': 22.3.0-next.2(@angular/animations@22.3.0-next.2(@angular/core@22.3.0-next.2(@angular/compiler@22.3.0-next.2)(rxjs@7.8.2)(zone.js@0.16.3)))(@angular/common@22.3.0-next.2(@angular/core@22.3.0-next.2(@angular/compiler@22.3.0-next.2)(rxjs@7.8.2)(zone.js@0.16.3))(rxjs@7.8.2))(@angular/core@22.3.0-next.2(@angular/compiler@22.3.0-next.2)(rxjs@7.8.2)(zone.js@0.16.3)) parse5: 8.0.1 rxjs: 7.8.2 tslib: 2.8.1 - '@angular/common@22.3.0-next.1(@angular/core@22.3.0-next.1(@angular/compiler@22.3.0-next.1)(rxjs@7.8.2)(zone.js@0.16.3))(rxjs@7.8.2)': + '@angular/common@22.3.0-next.2(@angular/core@22.3.0-next.2(@angular/compiler@22.3.0-next.2)(rxjs@7.8.2)(zone.js@0.16.3))(rxjs@7.8.2)': dependencies: - '@angular/core': 22.3.0-next.1(@angular/compiler@22.3.0-next.1)(rxjs@7.8.2)(zone.js@0.16.3) + '@angular/core': 22.3.0-next.2(@angular/compiler@22.3.0-next.2)(rxjs@7.8.2)(zone.js@0.16.3) rxjs: 7.8.2 tslib: 2.8.1 - '@angular/compiler-cli@22.3.0-next.1(@angular/compiler@22.3.0-next.1)(typescript@6.0.3)': + '@angular/compiler-cli@22.3.0-next.2(@angular/compiler@22.3.0-next.2)(typescript@6.0.3)': dependencies: - '@angular/compiler': 22.3.0-next.1 - '@babel/core': 8.0.6 + '@angular/compiler': 22.3.0-next.2 + '@babel/core': 8.0.7 '@jridgewell/sourcemap-codec': 1.6.0 chokidar: 5.0.0 convert-source-map: 1.9.0 @@ -8850,70 +8897,68 @@ snapshots: optionalDependencies: typescript: 6.0.3 - '@angular/compiler@22.3.0-next.1': + '@angular/compiler@22.3.0-next.2': dependencies: tslib: 2.8.1 - '@angular/core@22.3.0-next.1(@angular/compiler@22.3.0-next.1)(rxjs@7.8.2)(zone.js@0.16.3)': + '@angular/core@22.3.0-next.2(@angular/compiler@22.3.0-next.2)(rxjs@7.8.2)(zone.js@0.16.3)': dependencies: rxjs: 7.8.2 tslib: 2.8.1 optionalDependencies: - '@angular/compiler': 22.3.0-next.1 + '@angular/compiler': 22.3.0-next.2 zone.js: 0.16.3 - '@angular/forms@22.3.0-next.1(@angular/common@22.3.0-next.1(@angular/core@22.3.0-next.1(@angular/compiler@22.3.0-next.1)(rxjs@7.8.2)(zone.js@0.16.3))(rxjs@7.8.2))(@angular/core@22.3.0-next.1(@angular/compiler@22.3.0-next.1)(rxjs@7.8.2)(zone.js@0.16.3))(@angular/platform-browser@22.3.0-next.1(@angular/animations@22.3.0-next.1(@angular/core@22.3.0-next.1(@angular/compiler@22.3.0-next.1)(rxjs@7.8.2)(zone.js@0.16.3)))(@angular/common@22.3.0-next.1(@angular/core@22.3.0-next.1(@angular/compiler@22.3.0-next.1)(rxjs@7.8.2)(zone.js@0.16.3))(rxjs@7.8.2))(@angular/core@22.3.0-next.1(@angular/compiler@22.3.0-next.1)(rxjs@7.8.2)(zone.js@0.16.3)))(rxjs@7.8.2)': + '@angular/forms@22.3.0-next.2(@angular/common@22.3.0-next.2(@angular/core@22.3.0-next.2(@angular/compiler@22.3.0-next.2)(rxjs@7.8.2)(zone.js@0.16.3))(rxjs@7.8.2))(@angular/core@22.3.0-next.2(@angular/compiler@22.3.0-next.2)(rxjs@7.8.2)(zone.js@0.16.3))(@angular/platform-browser@22.3.0-next.2(@angular/animations@22.3.0-next.2(@angular/core@22.3.0-next.2(@angular/compiler@22.3.0-next.2)(rxjs@7.8.2)(zone.js@0.16.3)))(@angular/common@22.3.0-next.2(@angular/core@22.3.0-next.2(@angular/compiler@22.3.0-next.2)(rxjs@7.8.2)(zone.js@0.16.3))(rxjs@7.8.2))(@angular/core@22.3.0-next.2(@angular/compiler@22.3.0-next.2)(rxjs@7.8.2)(zone.js@0.16.3)))(rxjs@7.8.2)': dependencies: - '@angular/common': 22.3.0-next.1(@angular/core@22.3.0-next.1(@angular/compiler@22.3.0-next.1)(rxjs@7.8.2)(zone.js@0.16.3))(rxjs@7.8.2) - '@angular/core': 22.3.0-next.1(@angular/compiler@22.3.0-next.1)(rxjs@7.8.2)(zone.js@0.16.3) - '@angular/platform-browser': 22.3.0-next.1(@angular/animations@22.3.0-next.1(@angular/core@22.3.0-next.1(@angular/compiler@22.3.0-next.1)(rxjs@7.8.2)(zone.js@0.16.3)))(@angular/common@22.3.0-next.1(@angular/core@22.3.0-next.1(@angular/compiler@22.3.0-next.1)(rxjs@7.8.2)(zone.js@0.16.3))(rxjs@7.8.2))(@angular/core@22.3.0-next.1(@angular/compiler@22.3.0-next.1)(rxjs@7.8.2)(zone.js@0.16.3)) + '@angular/common': 22.3.0-next.2(@angular/core@22.3.0-next.2(@angular/compiler@22.3.0-next.2)(rxjs@7.8.2)(zone.js@0.16.3))(rxjs@7.8.2) + '@angular/core': 22.3.0-next.2(@angular/compiler@22.3.0-next.2)(rxjs@7.8.2)(zone.js@0.16.3) + '@angular/platform-browser': 22.3.0-next.2(@angular/animations@22.3.0-next.2(@angular/core@22.3.0-next.2(@angular/compiler@22.3.0-next.2)(rxjs@7.8.2)(zone.js@0.16.3)))(@angular/common@22.3.0-next.2(@angular/core@22.3.0-next.2(@angular/compiler@22.3.0-next.2)(rxjs@7.8.2)(zone.js@0.16.3))(rxjs@7.8.2))(@angular/core@22.3.0-next.2(@angular/compiler@22.3.0-next.2)(rxjs@7.8.2)(zone.js@0.16.3)) '@standard-schema/spec': 1.1.0 rxjs: 7.8.2 tslib: 2.8.1 - zod: 4.6.5 + zod: 4.6.7 - '@angular/localize@22.3.0-next.1(@angular/compiler-cli@22.3.0-next.1(@angular/compiler@22.3.0-next.1)(typescript@6.0.3))(@angular/compiler@22.3.0-next.1)': + '@angular/localize@22.3.0-next.2(@angular/compiler-cli@22.3.0-next.2(@angular/compiler@22.3.0-next.2)(typescript@6.0.3))(@angular/compiler@22.3.0-next.2)': dependencies: - '@angular/compiler': 22.3.0-next.1 - '@angular/compiler-cli': 22.3.0-next.1(@angular/compiler@22.3.0-next.1)(typescript@6.0.3) - '@babel/core': 8.0.6 + '@angular/compiler': 22.3.0-next.2 + '@angular/compiler-cli': 22.3.0-next.2(@angular/compiler@22.3.0-next.2)(typescript@6.0.3) + '@babel/core': 8.0.7 tinyglobby: 0.2.17 yargs: 18.2.0 - '@angular/material@22.3.0-next.1(837417520965053888cf2615dc986a27)': + '@angular/material@22.3.0-next.2(4016851894464b4783f4f873310c276b)': dependencies: - '@angular/cdk': 22.3.0-next.1(1e228ab395252fe57958c1335d3a301f) - '@angular/common': 22.3.0-next.1(@angular/core@22.3.0-next.1(@angular/compiler@22.3.0-next.1)(rxjs@7.8.2)(zone.js@0.16.3))(rxjs@7.8.2) - '@angular/core': 22.3.0-next.1(@angular/compiler@22.3.0-next.1)(rxjs@7.8.2)(zone.js@0.16.3) - '@angular/forms': 22.3.0-next.1(@angular/common@22.3.0-next.1(@angular/core@22.3.0-next.1(@angular/compiler@22.3.0-next.1)(rxjs@7.8.2)(zone.js@0.16.3))(rxjs@7.8.2))(@angular/core@22.3.0-next.1(@angular/compiler@22.3.0-next.1)(rxjs@7.8.2)(zone.js@0.16.3))(@angular/platform-browser@22.3.0-next.1(@angular/animations@22.3.0-next.1(@angular/core@22.3.0-next.1(@angular/compiler@22.3.0-next.1)(rxjs@7.8.2)(zone.js@0.16.3)))(@angular/common@22.3.0-next.1(@angular/core@22.3.0-next.1(@angular/compiler@22.3.0-next.1)(rxjs@7.8.2)(zone.js@0.16.3))(rxjs@7.8.2))(@angular/core@22.3.0-next.1(@angular/compiler@22.3.0-next.1)(rxjs@7.8.2)(zone.js@0.16.3)))(rxjs@7.8.2) - '@angular/platform-browser': 22.3.0-next.1(@angular/animations@22.3.0-next.1(@angular/core@22.3.0-next.1(@angular/compiler@22.3.0-next.1)(rxjs@7.8.2)(zone.js@0.16.3)))(@angular/common@22.3.0-next.1(@angular/core@22.3.0-next.1(@angular/compiler@22.3.0-next.1)(rxjs@7.8.2)(zone.js@0.16.3))(rxjs@7.8.2))(@angular/core@22.3.0-next.1(@angular/compiler@22.3.0-next.1)(rxjs@7.8.2)(zone.js@0.16.3)) + '@angular/cdk': 22.3.0-next.2(e02572097466d520d7eb144e95d6bbd2) + '@angular/common': 22.3.0-next.2(@angular/core@22.3.0-next.2(@angular/compiler@22.3.0-next.2)(rxjs@7.8.2)(zone.js@0.16.3))(rxjs@7.8.2) + '@angular/core': 22.3.0-next.2(@angular/compiler@22.3.0-next.2)(rxjs@7.8.2)(zone.js@0.16.3) + '@angular/forms': 22.3.0-next.2(@angular/common@22.3.0-next.2(@angular/core@22.3.0-next.2(@angular/compiler@22.3.0-next.2)(rxjs@7.8.2)(zone.js@0.16.3))(rxjs@7.8.2))(@angular/core@22.3.0-next.2(@angular/compiler@22.3.0-next.2)(rxjs@7.8.2)(zone.js@0.16.3))(@angular/platform-browser@22.3.0-next.2(@angular/animations@22.3.0-next.2(@angular/core@22.3.0-next.2(@angular/compiler@22.3.0-next.2)(rxjs@7.8.2)(zone.js@0.16.3)))(@angular/common@22.3.0-next.2(@angular/core@22.3.0-next.2(@angular/compiler@22.3.0-next.2)(rxjs@7.8.2)(zone.js@0.16.3))(rxjs@7.8.2))(@angular/core@22.3.0-next.2(@angular/compiler@22.3.0-next.2)(rxjs@7.8.2)(zone.js@0.16.3)))(rxjs@7.8.2) + '@angular/platform-browser': 22.3.0-next.2(@angular/animations@22.3.0-next.2(@angular/core@22.3.0-next.2(@angular/compiler@22.3.0-next.2)(rxjs@7.8.2)(zone.js@0.16.3)))(@angular/common@22.3.0-next.2(@angular/core@22.3.0-next.2(@angular/compiler@22.3.0-next.2)(rxjs@7.8.2)(zone.js@0.16.3))(rxjs@7.8.2))(@angular/core@22.3.0-next.2(@angular/compiler@22.3.0-next.2)(rxjs@7.8.2)(zone.js@0.16.3)) rxjs: 7.8.2 tslib: 2.8.1 - '@angular/ng-dev@https://codeload.github.com/angular/dev-infra-private-ng-dev-builds/tar.gz/89b8c002e5d47ba1cbc4483d3ec06b0ae0aaccb7': + '@angular/ng-dev@https://codeload.github.com/angular/dev-infra-private-ng-dev-builds/tar.gz/940c0beef0de4cb128e90962d47ff80e352a1cd8': dependencies: '@actions/core': 3.0.1 - '@conventional-changelog/git-client': 3.1.2(conventional-commits-filter@6.0.1)(conventional-commits-parser@7.1.2) + '@conventional-changelog/git-client': 3.2.0(conventional-commits-filter@6.0.1)(conventional-commits-parser@7.1.3) '@google-cloud/spanner': 8.0.0(supports-color@11.0.0) - '@google/genai': 2.25.0(bufferutil@4.1.0)(supports-color@11.0.0)(utf-8-validate@6.0.6) - '@inquirer/prompts': 8.7.2(@types/node@24.19.0) - '@inquirer/type': 4.1.1(@types/node@24.19.0) + '@google/genai': 2.29.1(bufferutil@4.1.0)(supports-color@11.0.0)(utf-8-validate@6.0.6) + '@inquirer/prompts': 8.7.3(@types/node@24.19.2) + '@inquirer/type': 4.2.0(@types/node@24.19.2) '@octokit/auth-app': 8.3.1 '@octokit/core': 7.0.8 '@octokit/graphql': 9.0.5 '@octokit/graphql-schema': 15.26.1 - '@octokit/openapi-types': 28.0.0 - '@octokit/plugin-paginate-rest': 15.0.0(@octokit/core@7.0.8) '@octokit/plugin-rest-endpoint-methods': 18.0.0(@octokit/core@7.0.8) '@octokit/request-error': 7.1.2 '@octokit/rest': 22.0.1 - '@octokit/types': 17.0.0 + '@octokit/types': 18.0.0 '@types/cli-progress': 3.11.6 '@types/ejs': 3.1.5 '@types/events': 3.0.3 '@types/folder-hash': 4.0.4 '@types/jasmine': 6.0.0 - '@types/node': 24.19.0 + '@types/node': 24.19.2 '@types/semver': 7.8.0 '@types/which': 3.0.4 '@types/yargs': 17.0.35 @@ -8922,19 +8967,19 @@ snapshots: bufferutil: 4.1.0 cli-progress: 3.12.0 conventional-commits-filter: 6.0.1 - conventional-commits-parser: 7.1.2 + conventional-commits-parser: 7.1.3 ejs: 7.0.1 encoding: 0.1.13 fast-glob: 3.3.3 - firebase: 12.19.0 - folder-hash: 4.1.3(supports-color@11.0.0) + firebase: 13.0.0 + folder-hash: 4.1.4(supports-color@11.0.0) jasmine: 6.3.0 jasmine-core: 6.3.0 jasmine-reporters: 2.5.2 jsonc-parser: 3.3.1 minimatch: 10.2.6 multimatch: 8.0.0 - nock: 14.0.17 + nock: 15.0.1(supports-color@11.0.0) semver: 7.8.5 supports-color: 11.0.0 tsx: 4.23.15 @@ -8944,44 +8989,44 @@ snapshots: which: 7.0.0 yaml: 2.9.1 yargs: 18.2.0 - zod: 4.6.5 + zod: 4.6.7 transitivePeerDependencies: - '@modelcontextprotocol/sdk' - '@react-native-async-storage/async-storage' - '@angular/platform-browser@22.3.0-next.1(@angular/animations@22.3.0-next.1(@angular/core@22.3.0-next.1(@angular/compiler@22.3.0-next.1)(rxjs@7.8.2)(zone.js@0.16.3)))(@angular/common@22.3.0-next.1(@angular/core@22.3.0-next.1(@angular/compiler@22.3.0-next.1)(rxjs@7.8.2)(zone.js@0.16.3))(rxjs@7.8.2))(@angular/core@22.3.0-next.1(@angular/compiler@22.3.0-next.1)(rxjs@7.8.2)(zone.js@0.16.3))': + '@angular/platform-browser@22.3.0-next.2(@angular/animations@22.3.0-next.2(@angular/core@22.3.0-next.2(@angular/compiler@22.3.0-next.2)(rxjs@7.8.2)(zone.js@0.16.3)))(@angular/common@22.3.0-next.2(@angular/core@22.3.0-next.2(@angular/compiler@22.3.0-next.2)(rxjs@7.8.2)(zone.js@0.16.3))(rxjs@7.8.2))(@angular/core@22.3.0-next.2(@angular/compiler@22.3.0-next.2)(rxjs@7.8.2)(zone.js@0.16.3))': dependencies: - '@angular/common': 22.3.0-next.1(@angular/core@22.3.0-next.1(@angular/compiler@22.3.0-next.1)(rxjs@7.8.2)(zone.js@0.16.3))(rxjs@7.8.2) - '@angular/core': 22.3.0-next.1(@angular/compiler@22.3.0-next.1)(rxjs@7.8.2)(zone.js@0.16.3) + '@angular/common': 22.3.0-next.2(@angular/core@22.3.0-next.2(@angular/compiler@22.3.0-next.2)(rxjs@7.8.2)(zone.js@0.16.3))(rxjs@7.8.2) + '@angular/core': 22.3.0-next.2(@angular/compiler@22.3.0-next.2)(rxjs@7.8.2)(zone.js@0.16.3) tslib: 2.8.1 optionalDependencies: - '@angular/animations': 22.3.0-next.1(@angular/core@22.3.0-next.1(@angular/compiler@22.3.0-next.1)(rxjs@7.8.2)(zone.js@0.16.3)) + '@angular/animations': 22.3.0-next.2(@angular/core@22.3.0-next.2(@angular/compiler@22.3.0-next.2)(rxjs@7.8.2)(zone.js@0.16.3)) - '@angular/platform-server@22.3.0-next.1(@angular/common@22.3.0-next.1(@angular/core@22.3.0-next.1(@angular/compiler@22.3.0-next.1)(rxjs@7.8.2)(zone.js@0.16.3))(rxjs@7.8.2))(@angular/compiler@22.3.0-next.1)(@angular/core@22.3.0-next.1(@angular/compiler@22.3.0-next.1)(rxjs@7.8.2)(zone.js@0.16.3))(@angular/platform-browser@22.3.0-next.1(@angular/animations@22.3.0-next.1(@angular/core@22.3.0-next.1(@angular/compiler@22.3.0-next.1)(rxjs@7.8.2)(zone.js@0.16.3)))(@angular/common@22.3.0-next.1(@angular/core@22.3.0-next.1(@angular/compiler@22.3.0-next.1)(rxjs@7.8.2)(zone.js@0.16.3))(rxjs@7.8.2))(@angular/core@22.3.0-next.1(@angular/compiler@22.3.0-next.1)(rxjs@7.8.2)(zone.js@0.16.3)))(rxjs@7.8.2)': + '@angular/platform-server@22.3.0-next.2(@angular/common@22.3.0-next.2(@angular/core@22.3.0-next.2(@angular/compiler@22.3.0-next.2)(rxjs@7.8.2)(zone.js@0.16.3))(rxjs@7.8.2))(@angular/compiler@22.3.0-next.2)(@angular/core@22.3.0-next.2(@angular/compiler@22.3.0-next.2)(rxjs@7.8.2)(zone.js@0.16.3))(@angular/platform-browser@22.3.0-next.2(@angular/animations@22.3.0-next.2(@angular/core@22.3.0-next.2(@angular/compiler@22.3.0-next.2)(rxjs@7.8.2)(zone.js@0.16.3)))(@angular/common@22.3.0-next.2(@angular/core@22.3.0-next.2(@angular/compiler@22.3.0-next.2)(rxjs@7.8.2)(zone.js@0.16.3))(rxjs@7.8.2))(@angular/core@22.3.0-next.2(@angular/compiler@22.3.0-next.2)(rxjs@7.8.2)(zone.js@0.16.3)))(rxjs@7.8.2)': dependencies: - '@angular/common': 22.3.0-next.1(@angular/core@22.3.0-next.1(@angular/compiler@22.3.0-next.1)(rxjs@7.8.2)(zone.js@0.16.3))(rxjs@7.8.2) - '@angular/compiler': 22.3.0-next.1 - '@angular/core': 22.3.0-next.1(@angular/compiler@22.3.0-next.1)(rxjs@7.8.2)(zone.js@0.16.3) - '@angular/platform-browser': 22.3.0-next.1(@angular/animations@22.3.0-next.1(@angular/core@22.3.0-next.1(@angular/compiler@22.3.0-next.1)(rxjs@7.8.2)(zone.js@0.16.3)))(@angular/common@22.3.0-next.1(@angular/core@22.3.0-next.1(@angular/compiler@22.3.0-next.1)(rxjs@7.8.2)(zone.js@0.16.3))(rxjs@7.8.2))(@angular/core@22.3.0-next.1(@angular/compiler@22.3.0-next.1)(rxjs@7.8.2)(zone.js@0.16.3)) + '@angular/common': 22.3.0-next.2(@angular/core@22.3.0-next.2(@angular/compiler@22.3.0-next.2)(rxjs@7.8.2)(zone.js@0.16.3))(rxjs@7.8.2) + '@angular/compiler': 22.3.0-next.2 + '@angular/core': 22.3.0-next.2(@angular/compiler@22.3.0-next.2)(rxjs@7.8.2)(zone.js@0.16.3) + '@angular/platform-browser': 22.3.0-next.2(@angular/animations@22.3.0-next.2(@angular/core@22.3.0-next.2(@angular/compiler@22.3.0-next.2)(rxjs@7.8.2)(zone.js@0.16.3)))(@angular/common@22.3.0-next.2(@angular/core@22.3.0-next.2(@angular/compiler@22.3.0-next.2)(rxjs@7.8.2)(zone.js@0.16.3))(rxjs@7.8.2))(@angular/core@22.3.0-next.2(@angular/compiler@22.3.0-next.2)(rxjs@7.8.2)(zone.js@0.16.3)) rxjs: 7.8.2 tslib: 2.8.1 xhr2: 0.2.1 - '@angular/router@22.3.0-next.1(@angular/common@22.3.0-next.1(@angular/core@22.3.0-next.1(@angular/compiler@22.3.0-next.1)(rxjs@7.8.2)(zone.js@0.16.3))(rxjs@7.8.2))(@angular/core@22.3.0-next.1(@angular/compiler@22.3.0-next.1)(rxjs@7.8.2)(zone.js@0.16.3))(@angular/platform-browser@22.3.0-next.1(@angular/animations@22.3.0-next.1(@angular/core@22.3.0-next.1(@angular/compiler@22.3.0-next.1)(rxjs@7.8.2)(zone.js@0.16.3)))(@angular/common@22.3.0-next.1(@angular/core@22.3.0-next.1(@angular/compiler@22.3.0-next.1)(rxjs@7.8.2)(zone.js@0.16.3))(rxjs@7.8.2))(@angular/core@22.3.0-next.1(@angular/compiler@22.3.0-next.1)(rxjs@7.8.2)(zone.js@0.16.3)))(rxjs@7.8.2)': + '@angular/router@22.3.0-next.2(@angular/common@22.3.0-next.2(@angular/core@22.3.0-next.2(@angular/compiler@22.3.0-next.2)(rxjs@7.8.2)(zone.js@0.16.3))(rxjs@7.8.2))(@angular/core@22.3.0-next.2(@angular/compiler@22.3.0-next.2)(rxjs@7.8.2)(zone.js@0.16.3))(@angular/platform-browser@22.3.0-next.2(@angular/animations@22.3.0-next.2(@angular/core@22.3.0-next.2(@angular/compiler@22.3.0-next.2)(rxjs@7.8.2)(zone.js@0.16.3)))(@angular/common@22.3.0-next.2(@angular/core@22.3.0-next.2(@angular/compiler@22.3.0-next.2)(rxjs@7.8.2)(zone.js@0.16.3))(rxjs@7.8.2))(@angular/core@22.3.0-next.2(@angular/compiler@22.3.0-next.2)(rxjs@7.8.2)(zone.js@0.16.3)))(rxjs@7.8.2)': dependencies: - '@angular/common': 22.3.0-next.1(@angular/core@22.3.0-next.1(@angular/compiler@22.3.0-next.1)(rxjs@7.8.2)(zone.js@0.16.3))(rxjs@7.8.2) - '@angular/core': 22.3.0-next.1(@angular/compiler@22.3.0-next.1)(rxjs@7.8.2)(zone.js@0.16.3) - '@angular/platform-browser': 22.3.0-next.1(@angular/animations@22.3.0-next.1(@angular/core@22.3.0-next.1(@angular/compiler@22.3.0-next.1)(rxjs@7.8.2)(zone.js@0.16.3)))(@angular/common@22.3.0-next.1(@angular/core@22.3.0-next.1(@angular/compiler@22.3.0-next.1)(rxjs@7.8.2)(zone.js@0.16.3))(rxjs@7.8.2))(@angular/core@22.3.0-next.1(@angular/compiler@22.3.0-next.1)(rxjs@7.8.2)(zone.js@0.16.3)) + '@angular/common': 22.3.0-next.2(@angular/core@22.3.0-next.2(@angular/compiler@22.3.0-next.2)(rxjs@7.8.2)(zone.js@0.16.3))(rxjs@7.8.2) + '@angular/core': 22.3.0-next.2(@angular/compiler@22.3.0-next.2)(rxjs@7.8.2)(zone.js@0.16.3) + '@angular/platform-browser': 22.3.0-next.2(@angular/animations@22.3.0-next.2(@angular/core@22.3.0-next.2(@angular/compiler@22.3.0-next.2)(rxjs@7.8.2)(zone.js@0.16.3)))(@angular/common@22.3.0-next.2(@angular/core@22.3.0-next.2(@angular/compiler@22.3.0-next.2)(rxjs@7.8.2)(zone.js@0.16.3))(rxjs@7.8.2))(@angular/core@22.3.0-next.2(@angular/compiler@22.3.0-next.2)(rxjs@7.8.2)(zone.js@0.16.3)) rxjs: 7.8.2 tslib: 2.8.1 - '@angular/service-worker@22.3.0-next.1(@angular/core@22.3.0-next.1(@angular/compiler@22.3.0-next.1)(rxjs@7.8.2)(zone.js@0.16.3))(rxjs@7.8.2)': + '@angular/service-worker@22.3.0-next.2(@angular/core@22.3.0-next.2(@angular/compiler@22.3.0-next.2)(rxjs@7.8.2)(zone.js@0.16.3))(rxjs@7.8.2)': dependencies: - '@angular/core': 22.3.0-next.1(@angular/compiler@22.3.0-next.1)(rxjs@7.8.2)(zone.js@0.16.3) + '@angular/core': 22.3.0-next.2(@angular/compiler@22.3.0-next.2)(rxjs@7.8.2)(zone.js@0.16.3) rxjs: 7.8.2 tslib: 2.8.1 - '@asamuzakjp/css-color@7.1.3': + '@asamuzakjp/css-color@7.1.5': dependencies: '@csstools/css-calc': 3.4.3(@csstools/css-parser-algorithms@4.0.2(@csstools/css-tokenizer@4.0.2))(@csstools/css-tokenizer@4.0.2) '@csstools/css-color-parser': 4.2.6(@csstools/css-parser-algorithms@4.0.2(@csstools/css-tokenizer@4.0.2))(@csstools/css-tokenizer@4.0.2) @@ -9017,10 +9062,10 @@ snapshots: '@babel/generator': 7.29.8 '@babel/helper-compilation-targets': 7.29.7 '@babel/helper-module-transforms': 7.29.7(@babel/core@7.29.7(supports-color@11.0.0))(supports-color@11.0.0) - '@babel/helpers': 7.29.7 + '@babel/helpers': 7.29.10 '@babel/parser': 7.29.9 '@babel/template': 7.29.7 - '@babel/traverse': 7.29.8(supports-color@11.0.0) + '@babel/traverse': 7.29.10(supports-color@11.0.0) '@babel/types': 7.29.8 '@jridgewell/remapping': 2.3.5 convert-source-map: 2.0.0 @@ -9031,25 +9076,6 @@ snapshots: transitivePeerDependencies: - supports-color - '@babel/core@8.0.6': - dependencies: - '@babel/code-frame': 8.0.6 - '@babel/generator': 8.0.6 - '@babel/helper-compilation-targets': 8.0.7 - '@babel/helpers': 8.0.7 - '@babel/parser': 8.0.7 - '@babel/template': 8.0.0 - '@babel/traverse': 8.0.7 - '@babel/types': 8.0.6 - '@types/gensync': 1.0.5 - convert-source-map: 2.0.0 - empathic: 2.1.0 - gensync: 1.0.0-beta.2 - import-meta-resolve: 4.2.0 - json5: 2.2.3 - obug: 2.2.1 - verkit: 0.3.2 - '@babel/core@8.0.7': dependencies: '@babel/code-frame': 8.0.6 @@ -9079,7 +9105,7 @@ snapshots: '@babel/generator@8.0.6': dependencies: - '@babel/parser': 8.0.6 + '@babel/parser': 8.0.7 '@babel/types': 8.0.6 '@jridgewell/gen-mapping': 0.4.0-beta.0 '@jridgewell/trace-mapping': 0.3.31 @@ -9098,14 +9124,6 @@ snapshots: lru-cache: 5.1.1 semver: 6.3.1 - '@babel/helper-compilation-targets@8.0.6': - dependencies: - '@babel/compat-data': 8.0.5 - '@babel/helper-validator-option': 8.0.0 - browserslist: 4.29.3 - flru: 1.0.2 - verkit: 0.3.2 - '@babel/helper-compilation-targets@8.0.7': dependencies: '@babel/compat-data': 8.0.5 @@ -9114,7 +9132,7 @@ snapshots: flru: 1.0.2 verkit: 0.5.0 - '@babel/helper-create-class-features-plugin@8.0.6(@babel/core@8.0.7)': + '@babel/helper-create-class-features-plugin@8.0.7(@babel/core@8.0.7)': dependencies: '@babel/core': 8.0.7 '@babel/helper-annotate-as-pure': 8.0.0 @@ -9122,20 +9140,20 @@ snapshots: '@babel/helper-optimise-call-expression': 8.0.0 '@babel/helper-replace-supers': 8.0.1(@babel/core@8.0.7) '@babel/helper-skip-transparent-expression-wrappers': 8.0.0 - '@babel/traverse': 8.0.6 - verkit: 0.3.2 + '@babel/traverse': 8.0.7 + verkit: 0.5.0 - '@babel/helper-create-regexp-features-plugin@8.0.5(@babel/core@8.0.7)': + '@babel/helper-create-regexp-features-plugin@8.0.7(@babel/core@8.0.7)': dependencies: '@babel/core': 8.0.7 '@babel/helper-annotate-as-pure': 8.0.0 - regexpu-core: 6.5.2 - verkit: 0.3.2 + regexpu-core: 6.5.3 + verkit: 0.5.0 '@babel/helper-define-polyfill-provider@1.0.0(@babel/core@8.0.7)': dependencies: '@babel/core': 8.0.7 - '@babel/helper-compilation-targets': 8.0.6 + '@babel/helper-compilation-targets': 8.0.7 '@babel/helper-plugin-utils': 8.0.1(@babel/core@8.0.7) lodash.debounce: 4.0.8 @@ -9145,19 +9163,19 @@ snapshots: '@babel/helper-member-expression-to-functions@8.0.5': dependencies: - '@babel/traverse': 8.0.6 + '@babel/traverse': 8.0.7 '@babel/types': 8.0.6 '@babel/helper-module-imports@7.29.7(supports-color@11.0.0)': dependencies: - '@babel/traverse': 7.29.8(supports-color@11.0.0) + '@babel/traverse': 7.29.10(supports-color@11.0.0) '@babel/types': 7.29.8 transitivePeerDependencies: - supports-color '@babel/helper-module-imports@8.0.0': dependencies: - '@babel/traverse': 8.0.6 + '@babel/traverse': 8.0.7 '@babel/types': 8.0.6 '@babel/helper-module-transforms@7.29.7(@babel/core@7.29.7(supports-color@11.0.0))(supports-color@11.0.0)': @@ -9165,7 +9183,7 @@ snapshots: '@babel/core': 7.29.7(supports-color@11.0.0) '@babel/helper-module-imports': 7.29.7(supports-color@11.0.0) '@babel/helper-validator-identifier': 7.29.7 - '@babel/traverse': 7.29.8(supports-color@11.0.0) + '@babel/traverse': 7.29.10(supports-color@11.0.0) transitivePeerDependencies: - supports-color @@ -9174,7 +9192,7 @@ snapshots: '@babel/core': 8.0.7 '@babel/helper-module-imports': 8.0.0 '@babel/helper-validator-identifier': 8.0.6 - '@babel/traverse': 8.0.6 + '@babel/traverse': 8.0.7 '@babel/helper-optimise-call-expression@8.0.0': dependencies: @@ -9189,18 +9207,18 @@ snapshots: '@babel/core': 8.0.7 '@babel/helper-annotate-as-pure': 8.0.0 '@babel/helper-wrap-function': 8.0.0 - '@babel/traverse': 8.0.6 + '@babel/traverse': 8.0.7 '@babel/helper-replace-supers@8.0.1(@babel/core@8.0.7)': dependencies: '@babel/core': 8.0.7 '@babel/helper-member-expression-to-functions': 8.0.5 '@babel/helper-optimise-call-expression': 8.0.0 - '@babel/traverse': 8.0.6 + '@babel/traverse': 8.0.7 '@babel/helper-skip-transparent-expression-wrappers@8.0.0': dependencies: - '@babel/traverse': 8.0.6 + '@babel/traverse': 8.0.7 '@babel/types': 8.0.6 '@babel/helper-split-export-declaration@7.24.7': @@ -9222,10 +9240,10 @@ snapshots: '@babel/helper-wrap-function@8.0.0': dependencies: '@babel/template': 8.0.0 - '@babel/traverse': 8.0.6 + '@babel/traverse': 8.0.7 '@babel/types': 8.0.6 - '@babel/helpers@7.29.7': + '@babel/helpers@7.29.10': dependencies: '@babel/template': 7.29.7 '@babel/types': 7.29.8 @@ -9239,10 +9257,6 @@ snapshots: dependencies: '@babel/types': 7.29.8 - '@babel/parser@8.0.6': - dependencies: - '@babel/types': 8.0.6 - '@babel/parser@8.0.7': dependencies: '@babel/types': 8.0.6 @@ -9290,7 +9304,7 @@ snapshots: '@babel/core': 8.0.7 '@babel/helper-plugin-utils': 8.0.1(@babel/core@8.0.7) '@babel/helper-remap-async-to-generator': 8.0.1(@babel/core@8.0.7) - '@babel/traverse': 8.0.6 + '@babel/traverse': 8.0.7 '@babel/plugin-transform-async-to-generator@8.0.1(@babel/core@8.0.7)': dependencies: @@ -9312,24 +9326,24 @@ snapshots: '@babel/plugin-transform-class-properties@8.0.1(@babel/core@8.0.7)': dependencies: '@babel/core': 8.0.7 - '@babel/helper-create-class-features-plugin': 8.0.6(@babel/core@8.0.7) + '@babel/helper-create-class-features-plugin': 8.0.7(@babel/core@8.0.7) '@babel/helper-plugin-utils': 8.0.1(@babel/core@8.0.7) '@babel/plugin-transform-class-static-block@8.0.5(@babel/core@8.0.7)': dependencies: '@babel/core': 8.0.7 - '@babel/helper-create-class-features-plugin': 8.0.6(@babel/core@8.0.7) + '@babel/helper-create-class-features-plugin': 8.0.7(@babel/core@8.0.7) '@babel/helper-plugin-utils': 8.0.1(@babel/core@8.0.7) '@babel/plugin-transform-classes@8.0.5(@babel/core@8.0.7)': dependencies: '@babel/core': 8.0.7 '@babel/helper-annotate-as-pure': 8.0.0 - '@babel/helper-compilation-targets': 8.0.6 + '@babel/helper-compilation-targets': 8.0.7 '@babel/helper-globals': 8.0.6 '@babel/helper-plugin-utils': 8.0.1(@babel/core@8.0.7) '@babel/helper-replace-supers': 8.0.1(@babel/core@8.0.7) - '@babel/traverse': 8.0.6 + '@babel/traverse': 8.0.7 '@babel/plugin-transform-computed-properties@8.0.1(@babel/core@8.0.7)': dependencies: @@ -9344,7 +9358,7 @@ snapshots: '@babel/plugin-transform-dotall-regex@8.0.1(@babel/core@8.0.7)': dependencies: '@babel/core': 8.0.7 - '@babel/helper-create-regexp-features-plugin': 8.0.5(@babel/core@8.0.7) + '@babel/helper-create-regexp-features-plugin': 8.0.7(@babel/core@8.0.7) '@babel/helper-plugin-utils': 8.0.1(@babel/core@8.0.7) '@babel/plugin-transform-duplicate-keys@8.0.1(@babel/core@8.0.7)': @@ -9355,7 +9369,7 @@ snapshots: '@babel/plugin-transform-duplicate-named-capturing-groups-regex@8.0.1(@babel/core@8.0.7)': dependencies: '@babel/core': 8.0.7 - '@babel/helper-create-regexp-features-plugin': 8.0.5(@babel/core@8.0.7) + '@babel/helper-create-regexp-features-plugin': 8.0.7(@babel/core@8.0.7) '@babel/helper-plugin-utils': 8.0.1(@babel/core@8.0.7) '@babel/plugin-transform-dynamic-import@8.0.1(@babel/core@8.0.7)': @@ -9388,7 +9402,7 @@ snapshots: '@babel/plugin-transform-function-name@8.0.1(@babel/core@8.0.7)': dependencies: '@babel/core': 8.0.7 - '@babel/helper-compilation-targets': 8.0.6 + '@babel/helper-compilation-targets': 8.0.7 '@babel/helper-plugin-utils': 8.0.1(@babel/core@8.0.7) '@babel/plugin-transform-json-strings@8.0.1(@babel/core@8.0.7)': @@ -9439,7 +9453,7 @@ snapshots: '@babel/plugin-transform-named-capturing-groups-regex@8.0.1(@babel/core@8.0.7)': dependencies: '@babel/core': 8.0.7 - '@babel/helper-create-regexp-features-plugin': 8.0.5(@babel/core@8.0.7) + '@babel/helper-create-regexp-features-plugin': 8.0.7(@babel/core@8.0.7) '@babel/helper-plugin-utils': 8.0.1(@babel/core@8.0.7) '@babel/plugin-transform-new-target@8.0.1(@babel/core@8.0.7)': @@ -9460,7 +9474,7 @@ snapshots: '@babel/plugin-transform-object-rest-spread@8.0.1(@babel/core@8.0.7)': dependencies: '@babel/core': 8.0.7 - '@babel/helper-compilation-targets': 8.0.6 + '@babel/helper-compilation-targets': 8.0.7 '@babel/helper-plugin-utils': 8.0.1(@babel/core@8.0.7) '@babel/plugin-transform-destructuring': 8.0.5(@babel/core@8.0.7) '@babel/plugin-transform-parameters': 8.0.5(@babel/core@8.0.7) @@ -9490,14 +9504,14 @@ snapshots: '@babel/plugin-transform-private-methods@8.0.1(@babel/core@8.0.7)': dependencies: '@babel/core': 8.0.7 - '@babel/helper-create-class-features-plugin': 8.0.6(@babel/core@8.0.7) + '@babel/helper-create-class-features-plugin': 8.0.7(@babel/core@8.0.7) '@babel/helper-plugin-utils': 8.0.1(@babel/core@8.0.7) '@babel/plugin-transform-private-property-in-object@8.0.5(@babel/core@8.0.7)': dependencies: '@babel/core': 8.0.7 '@babel/helper-annotate-as-pure': 8.0.0 - '@babel/helper-create-class-features-plugin': 8.0.6(@babel/core@8.0.7) + '@babel/helper-create-class-features-plugin': 8.0.7(@babel/core@8.0.7) '@babel/helper-plugin-utils': 8.0.1(@babel/core@8.0.7) '@babel/plugin-transform-property-literals@8.0.1(@babel/core@8.0.7)': @@ -9513,7 +9527,7 @@ snapshots: '@babel/plugin-transform-regexp-modifiers@8.0.1(@babel/core@8.0.7)': dependencies: '@babel/core': 8.0.7 - '@babel/helper-create-regexp-features-plugin': 8.0.5(@babel/core@8.0.7) + '@babel/helper-create-regexp-features-plugin': 8.0.7(@babel/core@8.0.7) '@babel/helper-plugin-utils': 8.0.1(@babel/core@8.0.7) '@babel/plugin-transform-reserved-words@8.0.1(@babel/core@8.0.7)': @@ -9561,19 +9575,19 @@ snapshots: '@babel/plugin-transform-unicode-property-regex@8.0.1(@babel/core@8.0.7)': dependencies: '@babel/core': 8.0.7 - '@babel/helper-create-regexp-features-plugin': 8.0.5(@babel/core@8.0.7) + '@babel/helper-create-regexp-features-plugin': 8.0.7(@babel/core@8.0.7) '@babel/helper-plugin-utils': 8.0.1(@babel/core@8.0.7) '@babel/plugin-transform-unicode-regex@8.0.1(@babel/core@8.0.7)': dependencies: '@babel/core': 8.0.7 - '@babel/helper-create-regexp-features-plugin': 8.0.5(@babel/core@8.0.7) + '@babel/helper-create-regexp-features-plugin': 8.0.7(@babel/core@8.0.7) '@babel/helper-plugin-utils': 8.0.1(@babel/core@8.0.7) '@babel/plugin-transform-unicode-sets-regex@8.0.1(@babel/core@8.0.7)': dependencies: '@babel/core': 8.0.7 - '@babel/helper-create-regexp-features-plugin': 8.0.5(@babel/core@8.0.7) + '@babel/helper-create-regexp-features-plugin': 8.0.7(@babel/core@8.0.7) '@babel/helper-plugin-utils': 8.0.1(@babel/core@8.0.7) '@babel/preset-env@8.0.7(@babel/core@8.0.7)': @@ -9665,10 +9679,10 @@ snapshots: '@babel/template@8.0.0': dependencies: '@babel/code-frame': 8.0.6 - '@babel/parser': 8.0.6 + '@babel/parser': 8.0.7 '@babel/types': 8.0.6 - '@babel/traverse@7.29.8(supports-color@11.0.0)': + '@babel/traverse@7.29.10(supports-color@11.0.0)': dependencies: '@babel/code-frame': 7.29.7 '@babel/generator': 7.29.8 @@ -9680,16 +9694,6 @@ snapshots: transitivePeerDependencies: - supports-color - '@babel/traverse@8.0.6': - dependencies: - '@babel/code-frame': 8.0.6 - '@babel/generator': 8.0.6 - '@babel/helper-globals': 8.0.6 - '@babel/parser': 8.0.6 - '@babel/template': 8.0.0 - '@babel/types': 8.0.6 - obug: 2.2.1 - '@babel/traverse@8.0.7': dependencies: '@babel/code-frame': 8.0.6 @@ -9718,7 +9722,7 @@ snapshots: '@bcoe/v8-coverage@1.0.2': {} - '@bramus/specificity@2.4.2': + '@bramus/specificity@2.4.3': dependencies: css-tree: 3.2.1 @@ -9738,14 +9742,14 @@ snapshots: '@colors/colors@1.5.0': {} - '@conventional-changelog/git-client@3.1.2(conventional-commits-filter@6.0.1)(conventional-commits-parser@7.1.2)': + '@conventional-changelog/git-client@3.2.0(conventional-commits-filter@6.0.1)(conventional-commits-parser@7.1.3)': dependencies: '@simple-libs/child-process-utils': 2.0.0 '@simple-libs/stream-utils': 2.0.0 - semver: 7.8.5 + verkit: 0.5.0 optionalDependencies: conventional-commits-filter: 6.0.1 - conventional-commits-parser: 7.1.2 + conventional-commits-parser: 7.1.3 '@csstools/color-helpers@6.1.2': {} @@ -9929,9 +9933,9 @@ snapshots: optionalDependencies: '@noble/hashes': 1.8.0 - '@firebase/ai@2.16.0(@firebase/app-types@0.9.6)(@firebase/app@0.16.2)': + '@firebase/ai@3.0.0(@firebase/app-types@0.9.6)(@firebase/app@0.16.3)': dependencies: - '@firebase/app': 0.16.2 + '@firebase/app': 0.16.3 '@firebase/app-check-interop-types': 0.3.5 '@firebase/app-types': 0.9.6 '@firebase/component': 0.7.5 @@ -9939,33 +9943,33 @@ snapshots: '@firebase/util': 1.15.3 tslib: 2.8.1 - '@firebase/analytics-compat@0.2.31(@firebase/app-compat@0.5.18)(@firebase/app@0.16.2)': + '@firebase/analytics-compat@0.2.31(@firebase/app-compat@0.5.19)(@firebase/app@0.16.3)': dependencies: - '@firebase/analytics': 0.10.25(@firebase/app@0.16.2) + '@firebase/analytics': 0.10.25(@firebase/app@0.16.3) '@firebase/analytics-types': 0.8.5 - '@firebase/app': 0.16.2 - '@firebase/app-compat': 0.5.18 + '@firebase/app': 0.16.3 + '@firebase/app-compat': 0.5.19 '@firebase/component': 0.7.5 '@firebase/util': 1.15.3 tslib: 2.8.1 '@firebase/analytics-types@0.8.5': {} - '@firebase/analytics@0.10.25(@firebase/app@0.16.2)': + '@firebase/analytics@0.10.25(@firebase/app@0.16.3)': dependencies: - '@firebase/app': 0.16.2 + '@firebase/app': 0.16.3 '@firebase/component': 0.7.5 - '@firebase/installations': 0.6.24(@firebase/app@0.16.2) + '@firebase/installations': 0.6.24(@firebase/app@0.16.3) '@firebase/logger': 0.5.2 '@firebase/util': 1.15.3 tslib: 2.8.1 - '@firebase/app-check-compat@0.4.7(@firebase/app-compat@0.5.18)(@firebase/app@0.16.2)': + '@firebase/app-check-compat@0.4.7(@firebase/app-compat@0.5.19)(@firebase/app@0.16.3)': dependencies: - '@firebase/app': 0.16.2 - '@firebase/app-check': 0.13.1(@firebase/app@0.16.2) + '@firebase/app': 0.16.3 + '@firebase/app-check': 0.13.1(@firebase/app@0.16.3) '@firebase/app-check-types': 0.5.5 - '@firebase/app-compat': 0.5.18 + '@firebase/app-compat': 0.5.19 '@firebase/component': 0.7.5 '@firebase/logger': 0.5.2 '@firebase/util': 1.15.3 @@ -9975,17 +9979,17 @@ snapshots: '@firebase/app-check-types@0.5.5': {} - '@firebase/app-check@0.13.1(@firebase/app@0.16.2)': + '@firebase/app-check@0.13.1(@firebase/app@0.16.3)': dependencies: - '@firebase/app': 0.16.2 + '@firebase/app': 0.16.3 '@firebase/component': 0.7.5 '@firebase/logger': 0.5.2 '@firebase/util': 1.15.3 tslib: 2.8.1 - '@firebase/app-compat@0.5.18': + '@firebase/app-compat@0.5.19': dependencies: - '@firebase/app': 0.16.2 + '@firebase/app': 0.16.3 '@firebase/component': 0.7.5 '@firebase/logger': 0.5.2 '@firebase/util': 1.15.3 @@ -9995,7 +9999,7 @@ snapshots: dependencies: '@firebase/logger': 0.5.2 - '@firebase/app@0.16.2': + '@firebase/app@0.16.3': dependencies: '@firebase/component': 0.7.5 '@firebase/logger': 0.5.2 @@ -10003,11 +10007,11 @@ snapshots: idb: 7.1.1 tslib: 2.8.1 - '@firebase/auth-compat@0.6.11(@firebase/app-compat@0.5.18)(@firebase/app-types@0.9.6)(@firebase/app@0.16.2)': + '@firebase/auth-compat@0.6.12(@firebase/app-compat@0.5.19)(@firebase/app-types@0.9.6)(@firebase/app@0.16.3)': dependencies: - '@firebase/app': 0.16.2 - '@firebase/app-compat': 0.5.18 - '@firebase/auth': 1.13.6(@firebase/app@0.16.2) + '@firebase/app': 0.16.3 + '@firebase/app-compat': 0.5.19 + '@firebase/auth': 1.13.7(@firebase/app@0.16.3) '@firebase/auth-types': 0.13.2(@firebase/app-types@0.9.6)(@firebase/util@1.15.3) '@firebase/component': 0.7.5 '@firebase/util': 1.15.3 @@ -10023,9 +10027,9 @@ snapshots: '@firebase/app-types': 0.9.6 '@firebase/util': 1.15.3 - '@firebase/auth@1.13.6(@firebase/app@0.16.2)': + '@firebase/auth@1.13.7(@firebase/app@0.16.3)': dependencies: - '@firebase/app': 0.16.2 + '@firebase/app': 0.16.3 '@firebase/component': 0.7.5 '@firebase/logger': 0.5.2 '@firebase/util': 1.15.3 @@ -10036,16 +10040,16 @@ snapshots: '@firebase/util': 1.15.3 tslib: 2.8.1 - '@firebase/data-connect@0.7.4(@firebase/app@0.16.2)': + '@firebase/data-connect@0.7.4(@firebase/app@0.16.3)': dependencies: - '@firebase/app': 0.16.2 + '@firebase/app': 0.16.3 '@firebase/auth-interop-types': 0.2.6 '@firebase/component': 0.7.5 '@firebase/logger': 0.5.2 '@firebase/util': 1.15.3 tslib: 2.8.1 - '@firebase/database-compat@2.1.7(@firebase/app-compat@0.5.18)(@firebase/app@0.16.2)': + '@firebase/database-compat@2.1.7(@firebase/app-compat@0.5.19)(@firebase/app@0.16.3)': dependencies: '@firebase/component': 0.7.5 '@firebase/database': 1.1.5 @@ -10054,8 +10058,8 @@ snapshots: '@firebase/util': 1.15.3 tslib: 2.8.1 optionalDependencies: - '@firebase/app': 0.16.2 - '@firebase/app-compat': 0.5.18 + '@firebase/app': 0.16.3 + '@firebase/app-compat': 0.5.19 '@firebase/database-types@1.0.22': dependencies: @@ -10072,12 +10076,12 @@ snapshots: faye-websocket: 0.11.4 tslib: 2.8.1 - '@firebase/firestore-compat@0.4.14(@firebase/app-compat@0.5.18)(@firebase/app-types@0.9.6)(@firebase/app@0.16.2)': + '@firebase/firestore-compat@0.4.15(@firebase/app-compat@0.5.19)(@firebase/app-types@0.9.6)(@firebase/app@0.16.3)': dependencies: - '@firebase/app': 0.16.2 - '@firebase/app-compat': 0.5.18 + '@firebase/app': 0.16.3 + '@firebase/app-compat': 0.5.19 '@firebase/component': 0.7.5 - '@firebase/firestore': 4.17.2(@firebase/app@0.16.2) + '@firebase/firestore': 4.18.0(@firebase/app@0.16.3) '@firebase/firestore-types': 3.0.5(@firebase/app-types@0.9.6)(@firebase/util@1.15.3) '@firebase/util': 1.15.3 tslib: 2.8.1 @@ -10089,9 +10093,9 @@ snapshots: '@firebase/app-types': 0.9.6 '@firebase/util': 1.15.3 - '@firebase/firestore@4.17.2(@firebase/app@0.16.2)': + '@firebase/firestore@4.18.0(@firebase/app@0.16.3)': dependencies: - '@firebase/app': 0.16.2 + '@firebase/app': 0.16.3 '@firebase/component': 0.7.5 '@firebase/logger': 0.5.2 '@firebase/util': 1.15.3 @@ -10101,21 +10105,21 @@ snapshots: re2js: 2.8.6 tslib: 2.8.1 - '@firebase/functions-compat@0.5.0(@firebase/app-compat@0.5.18)(@firebase/app@0.16.2)': + '@firebase/functions-compat@0.5.0(@firebase/app-compat@0.5.19)(@firebase/app@0.16.3)': dependencies: - '@firebase/app': 0.16.2 - '@firebase/app-compat': 0.5.18 + '@firebase/app': 0.16.3 + '@firebase/app-compat': 0.5.19 '@firebase/component': 0.7.5 - '@firebase/functions': 0.14.0(@firebase/app@0.16.2) + '@firebase/functions': 0.14.0(@firebase/app@0.16.3) '@firebase/functions-types': 0.6.5 '@firebase/util': 1.15.3 tslib: 2.8.1 '@firebase/functions-types@0.6.5': {} - '@firebase/functions@0.14.0(@firebase/app@0.16.2)': + '@firebase/functions@0.14.0(@firebase/app@0.16.3)': dependencies: - '@firebase/app': 0.16.2 + '@firebase/app': 0.16.3 '@firebase/app-check-interop-types': 0.3.5 '@firebase/auth-interop-types': 0.2.6 '@firebase/component': 0.7.5 @@ -10123,12 +10127,12 @@ snapshots: '@firebase/util': 1.15.3 tslib: 2.8.1 - '@firebase/installations-compat@0.2.24(@firebase/app-compat@0.5.18)(@firebase/app-types@0.9.6)(@firebase/app@0.16.2)': + '@firebase/installations-compat@0.2.24(@firebase/app-compat@0.5.19)(@firebase/app-types@0.9.6)(@firebase/app@0.16.3)': dependencies: - '@firebase/app': 0.16.2 - '@firebase/app-compat': 0.5.18 + '@firebase/app': 0.16.3 + '@firebase/app-compat': 0.5.19 '@firebase/component': 0.7.5 - '@firebase/installations': 0.6.24(@firebase/app@0.16.2) + '@firebase/installations': 0.6.24(@firebase/app@0.16.3) '@firebase/installations-types': 0.5.5(@firebase/app-types@0.9.6) '@firebase/util': 1.15.3 tslib: 2.8.1 @@ -10139,9 +10143,9 @@ snapshots: dependencies: '@firebase/app-types': 0.9.6 - '@firebase/installations@0.6.24(@firebase/app@0.16.2)': + '@firebase/installations@0.6.24(@firebase/app@0.16.3)': dependencies: - '@firebase/app': 0.16.2 + '@firebase/app': 0.16.3 '@firebase/component': 0.7.5 '@firebase/util': 1.15.3 idb: 7.1.1 @@ -10151,78 +10155,78 @@ snapshots: dependencies: tslib: 2.8.1 - '@firebase/messaging-compat@0.2.30(@firebase/app-compat@0.5.18)(@firebase/app@0.16.2)': + '@firebase/messaging-compat@0.2.30(@firebase/app-compat@0.5.19)(@firebase/app@0.16.3)': dependencies: - '@firebase/app': 0.16.2 - '@firebase/app-compat': 0.5.18 + '@firebase/app': 0.16.3 + '@firebase/app-compat': 0.5.19 '@firebase/component': 0.7.5 - '@firebase/messaging': 0.13.3(@firebase/app@0.16.2) + '@firebase/messaging': 0.13.3(@firebase/app@0.16.3) '@firebase/util': 1.15.3 tslib: 2.8.1 '@firebase/messaging-interop-types@0.2.6': {} - '@firebase/messaging@0.13.3(@firebase/app@0.16.2)': + '@firebase/messaging@0.13.3(@firebase/app@0.16.3)': dependencies: - '@firebase/app': 0.16.2 + '@firebase/app': 0.16.3 '@firebase/component': 0.7.5 - '@firebase/installations': 0.6.24(@firebase/app@0.16.2) + '@firebase/installations': 0.6.24(@firebase/app@0.16.3) '@firebase/messaging-interop-types': 0.2.6 '@firebase/util': 1.15.3 idb: 7.1.1 tslib: 2.8.1 - '@firebase/performance-compat@0.2.27(@firebase/app-compat@0.5.18)(@firebase/app@0.16.2)': + '@firebase/performance-compat@0.2.27(@firebase/app-compat@0.5.19)(@firebase/app@0.16.3)': dependencies: - '@firebase/app': 0.16.2 - '@firebase/app-compat': 0.5.18 + '@firebase/app': 0.16.3 + '@firebase/app-compat': 0.5.19 '@firebase/component': 0.7.5 '@firebase/logger': 0.5.2 - '@firebase/performance': 0.7.14(@firebase/app@0.16.2) + '@firebase/performance': 0.7.14(@firebase/app@0.16.3) '@firebase/performance-types': 0.2.5 '@firebase/util': 1.15.3 tslib: 2.8.1 '@firebase/performance-types@0.2.5': {} - '@firebase/performance@0.7.14(@firebase/app@0.16.2)': + '@firebase/performance@0.7.14(@firebase/app@0.16.3)': dependencies: - '@firebase/app': 0.16.2 + '@firebase/app': 0.16.3 '@firebase/component': 0.7.5 - '@firebase/installations': 0.6.24(@firebase/app@0.16.2) + '@firebase/installations': 0.6.24(@firebase/app@0.16.3) '@firebase/logger': 0.5.2 '@firebase/util': 1.15.3 tslib: 2.8.1 web-vitals: 4.2.4 - '@firebase/remote-config-compat@0.2.29(@firebase/app-compat@0.5.18)(@firebase/app@0.16.2)': + '@firebase/remote-config-compat@0.2.30(@firebase/app-compat@0.5.19)(@firebase/app@0.16.3)': dependencies: - '@firebase/app': 0.16.2 - '@firebase/app-compat': 0.5.18 + '@firebase/app': 0.16.3 + '@firebase/app-compat': 0.5.19 '@firebase/component': 0.7.5 '@firebase/logger': 0.5.2 - '@firebase/remote-config': 0.9.2(@firebase/app@0.16.2) + '@firebase/remote-config': 0.9.3(@firebase/app@0.16.3) '@firebase/remote-config-types': 0.5.2 '@firebase/util': 1.15.3 tslib: 2.8.1 '@firebase/remote-config-types@0.5.2': {} - '@firebase/remote-config@0.9.2(@firebase/app@0.16.2)': + '@firebase/remote-config@0.9.3(@firebase/app@0.16.3)': dependencies: - '@firebase/app': 0.16.2 + '@firebase/app': 0.16.3 '@firebase/component': 0.7.5 - '@firebase/installations': 0.6.24(@firebase/app@0.16.2) + '@firebase/installations': 0.6.24(@firebase/app@0.16.3) '@firebase/logger': 0.5.2 '@firebase/util': 1.15.3 tslib: 2.8.1 - '@firebase/storage-compat@0.4.5(@firebase/app-compat@0.5.18)(@firebase/app-types@0.9.6)(@firebase/app@0.16.2)': + '@firebase/storage-compat@0.4.6(@firebase/app-compat@0.5.19)(@firebase/app-types@0.9.6)(@firebase/app@0.16.3)': dependencies: - '@firebase/app': 0.16.2 - '@firebase/app-compat': 0.5.18 + '@firebase/app': 0.16.3 + '@firebase/app-compat': 0.5.19 '@firebase/component': 0.7.5 - '@firebase/storage': 0.14.5(@firebase/app@0.16.2) + '@firebase/storage': 0.14.6(@firebase/app@0.16.3) '@firebase/storage-types': 0.8.5(@firebase/app-types@0.9.6)(@firebase/util@1.15.3) '@firebase/util': 1.15.3 tslib: 2.8.1 @@ -10234,9 +10238,9 @@ snapshots: '@firebase/app-types': 0.9.6 '@firebase/util': 1.15.3 - '@firebase/storage@0.14.5(@firebase/app@0.16.2)': + '@firebase/storage@0.14.6(@firebase/app@0.16.3)': dependencies: - '@firebase/app': 0.16.2 + '@firebase/app': 0.16.3 '@firebase/component': 0.7.5 '@firebase/util': 1.15.3 tslib: 2.8.1 @@ -10281,8 +10285,8 @@ snapshots: '@google-cloud/promisify': 5.1.0 '@grpc/proto-loader': 0.7.15 '@opentelemetry/api': 1.9.1 - '@opentelemetry/context-async-hooks': 2.11.0(@opentelemetry/api@1.9.1) - '@opentelemetry/core': 2.11.0(@opentelemetry/api@1.9.1) + '@opentelemetry/context-async-hooks': 2.12.0(@opentelemetry/api@1.9.1) + '@opentelemetry/core': 2.12.0(@opentelemetry/api@1.9.1) '@opentelemetry/semantic-conventions': 1.43.0 '@types/big.js': 6.2.2 '@types/stack-trace': 0.0.33 @@ -10308,7 +10312,7 @@ snapshots: transitivePeerDependencies: - supports-color - '@google/genai@2.25.0(bufferutil@4.1.0)(supports-color@11.0.0)(utf-8-validate@6.0.6)': + '@google/genai@2.29.1(bufferutil@4.1.0)(supports-color@11.0.0)(utf-8-validate@6.0.6)': dependencies: google-auth-library: 10.9.1(supports-color@11.0.0) p-retry: 4.6.2 @@ -10319,7 +10323,7 @@ snapshots: - supports-color - utf-8-validate - '@grpc/grpc-js@1.14.5': + '@grpc/grpc-js@1.14.6': dependencies: '@grpc/proto-loader': 0.8.1 '@js-sdsl/ordered-map': 4.4.2 @@ -10373,21 +10377,14 @@ snapshots: optionalDependencies: '@types/node': 22.20.5 - '@inquirer/checkbox@5.2.6(@types/node@24.19.0)': + '@inquirer/checkbox@5.2.6(@types/node@24.19.2)': dependencies: '@inquirer/ansi': 2.0.9 - '@inquirer/core': 12.0.4(@types/node@24.19.0) + '@inquirer/core': 12.0.4(@types/node@24.19.2) '@inquirer/figures': 2.0.9 - '@inquirer/type': 4.2.0(@types/node@24.19.0) + '@inquirer/type': 4.2.0(@types/node@24.19.2) optionalDependencies: - '@types/node': 24.19.0 - - '@inquirer/confirm@6.3.2(@types/node@24.19.0)': - dependencies: - '@inquirer/core': 12.0.4(@types/node@24.19.0) - '@inquirer/type': 4.1.1(@types/node@24.19.0) - optionalDependencies: - '@types/node': 24.19.0 + '@types/node': 24.19.2 '@inquirer/confirm@6.3.3(@types/node@22.20.5)': dependencies: @@ -10396,6 +10393,13 @@ snapshots: optionalDependencies: '@types/node': 22.20.5 + '@inquirer/confirm@6.3.3(@types/node@24.19.2)': + dependencies: + '@inquirer/core': 12.0.4(@types/node@24.19.2) + '@inquirer/type': 4.2.0(@types/node@24.19.2) + optionalDependencies: + '@types/node': 24.19.2 + '@inquirer/core@12.0.4(@types/node@22.20.5)': dependencies: '@inquirer/ansi': 2.0.9 @@ -10408,17 +10412,17 @@ snapshots: optionalDependencies: '@types/node': 22.20.5 - '@inquirer/core@12.0.4(@types/node@24.19.0)': + '@inquirer/core@12.0.4(@types/node@24.19.2)': dependencies: '@inquirer/ansi': 2.0.9 '@inquirer/figures': 2.0.9 - '@inquirer/type': 4.2.0(@types/node@24.19.0) + '@inquirer/type': 4.2.0(@types/node@24.19.2) cli-width: 4.1.0 fast-wrap-ansi: 0.2.2 mute-stream: 3.0.0 signal-exit: 4.1.0 optionalDependencies: - '@types/node': 24.19.0 + '@types/node': 24.19.2 '@inquirer/editor@5.3.4(@types/node@22.20.5)': dependencies: @@ -10428,13 +10432,13 @@ snapshots: optionalDependencies: '@types/node': 22.20.5 - '@inquirer/editor@5.3.4(@types/node@24.19.0)': + '@inquirer/editor@5.3.4(@types/node@24.19.2)': dependencies: - '@inquirer/core': 12.0.4(@types/node@24.19.0) - '@inquirer/external-editor': 3.0.6(@types/node@24.19.0) - '@inquirer/type': 4.2.0(@types/node@24.19.0) + '@inquirer/core': 12.0.4(@types/node@24.19.2) + '@inquirer/external-editor': 3.0.6(@types/node@24.19.2) + '@inquirer/type': 4.2.0(@types/node@24.19.2) optionalDependencies: - '@types/node': 24.19.0 + '@types/node': 24.19.2 '@inquirer/expand@5.1.6(@types/node@22.20.5)': dependencies: @@ -10443,12 +10447,12 @@ snapshots: optionalDependencies: '@types/node': 22.20.5 - '@inquirer/expand@5.1.6(@types/node@24.19.0)': + '@inquirer/expand@5.1.6(@types/node@24.19.2)': dependencies: - '@inquirer/core': 12.0.4(@types/node@24.19.0) - '@inquirer/type': 4.2.0(@types/node@24.19.0) + '@inquirer/core': 12.0.4(@types/node@24.19.2) + '@inquirer/type': 4.2.0(@types/node@24.19.2) optionalDependencies: - '@types/node': 24.19.0 + '@types/node': 24.19.2 '@inquirer/external-editor@3.0.6(@types/node@22.20.5)': dependencies: @@ -10457,12 +10461,12 @@ snapshots: optionalDependencies: '@types/node': 22.20.5 - '@inquirer/external-editor@3.0.6(@types/node@24.19.0)': + '@inquirer/external-editor@3.0.6(@types/node@24.19.2)': dependencies: chardet: 2.2.0 iconv-lite: 0.7.3 optionalDependencies: - '@types/node': 24.19.0 + '@types/node': 24.19.2 '@inquirer/figures@2.0.9': {} @@ -10473,12 +10477,12 @@ snapshots: optionalDependencies: '@types/node': 22.20.5 - '@inquirer/input@5.1.7(@types/node@24.19.0)': + '@inquirer/input@5.1.7(@types/node@24.19.2)': dependencies: - '@inquirer/core': 12.0.4(@types/node@24.19.0) - '@inquirer/type': 4.2.0(@types/node@24.19.0) + '@inquirer/core': 12.0.4(@types/node@24.19.2) + '@inquirer/type': 4.2.0(@types/node@24.19.2) optionalDependencies: - '@types/node': 24.19.0 + '@types/node': 24.19.2 '@inquirer/number@4.2.4(@types/node@22.20.5)': dependencies: @@ -10487,12 +10491,12 @@ snapshots: optionalDependencies: '@types/node': 22.20.5 - '@inquirer/number@4.2.4(@types/node@24.19.0)': + '@inquirer/number@4.2.4(@types/node@24.19.2)': dependencies: - '@inquirer/core': 12.0.4(@types/node@24.19.0) - '@inquirer/type': 4.2.0(@types/node@24.19.0) + '@inquirer/core': 12.0.4(@types/node@24.19.2) + '@inquirer/type': 4.2.0(@types/node@24.19.2) optionalDependencies: - '@types/node': 24.19.0 + '@types/node': 24.19.2 '@inquirer/password@5.2.3(@types/node@22.20.5)': dependencies: @@ -10502,28 +10506,13 @@ snapshots: optionalDependencies: '@types/node': 22.20.5 - '@inquirer/password@5.2.3(@types/node@24.19.0)': + '@inquirer/password@5.2.3(@types/node@24.19.2)': dependencies: '@inquirer/ansi': 2.0.9 - '@inquirer/core': 12.0.4(@types/node@24.19.0) - '@inquirer/type': 4.2.0(@types/node@24.19.0) + '@inquirer/core': 12.0.4(@types/node@24.19.2) + '@inquirer/type': 4.2.0(@types/node@24.19.2) optionalDependencies: - '@types/node': 24.19.0 - - '@inquirer/prompts@8.7.2(@types/node@24.19.0)': - dependencies: - '@inquirer/checkbox': 5.2.6(@types/node@24.19.0) - '@inquirer/confirm': 6.3.2(@types/node@24.19.0) - '@inquirer/editor': 5.3.4(@types/node@24.19.0) - '@inquirer/expand': 5.1.6(@types/node@24.19.0) - '@inquirer/input': 5.1.7(@types/node@24.19.0) - '@inquirer/number': 4.2.4(@types/node@24.19.0) - '@inquirer/password': 5.2.3(@types/node@24.19.0) - '@inquirer/rawlist': 5.3.6(@types/node@24.19.0) - '@inquirer/search': 4.3.4(@types/node@24.19.0) - '@inquirer/select': 5.2.6(@types/node@24.19.0) - optionalDependencies: - '@types/node': 24.19.0 + '@types/node': 24.19.2 '@inquirer/prompts@8.7.3(@types/node@22.20.5)': dependencies: @@ -10540,6 +10529,21 @@ snapshots: optionalDependencies: '@types/node': 22.20.5 + '@inquirer/prompts@8.7.3(@types/node@24.19.2)': + dependencies: + '@inquirer/checkbox': 5.2.6(@types/node@24.19.2) + '@inquirer/confirm': 6.3.3(@types/node@24.19.2) + '@inquirer/editor': 5.3.4(@types/node@24.19.2) + '@inquirer/expand': 5.1.6(@types/node@24.19.2) + '@inquirer/input': 5.1.7(@types/node@24.19.2) + '@inquirer/number': 4.2.4(@types/node@24.19.2) + '@inquirer/password': 5.2.3(@types/node@24.19.2) + '@inquirer/rawlist': 5.3.6(@types/node@24.19.2) + '@inquirer/search': 4.3.4(@types/node@24.19.2) + '@inquirer/select': 5.2.6(@types/node@24.19.2) + optionalDependencies: + '@types/node': 24.19.2 + '@inquirer/rawlist@5.3.6(@types/node@22.20.5)': dependencies: '@inquirer/core': 12.0.4(@types/node@22.20.5) @@ -10547,12 +10551,12 @@ snapshots: optionalDependencies: '@types/node': 22.20.5 - '@inquirer/rawlist@5.3.6(@types/node@24.19.0)': + '@inquirer/rawlist@5.3.6(@types/node@24.19.2)': dependencies: - '@inquirer/core': 12.0.4(@types/node@24.19.0) - '@inquirer/type': 4.2.0(@types/node@24.19.0) + '@inquirer/core': 12.0.4(@types/node@24.19.2) + '@inquirer/type': 4.2.0(@types/node@24.19.2) optionalDependencies: - '@types/node': 24.19.0 + '@types/node': 24.19.2 '@inquirer/search@4.3.4(@types/node@22.20.5)': dependencies: @@ -10562,13 +10566,13 @@ snapshots: optionalDependencies: '@types/node': 22.20.5 - '@inquirer/search@4.3.4(@types/node@24.19.0)': + '@inquirer/search@4.3.4(@types/node@24.19.2)': dependencies: - '@inquirer/core': 12.0.4(@types/node@24.19.0) + '@inquirer/core': 12.0.4(@types/node@24.19.2) '@inquirer/figures': 2.0.9 - '@inquirer/type': 4.2.0(@types/node@24.19.0) + '@inquirer/type': 4.2.0(@types/node@24.19.2) optionalDependencies: - '@types/node': 24.19.0 + '@types/node': 24.19.2 '@inquirer/select@5.2.6(@types/node@22.20.5)': dependencies: @@ -10579,26 +10583,22 @@ snapshots: optionalDependencies: '@types/node': 22.20.5 - '@inquirer/select@5.2.6(@types/node@24.19.0)': + '@inquirer/select@5.2.6(@types/node@24.19.2)': dependencies: '@inquirer/ansi': 2.0.9 - '@inquirer/core': 12.0.4(@types/node@24.19.0) + '@inquirer/core': 12.0.4(@types/node@24.19.2) '@inquirer/figures': 2.0.9 - '@inquirer/type': 4.2.0(@types/node@24.19.0) - optionalDependencies: - '@types/node': 24.19.0 - - '@inquirer/type@4.1.1(@types/node@24.19.0)': + '@inquirer/type': 4.2.0(@types/node@24.19.2) optionalDependencies: - '@types/node': 24.19.0 + '@types/node': 24.19.2 '@inquirer/type@4.2.0(@types/node@22.20.5)': optionalDependencies: '@types/node': 22.20.5 - '@inquirer/type@4.2.0(@types/node@24.19.0)': + '@inquirer/type@4.2.0(@types/node@24.19.2)': optionalDependencies: - '@types/node': 24.19.0 + '@types/node': 24.19.2 '@isaacs/cliui@8.0.2': dependencies: @@ -10782,11 +10782,11 @@ snapshots: '@leichtgewicht/ip-codec@2.0.5': {} - '@listr2/prompt-adapter-inquirer@4.2.7(@inquirer/prompts@8.7.3(@types/node@22.20.5))(@types/node@22.20.5)(listr2@11.1.1)': + '@listr2/prompt-adapter-inquirer@4.3.1(@inquirer/prompts@8.7.3(@types/node@22.20.5))(@types/node@22.20.5)(listr2@11.2.0)': dependencies: '@inquirer/prompts': 8.7.3(@types/node@22.20.5) '@inquirer/type': 4.2.0(@types/node@22.20.5) - listr2: 11.1.1 + listr2: 11.2.0 transitivePeerDependencies: - '@types/node' @@ -10813,12 +10813,12 @@ snapshots: '@modelcontextprotocol/core@2.3.1': dependencies: - zod: 4.6.5 + zod: 4.6.7 '@modelcontextprotocol/server@2.3.1': dependencies: '@modelcontextprotocol/core': 2.3.1 - zod: 4.6.5 + zod: 4.6.7 '@msgpackr-extract/msgpackr-extract-darwin-arm64@3.0.4': optional: true @@ -10838,14 +10838,16 @@ snapshots: '@msgpackr-extract/msgpackr-extract-win32-x64@3.0.4': optional: true - '@mswjs/interceptors@0.41.9': + '@mswjs/interceptors@0.44.0(supports-color@11.0.0)': dependencies: - '@open-draft/deferred-promise': 2.2.0 - '@open-draft/logger': 0.3.0 - '@open-draft/until': 2.1.0 + '@open-draft/until': 3.0.1 + '@types/debug': 4.1.13 + debug: 4.4.3(supports-color@11.0.0) is-node-process: 1.2.0 outvariant: 1.4.3 - strict-event-emitter: 0.5.1 + rettime: 0.11.12 + transitivePeerDependencies: + - supports-color '@napi-rs/lzma-linux-x64-gnu@1.5.1': optional: true @@ -11018,11 +11020,6 @@ snapshots: '@octokit/core': 7.0.8 '@octokit/types': 16.0.0 - '@octokit/plugin-paginate-rest@15.0.0(@octokit/core@7.0.8)': - dependencies: - '@octokit/core': 7.0.8 - '@octokit/types': 17.0.0 - '@octokit/plugin-request-log@6.0.0(@octokit/core@7.0.8)': dependencies: '@octokit/core': 7.0.8 @@ -11069,90 +11066,81 @@ snapshots: dependencies: '@octokit/openapi-types': 29.0.1 - '@open-draft/deferred-promise@2.2.0': {} - - '@open-draft/logger@0.3.0': - dependencies: - is-node-process: 1.2.0 - outvariant: 1.4.3 - - '@open-draft/until@2.1.0': {} + '@open-draft/until@3.0.1': {} '@opentelemetry/api@1.9.1': {} - '@opentelemetry/context-async-hooks@2.11.0(@opentelemetry/api@1.9.1)': + '@opentelemetry/context-async-hooks@2.12.0(@opentelemetry/api@1.9.1)': dependencies: '@opentelemetry/api': 1.9.1 - '@opentelemetry/core@2.11.0(@opentelemetry/api@1.9.1)': + '@opentelemetry/core@2.12.0(@opentelemetry/api@1.9.1)': dependencies: '@opentelemetry/api': 1.9.1 '@opentelemetry/semantic-conventions': 1.43.0 '@opentelemetry/semantic-conventions@1.43.0': {} - '@oxc-parser/binding-android-arm-eabi@0.153.0': + '@oxc-parser/binding-android-arm-eabi@0.154.0': optional: true - '@oxc-parser/binding-android-arm64@0.153.0': + '@oxc-parser/binding-android-arm64@0.154.0': optional: true - '@oxc-parser/binding-darwin-arm64@0.153.0': + '@oxc-parser/binding-darwin-arm64@0.154.0': optional: true - '@oxc-parser/binding-darwin-x64@0.153.0': + '@oxc-parser/binding-darwin-x64@0.154.0': optional: true - '@oxc-parser/binding-freebsd-x64@0.153.0': + '@oxc-parser/binding-freebsd-x64@0.154.0': optional: true - '@oxc-parser/binding-linux-arm-gnueabihf@0.153.0': + '@oxc-parser/binding-linux-arm-gnueabihf@0.154.0': optional: true - '@oxc-parser/binding-linux-arm-musleabihf@0.153.0': + '@oxc-parser/binding-linux-arm-musleabihf@0.154.0': optional: true - '@oxc-parser/binding-linux-arm64-gnu@0.153.0': + '@oxc-parser/binding-linux-arm64-gnu@0.154.0': optional: true - '@oxc-parser/binding-linux-arm64-musl@0.153.0': + '@oxc-parser/binding-linux-arm64-musl@0.154.0': optional: true - '@oxc-parser/binding-linux-ppc64-gnu@0.153.0': + '@oxc-parser/binding-linux-ppc64-gnu@0.154.0': optional: true - '@oxc-parser/binding-linux-riscv64-gnu@0.153.0': + '@oxc-parser/binding-linux-riscv64-gnu@0.154.0': optional: true - '@oxc-parser/binding-linux-riscv64-musl@0.153.0': + '@oxc-parser/binding-linux-riscv64-musl@0.154.0': optional: true - '@oxc-parser/binding-linux-s390x-gnu@0.153.0': + '@oxc-parser/binding-linux-s390x-gnu@0.154.0': optional: true - '@oxc-parser/binding-linux-x64-gnu@0.153.0': + '@oxc-parser/binding-linux-x64-gnu@0.154.0': optional: true - '@oxc-parser/binding-linux-x64-musl@0.153.0': + '@oxc-parser/binding-linux-x64-musl@0.154.0': optional: true - '@oxc-parser/binding-openharmony-arm64@0.153.0': + '@oxc-parser/binding-openharmony-arm64@0.154.0': optional: true - '@oxc-parser/binding-win32-arm64-msvc@0.153.0': + '@oxc-parser/binding-win32-arm64-msvc@0.154.0': optional: true - '@oxc-parser/binding-win32-ia32-msvc@0.153.0': + '@oxc-parser/binding-win32-ia32-msvc@0.154.0': optional: true - '@oxc-parser/binding-win32-x64-msvc@0.153.0': + '@oxc-parser/binding-win32-x64-msvc@0.154.0': optional: true - '@oxc-project/types@0.151.0': {} - '@oxc-project/types@0.152.0': {} - '@oxc-project/types@0.153.0': {} + '@oxc-project/types@0.154.0': {} '@parcel/watcher-android-arm64@2.6.0': optional: true @@ -11329,106 +11317,106 @@ snapshots: '@protobufjs/utf8@1.1.2': {} - '@puppeteer/browsers@3.2.3': + '@puppeteer/browsers@3.2.5': dependencies: modern-tar: 0.8.5 yargs: 18.2.0 - '@rolldown/binding-android-arm-eabi@1.2.11': - optional: true - '@rolldown/binding-android-arm-eabi@1.2.12': optional: true - '@rolldown/binding-android-arm64@1.2.11': + '@rolldown/binding-android-arm-eabi@1.2.14': optional: true '@rolldown/binding-android-arm64@1.2.12': optional: true - '@rolldown/binding-darwin-arm64@1.2.11': + '@rolldown/binding-android-arm64@1.2.14': optional: true '@rolldown/binding-darwin-arm64@1.2.12': optional: true - '@rolldown/binding-darwin-x64@1.2.11': + '@rolldown/binding-darwin-arm64@1.2.14': optional: true '@rolldown/binding-darwin-x64@1.2.12': optional: true - '@rolldown/binding-freebsd-x64@1.2.11': + '@rolldown/binding-darwin-x64@1.2.14': optional: true '@rolldown/binding-freebsd-x64@1.2.12': optional: true - '@rolldown/binding-linux-arm-gnueabihf@1.2.11': + '@rolldown/binding-freebsd-x64@1.2.14': optional: true '@rolldown/binding-linux-arm-gnueabihf@1.2.12': optional: true - '@rolldown/binding-linux-arm64-gnu@1.2.11': + '@rolldown/binding-linux-arm-gnueabihf@1.2.14': optional: true '@rolldown/binding-linux-arm64-gnu@1.2.12': optional: true - '@rolldown/binding-linux-arm64-musl@1.2.11': + '@rolldown/binding-linux-arm64-gnu@1.2.14': optional: true '@rolldown/binding-linux-arm64-musl@1.2.12': optional: true - '@rolldown/binding-linux-ppc64-gnu@1.2.11': + '@rolldown/binding-linux-arm64-musl@1.2.14': optional: true '@rolldown/binding-linux-ppc64-gnu@1.2.12': optional: true - '@rolldown/binding-linux-s390x-gnu@1.2.11': + '@rolldown/binding-linux-ppc64-gnu@1.2.14': optional: true '@rolldown/binding-linux-s390x-gnu@1.2.12': optional: true - '@rolldown/binding-linux-x64-gnu@1.2.11': + '@rolldown/binding-linux-s390x-gnu@1.2.14': optional: true '@rolldown/binding-linux-x64-gnu@1.2.12': optional: true - '@rolldown/binding-linux-x64-musl@1.2.11': + '@rolldown/binding-linux-x64-gnu@1.2.14': optional: true '@rolldown/binding-linux-x64-musl@1.2.12': optional: true - '@rolldown/binding-openharmony-arm64@1.2.11': + '@rolldown/binding-linux-x64-musl@1.2.14': optional: true '@rolldown/binding-openharmony-arm64@1.2.12': optional: true - '@rolldown/binding-win32-arm64-msvc@1.2.11': + '@rolldown/binding-openharmony-arm64@1.2.14': optional: true '@rolldown/binding-win32-arm64-msvc@1.2.12': optional: true - '@rolldown/binding-win32-x64-msvc@1.2.11': + '@rolldown/binding-win32-arm64-msvc@1.2.14': optional: true '@rolldown/binding-win32-x64-msvc@1.2.12': optional: true + '@rolldown/binding-win32-x64-msvc@1.2.14': + optional: true + '@rolldown/pluginutils@1.0.1': {} - '@rollup/plugin-commonjs@29.0.3(rollup@4.64.1)': + '@rollup/plugin-commonjs@29.0.3(rollup@4.64.6)': dependencies: - '@rollup/pluginutils': 5.4.0(rollup@4.64.1) + '@rollup/pluginutils': 5.4.0(rollup@4.64.6) commondir: 1.0.1 estree-walker: 2.0.2 fdir: 6.5.0(picomatch@4.0.7) @@ -11436,108 +11424,108 @@ snapshots: magic-string: 0.30.21 picomatch: 4.0.7 optionalDependencies: - rollup: 4.64.1 + rollup: 4.64.6 - '@rollup/plugin-json@6.1.0(rollup@4.64.1)': + '@rollup/plugin-json@6.1.0(rollup@4.64.6)': dependencies: - '@rollup/pluginutils': 5.4.0(rollup@4.64.1) + '@rollup/pluginutils': 5.4.0(rollup@4.64.6) optionalDependencies: - rollup: 4.64.1 + rollup: 4.64.6 - '@rollup/plugin-node-resolve@16.0.3(rollup@4.64.1)': + '@rollup/plugin-node-resolve@16.0.3(rollup@4.64.6)': dependencies: - '@rollup/pluginutils': 5.4.0(rollup@4.64.1) + '@rollup/pluginutils': 5.4.0(rollup@4.64.6) '@types/resolve': 1.20.2 deepmerge: 4.3.1 is-module: 1.0.0 - resolve: 1.22.12 + resolve: 1.22.13 optionalDependencies: - rollup: 4.64.1 + rollup: 4.64.6 - '@rollup/pluginutils@5.4.0(rollup@4.64.1)': + '@rollup/pluginutils@5.4.0(rollup@4.64.6)': dependencies: '@types/estree': 1.0.9 estree-walker: 2.0.2 picomatch: 4.0.7 optionalDependencies: - rollup: 4.64.1 + rollup: 4.64.6 - '@rollup/rollup-android-arm-eabi@4.64.1': + '@rollup/rollup-android-arm-eabi@4.64.6': optional: true - '@rollup/rollup-android-arm64@4.64.1': + '@rollup/rollup-android-arm64@4.64.6': optional: true - '@rollup/rollup-darwin-arm64@4.64.1': + '@rollup/rollup-darwin-arm64@4.64.6': optional: true - '@rollup/rollup-darwin-x64@4.64.1': + '@rollup/rollup-darwin-x64@4.64.6': optional: true - '@rollup/rollup-freebsd-arm64@4.64.1': + '@rollup/rollup-freebsd-arm64@4.64.6': optional: true - '@rollup/rollup-freebsd-x64@4.64.1': + '@rollup/rollup-freebsd-x64@4.64.6': optional: true - '@rollup/rollup-linux-arm-gnueabihf@4.64.1': + '@rollup/rollup-linux-arm-gnueabihf@4.64.6': optional: true - '@rollup/rollup-linux-arm-musleabihf@4.64.1': + '@rollup/rollup-linux-arm-musleabihf@4.64.6': optional: true - '@rollup/rollup-linux-arm64-gnu@4.64.1': + '@rollup/rollup-linux-arm64-gnu@4.64.6': optional: true - '@rollup/rollup-linux-arm64-musl@4.64.1': + '@rollup/rollup-linux-arm64-musl@4.64.6': optional: true - '@rollup/rollup-linux-loong64-gnu@4.64.1': + '@rollup/rollup-linux-loong64-gnu@4.64.6': optional: true - '@rollup/rollup-linux-loong64-musl@4.64.1': + '@rollup/rollup-linux-loong64-musl@4.64.6': optional: true - '@rollup/rollup-linux-ppc64-gnu@4.64.1': + '@rollup/rollup-linux-ppc64-gnu@4.64.6': optional: true - '@rollup/rollup-linux-ppc64-musl@4.64.1': + '@rollup/rollup-linux-ppc64-musl@4.64.6': optional: true - '@rollup/rollup-linux-riscv64-gnu@4.64.1': + '@rollup/rollup-linux-riscv64-gnu@4.64.6': optional: true - '@rollup/rollup-linux-riscv64-musl@4.64.1': + '@rollup/rollup-linux-riscv64-musl@4.64.6': optional: true - '@rollup/rollup-linux-s390x-gnu@4.64.1': + '@rollup/rollup-linux-s390x-gnu@4.64.6': optional: true - '@rollup/rollup-linux-x64-gnu@4.64.1': + '@rollup/rollup-linux-x64-gnu@4.64.6': optional: true - '@rollup/rollup-linux-x64-musl@4.64.1': + '@rollup/rollup-linux-x64-musl@4.64.6': optional: true - '@rollup/rollup-openbsd-x64@4.64.1': + '@rollup/rollup-openbsd-x64@4.64.6': optional: true - '@rollup/rollup-openharmony-arm64@4.64.1': + '@rollup/rollup-openharmony-arm64@4.64.6': optional: true - '@rollup/rollup-win32-arm64-msvc@4.64.1': + '@rollup/rollup-win32-arm64-msvc@4.64.6': optional: true - '@rollup/rollup-win32-ia32-msvc@4.64.1': + '@rollup/rollup-win32-ia32-msvc@4.64.6': optional: true - '@rollup/rollup-win32-x64-gnu@4.64.1': + '@rollup/rollup-win32-x64-gnu@4.64.6': optional: true - '@rollup/rollup-win32-x64-msvc@4.64.1': + '@rollup/rollup-win32-x64-msvc@4.64.6': optional: true - '@rollup/wasm-node@4.64.1': + '@rollup/wasm-node@4.64.6': dependencies: '@types/estree': 1.0.9 optionalDependencies: @@ -11563,7 +11551,7 @@ snapshots: '@stylistic/eslint-plugin@5.10.0(eslint@10.12.0(jiti@2.7.0)(supports-color@11.0.0))': dependencies: '@eslint-community/eslint-utils': 4.10.1(eslint@10.12.0(jiti@2.7.0)(supports-color@11.0.0)) - '@typescript-eslint/types': 8.71.0 + '@typescript-eslint/types': 8.71.1 eslint: 10.12.0(jiti@2.7.0)(supports-color@11.0.0) eslint-visitor-keys: 4.2.1 espree: 10.4.0 @@ -11635,6 +11623,10 @@ snapshots: dependencies: '@types/node': 22.20.5 + '@types/debug@4.1.13': + dependencies: + '@types/ms': 2.1.0 + '@types/deep-eql@4.0.2': {} '@types/duplexify@3.6.5': @@ -11698,7 +11690,7 @@ snapshots: '@types/loader-utils@3.0.0(esbuild@0.28.2)(lightningcss@1.33.0)(uglify-js@3.19.3)': dependencies: '@types/node': 22.20.5 - webpack: 5.111.1(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.29)(uglify-js@3.19.3) + webpack: 5.111.1(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.30)(uglify-js@3.19.3) transitivePeerDependencies: - '@minify-html/node' - '@napi-rs/image' @@ -11718,17 +11710,19 @@ snapshots: - uglify-js - webpack-cli - '@types/lodash@4.17.25': {} + '@types/lodash@4.17.26': {} '@types/micromatch@2.3.35': dependencies: '@types/parse-glob': 3.0.32 + '@types/ms@2.1.0': {} + '@types/node@22.20.5': dependencies: undici-types: 6.21.0 - '@types/node@24.19.0': + '@types/node@24.19.2': dependencies: undici-types: 7.24.6 @@ -11797,14 +11791,14 @@ snapshots: '@types/yarnpkg__lockfile@1.1.9': {} - '@typescript-eslint/eslint-plugin@8.71.1(@typescript-eslint/parser@8.71.1(eslint@10.12.0(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0)(typescript@6.0.3))(eslint@10.12.0(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0)(typescript@6.0.3)': + '@typescript-eslint/eslint-plugin@8.71.2(@typescript-eslint/parser@8.71.2(eslint@10.12.0(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0)(typescript@6.0.3))(eslint@10.12.0(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0)(typescript@6.0.3)': dependencies: '@eslint-community/regexpp': 4.12.2 - '@typescript-eslint/parser': 8.71.1(eslint@10.12.0(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0)(typescript@6.0.3) - '@typescript-eslint/scope-manager': 8.71.1 - '@typescript-eslint/type-utils': 8.71.1(eslint@10.12.0(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0)(typescript@6.0.3) - '@typescript-eslint/utils': 8.71.1(eslint@10.12.0(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0)(typescript@6.0.3) - '@typescript-eslint/visitor-keys': 8.71.1 + '@typescript-eslint/parser': 8.71.2(eslint@10.12.0(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0)(typescript@6.0.3) + '@typescript-eslint/scope-manager': 8.71.2 + '@typescript-eslint/type-utils': 8.71.2(eslint@10.12.0(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0)(typescript@6.0.3) + '@typescript-eslint/utils': 8.71.2(eslint@10.12.0(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0)(typescript@6.0.3) + '@typescript-eslint/visitor-keys': 8.71.2 eslint: 10.12.0(jiti@2.7.0)(supports-color@11.0.0) ignore: 7.0.12 natural-compare: 1.4.0 @@ -11813,41 +11807,41 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@8.71.1(eslint@10.12.0(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0)(typescript@6.0.3)': + '@typescript-eslint/parser@8.71.2(eslint@10.12.0(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0)(typescript@6.0.3)': dependencies: - '@typescript-eslint/scope-manager': 8.71.1 - '@typescript-eslint/types': 8.71.1 - '@typescript-eslint/typescript-estree': 8.71.1(supports-color@11.0.0)(typescript@6.0.3) - '@typescript-eslint/visitor-keys': 8.71.1 + '@typescript-eslint/scope-manager': 8.71.2 + '@typescript-eslint/types': 8.71.2 + '@typescript-eslint/typescript-estree': 8.71.2(supports-color@11.0.0)(typescript@6.0.3) + '@typescript-eslint/visitor-keys': 8.71.2 debug: 4.4.3(supports-color@11.0.0) eslint: 10.12.0(jiti@2.7.0)(supports-color@11.0.0) typescript: 6.0.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/project-service@8.71.1(supports-color@11.0.0)(typescript@6.0.3)': + '@typescript-eslint/project-service@8.71.2(supports-color@11.0.0)(typescript@6.0.3)': dependencies: - '@typescript-eslint/tsconfig-utils': 8.71.1(typescript@6.0.3) - '@typescript-eslint/types': 8.71.1 + '@typescript-eslint/tsconfig-utils': 8.71.2(typescript@6.0.3) + '@typescript-eslint/types': 8.71.2 debug: 4.4.3(supports-color@11.0.0) typescript: 6.0.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/scope-manager@8.71.1': + '@typescript-eslint/scope-manager@8.71.2': dependencies: - '@typescript-eslint/types': 8.71.1 - '@typescript-eslint/visitor-keys': 8.71.1 + '@typescript-eslint/types': 8.71.2 + '@typescript-eslint/visitor-keys': 8.71.2 - '@typescript-eslint/tsconfig-utils@8.71.1(typescript@6.0.3)': + '@typescript-eslint/tsconfig-utils@8.71.2(typescript@6.0.3)': dependencies: typescript: 6.0.3 - '@typescript-eslint/type-utils@8.71.1(eslint@10.12.0(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0)(typescript@6.0.3)': + '@typescript-eslint/type-utils@8.71.2(eslint@10.12.0(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0)(typescript@6.0.3)': dependencies: - '@typescript-eslint/types': 8.71.1 - '@typescript-eslint/typescript-estree': 8.71.1(supports-color@11.0.0)(typescript@6.0.3) - '@typescript-eslint/utils': 8.71.1(eslint@10.12.0(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0)(typescript@6.0.3) + '@typescript-eslint/types': 8.71.2 + '@typescript-eslint/typescript-estree': 8.71.2(supports-color@11.0.0)(typescript@6.0.3) + '@typescript-eslint/utils': 8.71.2(eslint@10.12.0(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0)(typescript@6.0.3) debug: 4.4.3(supports-color@11.0.0) eslint: 10.12.0(jiti@2.7.0)(supports-color@11.0.0) ts-api-utils: 2.5.0(typescript@6.0.3) @@ -11855,16 +11849,16 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/types@8.71.0': {} - '@typescript-eslint/types@8.71.1': {} - '@typescript-eslint/typescript-estree@8.71.1(supports-color@11.0.0)(typescript@6.0.3)': + '@typescript-eslint/types@8.71.2': {} + + '@typescript-eslint/typescript-estree@8.71.2(supports-color@11.0.0)(typescript@6.0.3)': dependencies: - '@typescript-eslint/project-service': 8.71.1(supports-color@11.0.0)(typescript@6.0.3) - '@typescript-eslint/tsconfig-utils': 8.71.1(typescript@6.0.3) - '@typescript-eslint/types': 8.71.1 - '@typescript-eslint/visitor-keys': 8.71.1 + '@typescript-eslint/project-service': 8.71.2(supports-color@11.0.0)(typescript@6.0.3) + '@typescript-eslint/tsconfig-utils': 8.71.2(typescript@6.0.3) + '@typescript-eslint/types': 8.71.2 + '@typescript-eslint/visitor-keys': 8.71.2 debug: 4.4.3(supports-color@11.0.0) minimatch: 10.2.6 semver: 7.8.5 @@ -11874,20 +11868,20 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@8.71.1(eslint@10.12.0(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0)(typescript@6.0.3)': + '@typescript-eslint/utils@8.71.2(eslint@10.12.0(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0)(typescript@6.0.3)': dependencies: '@eslint-community/eslint-utils': 4.10.1(eslint@10.12.0(jiti@2.7.0)(supports-color@11.0.0)) - '@typescript-eslint/scope-manager': 8.71.1 - '@typescript-eslint/types': 8.71.1 - '@typescript-eslint/typescript-estree': 8.71.1(supports-color@11.0.0)(typescript@6.0.3) + '@typescript-eslint/scope-manager': 8.71.2 + '@typescript-eslint/types': 8.71.2 + '@typescript-eslint/typescript-estree': 8.71.2(supports-color@11.0.0)(typescript@6.0.3) eslint: 10.12.0(jiti@2.7.0)(supports-color@11.0.0) typescript: 6.0.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/visitor-keys@8.71.1': + '@typescript-eslint/visitor-keys@8.71.2': dependencies: - '@typescript-eslint/types': 8.71.1 + '@typescript-eslint/types': 8.71.2 eslint-visitor-keys: 5.0.1 '@verdaccio/auth@8.1.5(supports-color@11.0.0)': @@ -11921,6 +11915,16 @@ snapshots: process-warning: 1.0.0 semver: 7.8.5 + '@verdaccio/core@8.3.3': + dependencies: + ajv: 8.20.0 + http-errors: 2.0.1 + http-status-codes: 2.3.0 + lodash: 4.18.1 + minimatch: 10.2.6 + process-warning: 1.0.0 + semver: 7.8.5 + '@verdaccio/file-locking@13.1.0': dependencies: lockfile: 1.0.4 @@ -12048,21 +12052,21 @@ snapshots: transitivePeerDependencies: - supports-color - '@vitejs/plugin-basic-ssl@2.3.0(vite@8.3.3(@types/node@22.20.5)(esbuild@0.28.2)(jiti@2.7.0)(less@4.9.1(supports-color@11.0.0))(sass-embedded@1.105.1)(sass@1.105.1)(terser@5.51.2)(tsx@4.23.15)(yaml@2.9.1))': + '@vitejs/plugin-basic-ssl@2.3.0(vite@8.3.4(@types/node@22.20.5)(esbuild@0.28.2)(jiti@2.7.0)(less@4.9.1(supports-color@11.0.0))(sass-embedded@1.105.1)(sass@1.105.1)(terser@5.51.2)(tsx@4.23.15)(yaml@2.9.1))': dependencies: - vite: 8.3.3(@types/node@22.20.5)(esbuild@0.28.2)(jiti@2.7.0)(less@4.9.1(supports-color@11.0.0))(sass-embedded@1.105.1)(sass@1.105.1)(terser@5.51.2)(tsx@4.23.15)(yaml@2.9.1) + vite: 8.3.4(@types/node@22.20.5)(esbuild@0.28.2)(jiti@2.7.0)(less@4.9.1(supports-color@11.0.0))(sass-embedded@1.105.1)(sass@1.105.1)(terser@5.51.2)(tsx@4.23.15)(yaml@2.9.1) - '@vitest/coverage-v8@5.0.3(vitest@5.0.3)': + '@vitest/coverage-v8@5.0.4(vitest@5.0.4)': dependencies: '@bcoe/v8-coverage': 1.0.2 '@vitest/istanbul-lib-coverage': 1.0.2 '@vitest/istanbul-lib-report': 1.0.2 ast-v8-to-istanbul: 1.0.7 magicast: 0.5.5 - obug: 2.2.1 + obug: 3.0.0 std-env: 4.3.0 tinyrainbow: 3.2.0 - vitest: 5.0.3(@opentelemetry/api@1.9.1)(@types/node@22.20.5)(@vitest/coverage-v8@5.0.3)(jsdom@30.1.2(@noble/hashes@1.8.0))(vite@8.3.3(@types/node@22.20.5)(esbuild@0.28.2)(jiti@2.7.0)(less@4.9.1(supports-color@11.0.0))(sass-embedded@1.105.1)(sass@1.105.1)(terser@5.51.2)(tsx@4.23.15)(yaml@2.9.1)) + vitest: 5.0.4(@opentelemetry/api@1.9.1)(@types/node@22.20.5)(@vitest/coverage-v8@5.0.4)(jsdom@30.1.2(@noble/hashes@1.8.0))(vite@8.3.4(@types/node@22.20.5)(esbuild@0.28.2)(jiti@2.7.0)(less@4.9.1(supports-color@11.0.0))(sass-embedded@1.105.1)(sass@1.105.1)(terser@5.51.2)(tsx@4.23.15)(yaml@2.9.1)) '@vitest/istanbul-lib-coverage@1.0.2': {} @@ -12070,16 +12074,16 @@ snapshots: dependencies: '@vitest/istanbul-lib-coverage': 1.0.2 - '@vitest/mocker@5.0.3(vite@8.3.3(@types/node@22.20.5)(esbuild@0.28.2)(jiti@2.7.0)(less@4.9.1(supports-color@11.0.0))(sass-embedded@1.105.1)(sass@1.105.1)(terser@5.51.2)(tsx@4.23.15)(yaml@2.9.1))': + '@vitest/mocker@5.0.4(vite@8.3.4(@types/node@22.20.5)(esbuild@0.28.2)(jiti@2.7.0)(less@4.9.1(supports-color@11.0.0))(sass-embedded@1.105.1)(sass@1.105.1)(terser@5.51.2)(tsx@4.23.15)(yaml@2.9.1))': dependencies: '@jridgewell/trace-mapping': 0.3.31 - '@vitest/spy': 5.0.3 + '@vitest/spy': 5.0.4 estree-walker: 3.0.3 - magic-string: 1.4.2 + magic-string: 1.4.3 optionalDependencies: - vite: 8.3.3(@types/node@22.20.5)(esbuild@0.28.2)(jiti@2.7.0)(less@4.9.1(supports-color@11.0.0))(sass-embedded@1.105.1)(sass@1.105.1)(terser@5.51.2)(tsx@4.23.15)(yaml@2.9.1) + vite: 8.3.4(@types/node@22.20.5)(esbuild@0.28.2)(jiti@2.7.0)(less@4.9.1(supports-color@11.0.0))(sass-embedded@1.105.1)(sass@1.105.1)(terser@5.51.2)(tsx@4.23.15)(yaml@2.9.1) - '@vitest/spy@5.0.3': {} + '@vitest/spy@5.0.4': {} '@webassemblyjs/ast@1.14.1': dependencies: @@ -12201,6 +12205,42 @@ snapshots: '@yuku-codegen/binding-win32-x64@0.10.2': optional: true + '@yuku-core/binding-android-arm64@0.19.0': + optional: true + + '@yuku-core/binding-darwin-arm64@0.19.0': + optional: true + + '@yuku-core/binding-darwin-x64@0.19.0': + optional: true + + '@yuku-core/binding-freebsd-x64@0.19.0': + optional: true + + '@yuku-core/binding-linux-arm-gnu@0.19.0': + optional: true + + '@yuku-core/binding-linux-arm-musl@0.19.0': + optional: true + + '@yuku-core/binding-linux-arm64-gnu@0.19.0': + optional: true + + '@yuku-core/binding-linux-arm64-musl@0.19.0': + optional: true + + '@yuku-core/binding-linux-x64-gnu@0.19.0': + optional: true + + '@yuku-core/binding-linux-x64-musl@0.19.0': + optional: true + + '@yuku-core/binding-win32-arm64@0.19.0': + optional: true + + '@yuku-core/binding-win32-x64@0.19.0': + optional: true + '@yuku-parser/binding-android-arm64@0.10.2': optional: true @@ -12239,6 +12279,8 @@ snapshots: '@yuku-toolchain/types@0.10.2': {} + '@yuku-toolchain/types@0.19.0': {} + JSONStream@1.3.5: dependencies: jsonparse: 1.3.1 @@ -12258,11 +12300,11 @@ snapshots: mime-types: 3.0.2 negotiator: 1.1.0 - acorn-jsx@5.3.2(acorn@8.18.0): + acorn-jsx@5.3.2(acorn@8.19.0): dependencies: - acorn: 8.18.0 + acorn: 8.19.0 - acorn@8.18.0: {} + acorn@8.19.0: {} adjust-sourcemap-loader@4.0.0: dependencies: @@ -12425,13 +12467,13 @@ snapshots: atomic-sleep@1.0.0: {} - autoprefixer@10.6.1(postcss@8.5.29): + autoprefixer@10.6.1(postcss@8.5.30): dependencies: browserslist: 4.29.3 - caniuse-lite: 1.0.30001814 + caniuse-lite: 1.0.30001815 fraction.js: 5.3.4 picocolors: 1.1.1 - postcss: 8.5.29 + postcss: 8.5.30 postcss-value-parser: 4.2.0 available-typed-arrays@1.0.7: @@ -12444,12 +12486,12 @@ snapshots: b4a@1.9.0: {} - babel-loader@10.1.1(@babel/core@8.0.7)(webpack@5.111.1(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.29)(uglify-js@3.19.3)): + babel-loader@10.1.1(@babel/core@8.0.7)(webpack@5.111.1(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.30)(uglify-js@3.19.3)): dependencies: '@babel/core': 8.0.7 find-up: 5.0.0 optionalDependencies: - webpack: 5.111.1(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.29)(uglify-js@3.19.3) + webpack: 5.111.1(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.30)(uglify-js@3.19.3) babel-plugin-polyfill-corejs3@1.0.0(@babel/core@8.0.7): dependencies: @@ -12463,7 +12505,7 @@ snapshots: bare-events@2.9.2: {} - bare-fs@4.8.2: + bare-fs@4.8.4: dependencies: bare-events: 2.9.2 bare-path: 3.1.2 @@ -12492,7 +12534,7 @@ snapshots: base64-js@1.5.1: {} - baseline-browser-mapping@2.11.27: {} + baseline-browser-mapping@2.11.28: {} batch@0.6.1: {} @@ -12502,7 +12544,7 @@ snapshots: bcryptjs@2.4.3: {} - beasties@0.5.4: + beasties@0.5.5: dependencies: css-select: 7.0.0 css-what: 8.0.0 @@ -12510,9 +12552,9 @@ snapshots: domhandler: 6.0.1 htmlparser2: 12.0.0 picocolors: 1.1.1 - postcss: 8.5.29 + postcss: 8.5.30 postcss-media-query-parser: 0.2.3 - postcss-safe-parser: 7.1.0(postcss@8.5.29) + postcss-safe-parser: 7.1.0(postcss@8.5.30) before-after-hook@4.0.0: {} @@ -12647,11 +12689,11 @@ snapshots: browserslist@4.29.3: dependencies: - baseline-browser-mapping: 2.11.27 - caniuse-lite: 1.0.30001814 - electron-to-chromium: 1.5.444 - node-releases: 2.0.57 - update-browserslist-db: 1.3.3(browserslist@4.29.3) + baseline-browser-mapping: 2.11.28 + caniuse-lite: 1.0.30001815 + electron-to-chromium: 1.5.452 + node-releases: 2.0.58 + update-browserslist-db: 1.3.4(browserslist@4.29.3) bs-recipes@1.3.4: {} @@ -12717,7 +12759,7 @@ snapshots: callsites@3.1.0: {} - caniuse-lite@1.0.30001814: {} + caniuse-lite@1.0.30001815: {} caseless@0.12.0: {} @@ -12758,11 +12800,11 @@ snapshots: chrome-trace-event@1.0.4: {} - chromium-bidi@17.0.2(devtools-protocol@0.0.1687809): + chromium-bidi@157.0.8090-0(devtools-protocol@0.0.1696802): dependencies: - devtools-protocol: 0.0.1687809 + devtools-protocol: 0.0.1696802 mitt: 3.0.1 - zod: 3.25.76 + zod: 4.6.7 chunk-data@0.1.0: {} @@ -12896,6 +12938,8 @@ snapshots: content-disposition@1.1.0: {} + content-disposition@2.0.1: {} + content-type@1.0.5: {} content-type@2.1.0: {} @@ -12904,7 +12948,7 @@ snapshots: conventional-commits-filter@6.0.1: {} - conventional-commits-parser@7.1.2: + conventional-commits-parser@7.1.3: dependencies: '@simple-libs/stream-utils': 2.0.0 argue-cli: 3.2.0 @@ -12923,14 +12967,14 @@ snapshots: dependencies: is-what: 4.1.16 - copy-webpack-plugin@14.0.0(webpack@5.111.1(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.29)(uglify-js@3.19.3)): + copy-webpack-plugin@14.0.0(webpack@5.111.1(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.30)(uglify-js@3.19.3)): dependencies: glob-parent: 6.0.2 normalize-path: 3.0.0 schema-utils: 4.5.0 serialize-javascript: 7.1.2 tinyglobby: 0.2.17 - webpack: 5.111.1(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.29)(uglify-js@3.19.3) + webpack: 5.111.1(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.30)(uglify-js@3.19.3) core-js-compat@3.50.0: dependencies: @@ -12960,18 +13004,18 @@ snapshots: shebang-command: 2.0.0 which: 2.0.2 - css-loader@7.1.5(webpack@5.111.1(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.29)(uglify-js@3.19.3)): + css-loader@7.1.5(webpack@5.111.1(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.30)(uglify-js@3.19.3)): dependencies: - icss-utils: 5.1.0(postcss@8.5.29) - postcss: 8.5.29 - postcss-modules-extract-imports: 3.1.0(postcss@8.5.29) - postcss-modules-local-by-default: 4.2.0(postcss@8.5.29) - postcss-modules-scope: 3.2.1(postcss@8.5.29) - postcss-modules-values: 4.0.0(postcss@8.5.29) + icss-utils: 5.1.0(postcss@8.5.30) + postcss: 8.5.30 + postcss-modules-extract-imports: 3.1.0(postcss@8.5.30) + postcss-modules-local-by-default: 4.2.0(postcss@8.5.30) + postcss-modules-scope: 3.2.1(postcss@8.5.30) + postcss-modules-values: 4.0.0(postcss@8.5.30) postcss-value-parser: 4.2.0 semver: 7.8.5 optionalDependencies: - webpack: 5.111.1(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.29)(uglify-js@3.19.3) + webpack: 5.111.1(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.30)(uglify-js@3.19.3) css-select@7.0.0: dependencies: @@ -13039,12 +13083,6 @@ snapshots: optionalDependencies: supports-color: 11.0.0 - debug@4.4.0(supports-color@11.0.0): - dependencies: - ms: 2.1.3 - optionalDependencies: - supports-color: 11.0.0 - debug@4.4.3(supports-color@11.0.0): dependencies: ms: 2.1.3 @@ -13096,7 +13134,7 @@ snapshots: dev-ip@1.0.1: {} - devtools-protocol@0.0.1687809: {} + devtools-protocol@0.0.1696802: {} di@0.0.1: {} @@ -13182,7 +13220,7 @@ snapshots: ejs@7.0.1: {} - electron-to-chromium@1.5.444: {} + electron-to-chromium@1.5.452: {} emoji-regex@10.6.0: {} @@ -13408,17 +13446,17 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-module-utils@2.14.0(@typescript-eslint/parser@8.71.1(eslint@10.12.0(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0)(typescript@6.0.3))(eslint-import-resolver-node@0.3.10(supports-color@11.0.0))(eslint@10.12.0(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0): + eslint-module-utils@2.14.0(@typescript-eslint/parser@8.71.2(eslint@10.12.0(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0)(typescript@6.0.3))(eslint-import-resolver-node@0.3.10(supports-color@11.0.0))(eslint@10.12.0(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0): dependencies: debug: 3.2.7(supports-color@11.0.0) optionalDependencies: - '@typescript-eslint/parser': 8.71.1(eslint@10.12.0(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0)(typescript@6.0.3) + '@typescript-eslint/parser': 8.71.2(eslint@10.12.0(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0)(typescript@6.0.3) eslint: 10.12.0(jiti@2.7.0)(supports-color@11.0.0) eslint-import-resolver-node: 0.3.10(supports-color@11.0.0) transitivePeerDependencies: - supports-color - eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.71.1(eslint@10.12.0(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0)(typescript@6.0.3))(eslint@10.12.0(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0): + eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.71.2(eslint@10.12.0(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0)(typescript@6.0.3))(eslint@10.12.0(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0): dependencies: '@rtsao/scc': 1.1.0 array-includes: 3.2.0 @@ -13429,7 +13467,7 @@ snapshots: doctrine: 2.1.0 eslint: 10.12.0(jiti@2.7.0)(supports-color@11.0.0) eslint-import-resolver-node: 0.3.10(supports-color@11.0.0) - eslint-module-utils: 2.14.0(@typescript-eslint/parser@8.71.1(eslint@10.12.0(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0)(typescript@6.0.3))(eslint-import-resolver-node@0.3.10(supports-color@11.0.0))(eslint@10.12.0(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0) + eslint-module-utils: 2.14.0(@typescript-eslint/parser@8.71.2(eslint@10.12.0(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0)(typescript@6.0.3))(eslint-import-resolver-node@0.3.10(supports-color@11.0.0))(eslint@10.12.0(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0) hasown: 2.0.4 is-core-module: 2.17.0 is-glob: 4.0.3 @@ -13441,7 +13479,7 @@ snapshots: string.prototype.trimend: 1.0.10 tsconfig-paths: 3.15.0 optionalDependencies: - '@typescript-eslint/parser': 8.71.1(eslint@10.12.0(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0)(typescript@6.0.3) + '@typescript-eslint/parser': 8.71.2(eslint@10.12.0(jiti@2.7.0)(supports-color@11.0.0))(supports-color@11.0.0)(typescript@6.0.3) transitivePeerDependencies: - eslint-import-resolver-typescript - eslint-import-resolver-webpack @@ -13499,14 +13537,14 @@ snapshots: espree@10.4.0: dependencies: - acorn: 8.18.0 - acorn-jsx: 5.3.2(acorn@8.18.0) + acorn: 8.19.0 + acorn-jsx: 5.3.2(acorn@8.19.0) eslint-visitor-keys: 4.2.1 espree@11.2.0: dependencies: - acorn: 8.18.0 - acorn-jsx: 5.3.2(acorn@8.18.0) + acorn: 8.19.0 + acorn-jsx: 5.3.2(acorn@8.19.0) eslint-visitor-keys: 5.0.1 esquery@1.7.0: @@ -13616,6 +13654,39 @@ snapshots: transitivePeerDependencies: - supports-color + express@5.3.0(supports-color@11.0.0): + dependencies: + accepts: 2.0.0 + body-parser: 2.3.0(supports-color@11.0.0) + content-disposition: 2.0.1 + content-type: 2.1.0 + cookie: 0.7.2 + cookie-signature: 1.2.2 + debug: 4.4.3(supports-color@11.0.0) + depd: 2.0.0 + encodeurl: 2.0.0 + escape-html: 1.0.3 + etag: 1.8.1 + finalhandler: 2.1.1(supports-color@11.0.0) + fresh: 2.0.0 + http-errors: 2.0.1 + merge-descriptors: 2.0.0 + mime-types: 3.0.2 + on-finished: 2.4.1 + once: 1.4.0 + parseurl: 1.3.3 + proxy-addr: 2.0.8 + qs: 6.16.0 + range-parser: 1.3.0 + router: 2.2.0(supports-color@11.0.0) + send: 1.2.1(supports-color@11.0.0) + serve-static: 2.2.1(supports-color@11.0.0) + statuses: 2.0.2 + type-is: 2.1.0 + vary: 1.1.2 + transitivePeerDependencies: + - supports-color + extend@3.0.2: {} extsprintf@1.3.0: {} @@ -13732,35 +13803,35 @@ snapshots: locate-path: 6.0.0 path-exists: 4.0.0 - firebase@12.19.0: + firebase@13.0.0: dependencies: - '@firebase/ai': 2.16.0(@firebase/app-types@0.9.6)(@firebase/app@0.16.2) - '@firebase/analytics': 0.10.25(@firebase/app@0.16.2) - '@firebase/analytics-compat': 0.2.31(@firebase/app-compat@0.5.18)(@firebase/app@0.16.2) - '@firebase/app': 0.16.2 - '@firebase/app-check': 0.13.1(@firebase/app@0.16.2) - '@firebase/app-check-compat': 0.4.7(@firebase/app-compat@0.5.18)(@firebase/app@0.16.2) - '@firebase/app-compat': 0.5.18 + '@firebase/ai': 3.0.0(@firebase/app-types@0.9.6)(@firebase/app@0.16.3) + '@firebase/analytics': 0.10.25(@firebase/app@0.16.3) + '@firebase/analytics-compat': 0.2.31(@firebase/app-compat@0.5.19)(@firebase/app@0.16.3) + '@firebase/app': 0.16.3 + '@firebase/app-check': 0.13.1(@firebase/app@0.16.3) + '@firebase/app-check-compat': 0.4.7(@firebase/app-compat@0.5.19)(@firebase/app@0.16.3) + '@firebase/app-compat': 0.5.19 '@firebase/app-types': 0.9.6 - '@firebase/auth': 1.13.6(@firebase/app@0.16.2) - '@firebase/auth-compat': 0.6.11(@firebase/app-compat@0.5.18)(@firebase/app-types@0.9.6)(@firebase/app@0.16.2) - '@firebase/data-connect': 0.7.4(@firebase/app@0.16.2) + '@firebase/auth': 1.13.7(@firebase/app@0.16.3) + '@firebase/auth-compat': 0.6.12(@firebase/app-compat@0.5.19)(@firebase/app-types@0.9.6)(@firebase/app@0.16.3) + '@firebase/data-connect': 0.7.4(@firebase/app@0.16.3) '@firebase/database': 1.1.5 - '@firebase/database-compat': 2.1.7(@firebase/app-compat@0.5.18)(@firebase/app@0.16.2) - '@firebase/firestore': 4.17.2(@firebase/app@0.16.2) - '@firebase/firestore-compat': 0.4.14(@firebase/app-compat@0.5.18)(@firebase/app-types@0.9.6)(@firebase/app@0.16.2) - '@firebase/functions': 0.14.0(@firebase/app@0.16.2) - '@firebase/functions-compat': 0.5.0(@firebase/app-compat@0.5.18)(@firebase/app@0.16.2) - '@firebase/installations': 0.6.24(@firebase/app@0.16.2) - '@firebase/installations-compat': 0.2.24(@firebase/app-compat@0.5.18)(@firebase/app-types@0.9.6)(@firebase/app@0.16.2) - '@firebase/messaging': 0.13.3(@firebase/app@0.16.2) - '@firebase/messaging-compat': 0.2.30(@firebase/app-compat@0.5.18)(@firebase/app@0.16.2) - '@firebase/performance': 0.7.14(@firebase/app@0.16.2) - '@firebase/performance-compat': 0.2.27(@firebase/app-compat@0.5.18)(@firebase/app@0.16.2) - '@firebase/remote-config': 0.9.2(@firebase/app@0.16.2) - '@firebase/remote-config-compat': 0.2.29(@firebase/app-compat@0.5.18)(@firebase/app@0.16.2) - '@firebase/storage': 0.14.5(@firebase/app@0.16.2) - '@firebase/storage-compat': 0.4.5(@firebase/app-compat@0.5.18)(@firebase/app-types@0.9.6)(@firebase/app@0.16.2) + '@firebase/database-compat': 2.1.7(@firebase/app-compat@0.5.19)(@firebase/app@0.16.3) + '@firebase/firestore': 4.18.0(@firebase/app@0.16.3) + '@firebase/firestore-compat': 0.4.15(@firebase/app-compat@0.5.19)(@firebase/app-types@0.9.6)(@firebase/app@0.16.3) + '@firebase/functions': 0.14.0(@firebase/app@0.16.3) + '@firebase/functions-compat': 0.5.0(@firebase/app-compat@0.5.19)(@firebase/app@0.16.3) + '@firebase/installations': 0.6.24(@firebase/app@0.16.3) + '@firebase/installations-compat': 0.2.24(@firebase/app-compat@0.5.19)(@firebase/app-types@0.9.6)(@firebase/app@0.16.3) + '@firebase/messaging': 0.13.3(@firebase/app@0.16.3) + '@firebase/messaging-compat': 0.2.30(@firebase/app-compat@0.5.19)(@firebase/app@0.16.3) + '@firebase/performance': 0.7.14(@firebase/app@0.16.3) + '@firebase/performance-compat': 0.2.27(@firebase/app-compat@0.5.19)(@firebase/app@0.16.3) + '@firebase/remote-config': 0.9.3(@firebase/app@0.16.3) + '@firebase/remote-config-compat': 0.2.30(@firebase/app-compat@0.5.19)(@firebase/app@0.16.3) + '@firebase/storage': 0.14.6(@firebase/app@0.16.3) + '@firebase/storage-compat': 0.4.6(@firebase/app-compat@0.5.19)(@firebase/app-types@0.9.6)(@firebase/app@0.16.3) '@firebase/util': 1.15.3 transitivePeerDependencies: - '@react-native-async-storage/async-storage' @@ -13777,9 +13848,9 @@ snapshots: flru@1.0.2: {} - folder-hash@4.1.3(supports-color@11.0.0): + folder-hash@4.1.4(supports-color@11.0.0): dependencies: - debug: 4.4.0(supports-color@11.0.0) + debug: 4.4.3(supports-color@11.0.0) minimatch: 7.4.9 transitivePeerDependencies: - supports-color @@ -14011,7 +14082,7 @@ snapshots: google-gax@5.0.8(supports-color@11.0.0): dependencies: - '@grpc/grpc-js': 1.14.5 + '@grpc/grpc-js': 1.14.6 '@grpc/proto-loader': 0.8.1 duplexify: 4.1.3 google-auth-library: 10.5.0(supports-color@11.0.0) @@ -14055,7 +14126,7 @@ snapshots: grpc-gcp@1.1.1: dependencies: - '@grpc/grpc-js': 1.14.5 + '@grpc/grpc-js': 1.14.6 protobufjs: 7.6.6 gtoken@8.0.0(supports-color@11.0.0): @@ -14233,9 +14304,9 @@ snapshots: dependencies: safer-buffer: 2.1.2 - icss-utils@5.1.0(postcss@8.5.29): + icss-utils@5.1.0(postcss@8.5.30): dependencies: - postcss: 8.5.29 + postcss: 8.5.30 idb@7.1.1: {} @@ -14567,9 +14638,9 @@ snapshots: jsdom@30.1.2(@noble/hashes@1.8.0): dependencies: - '@asamuzakjp/css-color': 7.1.3 + '@asamuzakjp/css-color': 7.1.5 '@asamuzakjp/dom-selector': 9.2.4 - '@bramus/specificity': 2.4.2 + '@bramus/specificity': 2.4.3 '@csstools/css-syntax-patches-for-csstree': 1.1.15(css-tree@3.2.1) '@exodus/bytes': 1.16.0(@noble/hashes@1.8.0) css-tree: 3.2.1 @@ -14727,17 +14798,17 @@ snapshots: kind-of@6.0.3: {} - launch-editor@2.14.1: + launch-editor@2.14.2: dependencies: picocolors: 1.1.1 shell-quote: 1.12.0 - less-loader@13.0.0(less@4.9.1(supports-color@11.0.0))(webpack@5.111.1(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.29)(uglify-js@3.19.3)): + less-loader@13.0.0(less@4.9.1(supports-color@11.0.0))(webpack@5.111.1(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.30)(uglify-js@3.19.3)): dependencies: '@types/less': 3.0.8 less: 4.9.1(supports-color@11.0.0) optionalDependencies: - webpack: 5.111.1(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.29)(uglify-js@3.19.3) + webpack: 5.111.1(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.30)(uglify-js@3.19.3) less@4.9.1(supports-color@11.0.0): dependencies: @@ -14759,11 +14830,11 @@ snapshots: prelude-ls: 1.2.1 type-check: 0.4.0 - license-webpack-plugin@4.0.2(webpack@5.111.1(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.29)(uglify-js@3.19.3)): + license-webpack-plugin@4.0.2(webpack@5.111.1(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.30)(uglify-js@3.19.3)): dependencies: webpack-sources: 3.6.0 optionalDependencies: - webpack: 5.111.1(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.29)(uglify-js@3.19.3) + webpack: 5.111.1(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.30)(uglify-js@3.19.3) lightningcss-android-arm64@1.33.0: optional: true @@ -14820,7 +14891,7 @@ snapshots: lines-and-columns@1.2.4: {} - listr2@11.1.1: + listr2@11.2.0: dependencies: cli-truncate: 6.1.1 log-update: 8.0.0 @@ -14939,10 +15010,6 @@ snapshots: dependencies: '@jridgewell/sourcemap-codec': 1.6.0 - magic-string@1.4.2: - dependencies: - '@jridgewell/sourcemap-codec': 1.6.0 - magic-string@1.4.3: dependencies: '@jridgewell/sourcemap-codec': 1.6.0 @@ -15022,11 +15089,11 @@ snapshots: mimic-response@4.0.0: {} - mini-css-extract-plugin@2.10.2(webpack@5.111.1(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.29)(uglify-js@3.19.3)): + mini-css-extract-plugin@2.10.2(webpack@5.111.1(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.30)(uglify-js@3.19.3)): dependencies: schema-utils: 4.5.0 tapable: 2.3.3 - webpack: 5.111.1(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.29)(uglify-js@3.19.3) + webpack: 5.111.1(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.30)(uglify-js@3.19.3) minimatch@10.2.6: dependencies: @@ -15046,30 +15113,30 @@ snapshots: minimist@1.2.8: {} - minimizer-webpack-plugin@5.13.0(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.28)(uglify-js@3.19.3)(webpack@5.111.1(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.28)(uglify-js@3.19.3)): + minimizer-webpack-plugin@5.13.0(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.29)(uglify-js@3.19.3)(webpack@5.111.1(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.29)(uglify-js@3.19.3)): dependencies: '@jridgewell/trace-mapping': 0.3.31 jest-worker: 27.5.1 schema-utils: 4.5.0 terser: 5.51.2 - webpack: 5.111.1(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.28)(uglify-js@3.19.3) + webpack: 5.111.1(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.29)(uglify-js@3.19.3) optionalDependencies: esbuild: 0.28.2 lightningcss: 1.33.0 - postcss: 8.5.28 + postcss: 8.5.29 uglify-js: 3.19.3 - minimizer-webpack-plugin@5.13.0(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.29)(uglify-js@3.19.3)(webpack@5.111.1(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.29)(uglify-js@3.19.3)): + minimizer-webpack-plugin@5.13.0(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.30)(uglify-js@3.19.3)(webpack@5.111.1(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.30)(uglify-js@3.19.3)): dependencies: '@jridgewell/trace-mapping': 0.3.31 jest-worker: 27.5.1 schema-utils: 4.5.0 terser: 5.51.2 - webpack: 5.111.1(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.29)(uglify-js@3.19.3) + webpack: 5.111.1(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.30)(uglify-js@3.19.3) optionalDependencies: esbuild: 0.28.2 lightningcss: 1.33.0 - postcss: 8.5.29 + postcss: 8.5.30 uglify-js: 3.19.3 minipass@7.1.3: {} @@ -15122,7 +15189,7 @@ snapshots: mute-stream@3.0.0: {} - nanoid@3.3.19: {} + nanoid@3.3.20: {} natural-compare@1.4.0: {} @@ -15151,10 +15218,10 @@ snapshots: neo-async@2.6.2: {} - ng-packagr@22.3.0-next.2(@angular/compiler-cli@22.3.0-next.1(@angular/compiler@22.3.0-next.1)(typescript@6.0.3))(supports-color@11.0.0)(tslib@2.8.1)(typescript@6.0.3): + ng-packagr@22.3.0-next.2(@angular/compiler-cli@22.3.0-next.2(@angular/compiler@22.3.0-next.2)(typescript@6.0.3))(supports-color@11.0.0)(tslib@2.8.1)(typescript@6.0.3): dependencies: '@ampproject/remapping': 2.3.0 - '@angular/compiler-cli': 22.3.0-next.1(@angular/compiler@22.3.0-next.1)(typescript@6.0.3) + '@angular/compiler-cli': 22.3.0-next.2(@angular/compiler@22.3.0-next.2)(typescript@6.0.3) ajv: 8.20.0 browserslist: 4.29.3 chokidar: 5.0.0 @@ -15166,9 +15233,9 @@ snapshots: jsonc-parser: 3.3.1 less: 4.9.1(supports-color@11.0.0) ora: 9.4.1 - postcss: 8.5.28 - rolldown: 1.2.11 - rolldown-plugin-dts: 0.28.6(rolldown@1.2.11)(typescript@6.0.3) + postcss: 8.5.29 + rolldown: 1.2.12 + rolldown-plugin-dts: 0.28.6(rolldown@1.2.12)(typescript@6.0.3) rxjs: 7.8.2 sass: 1.105.1 sass-embedded: 1.105.1 @@ -15183,11 +15250,11 @@ snapshots: - supports-color - vue-tsc - nock@14.0.17: + nock@15.0.1(supports-color@11.0.0): dependencies: - '@mswjs/interceptors': 0.41.9 - json-stringify-safe: 5.0.1 - propagate: 2.0.1 + '@mswjs/interceptors': 0.44.0(supports-color@11.0.0) + transitivePeerDependencies: + - supports-color node-addon-api@6.1.0: optional: true @@ -15222,7 +15289,7 @@ snapshots: node-gyp-build@4.8.4: {} - node-releases@2.0.57: {} + node-releases@2.0.58: {} normalize-path@3.0.0: {} @@ -15283,8 +15350,6 @@ snapshots: define-properties: 1.2.1 es-object-atoms: 1.1.2 - obug@2.2.1: {} - obug@3.0.0: {} on-exit-leak-free@2.1.2: {} @@ -15352,29 +15417,29 @@ snapshots: object-keys: 1.1.1 safe-push-apply: 1.0.0 - oxc-parser@0.153.0: + oxc-parser@0.154.0: dependencies: - '@oxc-project/types': 0.153.0 + '@oxc-project/types': 0.154.0 optionalDependencies: - '@oxc-parser/binding-android-arm-eabi': 0.153.0 - '@oxc-parser/binding-android-arm64': 0.153.0 - '@oxc-parser/binding-darwin-arm64': 0.153.0 - '@oxc-parser/binding-darwin-x64': 0.153.0 - '@oxc-parser/binding-freebsd-x64': 0.153.0 - '@oxc-parser/binding-linux-arm-gnueabihf': 0.153.0 - '@oxc-parser/binding-linux-arm-musleabihf': 0.153.0 - '@oxc-parser/binding-linux-arm64-gnu': 0.153.0 - '@oxc-parser/binding-linux-arm64-musl': 0.153.0 - '@oxc-parser/binding-linux-ppc64-gnu': 0.153.0 - '@oxc-parser/binding-linux-riscv64-gnu': 0.153.0 - '@oxc-parser/binding-linux-riscv64-musl': 0.153.0 - '@oxc-parser/binding-linux-s390x-gnu': 0.153.0 - '@oxc-parser/binding-linux-x64-gnu': 0.153.0 - '@oxc-parser/binding-linux-x64-musl': 0.153.0 - '@oxc-parser/binding-openharmony-arm64': 0.153.0 - '@oxc-parser/binding-win32-arm64-msvc': 0.153.0 - '@oxc-parser/binding-win32-ia32-msvc': 0.153.0 - '@oxc-parser/binding-win32-x64-msvc': 0.153.0 + '@oxc-parser/binding-android-arm-eabi': 0.154.0 + '@oxc-parser/binding-android-arm64': 0.154.0 + '@oxc-parser/binding-darwin-arm64': 0.154.0 + '@oxc-parser/binding-darwin-x64': 0.154.0 + '@oxc-parser/binding-freebsd-x64': 0.154.0 + '@oxc-parser/binding-linux-arm-gnueabihf': 0.154.0 + '@oxc-parser/binding-linux-arm-musleabihf': 0.154.0 + '@oxc-parser/binding-linux-arm64-gnu': 0.154.0 + '@oxc-parser/binding-linux-arm64-musl': 0.154.0 + '@oxc-parser/binding-linux-ppc64-gnu': 0.154.0 + '@oxc-parser/binding-linux-riscv64-gnu': 0.154.0 + '@oxc-parser/binding-linux-riscv64-musl': 0.154.0 + '@oxc-parser/binding-linux-s390x-gnu': 0.154.0 + '@oxc-parser/binding-linux-x64-gnu': 0.154.0 + '@oxc-parser/binding-linux-x64-musl': 0.154.0 + '@oxc-parser/binding-openharmony-arm64': 0.154.0 + '@oxc-parser/binding-win32-arm64-msvc': 0.154.0 + '@oxc-parser/binding-win32-ia32-msvc': 0.154.0 + '@oxc-parser/binding-win32-x64-msvc': 0.154.0 p-finally@1.0.0: {} @@ -15479,7 +15544,7 @@ snapshots: pino-abstract-transport@1.2.0: dependencies: - readable-stream: 4.7.0 + readable-stream: 4.8.0 split2: 4.2.0 pino-abstract-transport@2.0.0: @@ -15495,7 +15560,7 @@ snapshots: on-exit-leak-free: 2.1.2 pino-abstract-transport: 2.0.0 pino-std-serializers: 7.1.0 - process-warning: 5.1.0 + process-warning: 5.1.1 quick-format-unescaped: 4.0.4 real-require: 0.2.0 safe-stable-stringify: 2.5.0 @@ -15528,43 +15593,43 @@ snapshots: possible-typed-array-names@1.1.0: {} - postcss-loader@8.2.1(postcss@8.5.29)(typescript@6.0.3)(webpack@5.111.1(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.29)(uglify-js@3.19.3)): + postcss-loader@8.2.1(postcss@8.5.30)(typescript@6.0.3)(webpack@5.111.1(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.30)(uglify-js@3.19.3)): dependencies: cosmiconfig: 9.0.2(typescript@6.0.3) jiti: 2.7.0 - postcss: 8.5.29 + postcss: 8.5.30 semver: 7.8.5 optionalDependencies: - webpack: 5.111.1(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.29)(uglify-js@3.19.3) + webpack: 5.111.1(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.30)(uglify-js@3.19.3) transitivePeerDependencies: - typescript postcss-media-query-parser@0.2.3: {} - postcss-modules-extract-imports@3.1.0(postcss@8.5.29): + postcss-modules-extract-imports@3.1.0(postcss@8.5.30): dependencies: - postcss: 8.5.29 + postcss: 8.5.30 - postcss-modules-local-by-default@4.2.0(postcss@8.5.29): + postcss-modules-local-by-default@4.2.0(postcss@8.5.30): dependencies: - icss-utils: 5.1.0(postcss@8.5.29) - postcss: 8.5.29 + icss-utils: 5.1.0(postcss@8.5.30) + postcss: 8.5.30 postcss-selector-parser: 7.1.6 postcss-value-parser: 4.2.0 - postcss-modules-scope@3.2.1(postcss@8.5.29): + postcss-modules-scope@3.2.1(postcss@8.5.30): dependencies: - postcss: 8.5.29 + postcss: 8.5.30 postcss-selector-parser: 7.1.6 - postcss-modules-values@4.0.0(postcss@8.5.29): + postcss-modules-values@4.0.0(postcss@8.5.30): dependencies: - icss-utils: 5.1.0(postcss@8.5.29) - postcss: 8.5.29 + icss-utils: 5.1.0(postcss@8.5.30) + postcss: 8.5.30 - postcss-safe-parser@7.1.0(postcss@8.5.29): + postcss-safe-parser@7.1.0(postcss@8.5.30): dependencies: - postcss: 8.5.29 + postcss: 8.5.30 postcss-selector-parser@7.1.6: dependencies: @@ -15573,15 +15638,15 @@ snapshots: postcss-value-parser@4.2.0: {} - postcss@8.5.28: + postcss@8.5.29: dependencies: - nanoid: 3.3.19 + nanoid: 3.3.20 picocolors: 1.1.1 source-map-js: 1.2.2 - postcss@8.5.29: + postcss@8.5.30: dependencies: - nanoid: 3.3.19 + nanoid: 3.3.20 picocolors: 1.1.1 source-map-js: 1.2.2 @@ -15589,7 +15654,7 @@ snapshots: prelude-ls@1.2.1: {} - prettier@3.9.9: {} + prettier@3.9.10: {} probe-image-size@7.4.0(supports-color@11.0.0): dependencies: @@ -15606,12 +15671,10 @@ snapshots: process-warning@1.0.0: {} - process-warning@5.1.0: {} + process-warning@5.1.1: {} process@0.11.10: {} - propagate@2.0.1: {} - proto3-json-serializer@3.0.4: dependencies: protobufjs: 7.6.6 @@ -15655,13 +15718,13 @@ snapshots: punycode@2.3.1: {} - puppeteer-core@25.12.0(bufferutil@4.1.0)(utf-8-validate@6.0.6): + puppeteer-core@25.14.0(bufferutil@4.1.0)(utf-8-validate@6.0.6): dependencies: - '@puppeteer/browsers': 3.2.3 - chromium-bidi: 17.0.2(devtools-protocol@0.0.1687809) - devtools-protocol: 0.0.1687809 + '@puppeteer/browsers': 3.2.5 + chromium-bidi: 157.0.8090-0(devtools-protocol@0.0.1696802) + devtools-protocol: 0.0.1696802 typed-query-selector: 2.12.3 - webdriver-bidi-protocol: 0.4.3 + webdriver-bidi-protocol: 1.0.1 ws: 8.22.0(bufferutil@4.1.0)(utf-8-validate@6.0.6) transitivePeerDependencies: - bufferutil @@ -15669,13 +15732,13 @@ snapshots: - utf-8-validate - yauzl - puppeteer@25.12.0(bufferutil@4.1.0)(utf-8-validate@6.0.6): + puppeteer@25.14.0(bufferutil@4.1.0)(utf-8-validate@6.0.6): dependencies: - '@puppeteer/browsers': 3.2.3 - chromium-bidi: 17.0.2(devtools-protocol@0.0.1687809) - devtools-protocol: 0.0.1687809 + '@puppeteer/browsers': 3.2.5 + chromium-bidi: 157.0.8090-0(devtools-protocol@0.0.1696802) + devtools-protocol: 0.0.1696802 lilconfig: 3.1.3 - puppeteer-core: 25.12.0(bufferutil@4.1.0)(utf-8-validate@6.0.6) + puppeteer-core: 25.14.0(bufferutil@4.1.0)(utf-8-validate@6.0.6) typed-query-selector: 2.12.3 transitivePeerDependencies: - bufferutil @@ -15766,7 +15829,7 @@ snapshots: process: 0.11.10 string_decoder: 1.3.0 - readable-stream@4.7.0: + readable-stream@4.8.0: dependencies: abort-controller: 3.0.0 buffer: 6.0.3 @@ -15812,18 +15875,18 @@ snapshots: gopd: 1.2.0 set-function-name: 2.0.2 - regexpu-core@6.5.2: + regexpu-core@6.5.3: dependencies: regenerate: 1.5.0 regenerate-unicode-properties: 10.3.0 regjsgen: 0.8.0 - regjsparser: 0.13.3 + regjsparser: 0.13.4 unicode-match-property-ecmascript: 2.0.0 unicode-match-property-value-ecmascript: 2.2.1 regjsgen@0.8.0: {} - regjsparser@0.13.3: + regjsparser@0.13.4: dependencies: jsesc: 3.1.0 @@ -15844,10 +15907,10 @@ snapshots: adjust-sourcemap-loader: 4.0.0 convert-source-map: 1.9.0 loader-utils: 2.0.4 - postcss: 8.5.29 + postcss: 8.5.30 source-map: 0.6.1 - resolve@1.22.12: + resolve@1.22.13: dependencies: es-errors: 1.3.0 is-core-module: 2.17.0 @@ -15888,6 +15951,8 @@ snapshots: retry@0.13.1: {} + rettime@0.11.12: {} + reusify@1.1.0: {} rfdc@1.4.1: {} @@ -15900,12 +15965,12 @@ snapshots: dependencies: glob: 10.5.0 - rolldown-plugin-dts@0.28.6(rolldown@1.2.11)(typescript@6.0.3): + rolldown-plugin-dts@0.28.6(rolldown@1.2.12)(typescript@6.0.3): dependencies: dts-resolver: 3.0.0 get-tsconfig: 5.0.0-beta.6 obug: 3.0.0 - rolldown: 1.2.11 + rolldown: 1.2.12 yuku-ast: 0.10.2 yuku-codegen: 0.10.2 yuku-parser: 0.10.2 @@ -15914,41 +15979,20 @@ snapshots: transitivePeerDependencies: - oxc-resolver - rolldown-plugin-dts@0.28.6(rolldown@1.2.12)(typescript@6.0.3): + rolldown-plugin-dts@0.28.8(rolldown@1.2.14)(typescript@6.0.3): dependencies: dts-resolver: 3.0.0 get-tsconfig: 5.0.0-beta.6 obug: 3.0.0 - rolldown: 1.2.12 - yuku-ast: 0.10.2 - yuku-codegen: 0.10.2 - yuku-parser: 0.10.2 + rolldown: 1.2.14 + yuku-ast: 0.19.0 + yuku-codegen: 0.19.0 + yuku-parser: 0.19.0 optionalDependencies: typescript: 6.0.3 transitivePeerDependencies: - oxc-resolver - rolldown@1.2.11: - dependencies: - '@oxc-project/types': 0.151.0 - '@rolldown/pluginutils': 1.0.1 - optionalDependencies: - '@rolldown/binding-android-arm-eabi': 1.2.11 - '@rolldown/binding-android-arm64': 1.2.11 - '@rolldown/binding-darwin-arm64': 1.2.11 - '@rolldown/binding-darwin-x64': 1.2.11 - '@rolldown/binding-freebsd-x64': 1.2.11 - '@rolldown/binding-linux-arm-gnueabihf': 1.2.11 - '@rolldown/binding-linux-arm64-gnu': 1.2.11 - '@rolldown/binding-linux-arm64-musl': 1.2.11 - '@rolldown/binding-linux-ppc64-gnu': 1.2.11 - '@rolldown/binding-linux-s390x-gnu': 1.2.11 - '@rolldown/binding-linux-x64-gnu': 1.2.11 - '@rolldown/binding-linux-x64-musl': 1.2.11 - '@rolldown/binding-openharmony-arm64': 1.2.11 - '@rolldown/binding-win32-arm64-msvc': 1.2.11 - '@rolldown/binding-win32-x64-msvc': 1.2.11 - rolldown@1.2.12: dependencies: '@oxc-project/types': 0.152.0 @@ -15970,54 +16014,75 @@ snapshots: '@rolldown/binding-win32-arm64-msvc': 1.2.12 '@rolldown/binding-win32-x64-msvc': 1.2.12 - rollup-plugin-dts@6.5.1(rollup@4.64.1)(typescript@6.0.3): + rolldown@1.2.14: + dependencies: + '@oxc-project/types': 0.154.0 + '@rolldown/pluginutils': 1.0.1 + optionalDependencies: + '@rolldown/binding-android-arm-eabi': 1.2.14 + '@rolldown/binding-android-arm64': 1.2.14 + '@rolldown/binding-darwin-arm64': 1.2.14 + '@rolldown/binding-darwin-x64': 1.2.14 + '@rolldown/binding-freebsd-x64': 1.2.14 + '@rolldown/binding-linux-arm-gnueabihf': 1.2.14 + '@rolldown/binding-linux-arm64-gnu': 1.2.14 + '@rolldown/binding-linux-arm64-musl': 1.2.14 + '@rolldown/binding-linux-ppc64-gnu': 1.2.14 + '@rolldown/binding-linux-s390x-gnu': 1.2.14 + '@rolldown/binding-linux-x64-gnu': 1.2.14 + '@rolldown/binding-linux-x64-musl': 1.2.14 + '@rolldown/binding-openharmony-arm64': 1.2.14 + '@rolldown/binding-win32-arm64-msvc': 1.2.14 + '@rolldown/binding-win32-x64-msvc': 1.2.14 + + rollup-plugin-dts@6.5.1(rollup@4.64.6)(typescript@6.0.3): dependencies: '@jridgewell/remapping': 2.3.5 '@jridgewell/sourcemap-codec': 1.6.0 convert-source-map: 2.0.0 magic-string: 0.30.21 - rollup: 4.64.1 + rollup: 4.64.6 typescript: 6.0.3 optionalDependencies: '@babel/code-frame': 8.0.6 - rollup-plugin-sourcemaps2@0.5.9(@types/node@22.20.5)(rollup@4.64.1): + rollup-plugin-sourcemaps2@0.5.9(@types/node@22.20.5)(rollup@4.64.6): dependencies: - '@rollup/pluginutils': 5.4.0(rollup@4.64.1) - rollup: 4.64.1 + '@rollup/pluginutils': 5.4.0(rollup@4.64.6) + rollup: 4.64.6 optionalDependencies: '@types/node': 22.20.5 - rollup@4.64.1: + rollup@4.64.6: dependencies: '@types/estree': 1.0.9 optionalDependencies: '@napi-rs/lzma-linux-x64-gnu': 1.5.1 - '@rollup/rollup-android-arm-eabi': 4.64.1 - '@rollup/rollup-android-arm64': 4.64.1 - '@rollup/rollup-darwin-arm64': 4.64.1 - '@rollup/rollup-darwin-x64': 4.64.1 - '@rollup/rollup-freebsd-arm64': 4.64.1 - '@rollup/rollup-freebsd-x64': 4.64.1 - '@rollup/rollup-linux-arm-gnueabihf': 4.64.1 - '@rollup/rollup-linux-arm-musleabihf': 4.64.1 - '@rollup/rollup-linux-arm64-gnu': 4.64.1 - '@rollup/rollup-linux-arm64-musl': 4.64.1 - '@rollup/rollup-linux-loong64-gnu': 4.64.1 - '@rollup/rollup-linux-loong64-musl': 4.64.1 - '@rollup/rollup-linux-ppc64-gnu': 4.64.1 - '@rollup/rollup-linux-ppc64-musl': 4.64.1 - '@rollup/rollup-linux-riscv64-gnu': 4.64.1 - '@rollup/rollup-linux-riscv64-musl': 4.64.1 - '@rollup/rollup-linux-s390x-gnu': 4.64.1 - '@rollup/rollup-linux-x64-gnu': 4.64.1 - '@rollup/rollup-linux-x64-musl': 4.64.1 - '@rollup/rollup-openbsd-x64': 4.64.1 - '@rollup/rollup-openharmony-arm64': 4.64.1 - '@rollup/rollup-win32-arm64-msvc': 4.64.1 - '@rollup/rollup-win32-ia32-msvc': 4.64.1 - '@rollup/rollup-win32-x64-gnu': 4.64.1 - '@rollup/rollup-win32-x64-msvc': 4.64.1 + '@rollup/rollup-android-arm-eabi': 4.64.6 + '@rollup/rollup-android-arm64': 4.64.6 + '@rollup/rollup-darwin-arm64': 4.64.6 + '@rollup/rollup-darwin-x64': 4.64.6 + '@rollup/rollup-freebsd-arm64': 4.64.6 + '@rollup/rollup-freebsd-x64': 4.64.6 + '@rollup/rollup-linux-arm-gnueabihf': 4.64.6 + '@rollup/rollup-linux-arm-musleabihf': 4.64.6 + '@rollup/rollup-linux-arm64-gnu': 4.64.6 + '@rollup/rollup-linux-arm64-musl': 4.64.6 + '@rollup/rollup-linux-loong64-gnu': 4.64.6 + '@rollup/rollup-linux-loong64-musl': 4.64.6 + '@rollup/rollup-linux-ppc64-gnu': 4.64.6 + '@rollup/rollup-linux-ppc64-musl': 4.64.6 + '@rollup/rollup-linux-riscv64-gnu': 4.64.6 + '@rollup/rollup-linux-riscv64-musl': 4.64.6 + '@rollup/rollup-linux-s390x-gnu': 4.64.6 + '@rollup/rollup-linux-x64-gnu': 4.64.6 + '@rollup/rollup-linux-x64-musl': 4.64.6 + '@rollup/rollup-openbsd-x64': 4.64.6 + '@rollup/rollup-openharmony-arm64': 4.64.6 + '@rollup/rollup-win32-arm64-msvc': 4.64.6 + '@rollup/rollup-win32-ia32-msvc': 4.64.6 + '@rollup/rollup-win32-x64-gnu': 4.64.6 + '@rollup/rollup-win32-x64-msvc': 4.64.6 fsevents: 2.3.3 router@2.2.0(supports-color@11.0.0): @@ -16160,11 +16225,11 @@ snapshots: sass-embedded-win32-arm64: 1.105.1 sass-embedded-win32-x64: 1.105.1 - sass-loader@17.0.1(sass-embedded@1.105.1)(sass@1.105.1)(webpack@5.111.1(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.29)(uglify-js@3.19.3)): + sass-loader@17.0.1(sass-embedded@1.105.1)(sass@1.105.1)(webpack@5.111.1(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.30)(uglify-js@3.19.3)): optionalDependencies: sass: 1.105.1 sass-embedded: 1.105.1 - webpack: 5.111.1(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.29)(uglify-js@3.19.3) + webpack: 5.111.1(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.30)(uglify-js@3.19.3) sass@1.105.1: dependencies: @@ -16390,11 +16455,11 @@ snapshots: source-map-js@1.2.2: {} - source-map-loader@5.0.0(webpack@5.111.1(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.29)(uglify-js@3.19.3)): + source-map-loader@5.0.0(webpack@5.111.1(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.30)(uglify-js@3.19.3)): dependencies: iconv-lite: 0.6.3 source-map-js: 1.2.2 - webpack: 5.111.1(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.29)(uglify-js@3.19.3) + webpack: 5.111.1(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.30)(uglify-js@3.19.3) source-map-support@0.5.21: dependencies: @@ -16484,8 +16549,6 @@ snapshots: - bare-abort-controller - react-native-b4a - strict-event-emitter@0.5.1: {} - string-width@4.2.3: dependencies: emoji-regex: 8.0.0 @@ -16580,7 +16643,7 @@ snapshots: tar-stream@3.2.0: dependencies: b4a: 1.9.0 - bare-fs: 4.8.2 + bare-fs: 4.8.4 fast-fifo: 1.3.2 streamx: 2.28.1 transitivePeerDependencies: @@ -16591,7 +16654,7 @@ snapshots: tar-stream@3.2.2: dependencies: b4a: 1.9.0 - bare-fs: 4.8.2 + bare-fs: 4.8.4 fast-fifo: 1.3.2 streamx: 2.28.1 transitivePeerDependencies: @@ -16618,7 +16681,7 @@ snapshots: terser@5.51.2: dependencies: '@jridgewell/source-map': 0.3.11 - acorn: 8.18.0 + acorn: 8.19.0 commander: 2.20.3 source-map-support: 0.5.21 @@ -16651,7 +16714,7 @@ snapshots: tiny-inflate@1.0.3: {} - tinybench@6.2.0: {} + tinybench@6.2.2: {} tinyexec@1.3.1: {} @@ -16664,15 +16727,15 @@ snapshots: tldts-core@6.1.86: {} - tldts-core@7.4.16: {} + tldts-core@7.4.18: {} tldts@6.1.86: dependencies: tldts-core: 6.1.86 - tldts@7.4.16: + tldts@7.4.18: dependencies: - tldts-core: 7.4.16 + tldts-core: 7.4.18 tmp@0.2.7: {} @@ -16690,7 +16753,7 @@ snapshots: tough-cookie@6.0.2: dependencies: - tldts: 7.4.16 + tldts: 7.4.18 tr46@0.0.3: {} @@ -16824,6 +16887,8 @@ snapshots: undici@8.11.2: {} + undici@8.12.1: {} + unicode-canonical-property-names-ecmascript@2.0.1: {} unicode-match-property-ecmascript@2.0.0: @@ -16855,7 +16920,7 @@ snapshots: unpipe@1.0.0: {} - update-browserslist-db@1.3.3(browserslist@4.29.3): + update-browserslist-db@1.3.4(browserslist@4.29.3): dependencies: browserslist: 4.29.3 escalade: 3.2.0 @@ -16896,9 +16961,9 @@ snapshots: - encoding - supports-color - verdaccio-auth-memory@13.1.5(supports-color@11.0.0): + verdaccio-auth-memory@13.1.6(supports-color@11.0.0): dependencies: - '@verdaccio/core': 8.3.2 + '@verdaccio/core': 8.3.3 debug: 4.4.3(supports-color@11.0.0) transitivePeerDependencies: - supports-color @@ -16954,8 +17019,6 @@ snapshots: - react-native-b4a - supports-color - verkit@0.3.2: {} - verkit@0.5.0: {} verror@1.10.0: @@ -16964,12 +17027,12 @@ snapshots: core-util-is: 1.0.2 extsprintf: 1.3.0 - vite@8.3.3(@types/node@22.20.5)(esbuild@0.28.2)(jiti@2.7.0)(less@4.9.1(supports-color@11.0.0))(sass-embedded@1.105.1)(sass@1.105.1)(terser@5.51.2)(tsx@4.23.15)(yaml@2.9.1): + vite@8.3.4(@types/node@22.20.5)(esbuild@0.28.2)(jiti@2.7.0)(less@4.9.1(supports-color@11.0.0))(sass-embedded@1.105.1)(sass@1.105.1)(terser@5.51.2)(tsx@4.23.15)(yaml@2.9.1): dependencies: lightningcss: 1.33.0 picomatch: 4.0.7 - postcss: 8.5.28 - rolldown: 1.2.11 + postcss: 8.5.30 + rolldown: 1.2.14 tinyglobby: 0.2.17 optionalDependencies: '@types/node': 22.20.5 @@ -16983,26 +17046,26 @@ snapshots: tsx: 4.23.15 yaml: 2.9.1 - vitest@5.0.3(@opentelemetry/api@1.9.1)(@types/node@22.20.5)(@vitest/coverage-v8@5.0.3)(jsdom@30.1.2(@noble/hashes@1.8.0))(vite@8.3.3(@types/node@22.20.5)(esbuild@0.28.2)(jiti@2.7.0)(less@4.9.1(supports-color@11.0.0))(sass-embedded@1.105.1)(sass@1.105.1)(terser@5.51.2)(tsx@4.23.15)(yaml@2.9.1)): + vitest@5.0.4(@opentelemetry/api@1.9.1)(@types/node@22.20.5)(@vitest/coverage-v8@5.0.4)(jsdom@30.1.2(@noble/hashes@1.8.0))(vite@8.3.4(@types/node@22.20.5)(esbuild@0.28.2)(jiti@2.7.0)(less@4.9.1(supports-color@11.0.0))(sass-embedded@1.105.1)(sass@1.105.1)(terser@5.51.2)(tsx@4.23.15)(yaml@2.9.1)): dependencies: '@types/chai': 5.2.3 - '@vitest/mocker': 5.0.3(vite@8.3.3(@types/node@22.20.5)(esbuild@0.28.2)(jiti@2.7.0)(less@4.9.1(supports-color@11.0.0))(sass-embedded@1.105.1)(sass@1.105.1)(terser@5.51.2)(tsx@4.23.15)(yaml@2.9.1)) + '@vitest/mocker': 5.0.4(vite@8.3.4(@types/node@22.20.5)(esbuild@0.28.2)(jiti@2.7.0)(less@4.9.1(supports-color@11.0.0))(sass-embedded@1.105.1)(sass@1.105.1)(terser@5.51.2)(tsx@4.23.15)(yaml@2.9.1)) chai: 6.3.0 es-module-lexer: 2.3.2 expect-type: 1.4.0 - magic-string: 1.4.2 - obug: 2.2.1 + magic-string: 1.4.3 + obug: 3.0.0 picomatch: 4.0.7 std-env: 4.3.0 - tinybench: 6.2.0 + tinybench: 6.2.2 tinyexec: 1.3.1 tinyglobby: 0.2.17 - vite: 8.3.3(@types/node@22.20.5)(esbuild@0.28.2)(jiti@2.7.0)(less@4.9.1(supports-color@11.0.0))(sass-embedded@1.105.1)(sass@1.105.1)(terser@5.51.2)(tsx@4.23.15)(yaml@2.9.1) + vite: 8.3.4(@types/node@22.20.5)(esbuild@0.28.2)(jiti@2.7.0)(less@4.9.1(supports-color@11.0.0))(sass-embedded@1.105.1)(sass@1.105.1)(terser@5.51.2)(tsx@4.23.15)(yaml@2.9.1) why-is-node-running: 3.2.1 optionalDependencies: '@opentelemetry/api': 1.9.1 '@types/node': 22.20.5 - '@vitest/coverage-v8': 5.0.3(vitest@5.0.3) + '@vitest/coverage-v8': 5.0.4(vitest@5.0.4) jsdom: 30.1.2(@noble/hashes@1.8.0) transitivePeerDependencies: - msw @@ -17024,13 +17087,13 @@ snapshots: web-vitals@4.2.4: {} - webdriver-bidi-protocol@0.4.3: {} + webdriver-bidi-protocol@1.0.1: {} webidl-conversions@3.0.1: {} webidl-conversions@8.0.1: {} - webpack-dev-middleware@8.3.0(webpack@5.111.1(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.28)(uglify-js@3.19.3)): + webpack-dev-middleware@8.3.0(webpack@5.111.1(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.29)(uglify-js@3.19.3)): dependencies: ansi-html-community: 0.0.8 memfs: 4.80.0 @@ -17038,9 +17101,9 @@ snapshots: range-parser: 1.3.0 schema-utils: 4.5.0 optionalDependencies: - webpack: 5.111.1(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.28)(uglify-js@3.19.3) + webpack: 5.111.1(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.29)(uglify-js@3.19.3) - webpack-dev-middleware@8.3.0(webpack@5.111.1(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.29)(uglify-js@3.19.3)): + webpack-dev-middleware@8.3.0(webpack@5.111.1(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.30)(uglify-js@3.19.3)): dependencies: ansi-html-community: 0.0.8 memfs: 4.80.0 @@ -17048,9 +17111,9 @@ snapshots: range-parser: 1.3.0 schema-utils: 4.5.0 optionalDependencies: - webpack: 5.111.1(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.29)(uglify-js@3.19.3) + webpack: 5.111.1(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.30)(uglify-js@3.19.3) - webpack-dev-server@6.0.0(bufferutil@4.1.0)(supports-color@11.0.0)(utf-8-validate@6.0.6)(webpack@5.111.1(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.28)(uglify-js@3.19.3)): + webpack-dev-server@6.0.0(bufferutil@4.1.0)(supports-color@11.0.0)(utf-8-validate@6.0.6)(webpack@5.111.1(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.29)(uglify-js@3.19.3)): dependencies: '@types/bonjour': 3.5.13 '@types/connect-history-api-fallback': 1.5.4 @@ -17068,23 +17131,23 @@ snapshots: graceful-fs: 4.2.11 http-proxy-middleware: 4.2.0(supports-color@11.0.0) ipaddr.js: 2.5.0 - launch-editor: 2.14.1 + launch-editor: 2.14.2 open: 11.0.4 p-retry: 8.0.1 schema-utils: 4.5.0 selfsigned: 5.5.0 serve-index: 1.9.2(supports-color@11.0.0) tinyglobby: 0.2.17 - webpack-dev-middleware: 8.3.0(webpack@5.111.1(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.28)(uglify-js@3.19.3)) + webpack-dev-middleware: 8.3.0(webpack@5.111.1(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.29)(uglify-js@3.19.3)) ws: 8.22.0(bufferutil@4.1.0)(utf-8-validate@6.0.6) optionalDependencies: - webpack: 5.111.1(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.28)(uglify-js@3.19.3) + webpack: 5.111.1(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.29)(uglify-js@3.19.3) transitivePeerDependencies: - bufferutil - supports-color - utf-8-validate - webpack-dev-server@6.0.0(bufferutil@4.1.0)(supports-color@11.0.0)(utf-8-validate@6.0.6)(webpack@5.111.1(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.29)(uglify-js@3.19.3)): + webpack-dev-server@6.0.0(bufferutil@4.1.0)(supports-color@11.0.0)(utf-8-validate@6.0.6)(webpack@5.111.1(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.30)(uglify-js@3.19.3)): dependencies: '@types/bonjour': 3.5.13 '@types/connect-history-api-fallback': 1.5.4 @@ -17102,17 +17165,17 @@ snapshots: graceful-fs: 4.2.11 http-proxy-middleware: 4.2.0(supports-color@11.0.0) ipaddr.js: 2.5.0 - launch-editor: 2.14.1 + launch-editor: 2.14.2 open: 11.0.4 p-retry: 8.0.1 schema-utils: 4.5.0 selfsigned: 5.5.0 serve-index: 1.9.2(supports-color@11.0.0) tinyglobby: 0.2.17 - webpack-dev-middleware: 8.3.0(webpack@5.111.1(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.29)(uglify-js@3.19.3)) + webpack-dev-middleware: 8.3.0(webpack@5.111.1(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.30)(uglify-js@3.19.3)) ws: 8.22.0(bufferutil@4.1.0)(utf-8-validate@6.0.6) optionalDependencies: - webpack: 5.111.1(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.29)(uglify-js@3.19.3) + webpack: 5.111.1(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.30)(uglify-js@3.19.3) transitivePeerDependencies: - bufferutil - supports-color @@ -17126,12 +17189,12 @@ snapshots: webpack-sources@3.6.0: {} - webpack-subresource-integrity@5.1.0(webpack@5.111.1(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.29)(uglify-js@3.19.3)): + webpack-subresource-integrity@5.1.0(webpack@5.111.1(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.30)(uglify-js@3.19.3)): dependencies: typed-assert: 1.0.9 - webpack: 5.111.1(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.29)(uglify-js@3.19.3) + webpack: 5.111.1(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.30)(uglify-js@3.19.3) - webpack@5.111.1(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.28)(uglify-js@3.19.3): + webpack@5.111.1(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.29)(uglify-js@3.19.3): dependencies: '@types/estree': 1.0.9 '@types/json-schema': 7.0.15 @@ -17145,7 +17208,7 @@ snapshots: events: 3.3.0 graceful-fs: 4.2.11 mime-db: 1.54.0 - minimizer-webpack-plugin: 5.13.0(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.28)(uglify-js@3.19.3)(webpack@5.111.1(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.28)(uglify-js@3.19.3)) + minimizer-webpack-plugin: 5.13.0(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.29)(uglify-js@3.19.3)(webpack@5.111.1(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.29)(uglify-js@3.19.3)) schema-utils: 4.5.0 tapable: 2.3.3 watchpack: 2.5.2 @@ -17168,7 +17231,7 @@ snapshots: - svgo - uglify-js - webpack@5.111.1(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.29)(uglify-js@3.19.3): + webpack@5.111.1(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.30)(uglify-js@3.19.3): dependencies: '@types/estree': 1.0.9 '@types/json-schema': 7.0.15 @@ -17182,7 +17245,7 @@ snapshots: events: 3.3.0 graceful-fs: 4.2.11 mime-db: 1.54.0 - minimizer-webpack-plugin: 5.13.0(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.29)(uglify-js@3.19.3)(webpack@5.111.1(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.29)(uglify-js@3.19.3)) + minimizer-webpack-plugin: 5.13.0(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.30)(uglify-js@3.19.3)(webpack@5.111.1(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.30)(uglify-js@3.19.3)) schema-utils: 4.5.0 tapable: 2.3.3 watchpack: 2.5.2 @@ -17390,6 +17453,10 @@ snapshots: dependencies: '@yuku-toolchain/types': 0.10.2 + yuku-ast@0.19.0: + dependencies: + '@yuku-toolchain/types': 0.19.0 + yuku-codegen@0.10.2: dependencies: '@yuku-toolchain/types': 0.10.2 @@ -17407,6 +17474,27 @@ snapshots: '@yuku-codegen/binding-win32-arm64': 0.10.2 '@yuku-codegen/binding-win32-x64': 0.10.2 + yuku-codegen@0.19.0: + dependencies: + '@yuku-toolchain/types': 0.19.0 + + yuku-core@0.19.0: + dependencies: + '@yuku-toolchain/types': 0.19.0 + optionalDependencies: + '@yuku-core/binding-android-arm64': 0.19.0 + '@yuku-core/binding-darwin-arm64': 0.19.0 + '@yuku-core/binding-darwin-x64': 0.19.0 + '@yuku-core/binding-freebsd-x64': 0.19.0 + '@yuku-core/binding-linux-arm-gnu': 0.19.0 + '@yuku-core/binding-linux-arm-musl': 0.19.0 + '@yuku-core/binding-linux-arm64-gnu': 0.19.0 + '@yuku-core/binding-linux-arm64-musl': 0.19.0 + '@yuku-core/binding-linux-x64-gnu': 0.19.0 + '@yuku-core/binding-linux-x64-musl': 0.19.0 + '@yuku-core/binding-win32-arm64': 0.19.0 + '@yuku-core/binding-win32-x64': 0.19.0 + yuku-parser@0.10.2: dependencies: '@yuku-toolchain/types': 0.10.2 @@ -17425,8 +17513,13 @@ snapshots: '@yuku-parser/binding-win32-arm64': 0.10.2 '@yuku-parser/binding-win32-x64': 0.10.2 - zod@3.25.76: {} + yuku-parser@0.19.0: + dependencies: + '@yuku-toolchain/types': 0.19.0 + yuku-core: 0.19.0 zod@4.6.5: {} + zod@4.6.7: {} + zone.js@0.16.3: {} diff --git a/tests/e2e/ng-snapshot/package.json b/tests/e2e/ng-snapshot/package.json index a3e554ca1640..e02c1dcd10e4 100644 --- a/tests/e2e/ng-snapshot/package.json +++ b/tests/e2e/ng-snapshot/package.json @@ -2,21 +2,21 @@ "description": "snapshot versions of Angular for e2e testing", "private": true, "dependencies": { - "@angular/animations": "github:angular/animations-builds#3ec405d1cb11a6f18eacdd7e09378cd097a9a427", - "@angular/cdk": "github:angular/cdk-builds#8eedcdb798ec78b2c0191953b46804f54296ed4f", - "@angular/common": "github:angular/common-builds#bae1a4226a83f02bcb77b6ef447e039d8b5081f3", - "@angular/compiler": "github:angular/compiler-builds#fc8fced0b21574f7846b999a751511f9a453e0cd", - "@angular/compiler-cli": "github:angular/compiler-cli-builds#20b42c8d2e12f28eea2376ff0b0f1f5ec59cac63", - "@angular/core": "github:angular/core-builds#080d86fc456c2029952d72e43ae5e019374a3546", - "@angular/forms": "github:angular/forms-builds#b8ff96856639a22bb3dec5c8bebb562c5395c638", - "@angular/language-service": "github:angular/language-service-builds#9e0a5e50a0f37d5671fc2478ebf2626399fb23f4", - "@angular/localize": "github:angular/localize-builds#fec5dc5c69698fbf47aa794ac8fd80a2baa9b1d5", - "@angular/material": "github:angular/material-builds#72d50c0fd59ea4d1f7fc2053d74d4f2610503730", - "@angular/material-moment-adapter": "github:angular/material-moment-adapter-builds#30012ad773e0536aa4e5ff009c1b65ec8bb1e5f2", - "@angular/platform-browser": "github:angular/platform-browser-builds#ca08a1a24ac892344fb1f69c6f5ac15eb504370b", - "@angular/platform-browser-dynamic": "github:angular/platform-browser-dynamic-builds#163f8f1af3365efeab12b2656ad229a8d76e39b9", - "@angular/platform-server": "github:angular/platform-server-builds#fe61afa721e53041b0bac92f0241a82326d72a47", - "@angular/router": "github:angular/router-builds#7df611a733d4a35d41fd4b5596e0a9800cc67e8e", - "@angular/service-worker": "github:angular/service-worker-builds#3ef11b4fbbd965535f0f45de2ba41a4e3886b918" + "@angular/animations": "github:angular/animations-builds#43efef5cbf38f5261191e5c11d7f49c276428137", + "@angular/cdk": "github:angular/cdk-builds#43c02174c1dc0f4b74a85342bbd9f43d040bf1ae", + "@angular/common": "github:angular/common-builds#89d9b5d43c81051c59a33d24ccccd75b51f6dc48", + "@angular/compiler": "github:angular/compiler-builds#e91181d83e7086debe8f32f21bfa434c4bd94afd", + "@angular/compiler-cli": "github:angular/compiler-cli-builds#bfd2191e1955c38a1ce52aa467508702d132e7ba", + "@angular/core": "github:angular/core-builds#31cf6f74f000a8499b3ce204ad9b339e830dcc39", + "@angular/forms": "github:angular/forms-builds#1dbb38cca305af00e3362cb59c5293b44a7ae7f1", + "@angular/language-service": "github:angular/language-service-builds#54ce2dee3572da8657674dc965d010fd37b6d15b", + "@angular/localize": "github:angular/localize-builds#bcdfcaf668fd983db3496cdb696ca8b3aa2f68aa", + "@angular/material": "github:angular/material-builds#2c82c94514f7cb2bc3dafd3b5db2160aa9c01517", + "@angular/material-moment-adapter": "github:angular/material-moment-adapter-builds#728a7be6d90391791e0967357a07959d2a4fa3d3", + "@angular/platform-browser": "github:angular/platform-browser-builds#a1b99915c2e91ab779ee0443904bd0194b8e2fb0", + "@angular/platform-browser-dynamic": "github:angular/platform-browser-dynamic-builds#cdfb691b31a234662977f12f0982b880035bb72e", + "@angular/platform-server": "github:angular/platform-server-builds#9e3f315c175ed493a83d91b2d1743582e4934ab3", + "@angular/router": "github:angular/router-builds#972a0d117fd048a478e2cb035ef5b442e8cc5ceb", + "@angular/service-worker": "github:angular/service-worker-builds#f3e0f7d39cadb10a0257cf826cc0519e7809e8e2" } } diff --git a/tests/e2e/tests/i18n/extract-ivy.ts b/tests/e2e/tests/i18n/extract-ivy.ts index 382051e83cce..cf0d23da08e3 100644 --- a/tests/e2e/tests/i18n/extract-ivy.ts +++ b/tests/e2e/tests/i18n/extract-ivy.ts @@ -1,8 +1,9 @@ import { join } from 'node:path'; import { getGlobalVariable } from '../../utils/env'; -import { expectFileToMatch, writeFile } from '../../utils/fs'; +import { deleteFile, expectFileToMatch, writeFile } from '../../utils/fs'; import { uninstallPackage } from '../../utils/packages'; import { ng } from '../../utils/process'; +import { updateJsonFile } from '../../utils/project'; import { expectToFail } from '../../utils/utils'; import { readNgVersion } from '../../utils/version'; @@ -52,5 +53,13 @@ export default async function () { await expectFileToMatch('messages.xlf', 'Hello world'); + // Template messages should also be extracted when the build target disables AOT + await updateJsonFile('angular.json', (workspaceJson) => { + workspaceJson.projects['test-project'].architect.build.options.aot = false; + }); + await deleteFile('messages.xlf'); + await ng('extract-i18n'); + await expectFileToMatch('messages.xlf', 'Hello world'); + await uninstallPackage('@angular/localize'); }