diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1b8efbb..e84d00e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -5,6 +5,12 @@ on: branches: [main] pull_request: +# The job builds and inspects; it never writes to the repository. Without this +# block it would inherit whatever the repository default happens to be, which is +# the only privileged path in the file. +permissions: + contents: read + jobs: build: runs-on: ubuntu-latest @@ -12,6 +18,21 @@ jobs: - name: Checkout uses: actions/checkout@v4 + # The vendored OpenAPI spec is a copy of the engine's. Without the source + # beside it there is nothing to compare against, and a drift check that + # cannot see the source is a check that always passes. + # + # Full history, because the check reads the spec at the commit recorded in + # openapi-source.json rather than at whatever is currently on main. See + # that script's header for why the pin exists. + - name: Checkout dpp-engine (source of the vendored API spec) + uses: actions/checkout@v4 + with: + repository: odal-node/dpp-engine + path: .dpp-engine + fetch-depth: 0 + persist-credentials: false + - name: Enable Corepack run: corepack enable @@ -29,3 +50,48 @@ jobs: - name: Check run: pnpm -r check + + # Markdown link targets are opaque strings to `astro check`. This reads + # the built output, so it sees what is actually published — including + # cross-site links, which neither site's own tooling can resolve. + - name: Check links + run: pnpm run check:links + + # This repository is public. Internal decision-record numbers and paths + # into the private docs repo must not appear in it — including inside + # `public/`, which is served verbatim. + - name: Check for internal-vocabulary leakage + run: pnpm run check:leakage + + - name: Check the vendored API spec against the engine + run: pnpm run check:openapi + env: + DPP_ENGINE_DIR: ${{ github.workspace }}/.dpp-engine + + # Reports, without failing, how far the pin is behind the engine's main. + # Deliberately not a gate: the pinned copy being *correct* is this repo's + # problem and is enforced above, but the pin being *old* is a release- + # cadence judgement, and failing on it would redden every pull request + # here every time the engine merges anything. The number is printed on + # every run so the drift that started this — a published spec fifteen + # endpoints behind, with nothing to reveal it — cannot go unnoticed again. + - name: Report how far the API-spec pin is behind + if: always() + run: | + PIN=$(node -p "require('./site/dpp-docs/openapi-source.json').commit") + cd .dpp-engine + BEHIND=$(git rev-list --count "$PIN"..origin/main -- api/openapi.yaml 2>/dev/null || echo "?") + if [ "$BEHIND" = "0" ]; then + echo "API spec pin is current with the engine's main branch." + else + echo "::notice::The vendored API spec is pinned $BEHIND commit(s) behind changes to api/openapi.yaml on the engine's main. Run 'pnpm run sync:openapi' to bring it forward." + git --no-pager log --oneline "$PIN"..origin/main -- api/openapi.yaml || true + fi + + # Fails on a high-severity advisory. The remaining advisories are all + # build-time or dev-server issues in transitive dependencies, which do not + # reach a static deploy — so this is set to fail on `critical` today and + # should be tightened to `high` once those clear. The point is that a new + # advisory becomes visible on the pull request that introduces it. + - name: Audit dependencies + run: pnpm audit --audit-level critical diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml new file mode 100644 index 0000000..a671e2f --- /dev/null +++ b/.github/workflows/deploy.yml @@ -0,0 +1,81 @@ +name: Deploy + +on: + workflow_dispatch: + push: + branches: + - main + paths: + - 'site/**' + - 'packages/**' + - 'public/**' + - '.github/workflows/deploy.yml' + +jobs: + purge-cloudflare-cache-landing: + name: Purge Cloudflare Cache (landing) + runs-on: ubuntu-latest + environment: ${{ vars.CLOUDFLARE_ENVIRONMENT_LANDING || 'landing' }} + + steps: + - name: Check Cloudflare secrets + id: cloudflare + env: + CLOUDFLARE_ZONE_ID: ${{ secrets.CLOUDFLARE_ZONE_ID }} + CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} + run: | + if [ -n "$CLOUDFLARE_ZONE_ID" ] && [ -n "$CLOUDFLARE_API_TOKEN" ]; then + echo "ready=true" >> "$GITHUB_OUTPUT" + else + echo "ready=false" >> "$GITHUB_OUTPUT" + fi + + - name: Purge Cloudflare cache + if: steps.cloudflare.outputs.ready == 'true' + env: + CLOUDFLARE_ZONE_ID: ${{ secrets.CLOUDFLARE_ZONE_ID }} + CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} + run: | + curl --fail --silent --show-error \ + -X POST "https://api.cloudflare.com/client/v4/zones/${CLOUDFLARE_ZONE_ID}/purge_cache" \ + -H "Authorization: Bearer ${CLOUDFLARE_API_TOKEN}" \ + -H "Content-Type: application/json" \ + --data '{"purge_everything":true}' + + - name: Skip purge if secrets are missing + if: steps.cloudflare.outputs.ready != 'true' + run: echo "Skipping Cloudflare cache purge because the required secrets are not configured for the landing environment." + + purge-cloudflare-cache-docs: + name: Purge Cloudflare Cache (docs) + runs-on: ubuntu-latest + environment: ${{ vars.CLOUDFLARE_ENVIRONMENT_DOCS || 'docs' }} + + steps: + - name: Check Cloudflare secrets + id: cloudflare + env: + CLOUDFLARE_ZONE_ID: ${{ secrets.CLOUDFLARE_ZONE_ID }} + CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} + run: | + if [ -n "$CLOUDFLARE_ZONE_ID" ] && [ -n "$CLOUDFLARE_API_TOKEN" ]; then + echo "ready=true" >> "$GITHUB_OUTPUT" + else + echo "ready=false" >> "$GITHUB_OUTPUT" + fi + + - name: Purge Cloudflare cache + if: steps.cloudflare.outputs.ready == 'true' + env: + CLOUDFLARE_ZONE_ID: ${{ secrets.CLOUDFLARE_ZONE_ID }} + CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} + run: | + curl --fail --silent --show-error \ + -X POST "https://api.cloudflare.com/client/v4/zones/${CLOUDFLARE_ZONE_ID}/purge_cache" \ + -H "Authorization: Bearer ${CLOUDFLARE_API_TOKEN}" \ + -H "Content-Type: application/json" \ + --data '{"purge_everything":true}' + + - name: Skip purge if secrets are missing + if: steps.cloudflare.outputs.ready != 'true' + run: echo "Skipping Cloudflare cache purge because the required secrets are not configured for the docs environment." \ No newline at end of file diff --git a/.gitignore b/.gitignore index 23f201a..019076b 100644 --- a/.gitignore +++ b/.gitignore @@ -26,3 +26,7 @@ Thumbs.db # CI / deploy artefacts .wrangler/ .cloudflare/ + +# local-only assets (never publish, never commit — this repo is public) +deprecated/ +.claude/ diff --git a/package.json b/package.json index 2a4d075..f108ca2 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,10 @@ "build:docs": "pnpm --filter dpp-docs build", "build": "pnpm -r build", "check": "pnpm -r check", + "check:links": "node scripts/check-links.mjs", + "check:leakage": "node scripts/check-leakage.mjs", + "check:openapi": "pnpm --filter dpp-docs run check:openapi", + "sync:openapi": "pnpm --filter dpp-docs run sync:openapi", "preview:landing": "pnpm --filter dpp-landing preview", "preview:docs": "pnpm --filter dpp-docs preview", "clean": "pnpm -r exec rm -rf dist .astro node_modules && rm -rf node_modules" diff --git a/packages/brand-tokens/src/colors.ts b/packages/brand-tokens/src/colors.ts index f2675b8..d6ac8a1 100644 --- a/packages/brand-tokens/src/colors.ts +++ b/packages/brand-tokens/src/colors.ts @@ -6,12 +6,12 @@ * Starlight CSS-variable overrides import from here (directly, or via the * mirrored CSS custom properties in `tokens.css`). * - * See BRAND.md section 4.1 for the editorial rationale behind each scale. + * Each scale is tuned for a specific surface; see the contrast notes below. */ /** * Primary scale — navy/ice blue family, anchored on the logo - * (decision 2026-06-10, docs/redesign/DESIGN_SPEC.md §1). + * (decision 2026-06-10). * 50–300 are ice tints (the logo stroke is 300); 500/600 are the interactive * action blues (AA on white); 800/900 are the navy surfaces (logo field = 900). */ diff --git a/packages/brand-tokens/src/spacing.ts b/packages/brand-tokens/src/spacing.ts index a8f9020..4510743 100644 --- a/packages/brand-tokens/src/spacing.ts +++ b/packages/brand-tokens/src/spacing.ts @@ -1,7 +1,7 @@ /** * Odal Node — spacing and radius tokens. * - * 8-pixel base scale with a 12px outlier (see BRAND.md section 4.3). + * 8-pixel base scale with a 12px outlier. */ export const spacing = { diff --git a/packages/brand-tokens/src/tokens.css b/packages/brand-tokens/src/tokens.css index 7cdeb8a..2cae494 100644 --- a/packages/brand-tokens/src/tokens.css +++ b/packages/brand-tokens/src/tokens.css @@ -8,7 +8,7 @@ * that does `@import "tailwindcss"; @import "@odal/brand-tokens/tokens.css";` * gets utility classes for the full brand palette without further config. * - * Palette decision 2026-06-10 (docs/redesign/DESIGN_SPEC.md §1): the brand + * Palette decision 2026-06-10: the brand * colour system follows the logo — navy field (#080C2C) + ice-blue strokes * (#B7D4F0) — with a darkened action blue for interactive elements so links * and buttons hold AA contrast on white. The former green scale is retired. diff --git a/packages/brand-tokens/src/typography.ts b/packages/brand-tokens/src/typography.ts index 444628d..31d6e6b 100644 --- a/packages/brand-tokens/src/typography.ts +++ b/packages/brand-tokens/src/typography.ts @@ -1,7 +1,7 @@ /** * Odal Node — typography tokens. * - * System-stack-first. No web fonts. See BRAND.md section 4.2 for rationale. + * System-stack-first. No web fonts, so no page issues a third-party font request. */ export const fontFamily = { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 0b248f7..f34bd16 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -13,8 +13,8 @@ importers: site/dpp-docs: dependencies: '@astrojs/starlight': - specifier: ^0.30.6 - version: 0.30.6(astro@5.18.2(@types/node@24.12.4)(jiti@2.7.0)(lightningcss@1.32.0)(rollup@4.60.4)(typescript@5.9.3)(yaml@2.9.0)) + specifier: ^0.41.7 + version: 0.41.7(@astrojs/markdown-remark@7.2.4)(astro@7.2.4(@astrojs/markdown-remark@7.2.4)(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)(@types/node@24.12.4)(jiti@2.7.0)(yaml@2.9.0))(typescript@5.9.3) '@odal/brand-tokens': specifier: workspace:* version: link:../../packages/brand-tokens @@ -22,8 +22,8 @@ importers: specifier: ^1.62.5 version: 1.62.5(tailwindcss@4.3.0)(typescript@5.9.3)(zod@4.4.3) astro: - specifier: ^5 - version: 5.18.2(@types/node@24.12.4)(jiti@2.7.0)(lightningcss@1.32.0)(rollup@4.60.4)(typescript@5.9.3)(yaml@2.9.0) + specifier: ^7.2.4 + version: 7.2.4(@astrojs/markdown-remark@7.2.4)(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)(@types/node@24.12.4)(jiti@2.7.0)(yaml@2.9.0) vue: specifier: ^3.5.39 version: 3.5.39(typescript@5.9.3) @@ -36,7 +36,7 @@ importers: version: 5.9.3 vite-plugin-static-copy: specifier: ^4.1.0 - version: 4.1.0(vite@6.4.2(@types/node@24.12.4)(jiti@2.7.0)(lightningcss@1.32.0)(yaml@2.9.0)) + version: 4.1.0(vite@8.2.1(@types/node@24.12.4)(esbuild@0.28.2)(jiti@2.7.0)(yaml@2.9.0)) site/dpp-landing: dependencies: @@ -48,10 +48,10 @@ importers: version: link:../../packages/brand-tokens '@tailwindcss/vite': specifier: ^4 - version: 4.3.0(vite@6.4.2(@types/node@24.12.4)(jiti@2.7.0)(lightningcss@1.32.0)(yaml@2.9.0)) + version: 4.3.0(vite@8.2.1(@types/node@24.12.4)(esbuild@0.28.2)(jiti@2.7.0)(yaml@2.9.0)) astro: - specifier: ^5 - version: 5.18.2(@types/node@24.12.4)(jiti@2.7.0)(lightningcss@1.32.0)(rollup@4.60.4)(typescript@5.9.3)(yaml@2.9.0) + specifier: ^7.2.4 + version: 7.2.4(@astrojs/markdown-remark@7.2.4)(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)(@types/node@24.12.4)(jiti@2.7.0)(yaml@2.9.0) tailwindcss: specifier: ^4 version: 4.3.0 @@ -59,15 +59,12 @@ importers: '@astrojs/check': specifier: ^0.9 version: 0.9.9(prettier@3.8.3)(typescript@5.9.3) - '@types/react': - specifier: ^19.2.16 - version: 19.2.16 typescript: specifier: ^5 version: 5.9.3 vite-plugin-static-copy: specifier: ^4.1.0 - version: 4.1.0(vite@6.4.2(@types/node@24.12.4)(jiti@2.7.0)(lightningcss@1.32.0)(yaml@2.9.0)) + version: 4.1.0(vite@8.2.1(@types/node@24.12.4)(esbuild@0.28.2)(jiti@2.7.0)(yaml@2.9.0)) packages: @@ -99,11 +96,76 @@ packages: peerDependencies: typescript: ^5.0.0 || ^6.0.0 + '@astrojs/compiler-binding-darwin-arm64@0.3.2': + resolution: {integrity: sha512-MM8tn8CSimcfytaOla4b6acN8mKWiL/rlAA1fpT3/Wl7dNGSE4y8FjTN/zJVNnb63CsLWG5zZwCt01TXtDKh9g==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [darwin] + + '@astrojs/compiler-binding-darwin-x64@0.3.2': + resolution: {integrity: sha512-2lXOlzf8xb7jLomRsf/aswh61/NnGusynB2OwFkK6k4pmOtpfXMYnG0PLfXrEvxXYj69NdCnmUYXtHDd+JOOag==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [darwin] + + '@astrojs/compiler-binding-linux-arm64-gnu@0.3.2': + resolution: {integrity: sha512-BmU3kWj7qnLrd4vzm49zFEPJ5oFnn1tCT4Vt9hZbqdU5Cmb8GZl7fn6VFsnNfe7B18a2gIFtVzbLINtYl5kBjQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@astrojs/compiler-binding-linux-arm64-musl@0.3.2': + resolution: {integrity: sha512-f0heT9ZZEseSu5bHCeb80eL2DH07ArE6U9xi1WT/PEusNjzPmEr3GJsjG1tRLo5VYUUYX7h3ScaqGmGrMOVGmw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@astrojs/compiler-binding-linux-x64-gnu@0.3.2': + resolution: {integrity: sha512-M8fOUt0itRpqiGyoEA/ij184s8O+hqbCz3+YozRusOOM3osgGljpDThhbKAJjqh82wOo6FioQ4w8PBvU1XMD5Q==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@astrojs/compiler-binding-linux-x64-musl@0.3.2': + resolution: {integrity: sha512-/Kebk8sO6HnLeSd691JkaAPfN7CqR9/KEXmWvyNPkaKNGmj8rTZ/lf2uXtnPu92Lan84UrKdIKVPy1fSo2encQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [musl] + + '@astrojs/compiler-binding-wasm32-wasi@0.3.2': + resolution: {integrity: sha512-pUA6xbcOSB7DhfzIArB8BCAkFfAIqriiR7zl5zOStd6oU2G0kIKj+GUdGnyXyhfiv881Hffyk5tC0mR18sDjDw==} + engines: {node: '>=14.0.0'} + cpu: [wasm32] + + '@astrojs/compiler-binding-win32-arm64-msvc@0.3.2': + resolution: {integrity: sha512-ESruf+6Qkl1trHUFxI6GSf6t52j8yN2kCNSzMWdzt7V/T09tFHrYzrVaJQohb2C9bJUH76pNvX6Zb51+xCQc9Q==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [win32] + + '@astrojs/compiler-binding-win32-x64-msvc@0.3.2': + resolution: {integrity: sha512-wzzVrEbOwbsLWOdEbocskjMRx2aZPxJ7ZbmL+jnpBamFwmigm+2M/wzuM6JWncocgYwLic1csSpalBh96kQKXA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [win32] + + '@astrojs/compiler-binding@0.3.2': + resolution: {integrity: sha512-8w/9CWmYrAJJ8N0SY3O43ws2BgxoW6u3QsD8u2mE140lMYAlwh+tlNoUeSBq22wVheFuiBbR212l6ixZ2IIgCQ==} + engines: {node: ^20.19.0 || >=22.12.0} + + '@astrojs/compiler-rs@0.3.2': + resolution: {integrity: sha512-xlx/T7JovIKduu4ucbTQUxQ5+Q8wxkHxhLjnZk3VlJbhbQ9RLvvuDk1p2YYFYFQ5y14dVm3FGGO4isQXa4F+Tg==} + engines: {node: '>=22.12.0'} + '@astrojs/compiler@2.13.1': resolution: {integrity: sha512-f3FN83d2G/v32ipNClRKgYv30onQlMZX1vCeZMjPsMMPl1mDpmbl0+N5BYo4S/ofzqJyS5hvwacEo0CCVDn/Qg==} - '@astrojs/internal-helpers@0.7.6': - resolution: {integrity: sha512-GOle7smBWKfMSP8osUIGOlB5kaHdQLV3foCsf+5Q9Wsuu+C6Fs3Ez/ttXmhjZ1HkSgsogcM1RXSjjOVieHq16Q==} + '@astrojs/internal-helpers@0.10.4': + resolution: {integrity: sha512-nozZSy/mKYLqe4YrqbKtdOszedAfXYCtw3wZ0d+CAjz4GqQ4L9rl1ltIL5BlgwmYVinJg/RZ0MgGuWOdlyRZlA==} '@astrojs/language-server@2.16.10': resolution: {integrity: sha512-87VQ/5GSdHlRnUA+hGuerYyIGAj+9RbZmATyuKLEUePinUXhQ5YkRnRrHhOD9sSi5JOErLjrLkHnfZFEvGrV8w==} @@ -117,29 +179,40 @@ packages: prettier-plugin-astro: optional: true - '@astrojs/markdown-remark@6.3.11': - resolution: {integrity: sha512-hcaxX/5aC6lQgHeGh1i+aauvSwIT6cfyFjKWvExYSxUhZZBBdvCliOtu06gbQyhbe0pGJNoNmqNlQZ5zYUuIyQ==} + '@astrojs/markdown-remark@7.2.4': + resolution: {integrity: sha512-MvspGMynWKAjTe4/lTUdmBPHIFKNVLTCF6UlyWGogTGzNrTvjD+D4n48k7h8swxsEPKHK2TwxkZO7uoaCv1Pow==} - '@astrojs/mdx@4.3.14': - resolution: {integrity: sha512-FBrqJQORVm+rkRa2TS5CjU9PBA6hkhrwLVBSS9A77gN2+iehvjq1w6yya/d0YKC7osiVorKkr3Qd9wNbl0ZkGA==} - engines: {node: 18.20.8 || ^20.3.0 || >=22.0.0} + '@astrojs/markdown-satteri@0.3.7': + resolution: {integrity: sha512-NHcHbrKW/opbZnTZQ5nH293BdcK2VV0tjuzI88CLnvy2njiEJVwUQ5KFnYd4NoWgHJCY/I1CyjGWCR4Vv3khXQ==} + + '@astrojs/mdx@7.0.7': + resolution: {integrity: sha512-hv+NJh2s+/KDrjXaYNOaS344e3M2ekMXHBR7x9EwBwE3VvE1y/9Ifh1rhR1H2zK2sMgXoUnakI9e9ZeCKPX9/Q==} + engines: {node: '>=22.12.0'} peerDependencies: - astro: ^5.0.0 + '@astrojs/markdown-satteri': ^0.3.1 + astro: ^7.0.0 + peerDependenciesMeta: + '@astrojs/markdown-satteri': + optional: true - '@astrojs/prism@3.3.0': - resolution: {integrity: sha512-q8VwfU/fDZNoDOf+r7jUnMC2//H2l0TuQ6FkGJL8vD8nw/q5KiL3DS1KKBI3QhI9UQhpJ5dc7AtqfbXWuOgLCQ==} - engines: {node: 18.20.8 || ^20.3.0 || >=22.0.0} + '@astrojs/prism@4.0.2': + resolution: {integrity: sha512-KTivpmnz6lDsC6o9H4+DNm2SrE/GHzw8cNAvEJwAvUT+eoaEnn/4NtbDNfRRaxaJHdp15gf+tfHAWiXR4wB3BA==} + engines: {node: '>=22.12.0'} '@astrojs/sitemap@3.7.3': resolution: {integrity: sha512-f8euLVsyeAmAkSm/1M2Kb8sL8byQmfgbvBNaHFItCheTj/IpiJYSEWVcqDHZ/yEHxiS7+w87mQkzwZaPHmk5GA==} - '@astrojs/starlight@0.30.6': - resolution: {integrity: sha512-/AoLXjPPD1MqixkTd2Lp3qahSzfCejePWHZQ3+fDjj1CuXI7Gjrr5bR3zNV0b9tynloPAIBM0HOyBNEGAo9uAQ==} + '@astrojs/starlight@0.41.7': + resolution: {integrity: sha512-579VJuZgo20UpNQPm9EIez5W3DFSrD16uiV2YX6rUlpLtjgKSdnc69TxVTZXn4AtI2B731TI2qhW1O3K+vwtrQ==} peerDependencies: - astro: ^5.0.0 + '@astrojs/markdown-remark': ^7.2.0 + astro: ^7.0.2 + peerDependenciesMeta: + '@astrojs/markdown-remark': + optional: true - '@astrojs/telemetry@3.3.0': - resolution: {integrity: sha512-UFBgfeldP06qu6khs/yY+q1cDAaArM2/7AEIqQ9Cuvf7B1hNLq0xDrZkct+QoIGyjq56y8IaE2I3CTvG99mlhQ==} + '@astrojs/telemetry@3.3.3': + resolution: {integrity: sha512-C1TLn5sPJr0x4vk56piHWKbnqlEB8BKyte5Y45V02U+D7BGO5eMqZDH5aPjnkXQWJggvmsTXxH03QMZ9NgWLzQ==} engines: {node: 18.20.8 || ^20.3.0 || >=22.0.0} '@astrojs/yaml2ts@0.2.4': @@ -158,18 +231,109 @@ packages: engines: {node: '>=6.0.0'} hasBin: true - '@babel/runtime@7.29.7': - resolution: {integrity: sha512-Nq8OhGWiZIZGV6hLHoyAKLLcJihP/xFeBMGJoUrxTX2psI8dCifzLhZISFb+VWS3wFMRDmCGw5R+dOySCqPLhw==} - engines: {node: '>=6.9.0'} - '@babel/types@7.29.7': resolution: {integrity: sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==} engines: {node: '>=6.9.0'} + '@bruits/satteri-darwin-arm64@0.9.5': + resolution: {integrity: sha512-iw4nZgx9v30lWo/MTngQqi1pI78KI0DnkSm+lVJGYdmPLgAyDNJigVhpG42/Iq55A6c1Ll8q66ljyyRiQUxwow==} + cpu: [arm64] + os: [darwin] + + '@bruits/satteri-darwin-x64@0.10.4': + resolution: {integrity: sha512-pe8jABpoGekJIDT5VoGdVGSo949MJEVMOqKSTNe3/CxvTe/ii92FUBsx8MSbhiBgXZAZdpdgIs7hMOHe7qj9tQ==} + cpu: [x64] + os: [darwin] + + '@bruits/satteri-darwin-x64@0.9.5': + resolution: {integrity: sha512-6T26Z5Kf3cFW2PSlk9p7zT7yVxvuBSiJvYyz9u8KjYwMTqZyIDOj2wDyNpxKV4+6yUVG7rddq2QwvG/8LJA2+Q==} + cpu: [x64] + os: [darwin] + + '@bruits/satteri-linux-arm64-gnu@0.10.4': + resolution: {integrity: sha512-XG1D35zCxklN400MPDYZAilX7ZLYLlh++pD8QL9aRXTGm6qINI5k9AFy2L6O/Se0epw07M9Zgtq1Ab3urTkL9g==} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@bruits/satteri-linux-arm64-gnu@0.9.5': + resolution: {integrity: sha512-u51id17uJwNEMK9nBlICsq6U31c+XVqQueVBkwRIzZG+gMpS8TOJctt5h5Wz33Z8xnMdTd+adtACVz0yHgGuOA==} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@bruits/satteri-linux-arm64-musl@0.10.4': + resolution: {integrity: sha512-jeB81UUbbAWXJkSMd5Xd6cK1J/NRaSWICsks3RzV5eQZQoYcX7/heeSBd07Y8p0uFsLL6aMWLOglLx86ydarFA==} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@bruits/satteri-linux-arm64-musl@0.9.5': + resolution: {integrity: sha512-v39HxiwGC5Rqm01HksP6+5Y+xKLPlsuVFgIgpEAo+SiQ22c+mJVhS3u7Z6ePAKdhL5NJoK1xq70kLz3L13AhpQ==} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@bruits/satteri-linux-x64-gnu@0.10.4': + resolution: {integrity: sha512-X8M5JU3uui8KHPSZpvKESH1e3QLCt8JMgXg5oqzSKQxMmsebraQvImiKtFeBzVqJvinjKk86pwVRNFysgmItVw==} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@bruits/satteri-linux-x64-gnu@0.9.5': + resolution: {integrity: sha512-F3uO8uFp3pAP5ZGXttwvh57GS7s0lL953tnNdyI2gRyP4kOOkp6pyGojNJzCjkDvWI2Cvb9iNrKok3aqQPauAw==} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@bruits/satteri-linux-x64-musl@0.9.5': + resolution: {integrity: sha512-bicEqglLlz++mWyADaZoP0JY20s4vDfLjaPYgQqC+NI4zZLTOOg1T4GB8aqtc822Pqji8SQBmSrTb7CrP8i08Q==} + cpu: [x64] + os: [linux] + libc: [musl] + + '@bruits/satteri-wasm32-wasi@0.10.4': + resolution: {integrity: sha512-Upo3tIdX6haC89VEcSISc2wcuq7jtoL1CiHITUK6jKgloQSBBebfQXZ0XvyKIDVcq3EkCxcMN3FcFgQ2e+APdQ==} + engines: {node: '>=14.0.0'} + cpu: [wasm32] + + '@bruits/satteri-wasm32-wasi@0.9.5': + resolution: {integrity: sha512-zauAuMwfPnKPUkd4AFixRFpXdgKwP2mKgxrIIo2gJzW0/ZneF9dbHnLkojSpaBnCCp7VUL1hIi5WWZvB1CqmAQ==} + engines: {node: '>=14.0.0'} + cpu: [wasm32] + + '@bruits/satteri-win32-arm64-msvc@0.10.4': + resolution: {integrity: sha512-2VKmGOBnqS0QrEnpzv0Od1dc26qG/01M+s8Rr5/SuNZEUkenWwLZPVzXstI4hhyg1r/OekG485xSIT6Vslovug==} + cpu: [arm64] + os: [win32] + + '@bruits/satteri-win32-arm64-msvc@0.9.5': + resolution: {integrity: sha512-SrfE7NEsgZjBvU3c+RR6oQRu0ToXY5uVJEbieXEF0YTctIV2zAVlbaMjWLts074QCgh3a+XHWkR/lWh2VH2LUg==} + cpu: [arm64] + os: [win32] + + '@bruits/satteri-win32-x64-msvc@0.10.4': + resolution: {integrity: sha512-xYWFT0DCjNEgaHWzR8UOF4QgMKDpZtLnXzF+VS4Umg17pP1I8YuJZLfFtXdEWRxruWd+x7O7R5ZYAH7khuJ+hw==} + cpu: [x64] + os: [win32] + + '@bruits/satteri-win32-x64-msvc@0.9.5': + resolution: {integrity: sha512-5Kw9ZAtTGS8WHizyn+CJhjjfIQrw+7jcZodpmpXJjefnO15M8UexIi6JR2E5thyvsmHyhL6ZDDMUNR4bKJPd4g==} + cpu: [x64] + os: [win32] + '@capsizecss/unpack@4.0.0': resolution: {integrity: sha512-VERIM64vtTP1C4mxQ5thVT9fK0apjPFobqybMtA1UdUujWka24ERHbRHFGmpbbhp73MhV+KSsHQH9C6uOTdEQA==} engines: {node: '>=18'} + '@clack/core@1.4.3': + resolution: {integrity: sha512-/kr3UWNtdJfxZtPgDqUOmG2pvwlmcLGheex5yiZKdwbzZJxhV+HMNR9QNmyY5cGwTNV6LrR7Jtp+KjhUAP1qBQ==} + engines: {node: '>= 20.12.0'} + + '@clack/prompts@1.7.0': + resolution: {integrity: sha512-y7/yvZ2TPAnR9+jnc00klvNNLkJiXFFrQA/hlLCcxA9a2A4zQIOimyFQ9XfwYKiGD1fb5GY8vbKIIgO8d5Tb2A==} + engines: {node: '>= 20.12.0'} + '@codemirror/autocomplete@6.20.3': resolution: {integrity: sha512-tlosUqb+3BbxCxZdu4tKeRghPFC+QM7q4X5YhKV2eCmPG+1r2F3f4AaSz5sCrFqUtX4Jh20VFTKecl16MgiV9g==} @@ -231,332 +395,182 @@ packages: '@emmetio/stream-reader@2.2.0': resolution: {integrity: sha512-fXVXEyFA5Yv3M3n8sUGT7+fvecGrZP4k6FnWWMSZVQf69kAq0LLpaBQLGcPR30m3zMmKYhECP4k/ZkzvhEW5kw==} - '@emnapi/runtime@1.10.0': - resolution: {integrity: sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==} + '@emnapi/core@1.11.1': + resolution: {integrity: sha512-RSvbQmHzdKzNsLYa/wHrbc3KN4sYLKAdPZxqiM2HATqv/SBk2/ENSHpvXGaLOMcsAyz0poEGqkmmKYG3OWiJEQ==} - '@esbuild/aix-ppc64@0.25.12': - resolution: {integrity: sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==} - engines: {node: '>=18'} - cpu: [ppc64] - os: [aix] + '@emnapi/runtime@1.11.1': + resolution: {integrity: sha512-vgj7R3y3Wgx24IQaGPA/R6YFXLHVMOZ0uVEyIQPaWs+rd1AzfEMXlAC22FYwO1XkKR6NPsq7mUandH8oIRdZFw==} + + '@emnapi/wasi-threads@1.2.2': + resolution: {integrity: sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA==} - '@esbuild/aix-ppc64@0.27.7': - resolution: {integrity: sha512-EKX3Qwmhz1eMdEJokhALr0YiD0lhQNwDqkPYyPhiSwKrh7/4KRjQc04sZ8db+5DVVnZ1LmbNDI1uAMPEUBnQPg==} + '@esbuild/aix-ppc64@0.28.2': + resolution: {integrity: sha512-XExcO+dvLKvVtNTibSTBej1NCAbaGhWn9Ww1ZPx80qsahhPFe/8jgWP0IchNe0F3HwkU7n8ejhH8bjonqht8mQ==} engines: {node: '>=18'} cpu: [ppc64] os: [aix] - '@esbuild/android-arm64@0.25.12': - resolution: {integrity: sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==} + '@esbuild/android-arm64@0.28.2': + resolution: {integrity: sha512-5YfKeeI8qWfBZIX+u2xZC3Zlb3Os/gLS2sbEKM+I4ZOcsWmHS2WLysCcQZDAFRslDUU5Oiq44gf6PYN1vGwG5A==} engines: {node: '>=18'} cpu: [arm64] os: [android] - '@esbuild/android-arm64@0.27.7': - resolution: {integrity: sha512-62dPZHpIXzvChfvfLJow3q5dDtiNMkwiRzPylSCfriLvZeq0a1bWChrGx/BbUbPwOrsWKMn8idSllklzBy+dgQ==} - engines: {node: '>=18'} - cpu: [arm64] - os: [android] - - '@esbuild/android-arm@0.25.12': - resolution: {integrity: sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==} + '@esbuild/android-arm@0.28.2': + resolution: {integrity: sha512-kXXoiPVVGQcnIYGOeaovwOURpniDBpSq4A03qkQ+BMQqtGG6HYap3xne9C1O1yo4TR3qxlCX5IqqmX6fFo2Lqg==} engines: {node: '>=18'} cpu: [arm] os: [android] - '@esbuild/android-arm@0.27.7': - resolution: {integrity: sha512-jbPXvB4Yj2yBV7HUfE2KHe4GJX51QplCN1pGbYjvsyCZbQmies29EoJbkEc+vYuU5o45AfQn37vZlyXy4YJ8RQ==} - engines: {node: '>=18'} - cpu: [arm] - os: [android] - - '@esbuild/android-x64@0.25.12': - resolution: {integrity: sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==} + '@esbuild/android-x64@0.28.2': + resolution: {integrity: sha512-O387ite7SzUyCcy3JQX4P4bLtEA7bLLkx+esve5JHnyYfNTxcVpXZo9jhdB0lTKN44gztELTdU7nS8Nr16Fs1Q==} engines: {node: '>=18'} cpu: [x64] os: [android] - '@esbuild/android-x64@0.27.7': - resolution: {integrity: sha512-x5VpMODneVDb70PYV2VQOmIUUiBtY3D3mPBG8NxVk5CogneYhkR7MmM3yR/uMdITLrC1ml/NV1rj4bMJuy9MCg==} - engines: {node: '>=18'} - cpu: [x64] - os: [android] - - '@esbuild/darwin-arm64@0.25.12': - resolution: {integrity: sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==} - engines: {node: '>=18'} - cpu: [arm64] - os: [darwin] - - '@esbuild/darwin-arm64@0.27.7': - resolution: {integrity: sha512-5lckdqeuBPlKUwvoCXIgI2D9/ABmPq3Rdp7IfL70393YgaASt7tbju3Ac+ePVi3KDH6N2RqePfHnXkaDtY9fkw==} + '@esbuild/darwin-arm64@0.28.2': + resolution: {integrity: sha512-n4KqkOQrraxHJcgjM1RvwbigfQKIKJVpM7xp+KsxiyUSrRdIXnt73VhrPAx0fV44hgfmIVKjxMN9J1t5jySVkw==} engines: {node: '>=18'} cpu: [arm64] os: [darwin] - '@esbuild/darwin-x64@0.25.12': - resolution: {integrity: sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==} + '@esbuild/darwin-x64@0.28.2': + resolution: {integrity: sha512-uq6suIWYP37qzGddBKPw5QEQPi6HiLGsO7UmkpfyaYNQ3D+rN6w6WfwH+nuqcGXWvawGwxOEroO4YGnFh95azw==} engines: {node: '>=18'} cpu: [x64] os: [darwin] - '@esbuild/darwin-x64@0.27.7': - resolution: {integrity: sha512-rYnXrKcXuT7Z+WL5K980jVFdvVKhCHhUwid+dDYQpH+qu+TefcomiMAJpIiC2EM3Rjtq0sO3StMV/+3w3MyyqQ==} - engines: {node: '>=18'} - cpu: [x64] - os: [darwin] - - '@esbuild/freebsd-arm64@0.25.12': - resolution: {integrity: sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==} - engines: {node: '>=18'} - cpu: [arm64] - os: [freebsd] - - '@esbuild/freebsd-arm64@0.27.7': - resolution: {integrity: sha512-B48PqeCsEgOtzME2GbNM2roU29AMTuOIN91dsMO30t+Ydis3z/3Ngoj5hhnsOSSwNzS+6JppqWsuhTp6E82l2w==} + '@esbuild/freebsd-arm64@0.28.2': + resolution: {integrity: sha512-n+I0BTSRIoy+d6RPKnEVwql5UwBJolytvY4mAOIEJorKlqgPII8ix6slVVrfZ5Tnj7glIZvloylbB/EJPMWEXw==} engines: {node: '>=18'} cpu: [arm64] os: [freebsd] - '@esbuild/freebsd-x64@0.25.12': - resolution: {integrity: sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==} + '@esbuild/freebsd-x64@0.28.2': + resolution: {integrity: sha512-78XJTJkvPs0kz2w61301PJjXl4g7q3JqiYMZ/M/yVI73EHBrCRTgkhu9oqG7vPqq+a/yadEW8aD+agKlk5xrmg==} engines: {node: '>=18'} cpu: [x64] os: [freebsd] - '@esbuild/freebsd-x64@0.27.7': - resolution: {integrity: sha512-jOBDK5XEjA4m5IJK3bpAQF9/Lelu/Z9ZcdhTRLf4cajlB+8VEhFFRjWgfy3M1O4rO2GQ/b2dLwCUGpiF/eATNQ==} - engines: {node: '>=18'} - cpu: [x64] - os: [freebsd] - - '@esbuild/linux-arm64@0.25.12': - resolution: {integrity: sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==} - engines: {node: '>=18'} - cpu: [arm64] - os: [linux] - - '@esbuild/linux-arm64@0.27.7': - resolution: {integrity: sha512-RZPHBoxXuNnPQO9rvjh5jdkRmVizktkT7TCDkDmQ0W2SwHInKCAV95GRuvdSvA7w4VMwfCjUiPwDi0ZO6Nfe9A==} + '@esbuild/linux-arm64@0.28.2': + resolution: {integrity: sha512-pW4AC0P3it8c7do9MVM4p51FzHzdM/TZrerurgRcHJ2WTa1VQ1CIq18xncfpBJw4ojkiZZrKW2yIBWBP92j6Ug==} engines: {node: '>=18'} cpu: [arm64] os: [linux] - '@esbuild/linux-arm@0.25.12': - resolution: {integrity: sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==} + '@esbuild/linux-arm@0.28.2': + resolution: {integrity: sha512-XlDnu2q5yoqems+xay6wSAcg9DDD7K9RLKZEBOMZm3ckNpJBvOX20tSfby8KfrrhINDyv9V2YVZKY/SpoGJI8w==} engines: {node: '>=18'} cpu: [arm] os: [linux] - '@esbuild/linux-arm@0.27.7': - resolution: {integrity: sha512-RkT/YXYBTSULo3+af8Ib0ykH8u2MBh57o7q/DAs3lTJlyVQkgQvlrPTnjIzzRPQyavxtPtfg0EopvDyIt0j1rA==} - engines: {node: '>=18'} - cpu: [arm] - os: [linux] - - '@esbuild/linux-ia32@0.25.12': - resolution: {integrity: sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==} - engines: {node: '>=18'} - cpu: [ia32] - os: [linux] - - '@esbuild/linux-ia32@0.27.7': - resolution: {integrity: sha512-GA48aKNkyQDbd3KtkplYWT102C5sn/EZTY4XROkxONgruHPU72l+gW+FfF8tf2cFjeHaRbWpOYa/uRBz/Xq1Pg==} + '@esbuild/linux-ia32@0.28.2': + resolution: {integrity: sha512-CYbnj78HsIeA+DhgUKgFCfvNsTHFhMMrinUrMZpDXJXKN8T3XViTZ/+wtHeVxEWY8ewSzTFN+nRmSwO2tZaLUQ==} engines: {node: '>=18'} cpu: [ia32] os: [linux] - '@esbuild/linux-loong64@0.25.12': - resolution: {integrity: sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==} - engines: {node: '>=18'} - cpu: [loong64] - os: [linux] - - '@esbuild/linux-loong64@0.27.7': - resolution: {integrity: sha512-a4POruNM2oWsD4WKvBSEKGIiWQF8fZOAsycHOt6JBpZ+JN2n2JH9WAv56SOyu9X5IqAjqSIPTaJkqN8F7XOQ5Q==} + '@esbuild/linux-loong64@0.28.2': + resolution: {integrity: sha512-buwkd8nsph4R+ajRvw0qM5Hja/TXQow3ptzWO2EbG/cqcIkHloRrdlBtQlshyYGTNFvfkfJ5tpPLVkY4DtsPfQ==} engines: {node: '>=18'} cpu: [loong64] os: [linux] - '@esbuild/linux-mips64el@0.25.12': - resolution: {integrity: sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==} - engines: {node: '>=18'} - cpu: [mips64el] - os: [linux] - - '@esbuild/linux-mips64el@0.27.7': - resolution: {integrity: sha512-KabT5I6StirGfIz0FMgl1I+R1H73Gp0ofL9A3nG3i/cYFJzKHhouBV5VWK1CSgKvVaG4q1RNpCTR2LuTVB3fIw==} + '@esbuild/linux-mips64el@0.28.2': + resolution: {integrity: sha512-ZVykbDyk7519VwiNb9Lcj9m8XM6v5V9uKPvrEMkkEedVewf+0itkhahp4HDpgERXhwLRpWFypsGbG/J8s0QjJA==} engines: {node: '>=18'} cpu: [mips64el] os: [linux] - '@esbuild/linux-ppc64@0.25.12': - resolution: {integrity: sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==} + '@esbuild/linux-ppc64@0.28.2': + resolution: {integrity: sha512-CAXl+Dtd9UUuJd8pKKdwh6MLm3MUMiqMPmhZ3tTSXPqfyQ3vDl6R5hZdZ/kYojK4ofXtdfSv1tFq8XzWx3heNQ==} engines: {node: '>=18'} cpu: [ppc64] os: [linux] - '@esbuild/linux-ppc64@0.27.7': - resolution: {integrity: sha512-gRsL4x6wsGHGRqhtI+ifpN/vpOFTQtnbsupUF5R5YTAg+y/lKelYR1hXbnBdzDjGbMYjVJLJTd2OFmMewAgwlQ==} - engines: {node: '>=18'} - cpu: [ppc64] - os: [linux] - - '@esbuild/linux-riscv64@0.25.12': - resolution: {integrity: sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==} + '@esbuild/linux-riscv64@0.28.2': + resolution: {integrity: sha512-GeXCej4IQtU1B+QlDV8W/RRvbzI3O/Stss+/bCXv4lZls5WGRtu2a+3JkA3i4qIUlMXpcHebWpF8AkJhATowuA==} engines: {node: '>=18'} cpu: [riscv64] os: [linux] - '@esbuild/linux-riscv64@0.27.7': - resolution: {integrity: sha512-hL25LbxO1QOngGzu2U5xeXtxXcW+/GvMN3ejANqXkxZ/opySAZMrc+9LY/WyjAan41unrR3YrmtTsUpwT66InQ==} - engines: {node: '>=18'} - cpu: [riscv64] - os: [linux] - - '@esbuild/linux-s390x@0.25.12': - resolution: {integrity: sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==} - engines: {node: '>=18'} - cpu: [s390x] - os: [linux] - - '@esbuild/linux-s390x@0.27.7': - resolution: {integrity: sha512-2k8go8Ycu1Kb46vEelhu1vqEP+UeRVj2zY1pSuPdgvbd5ykAw82Lrro28vXUrRmzEsUV0NzCf54yARIK8r0fdw==} + '@esbuild/linux-s390x@0.28.2': + resolution: {integrity: sha512-3H1weTYZPxt/WOhByszQZybS9w5lKzUn1FDMsgEChbHWQwHYQQRfBxgCcZvPhjHfKyJjIievvMmEUawJrdY9Dg==} engines: {node: '>=18'} cpu: [s390x] os: [linux] - '@esbuild/linux-x64@0.25.12': - resolution: {integrity: sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==} + '@esbuild/linux-x64@0.28.2': + resolution: {integrity: sha512-4xTZr1FUmSoQW4XIWmit3tzQrUTZM+N3P0XV8xROKYF50XfI7xeO90+1bZvNwxIufQ9hDQVRJH5YhgPVF8A/HQ==} engines: {node: '>=18'} cpu: [x64] os: [linux] - '@esbuild/linux-x64@0.27.7': - resolution: {integrity: sha512-hzznmADPt+OmsYzw1EE33ccA+HPdIqiCRq7cQeL1Jlq2gb1+OyWBkMCrYGBJ+sxVzve2ZJEVeePbLM2iEIZSxA==} - engines: {node: '>=18'} - cpu: [x64] - os: [linux] - - '@esbuild/netbsd-arm64@0.25.12': - resolution: {integrity: sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==} - engines: {node: '>=18'} - cpu: [arm64] - os: [netbsd] - - '@esbuild/netbsd-arm64@0.27.7': - resolution: {integrity: sha512-b6pqtrQdigZBwZxAn1UpazEisvwaIDvdbMbmrly7cDTMFnw/+3lVxxCTGOrkPVnsYIosJJXAsILG9XcQS+Yu6w==} + '@esbuild/netbsd-arm64@0.28.2': + resolution: {integrity: sha512-sSATRjPeDBg3pdgHoQfoYBob11Kk1FGa9lui5RIHZCoCkJa9QKlvl3/vKz2usCmYYjs7ymJR/2Nnsqe+Hjt5nw==} engines: {node: '>=18'} cpu: [arm64] os: [netbsd] - '@esbuild/netbsd-x64@0.25.12': - resolution: {integrity: sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==} + '@esbuild/netbsd-x64@0.28.2': + resolution: {integrity: sha512-lqnzCV+mM0gIADaKihiCg6ifgfU2L3h5E33rNQBN1Y4MaVGnzryzmvvf7UHxprpQdE8hpqLolJ9Rl+SkIRDpyw==} engines: {node: '>=18'} cpu: [x64] os: [netbsd] - '@esbuild/netbsd-x64@0.27.7': - resolution: {integrity: sha512-OfatkLojr6U+WN5EDYuoQhtM+1xco+/6FSzJJnuWiUw5eVcicbyK3dq5EeV/QHT1uy6GoDhGbFpprUiHUYggrw==} - engines: {node: '>=18'} - cpu: [x64] - os: [netbsd] - - '@esbuild/openbsd-arm64@0.25.12': - resolution: {integrity: sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==} + '@esbuild/openbsd-arm64@0.28.2': + resolution: {integrity: sha512-AL2qJILH7lNjrDmCQDvdxMfAUIv8KMNZOvrwAQ8i8//ntL9FflhOyMJ8OZSMBb8/AWXe3/5v5S20y3zCoZWKoQ==} engines: {node: '>=18'} cpu: [arm64] os: [openbsd] - '@esbuild/openbsd-arm64@0.27.7': - resolution: {integrity: sha512-AFuojMQTxAz75Fo8idVcqoQWEHIXFRbOc1TrVcFSgCZtQfSdc1RXgB3tjOn/krRHENUB4j00bfGjyl2mJrU37A==} - engines: {node: '>=18'} - cpu: [arm64] - os: [openbsd] - - '@esbuild/openbsd-x64@0.25.12': - resolution: {integrity: sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==} - engines: {node: '>=18'} - cpu: [x64] - os: [openbsd] - - '@esbuild/openbsd-x64@0.27.7': - resolution: {integrity: sha512-+A1NJmfM8WNDv5CLVQYJ5PshuRm/4cI6WMZRg1by1GwPIQPCTs1GLEUHwiiQGT5zDdyLiRM/l1G0Pv54gvtKIg==} + '@esbuild/openbsd-x64@0.28.2': + resolution: {integrity: sha512-QtiuPytchRyC4rwUKhexJdQKvDuZ6hWloi3igqPQNUJCS1/v9EiO3UTOXR6A3FoMo4fnAKbWJdqaIwhOzh8qEw==} engines: {node: '>=18'} cpu: [x64] os: [openbsd] - '@esbuild/openharmony-arm64@0.25.12': - resolution: {integrity: sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==} - engines: {node: '>=18'} - cpu: [arm64] - os: [openharmony] - - '@esbuild/openharmony-arm64@0.27.7': - resolution: {integrity: sha512-+KrvYb/C8zA9CU/g0sR6w2RBw7IGc5J2BPnc3dYc5VJxHCSF1yNMxTV5LQ7GuKteQXZtspjFbiuW5/dOj7H4Yw==} + '@esbuild/openharmony-arm64@0.28.2': + resolution: {integrity: sha512-WkhYDmpTjLvGlScA1rwjRUmhl4k8oXR3cIbtqWmELgU/dFeHHlEllxDvdWcNJV9rbzCexB5vz8gtNewWLgCT7Q==} engines: {node: '>=18'} cpu: [arm64] os: [openharmony] - '@esbuild/sunos-x64@0.25.12': - resolution: {integrity: sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==} + '@esbuild/sunos-x64@0.28.2': + resolution: {integrity: sha512-GPMSkTOtMnv2U2F8gxe4Io6qmVs+YKyp832Etqqxr0hFngmXQ3rzwytelm3GIn7T4VviRUlf3sOgBOiTdvaf7g==} engines: {node: '>=18'} cpu: [x64] os: [sunos] - '@esbuild/sunos-x64@0.27.7': - resolution: {integrity: sha512-ikktIhFBzQNt/QDyOL580ti9+5mL/YZeUPKU2ivGtGjdTYoqz6jObj6nOMfhASpS4GU4Q/Clh1QtxWAvcYKamA==} - engines: {node: '>=18'} - cpu: [x64] - os: [sunos] - - '@esbuild/win32-arm64@0.25.12': - resolution: {integrity: sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==} + '@esbuild/win32-arm64@0.28.2': + resolution: {integrity: sha512-PIhhEkE9uPBleRBrQEJpUn7MBnibZzbGzYWPmY3x+YoVg/95zbjB4CxPPOQ8l5tYYM4mMaCthF8/1DIfBQQyWQ==} engines: {node: '>=18'} cpu: [arm64] os: [win32] - '@esbuild/win32-arm64@0.27.7': - resolution: {integrity: sha512-7yRhbHvPqSpRUV7Q20VuDwbjW5kIMwTHpptuUzV+AA46kiPze5Z7qgt6CLCK3pWFrHeNfDd1VKgyP4O+ng17CA==} - engines: {node: '>=18'} - cpu: [arm64] - os: [win32] - - '@esbuild/win32-ia32@0.25.12': - resolution: {integrity: sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==} + '@esbuild/win32-ia32@0.28.2': + resolution: {integrity: sha512-YmJbfTlvU7Sdn9BB+4PRES4oB6pxgS37MAONj+hBr/cpXS1aBPKXxNnDbu+QCWPj0o9dgyxeq79g6c5P8KeuYA==} engines: {node: '>=18'} cpu: [ia32] os: [win32] - '@esbuild/win32-ia32@0.27.7': - resolution: {integrity: sha512-SmwKXe6VHIyZYbBLJrhOoCJRB/Z1tckzmgTLfFYOfpMAx63BJEaL9ExI8x7v0oAO3Zh6D/Oi1gVxEYr5oUCFhw==} - engines: {node: '>=18'} - cpu: [ia32] - os: [win32] - - '@esbuild/win32-x64@0.25.12': - resolution: {integrity: sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==} - engines: {node: '>=18'} - cpu: [x64] - os: [win32] - - '@esbuild/win32-x64@0.27.7': - resolution: {integrity: sha512-56hiAJPhwQ1R4i+21FVF7V8kSD5zZTdHcVuRFMW0hn753vVfQN8xlx4uOPT4xoGH0Z/oVATuR82AiqSTDIpaHg==} + '@esbuild/win32-x64@0.28.2': + resolution: {integrity: sha512-5ebpxr3nWMzrL/rnUI755Jkuee0bHL/Gq0WTF9lvcpv73wAp5eu8MfBUgWK9bhWvZjj7yX8etf/8tI8Ney695g==} engines: {node: '>=18'} cpu: [x64] os: [win32] - '@expressive-code/core@0.38.3': - resolution: {integrity: sha512-s0/OtdRpBONwcn23O8nVwDNQqpBGKscysejkeBkwlIeHRLZWgiTVrusT5Idrdz1d8cW5wRk9iGsAIQmwDPXgJg==} + '@expressive-code/core@0.44.1': + resolution: {integrity: sha512-3dDo9N8D7hYrLNNMMWFovg3+aDUtnQm7c7z0GZc1c0LEFVBc0Q6lKG+tVT28gDadOvsgOANfCn35fgpe97Pmgg==} - '@expressive-code/plugin-frames@0.38.3': - resolution: {integrity: sha512-qL2oC6FplmHNQfZ8ZkTR64/wKo9x0c8uP2WDftR/ydwN/yhe1ed7ZWYb8r3dezxsls+tDokCnN4zYR594jbpvg==} + '@expressive-code/plugin-frames@0.44.1': + resolution: {integrity: sha512-HC/bdRao9225ApcgO/e3jn8ZOhldKO7ob1O/Tcipvtv7Vb5nMphZhMtD9uuywpvxkPYBHJi3504WhrKg05Dwqg==} - '@expressive-code/plugin-shiki@0.38.3': - resolution: {integrity: sha512-kqHnglZeesqG3UKrb6e9Fq5W36AZ05Y9tCREmSN2lw8LVTqENIeCIkLDdWtQ5VoHlKqwUEQFTVlRehdwoY7Gmw==} + '@expressive-code/plugin-shiki@0.44.1': + resolution: {integrity: sha512-YApiZt3buUzBwL5tqj8G+sYC5NjMjRCHgQwr9bmGl69rtcHy6fE9dooWUeKYB978fJT2BuxT5FeHcF47rA3SEg==} - '@expressive-code/plugin-text-markers@0.38.3': - resolution: {integrity: sha512-dPK3+BVGTbTmGQGU3Fkj3jZ3OltWUAlxetMHI6limUGCWBCucZiwoZeFM/WmqQa71GyKRzhBT+iEov6kkz2xVA==} + '@expressive-code/plugin-text-markers@0.44.1': + resolution: {integrity: sha512-B3BsJoJ8CFMlcIX9f+X9tcI3C4zPDO601+YuLi9GheSTNro7ZfqSjLptMQKBHOWZvxnAtY5zvIX7iO/qtBhNBg==} '@floating-ui/core@1.8.0': resolution: {integrity: sha512-0CIZ5itps/8x7BG8dEIhs53BvCUH2PCoogtakwRTut+Arm58sJooJ0AuZhLw2HJYIR5cMLNPBSS728sPho2khQ==} @@ -796,6 +810,13 @@ packages: '@mdx-js/mdx@3.1.1': resolution: {integrity: sha512-f6ZO2ifpwAQIpzGWaBQT2TXxPv6z3RBzQKpVftEWN78Vl/YweF1uwussDx8ECAXVtr3Rs89fKyG9YlzUs9DyGQ==} + '@napi-rs/wasm-runtime@1.2.3': + resolution: {integrity: sha512-UMduMbqO5s5zF2NkNacMT/yK5Y5QiKvWr2+50bzIIxFDwVJ2h49b+oyjaCGPhJxd2/gC2x39EHv/gHVuu36x2Q==} + engines: {node: ^20.19.0 || ^22.13.0 || >=23.5.0} + peerDependencies: + '@emnapi/core': ^1.7.1 || ^2.0.0-alpha.4 + '@emnapi/runtime': ^1.7.1 || ^2.0.0-alpha.4 + '@opentelemetry/api@1.9.0': resolution: {integrity: sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==} engines: {node: '>=8.0.0'} @@ -803,6 +824,9 @@ packages: '@oslojs/encoding@1.1.0': resolution: {integrity: sha512-70wQhgYmndg4GCPxPPxPGevRKqTIJ2Nh4OkiMWmDAVYsTQ+Ta7Sq+rPevXyXGdzr30/qZBnyOalCszoMxlyldQ==} + '@oxc-project/types@0.146.0': + resolution: {integrity: sha512-XC0QsnnhVe7sLIWmYmdPw7x5P0h4W8vUU3Nv1ySgWXtvCz8NizoAEpGXA0sOYoJQV2Rl13LgURAHQ5cI5ILCSA==} + '@pagefind/darwin-arm64@1.5.2': resolution: {integrity: sha512-MXpI+7HsAdPkvJ0gk9xj9g541BCqBZOBbdwj9g6lB5LCj6kSV6nqDSjzcAJwvOsfu0fjwvC8hQU+ecfhp+MpiQ==} cpu: [arm64] @@ -851,152 +875,104 @@ packages: '@codemirror/state': ^6.0.0 '@codemirror/view': ^6.0.0 - '@rollup/pluginutils@5.3.0': - resolution: {integrity: sha512-5EdhGZtnu3V88ces7s53hhfK5KSASnJZv8Lulpc04cWO3REESroJXg73DFsOmgbU2BhwV0E20bu2IDZb3VKW4Q==} - engines: {node: '>=14.0.0'} - peerDependencies: - rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 - peerDependenciesMeta: - rollup: - optional: true - - '@rollup/rollup-android-arm-eabi@4.60.4': - resolution: {integrity: sha512-F5QXMSiFebS9hKZj02XhWLLnRpJ3B3AROP0tWbFBSj+6kCbg5m9j5JoHKd4mmSVy5mS/IMQloYgYxCuJC0fxEQ==} + '@rolldown/binding-android-arm-eabi@1.2.5': + resolution: {integrity: sha512-DLe/i+l8ynIBY7XEQ191TeZvCoowIGa18R+dIV30GW7DiOtp74i/xX8hs8GUjW5ARV7VZuie3d6AumSmCwbeRA==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [android] - '@rollup/rollup-android-arm64@4.60.4': - resolution: {integrity: sha512-GxxTKApUpzRhof7poWvCJHRF51C67u1R7D6DiluBE8wKU1u5GWE8t+v81JvJYtbawoBFX1hLv5Ei4eVjkWokaw==} + '@rolldown/binding-android-arm64@1.2.5': + resolution: {integrity: sha512-zXcwKlQApYAOELHd8PwKDFkagYF9Wy4e0RJ+0qnzl9Pjnpj75TEG8ufv40p2J7kCEfwZAsNiuzRIyNNMWT38ig==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [android] - '@rollup/rollup-darwin-arm64@4.60.4': - resolution: {integrity: sha512-tua0TaJxMOB1R0V0RS1jFZ/RpURFDJIOR2A6jWwQeawuFyS4gBW+rntLRaQd0EQ4bd6Vp44Z2rXW+YYDBsj6IA==} + '@rolldown/binding-darwin-arm64@1.2.5': + resolution: {integrity: sha512-dK4QakI42nzWgJT5sm4y4y/O//D4OxM75/cH28RLV+nzIN9AY+YsbuUVrUTjlLjXR6vpyxFbSsbmNuJ6BP9sww==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [darwin] - '@rollup/rollup-darwin-x64@4.60.4': - resolution: {integrity: sha512-CSKq7MsP+5PFIcydhAiR1K0UhEI1A2jWXVKHPCBZ151yOutENwvnPocgVHkivu2kviURtCEB6zUQw0vs8RrhMg==} + '@rolldown/binding-darwin-x64@1.2.5': + resolution: {integrity: sha512-fqSALaUu1Wjd1nK2uW2kJDWdLCc8lx1IcY+MTY26Aurfdx19anlzhqXOgCFbBFQnlFDTn4TC1/7Nz4Bl2mLP3A==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [darwin] - '@rollup/rollup-freebsd-arm64@4.60.4': - resolution: {integrity: sha512-+O8OkVdyvXMtJEciu2wS/pzm1IxntEEQx3z5TAVy4l32G0etZn+RsA48ARRrFm6Ri8fvqPQfgrvNxSjKAbnd3g==} - cpu: [arm64] - os: [freebsd] - - '@rollup/rollup-freebsd-x64@4.60.4': - resolution: {integrity: sha512-Iw3oMskH3AfNuhU0MSN7vNbdi4me/NiYo2azqPz/Le16zHSa+3RRmliCMWWQmh4lcndccU40xcJuTYJZxNo/lw==} + '@rolldown/binding-freebsd-x64@1.2.5': + resolution: {integrity: sha512-/vCnNxlkxs9tKxNDcyWUePpJ/PgTzxIaVhoM5SmG8UV+GR/IcPam4VYxi7GIMo7PSDuNqlJqvprqii9NqqVCMw==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [freebsd] - '@rollup/rollup-linux-arm-gnueabihf@4.60.4': - resolution: {integrity: sha512-EIPRXTVQpHyF8WOo219AD2yEltPehLTcTMz2fn6JsatLYSzQf00hj3rulF+yauOlF9/FtM2WpkT/hJh/KJFGhA==} + '@rolldown/binding-linux-arm-gnueabihf@1.2.5': + resolution: {integrity: sha512-abk0NLA519LxRCszmbE0jYKuQ9YPocOXTiOXOo6Yr+YAT95VH+PtqYAjOJvGKt3viEd/x4qzabAlwd5bHOOARg==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] - libc: [glibc] - - '@rollup/rollup-linux-arm-musleabihf@4.60.4': - resolution: {integrity: sha512-J3Yh9PzzF1Ovah2At+lHiGQdsYgArxBbXv/zHfSyaiFQEqvNv7DcW98pCrmdjCZBrqBiKrKKe2V+aaSGWuBe/w==} - cpu: [arm] - os: [linux] - libc: [musl] - '@rollup/rollup-linux-arm64-gnu@4.60.4': - resolution: {integrity: sha512-BFDEZMYfUvLn37ONE1yMBojPxnMlTFsdyNoqncT0qFq1mAfllL+ATMMJd8TeuVMiX84s1KbcxcZbXInmcO2mRg==} + '@rolldown/binding-linux-arm64-gnu@1.2.5': + resolution: {integrity: sha512-Y7eALiJ8lr0M2HH103Js+g7V34wf6snlpZLAsHI90uLhr3PVlNsbFVAXJC9d/V6BnPyKtpSwI+NcB/RLxsQxuA==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] libc: [glibc] - '@rollup/rollup-linux-arm64-musl@4.60.4': - resolution: {integrity: sha512-pc9EYOSlOgdQ2uPl1o9PF6/kLSgaUosia7gOuS8mB69IxJvlclko1MECXysjs5ryez1/5zjYqx3+xYU0TU6R1A==} + '@rolldown/binding-linux-arm64-musl@1.2.5': + resolution: {integrity: sha512-xMvZgnbZg4YVnR/AX2b3oOPDTFYJvUVaJg5FedA/LuvexAtXibZQej4cnTkw3rjsJ/ggUROB64TdtETiim+FYA==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] libc: [musl] - '@rollup/rollup-linux-loong64-gnu@4.60.4': - resolution: {integrity: sha512-NxnomyxYerDh5n4iLrNa+sH+Z+U4BMEE46V2PgQ/hoB909i8gV1M5wPojWg9fk1jWpO3IQnOs20K4wyZuFLEFQ==} - cpu: [loong64] - os: [linux] - libc: [glibc] - - '@rollup/rollup-linux-loong64-musl@4.60.4': - resolution: {integrity: sha512-nbJnQ8a3z1mtmrwImCYhc6BGpThAyYVRQxw9uKSKG4wR6aAYno9sVjJ0zaZcW9BPJX1GbrDPf+SvdWjgTuDmnw==} - cpu: [loong64] - os: [linux] - libc: [musl] - - '@rollup/rollup-linux-ppc64-gnu@4.60.4': - resolution: {integrity: sha512-2EU6acNrQLd8tYvo/LXW535wupT3m6fo7HKo6lr7ktQoItxTyOL1ZCR/GfGCuXl2vR+zmfI6eRXkSemafv+iVg==} - cpu: [ppc64] - os: [linux] - libc: [glibc] - - '@rollup/rollup-linux-ppc64-musl@4.60.4': - resolution: {integrity: sha512-WeBtoMuaMxiiIrO2IYP3xs6GMWkJP2C0EoT8beTLkUPmzV1i/UcOSVw1d5r9KBODtHKilG5yFxsGRnBbK3wJ4A==} + '@rolldown/binding-linux-ppc64-gnu@1.2.5': + resolution: {integrity: sha512-GRjeqTUDHTo5GwntsLaAMcBahG3nlpjftXWZLN73HiYQlhwEowvarFgQnRnQZtIp4keXX7quXFbG38uPZBa2EA==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [ppc64] os: [linux] - libc: [musl] - - '@rollup/rollup-linux-riscv64-gnu@4.60.4': - resolution: {integrity: sha512-FJHFfqpKUI3A10WrWKiFbBZ7yVbGT4q4B5o1qKFFojqpaYoh9LrQgqWCmmcxQzVSXYtyB5bzkXrYzlHTs21MYA==} - cpu: [riscv64] - os: [linux] libc: [glibc] - '@rollup/rollup-linux-riscv64-musl@4.60.4': - resolution: {integrity: sha512-mcEl6CUT5IAUmQf1m9FYSmVqCJlpQ8r8eyftFUHG8i9OhY7BkBXSUdnLH5DOf0wCOjcP9v/QO93zpmF1SptCCw==} - cpu: [riscv64] - os: [linux] - libc: [musl] - - '@rollup/rollup-linux-s390x-gnu@4.60.4': - resolution: {integrity: sha512-ynt3JxVd2w2buzoKDWIyiV1pJW93xlQic1THVLXilz429oijRpSHivZAgp65KBu+cMcgf1eVVjdnTLvPxgCuoQ==} + '@rolldown/binding-linux-s390x-gnu@1.2.5': + resolution: {integrity: sha512-vLNTR45F2Uwc8AufkNXPmB4VliaXs+FvcheEogIzOXzO4l+LzieXF5A/TWxLy5HtqpsRCHUfd0lPVrrdgXdLHQ==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [s390x] os: [linux] libc: [glibc] - '@rollup/rollup-linux-x64-gnu@4.60.4': - resolution: {integrity: sha512-Boiz5+MsaROEWDf+GGEwF8VMHGhlUoQMtIPjOgA5fv4osupqTVnJteQNKJwUcnUog2G55jYXH7KZFFiJe0TEzQ==} + '@rolldown/binding-linux-x64-gnu@1.2.5': + resolution: {integrity: sha512-Mgj59/HTuYeK9Gz2MA+mBWKnHsAgkBSec15ZMb1st3oIfFbX7gCjOae7GydHhzcyQi9Z/7M1QuN9bR3oFqF0jQ==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] libc: [glibc] - '@rollup/rollup-linux-x64-musl@4.60.4': - resolution: {integrity: sha512-+qfSY27qIrFfI/Hom04KYFw3GKZSGU4lXus51wsb5EuySfFlWRwjkKWoE9emgRw/ukoT4Udsj4W/+xxG8VbPKg==} + '@rolldown/binding-linux-x64-musl@1.2.5': + resolution: {integrity: sha512-mY8AP0/ichsbhAxGnLa3d3+MwV0EfgrPND2bplI3Ym8T6R2pJ0N87bvrKVwNXmdy3jnr6eQBecdqx/HMknBmpA==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] libc: [musl] - '@rollup/rollup-openbsd-x64@4.60.4': - resolution: {integrity: sha512-VpTfOPHgVXEBeeR8hZ2O0F3aSso+JDWqTWmTmzcQKted54IAdUVbxE+j/MVxUsKa8L20HJhv3vUezVPoquqWjA==} - cpu: [x64] - os: [openbsd] - - '@rollup/rollup-openharmony-arm64@4.60.4': - resolution: {integrity: sha512-IPOsh5aRYuLv/nkU51X10Bf75Bsf6+gZdx1X+QP5QM6lIJFHHqbHLG0uJn/hWthzo13UAc2umiUorqZy3axoZg==} + '@rolldown/binding-openharmony-arm64@1.2.5': + resolution: {integrity: sha512-8SLssA2oweAxyRgDp789ACfRb/3P+zNRJpzZxSizxF9m8NUDQ4+3xjo8ttjhVGGw6Qxb70oZiEtIjaKikCO7Yw==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [openharmony] - '@rollup/rollup-win32-arm64-msvc@4.60.4': - resolution: {integrity: sha512-4QzE9E81OohJ/HKzHhsqU+zcYYojVOXlFMs1DdyMT6qXl/niOH7AVElmmEdUNHHS/oRkc++d5k6Vy85zFs0DEw==} + '@rolldown/binding-win32-arm64-msvc@1.2.5': + resolution: {integrity: sha512-vGbruD5zquhoc8D9SViXgN2FBJtNdTyQ4DtG+SWiEGlJiAzoKcZ2xp+xuXCffhubVdt0NJlTZqkeRuERy7g8Cw==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [win32] - '@rollup/rollup-win32-ia32-msvc@4.60.4': - resolution: {integrity: sha512-zTPgT1YuHHcd+Tmx7h8aml0FWFVelV5N54oHow9SLj+GfoDy/huQ+UV396N/C7KpMDMiPspRktzM1/0r1usYEA==} - cpu: [ia32] - os: [win32] - - '@rollup/rollup-win32-x64-gnu@4.60.4': - resolution: {integrity: sha512-DRS4G7mi9lJxqEDezIkKCaUIKCrLUUDCUaCsTPCi/rtqaC6D/jjwslMQyiDU50Ka0JKpeXeRBFBAXwArY52vBw==} + '@rolldown/binding-win32-x64-msvc@1.2.5': + resolution: {integrity: sha512-e/SXpgISz+IoqVcSSI0rx/d/he8zqLex+/rCWpnHpmVfmPIUjag9H6P7zotf0gJHwPUhQxZ/mF8tr6acebT9yw==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [win32] - '@rollup/rollup-win32-x64-msvc@4.60.4': - resolution: {integrity: sha512-QVTUovf40zgTqlFVrKA1uXMVvU2QWEFWfAH8Wdc48IxLvrJMQVMBRjuQyUpzZCDkakImib9eVazbWlC6ksWtJw==} - cpu: [x64] - os: [win32] + '@rolldown/pluginutils@1.0.1': + resolution: {integrity: sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==} '@scalar/agent-chat@0.12.18': resolution: {integrity: sha512-auIe3JYVm6jgoT7pa4Za+lrLFnEa3CDb4ncZA9aAYztti8C6qSFGn8nBrAtm/RarljhPTYZg5dhqeXJ7zyOu7w==} @@ -1093,41 +1069,33 @@ packages: resolution: {integrity: sha512-qqSSq8OSxx582h/DSgMYH68iqzp+M8c9B+YGB7/qi5ZQK3TzazeCM1BVakkSbFYvD+X4FanV4V4x/aAhjv6MfQ==} engines: {node: '>=22'} - '@shikijs/core@1.29.2': - resolution: {integrity: sha512-vju0lY9r27jJfOY4Z7+Rt/nIOjzJpZ3y+nYpqtUZInVoXQ/TJZcfGnNOGnKjFdVZb8qexiCuSlZRKcGfhhTTZQ==} - - '@shikijs/core@3.23.0': - resolution: {integrity: sha512-NSWQz0riNb67xthdm5br6lAkvpDJRTgB36fxlo37ZzM2yq0PQFFzbd8psqC2XMPgCzo1fW6cVi18+ArJ44wqgA==} - - '@shikijs/engine-javascript@1.29.2': - resolution: {integrity: sha512-iNEZv4IrLYPv64Q6k7EPpOCE/nuvGiKl7zxdq0WFuRPF5PAE9PRo2JGq/d8crLusM59BRemJ4eOqrFrC4wiQ+A==} - - '@shikijs/engine-javascript@3.23.0': - resolution: {integrity: sha512-aHt9eiGFobmWR5uqJUViySI1bHMqrAgamWE1TYSUoftkAeCCAiGawPMwM+VCadylQtF4V3VNOZ5LmfItH5f3yA==} - - '@shikijs/engine-oniguruma@1.29.2': - resolution: {integrity: sha512-7iiOx3SG8+g1MnlzZVDYiaeHe7Ez2Kf2HrJzdmGwkRisT7r4rak0e655AcM/tF9JG/kg5fMNYlLLKglbN7gBqA==} - - '@shikijs/engine-oniguruma@3.23.0': - resolution: {integrity: sha512-1nWINwKXxKKLqPibT5f4pAFLej9oZzQTsby8942OTlsJzOBZ0MWKiwzMsd+jhzu8YPCHAswGnnN1YtQfirL35g==} + '@shikijs/core@4.4.3': + resolution: {integrity: sha512-QCR4q2ZO/ILJEuwiBMel4wdcTDb1JGwfjKTxPDF6x8ixOaluPrVqIn06C99AcRPhmYlBR56d/Fb+GN58GzExpg==} + engines: {node: '>=20'} - '@shikijs/langs@1.29.2': - resolution: {integrity: sha512-FIBA7N3LZ+223U7cJDUYd5shmciFQlYkFXlkKVaHsCPgfVLiO+e12FmQE6Tf9vuyEsFe3dIl8qGWKXgEHL9wmQ==} + '@shikijs/engine-javascript@4.4.3': + resolution: {integrity: sha512-FbOjFJp9VLdo1Wevs10BBtVxiTWwNLqZh5Gkhjgda/ioL15YOgeSl9n+6XMa3qRlPQzfhFNe641SrynFHYG0nQ==} + engines: {node: '>=20'} - '@shikijs/langs@3.23.0': - resolution: {integrity: sha512-2Ep4W3Re5aB1/62RSYQInK9mM3HsLeB91cHqznAJMuylqjzNVAVCMnNWRHFtcNHXsoNRayP9z1qj4Sq3nMqYXg==} + '@shikijs/engine-oniguruma@4.4.3': + resolution: {integrity: sha512-EcOQkxdxGQrc1Row/cC2c96/v1dbZqGnEVu1qTuT/MJmp6+cXCvQussowVmCv5Tqr3KuY3c7IbM6HTW3LJ1k9w==} + engines: {node: '>=20'} - '@shikijs/themes@1.29.2': - resolution: {integrity: sha512-i9TNZlsq4uoyqSbluIcZkmPL9Bfi3djVxRnofUHwvx/h6SRW3cwgBC5SML7vsDcWyukY0eCzVN980rqP6qNl9g==} + '@shikijs/langs@4.4.3': + resolution: {integrity: sha512-ePic0yfAJGOF83D5wBHK/00EjK65oahBYxFk5epgq33WRv7X9UuxLEV8PtR0szC0z8dl7INIpIodB99JRFlR+A==} + engines: {node: '>=20'} - '@shikijs/themes@3.23.0': - resolution: {integrity: sha512-5qySYa1ZgAT18HR/ypENL9cUSGOeI2x+4IvYJu4JgVJdizn6kG4ia5Q1jDEOi7gTbN4RbuYtmHh0W3eccOrjMA==} + '@shikijs/primitive@4.4.3': + resolution: {integrity: sha512-m0wBeLDQDeIxRdUmrCPdQqfuUamDwRL5isCfYbguKD6NiaKpVbsv+3J81DyIKgNW5h4WAIIr8T4EkgQrBBxvaQ==} + engines: {node: '>=20'} - '@shikijs/types@1.29.2': - resolution: {integrity: sha512-VJjK0eIijTZf0QSTODEXCqinjBn0joAHQ+aPSBzrv4O2d/QSbsMw+ZeSRx03kV34Hy7NzUvV/7NqfYGRLrASmw==} + '@shikijs/themes@4.4.3': + resolution: {integrity: sha512-w8UHjeUnIR965KMWJHUPXOc2mNJUnK3vpVLYLvw5IYU2mnTTJ89E24OrJDBNiJDQ0qzb0tc4l7mrIXx5cFeIyw==} + engines: {node: '>=20'} - '@shikijs/types@3.23.0': - resolution: {integrity: sha512-3JZ5HXOZfYjsYSk0yPwBrkupyYSLpAE26Qc0HLghhZNGTZg/SKxXIIgoxOpmmeQP0RRSDJTk1/vPfw9tbw+jSQ==} + '@shikijs/types@4.4.3': + resolution: {integrity: sha512-UEJxmRR++MAGR6hugn0vgVS2W/6lWAts84FFSrnlH9sP0LNol7E5+NQ792pH8liWUhyMyjhTgSUH3k7iD7tc5g==} + engines: {node: '>=20'} '@shikijs/vscode-textmate@10.0.2': resolution: {integrity: sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==} @@ -1240,23 +1208,23 @@ packages: peerDependencies: vue: ^2.7.0 || ^3.0.0 + '@tybys/wasm-util@0.10.3': + resolution: {integrity: sha512-F3fo1MYrRJYL3zER0OUOmkutjr1Vp23m7OsSgp7nq4SP6OqX6C/56XFIPAl5bt3zaBRjmW7SGz3u/6LwFpYcOg==} + '@types/debug@4.1.13': resolution: {integrity: sha512-KSVgmQmzMwPlmtljOomayoR89W4FynCAi3E8PPs7vmDVPe84hT+vGPKkJfThkmXs0x0jAaa9U8uW8bbfyS2fWw==} '@types/estree-jsx@1.0.5': resolution: {integrity: sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==} - '@types/estree@1.0.8': - resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} - '@types/estree@1.0.9': resolution: {integrity: sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==} '@types/har-format@1.2.16': resolution: {integrity: sha512-fluxdy7ryD3MV6h8pTfTYpy/xQzCFC7m89nOH9y94cNqJ1mDIDPut7MnRHI3F6qRmh/cT2fUjG1MLdCNb4hE9A==} - '@types/hast@3.0.4': - resolution: {integrity: sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==} + '@types/hast@3.0.5': + resolution: {integrity: sha512-rp/ezSWaD1m44dPKICGhiskI13nVr7qTloFwDa/IYkhhf5nzwP+zIQcIJh3WIFSBOy/H1PzB40jPjMDksN4F+g==} '@types/js-yaml@4.0.9': resolution: {integrity: sha512-k4MGaQl5TGo/iipqb2UDG2UwjXziSWkh0uysQelTlJpX1qGlpUZYm8PnO4DxG1qBomtJUdYJ6qR6xdIah10JLg==} @@ -1276,9 +1244,6 @@ packages: '@types/node@24.12.4': resolution: {integrity: sha512-GUUEShf+PBCGW2KaXwcIt3Yk+e3pkKwWKb9GSyM9WQVE+ep2jzmHdGsHzu4wgcZy5fN9FBdVzjpBQsYlpfpgLA==} - '@types/react@19.2.16': - resolution: {integrity: sha512-esJiCAnl0kfpNdE69f3So4WJUXy95dLZydX0KwK46riIHDzHM7O9Vtf9xCHW0PXIqvgqNrswl522kA/5yx+F4w==} - '@types/sax@1.2.7': resolution: {integrity: sha512-rO73L89PJxeYM3s3pPPjiPgVVcymqU490g0YO5n5By0k2Erzj6tay/4lr1CHAAU4JyOWd1rpQ8bCf6cZfHU96A==} @@ -1452,25 +1417,18 @@ packages: ajv@8.20.0: resolution: {integrity: sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==} - ansi-align@3.0.1: - resolution: {integrity: sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==} + am-i-vibing@0.4.0: + resolution: {integrity: sha512-MxT4XZL7pzLHpuvhDKdMaQHMGGkJDLluKBLsbstn+8wv9sWcFT6h+0ve9qkml95amVTZtZV83gQe2hY+ojgHLg==} + hasBin: true ansi-regex@5.0.1: resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} engines: {node: '>=8'} - ansi-regex@6.2.2: - resolution: {integrity: sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==} - engines: {node: '>=12'} - ansi-styles@4.3.0: resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} engines: {node: '>=8'} - ansi-styles@6.2.3: - resolution: {integrity: sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==} - engines: {node: '>=12'} - anymatch@3.1.3: resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} engines: {node: '>= 8'} @@ -1496,15 +1454,20 @@ packages: resolution: {integrity: sha512-LElXdjswlqjWrPpJFg1Fx4wpkOCxj1TDHlSV4PlaRxHGWko024xICaa97ZkMfs6DRKlCguiAI+rbXv5GWwXIkg==} hasBin: true - astro-expressive-code@0.38.3: - resolution: {integrity: sha512-Tvdc7RV0G92BbtyEOsfJtXU35w41CkM94fOAzxbQP67Wj5jArfserJ321FO4XA7WG9QMV0GIBmQq77NBIRDzpQ==} + astro-expressive-code@0.44.1: + resolution: {integrity: sha512-DT1LnCqbHasBKlvzJ3m6LR4VI94wwx3W9EV/YbP1te4rqjOHsvsezHYuqb5MeLWLftXms/1FA9QBbwCo43DnJQ==} peerDependencies: - astro: ^4.0.0-beta || ^5.0.0-beta || ^3.3.0 + astro: ^4.0.0-beta || ^5.0.0-beta || ^3.3.0 || ^6.0.0-beta || ^7.0.0 - astro@5.18.2: - resolution: {integrity: sha512-TnFwLnAXty5MXKPDGuKXqK4AMBXG+FH6RUdK7Oyc3gyfNoFIthT+4eRbzOK43bdRlLaZuxgciDSjgtggZ3OtGQ==} - engines: {node: 18.20.8 || ^20.3.0 || >=22.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0'} + astro@7.2.4: + resolution: {integrity: sha512-+cuLsBns2wwUHI9a10xZMbjrF91m7+QNwqTVeljTx0B8Lf+8h0LgVGjdVIL2FALDKD2I565lczeeS3BFC+KdZg==} + engines: {node: '>=22.12.0', npm: '>=9.6.5', pnpm: '>=7.1.0'} hasBin: true + peerDependencies: + '@astrojs/markdown-remark': 7.2.4 + peerDependenciesMeta: + '@astrojs/markdown-remark': + optional: true axobject-query@4.1.0: resolution: {integrity: sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==} @@ -1513,9 +1476,6 @@ packages: bail@2.0.2: resolution: {integrity: sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==} - base-64@1.0.0: - resolution: {integrity: sha512-kwDPIFCGx0NZHog36dj+tHiwP4QMzsZ3AgMViUBKI0+V5n4U0ufTCUMhnQ04diaRI8EX/QcPfql7zlhZ7j4zgg==} - bcp-47-match@2.0.3: resolution: {integrity: sha512-JtTezzbAibu8G0R9op9zb3vcWZd9JF6M0xOYGPn0fNCd7wOpRB1mU2mH9T8gaBGbAAyIIVgB2G7xG0GP98zMAQ==} @@ -1529,21 +1489,17 @@ packages: boolbase@1.0.0: resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==} - boxen@8.0.1: - resolution: {integrity: sha512-F3PH5k5juxom4xktynS7MoFY+NUWH5LC4CnH11YB8NPew+HLpmBLCybSAEyb2F+4pRXhuhWqFesoQd6DAyc2hw==} - engines: {node: '>=18'} - braces@3.0.3: resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} engines: {node: '>=8'} - camelcase@8.0.0: - resolution: {integrity: sha512-8WB3Jcas3swSvjIeA2yvCJ+Miyz5l1ZmB6HFb9R1317dt9LCQoswg/BGrmAmkWVEszSrrg4RwmO46qIm2OEnSA==} - engines: {node: '>=16'} - ccount@2.0.1: resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==} + chalk@4.1.2: + resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} + engines: {node: '>=10'} + chalk@5.6.2: resolution: {integrity: sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==} engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} @@ -1576,10 +1532,6 @@ packages: resolution: {integrity: sha512-77PSwercCZU2Fc4sX94eF8k8Pxte6JAwL4/ICZLFjJLqegs7kCuAsqqj/70NQF6TvDpgFjkubQB2FW2ZZddvQg==} engines: {node: '>=8'} - cli-boxes@3.0.0: - resolution: {integrity: sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g==} - engines: {node: '>=10'} - cliui@8.0.1: resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} engines: {node: '>=12'} @@ -1605,8 +1557,13 @@ packages: resolution: {integrity: sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==} engines: {node: '>=16'} - common-ancestor-path@1.0.1: - resolution: {integrity: sha512-L3sHRo1pXXEqX8VU28kfgUY+YGsk09hPqZiZmLacNib6XNTCM8ubYeT7ryXQw8asB1sKgcU5lkB7ONug08aB8w==} + commander@14.0.3: + resolution: {integrity: sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw==} + engines: {node: '>=20'} + + common-ancestor-path@2.0.0: + resolution: {integrity: sha512-dnN3ibLeoRf2HNC+OlCiNc5d2zxbLJXOtiZUudNFSXZrNSydxcCsSpRzXwfu7BBWCIfHPw+xTayeBvJCP/D8Ng==} + engines: {node: '>= 18'} convert-hrtime@5.0.0: resolution: {integrity: sha512-lOETlkIeYSJWcbbcvjRKGxVMXJR+8+OQb/mTPbA4ObPMytYIsUbuOE0Jzy60hjARYszq1id0j8KgVhC+WGZVTg==} @@ -1615,9 +1572,9 @@ packages: cookie-es@1.2.3: resolution: {integrity: sha512-lXVyvUvrNXblMqzIRrxHb57UUVmqsSWlxqt3XIjCkUP0wDAf6uicO6KMbEgYrMNtEvWgWHwe42CKxPu9MYAnWw==} - cookie@1.1.1: - resolution: {integrity: sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==} - engines: {node: '>=18'} + cookie@2.0.1: + resolution: {integrity: sha512-yuToqVvRrj6pfDXREyQAAv8SkAEk/8GS3jQRTiUMm66TVtBYmqQeoEjL2Lmq8Rpo6271vH76InTChTitEAm65w==} + engines: {node: '>=22'} crelt@1.0.7: resolution: {integrity: sha512-aK6BbWfhf4U/wCcLHKPJl/xa6VkVstRaPywWtMKGwuOLc/wZTyQYuoxgvZnNsBvv7Kg3YTBQYYBCggcviQczuA==} @@ -1689,10 +1646,6 @@ packages: resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} engines: {node: '>=8'} - deterministic-object-hash@2.0.2: - resolution: {integrity: sha512-KxektNH63SrbfUyDiwXqRb1rLwKt33AmMv+5Nhsw1kqZ13SJBRTgZHtGbE+hH3a1mVW1cz+4pqSWVPAtLVXTzQ==} - engines: {node: '>=18'} - devalue@5.8.1: resolution: {integrity: sha512-4CXDYRBGqN+57wVJkuXBYmpAVUSg3L6JAQa/DFqm238G73E1wuyc/JhGQJzN7vUf/CMphYau2zXbfWzDR5aTEw==} @@ -1707,9 +1660,6 @@ packages: resolution: {integrity: sha512-9S6m9Sukh1cZNknO1CWAr2QAWsbKLafQiyM5gZ7VgXHeuaoUwffKN4q6NC4A/Mf9iiPlOXQEKW/Mv/mh9/3YFA==} hasBin: true - dlv@1.1.3: - resolution: {integrity: sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==} - dom-serializer@2.0.0: resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==} @@ -1730,12 +1680,6 @@ packages: emmet@2.4.11: resolution: {integrity: sha512-23QPJB3moh/U9sT4rQzGgeyyGIrcM+GH5uVYg2C6wZIxAIJq7Ng3QLT79tl8FUwDXhyq9SusfknOrofAKqvgyQ==} - emoji-regex-xs@1.0.0: - resolution: {integrity: sha512-LRlerrMYoIDrT6jgpeZ2YYl/L8EulRTt5hQcYjy5AInh7HWXKimpqx68aknBFpGL2+/IcogTcaydJEgaTmOpDg==} - - emoji-regex@10.6.0: - resolution: {integrity: sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==} - emoji-regex@8.0.0: resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} @@ -1755,8 +1699,8 @@ packages: resolution: {integrity: sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==} engines: {node: '>=0.12'} - es-module-lexer@1.7.0: - resolution: {integrity: sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==} + es-module-lexer@2.3.2: + resolution: {integrity: sha512-poHGpORABojJJucnV9KbOavETW8lBVnphkW77ER5/BQ5Fz7oXSoCNek7IH3vR5nRjdsEz926ibFYX8KtLQmdyw==} esast-util-from-estree@2.0.0: resolution: {integrity: sha512-4CyanoAudUSBAn5K13H4JhsMH6L9ZP7XbLVe/dKybkxMO7eDyLsT8UHl9TRNrU2Gr9nz+FovfSIjuXWJ81uVwQ==} @@ -1764,13 +1708,8 @@ packages: esast-util-from-js@2.0.1: resolution: {integrity: sha512-8Ja+rNJ0Lt56Pcf3TAmpBZjmx8ZcK5Ts4cAzIOjsjevg9oSXJnl6SUQ2EevU8tv3h6ZLWmoKL5H4fgWvdvfETw==} - esbuild@0.25.12: - resolution: {integrity: sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==} - engines: {node: '>=18'} - hasBin: true - - esbuild@0.27.7: - resolution: {integrity: sha512-IxpibTjyVnmrIQo5aqNpCgoACA/dTKLTlhMHihVHhdkxKyPO1uBBthumT0rdHmcsk9uMonIWS0m4FljWzILh3w==} + esbuild@0.28.2: + resolution: {integrity: sha512-HKVLS8dvII+xoKW9kmqxbRKrnWEXfJJr/FZhhJmiqIB0e053QNYFqOBouTMO/k5sID4MvCiUCvv8b9M4h32wIA==} engines: {node: '>=18'} hasBin: true @@ -1813,8 +1752,8 @@ packages: resolution: {integrity: sha512-kJezFj9YFAMLeORyi7aCLxLbD5/qWMQnoMVlVPyHIll7lgRJCc3JVln9Vgl9nwQi0YkMnhdGTMNn7CkRRAptMg==} engines: {node: '>=18.0.0'} - expressive-code@0.38.3: - resolution: {integrity: sha512-COM04AiUotHCKJgWdn7NtW2lqu8OW8owAidMpkXt1qxrZ9Q2iC7+tok/1qIn2ocGnczvr9paIySgGnEwFeEQ8Q==} + expressive-code@0.44.1: + resolution: {integrity: sha512-GakidxhapWDzpKLqEaFQ8wGk6gAqEtPQibu8+yPBfnDLgev5Vdsh1pasTxnrXL/mzIknyqeTwhMHTghdaiUrTg==} extend@3.0.2: resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==} @@ -1822,9 +1761,18 @@ packages: fast-deep-equal@3.1.3: resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + fast-string-truncated-width@3.0.3: + resolution: {integrity: sha512-0jjjIEL6+0jag3l2XWWizO64/aZVtpiGE3t0Zgqxv0DPuxiMjvB3M24fCyhZUO4KomJQPj3LTSUnDP3GpdwC0g==} + + fast-string-width@3.0.2: + resolution: {integrity: sha512-gX8LrtNEI5hq8DVUfRQMbr5lpaS4nMIWV+7XEbXk2b8kiQIizgnlr12B4dA3ZEx3308ze0O4Q1R+cHts8kyUJg==} + fast-uri@3.1.2: resolution: {integrity: sha512-rVjf7ArG3LTk+FS6Yw81V1DLuZl1bRbNrev6Tmd/9RaroeeRRJhAt7jg/6YFxbvAQXUCavSoZhPPj6oOx+5KjQ==} + fast-wrap-ansi@0.2.2: + resolution: {integrity: sha512-7F2Fl+TjRSenLqlU3UjSH0iyqopqoZIu7eZVpEirP2g1GtWa2G/ecEmBdgz31+Mxr+ELclgg6sokpSFIQiZ02Q==} + fdir@6.5.0: resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} engines: {node: '>=12.0.0'} @@ -1838,6 +1786,10 @@ packages: resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} engines: {node: '>=8'} + find-process@2.1.1: + resolution: {integrity: sha512-SrQDx3QhlmHM90iqn9rdjCQcw/T+WlpOkHFsjoRgB+zTpDfltNA1VSNYeYELwhUTJy12UFxqjWhmhOrJc+o4sA==} + hasBin: true + flatted@3.4.2: resolution: {integrity: sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==} @@ -1872,14 +1824,14 @@ packages: resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} engines: {node: 6.* || 8.* || >= 10.*} - get-east-asian-width@1.6.0: - resolution: {integrity: sha512-QRbvDIbx6YklUe6RxeTeleMR0yv3cYH6PsPZHcnVn7xv7zO1BHN8r0XETu8n6Ye3Q+ahtSarc3WgtNWmehIBfA==} - engines: {node: '>=18'} - get-own-enumerable-keys@1.0.0: resolution: {integrity: sha512-PKsK2FSrQCyxcGHsGrLDcK0lx+0Ke+6e8KFFozA9/fIQLhQzPaRvJFdcz7+Axg3jUH/Mq+NI4xa5u/UT2tQskA==} engines: {node: '>=14.16'} + get-tsconfig@5.0.0-beta.4: + resolution: {integrity: sha512-7nF7C9fIPFEMHgEMEfgIlO9wDdZ8CyHw27rWciFZfHvHDReIiPhsYuzPRXsfvBCqFy1l8RRyyWV7QLM+ZhUJsQ==} + engines: {node: '>=20.20.0'} + github-slugger@2.0.0: resolution: {integrity: sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw==} @@ -1897,6 +1849,10 @@ packages: h3@1.15.11: resolution: {integrity: sha512-L3THSe2MPeBwgIZVSH5zLdBBU90TOxarvhK9d04IDY2AmVS8j2Jz2LIWtwsGOU3lu2I5jCN7FNvVfY2+XyF+mg==} + has-flag@4.0.0: + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} + engines: {node: '>=8'} + hast-util-embedded@3.0.0: resolution: {integrity: sha512-naH8sld4Pe2ep03qqULEtvYr7EjrLK2QHY8KJR6RJkTUjPGObe1vnx585uzem2hGra+s1q08DZZpfgDVYRbaXA==} @@ -1979,16 +1935,18 @@ packages: http-cache-semantics@4.2.0: resolution: {integrity: sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==} - i18next@23.16.8: - resolution: {integrity: sha512-06r/TitrM88Mg5FdUXAKL96dJMzgqLE5dv3ryBAra4KCwD9mJ4ndOTS95ZuymIGoE+2hzfdaMak2X11/es7ZWg==} + i18next@26.3.6: + resolution: {integrity: sha512-Bu5Z2nAXgfVyM8xvW3jk9EKRIuX37PudsrBViThNFx7CR7aaYTpP01cxNB/E4c4UUzTDiAZRstEhsRfPOL/8xA==} + peerDependencies: + typescript: ^5 || ^6 || ^7 + peerDependenciesMeta: + typescript: + optional: true identifier-regex@1.1.0: resolution: {integrity: sha512-SLX4H/vtcYlYnL7XqnuJKHU7Z8517TgsW9nmQiGOgMCjQ8V/deLYu6bEmbGoXe7WMMhc9+EUGyFFneHja8KabA==} engines: {node: '>=18'} - import-meta-resolve@4.2.0: - resolution: {integrity: sha512-Iqv2fzaTQN28s/FwZAoFq0ZSs/7hMAHJVX+w8PZl3cY19Pxk6jFFalxQoIfW2826i/fDLXv8IiEZRIT0lDuWcg==} - inline-style-parser@0.2.7: resolution: {integrity: sha512-Nb2ctOyNR8DqQoR0OwRG95uNWIC0C1lCgf5Naz5H6Ji72KZ8OcFZLz2P5sNgwlyoJ8Yif11oMuYs5pBQa86csA==} @@ -2012,9 +1970,9 @@ packages: is-decimal@2.0.1: resolution: {integrity: sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==} - is-docker@3.0.0: - resolution: {integrity: sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + is-docker@4.0.0: + resolution: {integrity: sha512-LHE+wROyG/Y/0ZnbktRCoTix2c1RhgWaZraMZ8o1Q7zCh0VSrICJQO5oqIIISrcSBtrXv0o233w1IYwsWCjTzA==} + engines: {node: '>=20'} hasBin: true is-extglob@2.1.1: @@ -2036,11 +1994,6 @@ packages: resolution: {integrity: sha512-NhOds0mDx9lJu+1lBRO0xbwFo5nobA7GCk/0e5xjr6+6XugX985+0OyGX35BNrTkPAsdLcIKg02HUQJOK8D8kw==} engines: {node: '>=18'} - is-inside-container@1.0.0: - resolution: {integrity: sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==} - engines: {node: '>=14.16'} - hasBin: true - is-number@7.0.0: resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} engines: {node: '>=0.12.0'} @@ -2057,10 +2010,6 @@ packages: resolution: {integrity: sha512-rbku49cWloU5bSMI+zaRaXdQHXnthP6DZ/vLnfdSKyL4zUzuWnomtOEiZZOd+ioQ+avFo/qau3KPTc7Fjy1uPA==} engines: {node: '>=12'} - is-wsl@3.1.1: - resolution: {integrity: sha512-e6rvdUCiQCAuumZslxRJWR/Doq4VpPR82kqclvcS0efgt430SlGIk05vdCN58+VrzgtIcfNODjozVielycD4Sw==} - engines: {node: '>=16'} - jiti@2.7.0: resolution: {integrity: sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==} hasBin: true @@ -2068,8 +2017,8 @@ packages: js-base64@3.8.1: resolution: {integrity: sha512-5xVjhUZlHHeuO2W7w2rDFj/Kl1xLX+HjZxdOQwCsUOifl6UaoH1o1wsbsTMz+r0aeC7gCijvru02j6TfKZWzKg==} - js-yaml@4.1.1: - resolution: {integrity: sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==} + js-yaml@4.3.1: + resolution: {integrity: sha512-CY6crGq313MX8GkwvB7tzgp99vjQxY1++5y10/BKN/GUfHqWaOGQMNZkBvqSzsZKWk/ijwHlWzzkLulsGHhjWQ==} hasBin: true json-schema-traverse@1.0.0: @@ -2084,44 +2033,74 @@ packages: jsonc-parser@3.3.1: resolution: {integrity: sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==} - kleur@3.0.3: - resolution: {integrity: sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==} - engines: {node: '>=6'} - kleur@4.1.5: resolution: {integrity: sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==} engines: {node: '>=6'} + klona@2.0.6: + resolution: {integrity: sha512-dhG34DXATL5hSxJbIexCft8FChFXtmskoZYnoPWjXQuebWYCNkVeV3KkGegCK9CP1oswI/vQibS2GY7Em/sJJA==} + engines: {node: '>= 8'} + lightningcss-android-arm64@1.32.0: resolution: {integrity: sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==} engines: {node: '>= 12.0.0'} cpu: [arm64] os: [android] + lightningcss-android-arm64@1.33.0: + resolution: {integrity: sha512-gEpRTalKdosp4Bb8qWtc2iOgE5SeIHlpS1up9bFq2wAyYhl1UdTObYiHe98zEM9SQvSoqQZ1IQD0JNpg3Ml5pg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [android] + lightningcss-darwin-arm64@1.32.0: resolution: {integrity: sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==} engines: {node: '>= 12.0.0'} cpu: [arm64] os: [darwin] + lightningcss-darwin-arm64@1.33.0: + resolution: {integrity: sha512-Sciaz8eenNTKn9b3t7+xr0ipTp9YxKQY4npwQ3mrRuL0BAVHBLyZxofhaKBAVtzmtRZ/zTyo0/to4B1uWG/Djg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [darwin] + lightningcss-darwin-x64@1.32.0: resolution: {integrity: sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [darwin] + lightningcss-darwin-x64@1.33.0: + resolution: {integrity: sha512-Z5UPAxzrjlWNNyGy6i65cJzzvgJ5D3T6wMvs+gWpY9d7qRhANrxqAp6LhxIgZhWEw18RfJTGcRxjuLIBr+m8XQ==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [darwin] + lightningcss-freebsd-x64@1.32.0: resolution: {integrity: sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [freebsd] + lightningcss-freebsd-x64@1.33.0: + resolution: {integrity: sha512-QQM/Ti/hQajJwCY+RiWuCZ9sdtI/XQk7nDK5vC8kkdwixezOlDgvDx7+RT+QjK6FcFT4MpsuoBnHIo/O3StRRg==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [freebsd] + lightningcss-linux-arm-gnueabihf@1.32.0: resolution: {integrity: sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==} engines: {node: '>= 12.0.0'} cpu: [arm] os: [linux] + lightningcss-linux-arm-gnueabihf@1.33.0: + resolution: {integrity: sha512-N7FVBe6iS24MlM6R/4RBTxGhQheZGs7tiQ9U32UtF75NzP5Q7xWPRqLBCKxlRQRk3rY1jCIPLzx7WzOhuUIRLQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm] + os: [linux] + lightningcss-linux-arm64-gnu@1.32.0: resolution: {integrity: sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==} engines: {node: '>= 12.0.0'} @@ -2129,6 +2108,13 @@ packages: os: [linux] libc: [glibc] + lightningcss-linux-arm64-gnu@1.33.0: + resolution: {integrity: sha512-j2v/itmy4HlNxlc6voKXYgBqNi0Ng2LShg4z7GufpEgs05P+2suBVyi9I6YHq5uoVFx9ETin3eCEhLVyXGQnKg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + libc: [glibc] + lightningcss-linux-arm64-musl@1.32.0: resolution: {integrity: sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==} engines: {node: '>= 12.0.0'} @@ -2136,6 +2122,13 @@ packages: os: [linux] libc: [musl] + lightningcss-linux-arm64-musl@1.33.0: + resolution: {integrity: sha512-yiO5ROMuYQgXbC60yjZU5CYSFZGKXL0HFATXt9mHJn1+zW55oCtMI9NfcVhYLMFDL7gV7oBPon/EmMMGg2OvtQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + libc: [musl] + lightningcss-linux-x64-gnu@1.32.0: resolution: {integrity: sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==} engines: {node: '>= 12.0.0'} @@ -2143,6 +2136,13 @@ packages: os: [linux] libc: [glibc] + lightningcss-linux-x64-gnu@1.33.0: + resolution: {integrity: sha512-ar+Ju7LmcN0Jo4FpL4hpFybwNG9/3A/Br5KW2n2jyODg3MEZXaDYADdemoNS+BDNfMgKvylJLj4S5tyRActuAg==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + libc: [glibc] + lightningcss-linux-x64-musl@1.32.0: resolution: {integrity: sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==} engines: {node: '>= 12.0.0'} @@ -2150,22 +2150,49 @@ packages: os: [linux] libc: [musl] + lightningcss-linux-x64-musl@1.33.0: + resolution: {integrity: sha512-RYiYbkokw0trfKqqzfF55lginwEPrD3OJDfTuJzFs1MK6iFnDenaz1fqLLtX4ITG3OktJQXOeTaw1awrBAlZPw==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + libc: [musl] + lightningcss-win32-arm64-msvc@1.32.0: resolution: {integrity: sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==} engines: {node: '>= 12.0.0'} cpu: [arm64] os: [win32] + lightningcss-win32-arm64-msvc@1.33.0: + resolution: {integrity: sha512-1K+MPfLSFVpphzpdbfkhlWk6wBrTObBzS2T6db10PNOZgR9GoVsAWzwNyuhUYYbTp23j+4RrncfujZ4uAzXvwA==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [win32] + lightningcss-win32-x64-msvc@1.32.0: resolution: {integrity: sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [win32] + lightningcss-win32-x64-msvc@1.33.0: + resolution: {integrity: sha512-OlEICDx/Xl0FqSp4bry8zFnCvGpig3Gl4gCquvYwHuqJKEC1+n9NgDniFvqHGmMv1ZkqDJrDqKKSykTDX+ehuA==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [win32] + lightningcss@1.32.0: resolution: {integrity: sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==} engines: {node: '>= 12.0.0'} + lightningcss@1.33.0: + resolution: {integrity: sha512-WkUDrojuJs0xkgGf2udWxa3yGBRxPtxUkB79i6aCZLRgc7PM8fZe9TosfPDcvEpQZbuFASnHYmRLBLUbmLOIIA==} + engines: {node: '>= 12.0.0'} + + loglevel@1.9.2: + resolution: {integrity: sha512-HgMmCqIJSAKqo68l0rS2AanEWfkxaZ5wNiEFb5ggm08lDs9Xl2KxBlX3PTcaD2chBM1gXAYf491/M2Rv8Jwayg==} + engines: {node: '>= 0.6.0'} + longest-streak@3.1.0: resolution: {integrity: sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==} @@ -2179,6 +2206,9 @@ packages: magic-string@0.30.21: resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} + magic-string@1.2.1: + resolution: {integrity: sha512-vCfXkt3lIJha02CjPT1igeysyHVfCsEpIeD20O+X9aJ2hML3/kKx8E9Iv1FB+aMSAlDOEAtpRWzuooQCrYwdUg==} + magicast@0.5.3: resolution: {integrity: sha512-pVKE4UdSQ7DvHzivsCIFx2BJn1mHG6KsyrFcaxFx6tONdneEuThrDx0Cj3AMg58KyN4pzYT+LHOotxDQDjNvkw==} @@ -2259,8 +2289,8 @@ packages: micromark-core-commonmark@2.0.3: resolution: {integrity: sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==} - micromark-extension-directive@3.0.2: - resolution: {integrity: sha512-wjcXHgk+PPdmvR58Le9d7zQYWy+vKEU9Se44p2CrCDPiLr2FMyiT4Fyb5UFKFC66wGB3kPlgD7q3TnoqPS7SZA==} + micromark-extension-directive@4.0.0: + resolution: {integrity: sha512-/C2nqVmXXmiseSSuCdItCMho7ybwwop6RrrRPk0KbOHW21JKoCldC+8rFOaundDoRBUWBnJJcxeA/Kvi34WQXg==} micromark-extension-gfm-autolink-literal@2.1.0: resolution: {integrity: sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw==} @@ -2379,13 +2409,18 @@ packages: engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true + nanoid@3.3.18: + resolution: {integrity: sha512-DTg4MJbGMWkfi6VZFdNt2/caMbQy4Ou+Op/hJQvGEWcnVfoA1QA+xzRKAzw9jD6+GVOOeYr/mIcuDSdug6F6+w==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + nanoid@5.1.16: resolution: {integrity: sha512-kVrnsrJqMR8+oLJnGEmSWw9BivK5mt7H3FZatVRjrc5wGqFYuBxX1yG7+A7Gi5AefkX6t/oCkizcQgpu0cY1dQ==} engines: {node: ^18 || >=20} hasBin: true - neotraverse@0.6.18: - resolution: {integrity: sha512-Z4SmBUweYa09+o6pG+eASabEpP6QkQ70yHj351pQoEXIs8uHbaU2DWVmzBANKgflPa47A50PtB2+NgRpQvr7vA==} + neotraverse@1.0.1: + resolution: {integrity: sha512-WmmLty1YWwJl9yZi77v2dVIV6X2kuYV8YYBI/G3LWGKdGHmHUvL1z7FW0iDvEvGAwNEoc5x1tOOOyDnf5jJw/w==} engines: {node: '>= 10'} neverpanic@0.0.8: @@ -2407,6 +2442,10 @@ packages: nth-check@2.1.1: resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==} + obug@2.1.4: + resolution: {integrity: sha512-4a+OsYv9UktOJKE+l1A4OufDgdRF9PifWj+tJnHURo/P+WOxpG4GzUFL9qCalmWauao6ogiG+QvnCovwPoyAWA==} + engines: {node: '>=12.20.0'} + ofetch@1.5.1: resolution: {integrity: sha512-2W4oUZlVaqAPAil6FUg/difl6YhqhUR7x2eZY4bQCko22UXg3hptq9KLQdqFClV+Wu85UX7hNtdGTngi/1BxcA==} @@ -2416,9 +2455,6 @@ packages: oniguruma-parser@0.12.2: resolution: {integrity: sha512-6HVa5oIrgMC6aA6WF6XyyqbhRPJrKR02L20+2+zpDtO5QAzGHAUGw5TKQvwi5vctNnRHkJYmjAhRVQF2EKdTQw==} - oniguruma-to-es@2.3.0: - resolution: {integrity: sha512-bwALDxriqfKGfUufKGGepCzu9x7nJQuoRoAFp4AnwehhC2crqrDIAP/uN2qdlsAvSMpeRC3+Yzhqc7hLmle5+g==} - oniguruma-to-es@4.3.6: resolution: {integrity: sha512-csuQ9x3Yr0cEIs/Zgx/OEt9iBw9vqIunAPQkx19R/fiMq2oGVTgcMqO/V3Ybqefr1TBvosI6jU539ksaBULJyA==} @@ -2426,22 +2462,26 @@ packages: resolution: {integrity: sha512-Q6Bekk5wpzW5qIyUP4gdMEujObYstZl6DMMOSenwBvV0BlE5LkDwkjs5yHbZmdCEq2o4RJx4tE1vwxFVf2FG1w==} engines: {node: '>=16.17'} - p-limit@6.2.0: - resolution: {integrity: sha512-kuUqqHNUqoIWp/c467RI4X6mmyuojY5jGutNU0wVTmEOOfcuwLqyMVoAi9MKi2Ak+5i9+nhmrK4ufZE8069kHA==} - engines: {node: '>=18'} + p-limit@7.3.1: + resolution: {integrity: sha512-0trZaiG7Y7kN/Egy9a8j47t9osC0Tch4PaIWd9yGF6bvmlk7muExRvGNYb8sXBwEKMoNKsbNN9P8EefuQekE4Q==} + engines: {node: '>=20'} p-map@7.0.4: resolution: {integrity: sha512-tkAQEw8ysMzmkhgw8k+1U/iPhWNhykKnSk4Rd5zLoPJCuJaGRPo6YposrZgaxHKzDHdDWWZvE/Sk7hsL2X/CpQ==} engines: {node: '>=18'} - p-queue@8.1.1: - resolution: {integrity: sha512-aNZ+VfjobsWryoiPnEApGGmf5WmNsCo9xu8dfaYamG5qaLP7ClhLN6NgsFe6SwJ2UbLEBK5dv9x8Mn5+RVhMWQ==} - engines: {node: '>=18'} + p-queue@9.3.3: + resolution: {integrity: sha512-NXAOdnEe5FsZJfT4oK84lE1Y5cFFdWlRuOo5tww8DyNMxyRXwn39fIkUtNLKppcPC+UYU/bXujNCUGDv01y7CA==} + engines: {node: '>=20'} p-timeout@6.1.4: resolution: {integrity: sha512-MyIV3ZA/PmyBN/ud8vV9XzwTrNtR4jFrObymZYnZqMmW0zA8Z17vnT0rBgFE/TlohB+YCHqXMgZzb3Csp49vqg==} engines: {node: '>=14.16'} + p-timeout@7.0.1: + resolution: {integrity: sha512-AxTM2wDGORHGEkPCt8yqxOTMgpfbEHqF51f/5fJCmwFC3C/zNcGT63SymH2ttOAaiIws2zVg4+izQCjrakcwHg==} + engines: {node: '>=20'} + package-manager-detector@1.6.0: resolution: {integrity: sha512-61A5ThoTiDG/C8s8UMZwSorAGwMJ0ERVGj2OjoW5pAalsNOg15+iQiPzrLJ4jhZ1HJzmC2PIHT2oEiH3R5fzNA==} @@ -2482,6 +2522,10 @@ packages: resolution: {integrity: sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==} engines: {node: '>=12'} + picomatch@4.0.5: + resolution: {integrity: sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==} + engines: {node: '>=12'} + postcss-nested@6.2.0: resolution: {integrity: sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==} engines: {node: '>=12.0'} @@ -2496,6 +2540,10 @@ packages: resolution: {integrity: sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==} engines: {node: ^10 || ^12 || >=14} + postcss@8.5.26: + resolution: {integrity: sha512-u82N74LFzG8ca+dD8puPnplTXoGH4fTPpVGuIbt36G3qvNlkvfD0lEAZSxaly3KX8TS/L1A1gsCEmvKmBcVbkQ==} + engines: {node: ^10 || ^12 || >=14} + prettier@3.8.3: resolution: {integrity: sha512-7igPTM53cGHMW8xWuVTydi2KO233VFiTNyF5hLJqpilHfmn8C8gPf+PS7dUT64YcXFbiMGZxS9pCSxL/Dxm/Jw==} engines: {node: '>=14'} @@ -2509,9 +2557,9 @@ packages: resolution: {integrity: sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw==} engines: {node: '>=6'} - prompts@2.4.2: - resolution: {integrity: sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==} - engines: {node: '>= 6'} + process-ancestry@0.1.0: + resolution: {integrity: sha512-tGqJW/UnclpYASFcM6Xh8D8l/BMtaQ9+CSG0vlJSJTcdMM4lDRv4c6H0Pdcsfted+bVczdYSfk2fdukg2gQkZg==} + engines: {node: '>=18.0.0'} property-information@7.1.0: resolution: {integrity: sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ==} @@ -2550,23 +2598,17 @@ packages: recma-stringify@1.0.0: resolution: {integrity: sha512-cjwII1MdIIVloKvC9ErQ+OgAtwHBmcZ0Bg4ciz78FtbT8In39aAYbaA7zvxQ61xVMSPE8WxhLwLbhif4Js2C+g==} - regex-recursion@5.1.1: - resolution: {integrity: sha512-ae7SBCbzVNrIjgSbh7wMznPcQel1DNlDtzensnFxpiNpXt1U2ju/bHugH422r+4LAVS1FpW1YCwilmnNsjum9w==} - regex-recursion@6.0.2: resolution: {integrity: sha512-0YCaSCq2VRIebiaUviZNs0cBz1kg5kVS2UKUfNIx8YVs1cN3AV7NTctO5FOKBA+UT2BPJIWZauYHPqJODG50cg==} regex-utilities@2.3.0: resolution: {integrity: sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng==} - regex@5.1.1: - resolution: {integrity: sha512-dN5I359AVGPnwzJm2jN1k0W9LPZ+ePvoOeVMMfqIMFz53sSwXkxaJoxr50ptnsC771lK95BnTrVSZxq0b9yCGw==} - regex@6.1.0: resolution: {integrity: sha512-6VwtthbV4o/7+OaAF9I5L5V3llLEsoPyq9P1JVXkedTP33c7MfCG0/5NOPcSJn0TzXcG9YUrR0gQSWioew3LDg==} - rehype-expressive-code@0.38.3: - resolution: {integrity: sha512-RYSSDkMBikoTbycZPkcWp6ELneANT4eTpND1DSRJ6nI2eVFUwTBDCvE2vO6jOOTaavwnPiydi4i/87NRyjpdOA==} + rehype-expressive-code@0.44.1: + resolution: {integrity: sha512-+VZgs7Evw4LXRN3owpoBNSTpYuW6GeOdjqcUT1TuY8o/4MGPtbd0EU7Bgrju7X8KrQ6SslOBAuGWJ5fV5TriJQ==} rehype-external-links@3.0.0: resolution: {integrity: sha512-yp+e5N9V3C6bwBeAC4n796kc86M4gJCdlVhiMTxIrJG5UHDMh+PJANf9heqORJbt1nrCbDwIlAZKjANIaVBbvw==} @@ -2592,8 +2634,8 @@ packages: rehype@13.0.2: resolution: {integrity: sha512-j31mdaRFrwFRUIlxGeuPXXKWQxet52RBQRvCmzl5eCefn/KGbomK5GMHNMsOJf55fgo3qw5tST5neDuarDYR2A==} - remark-directive@3.0.1: - resolution: {integrity: sha512-gwglrEQEZcZYgVyG1tQuA+h58EZfq5CSULw7J90AFuCTyib1thgHPoqQ+h9iFvU6R+vnZ5oNFQR5QKgGpk741A==} + remark-directive@4.0.0: + resolution: {integrity: sha512-7sxn4RfF1o3izevPV1DheyGDD6X4c9hrGpfdUpm7uC++dqrnJxIZVkk7CoKqcLm0VUMAuOol7Mno3m6g8cfMuA==} remark-gfm@4.0.1: resolution: {integrity: sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg==} @@ -2632,6 +2674,9 @@ packages: resolution: {integrity: sha512-yE7KUfFvaBFzGPs5H3Ops1RevfUEsDc5Iz65rOwWg4lE8HJSYtle77uul3+573457oHvBKuHYDl/xqUkKpEEdw==} engines: {node: '>=18'} + resolve-pkg-maps@1.0.0: + resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} + retext-latin@4.0.0: resolution: {integrity: sha512-hv9woG7Fy0M9IlRQloq/N6atV82NxLGveq+3H2WOi79dtIYWN8OaxogDm77f8YnVXJL2VD3bbqowu5E3EMhBYA==} @@ -2644,11 +2689,17 @@ packages: retext@9.0.0: resolution: {integrity: sha512-sbMDcpHCNjvlheSgMfEcVrZko3cDzdbe1x/e7G66dFp0Ff7Mldvi2uv6JkJQzdRcvLYE8CA8Oe8siQx8ZOgTcA==} - rollup@4.60.4: - resolution: {integrity: sha512-WHeFSbZYsPu3+bLoNRUuAO+wavNlocOPf3wSHTP7hcFKVnJeWsYlCDbr3mTS14FCizf9ccIxXA8sGL8zKeQN3g==} - engines: {node: '>=18.0.0', npm: '>=8.0.0'} + rolldown@1.2.5: + resolution: {integrity: sha512-VD2IE5PUG4Oj8zz2VGykiYd5wbnjdIiSsNQb8Qu5B+noEp+A78mu2iVvpp27g8es14Tk9rofNs5Tku9iQCS4fA==} + engines: {node: ^20.19.0 || >=22.12.0} hasBin: true + satteri@0.10.4: + resolution: {integrity: sha512-EFgJouHlS3aV9/ATS73J9h4/pgW8Q+ZBLP0TpUUTfbZxh/cZ47ewuhYDR3LYQ9WbTPPAon5sDfLdBebrr16qeA==} + + satteri@0.9.5: + resolution: {integrity: sha512-ZuWVl+vnM64y+/TtX8Kosv2c00W+hLQiiwnEL6H0UKVVrxFqMw4D2CJHHQaouVd89OAhtBBfjWLqhKi3TVUV4w==} + sax@1.6.0: resolution: {integrity: sha512-6R3J5M4AcbtLUdZmRv2SygeVaM7IhrLXu9BmnOGmmACak8fiUtOsYNWUS4uK7upbmHIBbLBeFeI//477BKLBzA==} engines: {node: '>=11.0.0'} @@ -2665,11 +2716,9 @@ packages: resolution: {integrity: sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - shiki@1.29.2: - resolution: {integrity: sha512-njXuliz/cP+67jU2hukkxCNuH1yUi4QfdZZY+sMr5PPrIyXSu5iTb/qYC4BiWWB0vZ+7TbdvYUCeL23zpwCfbg==} - - shiki@3.23.0: - resolution: {integrity: sha512-55Dj73uq9ZXL5zyeRPzHQsK7Nbyt6Y10k5s7OjuFZGMhpp4r/rsLBH0o/0fstIzX1Lep9VxefWljK/SKCzygIA==} + shiki@4.4.3: + resolution: {integrity: sha512-Mb/GvXPHBAXdgGIcnfU5L3ldpn1XcxrGkPHwqgRx17/I2XRfqlFKk2vGkHWINn1kdXvzJZeuO3is6I9KLPFm0g==} + engines: {node: '>=20'} sisteransi@1.0.5: resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==} @@ -2709,10 +2758,6 @@ packages: resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} engines: {node: '>=8'} - string-width@7.2.0: - resolution: {integrity: sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==} - engines: {node: '>=18'} - stringify-entities@4.0.4: resolution: {integrity: sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==} @@ -2724,10 +2769,6 @@ packages: resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} engines: {node: '>=8'} - strip-ansi@7.2.0: - resolution: {integrity: sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==} - engines: {node: '>=12'} - style-mod@4.1.3: resolution: {integrity: sha512-i/n8VsZydrugj3Iuzll8+x/00GH2vnYsk1eomD8QiRrSAeW6ItbCQDtfXCeJHd0iwiNagqjQkvpvREEPtW3IoQ==} @@ -2741,6 +2782,10 @@ packages: resolution: {integrity: sha512-WHkws2ZflZe41zj6AolvvmaTrWds/VuyeYr9iPVv/oQeaIoVxMKaushfFWpOGDT+GuBrM/sVqF8KUCYQlSSTdQ==} engines: {node: '>=18'} + supports-color@7.2.0: + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} + engines: {node: '>=8'} + svgo@4.0.1: resolution: {integrity: sha512-XDpWUOPC6FEibaLzjfe0ucaV0YrOjYotGJO1WpF0Zd+n6ZGEQUsSugaoLq9QkEZtAfQIxT42UChcssDVPP3+/w==} engines: {node: '>=16'} @@ -2775,6 +2820,10 @@ packages: tiny-inflate@1.0.3: resolution: {integrity: sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw==} + tinyclip@0.1.15: + resolution: {integrity: sha512-uo33abH+Ays0xYaDysoBt494Hb3hsEczMpcC0MwFl773pazORx4fmvKhclhR1wonUbB6vvpRsvVMwnhfqeMc+A==} + engines: {node: ^16.14.0 || >= 17.3.0} + tinyexec@1.2.2: resolution: {integrity: sha512-M/Q0B2cp4K7kynaT/vnED1j8TlLY+Pp7C6Wl2bl/7u/F0mUVwdyOpwomQb8JpYLitHUssAJRmLZdMCGsrx7i+g==} engines: {node: '>=18'} @@ -2783,6 +2832,10 @@ packages: resolution: {integrity: sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==} engines: {node: '>=12.0.0'} + tinyglobby@0.2.17: + resolution: {integrity: sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==} + engines: {node: '>=12.0.0'} + to-regex-range@5.0.1: resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} engines: {node: '>=8.0'} @@ -2797,16 +2850,6 @@ packages: resolution: {integrity: sha512-QVsbr1WhGLq2F0oDyYbqtOXcf3gcnL8C9H5EX8bBwAr8ZWvWGJzukpPrDrWgJMrNtgDbo74BIjI4kJu3q2xQWw==} engines: {node: '>=18.18.0'} - tsconfck@3.1.6: - resolution: {integrity: sha512-ks6Vjr/jEw0P1gmOVwutM3B7fWxoWBL2KRDb1JfqGVawBmO5UsvmWOQFGHBPl5yxYz4eERr19E6L7NMv+Fej4w==} - engines: {node: ^18 || >=20} - hasBin: true - peerDependencies: - typescript: ^5.0.0 - peerDependenciesMeta: - typescript: - optional: true - tslib@2.8.1: resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} @@ -2841,14 +2884,18 @@ packages: undici-types@7.16.0: resolution: {integrity: sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==} + undici@8.10.0: + resolution: {integrity: sha512-HvltHd7avK13QIw/oLe4qoOLyoVSoafqJ2jYOrtMRBkbYT31eiBQ8O0ehRKZiEZCMEyLFQNIADpgCWC5fALvYQ==} + engines: {node: '>=22.19.0'} + unhead@2.1.15: resolution: {integrity: sha512-MCt5T90mCWyr3Z6pUCdM9lVRXoMoVBlL7z7U4CYVIiaDiuzad/UCfLuMqz5MeNmpZUgoBCQnrucJimU7EZR+XA==} unified@11.0.5: resolution: {integrity: sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==} - unifont@0.7.4: - resolution: {integrity: sha512-oHeis4/xl42HUIeHuNZRGEvxj5AaIKR+bHPNegRq5LV1gdc3jundpONbjglKpihmJf+dswygdMJn3eftGIMemg==} + unifont@0.7.5: + resolution: {integrity: sha512-ULe/Cs+ZIsq+dcFofNkhqielCrUJnb5mr+Yc4EBM2VlL+6OZR6+cjtI2mT1bJvRBrVncqHAbLURxmPLcCXzWMg==} unist-util-find-after@5.0.0: resolution: {integrity: sha512-amQa0Ep2m6hE2g72AugUItjbuM8X8cGQnFoHk0pGfrFeT9GZhzN5SW8nRsiGKK7Aif4CrACPENkA6P/Lw6fHGQ==} @@ -2942,6 +2989,10 @@ packages: uploadthing: optional: true + url-extras@0.1.0: + resolution: {integrity: sha512-8tzwTeXFPuX/5PHuCDQE5Dd9Ts4rwoq2t9aIT+HS4iAVpmj5l4Ao7Q+BuuFjvWRqrLswBhQDk8O96ZicgCqQqw==} + engines: {node: '>=20'} + util-deprecate@1.0.2: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} @@ -2960,31 +3011,34 @@ packages: peerDependencies: vite: ^6.0.0 || ^7.0.0 || ^8.0.0 - vite@6.4.2: - resolution: {integrity: sha512-2N/55r4JDJ4gdrCvGgINMy+HH3iRpNIz8K6SFwVsA+JbQScLiC+clmAxBgwiSPgcG9U15QmvqCGWzMbqda5zGQ==} - engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} + vite@8.2.1: + resolution: {integrity: sha512-EU/eS7BH3XROHh2YnBefjM6DBKA6ZeMZEYQbj7NLWg5wHYlhB8B/Mayd5XsgWq+NFYccDOTemRpdETWR6Ka/lw==} + engines: {node: ^20.19.0 || >=22.12.0} hasBin: true peerDependencies: - '@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0 + '@types/node': ^20.19.0 || >=22.12.0 + '@vitejs/devtools': ^0.4.0 + esbuild: ^0.27.0 || ^0.28.0 jiti: '>=1.21.0' - less: '*' - lightningcss: ^1.21.0 - sass: '*' - sass-embedded: '*' - stylus: '*' - sugarss: '*' + less: ^4.0.0 + sass: ^1.70.0 + sass-embedded: ^1.70.0 + stylus: '>=0.54.8' + sugarss: ^5.0.0 terser: ^5.16.0 tsx: ^4.8.1 yaml: ^2.4.2 peerDependenciesMeta: '@types/node': optional: true + '@vitejs/devtools': + optional: true + esbuild: + optional: true jiti: optional: true less: optional: true - lightningcss: - optional: true sass: optional: true sass-embedded: @@ -3134,22 +3188,10 @@ packages: web-worker@1.5.0: resolution: {integrity: sha512-RiMReJrTAiA+mBjGONMnjVDP2u3p9R1vkcGz6gDIrOMT3oGuYwX2WRMYI9ipkphSuE5XKEhydbhNEJh4NY9mlw==} - which-pm-runs@1.1.0: - resolution: {integrity: sha512-n1brCuqClxfFfq/Rb0ICg9giSZqCS+pLtccdag6C2HyufBrh3fBOiy9nb6ggRMvWOVH5GrdJskj5iGTZNxd7SA==} - engines: {node: '>=4'} - - widest-line@5.0.0: - resolution: {integrity: sha512-c9bZp7b5YtRj2wOe6dlj32MK+Bx/M/d+9VB2SHM1OtsUHR0aV0tdP6DWh/iMt0kWi1t5g1Iudu6hQRNd1A4PVA==} - engines: {node: '>=18'} - wrap-ansi@7.0.0: resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} engines: {node: '>=10'} - wrap-ansi@9.0.2: - resolution: {integrity: sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==} - engines: {node: '>=18'} - xxhash-wasm@1.1.0: resolution: {integrity: sha512-147y/6YNh+tlp6nd/2pWq38i9h6mz/EuQ6njIrmW8D1BS5nCqs0P6DG+m6zTGnNz5I+uhZ0SHxBs9BsPrwcKDA==} @@ -3175,6 +3217,10 @@ packages: resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} engines: {node: '>=12'} + yargs-parser@22.0.0: + resolution: {integrity: sha512-rwu/ClNdSMpkSrUb+d6BRsSkLUq1fmfsY6TOpYzTwvwkg1/NRG85KBy3kq++A8LKQwX6lsu+aWad+2khvuXrqw==} + engines: {node: ^20.19.0 || ^22.12.0 || >=23} + yargs@17.7.2: resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} engines: {node: '>=12'} @@ -3183,28 +3229,6 @@ packages: resolution: {integrity: sha512-4LCcse/U2MHZ63HAJVE+v71o7yOdIe4cZ70Wpf8D/IyjDKYQLV5GD46B+hSTjJsvV5PztjvHoU580EftxjDZFQ==} engines: {node: '>=12.20'} - yocto-spinner@0.2.3: - resolution: {integrity: sha512-sqBChb33loEnkoXte1bLg45bEBsOP9N1kzQh5JZNKj/0rik4zAPTNSAVPj3uQAdc6slYJ0Ksc403G2XgxsJQFQ==} - engines: {node: '>=18.19'} - - yoctocolors@2.1.2: - resolution: {integrity: sha512-CzhO+pFNo8ajLM2d2IW/R93ipy99LWjtwblvC1RsoSUMZgyLbYFr221TnSNT7GjGdYui6P459mw9JH/g/zW2ug==} - engines: {node: '>=18'} - - zod-to-json-schema@3.25.2: - resolution: {integrity: sha512-O/PgfnpT1xKSDeQYSCfRI5Gy3hPf91mKVDuYLUHZJMiDFptvP41MSnWofm8dnCm0256ZNfZIM7DSzuSMAFnjHA==} - peerDependencies: - zod: ^3.25.28 || ^4 - - zod-to-ts@1.2.0: - resolution: {integrity: sha512-x30XE43V+InwGpvTySRNz9kB7qFU8DlyEy7BsSTCHPH1R0QasMmHWZDCzYm6bVXtj/9NNJAZF3jW8rzFvH5OFA==} - peerDependencies: - typescript: ^4.9.4 || ^5.0.2 - zod: ^3 - - zod@3.25.76: - resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==} - zod@4.4.3: resolution: {integrity: sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==} @@ -3251,9 +3275,72 @@ snapshots: - prettier - prettier-plugin-astro + '@astrojs/compiler-binding-darwin-arm64@0.3.2': + optional: true + + '@astrojs/compiler-binding-darwin-x64@0.3.2': + optional: true + + '@astrojs/compiler-binding-linux-arm64-gnu@0.3.2': + optional: true + + '@astrojs/compiler-binding-linux-arm64-musl@0.3.2': + optional: true + + '@astrojs/compiler-binding-linux-x64-gnu@0.3.2': + optional: true + + '@astrojs/compiler-binding-linux-x64-musl@0.3.2': + optional: true + + '@astrojs/compiler-binding-wasm32-wasi@0.3.2(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)': + dependencies: + '@napi-rs/wasm-runtime': 1.2.3(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1) + transitivePeerDependencies: + - '@emnapi/core' + - '@emnapi/runtime' + optional: true + + '@astrojs/compiler-binding-win32-arm64-msvc@0.3.2': + optional: true + + '@astrojs/compiler-binding-win32-x64-msvc@0.3.2': + optional: true + + '@astrojs/compiler-binding@0.3.2(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)': + optionalDependencies: + '@astrojs/compiler-binding-darwin-arm64': 0.3.2 + '@astrojs/compiler-binding-darwin-x64': 0.3.2 + '@astrojs/compiler-binding-linux-arm64-gnu': 0.3.2 + '@astrojs/compiler-binding-linux-arm64-musl': 0.3.2 + '@astrojs/compiler-binding-linux-x64-gnu': 0.3.2 + '@astrojs/compiler-binding-linux-x64-musl': 0.3.2 + '@astrojs/compiler-binding-wasm32-wasi': 0.3.2(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1) + '@astrojs/compiler-binding-win32-arm64-msvc': 0.3.2 + '@astrojs/compiler-binding-win32-x64-msvc': 0.3.2 + transitivePeerDependencies: + - '@emnapi/core' + - '@emnapi/runtime' + + '@astrojs/compiler-rs@0.3.2(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)': + dependencies: + '@astrojs/compiler-binding': 0.3.2(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1) + transitivePeerDependencies: + - '@emnapi/core' + - '@emnapi/runtime' + '@astrojs/compiler@2.13.1': {} - '@astrojs/internal-helpers@0.7.6': {} + '@astrojs/internal-helpers@0.10.4': + dependencies: + '@types/hast': 3.0.5 + '@types/mdast': 4.0.4 + js-yaml: 4.3.1 + picomatch: 4.0.5 + retext-smartypants: 6.2.0 + shiki: 4.4.3 + smol-toml: 1.6.1 + unified: 11.0.5 '@astrojs/language-server@2.16.10(prettier@3.8.3)(typescript@5.9.3)': dependencies: @@ -3280,15 +3367,13 @@ snapshots: transitivePeerDependencies: - typescript - '@astrojs/markdown-remark@6.3.11': + '@astrojs/markdown-remark@7.2.4': dependencies: - '@astrojs/internal-helpers': 0.7.6 - '@astrojs/prism': 3.3.0 + '@astrojs/internal-helpers': 0.10.4 + '@astrojs/prism': 4.0.2 github-slugger: 2.0.0 hast-util-from-html: 2.0.3 hast-util-to-text: 4.0.2 - import-meta-resolve: 4.2.0 - js-yaml: 4.1.1 mdast-util-definitions: 6.0.0 rehype-raw: 7.0.0 rehype-stringify: 10.0.1 @@ -3296,8 +3381,6 @@ snapshots: remark-parse: 11.0.0 remark-rehype: 11.1.2 remark-smartypants: 3.0.2 - shiki: 3.23.0 - smol-toml: 1.6.1 unified: 11.0.5 unist-util-remove-position: 5.0.0 unist-util-visit: 5.1.0 @@ -3306,13 +3389,21 @@ snapshots: transitivePeerDependencies: - supports-color - '@astrojs/mdx@4.3.14(astro@5.18.2(@types/node@24.12.4)(jiti@2.7.0)(lightningcss@1.32.0)(rollup@4.60.4)(typescript@5.9.3)(yaml@2.9.0))': + '@astrojs/markdown-satteri@0.3.7': dependencies: - '@astrojs/markdown-remark': 6.3.11 + '@astrojs/internal-helpers': 0.10.4 + '@astrojs/prism': 4.0.2 + github-slugger: 2.0.0 + satteri: 0.10.4 + + '@astrojs/mdx@7.0.7(@astrojs/markdown-satteri@0.3.7)(astro@7.2.4(@astrojs/markdown-remark@7.2.4)(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)(@types/node@24.12.4)(jiti@2.7.0)(yaml@2.9.0))': + dependencies: + '@astrojs/internal-helpers': 0.10.4 + '@astrojs/markdown-remark': 7.2.4 '@mdx-js/mdx': 3.1.1 acorn: 8.16.0 - astro: 5.18.2(@types/node@24.12.4)(jiti@2.7.0)(lightningcss@1.32.0)(rollup@4.60.4)(typescript@5.9.3)(yaml@2.9.0) - es-module-lexer: 1.7.0 + astro: 7.2.4(@astrojs/markdown-remark@7.2.4)(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)(@types/node@24.12.4)(jiti@2.7.0)(yaml@2.9.0) + es-module-lexer: 2.3.2 estree-util-visit: 2.0.0 hast-util-to-html: 9.0.5 piccolore: 0.1.3 @@ -3322,10 +3413,12 @@ snapshots: source-map: 0.7.6 unist-util-visit: 5.1.0 vfile: 6.0.3 + optionalDependencies: + '@astrojs/markdown-satteri': 0.3.7 transitivePeerDependencies: - supports-color - '@astrojs/prism@3.3.0': + '@astrojs/prism@4.0.2': dependencies: prismjs: 1.30.0 @@ -3335,47 +3428,50 @@ snapshots: stream-replace-string: 2.0.0 zod: 4.4.3 - '@astrojs/starlight@0.30.6(astro@5.18.2(@types/node@24.12.4)(jiti@2.7.0)(lightningcss@1.32.0)(rollup@4.60.4)(typescript@5.9.3)(yaml@2.9.0))': + '@astrojs/starlight@0.41.7(@astrojs/markdown-remark@7.2.4)(astro@7.2.4(@astrojs/markdown-remark@7.2.4)(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)(@types/node@24.12.4)(jiti@2.7.0)(yaml@2.9.0))(typescript@5.9.3)': dependencies: - '@astrojs/mdx': 4.3.14(astro@5.18.2(@types/node@24.12.4)(jiti@2.7.0)(lightningcss@1.32.0)(rollup@4.60.4)(typescript@5.9.3)(yaml@2.9.0)) + '@astrojs/markdown-satteri': 0.3.7 + '@astrojs/mdx': 7.0.7(@astrojs/markdown-satteri@0.3.7)(astro@7.2.4(@astrojs/markdown-remark@7.2.4)(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)(@types/node@24.12.4)(jiti@2.7.0)(yaml@2.9.0)) '@astrojs/sitemap': 3.7.3 '@pagefind/default-ui': 1.5.2 - '@types/hast': 3.0.4 + '@types/hast': 3.0.5 '@types/js-yaml': 4.0.9 '@types/mdast': 4.0.4 - astro: 5.18.2(@types/node@24.12.4)(jiti@2.7.0)(lightningcss@1.32.0)(rollup@4.60.4)(typescript@5.9.3)(yaml@2.9.0) - astro-expressive-code: 0.38.3(astro@5.18.2(@types/node@24.12.4)(jiti@2.7.0)(lightningcss@1.32.0)(rollup@4.60.4)(typescript@5.9.3)(yaml@2.9.0)) + astro: 7.2.4(@astrojs/markdown-remark@7.2.4)(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)(@types/node@24.12.4)(jiti@2.7.0)(yaml@2.9.0) + astro-expressive-code: 0.44.1(astro@7.2.4(@astrojs/markdown-remark@7.2.4)(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)(@types/node@24.12.4)(jiti@2.7.0)(yaml@2.9.0)) bcp-47: 2.1.0 hast-util-from-html: 2.0.3 hast-util-select: 6.0.4 hast-util-to-string: 3.0.1 hastscript: 9.0.1 - i18next: 23.16.8 - js-yaml: 4.1.1 + i18next: 26.3.6(typescript@5.9.3) + js-yaml: 4.3.1 + klona: 2.0.6 + magic-string: 0.30.21 mdast-util-directive: 3.1.0 mdast-util-to-markdown: 2.1.2 mdast-util-to-string: 4.0.0 pagefind: 1.5.2 rehype: 13.0.2 rehype-format: 5.0.1 - remark-directive: 3.0.1 + remark-directive: 4.0.0 + satteri: 0.9.5 + ultrahtml: 1.6.0 unified: 11.0.5 unist-util-visit: 5.1.0 vfile: 6.0.3 + optionalDependencies: + '@astrojs/markdown-remark': 7.2.4 transitivePeerDependencies: - supports-color + - typescript - '@astrojs/telemetry@3.3.0': + '@astrojs/telemetry@3.3.3': dependencies: ci-info: 4.4.0 - debug: 4.4.3 - dlv: 1.1.3 dset: 3.1.4 - is-docker: 3.0.0 - is-wsl: 3.1.1 - which-pm-runs: 1.1.0 - transitivePeerDependencies: - - supports-color + is-docker: 4.0.0 + package-manager-detector: 1.6.0 '@astrojs/yaml2ts@0.2.4': dependencies: @@ -3389,17 +3485,83 @@ snapshots: dependencies: '@babel/types': 7.29.7 - '@babel/runtime@7.29.7': {} - '@babel/types@7.29.7': dependencies: '@babel/helper-string-parser': 7.29.7 '@babel/helper-validator-identifier': 7.29.7 + '@bruits/satteri-darwin-arm64@0.9.5': + optional: true + + '@bruits/satteri-darwin-x64@0.10.4': + optional: true + + '@bruits/satteri-darwin-x64@0.9.5': + optional: true + + '@bruits/satteri-linux-arm64-gnu@0.10.4': + optional: true + + '@bruits/satteri-linux-arm64-gnu@0.9.5': + optional: true + + '@bruits/satteri-linux-arm64-musl@0.10.4': + optional: true + + '@bruits/satteri-linux-arm64-musl@0.9.5': + optional: true + + '@bruits/satteri-linux-x64-gnu@0.10.4': + optional: true + + '@bruits/satteri-linux-x64-gnu@0.9.5': + optional: true + + '@bruits/satteri-linux-x64-musl@0.9.5': + optional: true + + '@bruits/satteri-wasm32-wasi@0.10.4': + dependencies: + '@emnapi/core': 1.11.1 + '@emnapi/runtime': 1.11.1 + '@napi-rs/wasm-runtime': 1.2.3(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1) + optional: true + + '@bruits/satteri-wasm32-wasi@0.9.5': + dependencies: + '@emnapi/core': 1.11.1 + '@emnapi/runtime': 1.11.1 + '@napi-rs/wasm-runtime': 1.2.3(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1) + optional: true + + '@bruits/satteri-win32-arm64-msvc@0.10.4': + optional: true + + '@bruits/satteri-win32-arm64-msvc@0.9.5': + optional: true + + '@bruits/satteri-win32-x64-msvc@0.10.4': + optional: true + + '@bruits/satteri-win32-x64-msvc@0.9.5': + optional: true + '@capsizecss/unpack@4.0.0': dependencies: fontkitten: 1.0.3 + '@clack/core@1.4.3': + dependencies: + fast-wrap-ansi: 0.2.2 + sisteransi: 1.0.5 + + '@clack/prompts@1.7.0': + dependencies: + '@clack/core': 1.4.3 + fast-string-width: 3.0.2 + fast-wrap-ansi: 0.2.2 + sisteransi: 1.0.5 + '@codemirror/autocomplete@6.20.3': dependencies: '@codemirror/language': 6.12.4 @@ -3483,227 +3645,160 @@ snapshots: '@codemirror/view': 6.43.6 crelt: 1.0.7 - '@codemirror/state@6.7.1': - dependencies: - '@marijn/find-cluster-break': 1.0.3 - - '@codemirror/view@6.43.6': - dependencies: - '@codemirror/state': 6.7.1 - crelt: 1.0.7 - style-mod: 4.1.3 - w3c-keyname: 2.2.8 - - '@ctrl/tinycolor@4.2.0': {} - - '@emmetio/abbreviation@2.3.3': - dependencies: - '@emmetio/scanner': 1.0.4 - - '@emmetio/css-abbreviation@2.1.8': - dependencies: - '@emmetio/scanner': 1.0.4 - - '@emmetio/css-parser@0.4.1': - dependencies: - '@emmetio/stream-reader': 2.2.0 - '@emmetio/stream-reader-utils': 0.1.0 - - '@emmetio/html-matcher@1.3.0': - dependencies: - '@emmetio/scanner': 1.0.4 - - '@emmetio/scanner@1.0.4': {} - - '@emmetio/stream-reader-utils@0.1.0': {} - - '@emmetio/stream-reader@2.2.0': {} - - '@emnapi/runtime@1.10.0': - dependencies: - tslib: 2.8.1 - optional: true - - '@esbuild/aix-ppc64@0.25.12': - optional: true - - '@esbuild/aix-ppc64@0.27.7': - optional: true - - '@esbuild/android-arm64@0.25.12': - optional: true - - '@esbuild/android-arm64@0.27.7': - optional: true - - '@esbuild/android-arm@0.25.12': - optional: true - - '@esbuild/android-arm@0.27.7': - optional: true - - '@esbuild/android-x64@0.25.12': - optional: true - - '@esbuild/android-x64@0.27.7': - optional: true - - '@esbuild/darwin-arm64@0.25.12': - optional: true - - '@esbuild/darwin-arm64@0.27.7': - optional: true - - '@esbuild/darwin-x64@0.25.12': - optional: true - - '@esbuild/darwin-x64@0.27.7': - optional: true - - '@esbuild/freebsd-arm64@0.25.12': - optional: true - - '@esbuild/freebsd-arm64@0.27.7': - optional: true + '@codemirror/state@6.7.1': + dependencies: + '@marijn/find-cluster-break': 1.0.3 - '@esbuild/freebsd-x64@0.25.12': - optional: true + '@codemirror/view@6.43.6': + dependencies: + '@codemirror/state': 6.7.1 + crelt: 1.0.7 + style-mod: 4.1.3 + w3c-keyname: 2.2.8 - '@esbuild/freebsd-x64@0.27.7': - optional: true + '@ctrl/tinycolor@4.2.0': {} - '@esbuild/linux-arm64@0.25.12': - optional: true + '@emmetio/abbreviation@2.3.3': + dependencies: + '@emmetio/scanner': 1.0.4 - '@esbuild/linux-arm64@0.27.7': - optional: true + '@emmetio/css-abbreviation@2.1.8': + dependencies: + '@emmetio/scanner': 1.0.4 - '@esbuild/linux-arm@0.25.12': - optional: true + '@emmetio/css-parser@0.4.1': + dependencies: + '@emmetio/stream-reader': 2.2.0 + '@emmetio/stream-reader-utils': 0.1.0 - '@esbuild/linux-arm@0.27.7': - optional: true + '@emmetio/html-matcher@1.3.0': + dependencies: + '@emmetio/scanner': 1.0.4 - '@esbuild/linux-ia32@0.25.12': - optional: true + '@emmetio/scanner@1.0.4': {} - '@esbuild/linux-ia32@0.27.7': - optional: true + '@emmetio/stream-reader-utils@0.1.0': {} - '@esbuild/linux-loong64@0.25.12': - optional: true + '@emmetio/stream-reader@2.2.0': {} - '@esbuild/linux-loong64@0.27.7': + '@emnapi/core@1.11.1': + dependencies: + '@emnapi/wasi-threads': 1.2.2 + tslib: 2.8.1 optional: true - '@esbuild/linux-mips64el@0.25.12': + '@emnapi/runtime@1.11.1': + dependencies: + tslib: 2.8.1 optional: true - '@esbuild/linux-mips64el@0.27.7': + '@emnapi/wasi-threads@1.2.2': + dependencies: + tslib: 2.8.1 optional: true - '@esbuild/linux-ppc64@0.25.12': + '@esbuild/aix-ppc64@0.28.2': optional: true - '@esbuild/linux-ppc64@0.27.7': + '@esbuild/android-arm64@0.28.2': optional: true - '@esbuild/linux-riscv64@0.25.12': + '@esbuild/android-arm@0.28.2': optional: true - '@esbuild/linux-riscv64@0.27.7': + '@esbuild/android-x64@0.28.2': optional: true - '@esbuild/linux-s390x@0.25.12': + '@esbuild/darwin-arm64@0.28.2': optional: true - '@esbuild/linux-s390x@0.27.7': + '@esbuild/darwin-x64@0.28.2': optional: true - '@esbuild/linux-x64@0.25.12': + '@esbuild/freebsd-arm64@0.28.2': optional: true - '@esbuild/linux-x64@0.27.7': + '@esbuild/freebsd-x64@0.28.2': optional: true - '@esbuild/netbsd-arm64@0.25.12': + '@esbuild/linux-arm64@0.28.2': optional: true - '@esbuild/netbsd-arm64@0.27.7': + '@esbuild/linux-arm@0.28.2': optional: true - '@esbuild/netbsd-x64@0.25.12': + '@esbuild/linux-ia32@0.28.2': optional: true - '@esbuild/netbsd-x64@0.27.7': + '@esbuild/linux-loong64@0.28.2': optional: true - '@esbuild/openbsd-arm64@0.25.12': + '@esbuild/linux-mips64el@0.28.2': optional: true - '@esbuild/openbsd-arm64@0.27.7': + '@esbuild/linux-ppc64@0.28.2': optional: true - '@esbuild/openbsd-x64@0.25.12': + '@esbuild/linux-riscv64@0.28.2': optional: true - '@esbuild/openbsd-x64@0.27.7': + '@esbuild/linux-s390x@0.28.2': optional: true - '@esbuild/openharmony-arm64@0.25.12': + '@esbuild/linux-x64@0.28.2': optional: true - '@esbuild/openharmony-arm64@0.27.7': + '@esbuild/netbsd-arm64@0.28.2': optional: true - '@esbuild/sunos-x64@0.25.12': + '@esbuild/netbsd-x64@0.28.2': optional: true - '@esbuild/sunos-x64@0.27.7': + '@esbuild/openbsd-arm64@0.28.2': optional: true - '@esbuild/win32-arm64@0.25.12': + '@esbuild/openbsd-x64@0.28.2': optional: true - '@esbuild/win32-arm64@0.27.7': + '@esbuild/openharmony-arm64@0.28.2': optional: true - '@esbuild/win32-ia32@0.25.12': + '@esbuild/sunos-x64@0.28.2': optional: true - '@esbuild/win32-ia32@0.27.7': + '@esbuild/win32-arm64@0.28.2': optional: true - '@esbuild/win32-x64@0.25.12': + '@esbuild/win32-ia32@0.28.2': optional: true - '@esbuild/win32-x64@0.27.7': + '@esbuild/win32-x64@0.28.2': optional: true - '@expressive-code/core@0.38.3': + '@expressive-code/core@0.44.1': dependencies: '@ctrl/tinycolor': 4.2.0 hast-util-select: 6.0.4 hast-util-to-html: 9.0.5 hast-util-to-text: 4.0.2 hastscript: 9.0.1 - postcss: 8.5.15 - postcss-nested: 6.2.0(postcss@8.5.15) + postcss: 8.5.26 + postcss-nested: 6.2.0(postcss@8.5.26) unist-util-visit: 5.1.0 unist-util-visit-parents: 6.0.2 - '@expressive-code/plugin-frames@0.38.3': + '@expressive-code/plugin-frames@0.44.1': dependencies: - '@expressive-code/core': 0.38.3 + '@expressive-code/core': 0.44.1 - '@expressive-code/plugin-shiki@0.38.3': + '@expressive-code/plugin-shiki@0.44.1': dependencies: - '@expressive-code/core': 0.38.3 - shiki: 1.29.2 + '@expressive-code/core': 0.44.1 + shiki: 4.4.3 - '@expressive-code/plugin-text-markers@0.38.3': + '@expressive-code/plugin-text-markers@0.44.1': dependencies: - '@expressive-code/core': 0.38.3 + '@expressive-code/core': 0.44.1 '@floating-ui/core@1.8.0': dependencies: @@ -3830,7 +3925,7 @@ snapshots: '@img/sharp-wasm32@0.34.5': dependencies: - '@emnapi/runtime': 1.10.0 + '@emnapi/runtime': 1.11.1 optional: true '@img/sharp-win32-arm64@0.34.5': @@ -3921,7 +4016,7 @@ snapshots: dependencies: '@types/estree': 1.0.9 '@types/estree-jsx': 1.0.5 - '@types/hast': 3.0.4 + '@types/hast': 3.0.5 '@types/mdx': 2.0.13 acorn: 8.16.0 collapse-white-space: 2.1.0 @@ -3947,10 +4042,19 @@ snapshots: transitivePeerDependencies: - supports-color + '@napi-rs/wasm-runtime@1.2.3(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)': + dependencies: + '@emnapi/core': 1.11.1 + '@emnapi/runtime': 1.11.1 + '@tybys/wasm-util': 0.10.3 + optional: true + '@opentelemetry/api@1.9.0': {} '@oslojs/encoding@1.1.0': {} + '@oxc-project/types@0.146.0': {} + '@pagefind/darwin-arm64@1.5.2': optional: true @@ -3982,88 +4086,52 @@ snapshots: '@codemirror/state': 6.7.1 '@codemirror/view': 6.43.6 - '@rollup/pluginutils@5.3.0(rollup@4.60.4)': - dependencies: - '@types/estree': 1.0.9 - estree-walker: 2.0.2 - picomatch: 4.0.4 - optionalDependencies: - rollup: 4.60.4 - - '@rollup/rollup-android-arm-eabi@4.60.4': - optional: true - - '@rollup/rollup-android-arm64@4.60.4': - optional: true - - '@rollup/rollup-darwin-arm64@4.60.4': - optional: true - - '@rollup/rollup-darwin-x64@4.60.4': - optional: true - - '@rollup/rollup-freebsd-arm64@4.60.4': - optional: true - - '@rollup/rollup-freebsd-x64@4.60.4': - optional: true - - '@rollup/rollup-linux-arm-gnueabihf@4.60.4': + '@rolldown/binding-android-arm-eabi@1.2.5': optional: true - '@rollup/rollup-linux-arm-musleabihf@4.60.4': + '@rolldown/binding-android-arm64@1.2.5': optional: true - '@rollup/rollup-linux-arm64-gnu@4.60.4': + '@rolldown/binding-darwin-arm64@1.2.5': optional: true - '@rollup/rollup-linux-arm64-musl@4.60.4': + '@rolldown/binding-darwin-x64@1.2.5': optional: true - '@rollup/rollup-linux-loong64-gnu@4.60.4': + '@rolldown/binding-freebsd-x64@1.2.5': optional: true - '@rollup/rollup-linux-loong64-musl@4.60.4': + '@rolldown/binding-linux-arm-gnueabihf@1.2.5': optional: true - '@rollup/rollup-linux-ppc64-gnu@4.60.4': + '@rolldown/binding-linux-arm64-gnu@1.2.5': optional: true - '@rollup/rollup-linux-ppc64-musl@4.60.4': + '@rolldown/binding-linux-arm64-musl@1.2.5': optional: true - '@rollup/rollup-linux-riscv64-gnu@4.60.4': + '@rolldown/binding-linux-ppc64-gnu@1.2.5': optional: true - '@rollup/rollup-linux-riscv64-musl@4.60.4': + '@rolldown/binding-linux-s390x-gnu@1.2.5': optional: true - '@rollup/rollup-linux-s390x-gnu@4.60.4': + '@rolldown/binding-linux-x64-gnu@1.2.5': optional: true - '@rollup/rollup-linux-x64-gnu@4.60.4': + '@rolldown/binding-linux-x64-musl@1.2.5': optional: true - '@rollup/rollup-linux-x64-musl@4.60.4': + '@rolldown/binding-openharmony-arm64@1.2.5': optional: true - '@rollup/rollup-openbsd-x64@4.60.4': + '@rolldown/binding-win32-arm64-msvc@1.2.5': optional: true - '@rollup/rollup-openharmony-arm64@4.60.4': + '@rolldown/binding-win32-x64-msvc@1.2.5': optional: true - '@rollup/rollup-win32-arm64-msvc@4.60.4': - optional: true - - '@rollup/rollup-win32-ia32-msvc@4.60.4': - optional: true - - '@rollup/rollup-win32-x64-gnu@4.60.4': - optional: true - - '@rollup/rollup-win32-x64-msvc@4.60.4': - optional: true + '@rolldown/pluginutils@1.0.1': {} '@scalar/agent-chat@0.12.18(tailwindcss@4.3.0)(typescript@5.9.3)(zod@4.4.3)': dependencies: @@ -4392,69 +4460,43 @@ snapshots: transitivePeerDependencies: - typescript - '@shikijs/core@1.29.2': - dependencies: - '@shikijs/engine-javascript': 1.29.2 - '@shikijs/engine-oniguruma': 1.29.2 - '@shikijs/types': 1.29.2 - '@shikijs/vscode-textmate': 10.0.2 - '@types/hast': 3.0.4 - hast-util-to-html: 9.0.5 - - '@shikijs/core@3.23.0': + '@shikijs/core@4.4.3': dependencies: - '@shikijs/types': 3.23.0 + '@shikijs/primitive': 4.4.3 + '@shikijs/types': 4.4.3 '@shikijs/vscode-textmate': 10.0.2 - '@types/hast': 3.0.4 + '@types/hast': 3.0.5 hast-util-to-html: 9.0.5 - '@shikijs/engine-javascript@1.29.2': - dependencies: - '@shikijs/types': 1.29.2 - '@shikijs/vscode-textmate': 10.0.2 - oniguruma-to-es: 2.3.0 - - '@shikijs/engine-javascript@3.23.0': + '@shikijs/engine-javascript@4.4.3': dependencies: - '@shikijs/types': 3.23.0 + '@shikijs/types': 4.4.3 '@shikijs/vscode-textmate': 10.0.2 oniguruma-to-es: 4.3.6 - '@shikijs/engine-oniguruma@1.29.2': - dependencies: - '@shikijs/types': 1.29.2 - '@shikijs/vscode-textmate': 10.0.2 - - '@shikijs/engine-oniguruma@3.23.0': + '@shikijs/engine-oniguruma@4.4.3': dependencies: - '@shikijs/types': 3.23.0 + '@shikijs/types': 4.4.3 '@shikijs/vscode-textmate': 10.0.2 - '@shikijs/langs@1.29.2': - dependencies: - '@shikijs/types': 1.29.2 - - '@shikijs/langs@3.23.0': - dependencies: - '@shikijs/types': 3.23.0 - - '@shikijs/themes@1.29.2': + '@shikijs/langs@4.4.3': dependencies: - '@shikijs/types': 1.29.2 + '@shikijs/types': 4.4.3 - '@shikijs/themes@3.23.0': + '@shikijs/primitive@4.4.3': dependencies: - '@shikijs/types': 3.23.0 + '@shikijs/types': 4.4.3 + '@shikijs/vscode-textmate': 10.0.2 + '@types/hast': 3.0.5 - '@shikijs/types@1.29.2': + '@shikijs/themes@4.4.3': dependencies: - '@shikijs/vscode-textmate': 10.0.2 - '@types/hast': 3.0.4 + '@shikijs/types': 4.4.3 - '@shikijs/types@3.23.0': + '@shikijs/types@4.4.3': dependencies: '@shikijs/vscode-textmate': 10.0.2 - '@types/hast': 3.0.4 + '@types/hast': 3.0.5 '@shikijs/vscode-textmate@10.0.2': {} @@ -4525,12 +4567,12 @@ snapshots: '@tailwindcss/oxide-win32-arm64-msvc': 4.3.0 '@tailwindcss/oxide-win32-x64-msvc': 4.3.0 - '@tailwindcss/vite@4.3.0(vite@6.4.2(@types/node@24.12.4)(jiti@2.7.0)(lightningcss@1.32.0)(yaml@2.9.0))': + '@tailwindcss/vite@4.3.0(vite@8.2.1(@types/node@24.12.4)(esbuild@0.28.2)(jiti@2.7.0)(yaml@2.9.0))': dependencies: '@tailwindcss/node': 4.3.0 '@tailwindcss/oxide': 4.3.0 tailwindcss: 4.3.0 - vite: 6.4.2(@types/node@24.12.4)(jiti@2.7.0)(lightningcss@1.32.0)(yaml@2.9.0) + vite: 8.2.1(@types/node@24.12.4)(esbuild@0.28.2)(jiti@2.7.0)(yaml@2.9.0) '@tanstack/virtual-core@3.17.3': {} @@ -4539,6 +4581,11 @@ snapshots: '@tanstack/virtual-core': 3.17.3 vue: 3.5.39(typescript@5.9.3) + '@tybys/wasm-util@0.10.3': + dependencies: + tslib: 2.8.1 + optional: true + '@types/debug@4.1.13': dependencies: '@types/ms': 2.1.0 @@ -4547,13 +4594,11 @@ snapshots: dependencies: '@types/estree': 1.0.9 - '@types/estree@1.0.8': {} - '@types/estree@1.0.9': {} '@types/har-format@1.2.16': {} - '@types/hast@3.0.4': + '@types/hast@3.0.5': dependencies: '@types/unist': 3.0.3 @@ -4575,10 +4620,6 @@ snapshots: dependencies: undici-types: 7.16.0 - '@types/react@19.2.16': - dependencies: - csstype: 3.2.3 - '@types/sax@1.2.7': dependencies: '@types/node': 24.12.4 @@ -4771,20 +4812,16 @@ snapshots: json-schema-traverse: 1.0.0 require-from-string: 2.0.2 - ansi-align@3.0.1: + am-i-vibing@0.4.0: dependencies: - string-width: 4.2.3 + process-ancestry: 0.1.0 ansi-regex@5.0.1: {} - ansi-regex@6.2.2: {} - ansi-styles@4.3.0: dependencies: color-convert: 2.0.1 - ansi-styles@6.2.3: {} - anymatch@3.1.3: dependencies: normalize-path: 3.0.0 @@ -4804,77 +4841,69 @@ snapshots: astring@1.9.0: {} - astro-expressive-code@0.38.3(astro@5.18.2(@types/node@24.12.4)(jiti@2.7.0)(lightningcss@1.32.0)(rollup@4.60.4)(typescript@5.9.3)(yaml@2.9.0)): + astro-expressive-code@0.44.1(astro@7.2.4(@astrojs/markdown-remark@7.2.4)(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)(@types/node@24.12.4)(jiti@2.7.0)(yaml@2.9.0)): dependencies: - astro: 5.18.2(@types/node@24.12.4)(jiti@2.7.0)(lightningcss@1.32.0)(rollup@4.60.4)(typescript@5.9.3)(yaml@2.9.0) - rehype-expressive-code: 0.38.3 + astro: 7.2.4(@astrojs/markdown-remark@7.2.4)(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)(@types/node@24.12.4)(jiti@2.7.0)(yaml@2.9.0) + rehype-expressive-code: 0.44.1 + url-extras: 0.1.0 - astro@5.18.2(@types/node@24.12.4)(jiti@2.7.0)(lightningcss@1.32.0)(rollup@4.60.4)(typescript@5.9.3)(yaml@2.9.0): + astro@7.2.4(@astrojs/markdown-remark@7.2.4)(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)(@types/node@24.12.4)(jiti@2.7.0)(yaml@2.9.0): dependencies: - '@astrojs/compiler': 2.13.1 - '@astrojs/internal-helpers': 0.7.6 - '@astrojs/markdown-remark': 6.3.11 - '@astrojs/telemetry': 3.3.0 + '@astrojs/compiler-rs': 0.3.2(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1) + '@astrojs/internal-helpers': 0.10.4 + '@astrojs/markdown-satteri': 0.3.7 + '@astrojs/telemetry': 3.3.3 '@capsizecss/unpack': 4.0.0 + '@clack/prompts': 1.7.0 '@oslojs/encoding': 1.1.0 - '@rollup/pluginutils': 5.3.0(rollup@4.60.4) - acorn: 8.16.0 + am-i-vibing: 0.4.0 aria-query: 5.3.2 axobject-query: 4.1.0 - boxen: 8.0.1 ci-info: 4.4.0 clsx: 2.1.1 - common-ancestor-path: 1.0.1 - cookie: 1.1.1 - cssesc: 3.0.0 - debug: 4.4.3 - deterministic-object-hash: 2.0.2 + common-ancestor-path: 2.0.0 + cookie: 2.0.1 devalue: 5.8.1 diff: 8.0.4 - dlv: 1.1.3 dset: 3.1.4 - es-module-lexer: 1.7.0 - esbuild: 0.27.7 - estree-walker: 3.0.3 + es-module-lexer: 2.3.2 + esbuild: 0.28.2 + find-process: 2.1.1 flattie: 1.1.1 fontace: 0.4.1 + get-tsconfig: 5.0.0-beta.4 github-slugger: 2.0.0 html-escaper: 3.0.3 http-cache-semantics: 4.2.0 - import-meta-resolve: 4.2.0 - js-yaml: 4.1.1 - magic-string: 0.30.21 + js-yaml: 4.3.1 + jsonc-parser: 3.3.1 + magic-string: 1.2.1 magicast: 0.5.3 mrmime: 2.0.1 - neotraverse: 0.6.18 - p-limit: 6.2.0 - p-queue: 8.1.1 + neotraverse: 1.0.1 + obug: 2.1.4 + p-limit: 7.3.1 + p-queue: 9.3.3 package-manager-detector: 1.6.0 piccolore: 0.1.3 - picomatch: 4.0.4 - prompts: 2.4.2 - rehype: 13.0.2 + picomatch: 4.0.5 semver: 7.8.1 - shiki: 3.23.0 + shiki: 4.4.3 smol-toml: 1.6.1 svgo: 4.0.1 + tinyclip: 0.1.15 tinyexec: 1.2.2 - tinyglobby: 0.2.16 - tsconfck: 3.1.6(typescript@5.9.3) + tinyglobby: 0.2.17 ultrahtml: 1.6.0 - unifont: 0.7.4 - unist-util-visit: 5.1.0 + unifont: 0.7.5 unstorage: 1.17.5 - vfile: 6.0.3 - vite: 6.4.2(@types/node@24.12.4)(jiti@2.7.0)(lightningcss@1.32.0)(yaml@2.9.0) - vitefu: 1.1.3(vite@6.4.2(@types/node@24.12.4)(jiti@2.7.0)(lightningcss@1.32.0)(yaml@2.9.0)) + vite: 8.2.1(@types/node@24.12.4)(esbuild@0.28.2)(jiti@2.7.0)(yaml@2.9.0) + vitefu: 1.1.3(vite@8.2.1(@types/node@24.12.4)(esbuild@0.28.2)(jiti@2.7.0)(yaml@2.9.0)) xxhash-wasm: 1.1.0 - yargs-parser: 21.1.1 - yocto-spinner: 0.2.3 - zod: 3.25.76 - zod-to-json-schema: 3.25.2(zod@3.25.76) - zod-to-ts: 1.2.0(typescript@5.9.3)(zod@3.25.76) + yargs-parser: 22.0.0 + zod: 4.4.3 optionalDependencies: + '@astrojs/markdown-remark': 7.2.4 sharp: 0.34.5 transitivePeerDependencies: - '@azure/app-configuration' @@ -4885,6 +4914,8 @@ snapshots: - '@azure/storage-blob' - '@capacitor/preferences' - '@deno/kv' + - '@emnapi/core' + - '@emnapi/runtime' - '@netlify/blobs' - '@planetscale/database' - '@types/node' @@ -4892,22 +4923,19 @@ snapshots: - '@vercel/blob' - '@vercel/functions' - '@vercel/kv' + - '@vitejs/devtools' - aws4fetch - db0 - idb-keyval - ioredis - jiti - less - - lightningcss - - rollup - sass - sass-embedded - stylus - sugarss - - supports-color - terser - tsx - - typescript - uploadthing - yaml @@ -4915,8 +4943,6 @@ snapshots: bail@2.0.2: {} - base-64@1.0.0: {} - bcp-47-match@2.0.3: {} bcp-47@2.1.0: @@ -4929,25 +4955,17 @@ snapshots: boolbase@1.0.0: {} - boxen@8.0.1: - dependencies: - ansi-align: 3.0.1 - camelcase: 8.0.0 - chalk: 5.6.2 - cli-boxes: 3.0.0 - string-width: 7.2.0 - type-fest: 4.41.0 - widest-line: 5.0.0 - wrap-ansi: 9.0.2 - braces@3.0.3: dependencies: fill-range: 7.1.1 - camelcase@8.0.0: {} - ccount@2.0.1: {} + chalk@4.1.2: + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + chalk@5.6.2: {} character-entities-html4@2.1.0: {} @@ -4980,8 +4998,6 @@ snapshots: ci-info@4.4.0: {} - cli-boxes@3.0.0: {} - cliui@8.0.1: dependencies: string-width: 4.2.3 @@ -5002,13 +5018,15 @@ snapshots: commander@11.1.0: {} - common-ancestor-path@1.0.1: {} + commander@14.0.3: {} + + common-ancestor-path@2.0.0: {} convert-hrtime@5.0.0: {} cookie-es@1.2.3: {} - cookie@1.1.1: {} + cookie@2.0.1: {} crelt@1.0.7: {} @@ -5068,10 +5086,6 @@ snapshots: detect-libc@2.1.2: {} - deterministic-object-hash@2.0.2: - dependencies: - base-64: 1.0.0 - devalue@5.8.1: {} devlop@1.1.0: @@ -5082,8 +5096,6 @@ snapshots: direction@2.0.1: {} - dlv@1.1.3: {} - dom-serializer@2.0.0: dependencies: domelementtype: 2.3.0 @@ -5109,10 +5121,6 @@ snapshots: '@emmetio/abbreviation': 2.3.3 '@emmetio/css-abbreviation': 2.1.8 - emoji-regex-xs@1.0.0: {} - - emoji-regex@10.6.0: {} - emoji-regex@8.0.0: {} enhanced-resolve@5.22.1: @@ -5126,7 +5134,7 @@ snapshots: entities@7.0.1: {} - es-module-lexer@1.7.0: {} + es-module-lexer@2.3.2: {} esast-util-from-estree@2.0.0: dependencies: @@ -5142,63 +5150,34 @@ snapshots: esast-util-from-estree: 2.0.0 vfile-message: 4.0.3 - esbuild@0.25.12: + esbuild@0.28.2: optionalDependencies: - '@esbuild/aix-ppc64': 0.25.12 - '@esbuild/android-arm': 0.25.12 - '@esbuild/android-arm64': 0.25.12 - '@esbuild/android-x64': 0.25.12 - '@esbuild/darwin-arm64': 0.25.12 - '@esbuild/darwin-x64': 0.25.12 - '@esbuild/freebsd-arm64': 0.25.12 - '@esbuild/freebsd-x64': 0.25.12 - '@esbuild/linux-arm': 0.25.12 - '@esbuild/linux-arm64': 0.25.12 - '@esbuild/linux-ia32': 0.25.12 - '@esbuild/linux-loong64': 0.25.12 - '@esbuild/linux-mips64el': 0.25.12 - '@esbuild/linux-ppc64': 0.25.12 - '@esbuild/linux-riscv64': 0.25.12 - '@esbuild/linux-s390x': 0.25.12 - '@esbuild/linux-x64': 0.25.12 - '@esbuild/netbsd-arm64': 0.25.12 - '@esbuild/netbsd-x64': 0.25.12 - '@esbuild/openbsd-arm64': 0.25.12 - '@esbuild/openbsd-x64': 0.25.12 - '@esbuild/openharmony-arm64': 0.25.12 - '@esbuild/sunos-x64': 0.25.12 - '@esbuild/win32-arm64': 0.25.12 - '@esbuild/win32-ia32': 0.25.12 - '@esbuild/win32-x64': 0.25.12 - - esbuild@0.27.7: - optionalDependencies: - '@esbuild/aix-ppc64': 0.27.7 - '@esbuild/android-arm': 0.27.7 - '@esbuild/android-arm64': 0.27.7 - '@esbuild/android-x64': 0.27.7 - '@esbuild/darwin-arm64': 0.27.7 - '@esbuild/darwin-x64': 0.27.7 - '@esbuild/freebsd-arm64': 0.27.7 - '@esbuild/freebsd-x64': 0.27.7 - '@esbuild/linux-arm': 0.27.7 - '@esbuild/linux-arm64': 0.27.7 - '@esbuild/linux-ia32': 0.27.7 - '@esbuild/linux-loong64': 0.27.7 - '@esbuild/linux-mips64el': 0.27.7 - '@esbuild/linux-ppc64': 0.27.7 - '@esbuild/linux-riscv64': 0.27.7 - '@esbuild/linux-s390x': 0.27.7 - '@esbuild/linux-x64': 0.27.7 - '@esbuild/netbsd-arm64': 0.27.7 - '@esbuild/netbsd-x64': 0.27.7 - '@esbuild/openbsd-arm64': 0.27.7 - '@esbuild/openbsd-x64': 0.27.7 - '@esbuild/openharmony-arm64': 0.27.7 - '@esbuild/sunos-x64': 0.27.7 - '@esbuild/win32-arm64': 0.27.7 - '@esbuild/win32-ia32': 0.27.7 - '@esbuild/win32-x64': 0.27.7 + '@esbuild/aix-ppc64': 0.28.2 + '@esbuild/android-arm': 0.28.2 + '@esbuild/android-arm64': 0.28.2 + '@esbuild/android-x64': 0.28.2 + '@esbuild/darwin-arm64': 0.28.2 + '@esbuild/darwin-x64': 0.28.2 + '@esbuild/freebsd-arm64': 0.28.2 + '@esbuild/freebsd-x64': 0.28.2 + '@esbuild/linux-arm': 0.28.2 + '@esbuild/linux-arm64': 0.28.2 + '@esbuild/linux-ia32': 0.28.2 + '@esbuild/linux-loong64': 0.28.2 + '@esbuild/linux-mips64el': 0.28.2 + '@esbuild/linux-ppc64': 0.28.2 + '@esbuild/linux-riscv64': 0.28.2 + '@esbuild/linux-s390x': 0.28.2 + '@esbuild/linux-x64': 0.28.2 + '@esbuild/netbsd-arm64': 0.28.2 + '@esbuild/netbsd-x64': 0.28.2 + '@esbuild/openbsd-arm64': 0.28.2 + '@esbuild/openbsd-x64': 0.28.2 + '@esbuild/openharmony-arm64': 0.28.2 + '@esbuild/sunos-x64': 0.28.2 + '@esbuild/win32-arm64': 0.28.2 + '@esbuild/win32-ia32': 0.28.2 + '@esbuild/win32-x64': 0.28.2 escalade@3.2.0: {} @@ -5243,27 +5222,47 @@ snapshots: eventsource-parser@3.1.0: {} - expressive-code@0.38.3: + expressive-code@0.44.1: dependencies: - '@expressive-code/core': 0.38.3 - '@expressive-code/plugin-frames': 0.38.3 - '@expressive-code/plugin-shiki': 0.38.3 - '@expressive-code/plugin-text-markers': 0.38.3 + '@expressive-code/core': 0.44.1 + '@expressive-code/plugin-frames': 0.44.1 + '@expressive-code/plugin-shiki': 0.44.1 + '@expressive-code/plugin-text-markers': 0.44.1 extend@3.0.2: {} fast-deep-equal@3.1.3: {} + fast-string-truncated-width@3.0.3: {} + + fast-string-width@3.0.2: + dependencies: + fast-string-truncated-width: 3.0.3 + fast-uri@3.1.2: {} + fast-wrap-ansi@0.2.2: + dependencies: + fast-string-width: 3.0.2 + fdir@6.5.0(picomatch@4.0.4): optionalDependencies: picomatch: 4.0.4 + fdir@6.5.0(picomatch@4.0.5): + optionalDependencies: + picomatch: 4.0.5 + fill-range@7.1.1: dependencies: to-regex-range: 5.0.1 + find-process@2.1.1: + dependencies: + chalk: 4.1.2 + commander: 14.0.3 + loglevel: 1.9.2 + flatted@3.4.2: {} flattie@1.1.1: {} @@ -5289,10 +5288,12 @@ snapshots: get-caller-file@2.0.5: {} - get-east-asian-width@1.6.0: {} - get-own-enumerable-keys@1.0.0: {} + get-tsconfig@5.0.0-beta.4: + dependencies: + resolve-pkg-maps: 1.0.0 + github-slugger@2.0.0: {} glob-parent@5.1.2: @@ -5315,14 +5316,16 @@ snapshots: ufo: 1.6.4 uncrypto: 0.1.3 + has-flag@4.0.0: {} + hast-util-embedded@3.0.0: dependencies: - '@types/hast': 3.0.4 + '@types/hast': 3.0.5 hast-util-is-element: 3.0.0 hast-util-format@1.1.0: dependencies: - '@types/hast': 3.0.4 + '@types/hast': 3.0.5 hast-util-embedded: 3.0.0 hast-util-minify-whitespace: 1.0.1 hast-util-phrasing: 3.0.1 @@ -5332,7 +5335,7 @@ snapshots: hast-util-from-html@2.0.3: dependencies: - '@types/hast': 3.0.4 + '@types/hast': 3.0.5 devlop: 1.1.0 hast-util-from-parse5: 8.0.3 parse5: 7.3.0 @@ -5341,7 +5344,7 @@ snapshots: hast-util-from-parse5@8.0.3: dependencies: - '@types/hast': 3.0.4 + '@types/hast': 3.0.5 '@types/unist': 3.0.3 devlop: 1.1.0 hastscript: 9.0.1 @@ -5352,19 +5355,19 @@ snapshots: hast-util-has-property@3.0.0: dependencies: - '@types/hast': 3.0.4 + '@types/hast': 3.0.5 hast-util-is-body-ok-link@3.0.1: dependencies: - '@types/hast': 3.0.4 + '@types/hast': 3.0.5 hast-util-is-element@3.0.0: dependencies: - '@types/hast': 3.0.4 + '@types/hast': 3.0.5 hast-util-minify-whitespace@1.0.1: dependencies: - '@types/hast': 3.0.4 + '@types/hast': 3.0.5 hast-util-embedded: 3.0.0 hast-util-is-element: 3.0.0 hast-util-whitespace: 3.0.0 @@ -5372,11 +5375,11 @@ snapshots: hast-util-parse-selector@4.0.0: dependencies: - '@types/hast': 3.0.4 + '@types/hast': 3.0.5 hast-util-phrasing@3.0.1: dependencies: - '@types/hast': 3.0.4 + '@types/hast': 3.0.5 hast-util-embedded: 3.0.0 hast-util-has-property: 3.0.0 hast-util-is-body-ok-link: 3.0.1 @@ -5384,7 +5387,7 @@ snapshots: hast-util-raw@9.1.0: dependencies: - '@types/hast': 3.0.4 + '@types/hast': 3.0.5 '@types/unist': 3.0.3 '@ungap/structured-clone': 1.3.1 hast-util-from-parse5: 8.0.3 @@ -5400,13 +5403,13 @@ snapshots: hast-util-sanitize@5.0.2: dependencies: - '@types/hast': 3.0.4 + '@types/hast': 3.0.5 '@ungap/structured-clone': 1.3.1 unist-util-position: 5.0.0 hast-util-select@6.0.4: dependencies: - '@types/hast': 3.0.4 + '@types/hast': 3.0.5 '@types/unist': 3.0.3 bcp-47-match: 2.0.3 comma-separated-tokens: 2.0.3 @@ -5426,7 +5429,7 @@ snapshots: dependencies: '@types/estree': 1.0.9 '@types/estree-jsx': 1.0.5 - '@types/hast': 3.0.4 + '@types/hast': 3.0.5 comma-separated-tokens: 2.0.3 devlop: 1.1.0 estree-util-attach-comments: 3.0.0 @@ -5445,7 +5448,7 @@ snapshots: hast-util-to-html@9.0.5: dependencies: - '@types/hast': 3.0.4 + '@types/hast': 3.0.5 '@types/unist': 3.0.3 ccount: 2.0.1 comma-separated-tokens: 2.0.3 @@ -5460,7 +5463,7 @@ snapshots: hast-util-to-jsx-runtime@2.3.6: dependencies: '@types/estree': 1.0.9 - '@types/hast': 3.0.4 + '@types/hast': 3.0.5 '@types/unist': 3.0.3 comma-separated-tokens: 2.0.3 devlop: 1.1.0 @@ -5479,7 +5482,7 @@ snapshots: hast-util-to-parse5@8.0.1: dependencies: - '@types/hast': 3.0.4 + '@types/hast': 3.0.5 comma-separated-tokens: 2.0.3 devlop: 1.1.0 property-information: 7.1.0 @@ -5489,22 +5492,22 @@ snapshots: hast-util-to-string@3.0.1: dependencies: - '@types/hast': 3.0.4 + '@types/hast': 3.0.5 hast-util-to-text@4.0.2: dependencies: - '@types/hast': 3.0.4 + '@types/hast': 3.0.5 '@types/unist': 3.0.3 hast-util-is-element: 3.0.0 unist-util-find-after: 5.0.0 hast-util-whitespace@3.0.0: dependencies: - '@types/hast': 3.0.4 + '@types/hast': 3.0.5 hastscript@9.0.1: dependencies: - '@types/hast': 3.0.4 + '@types/hast': 3.0.5 comma-separated-tokens: 2.0.3 hast-util-parse-selector: 4.0.0 property-information: 7.1.0 @@ -5522,16 +5525,14 @@ snapshots: http-cache-semantics@4.2.0: {} - i18next@23.16.8: - dependencies: - '@babel/runtime': 7.29.7 + i18next@26.3.6(typescript@5.9.3): + optionalDependencies: + typescript: 5.9.3 identifier-regex@1.1.0: dependencies: reserved-identifiers: 1.2.0 - import-meta-resolve@4.2.0: {} - inline-style-parser@0.2.7: {} iron-webcrypto@1.2.1: {} @@ -5551,7 +5552,7 @@ snapshots: is-decimal@2.0.1: {} - is-docker@3.0.0: {} + is-docker@4.0.0: {} is-extglob@2.1.1: {} @@ -5568,10 +5569,6 @@ snapshots: identifier-regex: 1.1.0 super-regex: 1.1.0 - is-inside-container@1.0.0: - dependencies: - is-docker: 3.0.0 - is-number@7.0.0: {} is-obj@3.0.0: {} @@ -5580,15 +5577,11 @@ snapshots: is-regexp@3.1.0: {} - is-wsl@3.1.1: - dependencies: - is-inside-container: 1.0.0 - jiti@2.7.0: {} js-base64@3.8.1: {} - js-yaml@4.1.1: + js-yaml@4.3.1: dependencies: argparse: 2.0.1 @@ -5600,43 +5593,76 @@ snapshots: jsonc-parser@3.3.1: {} - kleur@3.0.3: {} - kleur@4.1.5: {} + klona@2.0.6: {} + lightningcss-android-arm64@1.32.0: optional: true + lightningcss-android-arm64@1.33.0: + optional: true + lightningcss-darwin-arm64@1.32.0: optional: true + lightningcss-darwin-arm64@1.33.0: + optional: true + lightningcss-darwin-x64@1.32.0: optional: true + lightningcss-darwin-x64@1.33.0: + optional: true + lightningcss-freebsd-x64@1.32.0: optional: true + lightningcss-freebsd-x64@1.33.0: + optional: true + lightningcss-linux-arm-gnueabihf@1.32.0: optional: true + lightningcss-linux-arm-gnueabihf@1.33.0: + optional: true + lightningcss-linux-arm64-gnu@1.32.0: optional: true + lightningcss-linux-arm64-gnu@1.33.0: + optional: true + lightningcss-linux-arm64-musl@1.32.0: optional: true + lightningcss-linux-arm64-musl@1.33.0: + optional: true + lightningcss-linux-x64-gnu@1.32.0: optional: true + lightningcss-linux-x64-gnu@1.33.0: + optional: true + lightningcss-linux-x64-musl@1.32.0: optional: true + lightningcss-linux-x64-musl@1.33.0: + optional: true + lightningcss-win32-arm64-msvc@1.32.0: optional: true + lightningcss-win32-arm64-msvc@1.33.0: + optional: true + lightningcss-win32-x64-msvc@1.32.0: optional: true + lightningcss-win32-x64-msvc@1.33.0: + optional: true + lightningcss@1.32.0: dependencies: detect-libc: 2.1.2 @@ -5653,11 +5679,29 @@ snapshots: lightningcss-win32-arm64-msvc: 1.32.0 lightningcss-win32-x64-msvc: 1.32.0 + lightningcss@1.33.0: + dependencies: + detect-libc: 2.1.2 + optionalDependencies: + lightningcss-android-arm64: 1.33.0 + lightningcss-darwin-arm64: 1.33.0 + lightningcss-darwin-x64: 1.33.0 + lightningcss-freebsd-x64: 1.33.0 + lightningcss-linux-arm-gnueabihf: 1.33.0 + lightningcss-linux-arm64-gnu: 1.33.0 + lightningcss-linux-arm64-musl: 1.33.0 + lightningcss-linux-x64-gnu: 1.33.0 + lightningcss-linux-x64-musl: 1.33.0 + lightningcss-win32-arm64-msvc: 1.33.0 + lightningcss-win32-x64-msvc: 1.33.0 + + loglevel@1.9.2: {} + longest-streak@3.1.0: {} lowlight@3.3.0: dependencies: - '@types/hast': 3.0.4 + '@types/hast': 3.0.5 devlop: 1.1.0 highlight.js: 11.11.1 @@ -5667,6 +5711,10 @@ snapshots: dependencies: '@jridgewell/sourcemap-codec': 1.5.5 + magic-string@1.2.1: + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + magicast@0.5.3: dependencies: '@babel/parser': 7.29.7 @@ -5787,7 +5835,7 @@ snapshots: mdast-util-mdx-expression@2.0.1: dependencies: '@types/estree-jsx': 1.0.5 - '@types/hast': 3.0.4 + '@types/hast': 3.0.5 '@types/mdast': 4.0.4 devlop: 1.1.0 mdast-util-from-markdown: 2.0.3 @@ -5798,7 +5846,7 @@ snapshots: mdast-util-mdx-jsx@3.2.0: dependencies: '@types/estree-jsx': 1.0.5 - '@types/hast': 3.0.4 + '@types/hast': 3.0.5 '@types/mdast': 4.0.4 '@types/unist': 3.0.3 ccount: 2.0.1 @@ -5825,7 +5873,7 @@ snapshots: mdast-util-mdxjs-esm@2.0.1: dependencies: '@types/estree-jsx': 1.0.5 - '@types/hast': 3.0.4 + '@types/hast': 3.0.5 '@types/mdast': 4.0.4 devlop: 1.1.0 mdast-util-from-markdown: 2.0.3 @@ -5840,7 +5888,7 @@ snapshots: mdast-util-to-hast@13.2.1: dependencies: - '@types/hast': 3.0.4 + '@types/hast': 3.0.5 '@types/mdast': 4.0.4 '@ungap/structured-clone': 1.3.1 devlop: 1.1.0 @@ -5891,7 +5939,7 @@ snapshots: micromark-util-symbol: 2.0.1 micromark-util-types: 2.0.2 - micromark-extension-directive@3.0.2: + micromark-extension-directive@4.0.0: dependencies: devlop: 1.1.0 micromark-factory-space: 2.0.1 @@ -6154,9 +6202,11 @@ snapshots: nanoid@3.3.12: {} + nanoid@3.3.18: {} + nanoid@5.1.16: {} - neotraverse@0.6.18: {} + neotraverse@1.0.1: {} neverpanic@0.0.8: {} @@ -6174,6 +6224,8 @@ snapshots: dependencies: boolbase: 1.0.0 + obug@2.1.4: {} + ofetch@1.5.1: dependencies: destr: 2.0.5 @@ -6184,12 +6236,6 @@ snapshots: oniguruma-parser@0.12.2: {} - oniguruma-to-es@2.3.0: - dependencies: - emoji-regex-xs: 1.0.0 - regex: 5.1.1 - regex-recursion: 5.1.1 - oniguruma-to-es@4.3.6: dependencies: oniguruma-parser: 0.12.2 @@ -6200,19 +6246,21 @@ snapshots: dependencies: p-timeout: 6.1.4 - p-limit@6.2.0: + p-limit@7.3.1: dependencies: yocto-queue: 1.2.2 p-map@7.0.4: {} - p-queue@8.1.1: + p-queue@9.3.3: dependencies: eventemitter3: 5.0.4 - p-timeout: 6.1.4 + p-timeout: 7.0.1 p-timeout@6.1.4: {} + p-timeout@7.0.1: {} + package-manager-detector@1.6.0: {} pagefind@1.5.2: @@ -6262,9 +6310,11 @@ snapshots: picomatch@4.0.4: {} - postcss-nested@6.2.0(postcss@8.5.15): + picomatch@4.0.5: {} + + postcss-nested@6.2.0(postcss@8.5.26): dependencies: - postcss: 8.5.15 + postcss: 8.5.26 postcss-selector-parser: 6.1.2 postcss-selector-parser@6.1.2: @@ -6278,6 +6328,12 @@ snapshots: picocolors: 1.1.1 source-map-js: 1.2.1 + postcss@8.5.26: + dependencies: + nanoid: 3.3.18 + picocolors: 1.1.1 + source-map-js: 1.2.1 + prettier@3.8.3: {} pretty-ms@9.3.0: @@ -6286,10 +6342,7 @@ snapshots: prismjs@1.30.0: {} - prompts@2.4.2: - dependencies: - kleur: 3.0.3 - sisteransi: 1.0.5 + process-ancestry@0.1.0: {} property-information@7.1.0: {} @@ -6349,32 +6402,23 @@ snapshots: unified: 11.0.5 vfile: 6.0.3 - regex-recursion@5.1.1: - dependencies: - regex: 5.1.1 - regex-utilities: 2.3.0 - regex-recursion@6.0.2: dependencies: regex-utilities: 2.3.0 regex-utilities@2.3.0: {} - regex@5.1.1: - dependencies: - regex-utilities: 2.3.0 - regex@6.1.0: dependencies: regex-utilities: 2.3.0 - rehype-expressive-code@0.38.3: + rehype-expressive-code@0.44.1: dependencies: - expressive-code: 0.38.3 + expressive-code: 0.44.1 rehype-external-links@3.0.0: dependencies: - '@types/hast': 3.0.4 + '@types/hast': 3.0.5 '@ungap/structured-clone': 1.3.1 hast-util-is-element: 3.0.0 is-absolute-url: 4.0.1 @@ -6383,52 +6427,52 @@ snapshots: rehype-format@5.0.1: dependencies: - '@types/hast': 3.0.4 + '@types/hast': 3.0.5 hast-util-format: 1.1.0 rehype-parse@9.0.1: dependencies: - '@types/hast': 3.0.4 + '@types/hast': 3.0.5 hast-util-from-html: 2.0.3 unified: 11.0.5 rehype-raw@7.0.0: dependencies: - '@types/hast': 3.0.4 + '@types/hast': 3.0.5 hast-util-raw: 9.1.0 vfile: 6.0.3 rehype-recma@1.0.0: dependencies: '@types/estree': 1.0.9 - '@types/hast': 3.0.4 + '@types/hast': 3.0.5 hast-util-to-estree: 3.1.3 transitivePeerDependencies: - supports-color rehype-sanitize@6.0.0: dependencies: - '@types/hast': 3.0.4 + '@types/hast': 3.0.5 hast-util-sanitize: 5.0.2 rehype-stringify@10.0.1: dependencies: - '@types/hast': 3.0.4 + '@types/hast': 3.0.5 hast-util-to-html: 9.0.5 unified: 11.0.5 rehype@13.0.2: dependencies: - '@types/hast': 3.0.4 + '@types/hast': 3.0.5 rehype-parse: 9.0.1 rehype-stringify: 10.0.1 unified: 11.0.5 - remark-directive@3.0.1: + remark-directive@4.0.0: dependencies: '@types/mdast': 4.0.4 mdast-util-directive: 3.1.0 - micromark-extension-directive: 3.0.2 + micromark-extension-directive: 4.0.0 unified: 11.0.5 transitivePeerDependencies: - supports-color @@ -6462,7 +6506,7 @@ snapshots: remark-rehype@11.1.2: dependencies: - '@types/hast': 3.0.4 + '@types/hast': 3.0.5 '@types/mdast': 4.0.4 mdast-util-to-hast: 13.2.1 unified: 11.0.5 @@ -6491,6 +6535,8 @@ snapshots: reserved-identifiers@1.2.0: {} + resolve-pkg-maps@1.0.0: {} + retext-latin@4.0.0: dependencies: '@types/nlcst': 2.0.3 @@ -6516,36 +6562,58 @@ snapshots: retext-stringify: 4.0.0 unified: 11.0.5 - rollup@4.60.4: + rolldown@1.2.5: dependencies: - '@types/estree': 1.0.8 + '@oxc-project/types': 0.146.0 + '@rolldown/pluginutils': 1.0.1 optionalDependencies: - '@rollup/rollup-android-arm-eabi': 4.60.4 - '@rollup/rollup-android-arm64': 4.60.4 - '@rollup/rollup-darwin-arm64': 4.60.4 - '@rollup/rollup-darwin-x64': 4.60.4 - '@rollup/rollup-freebsd-arm64': 4.60.4 - '@rollup/rollup-freebsd-x64': 4.60.4 - '@rollup/rollup-linux-arm-gnueabihf': 4.60.4 - '@rollup/rollup-linux-arm-musleabihf': 4.60.4 - '@rollup/rollup-linux-arm64-gnu': 4.60.4 - '@rollup/rollup-linux-arm64-musl': 4.60.4 - '@rollup/rollup-linux-loong64-gnu': 4.60.4 - '@rollup/rollup-linux-loong64-musl': 4.60.4 - '@rollup/rollup-linux-ppc64-gnu': 4.60.4 - '@rollup/rollup-linux-ppc64-musl': 4.60.4 - '@rollup/rollup-linux-riscv64-gnu': 4.60.4 - '@rollup/rollup-linux-riscv64-musl': 4.60.4 - '@rollup/rollup-linux-s390x-gnu': 4.60.4 - '@rollup/rollup-linux-x64-gnu': 4.60.4 - '@rollup/rollup-linux-x64-musl': 4.60.4 - '@rollup/rollup-openbsd-x64': 4.60.4 - '@rollup/rollup-openharmony-arm64': 4.60.4 - '@rollup/rollup-win32-arm64-msvc': 4.60.4 - '@rollup/rollup-win32-ia32-msvc': 4.60.4 - '@rollup/rollup-win32-x64-gnu': 4.60.4 - '@rollup/rollup-win32-x64-msvc': 4.60.4 - fsevents: 2.3.3 + '@rolldown/binding-android-arm-eabi': 1.2.5 + '@rolldown/binding-android-arm64': 1.2.5 + '@rolldown/binding-darwin-arm64': 1.2.5 + '@rolldown/binding-darwin-x64': 1.2.5 + '@rolldown/binding-freebsd-x64': 1.2.5 + '@rolldown/binding-linux-arm-gnueabihf': 1.2.5 + '@rolldown/binding-linux-arm64-gnu': 1.2.5 + '@rolldown/binding-linux-arm64-musl': 1.2.5 + '@rolldown/binding-linux-ppc64-gnu': 1.2.5 + '@rolldown/binding-linux-s390x-gnu': 1.2.5 + '@rolldown/binding-linux-x64-gnu': 1.2.5 + '@rolldown/binding-linux-x64-musl': 1.2.5 + '@rolldown/binding-openharmony-arm64': 1.2.5 + '@rolldown/binding-win32-arm64-msvc': 1.2.5 + '@rolldown/binding-win32-x64-msvc': 1.2.5 + + satteri@0.10.4: + dependencies: + '@types/estree-jsx': 1.0.5 + '@types/hast': 3.0.5 + '@types/mdast': 4.0.4 + '@types/unist': 3.0.3 + optionalDependencies: + '@bruits/satteri-darwin-x64': 0.10.4 + '@bruits/satteri-linux-arm64-gnu': 0.10.4 + '@bruits/satteri-linux-arm64-musl': 0.10.4 + '@bruits/satteri-linux-x64-gnu': 0.10.4 + '@bruits/satteri-wasm32-wasi': 0.10.4 + '@bruits/satteri-win32-arm64-msvc': 0.10.4 + '@bruits/satteri-win32-x64-msvc': 0.10.4 + + satteri@0.9.5: + dependencies: + '@types/estree-jsx': 1.0.5 + '@types/hast': 3.0.5 + '@types/mdast': 4.0.4 + '@types/unist': 3.0.3 + optionalDependencies: + '@bruits/satteri-darwin-arm64': 0.9.5 + '@bruits/satteri-darwin-x64': 0.9.5 + '@bruits/satteri-linux-arm64-gnu': 0.9.5 + '@bruits/satteri-linux-arm64-musl': 0.9.5 + '@bruits/satteri-linux-x64-gnu': 0.9.5 + '@bruits/satteri-linux-x64-musl': 0.9.5 + '@bruits/satteri-wasm32-wasi': 0.9.5 + '@bruits/satteri-win32-arm64-msvc': 0.9.5 + '@bruits/satteri-win32-x64-msvc': 0.9.5 sax@1.6.0: {} @@ -6585,27 +6653,16 @@ snapshots: '@img/sharp-win32-x64': 0.34.5 optional: true - shiki@1.29.2: - dependencies: - '@shikijs/core': 1.29.2 - '@shikijs/engine-javascript': 1.29.2 - '@shikijs/engine-oniguruma': 1.29.2 - '@shikijs/langs': 1.29.2 - '@shikijs/themes': 1.29.2 - '@shikijs/types': 1.29.2 - '@shikijs/vscode-textmate': 10.0.2 - '@types/hast': 3.0.4 - - shiki@3.23.0: + shiki@4.4.3: dependencies: - '@shikijs/core': 3.23.0 - '@shikijs/engine-javascript': 3.23.0 - '@shikijs/engine-oniguruma': 3.23.0 - '@shikijs/langs': 3.23.0 - '@shikijs/themes': 3.23.0 - '@shikijs/types': 3.23.0 + '@shikijs/core': 4.4.3 + '@shikijs/engine-javascript': 4.4.3 + '@shikijs/engine-oniguruma': 4.4.3 + '@shikijs/langs': 4.4.3 + '@shikijs/themes': 4.4.3 + '@shikijs/types': 4.4.3 '@shikijs/vscode-textmate': 10.0.2 - '@types/hast': 3.0.4 + '@types/hast': 3.0.5 sisteransi@1.0.5: {} @@ -6636,12 +6693,6 @@ snapshots: is-fullwidth-code-point: 3.0.0 strip-ansi: 6.0.1 - string-width@7.2.0: - dependencies: - emoji-regex: 10.6.0 - get-east-asian-width: 1.6.0 - strip-ansi: 7.2.0 - stringify-entities@4.0.4: dependencies: character-entities-html4: 2.1.0 @@ -6658,10 +6709,6 @@ snapshots: dependencies: ansi-regex: 5.0.1 - strip-ansi@7.2.0: - dependencies: - ansi-regex: 6.2.2 - style-mod@4.1.3: {} style-to-js@1.1.21: @@ -6678,6 +6725,10 @@ snapshots: make-asynchronous: 1.1.0 time-span: 5.1.0 + supports-color@7.2.0: + dependencies: + has-flag: 4.0.0 + svgo@4.0.1: dependencies: commander: 11.1.0 @@ -6708,6 +6759,8 @@ snapshots: tiny-inflate@1.0.3: {} + tinyclip@0.1.15: {} + tinyexec@1.2.2: {} tinyglobby@0.2.16: @@ -6715,6 +6768,11 @@ snapshots: fdir: 6.5.0(picomatch@4.0.4) picomatch: 4.0.4 + tinyglobby@0.2.17: + dependencies: + fdir: 6.5.0(picomatch@4.0.5) + picomatch: 4.0.5 + to-regex-range@5.0.1: dependencies: is-number: 7.0.0 @@ -6729,10 +6787,6 @@ snapshots: string-byte-length: 3.0.1 string-byte-slice: 3.0.1 - tsconfck@3.1.6(typescript@5.9.3): - optionalDependencies: - typescript: 5.9.3 - tslib@2.8.1: {} type-fest@4.41.0: {} @@ -6757,6 +6811,8 @@ snapshots: undici-types@7.16.0: {} + undici@8.10.0: {} + unhead@2.1.15: dependencies: hookable: 6.1.1 @@ -6771,11 +6827,11 @@ snapshots: trough: 2.2.0 vfile: 6.0.3 - unifont@0.7.4: + unifont@0.7.5: dependencies: css-tree: 3.2.1 - ofetch: 1.5.1 ohash: 2.0.11 + undici: 8.10.0 unist-util-find-after@5.0.0: dependencies: @@ -6834,6 +6890,8 @@ snapshots: ofetch: 1.5.1 ufo: 1.6.4 + url-extras@0.1.0: {} + util-deprecate@1.0.2: {} vfile-location@5.0.3: @@ -6851,32 +6909,31 @@ snapshots: '@types/unist': 3.0.3 vfile-message: 4.0.3 - vite-plugin-static-copy@4.1.0(vite@6.4.2(@types/node@24.12.4)(jiti@2.7.0)(lightningcss@1.32.0)(yaml@2.9.0)): + vite-plugin-static-copy@4.1.0(vite@8.2.1(@types/node@24.12.4)(esbuild@0.28.2)(jiti@2.7.0)(yaml@2.9.0)): dependencies: chokidar: 3.6.0 p-map: 7.0.4 picocolors: 1.1.1 tinyglobby: 0.2.16 - vite: 6.4.2(@types/node@24.12.4)(jiti@2.7.0)(lightningcss@1.32.0)(yaml@2.9.0) + vite: 8.2.1(@types/node@24.12.4)(esbuild@0.28.2)(jiti@2.7.0)(yaml@2.9.0) - vite@6.4.2(@types/node@24.12.4)(jiti@2.7.0)(lightningcss@1.32.0)(yaml@2.9.0): + vite@8.2.1(@types/node@24.12.4)(esbuild@0.28.2)(jiti@2.7.0)(yaml@2.9.0): dependencies: - esbuild: 0.25.12 - fdir: 6.5.0(picomatch@4.0.4) - picomatch: 4.0.4 - postcss: 8.5.15 - rollup: 4.60.4 - tinyglobby: 0.2.16 + lightningcss: 1.33.0 + picomatch: 4.0.5 + postcss: 8.5.26 + rolldown: 1.2.5 + tinyglobby: 0.2.17 optionalDependencies: '@types/node': 24.12.4 + esbuild: 0.28.2 fsevents: 2.3.3 jiti: 2.7.0 - lightningcss: 1.32.0 yaml: 2.9.0 - vitefu@1.1.3(vite@6.4.2(@types/node@24.12.4)(jiti@2.7.0)(lightningcss@1.32.0)(yaml@2.9.0)): + vitefu@1.1.3(vite@8.2.1(@types/node@24.12.4)(esbuild@0.28.2)(jiti@2.7.0)(yaml@2.9.0)): optionalDependencies: - vite: 6.4.2(@types/node@24.12.4)(jiti@2.7.0)(lightningcss@1.32.0)(yaml@2.9.0) + vite: 8.2.1(@types/node@24.12.4)(esbuild@0.28.2)(jiti@2.7.0)(yaml@2.9.0) volar-service-css@0.0.70(@volar/language-service@2.4.28): dependencies: @@ -6999,24 +7056,12 @@ snapshots: web-worker@1.5.0: {} - which-pm-runs@1.1.0: {} - - widest-line@5.0.0: - dependencies: - string-width: 7.2.0 - wrap-ansi@7.0.0: dependencies: ansi-styles: 4.3.0 string-width: 4.2.3 strip-ansi: 6.0.1 - wrap-ansi@9.0.2: - dependencies: - ansi-styles: 6.2.3 - string-width: 7.2.0 - strip-ansi: 7.2.0 - xxhash-wasm@1.1.0: {} y18n@5.0.8: {} @@ -7041,6 +7086,8 @@ snapshots: yargs-parser@21.1.1: {} + yargs-parser@22.0.0: {} + yargs@17.7.2: dependencies: cliui: 8.0.1 @@ -7053,23 +7100,6 @@ snapshots: yocto-queue@1.2.2: {} - yocto-spinner@0.2.3: - dependencies: - yoctocolors: 2.1.2 - - yoctocolors@2.1.2: {} - - zod-to-json-schema@3.25.2(zod@3.25.76): - dependencies: - zod: 3.25.76 - - zod-to-ts@1.2.0(typescript@5.9.3)(zod@3.25.76): - dependencies: - typescript: 5.9.3 - zod: 3.25.76 - - zod@3.25.76: {} - zod@4.4.3: {} zwitch@2.0.4: {} diff --git a/public/brand/favicon.svg b/public/brand/favicon.svg deleted file mode 100644 index f0ee6d1..0000000 --- a/public/brand/favicon.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/scripts/check-leakage.mjs b/scripts/check-leakage.mjs new file mode 100644 index 0000000..1d3765d --- /dev/null +++ b/scripts/check-leakage.mjs @@ -0,0 +1,112 @@ +// Fail the build if internal planning vocabulary or private-repo paths appear +// anywhere in this repository. +// +// This repo is public. Two classes leak here, and they need different scopes: +// +// * Decision-record numbers. One reached `public/openapi.yaml`, which is +// *served* at docs.odal-node.io/openapi.yaml and rendered into /api. A +// convention that only reads the source tree would never have caught it, +// which is why `public/` is explicitly in scope below. +// * Paths into the private documentation repository. Several are clickable +// relative links in READMEs that 404 for anyone browsing GitHub, and they +// disclose that repo's internal structure. +// +// Public artefacts must be self-contained: restate the design inline rather +// than pointing at something the reader cannot open. +// +// Usage: node scripts/check-leakage.mjs +import { readdirSync, readFileSync, statSync } from 'node:fs'; +import { execSync } from 'node:child_process'; +import { join, relative, extname, basename } from 'node:path'; + +const SKIP_DIRS = new Set([ + 'node_modules', + '.git', + '.claude', + 'dist', + '.astro', + '.pnpm-store', + '.dpp-engine', + 'deprecated', +]); +// This file necessarily contains the patterns it searches for. +const SKIP_FILES = new Set(['check-leakage.mjs']); +const BINARY = new Set(['.png', '.jpg', '.jpeg', '.webp', '.ico', '.woff', '.woff2', '.pdf']); + +const RULES = [ + { + // eslint-disable-next-line no-useless-escape + pattern: /ADR-\d+/g, + why: 'a decision-record number — meaningless outside the private repo and stale inside it', + }, + { pattern: /WEB_CONTENT_STRATEGY/g, why: 'a private-repo document path' }, + { pattern: /DESIGN_SPEC/g, why: 'a private-repo document path' }, + { pattern: /\bBRAND\.md\b/g, why: 'a private-repo document path' }, + { pattern: /\.\.\/\.\.\/docs\//g, why: 'a relative path into the private repo' }, +]; + +const walk = (dir, out = []) => { + for (const entry of readdirSync(dir)) { + if (SKIP_DIRS.has(entry)) continue; + const full = join(dir, entry); + if (statSync(full).isDirectory()) walk(full, out); + else if (!BINARY.has(extname(full)) && !SKIP_FILES.has(basename(full))) out.push(full); + } + return out; +}; + +const hits = []; +const files = walk(process.cwd()); + +for (const file of files) { + let text; + try { + text = readFileSync(file, 'utf8'); + } catch { + continue; // unreadable or genuinely binary + } + const lines = text.split('\n'); + for (const rule of RULES) { + lines.forEach((line, i) => { + const match = line.match(rule.pattern); + if (match) { + hits.push({ + file: relative(process.cwd(), file), + line: i + 1, + found: match[0], + why: rule.why, + }); + } + }); + } +} + +// The directories above are skipped because they are meant to be untracked. +// That assumption is worth testing: a stray `git add -A` on a branch that +// predates the .gitignore entry commits them to a public repository, and the +// content scan would never look. Ask git what is actually tracked. +const tracked = execSync('git ls-files', { encoding: 'utf8' }) + .split('\n') + .filter((p) => p.startsWith('deprecated/') || p.startsWith('.claude/')); + +if (tracked.length > 0) { + console.error(`leakage check: ${tracked.length} local-only file(s) are tracked.\n`); + for (const p of tracked.slice(0, 10)) console.error(` ${p}`); + if (tracked.length > 10) console.error(` … and ${tracked.length - 10} more`); + console.error('\n These directories are gitignored because they must not be published.'); + console.error(' Untrack them with `git rm -r --cached ` before committing.'); + process.exit(1); +} + +if (hits.length === 0) { + console.log(`leakage check: ${files.length} files scanned, clean. No local-only files tracked.`); + process.exit(0); +} + +console.error(`leakage check: ${hits.length} occurrence(s) of internal vocabulary.\n`); +for (const h of hits) { + console.error(` ${h.file}:${h.line} "${h.found}" — ${h.why}`); +} +console.error('\n Restate the mechanism inline. If provenance matters, "an internal'); +console.error(' decision record, dated X" is the most that may be said.'); +process.exit(1); diff --git a/scripts/check-links.mjs b/scripts/check-links.mjs new file mode 100644 index 0000000..24d623a --- /dev/null +++ b/scripts/check-links.mjs @@ -0,0 +1,104 @@ +// Crawl both built sites for internal links that resolve to nothing. +// +// `astro check` type-checks templates and validates content-collection +// references, but a markdown link target is an opaque string to it. Four +// `[Licensing](/engine/licensing)` links passed `check` and 404'd in production, +// because the page's source file is underscore-prefixed and never routed. +// +// This runs over `dist`, so it sees what is actually published rather than what +// the source appears to promise. Two things follow from that: +// +// * Astro's redirect stubs carry a real to their target, so a stub +// pointing at a missing page is caught without parsing meta-refresh. +// * Cross-site links are resolved against the *other* site's build. A link +// from the docs to odal-node.io/roadmap is invisible to either site's own +// tooling, which is exactly how that one survived. +// +// Usage: node scripts/check-links.mjs +import { readdirSync, readFileSync, existsSync, statSync } from 'node:fs'; +import { join, extname, relative } from 'node:path'; + +const SITES = [ + { origin: 'https://odal-node.io', dist: 'site/dpp-landing/dist', name: 'odal-node.io' }, + { origin: 'https://docs.odal-node.io', dist: 'site/dpp-docs/dist', name: 'docs.odal-node.io' }, +]; + +const walk = (dir, out = []) => { + for (const entry of readdirSync(dir)) { + const full = join(dir, entry); + if (statSync(full).isDirectory()) walk(full, out); + else if (extname(full) === '.html') out.push(full); + } + return out; +}; + +// A published path resolves if it names a real file, or a directory holding an +// index.html — the two shapes Astro's static output emits. +const resolves = (dist, path) => { + const clean = decodeURI(path.split('#')[0].split('?')[0]); + if (clean === '' || clean === '/') return existsSync(join(dist, 'index.html')); + const base = join(dist, clean); + if (extname(clean)) return existsSync(base); + return existsSync(join(base, 'index.html')) || existsSync(`${base}.html`); +}; + +const broken = []; +let linkCount = 0; +let pageCount = 0; + +for (const site of SITES) { + if (!existsSync(site.dist)) { + console.error(`link check: ${site.dist} not found — run \`pnpm -r build\` first.`); + process.exit(1); + } + + for (const file of walk(site.dist)) { + pageCount += 1; + // Astro preserves HTML comments in its output, and this repo comments out + // links rather than deleting them. A commented-out link is not published. + const html = readFileSync(file, 'utf8').replace(//g, ''); + for (const [, attr] of html.matchAll(/(?:href|src)="([^"]+)"/g)) { + // A canonical is a declaration about this page, not a dependency of it. + // The 404 page necessarily self-canonicalises to a path that is not a + // route, because it is served at every unmatched path. + if (html.includes(`rel="canonical" href="${attr}"`)) continue; + let target = null; + let dist = site.dist; + + if (attr.startsWith('/')) { + target = attr; + } else { + const other = SITES.find((s) => attr.startsWith(`${s.origin}/`) || attr === s.origin); + if (other) { + target = attr.slice(other.origin.length) || '/'; + dist = other.dist; + } + } + + // Anchors, mailto:, and third-party origins are out of scope. + if (target === null) continue; + linkCount += 1; + if (!resolves(dist, target)) { + broken.push({ from: relative(process.cwd(), file), target: attr }); + } + } + } +} + +if (broken.length === 0) { + console.log(`link check: ${linkCount} internal links across ${pageCount} pages, all resolve.`); + process.exit(0); +} + +console.error(`link check: ${broken.length} broken internal link(s).\n`); +const byTarget = new Map(); +for (const b of broken) { + if (!byTarget.has(b.target)) byTarget.set(b.target, []); + byTarget.get(b.target).push(b.from); +} +for (const [target, sources] of [...byTarget].sort((a, b) => b[1].length - a[1].length)) { + console.error(` ${target} <- ${sources.length} page(s)`); + for (const s of sources.slice(0, 6)) console.error(` ${s}`); + if (sources.length > 6) console.error(` … and ${sources.length - 6} more`); +} +process.exit(1); diff --git a/site/dpp-docs/README.md b/site/dpp-docs/README.md index a89142e..b21d898 100644 --- a/site/dpp-docs/README.md +++ b/site/dpp-docs/README.md @@ -39,9 +39,9 @@ site/dpp-docs/ │ └── custom.css # Starlight overrides via @odal/brand-tokens ``` -Brand assets shared with the landing site come from the workspace-root `../../public/brand/`, copied into the build by `viteStaticCopy` (see `astro.config.mjs`). +Brand assets live in this site’s own `public/` and `src/assets/`. There is no shared asset directory — the two sites are deployed independently, and a cross-site copy step was removed because it published a duplicate favicon at a path nothing referenced. -The sidebar structure is declared in `astro.config.mjs` and mirrored by the file-system layout under `src/content/docs/`. Renamed or removed slugs keep a redirect (e.g. `/design/proof-bound` → `/getting-started/what-odal-can-and-cannot-see`); the full redirect map is in `astro.config.mjs`. The current IA decisions live in [`../../docs/WEB_CONTENT_STRATEGY.md`](../../docs/WEB_CONTENT_STRATEGY.md) §6. +The sidebar structure is declared in `astro.config.mjs` and mirrored by the file-system layout under `src/content/docs/`. Renamed or removed slugs keep a redirect (e.g. `/design/proof-bound` → `/getting-started/what-odal-can-and-cannot-see`); the full redirect map is in `astro.config.mjs`, which is the source of truth for the information architecture. ## Honest stubs for `dpp-engine` @@ -49,7 +49,7 @@ Pages under `src/content/docs/engine/` that document unshipped surfaces use the ## Terminology rules -**Proof-bound architecture** (never "no-touch"); compliance calculators are **open** (never "pro-tier"); deployment claims only for shipped code — capability claims ("wasm32-safe, can run in edge runtimes") are fine. Full rules: [`../../docs/WEB_CONTENT_STRATEGY.md`](../../docs/WEB_CONTENT_STRATEGY.md) §7. +**Proof-bound architecture** (never "no-touch"); compliance calculators are **open** (never "pro-tier"); deployment claims only for shipped code — capability claims ("wasm32-safe, can run in edge runtimes") are fine. State what is built in the present tense and what is planned in the future tense, and never mix the two in one sentence. ## Deployment diff --git a/site/dpp-docs/astro.config.mjs b/site/dpp-docs/astro.config.mjs index d3b0b25..567939b 100644 --- a/site/dpp-docs/astro.config.mjs +++ b/site/dpp-docs/astro.config.mjs @@ -1,10 +1,9 @@ import { defineConfig } from 'astro/config'; import starlight from '@astrojs/starlight'; -import { viteStaticCopy } from 'vite-plugin-static-copy'; export default defineConfig({ site: 'https://docs.odal-node.io', - // Renamed pages keep their old URLs working (WEB_CONTENT_STRATEGY.md §6). + // Renamed pages keep their old URLs working. redirects: { // Design pages removed; redirect to the closest living equivalent. '/design/no-touch-data': '/getting-started/what-odal-can-and-cannot-see', @@ -39,9 +38,9 @@ export default defineConfig({ dark: './src/assets/logo-dark.svg', replacesTitle: false, }, - social: { - github: 'https://github.com/odal-node/dpp-core', - }, + social: [ + { icon: 'github', label: 'GitHub', href: 'https://github.com/odal-node/dpp-core' }, + ], favicon: '/favicon.svg', head: [ { tag: 'meta', attrs: { property: 'og:image', content: 'https://docs.odal-node.io/favicon.svg' } }, @@ -79,7 +78,7 @@ export default defineConfig({ { label: 'Operating securely', link: '/engine/security' }, { label: 'Self-Hosting', link: '/engine/self-hosted' }, { label: 'The CLI', link: '/engine/cli' }, - // { label: 'Licensing', link: '/engine/licensing' }, + { label: 'Licensing', link: '/engine/licensing' }, ], }, { @@ -89,7 +88,9 @@ export default defineConfig({ { label: 'ESPR Overview', link: '/regulatory/espr' }, { label: 'Battery DPP', link: '/regulatory/battery' }, { label: 'Textile DPP', link: '/regulatory/textile' }, - { label: 'Electronics DPP', link: '/regulatory/electronics' }, + // Electronics DPP is withdrawn pending a rewrite — its source is + // `_electronics.mdx`, which the underscore keeps out of the content + // collection. Restore this entry with the page, not before it. { label: 'Access Control', link: '/regulatory/access-control' }, { label: 'EU Central Registry', link: '/regulatory/central-registry' }, ], @@ -103,8 +104,5 @@ export default defineConfig({ customCss: ['./src/styles/custom.css'], }), ], - vite: { - plugins: [viteStaticCopy({ targets: [{ src: '../../public/brand', dest: '' }] })], - }, output: 'static', }); diff --git a/site/dpp-docs/openapi-source.json b/site/dpp-docs/openapi-source.json new file mode 100644 index 0000000..77b48af --- /dev/null +++ b/site/dpp-docs/openapi-source.json @@ -0,0 +1,6 @@ +{ + "_comment": "Provenance for public/openapi.yaml, which is vendored from dpp-engine. The commit is what `pnpm run sync:openapi` last copied from, and what `check:openapi` verifies the vendored copy against. Pinning to a commit rather than to the engine's main branch keeps this repo's CI deterministic: an unrelated merge in the engine cannot turn a pull request here red, and a coordinated change across both repos can land without deadlocking on which merges first. Bumping the pin is a deliberate, reviewable line in a diff.", + "repository": "odal-node/dpp-engine", + "path": "api/openapi.yaml", + "commit": "45f8aa884da0535a1059e0e51467c6de37c471ed" +} diff --git a/site/dpp-docs/package.json b/site/dpp-docs/package.json index 685d367..dfe82ab 100644 --- a/site/dpp-docs/package.json +++ b/site/dpp-docs/package.json @@ -10,13 +10,14 @@ "build": "astro build", "preview": "astro preview", "check": "astro check", - "sync:openapi": "node scripts/sync-openapi.mjs" + "sync:openapi": "node scripts/sync-openapi.mjs", + "check:openapi": "node scripts/sync-openapi.mjs --check" }, "dependencies": { - "@astrojs/starlight": "^0.30.6", + "@astrojs/starlight": "^0.41.7", "@odal/brand-tokens": "workspace:*", "@scalar/api-reference": "^1.62.5", - "astro": "^5", + "astro": "^7.2.4", "vue": "^3.5.39" }, "devDependencies": { diff --git a/site/dpp-docs/public/_headers b/site/dpp-docs/public/_headers new file mode 100644 index 0000000..261e0d1 --- /dev/null +++ b/site/dpp-docs/public/_headers @@ -0,0 +1,27 @@ +# Cloudflare Pages custom headers — docs.odal-node.io +# Mirrors site/dpp-landing/public/_headers. The docs site is the larger surface +# and carries the only JavaScript-heavy page (/api), so nosniff and DENY matter +# more here than on the marketing site, not less. + +/* + X-Content-Type-Options: nosniff + X-Frame-Options: DENY + Referrer-Policy: strict-origin-when-cross-origin + Permissions-Policy: camera=(), microphone=(), geolocation=() + +# Astro content-hashes everything here, so a new build produces new filenames +# and an old one never needs invalidating. +/_astro/* + Cache-Control: public, max-age=31536000, immutable + +# Pagefind emits content-addressed index shards under a stable directory. +/pagefind/* + Cache-Control: public, max-age=604800 + +/favicon.svg + Cache-Control: public, max-age=604800 + +# Vendored from the engine and re-synced on change, so it must not be pinned +# for a year the way a hashed asset can be. +/openapi.yaml + Cache-Control: public, max-age=3600 diff --git a/site/dpp-docs/public/openapi.yaml b/site/dpp-docs/public/openapi.yaml index a288d8d..f438615 100644 --- a/site/dpp-docs/public/openapi.yaml +++ b/site/dpp-docs/public/openapi.yaml @@ -62,8 +62,17 @@ components: type: http scheme: basic description: | - Local development fallback. Uses the `ADMIN_USERNAME` and - `ADMIN_PASSWORD` environment variables. Not available in production. + The operator's own bootstrap credential, from the `ADMIN_USERNAME` and + `ADMIN_PASSWORD` environment variables. It mints the first API key on a + fresh node — before any key exists — and is the lockout-recovery path + afterwards, since it carries no `keyId` and so can revoke any key. + + Active in every environment where both variables are set; there is no + production gate. Leave them unset once an API key exists if you do not + want the path available. + + Reached only via the `Basic` scheme. A `Bearer` token is never matched + against it, even one carrying the same `base64(user:pass)` payload. MutualTLS: type: mutualTLS @@ -84,30 +93,6 @@ components: description: UUID v7 identifier assigned on creation. Embedded in QR codes and public URLs. example: "019723f4-1a2b-7c3d-8e4f-5a6b7c8d9e0f" - ProductCategory: - description: | - Fine-grained product category (a data attribute — the dispatch key is - `sector`). Known categories serialise as snake_case strings; any other - category serialises as `{ "other": "" }`. - oneOf: - - type: string - enum: - - ev_battery - - industrial_battery - - lmt_battery - - apparel - - footwear - - home_textile - - smartphone - - laptop - - charger - - type: object - required: [other] - properties: - other: - type: string - example: "ev_battery" - PassportStatus: type: string enum: [draft, active, suspended, archived] @@ -231,12 +216,34 @@ components: type: string description: Optional batch or lot identifier example: "BATCH-2026-04-001" + placedOnMarketDate: + type: string + format: date + description: | + The date this product was placed on the EU market — the regulated + triggering event that fixes which law governs it. + + Optional, and omitting it is not neutral. A compliance determination + whose rule is phased by date has no answer without it: the node + reports the missing fact rather than substituting today's date, which + would produce a determination that silently changes its own answer + when a phase begins. For batteries this decides which EU 2023/1542 + Art. 8 minimum recycled shares apply. + example: "2026-03-14" schemaVersion: type: string - description: >- - Sector schema version. When omitted, the sector's current version is - used (e.g. battery 2.0.0); a supplied value is honoured. - example: "1.0.0" + description: | + Sector schema version. Optional, and the only accepted value is the + sector's **current** version — omitting it is equivalent. Any other + value is rejected with `422`. + + It is not the caller's to choose: the stored version selects the + disclosure table the passport's public view is filtered through and + signed under, and an older table classifies fewer fields, defaulting + the rest to public. The body is validated against the current schema + in either case, so a differing declaration is already false about the + body it accompanies. + example: "2.6.0" parentPassportRef: $ref: "#/components/schemas/PassportRef" componentRefs: @@ -291,8 +298,7 @@ components: verified: type: boolean reason: - type: string - nullable: true + type: [string, "null"] enum: [ unreachable, @@ -321,13 +327,10 @@ components: id: $ref: "#/components/schemas/DppId" batchId: - type: string - nullable: true + type: [string, "null"] productName: type: string example: "EcoCell Pro 48V" - productCategory: - $ref: "#/components/schemas/ProductCategory" manufacturer: $ref: "#/components/schemas/ManufacturerInfo" materials: @@ -335,23 +338,19 @@ components: items: $ref: "#/components/schemas/MaterialEntry" co2ePerUnit: - type: number - nullable: true + type: [number, "null"] repairabilityScore: - type: number - nullable: true + type: [number, "null"] sectorData: $ref: "#/components/schemas/SectorData" status: $ref: "#/components/schemas/PassportStatus" qrCodeUrl: - type: string + type: [string, "null"] format: uri - nullable: true description: "GS1 Digital Link the carrier (QR) encodes, set on publish: {resolverBase}/01/{gtin}/21/{serial} for a trade item, else {resolverBase}/dpp/{id}. resolverBase is per-deployment (RESOLVER_BASE_URL, default https://id.odal-node.io)." jwsSignature: - type: string - nullable: true + type: [string, "null"] description: JWS compact serialisation (Ed25519). Null until published. createdAt: type: string @@ -360,9 +359,8 @@ components: type: string format: date-time publishedAt: - type: string + type: [string, "null"] format: date-time - nullable: true schemaVersion: type: string example: "1.0.0" @@ -390,14 +388,11 @@ components: type: string example: "published" previousStatus: - type: string - nullable: true + type: [string, "null"] newStatus: - type: string - nullable: true + type: [string, "null"] metadata: - type: object - nullable: true + type: [object, "null"] timestamp: type: string format: date-time @@ -436,8 +431,7 @@ components: nodeVersion: type: string rulesetVersion: - type: string - nullable: true + type: [string, "null"] contentHashes: type: object description: member name -> hex SHA-256 of that member's JCS-canonical bytes. @@ -474,22 +468,62 @@ components: items: $ref: "#/components/schemas/AuditEntry" transferChain: - type: object - nullable: true + type: [object, "null"] description: Present iff the passport has ever changed responsible operator. eolEvent: - type: object - nullable: true + type: [object, "null"] description: Present iff the passport was declared end-of-life. checkpoint: - type: object - nullable: true + type: [object, "null"] description: Always `null` in format v1 — the signed-checkpoint layer is not yet built. calcReceipts: type: array description: Always empty in format v1 — `dpp-calc` invocation is not yet wired end to end. items: type: object + componentGraph: + # `anyOf` rather than `allOf` + `nullable`: this is OpenAPI 3.1, where + # `nullable` no longer exists and a nullable `$ref` is expressed as a + # union with the null type. + anyOf: + - $ref: "#/components/schemas/TreeReport" + - type: "null" + description: | + The recursive component-tree (bill-of-materials) verification report, + present iff the passport declares `componentRefs`. `null` for a unit + with no modelled sub-assemblies. + + Generated at dossier-assembly time by walking the tree and pin-checking + each node, then bound into `contentHashes` like every other member — so + a tampered report fails the dossier's `content_integrity` check rather + than passing as an unverifiable attachment. + + Integrity only, the same caveat as the standalone `verify-tree` route: + it proves each node's signed public view is unchanged against its + pinned hash, not the cryptographic validity of that node's signature. + qualifiedSeal: + type: [object, "null"] + description: | + The passport's eIDAS qualified seal, present iff one has been + applied. Carries the seal envelope plus `signedOverJws` and + `payloadHash`, so a verifier holding only this dossier has both the + CAdES and the preimage to check it against. + + Included because a dossier is what an authority is handed and the + seal is its one member carrying an Art. 35(2) presumption — and + because it is unreachable otherwise: the seal is stripped from + `fullView` and `publicView` alike, since it covers the full-payload + signature rather than any redaction. Bound into `contentHashes` like + every other member. `null` when the seal is still queued. + properties: + seal: + type: object + description: The `SealedEnvelope` as persisted on the passport. + signedOverJws: + type: string + payloadHash: + type: string + pattern: "^[0-9a-f]{64}$" EvidenceDossierRecord: type: object @@ -571,8 +605,7 @@ components: type: string example: "Odal Node GmbH" tradeName: - type: string - nullable: true + type: [string, "null"] address: type: string example: "Johannes Strauss 12" @@ -586,29 +619,23 @@ components: format: email example: "contact@odal-node.io" didWebUrl: - type: string + type: [string, "null"] format: uri - nullable: true productCategories: - type: array + type: [array, "null"] items: type: string - nullable: true brandPrimary: - type: string - nullable: true + type: [string, "null"] description: "Primary brand colour (hex)" example: "#2E7D32" brandSecondary: - type: string - nullable: true + type: [string, "null"] brandLogoUrl: - type: string + type: [string, "null"] format: uri - nullable: true customDomain: - type: string - nullable: true + type: [string, "null"] dataResidency: type: string default: "EU" @@ -616,16 +643,13 @@ components: type: integer default: 3650 featureFlags: - type: object - nullable: true + type: [object, "null"] createdAt: - type: string + type: [string, "null"] format: date-time - nullable: true updatedAt: - type: string + type: [string, "null"] format: date-time - nullable: true UpdateOperatorConfig: type: object @@ -682,13 +706,11 @@ components: type: string format: date-time lastUsedAt: - type: string + type: [string, "null"] format: date-time - nullable: true expiresAt: - type: string + type: [string, "null"] format: date-time - nullable: true NewApiKey: type: object @@ -712,9 +734,8 @@ components: description: Human-readable label for this key. example: "CI pipeline" expiresAt: - type: string + type: [string, "null"] format: date-time - nullable: true description: Optional expiration. Null = never expires. Facility: @@ -741,8 +762,7 @@ components: maxLength: 2 example: "DE" address: - type: string - nullable: true + type: [string, "null"] isDefault: type: boolean description: The default facility is stamped onto new passports. @@ -769,8 +789,7 @@ components: maxLength: 2 example: "DE" address: - type: string - nullable: true + type: [string, "null"] isDefault: type: boolean default: false @@ -792,8 +811,7 @@ components: type: string example: "5493001KJTIIGC8Y1R12" label: - type: string - nullable: true + type: [string, "null"] isPrimary: type: boolean description: The primary identifier is stamped onto new passports. @@ -817,8 +835,7 @@ components: accepted without structural verification. example: "5493001KJTIIGC8Y1R12" label: - type: string - nullable: true + type: [string, "null"] isPrimary: type: boolean default: false @@ -984,12 +1001,10 @@ components: total: type: integer result: - type: object - nullable: true + type: [object, "null"] description: Populated on completion (created/errors) or failure (reason). report: - type: object - nullable: true + type: [object, "null"] description: The row-addressed findings report — populated for every job, dry-run or apply, independent of `result`. ApiError: @@ -1003,6 +1018,312 @@ components: type: string example: "productName is required" + # ---- Service info ------------------------------------------------------- + + VaultInfo: + type: object + required: [version, coreVersion, authMethods, features] + description: Vault build/version metadata, for dashboard feature detection. + properties: + version: + type: string + description: This node's own dpp-vault crate version. + example: "0.11.0" + coreVersion: + type: string + description: The dpp-domain (dpp-core) version this build was compiled against. + example: "0.16.0" + authMethods: + type: array + items: + type: string + description: >- + Auth schemes the vault accepts. Currently a fixed list, not + derived from live config — `local` is listed even when + `ADMIN_USERNAME`/`ADMIN_PASSWORD` are unset. + example: ["api_key", "local"] + features: + type: array + items: + type: string + example: ["passthrough_compliance"] + + # ---- End-of-life ----------------------------------------------------- + + DerogationRef: + type: object + required: [category] + description: >- + A recognised derogation from the ESPR Art. 25 destruction ban. The + category list is fixed by the applicable delegated act; validated + against that list at the engine boundary, not by this schema. + properties: + category: + type: string + description: The derogation category as named by the delegated act. + example: "health-and-safety" + actCitation: + type: [string, "null"] + description: The act/article this derogation is grounded in (e.g. an OJ/CELEX ref). + + DeactivationReason: + description: >- + Why a passport reached end-of-life, internally tagged by `kind`. + Destruction alone requires a `derogation` citing the lawful basis. + oneOf: + - type: object + required: [kind] + properties: + kind: { type: string, enum: [recycled] } + - type: object + required: [kind, derogation] + properties: + kind: { type: string, enum: [destroyed] } + derogation: + $ref: "#/components/schemas/DerogationRef" + - type: object + required: [kind] + properties: + kind: { type: string, enum: [exported] } + - type: object + required: [kind] + properties: + kind: { type: string, enum: [lost] } + example: { kind: "recycled" } + + EolRequest: + type: object + required: [reason] + description: Request body for declaring a passport end-of-life. + properties: + reason: + $ref: "#/components/schemas/DeactivationReason" + declaredBy: + type: [string, "null"] + description: DID of the declaring operator; defaults to the authenticated actor. + materialRecovery: + type: [object, "null"] + description: Optional recovered-material summary (Battery Annex XIII circularity). + notes: + type: [string, "null"] + + # ---- Transfer of responsibility --------------------------------------- + + OperatorRole: + type: string + description: The role of an economic operator in the DPP supply chain. + enum: + - manufacturer + - importer + - distributor + - authorisedRepresentative + - remanufacturer + - repurposer + - preparerForReuse + - repairer + - recycler + + ResponsibleOperator: + type: object + required: [did, name, role, country] + description: An economic operator responsible for a DPP (ESPR "responsible economic operator"). + properties: + did: + type: string + example: "did:web:acme.example.com" + name: + type: string + role: + $ref: "#/components/schemas/OperatorRole" + euOperatorId: + type: [string, "null"] + description: EU-assigned economic operator identifier, if available. + euOperatorIdScheme: + type: [string, "null"] + description: 'Scheme euOperatorId is expressed in — "vat", "lei", "eori", "duns".' + country: + type: string + minLength: 2 + maxLength: 2 + description: ISO 3166-1 alpha-2 country code of the operator's establishment. + + TransferReason: + type: string + description: The reason for a transfer of DPP responsibility. + enum: + - sale + - return + - remanufacturing + - repurposing + - preparationForReuse + - import + - insolvencySuccession + + TransferInitiateRequest: + type: object + required: [fromOperator, toOperator, reason] + properties: + fromOperator: + allOf: + - $ref: "#/components/schemas/ResponsibleOperator" + description: The current (outgoing) responsible operator — must match the chain head. + toOperator: + allOf: + - $ref: "#/components/schemas/ResponsibleOperator" + description: The incoming responsible operator taking over the DPP. + reason: + $ref: "#/components/schemas/TransferReason" + notes: + type: [string, "null"] + + TransferRecord: + type: object + required: [transferId, passportId, fromOperator, toOperator, reason, initiatedAt] + description: A single transfer-of-responsibility event, dual-signed by the outgoing and incoming operators. + properties: + transferId: + type: string + format: uuid + passportId: + type: string + format: uuid + fromOperator: + $ref: "#/components/schemas/ResponsibleOperator" + toOperator: + $ref: "#/components/schemas/ResponsibleOperator" + reason: + $ref: "#/components/schemas/TransferReason" + fromSignature: + type: [string, "null"] + description: Compact JWS from the outgoing operator, authorising the handover. + toSignature: + type: [string, "null"] + description: Compact JWS from the incoming operator, accepting responsibility. + initiatedAt: + type: string + format: date-time + completedAt: + type: [string, "null"] + format: date-time + rejectedAt: + type: [string, "null"] + format: date-time + cancelledAt: + type: [string, "null"] + format: date-time + notes: + type: [string, "null"] + + # ---- Registry-identity audit ------------------------------------------- + + RegistryIdentityAudit: + type: object + required: [id, operatorId, entityType, entityId, action, actor, ts] + description: >- + An immutable audit record for a registry-identity mutation (a + facility per Annex III or an operator identifier per Art. 13). + Append-only. + properties: + id: + type: string + format: uuid + operatorId: + type: string + entityType: + type: string + enum: [facility, operator_identifier] + entityId: + type: string + format: uuid + action: + type: string + enum: [added, retired, set_default, set_primary] + actor: + type: string + description: user_id of the actor who performed the change. + snapshot: + type: [object, "null"] + description: The full record at the time of the action, for reconstruction. + ts: + type: string + format: date-time + + # ---- Scan telemetry (internal) ----------------------------------------- + + ScanVariant: + type: string + enum: [html, json] + + ScanCount: + type: object + required: [dppId, day, variant, count] + description: One aggregated scan increment since the resolver's last flush. + properties: + dppId: + type: string + description: The resolved passport id, as an opaque string (validated at ingest). + day: + type: string + format: date + variant: + $ref: "#/components/schemas/ScanVariant" + count: + type: integer + minimum: 0 + + QrRenderCount: + type: object + required: [dppId, day, count] + description: One aggregated QR-render increment since the resolver's last flush. + properties: + dppId: + type: string + day: + type: string + format: date + count: + type: integer + minimum: 0 + + ScanBatch: + type: object + required: [scans, qrRenders] + description: The full flush payload the resolver sends to the vault. + properties: + scans: + type: array + items: + $ref: "#/components/schemas/ScanCount" + qrRenders: + type: array + items: + $ref: "#/components/schemas/QrRenderCount" + + # ---- Internal identity verification ------------------------------------ + + VerifyRequest: + type: object + required: [operator_id, jws, payload] + description: "Internal verification request. Field names are snake_case (internal contract)." + properties: + operator_id: + type: string + description: Operator id whose key the signature is checked against. + example: "self_hosted" + jws: + type: string + description: The compact JWS to verify. + payload: + description: The payload the caller expects the JWS to have been signed over. + + VerifyResponse: + type: object + required: [valid] + properties: + valid: + type: boolean + description: True iff the signature verifies against the named operator's key AND was signed over exactly this payload. + responses: Unauthorized: description: Missing or invalid authentication credentials. @@ -1024,6 +1345,23 @@ components: error: "NOT_FOUND" message: "DPP not found." + NotAcceptable: + description: | + No representation matches the request's `Accept` header. The response + body names the media types this resource can produce. + + A passport carrying no GTIN — an unsold-goods report, or an untyped + sector — also gets this for `application/aas+json`: it identifies no + trade item, so it has no AAS asset identity and therefore no AAS + representation. + content: + application/problem+json: + schema: + $ref: "#/components/schemas/ApiError" + example: + error: "NOT_ACCEPTABLE" + message: "No representation matches 'application/pdf'. This resource is available as text/html, application/ld+json, or application/aas+json." + ValidationError: description: One or more fields failed validation. content: @@ -1116,8 +1454,8 @@ paths: Paginated list of DPPs for the authenticated operator. Supports filtering by status, free-text search across `productName`, `batchId`, and `manufacturer.name`, and an exact - `facilityId` match (ESPR Annex III; ADR-006 — a grouping filter, - never an isolation boundary). + `facilityId` match (ESPR Annex III). A grouping filter, never an + isolation boundary. tags: [DPP Management] security: - BearerApiKey: [] @@ -1404,16 +1742,17 @@ paths: "409": $ref: "#/components/responses/Conflict" - # ---- Audit History (odal-vault) ------------------------------------------ - - /vault/api/v1/dpp/{dppId}/history: - get: - operationId: getDppHistory - summary: Get DPP audit history + /vault/api/v1/dpp/{dppId}/eol: + post: + operationId: declareDppEol + summary: Declare a DPP end-of-life description: | - Returns the chronological audit trail for a passport: creation, - status transitions, field updates, etc. - tags: [DPP Management] + Transition a `published` or `suspended` DPP to `deactivated` + (terminal). The record is retained, never deleted — the passport + outlives the product. Destruction (`reason.kind: destroyed`) is only + lawful with a recognised derogation from the unsold-goods destruction + ban (ESPR Art. 25 delegated act). + tags: [DPP Lifecycle] security: - BearerApiKey: [] - BasicAuth: [] @@ -1423,33 +1762,36 @@ paths: required: true schema: $ref: "#/components/schemas/DppId" + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/EolRequest" responses: "200": - description: List of audit entries. + description: DPP deactivated. Returns the full passport record. content: application/json: schema: - type: array - items: - $ref: "#/components/schemas/AuditEntry" + $ref: "#/components/schemas/PassportResponse" "401": $ref: "#/components/responses/Unauthorized" "404": $ref: "#/components/responses/NotFound" + "409": + $ref: "#/components/responses/Conflict" - /vault/api/v1/dpp/{dppId}/verify-tree: - get: - operationId: verifyDppTree - summary: Recursively verify a passport's component tree (BOM) + /vault/api/v1/dpp/{dppId}/transfer/initiate: + post: + operationId: initiateDppTransfer + summary: Initiate a transfer of responsibility description: | - Walks the passport's `componentRefs` breadth-first, fetching each node - and checking its public JWS against the pinned hash. Fails closed on - every ambiguity, bounded by a depth cap and a total-node cap; the report - names the path from the root to any broken node. - - Integrity only: this proves each node's signed public view is unchanged - (hash pin), not the cryptographic validity of the signature. - tags: [DPP Management] + The outgoing operator signs a pending handover onto the passport's + transfer chain. Only a `published` DPP can be transferred. In the + managed single-node model the caller supplies both the outgoing and + incoming operator; the node signs on the outgoing operator's behalf. + tags: [DPP Lifecycle] security: - BearerApiKey: [] - BasicAuth: [] @@ -1459,34 +1801,38 @@ paths: required: true schema: $ref: "#/components/schemas/DppId" + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/TransferInitiateRequest" responses: "200": - description: The component-tree verification report. + description: Transfer initiated (pending acceptance). content: application/json: schema: - $ref: "#/components/schemas/TreeReport" + $ref: "#/components/schemas/TransferRecord" "401": $ref: "#/components/responses/Unauthorized" "404": $ref: "#/components/responses/NotFound" + "409": + $ref: "#/components/responses/Conflict" + "422": + $ref: "#/components/responses/ValidationError" - /vault/api/v1/dpp/{dppId}/lint: + /vault/api/v1/dpp/{dppId}/transfer/accept: post: - operationId: relintDpp - summary: Re-check plausibility-lint findings + operationId: acceptDppTransfer + summary: Accept a pending transfer of responsibility description: | - Recomputes the `dpp-rules` plausibility lint pack against the DPP's - current sector data and persists the refreshed `lintResult` (pack - version, findings, assessed-at timestamp). Findings are non-binding — - arithmetic and physical-plausibility checks distinct from binding - compliance rules — and never gate publish or any other transition. - - Works regardless of DPP status, including `active` (published): - re-checking does not retroactively affect the passport's JWS - signature, which is frozen over whatever `lintResult` looked like at - publish time. No request body is required. - tags: [DPP Management] + The incoming operator's signature completes a pending handover: the + outgoing operator's signature is verified before the node countersigns + on the incoming operator's behalf, and the incoming operator becomes + the passport's current responsible operator. + tags: [DPP Lifecycle] security: - BearerApiKey: [] - BasicAuth: [] @@ -1498,20 +1844,419 @@ paths: $ref: "#/components/schemas/DppId" responses: "200": - description: Lint findings refreshed. Returns the full passport record. + description: Transfer completed. content: application/json: schema: - $ref: "#/components/schemas/PassportResponse" + $ref: "#/components/schemas/TransferRecord" "401": $ref: "#/components/responses/Unauthorized" "404": - $ref: "#/components/responses/NotFound" + description: No pending transfer to accept for this DPP. + content: + application/json: + schema: + $ref: "#/components/schemas/ApiError" + "422": + $ref: "#/components/responses/ValidationError" - /vault/api/v1/dpp/{dppId}/evidence: - post: - operationId: generateDppEvidence - summary: Generate and store a signed evidence dossier + # ---- Audit History (odal-vault) ------------------------------------------ + + /vault/api/v1/dpp/{dppId}/history: + get: + operationId: getDppHistory + summary: Get DPP audit history + description: | + Returns the chronological audit trail for a passport: creation, + status transitions, field updates, etc. Unbounded — returns the full + trail with no pagination or limit. + tags: [DPP Management] + security: + - BearerApiKey: [] + - BasicAuth: [] + parameters: + - name: dppId + in: path + required: true + schema: + $ref: "#/components/schemas/DppId" + responses: + "200": + description: List of audit entries. + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/AuditEntry" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + + /vault/api/v1/dpp/{dppId}/stats: + get: + operationId: getDppScanStats + summary: Per-passport scan telemetry + description: | + Aggregate, privacy-safe resolution counts for one passport over a + trailing window. Scans and QR-image renders are reported as separate + fields and are never summed — a render is label production, not a + resolution. Nothing about the scanner (IP, agent, session) is collected + or returned; the counters carry no such fields. Returns zeros for a + passport that has never been scanned. + tags: [Scan Telemetry] + security: + - BearerApiKey: [] + - BasicAuth: [] + parameters: + - name: dppId + in: path + required: true + schema: + $ref: "#/components/schemas/DppId" + - name: days + in: query + required: false + description: Trailing window in days (default 30, clamped to 1..=730). + schema: + type: integer + minimum: 1 + maximum: 730 + default: 30 + responses: + "200": + description: Aggregate scan counts for the passport. + content: + application/json: + schema: + type: object + properties: + windowDays: { type: integer, example: 30 } + totalScans: { type: integer, example: 128 } + scansHtml: { type: integer, example: 96 } + scansJson: { type: integer, example: 32 } + qrRenders: { type: integer, example: 4 } + daily: + type: array + items: + type: object + properties: + day: { type: string, format: date } + count: { type: integer } + "401": + $ref: "#/components/responses/Unauthorized" + + /vault/api/v1/stats: + get: + operationId: getOperatorScanStats + summary: Operator-wide scan telemetry rollup + description: | + Aggregate resolution counts across all of the operator's passports over + a trailing window — the "your passports were resolved N times" figure. + Scans and QR-image renders are separate; nothing about the scanner is + collected. + tags: [Scan Telemetry] + security: + - BearerApiKey: [] + - BasicAuth: [] + parameters: + - name: days + in: query + required: false + description: Trailing window in days (default 30, clamped to 1..=730). + schema: + type: integer + minimum: 1 + maximum: 730 + default: 30 + responses: + "200": + description: Operator-wide aggregate scan counts. + content: + application/json: + schema: + type: object + properties: + windowDays: { type: integer, example: 30 } + totalScans: { type: integer, example: 4213 } + totalQrRenders: { type: integer, example: 57 } + distinctPassportsScanned: { type: integer, example: 312 } + "401": + $ref: "#/components/responses/Unauthorized" + + /vault/api/v1/dpp/{dppId}/verify-tree: + get: + operationId: verifyDppTree + summary: Recursively verify a passport's component tree (BOM) + description: | + Walks the passport's `componentRefs` breadth-first, fetching each node + and checking its public JWS against the pinned hash. Fails closed on + every ambiguity, bounded by a depth cap and a total-node cap; the report + names the path from the root to any broken node. + + Integrity only: this proves each node's signed public view is unchanged + (hash pin), not the cryptographic validity of the signature. + tags: [DPP Management] + security: + - BearerApiKey: [] + - BasicAuth: [] + parameters: + - name: dppId + in: path + required: true + schema: + $ref: "#/components/schemas/DppId" + responses: + "200": + description: The component-tree verification report. + content: + application/json: + schema: + $ref: "#/components/schemas/TreeReport" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + + /vault/api/v1/seal: + get: + operationId: getSealSummary + summary: Operator-wide sealing state + description: | + How many published passports carry no seal, plus the outbox totals + behind that number. + + `unsealedPublished` is the headline; the three row counts are context. + They answer different questions and can legitimately disagree: the + counts describe outbox **rows**, while the obligation is about + **passports**. Enqueueing happens after the publish commits, so a crash + in that window publishes a passport that no row will ever cover — + `pending: 0, exhausted: 0` is therefore consistent with any number of + unsealed passports, and a summary built on rows alone would report all + clear. A repair sweep queues those passports on its next pass. + + A passport whose seal covers a *superseded* signature is not counted + here — it carries a seal, and that seal remains a valid attestation of + the signature it was bought for. `GET /vault/api/v1/dpp/{dppId}/seal` + reports that case per passport as `coverage`. + + When `sealingConfigured` is `false` no seal provider is selected, so + every count is `0` because this node has no outbox — not because it has + nothing outstanding. + tags: [DPP Management] + security: + - BearerApiKey: [] + - BasicAuth: [] + responses: + "200": + description: Operator-wide sealing state. + content: + application/json: + schema: + type: object + required: + [ + unsealedPublished, + pending, + sealed, + exhausted, + sealingConfigured, + ] + properties: + unsealedPublished: + type: integer + format: int64 + description: >- + Published passports carrying no seal at all. `0` is the + healthy state. + example: 0 + pending: + type: integer + format: int64 + description: Outbox rows awaiting a sealing attempt. + sealed: + type: integer + format: int64 + description: Outbox rows whose seal is on the passport. + exhausted: + type: integer + format: int64 + description: Outbox rows that gave up after exhausting retries. + sealingConfigured: + type: boolean + description: >- + False when no seal provider is configured, in which case + every count above is `0` for that reason alone. + "401": + $ref: "#/components/responses/Unauthorized" + + /vault/api/v1/dpp/{dppId}/seal: + get: + operationId: getDppSeal + summary: Fetch the passport's eIDAS qualified electronic seal + description: | + Returns the qualified seal a QTSP applied to this passport, together with + the compact JWS it was taken over and that JWS's SHA-256 digest. + + The seal has its own route because it is stripped from every audience + view, public included: it covers the **full**-payload `jwsSignature`, so + attaching it to a redacted body would hand the reader a proof that + verifies against nothing they received. + + **This node does not validate the seal.** A detached CAdES must be + checked by an independent AdES validator against the EU Trusted List. A + verdict from the node that bought the seal would attest nothing, so none + is offered. + + `coverage` answers a narrower question that the node *can* answer, from + its own records: `sealedPayloadHash` is the digest it asked the backend + to seal, so a passport re-published after sealing shows as `superseded` + without any AdES tooling. That is a record of what was requested, not + proof of what the CAdES covers — the validator's extracted message + digest is the cross-check. A `superseded` seal remains valid for the + signature it does cover; a seal over the new signature has not landed + yet. + + `404` when the passport has no seal — it may be unpublished, its seal may + still be queued, or the node may have no QTSP configured. An unsealed + passport has no seal resource rather than an empty one. + tags: [DPP Management] + security: + - BearerApiKey: [] + - BasicAuth: [] + parameters: + - name: dppId + in: path + required: true + schema: + $ref: "#/components/schemas/DppId" + responses: + "200": + description: The qualified seal and the signature it attests to. + content: + application/json: + schema: + type: object + required: + [ + format, + sealValue, + sealedAt, + signingCertRef, + placeholder, + currentJws, + currentPayloadHash, + sealedPayloadHash, + coverage, + verification, + ] + properties: + format: + type: string + description: AdES format of `sealValue`. + example: CADES + sealValue: + type: string + description: Base64 detached CAdES (`.p7s`) as returned by the QTSP. + sealedAt: + type: string + format: date-time + signingCertRef: + type: [string, "null"] + description: | + Hex SHA-256 of the certificate the seal names as its + signer, **as reported by the seal** — read out of the CAdES + structure, never verified. + + It answers *which* certificate to ask about, not whether + that certificate was qualified or on the EU Trusted List + when the seal was made; both are the independent + validator's question. `null` when the seal predates + extraction or could not be parsed. + pattern: "^[0-9a-f]{64}$" + placeholder: + type: boolean + description: | + `true` when this is a development placeholder with no legal + validity. A production node refuses to boot in that state. + currentJws: + type: string + description: The passport's current compact JWS. + currentPayloadHash: + type: string + description: | + Hex SHA-256 of `currentJws` — the digest a seal over this + passport's present signature would be taken over. + pattern: "^[0-9a-f]{64}$" + sealedPayloadHash: + type: [string, "null"] + description: | + Hex SHA-256 this node asked the backend to seal, from the + outbox row that bought `sealValue`. `null` when the node + holds no such row — a seal restored from a backup or + produced elsewhere. + pattern: "^[0-9a-f]{64}$" + coverage: + type: string + enum: [current, superseded, unknown] + description: | + Whether the stored seal covers the passport's current + signature, per this node's own records. + + `current` — the requested digest is the passport's current + one. `superseded` — the passport was re-published after + this seal was bought. `unknown` — no record; only the + external validator can answer. + verification: + type: string + description: What was and was not checked by this node. + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + + /vault/api/v1/dpp/{dppId}/lint: + post: + operationId: relintDpp + summary: Re-check plausibility-lint findings + description: | + Recomputes the `dpp-rules` plausibility lint pack against the DPP's + current sector data and persists the refreshed `lintResult` (pack + version, findings, assessed-at timestamp). Findings are non-binding — + arithmetic and physical-plausibility checks distinct from binding + compliance rules — and never gate publish or any other transition. + + Works regardless of DPP status, including `active` (published): + re-checking does not retroactively affect the passport's JWS + signature, which is frozen over whatever `lintResult` looked like at + publish time. No request body is required. + tags: [DPP Management] + security: + - BearerApiKey: [] + - BasicAuth: [] + parameters: + - name: dppId + in: path + required: true + schema: + $ref: "#/components/schemas/DppId" + responses: + "200": + description: Lint findings refreshed. Returns the full passport record. + content: + application/json: + schema: + $ref: "#/components/schemas/PassportResponse" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + + /vault/api/v1/dpp/{dppId}/evidence: + post: + operationId: generateDppEvidence + summary: Generate and store a signed evidence dossier description: | Assembles a self-contained, signed dossier proving a passport's full proof chain — both JWS signatures, DID document snapshots, the @@ -1578,6 +2323,10 @@ paths: get: operationId: getEvidenceDossier summary: Fetch one stored dossier's document + description: >- + Returns the dossier document itself — the same shape + `POST .../evidence` returns on generation, not the summary wrapper + the list endpoint shows. tags: [Evidence Dossiers] security: - BearerApiKey: [] @@ -1794,6 +2543,76 @@ paths: "404": $ref: "#/components/responses/NotFound" + # ---- Plugins (odal-vault) ------------------------------------------------ + + /vault/api/v1/plugins: + post: + operationId: installPlugin + summary: Install a signed sector plugin + description: | + Verify, persist, and hot-swap a signed sector plugin — no node restart. + + The node verifies the uploaded artifact's detached signature against its + pinned publisher key, gates the plugin's declared ABI, instantiate-smokes + the module, persists it (so a restart re-loads it), and atomically swaps + it into service. Any rejection is fail-closed — the previously installed + plugin keeps serving. Admin-scoped. + + Both a portable `.wasm` (compiled on the node) and a precompiled `.cwasm` + (loaded only if it matches this node's engine) are accepted. + tags: [Plugins] + security: + - BearerApiKey: [] + - BasicAuth: [] + requestBody: + required: true + content: + multipart/form-data: + schema: + type: object + required: [wasm, sig] + properties: + wasm: + type: string + format: binary + description: > + The `.wasm` or precompiled `.cwasm` plugin artifact. Its + filename determines the sector when `sector` is omitted + (`sector-.wasm`) and whether it is treated as + precompiled (`.cwasm`). + sig: + type: string + format: binary + description: Detached Ed25519 signature over SHA-256 of the artifact bytes. + sector: + type: string + description: Sector key; derived from the filename if omitted. + example: battery + responses: + "201": + description: Plugin verified, persisted, and now serving. + content: + application/json: + schema: + type: object + properties: + sector: + type: string + example: battery + abiVersion: + type: string + example: "1.1" + "400": + description: Malformed multipart body (missing `wasm`/`sig`, or the sector could not be determined). + "401": + $ref: "#/components/responses/Unauthorized" + "403": + description: A non-admin credential attempted to install a plugin. + "422": + description: The artifact was rejected — bad signature, incompatible ABI, or a non-instantiable/incompatible module. + "501": + description: This node has no plugin host configured; runtime install is unavailable. + # ---- Webhooks (odal-vault) ----------------------------------------------- /vault/api/v1/webhooks: @@ -1823,7 +2642,7 @@ paths: type: array items: { type: string } active: { type: boolean } - description: { type: string, nullable: true } + description: { type: [string, "null"] } createdAt: { type: string, format: date-time } updatedAt: { type: string, format: date-time } "401": @@ -1874,7 +2693,7 @@ paths: type: array items: { type: string } active: { type: boolean } - description: { type: string, nullable: true } + description: { type: [string, "null"] } createdAt: { type: string, format: date-time } updatedAt: { type: string, format: date-time } secret: @@ -1967,7 +2786,7 @@ paths: "401": $ref: "#/components/responses/Unauthorized" - # ---- Registry Identity: Facilities (odal-vault) -------------------------- + # ---- Facilities (odal-vault) ---------------------------------------------- /vault/api/v1/facilities: get: @@ -1976,7 +2795,7 @@ paths: description: | Lists the operator's facilities (ESPR Annex III). The `isDefault` facility is stamped onto new passports. Requires an admin-scoped key. - tags: [Registry Identity] + tags: [Facilities] security: - BearerApiKey: [] - BasicAuth: [] @@ -1999,7 +2818,7 @@ paths: description: | Add a facility. The identifier is validated by scheme — a `gln` must pass the GS1 mod-10 check digit. Requires an admin-scoped key. - tags: [Registry Identity] + tags: [Facilities] security: - BearerApiKey: [] - BasicAuth: [] @@ -2027,7 +2846,11 @@ paths: delete: operationId: removeFacility summary: Remove a facility - tags: [Registry Identity] + description: >- + Retires the facility (soft-delete): the row is kept as Annex III + provenance for passports that already stamped its identifier — never + hard-deleted. Requires an admin-scoped key. + tags: [Facilities] security: - BearerApiKey: [] - BasicAuth: [] @@ -2048,12 +2871,46 @@ paths: "404": $ref: "#/components/responses/NotFound" + /vault/api/v1/facilities/{id}/audit: + get: + operationId: getFacilityAudit + summary: Facility audit trail + description: | + Append-only mutation history for one facility (added, retired, + set-default), oldest first. Requires an admin-scoped key. + tags: [Facilities] + security: + - BearerApiKey: [] + - BasicAuth: [] + parameters: + - name: id + in: path + required: true + schema: + type: string + format: uuid + responses: + "200": + description: List of audit entries for this facility. + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/RegistryIdentityAudit" + "401": + $ref: "#/components/responses/Unauthorized" + "403": + $ref: "#/components/responses/Forbidden" + /vault/api/v1/facilities/{id}/default: post: operationId: setDefaultFacility summary: Set the default facility - description: Makes this facility the sole default, stamped onto new passports. - tags: [Registry Identity] + description: >- + Makes this facility the sole default, stamped onto new passports. + Requires an admin-scoped key. + tags: [Facilities] security: - BearerApiKey: [] - BasicAuth: [] @@ -2074,7 +2931,7 @@ paths: "404": $ref: "#/components/responses/NotFound" - # ---- Registry Identity: Operator Identifiers (odal-vault) ---------------- + # ---- Operator Identifiers (odal-vault) ------------------------------------- /vault/api/v1/operator-identifiers: get: @@ -2083,7 +2940,7 @@ paths: description: | Lists the operator's economic-operator identifiers (ESPR Art. 13). The `isPrimary` identifier is stamped onto new passports. Admin scope required. - tags: [Registry Identity] + tags: [Operator Identifiers] security: - BearerApiKey: [] - BasicAuth: [] @@ -2107,7 +2964,7 @@ paths: Add an economic-operator identifier. Validated by scheme — LEI uses ISO 7064 MOD 97-10; DUNS is 9 digits; EORI/VAT require a country prefix. Requires an admin-scoped key. - tags: [Registry Identity] + tags: [Operator Identifiers] security: - BearerApiKey: [] - BasicAuth: [] @@ -2135,7 +2992,11 @@ paths: delete: operationId: removeOperatorIdentifier summary: Remove an operator identifier - tags: [Registry Identity] + description: >- + Retires the identifier (soft-delete): the row is kept as Art. 13 + provenance for passports that already stamped its value — never + hard-deleted. Requires an admin-scoped key. + tags: [Operator Identifiers] security: - BearerApiKey: [] - BasicAuth: [] @@ -2156,12 +3017,46 @@ paths: "404": $ref: "#/components/responses/NotFound" + /vault/api/v1/operator-identifiers/{id}/audit: + get: + operationId: getOperatorIdentifierAudit + summary: Operator-identifier audit trail + description: | + Append-only mutation history for one operator identifier (added, + retired, set-primary), oldest first. Requires an admin-scoped key. + tags: [Operator Identifiers] + security: + - BearerApiKey: [] + - BasicAuth: [] + parameters: + - name: id + in: path + required: true + schema: + type: string + format: uuid + responses: + "200": + description: List of audit entries for this operator identifier. + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/RegistryIdentityAudit" + "401": + $ref: "#/components/responses/Unauthorized" + "403": + $ref: "#/components/responses/Forbidden" + /vault/api/v1/operator-identifiers/{id}/primary: post: operationId: setPrimaryOperatorIdentifier summary: Set the primary operator identifier - description: Makes this identifier the sole primary, stamped onto new passports. - tags: [Registry Identity] + description: >- + Makes this identifier the sole primary, stamped onto new passports. + Requires an admin-scoped key. + tags: [Operator Identifiers] security: - BearerApiKey: [] - BasicAuth: [] @@ -2282,6 +3177,92 @@ paths: "404": $ref: "#/components/responses/NotFound" + # ---- Credentialed Access (odal-vault) -------------------------------------- + # Deliberately outside both `/public` (a public URL whose body varies by + # caller breaks caching and the meaning of `publicJwsSignature`) and + # `/api/v1` (API keys are the operator's own machine access; a repairer or + # authority holds a credential and no key). + + /vault/credential/dpp/{dppId}: + get: + operationId: readDppByCredential + summary: Audience-scoped read of a published DPP + description: | + Reads a published passport filtered to the caller's audience. No + `X-DPP-Credential` header returns the same signed public view as + `/public/dpp/{dppId}`. A verified credential returns the passport + filtered to that audience's disclosure classes (ESPR Art. 77(2)), + carrying the proof computed over that view. Credentialed reads are + recorded to the passport's audit trail; anonymous reads are not. + + Returns the public view (not an error) when credential verification + is not configured on this node. + tags: [Credentialed Access] + parameters: + - name: dppId + in: path + required: true + schema: + $ref: "#/components/schemas/DppId" + - name: X-DPP-Credential + in: header + required: false + description: A verifiable access credential. Absent means public access. + schema: + type: string + responses: + "200": + description: Passport filtered to the resolved audience. + content: + application/json: + schema: + $ref: "#/components/schemas/PassportResponse" + "401": + description: The presented credential failed verification. + content: + application/json: + schema: + $ref: "#/components/schemas/ApiError" + "404": + description: Not found or not published. + content: + application/json: + schema: + $ref: "#/components/schemas/ApiError" + "410": + description: This passport has been suspended. + content: + application/json: + schema: + $ref: "#/components/schemas/ApiError" + + # ---- Internal (mTLS service-to-service, odal-vault) ------------------------ + + /vault/internal/scan-batch: + post: + operationId: ingestScanBatch + summary: Flush a scan-telemetry batch (internal, mTLS) + description: | + The mTLS-gated sink the public resolver flushes its in-memory + aggregate scan/QR-render counters to (`CN=odal-resolver` only). The + resolver holds no operator API key and no database of its own. + tags: [Vault (internal)] + security: + - MutualTLS: [] + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/ScanBatch" + responses: + "204": + description: Batch ingested. + "401": + $ref: "#/components/responses/Unauthorized" + "403": + $ref: "#/components/responses/Forbidden" + /vault/health: get: operationId: vaultHealth @@ -2309,6 +3290,10 @@ paths: responses: "200": description: Service info. + content: + application/json: + schema: + $ref: "#/components/schemas/VaultInfo" # ---- Identity — public (odal-identity) ----------------------------------- # On the fused node these are served under /identity/*. The standalone @@ -2384,6 +3369,40 @@ paths: "422": $ref: "#/components/responses/ValidationError" + /internal/verify: + servers: + - url: http://localhost:8002 + description: "odal-identity standalone (mTLS internal)" + post: + operationId: internalVerify + summary: Verify a JWS this service issued (internal, mTLS) + description: | + Checks a compact JWS against the named operator's key *and* confirms + it was signed over the given payload — a validly-signed JWS for + different content does not pass. Never errors on a signature that + simply fails to verify; that is `{ "valid": false }`, not a fault. + Service-to-service only — gated by mTLS (`CN=odal-vault`). + tags: [Identity (internal)] + security: + - MutualTLS: [] + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/VerifyRequest" + responses: + "200": + description: >- + Verification result. Always `200` — an unverifiable signature is + `{ "valid": false }`, not an error status. + content: + application/json: + schema: + $ref: "#/components/schemas/VerifyResponse" + "422": + $ref: "#/components/responses/ValidationError" + /internal/keys/rotate: servers: - url: http://localhost:8002 @@ -2561,10 +3580,23 @@ paths: Resolve a published Digital Product Passport by ID. This endpoint is the target of QR code scans. No authentication required. + Every representation is built from the **signed** public payload, not + the live database row, so the body and the proof it carries agree by + construction. + Content negotiation via `Accept` header: - - `application/json` (default): returns JSON passport data - - `text/html`: returns the consumer-facing HTML passport page - with operator branding (logo, colours) + - `application/json` / `application/ld+json` (default): JSON-LD + passport data + - `text/html`: the consumer-facing HTML passport page with operator + branding (logo, colours) + - `application/aas+json`: an IDTA Asset Administration Shell + Environment (see below) + + An absent, empty, `*/*`, `application/*`, `application/json` or + `application/ld+json` header all reach the JSON-LD default. Only a + header naming something this route cannot produce gets `406`. + + Responses carry `Vary: Accept`. tags: [Public Resolver] parameters: - name: dppId @@ -2583,8 +3615,65 @@ paths: schema: type: string description: Consumer-facing HTML passport page. + application/aas+json: + schema: + type: object + description: | + An IDTA Asset Administration Shell `Environment` — shells + and submodels in one self-contained document. + + `conceptDescriptions` is **absent**, not empty. This node + coins no concept descriptions, and the metamodel constrains + that member to `minItems: 1`, so an empty array would make + the whole document invalid. + + **Public tier only.** The passport is filtered through the + disclosure seam before any AAS mapper sees it, so this door + never carries a field the JSON-LD door would withhold. + Restricted and conformity-tier data require a credentialed + channel and a different projection. + + **Schema-valid, not conformance-certified.** Every + Environment is validated in `dpp-core`'s CI against IDTA's + published AAS JSON Schemas for metamodel **3.0, 3.1 and + 3.2**, and must satisfy all three — no single revision is + the strictest, so the intersection is the only target that + means "loadable whichever revision your toolchain + implements". + + That establishes metamodel validity only: it is not a claim + of IDTA conformance, and it asserts nothing about whether a + submodel matches a published submodel template. Note also + that no AAS JSON Schema sets `additionalProperties`, so + schema validity alone cannot rule out a member the metamodel + does not define; `dpp-core` gates that separately. + + **Unsigned, and it says so in a header.** This is a derived + representation of the signed canonical public view, which is + what `application/ld+json` returns for this same URL. The + public proof covers that payload, not this serialisation of + it, so attaching the signature here would hand a verifier a + proof that fails against the bytes it arrived with. + + Every `200` therefore carries: + + ``` + Link: <{resolverBase}/dpp/{dppId}>; rel="alternate"; type="application/ld+json" + ``` + + `alternate` rather than `canonical`: the two representations + share one URL and are separated only by `Accept`, so a + `canonical` relation would point this resource at itself. + Follow the link with that `Accept` to obtain the signed + payload and its proof. + + `resolverBase` is per-deployment (`RESOLVER_BASE_URL`, + default `https://id.odal-node.io`). Error responses carry no + `Link` — an error is not a representation of the passport. "404": $ref: "#/components/responses/NotFound" + "406": + $ref: "#/components/responses/NotAcceptable" /dpp/{dppId}/qr: get: @@ -2650,6 +3739,137 @@ paths: "404": description: No published DPP for this GTIN, or unknown link type. + /01/{gtin}/21/{serial}: + get: + operationId: resolveByGtinSerial + summary: GS1 Digital Link resolver — GTIN + serial + description: | + A printed carrier may encode more than the GTIN — this node's own + publisher emits `/01/{gtin}/21/{serial}` for a serialised trade item. + **Resolution is keyed on the GTIN alone**: `serial` is accepted so no + conformant carrier 404s for carrying more precision than the resolver + indexes, but it is not looked up. Same behaviour as `/01/{gtin}` + otherwise (`linkType` / `Accept` negotiation). + tags: [Public Resolver] + parameters: + - name: gtin + in: path + required: true + schema: + type: string + example: "09506000134352" + - name: serial + in: path + required: true + schema: + type: string + description: Accepted and ignored — not a resolution key. + - name: linkType + in: query + required: false + schema: + type: string + description: "e.g. linkset, gs1:pip, gs1:dpp" + responses: + "200": + description: RFC 9264 linkset (when a linkset is requested). + content: + application/linkset+json: + schema: + type: object + "307": + description: Redirect to the DPP page (`Location` header). + "404": + description: No published DPP for this GTIN, or unknown link type. + + /01/{gtin}/10/{batch}: + get: + operationId: resolveByGtinBatch + summary: GS1 Digital Link resolver — GTIN + batch/lot + description: | + Accepts the batch/lot segment (AI 10) a carrier may include. + **Resolution is keyed on the GTIN alone**: `batch` is accepted and + ignored, not looked up. Same behaviour as `/01/{gtin}` otherwise + (`linkType` / `Accept` negotiation). + tags: [Public Resolver] + parameters: + - name: gtin + in: path + required: true + schema: + type: string + example: "09506000134352" + - name: batch + in: path + required: true + schema: + type: string + description: Accepted and ignored — not a resolution key. + - name: linkType + in: query + required: false + schema: + type: string + description: "e.g. linkset, gs1:pip, gs1:dpp" + responses: + "200": + description: RFC 9264 linkset (when a linkset is requested). + content: + application/linkset+json: + schema: + type: object + "307": + description: Redirect to the DPP page (`Location` header). + "404": + description: No published DPP for this GTIN, or unknown link type. + + /01/{gtin}/10/{batch}/21/{serial}: + get: + operationId: resolveByGtinBatchSerial + summary: GS1 Digital Link resolver — GTIN + batch/lot + serial + description: | + The full shape this node's own carrier emits for a batched, + serialised trade item. **Resolution is keyed on the GTIN alone**: + `batch` and `serial` are accepted and ignored. Same behaviour as + `/01/{gtin}` otherwise (`linkType` / `Accept` negotiation). + tags: [Public Resolver] + parameters: + - name: gtin + in: path + required: true + schema: + type: string + example: "09506000134352" + - name: batch + in: path + required: true + schema: + type: string + description: Accepted and ignored — not a resolution key. + - name: serial + in: path + required: true + schema: + type: string + description: Accepted and ignored — not a resolution key. + - name: linkType + in: query + required: false + schema: + type: string + description: "e.g. linkset, gs1:pip, gs1:dpp" + responses: + "200": + description: RFC 9264 linkset (when a linkset is requested). + content: + application/linkset+json: + schema: + type: object + "307": + description: Redirect to the DPP page (`Location` header). + "404": + description: No published DPP for this GTIN, or unknown link type. + /health: get: operationId: resolverHealth @@ -2676,14 +3896,22 @@ tags: description: Create, read, update, list, and audit Digital Product Passports. - name: DPP Lifecycle description: Lifecycle transitions — publish, suspend, archive. + - name: Scan Telemetry + description: Aggregate, privacy-safe resolution counts — per-passport and operator-wide rollups. - name: Evidence Dossiers description: Signed, self-contained evidence dossiers — generate, fetch, and verify (stored or uploaded) offline, with zero trust in the issuing node. - name: Operator description: Operator configuration (branding, legal info, retention policy). - name: API Keys description: API key management — create, list, revoke. - - name: Registry Identity - description: Facilities (ESPR Annex III) and operator identifiers (Art. 13) stamped onto new passports. + - name: Plugins + description: Signed sector-plugin hot-install — verify, persist, hot-swap (admin-only). + - name: Webhooks + description: Signed outbound event delivery — subscribe, list, remove, test. + - name: Facilities + description: Manufacturing/processing facilities (ESPR Annex III) stamped onto new passports. + - name: Operator Identifiers + description: Economic-operator identifiers (ESPR Art. 13) stamped onto new passports. - name: Node description: Node setup/readiness state. - name: Identity @@ -2694,6 +3922,12 @@ tags: description: CSV/XLSX bulk import — templates, upload, async job polling. - name: Public (Vault) description: Unauthenticated vault endpoints for inter-service communication. + - name: Credentialed Access + description: >- + Audience-scoped passport reads authenticated by a verifiable credential + rather than an API key — repairers, market-surveillance authorities. + - name: Vault (internal) + description: mTLS service-to-service telemetry ingestion (resolver → vault only). - name: Public Resolver description: Unauthenticated public endpoints for QR scan resolution. - name: Health diff --git a/site/dpp-docs/public/robots.txt b/site/dpp-docs/public/robots.txt new file mode 100644 index 0000000..9c804b6 --- /dev/null +++ b/site/dpp-docs/public/robots.txt @@ -0,0 +1,22 @@ +# robots.txt — docs.odal-node.io + +User-agent: * +Allow: / + +Sitemap: https://docs.odal-node.io/sitemap-index.xml + +# ---- Content signals (EU Directive 2019/790, Art. 4) ---- +# As a condition of accessing this website, you agree to abide by the following +# content signals: +# +# (a) If a content-signal = yes, you may collect content for the corresponding +# use. +# (b) If a content-signal = no, you may not collect content for the +# corresponding use. +# (c) If the website operator does not include a content signal for a +# corresponding use, the website operator neither grants nor restricts +# permission via content signal with respect to the corresponding use. +# +# search: yes +# ai-input: no +# ai-train: no diff --git a/site/dpp-docs/scripts/sync-openapi.mjs b/site/dpp-docs/scripts/sync-openapi.mjs index 2d69611..147a22d 100644 --- a/site/dpp-docs/scripts/sync-openapi.mjs +++ b/site/dpp-docs/scripts/sync-openapi.mjs @@ -1,18 +1,98 @@ -// Copy the canonical OpenAPI spec from the sibling dpp-engine repo into public/. +// Vendor the canonical OpenAPI spec from the sibling dpp-engine repo into public/. // -// The copy at public/openapi.yaml is **vendored** (committed) so CI builds work -// without dpp-engine checked out alongside. Run this locally (or from a bot job -// that has both repos) whenever the spec changes: pnpm run sync:openapi -import { copyFileSync, existsSync } from 'node:fs'; +// The copy at public/openapi.yaml is committed so a build works without +// dpp-engine checked out alongside. That convenience is also the hazard: a +// vendored file drifts silently, and the drift is published. +// +// pnpm run sync:openapi copy engine -> public/, and record what it came from +// pnpm run check:openapi compare only, fail on drift or on an absent source +// +// Both modes exit non-zero when the source cannot be read. A sync that did not +// sync is a failure, not a notice — an earlier version of this script warned +// and exited 0 in that case, so it could not report a problem through any path. +// +// WHY THIS COMPARES AGAINST A PINNED COMMIT, NOT AGAINST THE ENGINE'S MAIN +// +// Comparing against whatever is currently on the engine's main branch makes +// this repository's CI depend on another repository's moving state. Two things +// go wrong. An unrelated merge in the engine turns pull requests red here, for +// reasons that have nothing to do with the change under review. And a +// correction that must land in both repositories deadlocks: the web side cannot +// go green until the engine side merges, so neither can be reviewed on a green +// build. +// +// openapi-source.json records the exact commit the vendored copy came from. +// The check reads the spec at that commit, so it is deterministic and +// self-contained. Bumping the pin is then a deliberate, reviewable line in a +// diff — which is also what makes the vendored copy's provenance auditable. +import { copyFileSync, existsSync, readFileSync, writeFileSync } from 'node:fs'; +import { execFileSync } from 'node:child_process'; import { fileURLToPath } from 'node:url'; const root = fileURLToPath(new URL('..', import.meta.url)); -const src = fileURLToPath(new URL('../../../../dpp-engine/api/openapi.yaml', import.meta.url)); +const engineDir = + process.env.DPP_ENGINE_DIR ?? fileURLToPath(new URL('../../../../dpp-engine', import.meta.url)); +const pinPath = `${root}openapi-source.json`; const dest = `${root}public/openapi.yaml`; -if (existsSync(src)) { - copyFileSync(src, dest); - console.log(`synced ${src} -> ${dest}`); -} else { - console.warn(`skip: ${src} not found (dpp-engine not checked out) — using vendored ${dest}`); +const pin = JSON.parse(readFileSync(pinPath, 'utf8')); +const src = `${engineDir}/${pin.path}`; + +const checkOnly = process.argv.includes('--check'); + +// Git normalises to LF on commit, but a Windows working copy is CRLF. Compare +// content, not line terminators, or the check fails on every developer machine. +const normalise = (text) => text.replace(/\r\n/g, '\n'); + +const fail = (message) => { + console.error(`openapi ${checkOnly ? 'check' : 'sync'}: ${message}`); + process.exit(1); +}; + +const git = (...args) => + execFileSync('git', ['-C', engineDir, ...args], { encoding: 'utf8', maxBuffer: 32 * 1024 * 1024 }); + +if (!existsSync(engineDir)) { + fail( + `cannot find the engine repository at ${engineDir}\n` + + ' Check out dpp-engine beside this repo, or set DPP_ENGINE_DIR to its location.', + ); } + +if (checkOnly) { + if (!existsSync(dest)) fail(`vendored copy missing at ${dest}`); + + let pinned; + try { + pinned = git('show', `${pin.commit}:${pin.path}`); + } catch { + fail( + `cannot read ${pin.path} at commit ${pin.commit}\n` + + ` That commit is not present in ${engineDir}. Fetch it, or correct the\n` + + ` "commit" field in openapi-source.json.`, + ); + } + + if (normalise(pinned) === normalise(readFileSync(dest, 'utf8'))) { + console.log(`openapi check: vendored copy matches ${pin.repository}@${pin.commit.slice(0, 9)}.`); + process.exit(0); + } + + fail( + `the vendored copy does not match ${pin.repository}@${pin.commit.slice(0, 9)}.\n` + + ' Either it was edited by hand — it must not be, it is a copy — or the pin\n' + + ' is wrong. Run `pnpm run sync:openapi` and commit both files together.', + ); +} + +if (!existsSync(src)) fail(`cannot read ${src}`); + +copyFileSync(src, dest); + +// Record what was copied. Without this the vendored file has no provenance and +// the check above has nothing to verify against. +const head = git('rev-parse', 'HEAD').trim(); +writeFileSync(pinPath, `${JSON.stringify({ ...pin, commit: head }, null, 2)}\n`); + +console.log(`openapi sync: ${src} -> ${dest}`); +console.log(`openapi sync: pinned to ${pin.repository}@${head.slice(0, 9)}`); diff --git a/site/dpp-docs/src/content.config.ts b/site/dpp-docs/src/content.config.ts index c57c7b8..2406c2a 100644 --- a/site/dpp-docs/src/content.config.ts +++ b/site/dpp-docs/src/content.config.ts @@ -1,4 +1,5 @@ -import { defineCollection, z } from 'astro:content'; +import { defineCollection } from 'astro:content'; +import { z } from 'astro/zod'; import { docsLoader } from '@astrojs/starlight/loaders'; import { docsSchema } from '@astrojs/starlight/schema'; import { ALPHA_BANNER_TEXT } from './site-meta'; diff --git a/site/dpp-docs/src/content/docs/core-concepts.mdx b/site/dpp-docs/src/content/docs/core-concepts.mdx index 8cb765c..a685422 100644 --- a/site/dpp-docs/src/content/docs/core-concepts.mdx +++ b/site/dpp-docs/src/content/docs/core-concepts.mdx @@ -9,9 +9,9 @@ Three ideas explain almost everything about how Odal Node is built and why. Unde ## Proof-bound: the data stays yours -The most important decision in the project is that the operator's raw source files never enter Odal's systems. The manufacturer validates a passport locally, signs it with their own key, and the node discards the import files it was built from. What the node keeps and serves is the signed passport itself — the product data the operator chose to publish, every field bound to a proof and gated by access tier — not the raw exports, spreadsheets, or supply-chain detail behind it. +The most important decision in the project is that the operator's raw source files never enter Odal's systems. The manufacturer validates a passport locally, signs it with their own key, and the node discards the import files it was built from. What the node keeps and serves is the signed passport itself — the product data the operator chose to publish, every field bound to a proof and filtered by the reader's access rights — not the raw exports, spreadsheets, or supply-chain detail behind it. -This is a property of the software, not a promise about our conduct. Because the node discards the source files after signing, the raw inputs behind a passport are gone — there is nothing for an operator, or for us, to hand over later. Verification needs only the signed passport and the manufacturer's public identity; it does not need Odal to be online, or to exist. +This is a property of the software, not a promise about our conduct. Because the node discards the source files after signing, the uploaded file itself is gone. What survives is what the import produced: the passport, and a job record of what validated and what did not. Verification needs only the signed passport and the manufacturer's public identity; it does not need Odal to be online, or to exist. A fuller treatment of the data boundary is in [What Odal Node can and cannot see](/getting-started/what-odal-can-and-cannot-see). @@ -21,7 +21,7 @@ Odal Node is two parts with two licences. The **core** is the regulatory standar One rule decides which side any change belongs on: *if it changes because an EU regulation changed, it belongs in the core; if it changes because of how the system is deployed or operated, it belongs in the engine.* The dependency only ever points one way — the engine uses the core; the core knows nothing of the engine. -A fuller treatment is in [Licensing](/engine/licensing). +The licence table is on the [Introduction](/introduction). ## One seam for new regulation diff --git a/site/dpp-docs/src/content/docs/core/sectors.mdx b/site/dpp-docs/src/content/docs/core/sectors.mdx index 02ef6da..62db23b 100644 --- a/site/dpp-docs/src/content/docs/core/sectors.mdx +++ b/site/dpp-docs/src/content/docs/core/sectors.mdx @@ -3,7 +3,7 @@ title: Extending it — sectors & plugins description: New regulation arrives sector by sector and keeps moving; the core absorbs it through a single extension seam, so a new sector is a plugin, not a rewrite. --- -ESPR does not arrive all at once. It lands sector by sector — batteries, then textiles, then electronics — and each sector's rules keep changing as the delegated acts are finalised. The core is built so that movement is normal: every sector's compliance logic enters through one seam. +Product-passport obligations do not arrive all at once. They land sector by sector — batteries first, textiles next — and each sector's rules keep changing as the delegated acts are finalised. The core is built so that movement is normal: every sector's compliance logic enters through one seam. ## One seam, many sectors @@ -20,4 +20,4 @@ The seam is an architectural boundary, never a commercial one. The built-in sect ## Read next - [What the core does](/core/overview) — the standard sectors plug into. -- [Battery DPP](/regulatory/battery) · [Textile DPP](/regulatory/textile) · [Electronics DPP](/regulatory/electronics) — sectors in context. +- [Battery DPP](/regulatory/battery) · [Textile DPP](/regulatory/textile) — sectors in context. diff --git a/site/dpp-docs/src/content/docs/core/security.mdx b/site/dpp-docs/src/content/docs/core/security.mdx index f73c97e..e498d26 100644 --- a/site/dpp-docs/src/content/docs/core/security.mdx +++ b/site/dpp-docs/src/content/docs/core/security.mdx @@ -34,7 +34,7 @@ The practical guarantee is simple: the encrypted key file on its own is useless ## Sector logic is isolated by construction -The rules that decide whether a product's data is compliant change sector by sector and regulation by regulation — so they are deliberately *not* part of the trusted core. Each sector's compliance logic is compiled to WebAssembly and runs inside a sandbox with no ambient capabilities: no filesystem, no network, no way to reach the host. Product data goes in as plain memory, a validation verdict comes out, and nothing else crosses the boundary. Execution is bounded too — a fixed memory cap and a metered CPU budget — so a plugin that misbehaves is stopped, not obeyed. And a plugin is signature-verified before it is ever loaded. +The rules that decide whether a product's data is compliant change sector by sector and regulation by regulation — so they are deliberately *not* part of the trusted core. Each sector's compliance logic is compiled to WebAssembly and runs inside a sandbox with a deliberately short capability list. There is no filesystem and no network: no file can be read or written, and no socket opened. What the plugin *can* do beyond computing on the data it is given is narrow and enumerated — write a log line, read a clock pinned to one instant for the whole invocation, and draw OS randomness. Nothing in that set reaches the signing key, the database, or any other passport. Execution is bounded too — a fixed memory cap and a metered CPU budget — so a plugin that misbehaves is stopped, not obeyed. And a plugin is signature-verified before it is ever loaded. What this buys is a hard separation between *judging* data and *vouching for* it: a buggy or even hostile sector rule can fail a validation, but it can never touch the signing key, alter a passport, or forge a proof. New regulations arrive as new sandboxed plugins — the cryptographic guarantees on this page never depend on trusting them. The concrete runtime limits are described in [Operating a node securely](/engine/security). diff --git a/site/dpp-docs/src/content/docs/core/standards.mdx b/site/dpp-docs/src/content/docs/core/standards.mdx index 615e877..d9ebee5 100644 --- a/site/dpp-docs/src/content/docs/core/standards.mdx +++ b/site/dpp-docs/src/content/docs/core/standards.mdx @@ -13,7 +13,13 @@ A passport is only useful if the rest of the world can read it. The core speaks ## The European system standards, tracked clause by clause -The European DPP system standards — written by CEN/CENELEC JTC 24 — are landing in stages. Six were published in May 2026 — **EN 18216, 18219, 18220, 18221, 18222, 18223** — covering exchange, identifiers, data carriers, storage, lifecycle APIs, and interoperability. The remaining two — **EN 18239** (access rights, security, business confidentiality) and **EN 18246** (data authentication) — are at FprEN stage, expected around September 2026. Odal tracks them clause by clause in a maintained conformance matrix, and the core's identifier, carrier, API, and authentication semantics align to the published texts. One honest nuance: even a published standard is *available* but not yet *harmonised* (cited in the Official Journal) — so we build to them today and claim presumption of conformity only when that citation lands. +The European DPP system standards — written by CEN/CENELEC JTC 24, in the EN 182xx series — are landing in stages, covering exchange, identifiers, data carriers, storage, lifecycle APIs, interoperability, access rights and data authentication. + +We are **tracking** that series, not claiming conformance to it, and the distinction is deliberate. These are paid standards and we have not purchased the texts. Nobody here has read them, so there is no clause-by-clause matrix and no basis on which we could assert that our identifier, carrier, API or authentication semantics align with what they actually say. We would rather state that plainly than describe an alignment nobody could check. + +What we have built to instead are the open, published specifications those standards draw on — GS1 Digital Link, W3C Verifiable Credentials, `did:web`, the IDTA Asset Administration Shell — all of which are readable by anyone evaluating this claim. When the EN texts are purchased and read, this page will say what was found, including where it disagrees with what we built. + +One further nuance that will still apply then: even a published standard is *available* but not yet *harmonised* (cited in the Official Journal), so presumption of conformity is a claim for the day that citation lands, not before. ## Read next diff --git a/site/dpp-docs/src/content/docs/engine/architecture.mdx b/site/dpp-docs/src/content/docs/engine/architecture.mdx index 8cadd13..a39b29e 100644 --- a/site/dpp-docs/src/content/docs/engine/architecture.mdx +++ b/site/dpp-docs/src/content/docs/engine/architecture.mdx @@ -20,7 +20,7 @@ Inside the node, three surfaces handle those steps: - The **write path** — where a passport is created, validated, signed, and versioned. - **Bulk import** — for bringing many products in at once, each becoming a draft that flows into the write path. -- The **public read path** — where a published passport is served and verified, and where the [access tiers](/regulatory/access-control) are enforced on every request. +- The **public read path** — where a published passport is served and verified, and where [access rights](/regulatory/access-control) are enforced on every request. Signing happens inside the node itself — the operator's key never leaves their infrastructure. What the node keeps and what it discards is covered in [Permanence & retention](/engine/retention) and [Operating a node securely](/engine/security). @@ -34,4 +34,3 @@ A node serves a single operator. There are no shared tenants and no cross-operat - [Operating a node securely](/engine/security) — how the node protects keys, data, and access. - [Self-Hosting](/engine/self-hosted) — run a node on your own infrastructure. - [What Odal can and cannot see](/getting-started/what-odal-can-and-cannot-see) — the data boundary, precisely. -- [Licensing](/engine/licensing) — the terms the engine ships under. diff --git a/site/dpp-docs/src/content/docs/engine/_licensing.mdx b/site/dpp-docs/src/content/docs/engine/licensing.mdx similarity index 72% rename from site/dpp-docs/src/content/docs/engine/_licensing.mdx rename to site/dpp-docs/src/content/docs/engine/licensing.mdx index 2a80d0a..e114234 100644 --- a/site/dpp-docs/src/content/docs/engine/_licensing.mdx +++ b/site/dpp-docs/src/content/docs/engine/licensing.mdx @@ -34,16 +34,19 @@ Each `dpp-engine` release ships with a specific change date in its licence heade ## Dependency licences -`dpp-engine` builds on `dpp-core` (Apache-2.0) and other open-source dependencies. Each crate's `Cargo.toml` declares its full dependency tree. The key third-party licences are: - -- **Axum / Tokio** — MIT (HTTP framework and async runtime). -- **wasmtime** — Apache-2.0 (the sector-plugin sandbox). -- **ed25519-dalek** — BSD 3-Clause (signing). -- **PostgreSQL** — PostgreSQL Licence (liberal, BSD-style). -- **Redis** — BSD 3-Clause (resolver cache). -- **NATS** — Apache-2.0 (event bus). - -No dependency introduces a copyleft obligation that would propagate to `dpp-engine` users. +`dpp-engine` builds on `dpp-core` (Apache-2.0) and a tree of open-source Rust crates. + +This page deliberately does **not** reproduce a table of those licences. A restated licence +list is a claim that goes stale silently: the licence lives in the dependency's own metadata, +changes on the dependency's schedule rather than ours, and a copy here would keep asserting the +old terms long after they changed. That is not hypothetical — a previous version of this page +carried a licence for one component that had been wrong since 2024. + +The authoritative answer for any given build is the build itself. `cargo license` or +`cargo deny` over the workspace enumerates every crate and its licence from the resolved +dependency graph, and `Cargo.lock` is committed, so the result is reproducible for any tagged +release. Anyone evaluating the engine for licence compatibility should run it against the +version they intend to deploy rather than trust a list written against some earlier one. ## Read next diff --git a/site/dpp-docs/src/content/docs/engine/retention.mdx b/site/dpp-docs/src/content/docs/engine/retention.mdx index 6a874a6..cd1be88 100644 --- a/site/dpp-docs/src/content/docs/engine/retention.mdx +++ b/site/dpp-docs/src/content/docs/engine/retention.mdx @@ -3,7 +3,7 @@ title: Permanence & retention description: A published passport must stay accessible and unaltered for years. How a node guarantees permanence by construction, in line with ESPR Article 9. --- -ESPR Article 9 makes a published passport a long-lived obligation, not a transient record. Once a product is on the market, its passport has to stay accessible for the period its delegated act sets — at least ten years after end-of-life for batteries — and a backup has to exist with an independent, certified third party so the passport survives even if the operator does not. +ESPR makes a published passport a long-lived obligation, not a transient record. Once a product is on the market, Art. 9(2)(i) requires its passport to stay available for a period the delegated act sets, "at least the expected lifetime of a specific product" — ESPR itself fixes no number. And under Art. 10(4) the operator must make a **back-up copy** available through a "digital product passport service provider", so the passport survives even if the operator does not. A node is built so those guarantees hold by construction, not by good behaviour. Permanence is not a policy an operator opts into; it is a property of how the software works. @@ -31,7 +31,7 @@ The QR code on a product encodes a resolver address that has to keep working for ## The independent backup -Article 9's second requirement — a backup held by a certified third party, so a passport survives an operator's insolvency or shutdown — is the operator's obligation. The [proof-bound model](/getting-started/what-odal-can-and-cannot-see) makes it tractable: what has to be preserved is the signed passport and its history, not a sprawling production dataset. The registry-backed mechanisms that support discovery and continuity are modelled and waiting on the upstream specification — see [EU Central Registry](/regulatory/central-registry). +Article 10(4)'s back-up copy — held by a digital product passport service provider, so a passport survives an operator's insolvency or shutdown — is the operator's obligation, and Art. 11(e) requires availability to continue through insolvency, liquidation or cessation of activity. The [proof-bound model](/getting-started/what-odal-can-and-cannot-see) makes it tractable: what has to be preserved is the signed passport and its history, not a sprawling production dataset. How this meets the registry is described under [EU Central Registry](/regulatory/central-registry). ## Read next diff --git a/site/dpp-docs/src/content/docs/engine/security.mdx b/site/dpp-docs/src/content/docs/engine/security.mdx index 416b4fb..2212a2b 100644 --- a/site/dpp-docs/src/content/docs/engine/security.mdx +++ b/site/dpp-docs/src/content/docs/engine/security.mdx @@ -21,7 +21,7 @@ The public passport endpoint requires no credential at all — it only ever serv ## Least privilege in the database -The node connects to PostgreSQL with an application role that cannot change the schema and cannot delete rows. Schema migrations run under a *separate*, privileged credential that is used only at startup and never kept in the live connection pool. On top of that, the database enforces the [permanence guarantees](/engine/retention) itself — a trigger rejects edits to a locked passport, and the audit trail is append-only at the database level — so those rules hold even if application code is wrong. +The node connects to PostgreSQL with an application role that cannot change the schema, and that holds no DELETE grant anywhere except on the import-job table, where a cleanup sweep needs one. Schema migrations run under a *separate*, privileged credential that is used only at startup and never kept in the live connection pool. On top of that, the database enforces the [permanence guarantees](/engine/retention) itself — a trigger rejects edits to a locked passport, and the audit trail is append-only at the database level — so those rules hold even if application code is wrong. ## The node fails closed @@ -29,7 +29,7 @@ A node refuses to start without its secrets — database credentials, the key-st ## Sector logic is sandboxed -Each sector's compliance logic runs in a Wasm sandbox with no filesystem, no network, and no system clock or randomness, capped at 64 MiB of memory and a fixed CPU budget (fuel metering). A buggy or hostile plugin exhausts its budget and is stopped — it can never reach the signing key, the database, or the rest of the node. This is the host enforcing, with concrete limits, the sandbox model described in [Security & cryptography](/core/security). +Each sector's compliance logic runs in a Wasm sandbox with no filesystem and no network, capped at 64 MiB of memory and a fixed CPU budget (fuel metering). It is not a vacuum: the plugin can read a clock and draw randomness. The clock is **pinned** to a single instant for the whole invocation, so a determination cannot vary with the time it ran; randomness is real OS entropy, deliberately not pinned, because a fixed seed would make a plugin's hash iteration order predictable without making any determination more reproducible. A buggy or hostile plugin exhausts its budget and is stopped — it can never reach the signing key, the database, or the rest of the node. This is the host enforcing, with concrete limits, the sandbox model described in [Security & cryptography](/core/security). ## The database is the source of truth @@ -41,7 +41,7 @@ A node serves exactly one operator. Isolation is a property of the deployment ## Reporting a vulnerability -The engine implements no cryptography of its own — it delegates all of it to the core. Dependencies are scanned for known advisories on every change. Suspected vulnerabilities go to **security@odal-node.io** under coordinated disclosure, never to a public issue first. +The signing path is the core's: the engine holds no signature or key-derivation code of its own. It does hash on its own account — SHA-256 for API-key and admin-password digests, compared in constant time — because those are node-operational concerns rather than passport ones. Dependencies are scanned for known advisories on every change. Suspected vulnerabilities go to **security@odal-node.io** under coordinated disclosure, never to a public issue first. ## Read next diff --git a/site/dpp-docs/src/content/docs/engine/self-hosted.mdx b/site/dpp-docs/src/content/docs/engine/self-hosted.mdx index 654f167..3b40dde 100644 --- a/site/dpp-docs/src/content/docs/engine/self-hosted.mdx +++ b/site/dpp-docs/src/content/docs/engine/self-hosted.mdx @@ -29,4 +29,3 @@ The engine and the sector rules move as the regulation does. Updating a node bri - [How the node works](/engine/architecture) — what's running under the hood. - [The CLI](/engine/cli) — the commands you'll use day to day. -- [Licensing](/engine/licensing) — the self-host grant in full. diff --git a/site/dpp-docs/src/content/docs/getting-started/what-odal-can-and-cannot-see.mdx b/site/dpp-docs/src/content/docs/getting-started/what-odal-can-and-cannot-see.mdx index 1eca517..8476520 100644 --- a/site/dpp-docs/src/content/docs/getting-started/what-odal-can-and-cannot-see.mdx +++ b/site/dpp-docs/src/content/docs/getting-started/what-odal-can-and-cannot-see.mdx @@ -3,13 +3,13 @@ title: What Odal can and cannot see description: A precise statement of data access by deployment model — the proof-bound architecture, stated as verifiable facts. --- -The proof-bound architecture means the raw import files are read once on the operator's infrastructure — validated, used to sign the passport, then discarded. The signed passport itself, carrying the full product data across its access tiers, is what is stored and served. This page states precisely what Odal can see, cannot see, and could see but does not — by deployment model. +The proof-bound architecture means the raw import files are read once on the operator's infrastructure — validated, used to sign the passport, then discarded. The signed passport itself, carrying the full product data across its disclosure classes, is what is stored and served. This page states precisely what Odal can see, cannot see, and could see but does not — by deployment model. ## By deployment | Property | Self-hosted | Managed (Future) | |---|---|---| -| Node discards raw import files; retains the signed passport (all tiers) | Yes — architectural invariant | Yes — architectural invariant | +| Node discards raw import files; retains the whole signed passport | Yes — architectural invariant | Yes — architectural invariant | | Odal (the entity) can access stored data | No — not present in the deployment | Constrained by access controls, audit logging, and contract | | Odal can sign on the operator's behalf | No | No — the operator holds the signing keys | @@ -29,17 +29,16 @@ The signed passport published to a resolver we operate, and the metadata require ## What we could see but do not -The contents of your import files. The software reads them once, validates the data, signs the passport, and discards the input. There is no setting, configuration, or internal code path that retains the raw import after signing — it is not a choice made per customer; it is how the software works. +The contents of your import files. The software reads them once, validates the data, signs the passport, and discards the input. There is no setting, configuration, or internal code path that retains the uploaded file after signing — it is not a choice made per customer; it is how the software works. Be precise about what that covers: the *file* is not kept, while records *derived* from it during the import — the passport, and a job record carrying per-row validation findings — are. ## The mechanism 1. **Import** — product data arrives at your node (CSV, Excel, ERP export) on infrastructure you control. 2. **Validate** — locally against versioned sector schemas. Validation is a pure function — no network calls. 3. **Sign** — your Ed25519 private key, generated and held in-process, signs the validated passport into a JWS bound to your `did:web` identity. -4. **Publish** — the signed passport becomes resolvable; the raw import files are discarded. Public fields are served to anyone, restricted tiers only against a verified credential. +4. **Publish** — the signed passport becomes resolvable; the raw import files are discarded. Public fields are served to anyone; restricted fields only against a verified credential. 5. **Verify** — anyone verifies against your public DID Document. Odal is not in the verify loop. ## Read next [Core Concepts](/core-concepts) — the three governing principles, including this one. -[Licensing](/engine/licensing) — the open-core model and the self-host grant. diff --git a/site/dpp-docs/src/content/docs/regulatory/_electronics.mdx b/site/dpp-docs/src/content/docs/regulatory/_electronics.mdx new file mode 100644 index 0000000..461cb1b --- /dev/null +++ b/site/dpp-docs/src/content/docs/regulatory/_electronics.mdx @@ -0,0 +1,45 @@ +--- +title: Electronics DPP +description: Withheld pending a rewrite against the governing ecodesign and energy-labelling regulations. +--- + +{/* + UNPUBLISHED — the leading underscore excludes this file from the content + collection, so no /regulatory/electronics route is built. Do not remove it + until the page has been rewritten and reviewed. + + WHY THIS CAME DOWN (2026-08-20) + + The previous version of this page asserted an ESPR electronics delegated act + "adopted on 18 March 2026" and "in force on 1 April 2026", a two-tier rollout + with dated compliance windows, and four named priority product classes. None + of it is traceable to any instrument in the Official Journal. It also listed + foldable-display devices as first in scope, which the governing regulation + expressly excludes, and told a named class of manufacturer that they were + already subject to the obligation. + + It was withdrawn rather than corrected in place, because every sentence on the + page inherited from the act that does not exist — including its framing as + "the most commercially significant DPP mandate in the near term". + + WHAT IS ACTUALLY TRUE + + Electronics is not an ESPR sector. Its basis is ecodesign and energy + labelling: Regulation (EU) 2023/1670 Art. 1(1) and Regulation (EU) 2023/1669, + in force since 20 June 2025, covering smartphones, other mobile phones, + cordless phones and slate tablets — and only those. Laptops, monitors, + televisions, servers, routers, chargers, earphones and PCBs carry no DPP + obligation, in force or dated, under any EU instrument. + + WRITING THE REPLACEMENT + + The sector manifest in dpp-core (crates/dpp-domain/sectors/electronics.json) + is the single home for act numbers, regulatory status and applicability dates. + Source this page from it or from primary text. Do not restate a regulatory + detail from memory, and do not carry anything forward from the version this + replaced. + + Four live references pointed at this route and were repointed when it came + down — the docs sidebar, the sector list on the core sectors page, and two + sentences on the ESPR overview. Restoring the route means restoring them. +*/} diff --git a/site/dpp-docs/src/content/docs/regulatory/access-control.mdx b/site/dpp-docs/src/content/docs/regulatory/access-control.mdx index 586ec7f..9c26dde 100644 --- a/site/dpp-docs/src/content/docs/regulatory/access-control.mdx +++ b/site/dpp-docs/src/content/docs/regulatory/access-control.mdx @@ -1,32 +1,64 @@ --- -title: Access Control (Art. 10) -description: ESPR Article 10's three-tier access model — public, restricted, and private information — and how Odal enforces the boundaries. +title: Access Control +description: Who may read which parts of a Digital Product Passport — what ESPR actually says, what the Battery Regulation adds, and how Odal enforces it. --- -ESPR Article 10 establishes that a Digital Product Passport carries three categories of information with different access rules. The categories are not advisory — they are part of the regulation's substantive requirements, and an implementation that does not enforce them is not compliant. +Access to a Digital Product Passport is differentiated: not every reader sees every field. But the rules are not where they are commonly assumed to be, and getting that wrong produces confident, incorrect compliance claims. -## The three tiers +## What ESPR actually says -**Public information** is available to anyone who scans the QR code. It includes the product identity, the manufacturer, the basic compliance summary, and the information needed for a consumer to make informed sustainability decisions. +ESPR (Regulation (EU) 2024/1781) **does not itself define access tiers.** Article 11(b) requires that -**Restricted information** is available to authorised actors — recyclers, repair operators, dismantlers, customs and market-surveillance authorities. It includes the detailed material composition needed for end-of-life processing, the technical specifications needed for repair, and the compliance evidence needed for authority verification. +> customers, manufacturers, importers, distributors, dealers, professional repairers, independent operators, refurbishers, remanufacturers, recyclers, market surveillance authorities and customs authorities, civil society organisations, trade unions and other relevant actors shall have **free of charge** and easy access to the digital product passport **based on their respective access rights set out in the applicable delegated act adopted pursuant to Article 4** -**Private information** is available only to the economic operator. It includes anything that the operator legitimately considers commercial confidential — supplier identities, formulation details, manufacturing recipes — that does not need to be disclosed for the public-good purposes Article 10 protects. +Two things follow. First, ESPR names a broad **list of actors** — some fourteen classes — and assigns them nothing. Second, the actor-to-data mapping is delegated: Article 9(2)(f) requires each product-group delegated act to specify "the actors that are to have access to data in the digital product passport and to what data they are to have access". + +**No such delegated act has been adopted for any ESPR product group yet.** For textiles, furniture, steel, aluminium and tyres, the access mapping is not merely unimplemented — it does not yet legally exist. + +Article 10, sometimes cited as the source of a three-tier model, is titled "Requirements for the digital product passport" and establishes no access categories. + +## Where a specified model does exist: batteries + +The Battery Regulation (EU) 2023/1542, Article 77(2), is currently the only fully specified access model. It assigns three audiences to four Annex XIII data sets: + +| Audience | Annex XIII points | +|---|---| +| General public | 1 | +| Notified bodies, market surveillance authorities, the Commission | 2 and 3 | +| Persons with a legitimate interest | 2 and 4 | + +**This is a lattice, not a ranking.** Point 3 (conformity test reports) is authority-only; point 4 (individual-battery data — cycle counts, state of health, use history) is legitimate-interest-only. Neither audience contains the other, so no ordered "public → restricted → private" scale can express it: any such ordering necessarily either hands authorities data the regulation withholds, or hides data from someone entitled to it. + +Odal models this directly — audiences and disclosure classes as separate vocabularies, with an explicit table of which audience may see which class — rather than as a tier number. + +One detail is still pending: the delegated act under Article 77(9), which fixes the access rights for Annex XIII points 2 and 4, has not been adopted. + +## Constraints that apply everywhere + +Read from the primary texts of ESPR, the Battery Regulation, the Toy Safety Regulation (EU) 2025/2509, the Detergents Regulation (EU) 2026/405 and the Construction Products Regulation (EU) 2024/3110: + +- **Access is free of charge.** Every one of these instruments requires it. Charging a reader for passport access is not a lawful model. +- **Consumers must not be required to register or supply a password.** The toy and detergent regulations state this outright. The public view stays frictionless — no account, no sign-up, no gate. +- **Passports must remain available for years, surviving the operator.** Ten years after placing on the market under the toy, detergent and construction rules, "including in cases of insolvency, liquidation or cessation of activity"; ESPR ties the period to at least the product's expected lifetime. ## How Odal enforces the boundaries -Every field in a passport carries a tier marker, and every access request arrives with a credential — a Verifiable Credential issued by a trusted authority (a national body, a sector association) that asserts which actor category the requester belongs to. Odal evaluates the request against the passport's access policy: does the credential authorise the requested tier, is it signed by a trusted issuer, has it expired. +Every field carries a disclosure classification drawn from the sector's own definition rather than hard-coded. A request arrives with a credential — a W3C Verifiable Credential asserting the holder's role — and the node resolves that role to an audience, then filters the passport to the disclosure classes that audience may see. + +The filtering step is a **pure function**: no network, no database. The credential check verifies the signature, the expiry, the issuer's trust status and the revocation list. + +Durable artefacts — stored signatures, audit records — are keyed by the **disclosure classes** they cover, never by an audience name. That is deliberate: ESPR's eventual actor vocabulary differs from the Battery Regulation's, and anything keyed to today's audience names would need migrating when the first ESPR delegated act lands. -That evaluation is a **pure function** — no network, no database — so it runs inside the resolver at the edge, deciding each request in microseconds without reaching back into the node. The answer is simply allow or deny. +## Why credentials and not API keys -## Why credential-based access and not API keys +The natural alternative — issuing API keys to recyclers and authorities — fails on three counts. API keys are bearer secrets that get reused, leaked or sold. They carry no verifiable identity assertion: possession is proof of access, not proof of who holds it. And they bind to a single issuer's authentication system, so every regulator would have to integrate with every platform separately. -The natural alternative — handing out API keys to recyclers and authorities — fails on three properties. API keys are bearer secrets that get reused, leaked, or sold. API keys do not carry a verifiable identity assertion — possession is proof of access, but not proof of who the holder is. API keys are bound to a single issuer's authentication system, which means every regulator has to integrate with every platform separately. +Verifiable Credentials address all three. They are non-bearer, they carry a signed issuer assertion, and any platform that can verify them can accept them. -Verifiable Credentials solve all three. They are non-bearer (the holder proves possession of the credential's bound private key), they carry a verifiable identity assertion (the issuer is signed into the credential), and they integrate uniformly with any platform that knows how to verify them. +This is also where the regulation is heading. ESPR Article 11 empowers the Commission to adopt implementing acts on procedures to issue and verify "the digital credentials of economic operators and other relevant actors that have access rights", and the toy and detergent regulations both defer their credential procedures to that same provision. Those implementing acts are not yet adopted, so no conformance claim is available — but the direction is legislated rather than speculative. ## Read next [What the core does](/core/overview) — how passports are signed and verified. -[ESPR Overview](/regulatory/espr) — the framework regulation Article 10 sits inside. -[How the node works](/engine/architecture) — the public read path that enforces these access boundaries on every request. +[ESPR Overview](/regulatory/espr) — the framework regulation these provisions sit inside. +[How the node works](/engine/architecture) — the public read path. diff --git a/site/dpp-docs/src/content/docs/regulatory/battery.mdx b/site/dpp-docs/src/content/docs/regulatory/battery.mdx index a2eb2ac..d8deec5 100644 --- a/site/dpp-docs/src/content/docs/regulatory/battery.mdx +++ b/site/dpp-docs/src/content/docs/regulatory/battery.mdx @@ -3,7 +3,7 @@ title: Battery DPP description: The EU Battery Regulation (EU 2023/1542) — what a battery passport carries under Annex XIII, the supply-chain due-diligence record, and which checks are binding today. --- -The Battery Regulation (EU 2023/1542) is the first product-group regulation that mandates a Digital Product Passport in production. It applies to portable, industrial, light-means-of-transport (LMT), electric-vehicle (EV), and stationary energy storage batteries. The regulation is already in force, and the staged compliance dates have begun passing their first checkpoints. +The Battery Regulation (EU 2023/1542) is the first product-group regulation that mandates a Digital Product Passport in production. Article 1(3) applies it to five categories of battery, and only five: portable, starting-lighting-ignition (SLI), light means of transport (LMT), electric vehicle (EV) and industrial. The set is closed deliberately — where a battery could fall under more than one, the strictest requirements apply, and that tie-break only works over a closed set. Stationary energy-storage systems are not a sixth category; the Regulation places them inside industrial. The regulation is already in force, and the staged compliance dates have begun passing their first checkpoints. ## What the regulation requires @@ -23,9 +23,9 @@ A battery passport records the Annex XIII fields the regulation requires, with t The Battery Regulation is in force, but its requirements switch on in stages, and Odal validates only what is actually binding: -- **Enforced now** — the long-standing bans on mercury and cadmium (carried forward from the Batteries Directive, in force since 2008), and cross-field coherence checks such as a battery's operating-temperature range being internally consistent. -- **Declared now, binding later** — the Annex X minimum recycled-content shares for cobalt, lithium, nickel, and lead. These are finalised law but do not take effect until 2031, rising again in 2036, so today they are surfaced as advisory rather than as a pass/fail verdict. -- **Awaiting a delegated act** — minimum state-of-health thresholds (Article 10) and the A–E carbon-footprint class (Article 7), whose methodologies the Commission has not yet adopted. +- **Enforced now** — the restrictions on mercury and cadmium in Art. 6 and Annex I, and cross-field coherence checks such as a battery's operating-temperature range being internally consistent. +- **Declared now, binding later** — the minimum recycled-content shares for cobalt, lithium, nickel and lead, set by Art. 8(2) and 8(3) and cross-referenced to Annex VIII. (Annex X is the due-diligence raw-materials list, and has nothing to do with recycled content.) These are finalised law but do not take effect until 2031, rising again in 2036, so today they are surfaced as advisory rather than as a pass/fail verdict. +- **Awaiting a delegated act** — minimum electrochemical performance and durability values, empowered by Art. 10(5), and the carbon-footprint performance class under Art. 7(2). Art. 7(2) requires the label to declare a class but defers the classes themselves to an act that has not been adopted, and requires the scale to be revised every three years — so neither the labels nor how many there are is settled, and we do not guess at them. The passport itself becomes mandatory on **18 February 2027**. As each later requirement takes effect, the battery sector's checks extend to cover it, and passports already issued stay valid. The reasoning behind issuing a binding verdict only where the law binds is the [honesty model](/regulatory/espr) every sector follows. diff --git a/site/dpp-docs/src/content/docs/regulatory/central-registry.mdx b/site/dpp-docs/src/content/docs/regulatory/central-registry.mdx index 988f84b..1c43ce7 100644 --- a/site/dpp-docs/src/content/docs/regulatory/central-registry.mdx +++ b/site/dpp-docs/src/content/docs/regulatory/central-registry.mdx @@ -15,12 +15,14 @@ This design is the right one because it lets the regulation centralise discovery ## What the registry requires of implementations -The registry's API is being defined at the time of writing. The expected requirements are: every published passport must be registered with the registry, the registration carries the product identifier and the resolver endpoint, the issuing platform must keep the registration in sync as passports are suspended or archived, and the platform must respond to registry health checks. +The registry went live on **20 July 2026**, meeting the Commission's own Art. 13 deadline, and the implementing act adopted under Art. 13(5) sets out how registration works. So the requirements are no longer expectations: a published passport is registered with its unique identifier, the registration carries the resolver endpoint, and the registering platform keeps it in sync as passports are suspended or archived. -Odal has prepared for this beyond modelling the interface: **every published passport already commits its registration intent to a durable outbox, in the same database transaction as the publish itself.** A background worker drains that outbox with retry and backoff. The practical consequence: publishing never blocks on the registry, a crash never loses a registration, and the day the Commission's API goes live, the accumulated backlog registers without a single passport falling through. (For context: the Commission's own deadline to set up the registry was 19 July 2026; as of this writing, the API specification remains unpublished — so this durable-queue posture is not caution, it's the correct engineering for the actual situation.) +Odal has prepared for this beyond modelling the interface: **every published passport already commits its registration intent to a durable outbox, in the same database transaction as the publish itself.** A background worker drains that outbox with retry and backoff. The practical consequence: publishing never blocks on the registry, a crash never loses a registration, and a backlog accumulated during an outage drains without a single passport falling through. + +Two things stand between that and a battery registration succeeding today, and neither is ours to fix. The semantic catalogue a battery registration must reference is not yet defined, which blocks the registration path for everyone rather than for us specifically. And the registry caps a unique product identifier at 50 characters where ours runs to 65, with the identifier required to be a URL conforming to standards that must be purchased before that constraint can even be read properly. We would rather name both than describe a clean drain we cannot yet demonstrate. ## Why pre-investing in the bridge diff --git a/site/dpp-docs/src/content/docs/regulatory/electronics.mdx b/site/dpp-docs/src/content/docs/regulatory/electronics.mdx deleted file mode 100644 index 397fd55..0000000 --- a/site/dpp-docs/src/content/docs/regulatory/electronics.mdx +++ /dev/null @@ -1,56 +0,0 @@ ---- -title: Electronics DPP -description: The electronics delegated act under ESPR — tiered scope, data requirements, the repairability dimension, and where it stands. ---- - -import { Aside } from "@astrojs/starlight/components"; - -The electronics delegated act was adopted on 18 March 2026 and entered into force on 1 April 2026. It is the most commercially significant DPP mandate in the near term because of the sheer volume of electronics placed on the EU market and the tiered effective dates that create two distinct compliance windows. - -## Scope and timeline - -The act applies to electrical and electronic equipment (EEE) placed on the EU market, with a tiered rollout: - -| Tier | Product category | Effective date | -|---|---|---| -| **High-end** | AI servers, high-performance computing, high-end PCBs, foldable-display devices | 1 April 2026 (immediate) | -| **Low-end** | Consumer electronics — earphones, chargers, cables, small household appliances, and other general EEE | 1 January 2027 | - -The tiered structure means that high-end manufacturers are already subject to DPP requirements, while low-end manufacturers have until the end of 2026 to prepare. Both tiers share the same data categories; the difference is the deadline. - -## Data requirements - -The electronics DPP carries the following data categories: - -**Product identity** — manufacturer identification, product model, product code, place of manufacture. These are the base details every passport carries. - -**Eco-design parameters** — carbon footprint declaration (product-level, following the methodology in the act's annexes), energy efficiency class, material efficiency indicators. These feed the open compliance calculators. - -**Hazardous substances** — Substances of Very High Concern (SVHC) as defined under REACH, and CMR (carcinogenic, mutagenic, toxic to reproduction) substances. The passport must declare presence or absence, with concentration data where applicable. - -**Recycling and end-of-life** — disassembly instructions, material-recovery information, spare-parts availability (including the number of years spare parts are guaranteed to be available), and preparation for re-use indicators. The EU's 2025 repairability rules for smartphones and tablets are directly relevant here — the repairability scoring framework feeds into this data category. - -**Supply-chain traceability** — the chain from component supplier to finished product, with geographic origin information where consumer-protection regulators require it. - -## The repairability dimension - -Electronics is the first sector where repairability scoring has a mature regulatory framework feeding into DPP requirements. The EU's repairability label (applicable to smartphones, tablets, and expanding categories since June 2025) produces a score that the electronics DPP must carry, and an electronics passport carries it as a first-class field. - -This matters because repairability is not a static number — component availability changes, scoring methodologies are updated, and the delegated act's annexes may revise what constitutes a "repairable" product. Odal handles that by versioning the rules and recording which version a passport was validated against. - -## Where it stands - - - -The groundwork is in place: electronics slots into the same sector seam every other sector uses, so what remains is finalising the fields against the adopted act and validating them on a real manufacturer's data set. The open work — reading the act's annexes to fix the field mapping, settling the carbon-footprint methodology, and pinning down concrete fields like the spare-parts availability guarantee — is research against the regulation, not architectural change. - -Until those fields are pinned to the adopted act, an electronics passport is validated structurally but not yet given a binding compliance verdict — the same *not-yet-assessed* stance every sector takes where the law has not fully landed, described in the [ESPR Overview](/regulatory/espr). - -## Read next - -[Battery DPP](/regulatory/battery) — the battery-sector delegated act, the nearest hard mandate. -[Textile DPP](/regulatory/textile) — the textile-sector delegated act and the unsold-goods provision. -[ESPR Overview](/regulatory/espr) — the framework regulation. -[Access Control (Art. 10)](/regulatory/access-control) — the three-tier access model that applies across all sectors. diff --git a/site/dpp-docs/src/content/docs/regulatory/espr.mdx b/site/dpp-docs/src/content/docs/regulatory/espr.mdx index 6f24e2c..2159bcb 100644 --- a/site/dpp-docs/src/content/docs/regulatory/espr.mdx +++ b/site/dpp-docs/src/content/docs/regulatory/espr.mdx @@ -5,11 +5,11 @@ description: The Ecodesign for Sustainable Products Regulation — what it is, w import { Aside } from "@astrojs/starlight/components"; -The Ecodesign for Sustainable Products Regulation (ESPR, EU 2024/1781) is the framework regulation that establishes the Digital Product Passport requirement across the European Union. It entered into force in 2024, and the first product-group delegated acts started landing immediately. By 2027, every product placed on the EU market that falls under a delegated act needs a machine-readable DPP. +The Ecodesign for Sustainable Products Regulation (ESPR, EU 2024/1781) is the framework regulation that establishes the Digital Product Passport requirement across the European Union. It entered into force in 2024. **No delegated act under Article 4 has yet been adopted for any product group**, so ESPR itself binds nobody to a passport today — the framework is in force and the obligations that hang off it are not. The dated passport obligations that do exist come from other instruments: the Battery Regulation from 18 February 2027, and the Construction Products Regulation from 8 January 2027. ## What ESPR requires -The regulation establishes three core obligations that shape every implementation. The first is the **passport itself** — every regulated product must have a machine-readable Digital Product Passport accessible via a data carrier (typically a QR code) on the product. The second is **regulatory access control** — the passport must distinguish between public information, information available to authorised actors, and private information, with cryptographic enforcement of the boundaries. The third is **retention** — the passport must remain accessible for the lifetime of the product plus a regulator-defined retention horizon (10 to 20 years depending on sector). +The regulation establishes three core obligations that shape every implementation. The first is the **passport itself** — every regulated product must have a machine-readable Digital Product Passport accessible via a data carrier (typically a QR code) on the product. The second is **access control** — Art. 11(b) requires that a long list of actors, from customers to recyclers to market-surveillance authorities, have access **free of charge** "based on their respective access rights set out in the applicable delegated act". ESPR states no tiers and assigns no actor to any data; that mapping is delegated per product group under Art. 9(2)(f), and it prescribes no enforcement mechanism. The third is **retention** — Art. 9(2)(i) requires the passport to stay available for "at least the expected lifetime of a specific product", and sets no figure. Where a figure exists it comes from the product regulation: ten years for toys, detergents and the construction operator's own duty, and twenty-five years for the construction passport *system*. ## What ESPR does not specify @@ -21,18 +21,18 @@ The implications for the implementation are that the platform has to track sever Odal covers the articles of ESPR that are technically substantive for a passport implementation: -**Articles 8 and 9** — the format of the passport and its data carrier: the passport itself is the format, and GS1 Digital Link is how a scan resolves to it. +**Articles 8, 9 and 12** — what a delegated act must specify about the passport, and the identifiers behind it. Art. 9(2) fixes the act's content, including who may read and write which data and how long the passport stays available; **Art. 12 and Annex III** define the unique product, operator and facility identifiers. GS1 Digital Link is how a scan resolves to the passport. -**Article 10** — the three-tier access control. Odal carries the tier boundaries and the resolver enforces them on every request. +**Articles 10 and 11** — the passport's requirements and its technical design. Art. 11(b) is the access provision: readers get access "based on their respective access rights set out in the applicable delegated act", **free of charge**, with the actor-to-data mapping delegated to each product group under Art. 9(2)(f). ESPR fixes no access tiers of its own, and no product-group act has been adopted yet. **Transfer of responsibility** — when a product changes economic operator along the supply chain, Odal implements a dual-signature chain: both transferor and transferee sign, creating a verifiable chain of custody. One honest note: ESPR has **no single article** establishing transfer mechanics — the closest operative text is Art. 11(e) (passport continuity when an operator ceases activity). Our handshake is an engineering choice that satisfies and exceeds that continuity duty; we say so rather than inventing a citation. -**Article 13** — the EU Central Registry: the Commission-run directory of registered passports (its API specification is still unpublished). Public access to the passport itself is served by the resolver — the public view, cached to stay fast under load. +**Article 13** — the EU Central Registry: the Commission-run directory of registered passports, which went live on 20 July 2026. Public access to the passport itself is served by the resolver — the public view, cached to stay fast under load. **Articles 24 & 25** — the disclosure duty for discarded unsold consumer products (Art. 24) and the destruction ban for unsold textiles and footwear (Art. 25, Annex VII — in force for large companies since 19 July 2026). Odal carries a dedicated unsold-goods passport variant that records the disposal pathway as a verifiable record. ## Which sectors are covered @@ -42,24 +42,26 @@ ESPR is a framework; the binding detail arrives through per-product-group delega | Sector | Regulatory basis | Status today | |---|---|---| | **Battery** | EU 2023/1542 | In force — passport mandatory 18 Feb 2027; substance limits enforced now | -| **Textile** | ESPR delegated act (in drafting) | Structural validation now; unsold-goods ban (Art. 22) applies from 2026 | -| **Electronics** | ESPR delegated act (adopted Mar 2026) | Phasing in — high-end from Apr 2026, the rest from Jan 2027 | +| **Textile** | ESPR delegated act (in drafting) | Structural validation now; unsold-goods ban (Art. 25) applies from 2026 | +| **Electronics** | EU 2023/1670 + 2023/1669 — ecodesign and energy labelling, not ESPR | In force since 20 Jun 2025 for smartphones, other mobile phones, cordless phones and tablets | | **Steel · Aluminium** | CBAM 2023/956 / ESPR | Reference benchmarks only; no DPP mandate yet (aluminium expected ~2030) | -| **Construction** | CPR 2024/3110 | Awaiting delegated acts (2028–2032) | -| **Toys** | EU 2025/2509 | Awaiting delegated act (~2030) | -| **Furniture · Detergent · Tyre** | ESPR | Seams in place; awaiting delegated acts | +| **Construction** | CPR 2024/3110 | In force; passport applies from 8 Jan 2027. Operator owes 10 years of availability, the passport *system* 25 | +| **Toys** | EU 2025/2509 | In force; passport applies from 1 Aug 2030 — a date set by the Toy Safety Regulation itself, not by an awaited act | +| **Detergent** | EU 2026/405 | In force; passport applies from 23 Sep 2029 | +| **Tyre** | Tyre labelling 2020/740 / ESPR | Labelling in force; no DPP mandate yet | +| **Furniture** | ESPR | Seam in place; awaiting a delegated act | -Battery, textile, and electronics have dedicated pages below; the rest run on the same mechanism, waiting on their regulation. +Battery and textile have dedicated pages below; the rest run on the same mechanism, waiting on their regulation. ## What a compliance result actually claims Within any sector, some obligations are in force while others wait on a delegated act, and Odal is deliberate about not overstating what it can certify. Every passport is checked for structural and cross-field validity — that runs for every sector, in force or not. But a *binding* determination — a verdict of compliant or non-compliant — is issued only where the underlying obligation actually binds. Where it does not, the result is recorded as **not assessed** rather than guessed, and the passport carries that honestly. -As each delegated act takes effect, the matching sector's determination switches on, with no change to passports already issued. This is why a battery's banned-substance limits are enforced today while its 2031 recycled-content minima are surfaced only as advisory until they bind. The system would rather say *not yet assessed* than assert a compliance claim the law does not yet support. +When a delegated act takes effect, the matching sector's determination is switched on by a maintainer — the gate reads a status recorded in the sector's manifest, so it changes when a new version of the software is released, not on the date the law changes. Passports already issued are unaffected either way. This is why a battery's banned-substance limits are enforced today while its 2031 recycled-content minima are surfaced only as advisory until they bind. The system would rather say *not yet assessed* than assert a compliance claim the law does not yet support. ## Read next -[Battery DPP](/regulatory/battery) — the battery-sector delegated act in detail. -[Textile DPP](/regulatory/textile) — the textile-sector delegated act in detail. -[Access Control (Art. 10)](/regulatory/access-control) — the three-tier model in detail. +[Battery DPP](/regulatory/battery) — Regulation (EU) 2023/1542 in detail. +[Textile DPP](/regulatory/textile) — the textile act still in drafting, and the unsold-goods ban that is already in force. +[Access Control](/regulatory/access-control) — who may read what, and where the rules actually live. [EU Central Registry](/regulatory/central-registry) — what the central registry is and what it is not. diff --git a/site/dpp-docs/src/pages/api.astro b/site/dpp-docs/src/pages/api.astro index b676eea..d9b2d64 100644 --- a/site/dpp-docs/src/pages/api.astro +++ b/site/dpp-docs/src/pages/api.astro @@ -19,8 +19,10 @@ import logoDark from '../assets/logo-dark.svg'; // Same alpha banner Starlight renders on every docs page (content.config.ts // schema default) and the same shared CSS (banner.css) — this page skips the -// Starlight layout entirely so neither comes for free here. +// Starlight layout entirely so neither comes for free here. The text is +// imported from site-meta so the two banners cannot drift apart. import '../styles/banner.css'; +import { ALPHA_BANNER_TEXT } from '../site-meta'; // Full-page OpenAPI reference, mounted client-side from the **bundled** // @scalar/api-reference — no CDN, no third-party runtime dependency, so the @@ -79,9 +81,11 @@ import '../styles/banner.css';
- + +
{ALPHA_BANNER_TEXT}