From a3436bfcd4e50d45e1dd3790dbb1248dcc261522 Mon Sep 17 00:00:00 2001 From: Charles Gibson <46542971+perasperaactual@users.noreply.github.com> Date: Fri, 10 Apr 2026 11:26:25 -0400 Subject: [PATCH 01/27] fix(deploy): enable static export for R2 bucket hosting (#332) Co-authored-by: Stackwright Bot --- .github/workflows/deploy-docs.yml | 13 +------------ examples/stackwright-docs/next.config.js | 6 ++++++ 2 files changed, 7 insertions(+), 12 deletions(-) diff --git a/.github/workflows/deploy-docs.yml b/.github/workflows/deploy-docs.yml index 6d677c1f..b4b38fcc 100644 --- a/.github/workflows/deploy-docs.yml +++ b/.github/workflows/deploy-docs.yml @@ -117,20 +117,9 @@ jobs: run: | rclone sync \ --fast-list \ - --exclude ".next/BUILD_ID" \ - --exclude ".next/cache/**" \ --exclude "node_modules/**" \ --progress \ - examples/stackwright-docs/.next/ \ - "R2:${{ steps.bucket.outputs.bucket }}/" - - - name: Sync public assets to R2 - run: | - rclone copy \ - --fast-list \ - --exclude "node_modules/**" \ - --progress \ - examples/stackwright-docs/public/ \ + examples/stackwright-docs/out/ \ "R2:${{ steps.bucket.outputs.bucket }}/" - name: Deployment summary diff --git a/examples/stackwright-docs/next.config.js b/examples/stackwright-docs/next.config.js index 6bc13d83..0e5f56da 100644 --- a/examples/stackwright-docs/next.config.js +++ b/examples/stackwright-docs/next.config.js @@ -7,4 +7,10 @@ module.exports = createStackwrightNextConfig({ "@stackwright/themes", "@stackwright/types", ], + // Enable static export for R2/CDN hosting + output: 'export', + // Images must be unoptimized for static export + images: { + unoptimized: true, + }, }); From 6353011aa5738af98f8b3d158671e3d390792640 Mon Sep 17 00:00:00 2001 From: Charles Gibson <46542971+perasperaactual@users.noreply.github.com> Date: Fri, 10 Apr 2026 11:35:50 -0400 Subject: [PATCH 02/27] fix(deploy): add trailing slash support for R2 static hosting (#334) Co-authored-by: Stackwright Bot --- examples/stackwright-docs/next.config.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/examples/stackwright-docs/next.config.js b/examples/stackwright-docs/next.config.js index 0e5f56da..887e1ffc 100644 --- a/examples/stackwright-docs/next.config.js +++ b/examples/stackwright-docs/next.config.js @@ -13,4 +13,6 @@ module.exports = createStackwrightNextConfig({ images: { unoptimized: true, }, + // Add trailing slash so URLs work without requiring explicit trailing slashes on R2/CDN hosting + trailingSlash: true, }); From cb7679e57c0d3769c9d34bb7b8729b933e833e16 Mon Sep 17 00:00:00 2001 From: Charles Gibson <46542971+perasperaactual@users.noreply.github.com> Date: Fri, 10 Apr 2026 12:05:29 -0400 Subject: [PATCH 03/27] fix(deploy): migrate from R2 to Cloudflare Pages (#336) Co-authored-by: Stackwright Bot --- .github/workflows/deploy-docs.yml | 74 +++++++++---------------------- 1 file changed, 21 insertions(+), 53 deletions(-) diff --git a/.github/workflows/deploy-docs.yml b/.github/workflows/deploy-docs.yml index b4b38fcc..1834463a 100644 --- a/.github/workflows/deploy-docs.yml +++ b/.github/workflows/deploy-docs.yml @@ -1,21 +1,13 @@ # Deploy Docs Workflow # -# This workflow replaces the old pattern of: -# - Vercel deployments for preview/production -# - Separate repo syncing to R2 -# -# Now we build once and sync directly to R2 buckets with Cloudflare CDN serving. -# R2 uses Cloudflare API token auth — no AWS credentials needed. +# This workflow builds the documentation and deploys to Cloudflare Pages. # The dev branch deploys to stackwright-docs-dev for preview/testing. # The main branch deploys to stackwright-docs for production. +# Required secrets for Pages deployment: +# - CLOUDFLARE_API_TOKEN: Cloudflare API token with Pages edit permissions +# - CLOUDFLARE_ACCOUNT_ID: Cloudflare account ID -# Required secrets for R2 deployment: -# - CLOUDFLARE_API_TOKEN: Cloudflare API token with R2 read/write permissions -# - R2_ACCOUNT_ID: Cloudflare account ID -# - R2_BUCKET_STABLE: Production bucket name -# - R2_BUCKET_DEV: Development bucket name - -name: Deploy Docs to R2 +name: Deploy Docs to Pages on: push: @@ -77,50 +69,26 @@ jobs: # Deploy Steps # ============================================ - - name: Install rclone - run: | - # Download and install rclone binary - curl -O https://downloads.rclone.org/rclone-current-linux-amd64.zip - unzip rclone-current-linux-amd64.zip - sudo mv rclone-v1.73.4-linux-amd64/rclone /usr/local/bin/ - rm -rf rclone-current-linux-amd64.zip rclone-v1.73.4-linux-amd64 - rclone version - - - name: Configure rclone for R2 - run: | - # Create rclone config file with R2 remote - # R2 uses Cloudflare API token auth directly - cat > ~/.config/rclone/rclone.conf << EOF - [R2] - type = s3 - provider = Cloudflare - access_key_id = ${{ secrets.R2_ACCESS_KEY_ID }} - secret_access_key = ${{ secrets.R2_SECRET_ACCESS_KEY }} - region = auto - endpoint = https://${{ secrets.R2_ACCOUNT_ID }}.r2.cloudflarestorage.com - acl = public-read - EOF - - - name: Determine target bucket - id: bucket + - name: Determine target project + id: target run: | if [ "${{ github.ref_name }}" = "main" ]; then - echo "bucket=${{ secrets.R2_BUCKET_STABLE }}" >> $GITHUB_OUTPUT + echo "project=stackwright-docs" >> $GITHUB_OUTPUT echo "env=production" >> $GITHUB_OUTPUT else - echo "bucket=${{ secrets.R2_BUCKET_DEV }}" >> $GITHUB_OUTPUT + echo "project=stackwright-docs-dev" >> $GITHUB_OUTPUT echo "env=development" >> $GITHUB_OUTPUT fi - echo "Selected bucket: ${{ steps.bucket.outputs.bucket }}" + echo "Selected project: ${{ steps.target.outputs.project }}" - - name: Sync to R2 - run: | - rclone sync \ - --fast-list \ - --exclude "node_modules/**" \ - --progress \ - examples/stackwright-docs/out/ \ - "R2:${{ steps.bucket.outputs.bucket }}/" + - name: Deploy to Cloudflare Pages + uses: cloudflare/pages-action@v1 + with: + apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }} + accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} + projectName: ${{ steps.target.outputs.project }} + directory: examples/stackwright-docs/out + gitHubToken: ${{ secrets.GITHUB_TOKEN }} - name: Deployment summary run: | @@ -129,8 +97,8 @@ jobs: echo "| Detail | Value |" >> $GITHUB_STEP_SUMMARY echo "|--------|-------|" >> $GITHUB_STEP_SUMMARY echo "| Branch | \`${{ github.ref_name }}\` |" >> $GITHUB_STEP_SUMMARY - echo "| Environment | ${{ steps.bucket.outputs.env }} |" >> $GITHUB_STEP_SUMMARY - echo "| Bucket | \`${{ steps.bucket.outputs.bucket }}\` |" >> $GITHUB_STEP_SUMMARY + echo "| Environment | ${{ steps.target.outputs.env }} |" >> $GITHUB_STEP_SUMMARY + echo "| Project | \`${{ steps.target.outputs.project }}\` |" >> $GITHUB_STEP_SUMMARY echo "| Commit | \`${{ github.sha }}\` |" >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY - echo "**Note:** Cloudflare CDN serves directly from R2. No cache invalidation needed." + echo "**Deployed to Cloudflare Pages**" From abce1d7dc56508cc4cbfb75c0cf50177fd316474 Mon Sep 17 00:00:00 2001 From: Charles Gibson <46542971+perasperaactual@users.noreply.github.com> Date: Fri, 10 Apr 2026 12:23:25 -0400 Subject: [PATCH 04/27] Fix/cloudflare pages (#337) * fix(deploy): migrate from R2 to Cloudflare Pages * fix(deploy): use env vars for deployment variables --------- Co-authored-by: Stackwright Bot --- .github/workflows/deploy-docs.yml | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/.github/workflows/deploy-docs.yml b/.github/workflows/deploy-docs.yml index 1834463a..e5b55f0b 100644 --- a/.github/workflows/deploy-docs.yml +++ b/.github/workflows/deploy-docs.yml @@ -69,24 +69,23 @@ jobs: # Deploy Steps # ============================================ - - name: Determine target project - id: target + - name: Set deployment variables run: | if [ "${{ github.ref_name }}" = "main" ]; then - echo "project=stackwright-docs" >> $GITHUB_OUTPUT - echo "env=production" >> $GITHUB_OUTPUT + echo "DEPLOY_PROJECT=stackwright-docs" >> $GITHUB_ENV + echo "DEPLOY_ENV=production" >> $GITHUB_ENV else - echo "project=stackwright-docs-dev" >> $GITHUB_OUTPUT - echo "env=development" >> $GITHUB_OUTPUT + echo "DEPLOY_PROJECT=stackwright-docs-dev" >> $GITHUB_ENV + echo "DEPLOY_ENV=development" >> $GITHUB_ENV fi - echo "Selected project: ${{ steps.target.outputs.project }}" + echo "Selected project: ${{ env.DEPLOY_PROJECT }}" - name: Deploy to Cloudflare Pages uses: cloudflare/pages-action@v1 with: apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }} accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} - projectName: ${{ steps.target.outputs.project }} + projectName: ${{ env.DEPLOY_PROJECT }} directory: examples/stackwright-docs/out gitHubToken: ${{ secrets.GITHUB_TOKEN }} @@ -97,8 +96,8 @@ jobs: echo "| Detail | Value |" >> $GITHUB_STEP_SUMMARY echo "|--------|-------|" >> $GITHUB_STEP_SUMMARY echo "| Branch | \`${{ github.ref_name }}\` |" >> $GITHUB_STEP_SUMMARY - echo "| Environment | ${{ steps.target.outputs.env }} |" >> $GITHUB_STEP_SUMMARY - echo "| Project | \`${{ steps.target.outputs.project }}\` |" >> $GITHUB_STEP_SUMMARY + echo "| Environment | ${{ env.DEPLOY_ENV }} |" >> $GITHUB_STEP_SUMMARY + echo "| Project | \`${{ env.DEPLOY_PROJECT }}\` |" >> $GITHUB_STEP_SUMMARY echo "| Commit | \`${{ github.sha }}\` |" >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY echo "**Deployed to Cloudflare Pages**" From 5fb4e1b91b696d784919012dadb76a4062300915 Mon Sep 17 00:00:00 2001 From: Charles Gibson <46542971+perasperaactual@users.noreply.github.com> Date: Sun, 12 Apr 2026 16:22:58 -0400 Subject: [PATCH 05/27] feat(hooks): add @stackwright/hooks-registry for cross-module singleton (#341) * feat(hooks): add @stackwright/hooks-registry for cross-module singleton - Create new @stackwright/hooks-registry package using Symbol.for() pattern - Update @stackwright/scaffold-core to re-export from shared registry - Fix fallback:'blocking' + output:'export' incompatibility in template - Update E2E config to serve static out/ directory Fixes module isolation where Pro packages' hooks weren't visible to CLI. * fix(hooks): add resetForTesting export and improve singleton tests * fix: address lint warnings for PR #341 * chore: update visual regression baselines and SBOM files * fix(deps): pin undici to ^7.0.0 for jsdom compatibility --------- Co-authored-by: Stackwright Bot --- .changeset/hooks-registry-fix.md | 9 + .next/trace | 1 + .next/trace-build | 1 + .npmrc | 1 + examples/stackwright-docs/build-manifest.json | 31 +- examples/stackwright-docs/cyclonedx.json | 40 +- examples/stackwright-docs/pages/[slug].tsx | 2 +- examples/stackwright-docs/spdx.json | 50 +- examples/stackwright-docs/spdx.spdx | 31 +- package.json | 2 +- .../build-scripts/test/prebuild-fonts.test.ts | 6 +- packages/cli/src/commands/sbom.ts | 4 +- packages/cli/src/commands/scaffold.ts | 1 - packages/cli/src/utils/template-processor.ts | 1 - .../scaffold-template/pages/[...slug].tsx | 2 +- .../src/components/structural/PageLayout.tsx | 1 - packages/e2e/playwright.config.ts | 8 +- packages/hooks-registry/README.md | 158 + packages/hooks-registry/package.json | 37 + .../src/types => hooks-registry/src}/hooks.ts | 8 - packages/hooks-registry/src/index.ts | 19 + packages/hooks-registry/src/registry.ts | 135 + packages/hooks-registry/test/registry.test.ts | 457 +++ packages/hooks-registry/tsconfig.json | 8 + packages/hooks-registry/tsup.config.ts | 10 + packages/hooks-registry/vitest.config.ts | 8 + packages/launch-stackwright/src/index.ts | 2 +- packages/sbom-generator/src/formats/spdx.ts | 10 +- packages/sbom-generator/src/generator.ts | 4 +- packages/sbom-generator/src/utils/purl.ts | 3 +- packages/scaffold-core/package.json | 3 + packages/scaffold-core/src/index.ts | 13 +- packages/scaffold-core/src/registry.ts | 84 - packages/scaffold-core/test/registry.test.ts | 2 +- pnpm-lock.yaml | 3052 +++++------------ 35 files changed, 1833 insertions(+), 2371 deletions(-) create mode 100644 .changeset/hooks-registry-fix.md create mode 100644 .next/trace create mode 100644 .next/trace-build create mode 100644 .npmrc create mode 100644 packages/hooks-registry/README.md create mode 100644 packages/hooks-registry/package.json rename packages/{scaffold-core/src/types => hooks-registry/src}/hooks.ts (92%) create mode 100644 packages/hooks-registry/src/index.ts create mode 100644 packages/hooks-registry/src/registry.ts create mode 100644 packages/hooks-registry/test/registry.test.ts create mode 100644 packages/hooks-registry/tsconfig.json create mode 100644 packages/hooks-registry/tsup.config.ts create mode 100644 packages/hooks-registry/vitest.config.ts delete mode 100644 packages/scaffold-core/src/registry.ts diff --git a/.changeset/hooks-registry-fix.md b/.changeset/hooks-registry-fix.md new file mode 100644 index 00000000..0a3f2766 --- /dev/null +++ b/.changeset/hooks-registry-fix.md @@ -0,0 +1,9 @@ +--- +"@stackwright/hooks-registry": minor +"@stackwright/scaffold-core": minor +"@stackwright/cli": patch +"@stackwright/docs": patch +"@stackwright/e2e": patch +--- + +Add shared hooks-registry package for cross-module singleton and fix fallback static export compatibility diff --git a/.next/trace b/.next/trace new file mode 100644 index 00000000..7e754a84 --- /dev/null +++ b/.next/trace @@ -0,0 +1 @@ +[{"name":"generate-buildid","duration":172,"timestamp":94929785894,"id":4,"parentId":1,"tags":{},"startTime":1775652916940,"traceId":"1cce27f927293e70"},{"name":"load-custom-routes","duration":210,"timestamp":94929786138,"id":5,"parentId":1,"tags":{},"startTime":1775652916941,"traceId":"1cce27f927293e70"},{"name":"create-dist-dir","duration":904,"timestamp":94929786365,"id":6,"parentId":1,"tags":{},"startTime":1775652916941,"traceId":"1cce27f927293e70"},{"name":"clean","duration":160,"timestamp":94929787784,"id":7,"parentId":1,"tags":{},"startTime":1775652916942,"traceId":"1cce27f927293e70"},{"name":"next-build","duration":30673,"timestamp":94929757414,"id":1,"tags":{"buildMode":"default","version":"16.2.2","bundler":"turbopack","failed":true},"startTime":1775652916912,"traceId":"1cce27f927293e70"}] diff --git a/.next/trace-build b/.next/trace-build new file mode 100644 index 00000000..8280c7d8 --- /dev/null +++ b/.next/trace-build @@ -0,0 +1 @@ +[{"name":"next-build","duration":30673,"timestamp":94929757414,"id":1,"tags":{"buildMode":"default","version":"16.2.2","bundler":"turbopack","failed":true},"startTime":1775652916912,"traceId":"1cce27f927293e70"}] diff --git a/.npmrc b/.npmrc new file mode 100644 index 00000000..d67f3748 --- /dev/null +++ b/.npmrc @@ -0,0 +1 @@ +node-linker=hoisted diff --git a/examples/stackwright-docs/build-manifest.json b/examples/stackwright-docs/build-manifest.json index 1120cd6a..64085b1d 100644 --- a/examples/stackwright-docs/build-manifest.json +++ b/examples/stackwright-docs/build-manifest.json @@ -1,10 +1,10 @@ { "format": "stackwright-build-manifest", "version": "1.0.0", - "generated": "2026-04-03T20:10:38.344Z", + "generated": "2026-04-12T17:19:57.945Z", "project": { "name": "stackwright-docs", - "version": "0.1.1-alpha.1", + "version": "0.1.2-alpha.1", "root": "/home/charles/git/peraspera/stackwright/examples/stackwright-docs", "isMonorepo": false }, @@ -110,11 +110,11 @@ }, { "name": "next", - "version": "16.1.6", + "version": "16.2.2", "type": "direct", "category": "external", - "purl": "pkg:npm/next@16.1.6", - "integrity": "dab6b931b02c25bd4f20dd3f93ceb0b57f8971844a25f14feb62c8a20608db04", + "purl": "pkg:npm/next@16.2.2", + "integrity": "2037941b242dfcf433d88c748a094cc731b35a4559f7adbc45359fbbafe1d236", "depth": 0 }, { @@ -135,32 +135,23 @@ "integrity": "d321eb3d0478e2a58565081343e4d1da25caf8103b3dd72b3544a715f2e80418", "depth": 0 }, - { - "name": "stackwright-docs", - "version": "link:", - "type": "direct", - "category": "external", - "purl": "pkg:npm/stackwright-docs@link:", - "integrity": "048f32c842b18fe0c8c18f64b17cedd18149860b0064ba12a2c595890048e568", - "depth": 0 - }, { "name": "tailwind-merge", - "version": "2.6.0", + "version": "3.5.0", "type": "direct", "category": "external", - "purl": "pkg:npm/tailwind-merge@2.6.0", - "integrity": "f55becd055a25a9ecb32ee3cf37faf157aa62b955195bb7f695909e582e8ae33", + "purl": "pkg:npm/tailwind-merge@3.5.0", + "integrity": "039d2e92d475e4268b1babe22545c8cc507d46e9fc09aacaa75ee5925b885a10", "depth": 0 } ], "metadata": { - "totalDependencies": 16, - "directDependencies": 16, + "totalDependencies": 15, + "directDependencies": 15, "devDependencies": 0, "peerDependencies": 0, "transitiveDependencies": 0, "stackwrightInternal": 4, - "external": 12 + "external": 11 } } \ No newline at end of file diff --git a/examples/stackwright-docs/cyclonedx.json b/examples/stackwright-docs/cyclonedx.json index a6d08351..0ceeb1ab 100644 --- a/examples/stackwright-docs/cyclonedx.json +++ b/examples/stackwright-docs/cyclonedx.json @@ -3,7 +3,7 @@ "specVersion": "1.5", "version": 1, "metadata": { - "timestamp": "2026-04-03T20:10:38.343Z", + "timestamp": "2026-04-12T17:19:57.945Z", "tools": [ { "vendor": "Stackwright", @@ -14,8 +14,8 @@ "component": { "type": "application", "name": "stackwright-docs", - "version": "0.1.1-alpha.1", - "purl": "pkg:npm/stackwright-docs@0.1.1-alpha.1" + "version": "0.1.2-alpha.1", + "purl": "pkg:npm/stackwright-docs@0.1.2-alpha.1" } }, "components": [ @@ -231,13 +231,13 @@ { "type": "library", "name": "next", - "version": "16.1.6", - "purl": "pkg:npm/next@16.1.6", + "version": "16.2.2", + "purl": "pkg:npm/next@16.2.2", "scope": "required", "hashes": [ { "alg": "SHA-256", - "content": "dab6b931b02c25bd4f20dd3f93ceb0b57f8971844a25f14feb62c8a20608db04" + "content": "2037941b242dfcf433d88c748a094cc731b35a4559f7adbc45359fbbafe1d236" } ], "externalReferences": [ @@ -285,35 +285,16 @@ } ] }, - { - "type": "library", - "name": "stackwright-docs", - "version": "link:", - "purl": "pkg:npm/stackwright-docs@link:", - "scope": "required", - "hashes": [ - { - "alg": "SHA-256", - "content": "048f32c842b18fe0c8c18f64b17cedd18149860b0064ba12a2c595890048e568" - } - ], - "externalReferences": [ - { - "type": "package-manager", - "url": "https://www.npmjs.com/package/stackwright-docs" - } - ] - }, { "type": "library", "name": "tailwind-merge", - "version": "2.6.0", - "purl": "pkg:npm/tailwind-merge@2.6.0", + "version": "3.5.0", + "purl": "pkg:npm/tailwind-merge@3.5.0", "scope": "required", "hashes": [ { "alg": "SHA-256", - "content": "f55becd055a25a9ecb32ee3cf37faf157aa62b955195bb7f695909e582e8ae33" + "content": "039d2e92d475e4268b1babe22545c8cc507d46e9fc09aacaa75ee5925b885a10" } ], "externalReferences": [ @@ -326,7 +307,7 @@ ], "dependencies": [ { - "ref": "pkg:npm/stackwright-docs@0.1.1-alpha.1", + "ref": "pkg:npm/stackwright-docs@0.1.2-alpha.1", "dependsOn": [ "@radix-ui/react-accordion", "@radix-ui/react-slot", @@ -342,7 +323,6 @@ "next", "react", "react-dom", - "stackwright-docs", "tailwind-merge" ], "dependencyType": "direct" diff --git a/examples/stackwright-docs/pages/[slug].tsx b/examples/stackwright-docs/pages/[slug].tsx index 1eca315d..847068ba 100644 --- a/examples/stackwright-docs/pages/[slug].tsx +++ b/examples/stackwright-docs/pages/[slug].tsx @@ -13,7 +13,7 @@ export const getStaticPaths: GetStaticPaths = async () => { .map(f => f.replace(/\.json$/, "")); return { paths: slugs.map(slug => ({ params: { slug } })), - fallback: "blocking", + fallback: false, }; }; diff --git a/examples/stackwright-docs/spdx.json b/examples/stackwright-docs/spdx.json index 205d0ff4..585a877a 100644 --- a/examples/stackwright-docs/spdx.json +++ b/examples/stackwright-docs/spdx.json @@ -1,11 +1,11 @@ { "spdxVersion": "SPDX-2.3", "dataLicense": "CC0-1.0", - "SPDXID": "SPDXRef-DOCUMENT-0b5eb5d9", - "name": "stackwright-docs@0.1.1-alpha.1", - "documentNamespace": "https://stackwright.dev/spdx/stackwright-docs/2026-04-03T20:10:38.342Z", + "SPDXID": "SPDXRef-DOCUMENT-5a46aea3", + "name": "stackwright-docs@0.1.2-alpha.1", + "documentNamespace": "https://stackwright.dev/spdx/stackwright-docs/2026-04-12T17:19:57.943Z", "creationInfo": { - "created": "2026-04-03T20:10:38.342Z", + "created": "2026-04-12T17:19:57.943Z", "creators": [ "Tool: @stackwright/sbom-generator", "Tool-Version: 0.0.0" @@ -268,14 +268,14 @@ { "SPDXID": "SPDXRef-Package-next", "name": "next", - "versionInfo": "16.1.6", + "versionInfo": "16.2.2", "supplier": "NOASSERTION", "downloadLocation": "NOASSERTION", "filesAnalyzed": false, "checksums": [ { "algorithm": "SHA256", - "value": "dab6b931b02c25bd4f20dd3f93ceb0b57f8971844a25f14feb62c8a20608db04" + "value": "2037941b242dfcf433d88c748a094cc731b35a4559f7adbc45359fbbafe1d236" } ], "licenseConcluded": "NOASSERTION", @@ -284,7 +284,7 @@ { "referenceCategory": "PACKAGE-MANAGER", "referenceType": "purl", - "referenceLocator": "pkg:npm/next@16.1.6" + "referenceLocator": "pkg:npm/next@16.2.2" } ] }, @@ -334,40 +334,17 @@ } ] }, - { - "SPDXID": "SPDXRef-Package-stackwright-docs", - "name": "stackwright-docs", - "versionInfo": "link:", - "supplier": "NOASSERTION", - "downloadLocation": "NOASSERTION", - "filesAnalyzed": false, - "checksums": [ - { - "algorithm": "SHA256", - "value": "048f32c842b18fe0c8c18f64b17cedd18149860b0064ba12a2c595890048e568" - } - ], - "licenseConcluded": "NOASSERTION", - "licenseDeclared": "NOASSERTION", - "externalRefs": [ - { - "referenceCategory": "PACKAGE-MANAGER", - "referenceType": "purl", - "referenceLocator": "pkg:npm/stackwright-docs@link:" - } - ] - }, { "SPDXID": "SPDXRef-Package-tailwind-merge", "name": "tailwind-merge", - "versionInfo": "2.6.0", + "versionInfo": "3.5.0", "supplier": "NOASSERTION", "downloadLocation": "NOASSERTION", "filesAnalyzed": false, "checksums": [ { "algorithm": "SHA256", - "value": "f55becd055a25a9ecb32ee3cf37faf157aa62b955195bb7f695909e582e8ae33" + "value": "039d2e92d475e4268b1babe22545c8cc507d46e9fc09aacaa75ee5925b885a10" } ], "licenseConcluded": "NOASSERTION", @@ -376,14 +353,14 @@ { "referenceCategory": "PACKAGE-MANAGER", "referenceType": "purl", - "referenceLocator": "pkg:npm/tailwind-merge@2.6.0" + "referenceLocator": "pkg:npm/tailwind-merge@3.5.0" } ] } ], "relationships": [ { - "spdxElementId": "SPDXRef-DOCUMENT-0b5eb5d9", + "spdxElementId": "SPDXRef-DOCUMENT-5a46aea3", "relationshipType": "DESCRIBES", "relatedSpdxElement": "SPDXRef-Package-clsx" }, @@ -452,11 +429,6 @@ "relationshipType": "CONTAINS", "relatedSpdxElement": "SPDXRef-Package-react-dom" }, - { - "spdxElementId": "SPDXRef-Package-clsx", - "relationshipType": "CONTAINS", - "relatedSpdxElement": "SPDXRef-Package-stackwright-docs" - }, { "spdxElementId": "SPDXRef-Package-clsx", "relationshipType": "CONTAINS", diff --git a/examples/stackwright-docs/spdx.spdx b/examples/stackwright-docs/spdx.spdx index e2de6a75..1df1ac25 100644 --- a/examples/stackwright-docs/spdx.spdx +++ b/examples/stackwright-docs/spdx.spdx @@ -1,11 +1,11 @@ SPDXVersion: SPDX-2.3 DataLicense: CC0-1.0 -SPDXID: SPDXRef-DOCUMENT-0b5eb5d9 -DocumentName: stackwright-docs@0.1.1-alpha.1 -DocumentNamespace: https://stackwright.dev/spdx/stackwright-docs/2026-04-03T20:10:38.342Z +SPDXID: SPDXRef-DOCUMENT-5a46aea3 +DocumentName: stackwright-docs@0.1.2-alpha.1 +DocumentNamespace: https://stackwright.dev/spdx/stackwright-docs/2026-04-12T17:19:57.943Z CreationInfo: - Created: 2026-04-03T20:10:38.342Z + Created: 2026-04-12T17:19:57.943Z Creator: Tool: @stackwright/sbom-generator Creator: Tool-Version: 0.0.0 @@ -99,11 +99,11 @@ ExternalRef: PACKAGE-MANAGER purl pkg:npm/lucide-react@0.471.1 PackageName: next SPDXID: SPDXRef-Package-next -PackageVersion: 16.1.6 +PackageVersion: 16.2.2 PackageLicenseConcluded: NOASSERTION PackageLicenseDeclared: NOASSERTION -PackageChecksum: SHA256: dab6b931b02c25bd4f20dd3f93ceb0b57f8971844a25f14feb62c8a20608db04 -ExternalRef: PACKAGE-MANAGER purl pkg:npm/next@16.1.6 +PackageChecksum: SHA256: 2037941b242dfcf433d88c748a094cc731b35a4559f7adbc45359fbbafe1d236 +ExternalRef: PACKAGE-MANAGER purl pkg:npm/next@16.2.2 PackageName: react SPDXID: SPDXRef-Package-react @@ -121,23 +121,15 @@ PackageLicenseDeclared: NOASSERTION PackageChecksum: SHA256: d321eb3d0478e2a58565081343e4d1da25caf8103b3dd72b3544a715f2e80418 ExternalRef: PACKAGE-MANAGER purl pkg:npm/react-dom@19.2.4 -PackageName: stackwright-docs -SPDXID: SPDXRef-Package-stackwright-docs -PackageVersion: link: -PackageLicenseConcluded: NOASSERTION -PackageLicenseDeclared: NOASSERTION -PackageChecksum: SHA256: 048f32c842b18fe0c8c18f64b17cedd18149860b0064ba12a2c595890048e568 -ExternalRef: PACKAGE-MANAGER purl pkg:npm/stackwright-docs@link: - PackageName: tailwind-merge SPDXID: SPDXRef-Package-tailwind-merge -PackageVersion: 2.6.0 +PackageVersion: 3.5.0 PackageLicenseConcluded: NOASSERTION PackageLicenseDeclared: NOASSERTION -PackageChecksum: SHA256: f55becd055a25a9ecb32ee3cf37faf157aa62b955195bb7f695909e582e8ae33 -ExternalRef: PACKAGE-MANAGER purl pkg:npm/tailwind-merge@2.6.0 +PackageChecksum: SHA256: 039d2e92d475e4268b1babe22545c8cc507d46e9fc09aacaa75ee5925b885a10 +ExternalRef: PACKAGE-MANAGER purl pkg:npm/tailwind-merge@3.5.0 -Relationship: SPDXRef-DOCUMENT-0b5eb5d9 DESCRIBES SPDXRef-Package-clsx +Relationship: SPDXRef-DOCUMENT-5a46aea3 DESCRIBES SPDXRef-Package-clsx Relationship: SPDXRef-Package-clsx CONTAINS SPDXRef-Package--radix-ui-react-accordion Relationship: SPDXRef-Package-clsx CONTAINS SPDXRef-Package--radix-ui-react-slot Relationship: SPDXRef-Package-clsx CONTAINS SPDXRef-Package--radix-ui-react-tabs @@ -151,5 +143,4 @@ Relationship: SPDXRef-Package-clsx CONTAINS SPDXRef-Package-lucide-react Relationship: SPDXRef-Package-clsx CONTAINS SPDXRef-Package-next Relationship: SPDXRef-Package-clsx CONTAINS SPDXRef-Package-react Relationship: SPDXRef-Package-clsx CONTAINS SPDXRef-Package-react-dom -Relationship: SPDXRef-Package-clsx CONTAINS SPDXRef-Package-stackwright-docs Relationship: SPDXRef-Package-clsx CONTAINS SPDXRef-Package-tailwind-merge \ No newline at end of file diff --git a/package.json b/package.json index c1004201..ee850e6f 100644 --- a/package.json +++ b/package.json @@ -85,7 +85,7 @@ "path-to-regexp": ">=8.4.0", "diff": ">=4.0.4", "tmp": ">=0.2.4", - "undici": ">=7.24.0", + "undici": "^7.0.0", "tar": ">=6.2.1", "flatted": ">=3.4.2", "next": ">=16.1.7", diff --git a/packages/build-scripts/test/prebuild-fonts.test.ts b/packages/build-scripts/test/prebuild-fonts.test.ts index 2749b753..fa242986 100644 --- a/packages/build-scripts/test/prebuild-fonts.test.ts +++ b/packages/build-scripts/test/prebuild-fonts.test.ts @@ -1,13 +1,9 @@ -import { describe, it, expect, beforeEach, afterEach } from 'vitest'; -import fs from 'fs'; -import path from 'path'; -import os from 'os'; +import { describe, it, expect } from 'vitest'; import { extractGoogleFontNames, generateGoogleFontsUrl, generateFontLinkTags, } from '../src/prebuild'; -import { runPrebuild } from '../src/prebuild'; // -------------------------------------------------------------------------- // extractGoogleFontNames tests diff --git a/packages/cli/src/commands/sbom.ts b/packages/cli/src/commands/sbom.ts index bf6173a6..82834b8f 100644 --- a/packages/cli/src/commands/sbom.ts +++ b/packages/cli/src/commands/sbom.ts @@ -139,8 +139,8 @@ export interface SBOMDiffResult { changed: string[]; } -async function diffSBOM(options: SBOMDiffOptions): Promise { - const { quickSummary } = await import('@stackwright/sbom-generator'); +async function diffSBOM(_options: SBOMDiffOptions): Promise { + void (await import('@stackwright/sbom-generator')); const oldPath = path.resolve(options.oldFile); const newPath = path.resolve(options.newFile); diff --git a/packages/cli/src/commands/scaffold.ts b/packages/cli/src/commands/scaffold.ts index 70fe0047..49b95438 100644 --- a/packages/cli/src/commands/scaffold.ts +++ b/packages/cli/src/commands/scaffold.ts @@ -7,7 +7,6 @@ import { promptThemeSelection } from '../utils/theme-selector'; import { processTemplate } from '../utils/template-processor'; import { outputResult, outputError, getErrorCode, formatError } from '../utils/json-output'; import { runScaffoldHooks } from '@stackwright/scaffold-core'; -import type { ScaffoldHookContext } from '@stackwright/scaffold-core'; export interface ScaffoldOptions { name?: string; diff --git a/packages/cli/src/utils/template-processor.ts b/packages/cli/src/utils/template-processor.ts index 563d5d38..e4791a20 100644 --- a/packages/cli/src/utils/template-processor.ts +++ b/packages/cli/src/utils/template-processor.ts @@ -13,7 +13,6 @@ import { } from './scaffold-hints'; import { fetchTemplate } from './template-fetcher'; import { runScaffoldHooks } from '@stackwright/scaffold-core'; -import type { ScaffoldHookContext } from '@stackwright/scaffold-core'; /** * Walk up from the given directory looking for a pnpm-workspace.yaml. diff --git a/packages/cli/templates/scaffold-template/pages/[...slug].tsx b/packages/cli/templates/scaffold-template/pages/[...slug].tsx index 43700834..d12a10cd 100644 --- a/packages/cli/templates/scaffold-template/pages/[...slug].tsx +++ b/packages/cli/templates/scaffold-template/pages/[...slug].tsx @@ -34,7 +34,7 @@ export const getStaticPaths: GetStaticPaths = async () => { paths: slugPaths.map((slugPath) => ({ params: { slug: slugPath.split('/') }, })), - fallback: 'blocking', + fallback: false, }; }; diff --git a/packages/core/src/components/structural/PageLayout.tsx b/packages/core/src/components/structural/PageLayout.tsx index 66d47cc1..03d597d6 100644 --- a/packages/core/src/components/structural/PageLayout.tsx +++ b/packages/core/src/components/structural/PageLayout.tsx @@ -70,7 +70,6 @@ export default function PageLayout({ pageContent, siteConfig }: PageLayoutProps) const theme = useSafeTheme(); const config = siteConfig || defaultSiteConfig; const backgroundColor = theme.colors.background; - const hasSidebar = !!config.sidebar; // Resolve sidebar: page-level override > site-level default const resolvedSidebar = resolveSidebarConfig(pageContent.content.navSidebar, config.sidebar); diff --git a/packages/e2e/playwright.config.ts b/packages/e2e/playwright.config.ts index 394f650b..4e84b11b 100644 --- a/packages/e2e/playwright.config.ts +++ b/packages/e2e/playwright.config.ts @@ -28,7 +28,7 @@ const SITE_CONFIG: Record = { filter: 'stackwright-docs', port: 3000, }, - 'hellostackwrightnext': { + hellostackwrightnext: { filter: 'stackwright-example-app', port: 3000, }, @@ -36,7 +36,9 @@ const SITE_CONFIG: Record = { const site = SITE_CONFIG[TEST_SITE]; if (!site) { - throw new Error(`Unknown TEST_SITE="${TEST_SITE}". Valid: ${Object.keys(SITE_CONFIG).join(', ')}`); + throw new Error( + `Unknown TEST_SITE="${TEST_SITE}". Valid: ${Object.keys(SITE_CONFIG).join(', ')}` + ); } export default defineConfig({ @@ -106,7 +108,7 @@ export default defineConfig({ webServer: process.env.PERF_NO_SERVER ? undefined : { - command: `pnpm --filter ${site.filter} exec stackwright-prebuild && pnpm --filter ${site.filter} exec next build && pnpm --filter ${site.filter} exec next start`, + command: `pnpm --filter ${site.filter} exec stackwright-prebuild && pnpm --filter ${site.filter} exec next build && pnpm --filter ${site.filter} exec npx serve out -l ${site.port}`, cwd: path.resolve(__dirname, '../..'), port: site.port, timeout: 180_000, diff --git a/packages/hooks-registry/README.md b/packages/hooks-registry/README.md new file mode 100644 index 00000000..f942618b --- /dev/null +++ b/packages/hooks-registry/README.md @@ -0,0 +1,158 @@ +# @stackwright/hooks-registry + +Singleton registry for scaffold hooks. Uses `Symbol.for()` pattern to survive module boundary crossings between different package copies. + +## Why a Separate Package? + +The `@stackwright/scaffold-core` package already has a hooks registry, but it uses module-level variables which can be reset when modules are reloaded or when multiple instances of the package exist in different parts of the monorepo. + +This package provides a **truly persistent singleton** that: + +1. Survives module boundary crossings +2. Works correctly across different parts of a monorepo +3. Provides consistent behavior for Pro hooks + +## Installation + +```bash +# Already included via workspace +import { registerScaffoldHook } from '@stackwright/hooks-registry'; +``` + +## Usage + +### Register a Hook + +```typescript +import { registerScaffoldHook } from '@stackwright/hooks-registry'; + +registerScaffoldHook({ + type: 'preInstall', + name: 'enterprise-license', + priority: 10, + critical: true, + handler: async (context) => { + if (!process.env.PRO_API_KEY) { + throw new Error('PRO_API_KEY required'); + } + context.packageJson.dependencies['@stackwright-pro/license'] = '^1.0.0'; + }, +}); +``` + +### Get All Hooks + +```typescript +import { getScaffoldHooks, getScaffoldHooksForType } from '@stackwright/hooks-registry'; + +// Get all hooks +const allHooks = getScaffoldHooks(); + +// Get hooks for specific lifecycle point +const preInstallHooks = getScaffoldHooksForType('preInstall'); +``` + +### Run Hooks + +```typescript +import { runScaffoldHooks } from '@stackwright/hooks-registry'; + +const context = { + targetDir: '/path/to/project', + projectName: 'my-site', + siteTitle: 'My Site', + themeId: 'default', + packageJson: {}, + dependencyMode: 'workspace', +}; + +await runScaffoldHooks('preInstall', context); +``` + +### Clear Hooks + +```typescript +import { clearScaffoldHooks, resetForTesting } from '@stackwright/hooks-registry'; + +// Clear all hooks +clearScaffoldHooks(); + +// Reset for testing isolation +resetForTesting(); +``` + +## Hook Types + +| Type | When | Use Case | +|------|------|----------| +| `preScaffold` | Before scaffolding begins | Validate environment, check licenses | +| `preInstall` | After files created, before `pnpm install` | Modify `package.json`, add dependencies | +| `postInstall` | After `pnpm install` completes | Verify installation, run setup scripts | +| `postScaffold` | After scaffolding complete | Final configuration, cleanup | + +## Hook Options + +| Option | Type | Default | Description | +|--------|------|---------|-------------| +| `type` | `ScaffoldHookType` | Required | Lifecycle point | +| `name` | `string` | Required | Unique hook name | +| `priority` | `number` | `50` | Lower = runs first | +| `critical` | `boolean` | `false` | If true, failure fails entire scaffold | +| `handler` | `function` | Required | Async function to execute | + +## Context Object + +The context passed to hooks: + +```typescript +interface ScaffoldHookContext { + targetDir: string; // Project directory + projectName: string; // Project name + siteTitle: string; // Site title + themeId: string; // Theme ID + packageJson: Record; // Mutable - add dependencies + codePuppyConfig?: Record; // Mutable - add MCP config + dependencyMode: 'workspace' | 'standalone'; + pages?: string[]; // Pages being created + install?: boolean; // Whether install will run + [key: string]: any; // Hooks can add custom properties +} +``` + +## API Reference + +### registerScaffoldHook(hook) + +Register a hook to run during scaffolding. Hooks are sorted by priority (lower = runs first). + +### getScaffoldHooks() + +Get all registered hooks as a readonly array. + +### getScaffoldHooksForType(type) + +Get hooks for a specific lifecycle point. + +### clearScaffoldHooks() + +Remove all registered hooks. + +### resetForTesting() + +Clear hooks and reset global state for test isolation. + +### runScaffoldHooks(type, context) + +Execute all hooks of a given type. Critical hook failures throw; non-critical failures are logged and execution continues. + +## Testing + +```bash +pnpm --filter @stackwright/hooks-registry test +``` + +## Build + +```bash +pnpm --filter @stackwright/hooks-registry build +``` \ No newline at end of file diff --git a/packages/hooks-registry/package.json b/packages/hooks-registry/package.json new file mode 100644 index 00000000..4d1ce5f3 --- /dev/null +++ b/packages/hooks-registry/package.json @@ -0,0 +1,37 @@ +{ + "name": "@stackwright/hooks-registry", + "version": "0.1.0-alpha.0", + "description": "Singleton registry for scaffold hooks - survives module boundary crossings", + "license": "MIT", + "repository": { + "type": "git", + "url": "https://github.com/Per-Aspera-LLC/stackwright" + }, + "main": "./dist/index.js", + "module": "./dist/index.mjs", + "types": "./dist/index.d.ts", + "exports": { + ".": { + "types": "./dist/index.d.ts", + "import": "./dist/index.mjs", + "require": "./dist/index.js" + } + }, + "files": [ + "dist" + ], + "scripts": { + "build": "tsup", + "dev": "tsup --watch", + "test": "vitest run" + }, + "devDependencies": { + "@types/node": "^20.0.0", + "tsup": "^8.5.0", + "typescript": "^5.8.0", + "vitest": "workspace:*" + }, + "engines": { + "node": ">=20.0.0" + } +} \ No newline at end of file diff --git a/packages/scaffold-core/src/types/hooks.ts b/packages/hooks-registry/src/hooks.ts similarity index 92% rename from packages/scaffold-core/src/types/hooks.ts rename to packages/hooks-registry/src/hooks.ts index 1829f0e9..02fd2f46 100644 --- a/packages/scaffold-core/src/types/hooks.ts +++ b/packages/hooks-registry/src/hooks.ts @@ -33,14 +33,6 @@ export interface ScaffoldHook { handler: (context: ScaffoldHookContext) => Promise | void; } -/** - * Hook registration options - */ -export interface ScaffoldHookOptions extends Partial { - type: ScaffoldHookType; - name: string; -} - /** * Context passed to all hooks */ diff --git a/packages/hooks-registry/src/index.ts b/packages/hooks-registry/src/index.ts new file mode 100644 index 00000000..5e08b7ba --- /dev/null +++ b/packages/hooks-registry/src/index.ts @@ -0,0 +1,19 @@ +/** + * @stackwright/hooks-registry + * + * Singleton registry for scaffold hooks. + * Uses Symbol.for() to survive module boundary crossings between different package copies. + */ + +// Types re-export +export type { ScaffoldHook, ScaffoldHookType, ScaffoldHookContext } from './hooks'; + +// Registry functions +export { + registerScaffoldHook, + getScaffoldHooks, + getScaffoldHooksForType, + clearScaffoldHooks, + resetForTesting, + runScaffoldHooks, +} from './registry'; diff --git a/packages/hooks-registry/src/registry.ts b/packages/hooks-registry/src/registry.ts new file mode 100644 index 00000000..f90615b1 --- /dev/null +++ b/packages/hooks-registry/src/registry.ts @@ -0,0 +1,135 @@ +/** + * Scaffold Hook Registry + * + * Singleton registry using Symbol.for() to survive module boundary crossings. + * This allows the registry to work correctly even when multiple copies of this + * package are loaded by different parts of the monorepo. + */ + +import type { ScaffoldHook, ScaffoldHookType, ScaffoldHookContext } from './hooks'; + +/** + * Singleton key for cross-module registry sharing + */ +const REGISTRY_KEY = Symbol.for('@stackwright/hooks-registry:hooks'); + +/** + * Get or create the shared registry + * Uses Symbol.for() so multiple module instances share the same storage + */ +function getRegistry(): Map { + const global = globalThis as typeof globalThis & { + [REGISTRY_KEY]?: Map; + }; + if (!global[REGISTRY_KEY]) { + global[REGISTRY_KEY] = new Map(); + } + return global[REGISTRY_KEY]!; +} + +/** + * Hook entry stored in registry + */ +interface HookEntry { + type: ScaffoldHookType; + name: string; + priority: number; + critical: boolean; + handler: (context: any) => Promise | void; +} + +/** + * Register a hook to run during scaffolding + * + * @example + * import { registerScaffoldHook } from '@stackwright/hooks-registry'; + * + * registerScaffoldHook({ + * type: 'preInstall', + * name: 'enterprise-license', + * priority: 10, + * handler: addEnterpriseLicense, + * }); + */ +export function registerScaffoldHook(hook: ScaffoldHook): void { + const registry = getRegistry(); + registry.set(hook.name, { + type: hook.type, + name: hook.name, + priority: hook.priority ?? 50, + critical: hook.critical ?? false, + handler: hook.handler, + }); +} + +/** + * Get all registered hooks, sorted by priority + */ +export function getScaffoldHooks(): ReadonlyArray { + const registry = getRegistry(); + const entries = Array.from(registry.values()); + return entries + .sort((a, b) => a.priority - b.priority) + .map((entry) => ({ + type: entry.type, + name: entry.name, + priority: entry.priority, + critical: entry.critical, + handler: entry.handler, + })); +} + +/** + * Get hooks for a specific lifecycle point, sorted by priority + */ +export function getScaffoldHooksForType(type: ScaffoldHookType): ReadonlyArray { + return getScaffoldHooks().filter((h) => h.type === type); +} + +/** + * Clear all registered hooks + */ +export function clearScaffoldHooks(): void { + getRegistry().clear(); +} + +/** + * Reset registry for testing isolation. + * Combines clearing hooks with resetting global state. + */ +export function resetForTesting(): void { + clearScaffoldHooks(); +} + +/** + * Run all hooks of a given type + * + * @param type - Lifecycle point + * @param context - Context passed to hooks + * @throws If a critical hook fails + */ +export async function runScaffoldHooks( + type: ScaffoldHookType, + context: ScaffoldHookContext +): Promise { + const relevantHooks = getScaffoldHooksForType(type); + + for (const hook of relevantHooks) { + try { + await hook.handler(context); + } catch (error) { + if (hook.critical) { + throw new Error( + `Critical scaffold hook "${hook.name}" failed: ${(error as Error).message}` + ); + } + // Non-critical: warn but continue + console.warn( + `[Scaffold Hook] Non-critical hook "${hook.name}" failed: ${(error as Error).message}` + ); + } + } +} + +// Re-export types from hooks.ts +export type { ScaffoldHook, ScaffoldHookType, ScaffoldHookContext } from './hooks'; diff --git a/packages/hooks-registry/test/registry.test.ts b/packages/hooks-registry/test/registry.test.ts new file mode 100644 index 00000000..8e892e5d --- /dev/null +++ b/packages/hooks-registry/test/registry.test.ts @@ -0,0 +1,457 @@ +import { describe, it, expect, beforeEach, vi, afterEach } from 'vitest'; +import { + registerScaffoldHook, + clearScaffoldHooks, + getScaffoldHooks, + getScaffoldHooksForType, + runScaffoldHooks, + resetForTesting, +} from '../src/registry'; +import type { ScaffoldHookType, ScaffoldHookContext } from '../src/hooks'; + +describe('Scaffold Hook Registry', () => { + beforeEach(() => { + resetForTesting(); + }); + + afterEach(() => { + resetForTesting(); + }); + + describe('registerScaffoldHook', () => { + it('registers a hook successfully', () => { + const hook = { + type: 'preScaffold' as ScaffoldHookType, + name: 'test-hook', + handler: vi.fn(), + }; + registerScaffoldHook(hook); + const hooks = getScaffoldHooks(); + expect(hooks).toHaveLength(1); + expect(hooks[0].name).toBe('test-hook'); + }); + + it('registers multiple hooks', () => { + registerScaffoldHook({ + type: 'preInstall', + name: 'hook-1', + handler: vi.fn(), + }); + registerScaffoldHook({ + type: 'postInstall', + name: 'hook-2', + handler: vi.fn(), + }); + expect(getScaffoldHooks()).toHaveLength(2); + }); + + it('replaces hook with same name', () => { + registerScaffoldHook({ + type: 'preScaffold', + name: 'same-name', + priority: 10, + handler: vi.fn(), + }); + registerScaffoldHook({ + type: 'preScaffold', + name: 'same-name', + priority: 20, + handler: vi.fn(), + }); + const hooks = getScaffoldHooks(); + expect(hooks).toHaveLength(1); + expect(hooks[0].priority).toBe(20); + }); + + it('applies default priority', () => { + registerScaffoldHook({ + type: 'preScaffold', + name: 'no-priority', + handler: vi.fn(), + }); + const hooks = getScaffoldHooks(); + expect(hooks[0].priority).toBe(50); + }); + + it('applies default critical flag', () => { + registerScaffoldHook({ + type: 'preScaffold', + name: 'no-critical', + handler: vi.fn(), + }); + const hooks = getScaffoldHooks(); + expect(hooks[0].critical).toBe(false); + }); + }); + + describe('getScaffoldHooks', () => { + it('returns hooks sorted by priority (ascending)', () => { + registerScaffoldHook({ + type: 'preScaffold', + name: 'low-priority', + priority: 100, + handler: vi.fn(), + }); + registerScaffoldHook({ + type: 'preScaffold', + name: 'high-priority', + priority: 10, + handler: vi.fn(), + }); + registerScaffoldHook({ + type: 'preScaffold', + name: 'medium-priority', + priority: 50, + handler: vi.fn(), + }); + + const hooks = getScaffoldHooks(); + expect(hooks[0].name).toBe('high-priority'); + expect(hooks[1].name).toBe('medium-priority'); + expect(hooks[2].name).toBe('low-priority'); + }); + + it('returns empty array when no hooks registered', () => { + const hooks = getScaffoldHooks(); + expect(hooks).toHaveLength(0); + }); + + it('returns a new array each call (no mutation risk)', () => { + const hooks1 = getScaffoldHooks(); + const hooks2 = getScaffoldHooks(); + expect(hooks1).not.toBe(hooks2); + }); + }); + + describe('getScaffoldHooksForType', () => { + it('filters hooks by type', () => { + registerScaffoldHook({ + type: 'preScaffold', + name: 'pre-scaffold-hook', + handler: vi.fn(), + }); + registerScaffoldHook({ + type: 'preInstall', + name: 'pre-install-hook', + handler: vi.fn(), + }); + registerScaffoldHook({ + type: 'postInstall', + name: 'post-install-hook', + handler: vi.fn(), + }); + + const preScaffold = getScaffoldHooksForType('preScaffold'); + expect(preScaffold).toHaveLength(1); + expect(preScaffold[0].name).toBe('pre-scaffold-hook'); + + const preInstall = getScaffoldHooksForType('preInstall'); + expect(preInstall).toHaveLength(1); + expect(preInstall[0].name).toBe('pre-install-hook'); + }); + + it('returns empty array when no hooks match type', () => { + registerScaffoldHook({ + type: 'preScaffold', + name: 'pre-scaffold-hook', + handler: vi.fn(), + }); + + const postInstall = getScaffoldHooksForType('postInstall'); + expect(postInstall).toHaveLength(0); + }); + + it('returns hooks sorted by priority', () => { + registerScaffoldHook({ + type: 'preInstall', + name: 'low', + priority: 100, + handler: vi.fn(), + }); + registerScaffoldHook({ + type: 'preInstall', + name: 'high', + priority: 10, + handler: vi.fn(), + }); + + const hooks = getScaffoldHooksForType('preInstall'); + expect(hooks[0].name).toBe('high'); + expect(hooks[1].name).toBe('low'); + }); + }); + + describe('clearScaffoldHooks', () => { + it('removes all registered hooks', () => { + registerScaffoldHook({ + type: 'preScaffold', + name: 'hook-1', + handler: vi.fn(), + }); + registerScaffoldHook({ + type: 'preInstall', + name: 'hook-2', + handler: vi.fn(), + }); + + clearScaffoldHooks(); + expect(getScaffoldHooks()).toHaveLength(0); + }); + }); + + describe('resetForTesting', () => { + it('clears all hooks', () => { + registerScaffoldHook({ + type: 'preScaffold', + name: 'test-hook', + handler: vi.fn(), + }); + + resetForTesting(); + expect(getScaffoldHooks()).toHaveLength(0); + }); + }); + + describe('runScaffoldHooks', () => { + it('executes hooks in priority order', async () => { + const order: string[] = []; + const context = {} as ScaffoldHookContext; + + registerScaffoldHook({ + type: 'preScaffold', + name: 'first', + priority: 50, + handler: async () => { + order.push('first'); + }, + }); + registerScaffoldHook({ + type: 'preScaffold', + name: 'second', + priority: 100, + handler: async () => { + order.push('second'); + }, + }); + registerScaffoldHook({ + type: 'preScaffold', + name: 'third', + priority: 25, + handler: async () => { + order.push('third'); + }, + }); + + await runScaffoldHooks('preScaffold', context); + expect(order).toEqual(['third', 'first', 'second']); + }); + + it('passes context to hook handlers', async () => { + let receivedContext: ScaffoldHookContext | undefined; + const context: ScaffoldHookContext = { + targetDir: '/test/path', + projectName: 'test-project', + siteTitle: 'Test Site', + themeId: 'default', + packageJson: {}, + dependencyMode: 'workspace', + }; + + registerScaffoldHook({ + type: 'preScaffold', + name: 'context-checker', + handler: async (ctx) => { + receivedContext = ctx; + }, + }); + + await runScaffoldHooks('preScaffold', context); + expect(receivedContext?.targetDir).toBe('/test/path'); + expect(receivedContext?.projectName).toBe('test-project'); + }); + + it('continues after non-critical hook failure', async () => { + const order: string[] = []; + const context = {} as ScaffoldHookContext; + + registerScaffoldHook({ + type: 'preScaffold', + name: 'failing', + critical: false, + handler: async () => { + order.push('failing'); + throw new Error('Non-critical failure'); + }, + }); + registerScaffoldHook({ + type: 'preScaffold', + name: 'after', + handler: async () => { + order.push('after'); + }, + }); + + await runScaffoldHooks('preScaffold', context); + expect(order).toEqual(['failing', 'after']); + }); + + it('throws on critical hook failure', async () => { + const context = {} as ScaffoldHookContext; + + registerScaffoldHook({ + type: 'preScaffold', + name: 'critical-failing', + critical: true, + handler: async () => { + throw new Error('Critical failure'); + }, + }); + registerScaffoldHook({ + type: 'preScaffold', + name: 'should-not-run', + handler: async () => {}, + }); + + await expect(runScaffoldHooks('preScaffold', context)).rejects.toThrow( + 'Critical scaffold hook "critical-failing" failed: Critical failure' + ); + }); + + it('stops execution on critical hook failure', async () => { + const order: string[] = []; + const context = {} as ScaffoldHookContext; + + registerScaffoldHook({ + type: 'preScaffold', + name: 'run-first', + priority: 10, + handler: async () => { + order.push('run-first'); + }, + }); + registerScaffoldHook({ + type: 'preScaffold', + name: 'critical', + priority: 20, + critical: true, + handler: async () => { + order.push('critical'); + throw new Error('Critical failure'); + }, + }); + registerScaffoldHook({ + type: 'preScaffold', + name: 'should-not-run', + priority: 30, + handler: async () => { + order.push('should-not-run'); + }, + }); + + await expect(runScaffoldHooks('preScaffold', context)).rejects.toThrow(); + expect(order).toEqual(['run-first', 'critical']); + }); + + it('ignores hooks of other types', async () => { + const order: string[] = []; + const context = {} as ScaffoldHookContext; + + registerScaffoldHook({ + type: 'preInstall', + name: 'pre-install-hook', + handler: async () => { + order.push('pre-install'); + }, + }); + registerScaffoldHook({ + type: 'postInstall', + name: 'post-install-hook', + handler: async () => { + order.push('post-install'); + }, + }); + registerScaffoldHook({ + type: 'postScaffold', + name: 'post-scaffold-hook', + handler: async () => { + order.push('post-scaffold'); + }, + }); + + await runScaffoldHooks('preScaffold', context); + expect(order).toHaveLength(0); + }); + }); + + describe('singleton behavior', () => { + // The actual Symbol used by the registry + const REGISTRY_KEY = Symbol.for('@stackwright/hooks-registry:hooks'); + type HookEntry = { + type: string; + name: string; + priority: number; + critical: boolean; + handler: Function; + }; + + it('hooks persist in globalThis via Symbol.for', () => { + // Register a hook + registerScaffoldHook({ + type: 'preScaffold', + name: 'global-test-hook', + priority: 25, + handler: vi.fn(), + }); + + // Access the actual global registry + const global = globalThis as typeof globalThis & { + [REGISTRY_KEY]?: Map; + }; + + expect(global[REGISTRY_KEY]).toBeDefined(); + expect(global[REGISTRY_KEY]!.has('global-test-hook')).toBe(true); + expect(global[REGISTRY_KEY]!.get('global-test-hook')!.priority).toBe(25); + }); + + it('resetForTesting clears global registry', () => { + // Register a hook + registerScaffoldHook({ + type: 'preInstall', + name: 'should-be-cleared', + handler: vi.fn(), + }); + + // Verify it's in the global registry + const global = globalThis as typeof globalThis & { + [REGISTRY_KEY]?: Map; + }; + expect(global[REGISTRY_KEY]!.size).toBeGreaterThan(0); + + // Reset + resetForTesting(); + + // Verify global registry is cleared + expect(global[REGISTRY_KEY]!.size).toBe(0); + }); + + it('hooks survive simulated module boundary', () => { + // This simulates two "different" modules accessing the same registry + // Module A registers a hook + registerScaffoldHook({ + type: 'postScaffold', + name: 'module-a-hook', + handler: vi.fn(), + }); + + // Module B would see the same registry + // Verify by checking global directly + const global = globalThis as typeof globalThis & { + [REGISTRY_KEY]?: Map; + }; + + // The hook registered above should be visible + const hooks = Array.from(global[REGISTRY_KEY]!.values()); + expect(hooks.some((h) => h.name === 'module-a-hook')).toBe(true); + }); + }); +}); diff --git a/packages/hooks-registry/tsconfig.json b/packages/hooks-registry/tsconfig.json new file mode 100644 index 00000000..491dc9af --- /dev/null +++ b/packages/hooks-registry/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "./dist", + "rootDir": "./src" + }, + "include": ["src"] +} \ No newline at end of file diff --git a/packages/hooks-registry/tsup.config.ts b/packages/hooks-registry/tsup.config.ts new file mode 100644 index 00000000..f69c8f61 --- /dev/null +++ b/packages/hooks-registry/tsup.config.ts @@ -0,0 +1,10 @@ +import { defineConfig } from 'tsup'; + +export default defineConfig({ + entry: ['src/index.ts'], + format: ['esm', 'cjs'], + dts: true, + splitting: false, + sourcemap: true, + clean: true, +}); diff --git a/packages/hooks-registry/vitest.config.ts b/packages/hooks-registry/vitest.config.ts new file mode 100644 index 00000000..fa69665c --- /dev/null +++ b/packages/hooks-registry/vitest.config.ts @@ -0,0 +1,8 @@ +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + test: { + include: ['test/**/*.test.ts'], + environment: 'node', + }, +}); diff --git a/packages/launch-stackwright/src/index.ts b/packages/launch-stackwright/src/index.ts index 32e9729e..546ee6ef 100644 --- a/packages/launch-stackwright/src/index.ts +++ b/packages/launch-stackwright/src/index.ts @@ -50,7 +50,7 @@ function registerLaunchHooks(): void { }); } -function setupOtterRaft(targetDir: string): void { +function _setupOtterRaft(targetDir: string): void { console.log(chalk.cyan('\n🦦 Installing the otter raft...\n')); try { diff --git a/packages/sbom-generator/src/formats/spdx.ts b/packages/sbom-generator/src/formats/spdx.ts index 91a083c1..3a73b9bb 100644 --- a/packages/sbom-generator/src/formats/spdx.ts +++ b/packages/sbom-generator/src/formats/spdx.ts @@ -55,14 +55,14 @@ export interface SPDXRelationship { * Generate SPDX document from dependencies */ export function generateSPDX( - project: StackwrightProjectInfo, + _project: StackwrightProjectInfo, dependencies: NormalizedDependency[], options: { includeDevDependencies?: boolean; namespace?: string; } = {} ): SPDXDocument { - const { namespace = `https://stackwright.dev/spdx/${project.name}` } = options; + const { namespace = `https://stackwright.dev/spdx/${_project.name}` } = options; const docId = `SPDXRef-DOCUMENT-${randomUUID().split('-')[0]}`; const timestamp = new Date().toISOString(); @@ -70,14 +70,14 @@ export function generateSPDX( ? dependencies : dependencies.filter((d) => !d.isDev); - const packages: SPDXPackage[] = filteredDeps.map((dep) => createSPDXPackage(dep, project)); + const packages: SPDXPackage[] = filteredDeps.map((dep) => createSPDXPackage(dep, _project)); const relationships: SPDXRelationship[] = createRelationships(docId, packages); return { spdxVersion: 'SPDX-2.3', dataLicense: 'CC0-1.0', SPDXID: docId, - name: `${project.name}@${project.version}`, + name: `${_project.name}@${_project.version}`, documentNamespace: `${namespace}/${timestamp}`, creationInfo: { created: timestamp, @@ -93,7 +93,7 @@ export function generateSPDX( */ function createSPDXPackage( dep: NormalizedDependency, - project: StackwrightProjectInfo + _project: StackwrightProjectInfo ): SPDXPackage { const packageId = `SPDXRef-Package-${dep.name.replace(/[^a-zA-Z0-9]/g, '-')}`; const purl = npmPURL(dep.name, dep.version); diff --git a/packages/sbom-generator/src/generator.ts b/packages/sbom-generator/src/generator.ts index 5ba285c5..7e178b84 100644 --- a/packages/sbom-generator/src/generator.ts +++ b/packages/sbom-generator/src/generator.ts @@ -3,8 +3,8 @@ * @package @stackwright/sbom-generator */ -import { mkdir, writeFile, access } from 'node:fs/promises'; -import { resolve, dirname } from 'node:path'; +import { mkdir, writeFile } from 'node:fs/promises'; +import { resolve } from 'node:path'; import { analyzeDependencies } from './analyzer'; import { StackwrightProjectInfo, NormalizedDependency } from './types'; import { generateSPDX, toSPDXJSON, toSPDXTV, SPDXDocument } from './formats/spdx'; diff --git a/packages/sbom-generator/src/utils/purl.ts b/packages/sbom-generator/src/utils/purl.ts index a148e0d2..e8e1d023 100644 --- a/packages/sbom-generator/src/utils/purl.ts +++ b/packages/sbom-generator/src/utils/purl.ts @@ -123,7 +123,6 @@ export function parsePURL(purl: string): { let remaining = parts.slice(1); let namespace: string | undefined; - let name: string; let version: string | undefined; let qualifiers: Record | undefined; let subpath: string | undefined; @@ -161,7 +160,7 @@ export function parsePURL(purl: string): { remaining = remaining.slice(1); } - name = remaining.join('/'); + const name = remaining.join('/'); return { type, namespace, name, version, qualifiers, subpath }; } catch { diff --git a/packages/scaffold-core/package.json b/packages/scaffold-core/package.json index 4d49753c..039a5716 100644 --- a/packages/scaffold-core/package.json +++ b/packages/scaffold-core/package.json @@ -25,6 +25,9 @@ "dev": "tsup --watch", "test": "vitest run" }, + "dependencies": { + "@stackwright/hooks-registry": "workspace:*" + }, "devDependencies": { "@types/node": "^20.0.0", "tsup": "^8.5.0", diff --git a/packages/scaffold-core/src/index.ts b/packages/scaffold-core/src/index.ts index f553eb42..bf28622e 100644 --- a/packages/scaffold-core/src/index.ts +++ b/packages/scaffold-core/src/index.ts @@ -2,22 +2,23 @@ * @stackwright/scaffold-core * * Hook system for extensible scaffold processing. + * Re-exports from @stackwright/hooks-registry for backward compatibility. * Pro packages register hooks that run at lifecycle points. */ -// Types +// Re-export all types from the shared registry export type { - ScaffoldHookType, ScaffoldHook, - ScaffoldHookOptions, + ScaffoldHookType, ScaffoldHookContext, -} from './types/hooks'; +} from '@stackwright/hooks-registry'; -// Registry functions +// Re-export all registry functions export { registerScaffoldHook, getScaffoldHooks, getScaffoldHooksForType, clearScaffoldHooks, + resetForTesting, runScaffoldHooks, -} from './registry'; +} from '@stackwright/hooks-registry'; diff --git a/packages/scaffold-core/src/registry.ts b/packages/scaffold-core/src/registry.ts deleted file mode 100644 index 16577f78..00000000 --- a/packages/scaffold-core/src/registry.ts +++ /dev/null @@ -1,84 +0,0 @@ -/** - * Scaffold Hook Registry - * - * Singleton registry for scaffold hooks. Pro packages auto-register by importing. - */ - -import type { ScaffoldHook, ScaffoldHookContext, ScaffoldHookType } from './types/hooks'; - -// Internal storage -let hooks: ScaffoldHook[] = []; - -/** - * Register a hook to run during scaffolding - * - * @example - * import { registerScaffoldHook } from '@stackwright/scaffold-core'; - * - * registerScaffoldHook({ - * type: 'preInstall', - * name: 'enterprise-license', - * priority: 10, - * handler: addEnterpriseLicense, - * }); - */ -export function registerScaffoldHook(hook: ScaffoldHook): void { - hooks.push({ - ...hook, - priority: hook.priority ?? 50, - critical: hook.critical ?? false, - }); - // Sort by priority (lower = runs first) - hooks.sort((a, b) => a.priority! - b.priority!); -} - -/** - * Get all registered hooks - */ -export function getScaffoldHooks(): ReadonlyArray { - return [...hooks]; -} - -/** - * Get hooks for a specific lifecycle point - */ -export function getScaffoldHooksForType(type: ScaffoldHookType): ReadonlyArray { - return hooks.filter((h) => h.type === type); -} - -/** - * Clear all registered hooks (useful for testing) - */ -export function clearScaffoldHooks(): void { - hooks = []; -} - -/** - * Run all hooks of a given type - * - * @param type - Lifecycle point - * @param context - Context passed to hooks - * @throws If a critical hook fails - */ -export async function runScaffoldHooks( - type: ScaffoldHookType, - context: ScaffoldHookContext -): Promise { - const relevantHooks = getScaffoldHooksForType(type); - - for (const hook of relevantHooks) { - try { - await hook.handler(context); - } catch (error) { - if (hook.critical) { - throw new Error( - `Critical scaffold hook "${hook.name}" failed: ${(error as Error).message}` - ); - } - // Non-critical: warn but continue - console.warn( - `[Scaffold Hook] Non-critical hook "${hook.name}" failed: ${(error as Error).message}` - ); - } - } -} diff --git a/packages/scaffold-core/test/registry.test.ts b/packages/scaffold-core/test/registry.test.ts index a3393532..f73e2982 100644 --- a/packages/scaffold-core/test/registry.test.ts +++ b/packages/scaffold-core/test/registry.test.ts @@ -5,7 +5,7 @@ import { getScaffoldHooks, getScaffoldHooksForType, runScaffoldHooks, -} from '../src/registry'; +} from '../src/index'; describe('Scaffold Hook Registry', () => { beforeEach(() => { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 22c2262c..d501e0ea 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -23,7 +23,7 @@ overrides: path-to-regexp: '>=8.4.0' diff: '>=4.0.4' tmp: '>=0.2.4' - undici: '>=7.24.0' + undici: ^7.0.0 tar: '>=6.2.1' flatted: '>=3.4.2' next: '>=16.1.7' @@ -37,34 +37,34 @@ importers: devDependencies: '@changesets/cli': specifier: ^2.30.0 - version: 2.30.0(@types/node@24.11.0) + version: 2.30.0(@types/node@24.12.2) '@commitlint/cli': specifier: ^20.5.0 - version: 20.5.0(@types/node@24.11.0)(conventional-commits-parser@6.4.0)(typescript@5.9.3) + version: 20.5.0(@types/node@24.12.2)(conventional-commits-parser@6.4.0)(typescript@5.9.3) '@commitlint/config-conventional': specifier: ^20.5.0 version: 20.5.0 '@typescript-eslint/eslint-plugin': specifier: ^8.56.1 - version: 8.58.1(@typescript-eslint/parser@8.58.1(eslint@9.39.3(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.3(jiti@2.6.1))(typescript@5.9.3) + version: 8.58.1(@typescript-eslint/parser@8.58.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3) '@typescript-eslint/parser': specifier: ^8.56.1 - version: 8.58.1(eslint@9.39.3(jiti@2.6.1))(typescript@5.9.3) + version: 8.58.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3) '@vitest/coverage-v8': specifier: ^4.1.3 - version: 4.1.3(vitest@4.1.3) + version: 4.1.4(vitest@4.1.4) eslint: specifier: ^9.39.3 - version: 9.39.3(jiti@2.6.1) + version: 9.39.4(jiti@2.6.1) eslint-config-prettier: specifier: ^10.1.8 - version: 10.1.8(eslint@9.39.3(jiti@2.6.1)) + version: 10.1.8(eslint@9.39.4(jiti@2.6.1)) eslint-plugin-react: specifier: ^7.37.5 - version: 7.37.5(eslint@9.39.3(jiti@2.6.1)) + version: 7.37.5(eslint@9.39.4(jiti@2.6.1)) eslint-plugin-react-hooks: specifier: ^7.0.1 - version: 7.0.1(eslint@9.39.3(jiti@2.6.1)) + version: 7.0.1(eslint@9.39.4(jiti@2.6.1)) husky: specifier: ^9.1.7 version: 9.1.7 @@ -76,37 +76,43 @@ importers: version: 1.59.1 prettier: specifier: ^3.8.1 - version: 3.8.1 + version: 3.8.2 turbo: specifier: ^2.9.3 - version: 2.9.5 + version: 2.9.6 vitest: specifier: ^4.1.3 - version: 4.1.3(@opentelemetry/api@1.9.1)(@types/node@24.11.0)(@vitest/coverage-v8@4.1.3)(jsdom@29.0.2)(vite@7.3.2(@types/node@24.11.0)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.4(@opentelemetry/api@1.9.1)(@types/node@24.12.2)(@vitest/coverage-v8@4.1.4)(@vitest/ui@4.1.4)(jsdom@29.0.2)(vite@7.3.2(@types/node@24.12.2)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.3)) ../www: dependencies: + '@stackwright/cli': + specifier: 0.7.0-alpha.11 + version: 0.7.0-alpha.11(@types/node@24.12.2)(playwright@1.59.1)(react@19.2.4) '@stackwright/core': - specifier: latest + specifier: 0.7.0-alpha.7 version: 0.7.0-alpha.7(react-dom@19.2.4(react@19.2.4))(react@19.2.4) '@stackwright/icons': - specifier: latest + specifier: 0.4.0 version: 0.4.0(react@19.2.4) + '@stackwright/mcp': + specifier: 0.3.0-alpha.11 + version: 0.3.0-alpha.11(@types/node@24.12.2)(react@19.2.4) '@stackwright/nextjs': - specifier: latest - version: 0.3.1-alpha.7(next@16.2.2(@babel/core@7.29.0)(@opentelemetry/api@1.9.1)(@playwright/test@1.59.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + specifier: 0.3.1-alpha.7 + version: 0.3.1-alpha.7(next@16.2.3(@babel/core@7.29.0)(@opentelemetry/api@1.9.1)(@playwright/test@1.59.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react-dom@19.2.4(react@19.2.4))(react@19.2.4) '@stackwright/otters': specifier: latest version: 0.2.0 '@stackwright/ui-shadcn': - specifier: latest + specifier: 0.1.0 version: 0.1.0(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) js-yaml: specifier: ^4.1.1 version: 4.1.1 next: specifier: '>=16.1.7' - version: 16.2.2(@babel/core@7.29.0)(@opentelemetry/api@1.9.1)(@playwright/test@1.59.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + version: 16.2.3(@babel/core@7.29.0)(@opentelemetry/api@1.9.1)(@playwright/test@1.59.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) react: specifier: 19.2.4 version: 19.2.4 @@ -122,7 +128,7 @@ importers: version: 4.0.9 '@types/node': specifier: ^24.1.0 - version: 24.11.0 + version: 24.12.2 '@types/react': specifier: ^19.2.14 version: 19.2.14 @@ -130,11 +136,11 @@ importers: specifier: ^19.2.3 version: 19.2.3(@types/react@19.2.14) eslint: - specifier: ^9.39.2 - version: 9.39.3(jiti@2.6.1) + specifier: ^9.39.4 + version: 9.39.4(jiti@2.6.1) eslint-config-next: - specifier: ^16.1.6 - version: 16.2.2(@typescript-eslint/parser@8.58.1(eslint@9.39.3(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.3(jiti@2.6.1))(typescript@5.9.3) + specifier: ^16.2.3 + version: 16.2.3(@typescript-eslint/parser@8.58.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3) typescript: specifier: ^5.8.3 version: 5.9.3 @@ -167,7 +173,7 @@ importers: version: 2.1.1 fuse.js: specifier: ^7.1.0 - version: 7.1.0 + version: 7.3.0 js-yaml: specifier: ^4.1.1 version: 4.1.1 @@ -176,7 +182,7 @@ importers: version: 0.471.2(react@19.2.4) next: specifier: '>=16.1.7' - version: 16.2.2(@babel/core@7.29.0)(@opentelemetry/api@1.9.1)(@playwright/test@1.59.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + version: 16.2.3(@babel/core@7.29.0)(@opentelemetry/api@1.9.1)(@playwright/test@1.59.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) react: specifier: 19.2.4 version: 19.2.4 @@ -189,7 +195,7 @@ importers: devDependencies: '@playwright/browser-chromium': specifier: ^1.58.2 - version: 1.58.2 + version: 1.59.1 '@stackwright/build-scripts': specifier: workspace:* version: link:../../packages/build-scripts @@ -198,7 +204,7 @@ importers: version: 4.0.9 '@types/node': specifier: ^24.1.0 - version: 24.11.0 + version: 24.12.2 '@types/react': specifier: ^19.2.14 version: 19.2.14 @@ -207,13 +213,13 @@ importers: version: 19.2.3(@types/react@19.2.14) eslint: specifier: ^9.39.2 - version: 9.39.3(jiti@2.6.1) + version: 9.39.4(jiti@2.6.1) eslint-config-next: specifier: ^16.2.2 - version: 16.2.2(@typescript-eslint/parser@8.58.1(eslint@9.39.3(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.3(jiti@2.6.1))(typescript@5.9.3) + version: 16.2.3(@typescript-eslint/parser@8.58.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3) playwright: specifier: ^1.58.2 - version: 1.58.2 + version: 1.59.1 typescript: specifier: ^5.8.3 version: 5.9.3 @@ -235,22 +241,22 @@ importers: version: 4.0.9 '@types/node': specifier: ^24.1.0 - version: 24.10.13 + version: 24.12.2 tsup: specifier: ^8.5.0 - version: 8.5.0(@swc/core@1.15.18)(jiti@2.6.1)(postcss@8.5.9)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.3) + version: 8.5.1(@swc/core@1.15.24)(jiti@2.6.1)(postcss@8.5.9)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.3) typescript: specifier: ^5.8.3 version: 5.9.3 vitest: specifier: ^4.1.3 - version: 4.1.3(@opentelemetry/api@1.9.1)(@types/node@24.10.13)(@vitest/coverage-v8@4.1.3)(jsdom@29.0.2)(vite@7.3.2(@types/node@24.10.13)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.4(@opentelemetry/api@1.9.1)(@types/node@24.12.2)(@vitest/coverage-v8@4.1.4)(@vitest/ui@4.1.4)(jsdom@29.0.2)(vite@7.3.2(@types/node@24.12.2)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.3)) packages/cli: dependencies: '@inquirer/prompts': specifier: ^8.3.0 - version: 8.3.0(@types/node@24.2.1) + version: 8.4.1(@types/node@24.12.2) '@stackwright/build-scripts': specifier: workspace:* version: link:../build-scripts @@ -274,13 +280,13 @@ importers: version: 14.0.3 fs-extra: specifier: ^11.3 - version: 11.3.1 + version: 11.3.4 js-yaml: specifier: ^4.1.0 version: 4.1.1 playwright: specifier: ^1.52.0 - version: 1.58.2 + version: 1.59.1 zod: specifier: ^4.3.6 version: 4.3.6 @@ -293,31 +299,31 @@ importers: version: 4.0.9 '@types/node': specifier: ^24.1.0 - version: 24.2.1 + version: 24.12.2 tsup: specifier: ^8.5.0 - version: 8.5.0(@swc/core@1.15.18)(jiti@2.6.1)(postcss@8.5.9)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.3) + version: 8.5.1(@swc/core@1.15.24)(jiti@2.6.1)(postcss@8.5.9)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.3) typescript: specifier: ^5.8.3 version: 5.9.3 vitest: specifier: ^4.1.3 - version: 4.1.3(@opentelemetry/api@1.9.1)(@types/node@24.2.1)(@vitest/coverage-v8@4.1.3)(@vitest/ui@4.0.18)(jsdom@29.0.2)(vite@7.3.2(@types/node@24.2.1)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.4(@opentelemetry/api@1.9.1)(@types/node@24.12.2)(@vitest/coverage-v8@4.1.4)(@vitest/ui@4.1.4)(jsdom@29.0.2)(vite@7.3.2(@types/node@24.12.2)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.3)) packages/collections: devDependencies: '@types/node': specifier: ^24.1.0 - version: 24.11.0 + version: 24.12.2 tsup: specifier: ^8.5.0 - version: 8.5.0(@swc/core@1.15.18)(jiti@2.6.1)(postcss@8.5.9)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.3) + version: 8.5.1(@swc/core@1.15.24)(jiti@2.6.1)(postcss@8.5.9)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.3) typescript: specifier: ^5.8.3 version: 5.9.3 vitest: specifier: ^4.1.3 - version: 4.1.3(@opentelemetry/api@1.9.1)(@types/node@24.11.0)(@vitest/coverage-v8@4.1.3)(jsdom@29.0.2)(vite@7.3.2(@types/node@24.11.0)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.4(@opentelemetry/api@1.9.1)(@types/node@24.12.2)(@vitest/coverage-v8@4.1.4)(@vitest/ui@4.1.4)(jsdom@29.0.2)(vite@7.3.2(@types/node@24.12.2)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.3)) packages/core: dependencies: @@ -332,7 +338,7 @@ importers: version: link:../types fuse.js: specifier: ^7.1.0 - version: 7.1.0 + version: 7.3.0 js-yaml: specifier: ^4.1.0 version: 4.1.1 @@ -348,16 +354,16 @@ importers: devDependencies: '@swc/core': specifier: ^1.15.18 - version: 1.15.18 + version: 1.15.24 '@testing-library/jest-dom': specifier: ^6.6 - version: 6.6.4 + version: 6.9.1 '@testing-library/react': specifier: ^16.3 - version: 16.3.0(@testing-library/dom@10.4.1)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + version: 16.3.2(@testing-library/dom@10.4.1)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) '@types/node': specifier: ^24.1.0 - version: 24.2.1 + version: 24.12.2 '@types/prismjs': specifier: ^1.26.6 version: 1.26.6 @@ -369,7 +375,7 @@ importers: version: 19.2.3(@types/react@19.2.14) '@vitest/ui': specifier: ^4.0.18 - version: 4.0.18(vitest@4.1.3) + version: 4.1.4(vitest@4.1.4) jsdom: specifier: ^29.0.2 version: 29.0.2 @@ -381,13 +387,13 @@ importers: version: 19.2.4(react@19.2.4) tsup: specifier: ^8.5.0 - version: 8.5.0(@swc/core@1.15.18)(jiti@2.6.1)(postcss@8.5.9)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.3) + version: 8.5.1(@swc/core@1.15.24)(jiti@2.6.1)(postcss@8.5.9)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.3) typescript: specifier: ^5.8.3 version: 5.9.3 vitest: specifier: ^4.1.3 - version: 4.1.3(@opentelemetry/api@1.9.1)(@types/node@24.2.1)(@vitest/coverage-v8@4.1.3)(@vitest/ui@4.0.18)(jsdom@29.0.2)(vite@7.3.2(@types/node@24.2.1)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.4(@opentelemetry/api@1.9.1)(@types/node@24.12.2)(@vitest/coverage-v8@4.1.4)(@vitest/ui@4.1.4)(jsdom@29.0.2)(vite@7.3.2(@types/node@24.12.2)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.3)) packages/e2e: devDependencies: @@ -399,20 +405,35 @@ importers: version: 1.59.1 lighthouse: specifier: ^13.0.3 - version: 13.0.3 + version: 13.1.0 + + packages/hooks-registry: + devDependencies: + '@types/node': + specifier: ^24.1.0 + version: 24.12.2 + tsup: + specifier: ^8.5.0 + version: 8.5.1(@swc/core@1.15.24)(jiti@2.6.1)(postcss@8.5.9)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.3) + typescript: + specifier: ^5.8.3 + version: 5.9.3 + vitest: + specifier: ^4.1.3 + version: 4.1.4(@opentelemetry/api@1.9.1)(@types/node@24.12.2)(@vitest/coverage-v8@4.1.4)(@vitest/ui@4.1.4)(jsdom@29.0.2)(vite@7.3.2(@types/node@24.12.2)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.3)) packages/icons: dependencies: lucide-react: specifier: ^1.7.0 - version: 1.7.0(react@19.2.4) + version: 1.8.0(react@19.2.4) devDependencies: '@testing-library/jest-dom': specifier: ^6.6 version: 6.9.1 '@testing-library/react': specifier: ^16.3 - version: 16.3.0(@testing-library/dom@10.4.1)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + version: 16.3.2(@testing-library/dom@10.4.1)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) '@types/react': specifier: ^19.2.14 version: 19.2.14 @@ -430,13 +451,13 @@ importers: version: 19.2.4(react@19.2.4) tsup: specifier: ^8.5.0 - version: 8.5.0(@swc/core@1.15.18)(jiti@2.6.1)(postcss@8.5.9)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.3) + version: 8.5.1(@swc/core@1.15.24)(jiti@2.6.1)(postcss@8.5.9)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.3) typescript: specifier: ^5.8.3 version: 5.9.3 vitest: specifier: ^4.1.3 - version: 4.1.3(@opentelemetry/api@1.9.1)(@types/node@24.11.0)(@vitest/coverage-v8@4.1.3)(jsdom@29.0.2)(vite@7.3.2(@types/node@24.11.0)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.4(@opentelemetry/api@1.9.1)(@types/node@24.12.2)(@vitest/coverage-v8@4.1.4)(@vitest/ui@4.1.4)(jsdom@29.0.2)(vite@7.3.2(@types/node@24.12.2)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.3)) packages/launch-stackwright: dependencies: @@ -467,19 +488,19 @@ importers: version: 11.0.4 '@types/node': specifier: ^24.1.0 - version: 24.11.0 + version: 24.12.2 '@types/prompts': specifier: ^2.4.9 version: 2.4.9 tsup: specifier: ^8.5.0 - version: 8.5.0(@swc/core@1.15.18)(jiti@2.6.1)(postcss@8.5.9)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.3) + version: 8.5.1(@swc/core@1.15.24)(jiti@2.6.1)(postcss@8.5.9)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.3) typescript: specifier: ^5.8.3 version: 5.9.3 vitest: specifier: ^4.1.3 - version: 4.1.3(@opentelemetry/api@1.9.1)(@types/node@24.11.0)(@vitest/coverage-v8@4.1.3)(jsdom@29.0.2)(vite@7.3.2(@types/node@24.11.0)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.4(@opentelemetry/api@1.9.1)(@types/node@24.12.2)(@vitest/coverage-v8@4.1.4)(@vitest/ui@4.1.4)(jsdom@29.0.2)(vite@7.3.2(@types/node@24.12.2)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.3)) packages/maplibre: dependencies: @@ -488,7 +509,7 @@ importers: version: 4.7.1 react-map-gl: specifier: ^8.1.0 - version: 8.1.0(mapbox-gl@3.20.0)(maplibre-gl@4.7.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + version: 8.1.1(maplibre-gl@4.7.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) devDependencies: '@stackwright/core': specifier: workspace:* @@ -498,7 +519,7 @@ importers: version: 6.9.1 '@testing-library/react': specifier: ^16.3 - version: 16.3.0(@testing-library/dom@10.4.1)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + version: 16.3.2(@testing-library/dom@10.4.1)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) '@types/react': specifier: ^19.2.14 version: 19.2.14 @@ -516,19 +537,19 @@ importers: version: 19.2.4(react@19.2.4) tsup: specifier: ^8.5.0 - version: 8.5.0(@swc/core@1.15.18)(jiti@2.6.1)(postcss@8.5.9)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.3) + version: 8.5.1(@swc/core@1.15.24)(jiti@2.6.1)(postcss@8.5.9)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.3) typescript: specifier: ^5.8.3 version: 5.9.3 vitest: specifier: ^4.1.3 - version: 4.1.3(@opentelemetry/api@1.9.1)(@types/node@24.11.0)(@vitest/coverage-v8@4.1.3)(jsdom@28.1.0)(vite@7.3.2(@types/node@24.11.0)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.4(@opentelemetry/api@1.9.1)(@types/node@24.12.2)(@vitest/coverage-v8@4.1.4)(@vitest/ui@4.1.4)(jsdom@28.1.0)(vite@7.3.2(@types/node@24.12.2)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.3)) packages/mcp: dependencies: '@modelcontextprotocol/sdk': specifier: ^1.27.0 - version: 1.27.1(zod@4.3.6) + version: 1.29.0(zod@4.3.6) '@stackwright/cli': specifier: workspace:* version: link:../cli @@ -537,20 +558,20 @@ importers: version: link:../types playwright: specifier: ^1.52.0 - version: 1.58.2 + version: 1.59.1 zod: specifier: ^4.3.6 version: 4.3.6 devDependencies: '@types/node': specifier: ^24.1.0 - version: 24.10.13 + version: 24.12.2 fs-extra: specifier: ^11.3.4 version: 11.3.4 tsup: specifier: ^8.5.0 - version: 8.5.0(@swc/core@1.15.18)(jiti@2.6.1)(postcss@8.5.9)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.3) + version: 8.5.1(@swc/core@1.15.24)(jiti@2.6.1)(postcss@8.5.9)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.3) typescript: specifier: ^5.8.3 version: 5.9.3 @@ -572,13 +593,13 @@ importers: devDependencies: '@swc/core': specifier: ^1.15.18 - version: 1.15.18 + version: 1.15.24 '@testing-library/jest-dom': specifier: ^6.6 version: 6.9.1 '@testing-library/react': specifier: ^16.3 - version: 16.3.0(@testing-library/dom@10.4.1)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + version: 16.3.2(@testing-library/dom@10.4.1)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) '@types/js-yaml': specifier: ^4.0 version: 4.0.9 @@ -593,7 +614,7 @@ importers: version: 29.0.2 next: specifier: '>=16.1.7' - version: 16.2.2(@babel/core@7.29.0)(@opentelemetry/api@1.9.1)(@playwright/test@1.59.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + version: 16.2.3(@babel/core@7.29.0)(@opentelemetry/api@1.9.1)(@playwright/test@1.59.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) react: specifier: 19.2.4 version: 19.2.4 @@ -602,13 +623,13 @@ importers: version: 19.2.4(react@19.2.4) tsup: specifier: ^8.5.0 - version: 8.5.0(@swc/core@1.15.18)(jiti@2.6.1)(postcss@8.5.9)(tsx@4.21.0)(typescript@5.9.2)(yaml@2.8.3) + version: 8.5.1(@swc/core@1.15.24)(jiti@2.6.1)(postcss@8.5.9)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.3) typescript: specifier: ^5.8.3 - version: 5.9.2 + version: 5.9.3 vitest: specifier: ^4.1.3 - version: 4.1.3(@opentelemetry/api@1.9.1)(@types/node@24.11.0)(@vitest/coverage-v8@4.1.3)(jsdom@29.0.2)(vite@7.3.2(@types/node@24.11.0)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.4(@opentelemetry/api@1.9.1)(@types/node@24.12.2)(@vitest/coverage-v8@4.1.4)(@vitest/ui@4.1.4)(jsdom@29.0.2)(vite@7.3.2(@types/node@24.12.2)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.3)) packages/otters: {} @@ -626,31 +647,35 @@ importers: version: 4.0.9 '@types/node': specifier: ^24.1.0 - version: 24.11.0 + version: 24.12.2 tsup: specifier: ^8.5.0 - version: 8.5.0(@swc/core@1.15.18)(jiti@2.6.1)(postcss@8.5.9)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.3) + version: 8.5.1(@swc/core@1.15.24)(jiti@2.6.1)(postcss@8.5.9)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.3) typescript: specifier: ^5.8.3 version: 5.9.3 vitest: specifier: ^4.1.3 - version: 4.1.3(@opentelemetry/api@1.9.1)(@types/node@24.11.0)(@vitest/coverage-v8@4.1.3)(jsdom@29.0.2)(vite@7.3.2(@types/node@24.11.0)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.4(@opentelemetry/api@1.9.1)(@types/node@24.12.2)(@vitest/coverage-v8@4.1.4)(@vitest/ui@4.1.4)(jsdom@29.0.2)(vite@7.3.2(@types/node@24.12.2)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.3)) packages/scaffold-core: + dependencies: + '@stackwright/hooks-registry': + specifier: workspace:* + version: link:../hooks-registry devDependencies: '@types/node': specifier: ^24.1.0 - version: 24.11.0 + version: 24.12.2 tsup: specifier: ^8.5.0 - version: 8.5.0(@swc/core@1.15.18)(jiti@2.6.1)(postcss@8.5.9)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.3) + version: 8.5.1(@swc/core@1.15.24)(jiti@2.6.1)(postcss@8.5.9)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.3) typescript: specifier: ^5.8.3 version: 5.9.3 vitest: specifier: ^4.1.3 - version: 4.1.3(@opentelemetry/api@1.9.1)(@types/node@24.11.0)(@vitest/coverage-v8@4.1.3)(jsdom@29.0.2)(vite@7.3.2(@types/node@24.11.0)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.4(@opentelemetry/api@1.9.1)(@types/node@24.12.2)(@vitest/coverage-v8@4.1.4)(@vitest/ui@4.1.4)(jsdom@29.0.2)(vite@7.3.2(@types/node@24.12.2)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.3)) packages/themes: dependencies: @@ -666,7 +691,7 @@ importers: version: 6.9.1 '@testing-library/react': specifier: ^16.3 - version: 16.3.0(@testing-library/dom@10.4.1)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + version: 16.3.2(@testing-library/dom@10.4.1)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) '@types/js-yaml': specifier: ^4 version: 4.0.9 @@ -687,13 +712,13 @@ importers: version: 19.2.4(react@19.2.4) tsup: specifier: ^8.5.0 - version: 8.5.0(@swc/core@1.15.18)(jiti@2.6.1)(postcss@8.5.9)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.3) + version: 8.5.1(@swc/core@1.15.24)(jiti@2.6.1)(postcss@8.5.9)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.3) typescript: specifier: ^5.8.3 version: 5.9.3 vitest: specifier: ^4.1.3 - version: 4.1.3(@opentelemetry/api@1.9.1)(@types/node@24.11.0)(@vitest/coverage-v8@4.1.3)(jsdom@29.0.2)(vite@7.3.2(@types/node@24.11.0)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.4(@opentelemetry/api@1.9.1)(@types/node@24.12.2)(@vitest/coverage-v8@4.1.4)(@vitest/ui@4.1.4)(jsdom@29.0.2)(vite@7.3.2(@types/node@24.12.2)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.3)) packages/types: dependencies: @@ -724,7 +749,7 @@ importers: version: 11.3.4 tsup: specifier: ^8.5.0 - version: 8.5.0(@swc/core@1.15.18)(jiti@2.6.1)(postcss@8.5.9)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.3) + version: 8.5.1(@swc/core@1.15.24)(jiti@2.6.1)(postcss@8.5.9)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.3) tsx: specifier: ^4.21.0 version: 4.21.0 @@ -733,7 +758,7 @@ importers: version: 5.9.3 vitest: specifier: ^4.1.3 - version: 4.1.3(@opentelemetry/api@1.9.1)(@types/node@24.11.0)(@vitest/coverage-v8@4.1.3)(jsdom@29.0.2)(vite@7.3.2(@types/node@24.11.0)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.4(@opentelemetry/api@1.9.1)(@types/node@24.12.2)(@vitest/coverage-v8@4.1.4)(@vitest/ui@4.1.4)(jsdom@29.0.2)(vite@7.3.2(@types/node@24.12.2)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.3)) packages/ui-shadcn: dependencies: @@ -770,10 +795,10 @@ importers: version: 19.2.4 tailwindcss: specifier: ^4.1.11 - version: 4.2.1 + version: 4.2.2 tsup: specifier: ^8.5.0 - version: 8.5.0(@swc/core@1.15.18)(jiti@2.6.1)(postcss@8.5.9)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.3) + version: 8.5.1(@swc/core@1.15.24)(jiti@2.6.1)(postcss@8.5.9)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.3) typescript: specifier: ^5.8.3 version: 5.9.3 @@ -783,22 +808,18 @@ packages: '@acemir/cssom@0.9.31': resolution: {integrity: sha512-ZnR3GSaH+/vJ0YlHau21FjfLYjMpYVIzTD8M8vIEQvIGxeOXyXdzCI140rrCY862p/C/BbzWsjc1dgnM9mkoTA==} - '@adobe/css-tools@4.4.3': - resolution: {integrity: sha512-VQKMkwriZbaOgVCby1UDY/LDk5fIjhQicCvVPFqfe+69fWaPWydbWJ3wRt59/YzIwda1I81loas3oCoHxnqvdA==} + '@adobe/css-tools@4.4.4': + resolution: {integrity: sha512-Elp+iwUx5rN5+Y8xLt5/GRoG20WGoDCQ/1Fb+1LiGtvwbDavuSk0jhD/eZdckHAuzcDzccnkv+rEjyWfRx18gg==} - '@asamuzakjp/css-color@5.0.1': - resolution: {integrity: sha512-2SZFvqMyvboVV1d15lMf7XiI3m7SDqXUuKaTymJYLN6dSGadqp+fVojqJlVoMlbZnlTmu3S0TLwLTJpvBMO1Aw==} - engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} - - '@asamuzakjp/css-color@5.1.8': - resolution: {integrity: sha512-OISPR9c2uPo23rUdvfEQiLPjoMLOpEeLNnP5iGkxr6tDDxJd3NjD+6fxY0mdaMbIPUjFGL4HFOJqLvow5q4aqQ==} + '@asamuzakjp/css-color@5.1.10': + resolution: {integrity: sha512-02OhhkKtgNRuicQ/nF3TRnGsxL9wp0r3Y7VlKWyOHHGmGyvXv03y+PnymU8FKFJMTjIr1Bk8U2g1HWSLrpAHww==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} '@asamuzakjp/dom-selector@6.8.1': resolution: {integrity: sha512-MvRz1nCqW0fsy8Qz4dnLIvhOlMzqDVBabZx6lH+YywFDdjXhMY37SmpV1XFX3JzG5GWHn63j6HX6QPr3lZXHvQ==} - '@asamuzakjp/dom-selector@7.0.8': - resolution: {integrity: sha512-erMO6FgtM02dC24NGm0xufMzWz5OF0wXKR7BpvGD973bq/GbmR8/DbxNZbj0YevQ5hlToJaWSVK/G9/NDgGEVw==} + '@asamuzakjp/dom-selector@7.0.9': + resolution: {integrity: sha512-r3ElRr7y8ucyN2KdICwGsmj19RoN13CLCa/pvGydghWK6ZzeKQ+TcDjVdtEZz2ElpndM5jXw//B9CEee0mWnVg==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} '@asamuzakjp/nwsapi@2.3.9': @@ -855,19 +876,15 @@ packages: resolution: {integrity: sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==} engines: {node: '>=6.9.0'} - '@babel/helpers@7.28.6': - resolution: {integrity: sha512-xOBvwq86HHdB7WUDTfKfT/Vuxh7gElQ+Sfti2Cy6yIWNW05P8iUslOVcZ4/sKbE+/jQaukQAdz/gf3724kYdqw==} + '@babel/helpers@7.29.2': + resolution: {integrity: sha512-HoGuUs4sCZNezVEKdVcwqmZN8GoHirLUcLaYVNBK2J0DadGtdcqgr3BCbvH8+XUo4NGjNl3VOtSjEKNzqfFgKw==} engines: {node: '>=6.9.0'} - '@babel/parser@7.29.0': - resolution: {integrity: sha512-IyDgFV5GeDUVX4YdF/3CPULtVGSXXMLh1xVIgdCgxApktqnQV0r7/8Nqthg+8YLGaAtdyIlo2qIdZrbCv4+7ww==} + '@babel/parser@7.29.2': + resolution: {integrity: sha512-4GgRzy/+fsBa72/RZVJmGKPmZu9Byn8o4MoLpmNe1m8ZfYnz5emHLQz3U4gLud6Zwl0RZIcgiLD7Uq7ySFuDLA==} engines: {node: '>=6.0.0'} hasBin: true - '@babel/runtime@7.28.2': - resolution: {integrity: sha512-KHp2IflsnGywDjBWDkR9iEqiWSpc8GIi0lgTT3mOElT0PP1tG26P4tmFI2YvAdzgq9RGyoHZQEIEdZy6Ec5xCA==} - engines: {node: '>=6.9.0'} - '@babel/runtime@7.29.2': resolution: {integrity: sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==} engines: {node: '>=6.9.0'} @@ -1032,15 +1049,15 @@ packages: resolution: {integrity: sha512-LMGQLS9EuADloEFkcTBR3BwV/CGHV7zyDxVRtVDTwdI2Ca4it0CCVTT9wCkxSgokjE5Ho41hEPgb8OEUwoXr6Q==} engines: {node: '>=20.19.0'} - '@csstools/css-calc@3.1.1': - resolution: {integrity: sha512-HJ26Z/vmsZQqs/o3a6bgKslXGFAungXGbinULZO3eMsOyNJHeBBZfup5FiZInOghgoM4Hwnmw+OgbJCNg1wwUQ==} + '@csstools/css-calc@3.2.0': + resolution: {integrity: sha512-bR9e6o2BDB12jzN/gIbjHa5wLJ4UjD1CB9pM7ehlc0ddk6EBz+yYS1EV2MF55/HUxrHcB/hehAyt5vhsA3hx7w==} engines: {node: '>=20.19.0'} peerDependencies: '@csstools/css-parser-algorithms': ^4.0.0 '@csstools/css-tokenizer': ^4.0.0 - '@csstools/css-color-parser@4.0.2': - resolution: {integrity: sha512-0GEfbBLmTFf0dJlpsNU7zwxRIH0/BGEMuXLTCvFYxuL1tNhqzTbtnFICyJLTNK4a+RechKP75e7w42ClXSnJQw==} + '@csstools/css-color-parser@4.1.0': + resolution: {integrity: sha512-U0KhLYmy2GVj6q4T3WaAe6NPuFYCPQoE3b0dRGxejWDgcPp8TP7S5rVdM5ZrFaqu4N67X8YaPBw14dQSYx3IyQ==} engines: {node: '>=20.19.0'} peerDependencies: '@csstools/css-parser-algorithms': ^4.0.0 @@ -1052,11 +1069,8 @@ packages: peerDependencies: '@csstools/css-tokenizer': ^4.0.0 - '@csstools/css-syntax-patches-for-csstree@1.0.28': - resolution: {integrity: sha512-1NRf1CUBjnr3K7hu8BLxjQrKCxEe8FP/xmPTenAxCRZWVLbmGotkFvG9mfNpjA6k7Bw1bw4BilZq9cu19RA5pg==} - - '@csstools/css-syntax-patches-for-csstree@1.1.2': - resolution: {integrity: sha512-5GkLzz4prTIpoyeUiIu3iV6CSG3Plo7xRVOFPKI7FVEJ3mZ0A8SwK0XU3Gl7xAkiQ+mDyam+NNp875/C5y+jSA==} + '@csstools/css-syntax-patches-for-csstree@1.1.3': + resolution: {integrity: sha512-SH60bMfrRCJF3morcdk57WklujF4Jr/EsQUzqkarfHXEFcAR1gg7fS/chAE922Sehgzc1/+Tz5H3Ypa1HiEKrg==} peerDependencies: css-tree: ^3.2.1 peerDependenciesMeta: @@ -1076,468 +1090,156 @@ packages: '@emnapi/wasi-threads@1.2.1': resolution: {integrity: sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==} - '@esbuild/aix-ppc64@0.25.8': - resolution: {integrity: sha512-urAvrUedIqEiFR3FYSLTWQgLu5tb+m0qZw0NBEasUeo6wuqatkMDaRT+1uABiGXEu5vqgPd7FGE1BhsAIy9QVA==} - engines: {node: '>=18'} - cpu: [ppc64] - os: [aix] - - '@esbuild/aix-ppc64@0.27.3': - resolution: {integrity: sha512-9fJMTNFTWZMh5qwrBItuziu834eOCUcEqymSH7pY+zoMVEZg3gcPuBNxH1EvfVYe9h0x/Ptw8KBzv7qxb7l8dg==} - engines: {node: '>=18'} - cpu: [ppc64] - os: [aix] - '@esbuild/aix-ppc64@0.27.7': resolution: {integrity: sha512-EKX3Qwmhz1eMdEJokhALr0YiD0lhQNwDqkPYyPhiSwKrh7/4KRjQc04sZ8db+5DVVnZ1LmbNDI1uAMPEUBnQPg==} engines: {node: '>=18'} cpu: [ppc64] os: [aix] - '@esbuild/android-arm64@0.25.8': - resolution: {integrity: sha512-OD3p7LYzWpLhZEyATcTSJ67qB5D+20vbtr6vHlHWSQYhKtzUYrETuWThmzFpZtFsBIxRvhO07+UgVA9m0i/O1w==} - engines: {node: '>=18'} - cpu: [arm64] - os: [android] - - '@esbuild/android-arm64@0.27.3': - resolution: {integrity: sha512-YdghPYUmj/FX2SYKJ0OZxf+iaKgMsKHVPF1MAq/P8WirnSpCStzKJFjOjzsW0QQ7oIAiccHdcqjbHmJxRb/dmg==} - engines: {node: '>=18'} - cpu: [arm64] - os: [android] - '@esbuild/android-arm64@0.27.7': resolution: {integrity: sha512-62dPZHpIXzvChfvfLJow3q5dDtiNMkwiRzPylSCfriLvZeq0a1bWChrGx/BbUbPwOrsWKMn8idSllklzBy+dgQ==} engines: {node: '>=18'} cpu: [arm64] os: [android] - '@esbuild/android-arm@0.25.8': - resolution: {integrity: sha512-RONsAvGCz5oWyePVnLdZY/HHwA++nxYWIX1atInlaW6SEkwq6XkP3+cb825EUcRs5Vss/lGh/2YxAb5xqc07Uw==} - engines: {node: '>=18'} - cpu: [arm] - os: [android] - - '@esbuild/android-arm@0.27.3': - resolution: {integrity: sha512-i5D1hPY7GIQmXlXhs2w8AWHhenb00+GxjxRncS2ZM7YNVGNfaMxgzSGuO8o8SJzRc/oZwU2bcScvVERk03QhzA==} - engines: {node: '>=18'} - cpu: [arm] - os: [android] - '@esbuild/android-arm@0.27.7': resolution: {integrity: sha512-jbPXvB4Yj2yBV7HUfE2KHe4GJX51QplCN1pGbYjvsyCZbQmies29EoJbkEc+vYuU5o45AfQn37vZlyXy4YJ8RQ==} engines: {node: '>=18'} cpu: [arm] os: [android] - '@esbuild/android-x64@0.25.8': - resolution: {integrity: sha512-yJAVPklM5+4+9dTeKwHOaA+LQkmrKFX96BM0A/2zQrbS6ENCmxc4OVoBs5dPkCCak2roAD+jKCdnmOqKszPkjA==} - engines: {node: '>=18'} - cpu: [x64] - os: [android] - - '@esbuild/android-x64@0.27.3': - resolution: {integrity: sha512-IN/0BNTkHtk8lkOM8JWAYFg4ORxBkZQf9zXiEOfERX/CzxW3Vg1ewAhU7QSWQpVIzTW+b8Xy+lGzdYXV6UZObQ==} - engines: {node: '>=18'} - cpu: [x64] - os: [android] - '@esbuild/android-x64@0.27.7': resolution: {integrity: sha512-x5VpMODneVDb70PYV2VQOmIUUiBtY3D3mPBG8NxVk5CogneYhkR7MmM3yR/uMdITLrC1ml/NV1rj4bMJuy9MCg==} engines: {node: '>=18'} cpu: [x64] os: [android] - '@esbuild/darwin-arm64@0.25.8': - resolution: {integrity: sha512-Jw0mxgIaYX6R8ODrdkLLPwBqHTtYHJSmzzd+QeytSugzQ0Vg4c5rDky5VgkoowbZQahCbsv1rT1KW72MPIkevw==} - engines: {node: '>=18'} - cpu: [arm64] - os: [darwin] - - '@esbuild/darwin-arm64@0.27.3': - resolution: {integrity: sha512-Re491k7ByTVRy0t3EKWajdLIr0gz2kKKfzafkth4Q8A5n1xTHrkqZgLLjFEHVD+AXdUGgQMq+Godfq45mGpCKg==} - engines: {node: '>=18'} - cpu: [arm64] - os: [darwin] - '@esbuild/darwin-arm64@0.27.7': resolution: {integrity: sha512-5lckdqeuBPlKUwvoCXIgI2D9/ABmPq3Rdp7IfL70393YgaASt7tbju3Ac+ePVi3KDH6N2RqePfHnXkaDtY9fkw==} engines: {node: '>=18'} cpu: [arm64] os: [darwin] - '@esbuild/darwin-x64@0.25.8': - resolution: {integrity: sha512-Vh2gLxxHnuoQ+GjPNvDSDRpoBCUzY4Pu0kBqMBDlK4fuWbKgGtmDIeEC081xi26PPjn+1tct+Bh8FjyLlw1Zlg==} - engines: {node: '>=18'} - cpu: [x64] - os: [darwin] - - '@esbuild/darwin-x64@0.27.3': - resolution: {integrity: sha512-vHk/hA7/1AckjGzRqi6wbo+jaShzRowYip6rt6q7VYEDX4LEy1pZfDpdxCBnGtl+A5zq8iXDcyuxwtv3hNtHFg==} - engines: {node: '>=18'} - cpu: [x64] - os: [darwin] - '@esbuild/darwin-x64@0.27.7': resolution: {integrity: sha512-rYnXrKcXuT7Z+WL5K980jVFdvVKhCHhUwid+dDYQpH+qu+TefcomiMAJpIiC2EM3Rjtq0sO3StMV/+3w3MyyqQ==} engines: {node: '>=18'} cpu: [x64] os: [darwin] - '@esbuild/freebsd-arm64@0.25.8': - resolution: {integrity: sha512-YPJ7hDQ9DnNe5vxOm6jaie9QsTwcKedPvizTVlqWG9GBSq+BuyWEDazlGaDTC5NGU4QJd666V0yqCBL2oWKPfA==} - engines: {node: '>=18'} - cpu: [arm64] - os: [freebsd] - - '@esbuild/freebsd-arm64@0.27.3': - resolution: {integrity: sha512-ipTYM2fjt3kQAYOvo6vcxJx3nBYAzPjgTCk7QEgZG8AUO3ydUhvelmhrbOheMnGOlaSFUoHXB6un+A7q4ygY9w==} - engines: {node: '>=18'} - cpu: [arm64] - os: [freebsd] - '@esbuild/freebsd-arm64@0.27.7': resolution: {integrity: sha512-B48PqeCsEgOtzME2GbNM2roU29AMTuOIN91dsMO30t+Ydis3z/3Ngoj5hhnsOSSwNzS+6JppqWsuhTp6E82l2w==} engines: {node: '>=18'} cpu: [arm64] os: [freebsd] - '@esbuild/freebsd-x64@0.25.8': - resolution: {integrity: sha512-MmaEXxQRdXNFsRN/KcIimLnSJrk2r5H8v+WVafRWz5xdSVmWLoITZQXcgehI2ZE6gioE6HirAEToM/RvFBeuhw==} - engines: {node: '>=18'} - cpu: [x64] - os: [freebsd] - - '@esbuild/freebsd-x64@0.27.3': - resolution: {integrity: sha512-dDk0X87T7mI6U3K9VjWtHOXqwAMJBNN2r7bejDsc+j03SEjtD9HrOl8gVFByeM0aJksoUuUVU9TBaZa2rgj0oA==} - engines: {node: '>=18'} - cpu: [x64] - os: [freebsd] - '@esbuild/freebsd-x64@0.27.7': resolution: {integrity: sha512-jOBDK5XEjA4m5IJK3bpAQF9/Lelu/Z9ZcdhTRLf4cajlB+8VEhFFRjWgfy3M1O4rO2GQ/b2dLwCUGpiF/eATNQ==} engines: {node: '>=18'} cpu: [x64] os: [freebsd] - '@esbuild/linux-arm64@0.25.8': - resolution: {integrity: sha512-WIgg00ARWv/uYLU7lsuDK00d/hHSfES5BzdWAdAig1ioV5kaFNrtK8EqGcUBJhYqotlUByUKz5Qo6u8tt7iD/w==} - engines: {node: '>=18'} - cpu: [arm64] - os: [linux] - - '@esbuild/linux-arm64@0.27.3': - resolution: {integrity: sha512-sZOuFz/xWnZ4KH3YfFrKCf1WyPZHakVzTiqji3WDc0BCl2kBwiJLCXpzLzUBLgmp4veFZdvN5ChW4Eq/8Fc2Fg==} - engines: {node: '>=18'} - cpu: [arm64] - os: [linux] - '@esbuild/linux-arm64@0.27.7': resolution: {integrity: sha512-RZPHBoxXuNnPQO9rvjh5jdkRmVizktkT7TCDkDmQ0W2SwHInKCAV95GRuvdSvA7w4VMwfCjUiPwDi0ZO6Nfe9A==} engines: {node: '>=18'} cpu: [arm64] os: [linux] - '@esbuild/linux-arm@0.25.8': - resolution: {integrity: sha512-FuzEP9BixzZohl1kLf76KEVOsxtIBFwCaLupVuk4eFVnOZfU+Wsn+x5Ryam7nILV2pkq2TqQM9EZPsOBuMC+kg==} - engines: {node: '>=18'} - cpu: [arm] - os: [linux] - - '@esbuild/linux-arm@0.27.3': - resolution: {integrity: sha512-s6nPv2QkSupJwLYyfS+gwdirm0ukyTFNl3KTgZEAiJDd+iHZcbTPPcWCcRYH+WlNbwChgH2QkE9NSlNrMT8Gfw==} - engines: {node: '>=18'} - cpu: [arm] - os: [linux] - '@esbuild/linux-arm@0.27.7': resolution: {integrity: sha512-RkT/YXYBTSULo3+af8Ib0ykH8u2MBh57o7q/DAs3lTJlyVQkgQvlrPTnjIzzRPQyavxtPtfg0EopvDyIt0j1rA==} engines: {node: '>=18'} cpu: [arm] os: [linux] - '@esbuild/linux-ia32@0.25.8': - resolution: {integrity: sha512-A1D9YzRX1i+1AJZuFFUMP1E9fMaYY+GnSQil9Tlw05utlE86EKTUA7RjwHDkEitmLYiFsRd9HwKBPEftNdBfjg==} - engines: {node: '>=18'} - cpu: [ia32] - os: [linux] - - '@esbuild/linux-ia32@0.27.3': - resolution: {integrity: sha512-yGlQYjdxtLdh0a3jHjuwOrxQjOZYD/C9PfdbgJJF3TIZWnm/tMd/RcNiLngiu4iwcBAOezdnSLAwQDPqTmtTYg==} - engines: {node: '>=18'} - cpu: [ia32] - os: [linux] - '@esbuild/linux-ia32@0.27.7': resolution: {integrity: sha512-GA48aKNkyQDbd3KtkplYWT102C5sn/EZTY4XROkxONgruHPU72l+gW+FfF8tf2cFjeHaRbWpOYa/uRBz/Xq1Pg==} engines: {node: '>=18'} cpu: [ia32] os: [linux] - '@esbuild/linux-loong64@0.25.8': - resolution: {integrity: sha512-O7k1J/dwHkY1RMVvglFHl1HzutGEFFZ3kNiDMSOyUrB7WcoHGf96Sh+64nTRT26l3GMbCW01Ekh/ThKM5iI7hQ==} - engines: {node: '>=18'} - cpu: [loong64] - os: [linux] - - '@esbuild/linux-loong64@0.27.3': - resolution: {integrity: sha512-WO60Sn8ly3gtzhyjATDgieJNet/KqsDlX5nRC5Y3oTFcS1l0KWba+SEa9Ja1GfDqSF1z6hif/SkpQJbL63cgOA==} - engines: {node: '>=18'} - cpu: [loong64] - os: [linux] - '@esbuild/linux-loong64@0.27.7': resolution: {integrity: sha512-a4POruNM2oWsD4WKvBSEKGIiWQF8fZOAsycHOt6JBpZ+JN2n2JH9WAv56SOyu9X5IqAjqSIPTaJkqN8F7XOQ5Q==} engines: {node: '>=18'} cpu: [loong64] os: [linux] - '@esbuild/linux-mips64el@0.25.8': - resolution: {integrity: sha512-uv+dqfRazte3BzfMp8PAQXmdGHQt2oC/y2ovwpTteqrMx2lwaksiFZ/bdkXJC19ttTvNXBuWH53zy/aTj1FgGw==} - engines: {node: '>=18'} - cpu: [mips64el] - os: [linux] - - '@esbuild/linux-mips64el@0.27.3': - resolution: {integrity: sha512-APsymYA6sGcZ4pD6k+UxbDjOFSvPWyZhjaiPyl/f79xKxwTnrn5QUnXR5prvetuaSMsb4jgeHewIDCIWljrSxw==} - engines: {node: '>=18'} - cpu: [mips64el] - os: [linux] - '@esbuild/linux-mips64el@0.27.7': resolution: {integrity: sha512-KabT5I6StirGfIz0FMgl1I+R1H73Gp0ofL9A3nG3i/cYFJzKHhouBV5VWK1CSgKvVaG4q1RNpCTR2LuTVB3fIw==} engines: {node: '>=18'} cpu: [mips64el] os: [linux] - '@esbuild/linux-ppc64@0.25.8': - resolution: {integrity: sha512-GyG0KcMi1GBavP5JgAkkstMGyMholMDybAf8wF5A70CALlDM2p/f7YFE7H92eDeH/VBtFJA5MT4nRPDGg4JuzQ==} - engines: {node: '>=18'} - cpu: [ppc64] - os: [linux] - - '@esbuild/linux-ppc64@0.27.3': - resolution: {integrity: sha512-eizBnTeBefojtDb9nSh4vvVQ3V9Qf9Df01PfawPcRzJH4gFSgrObw+LveUyDoKU3kxi5+9RJTCWlj4FjYXVPEA==} - engines: {node: '>=18'} - cpu: [ppc64] - os: [linux] - '@esbuild/linux-ppc64@0.27.7': resolution: {integrity: sha512-gRsL4x6wsGHGRqhtI+ifpN/vpOFTQtnbsupUF5R5YTAg+y/lKelYR1hXbnBdzDjGbMYjVJLJTd2OFmMewAgwlQ==} engines: {node: '>=18'} cpu: [ppc64] os: [linux] - '@esbuild/linux-riscv64@0.25.8': - resolution: {integrity: sha512-rAqDYFv3yzMrq7GIcen3XP7TUEG/4LK86LUPMIz6RT8A6pRIDn0sDcvjudVZBiiTcZCY9y2SgYX2lgK3AF+1eg==} - engines: {node: '>=18'} - cpu: [riscv64] - os: [linux] - - '@esbuild/linux-riscv64@0.27.3': - resolution: {integrity: sha512-3Emwh0r5wmfm3ssTWRQSyVhbOHvqegUDRd0WhmXKX2mkHJe1SFCMJhagUleMq+Uci34wLSipf8Lagt4LlpRFWQ==} - engines: {node: '>=18'} - cpu: [riscv64] - os: [linux] - '@esbuild/linux-riscv64@0.27.7': resolution: {integrity: sha512-hL25LbxO1QOngGzu2U5xeXtxXcW+/GvMN3ejANqXkxZ/opySAZMrc+9LY/WyjAan41unrR3YrmtTsUpwT66InQ==} engines: {node: '>=18'} cpu: [riscv64] os: [linux] - '@esbuild/linux-s390x@0.25.8': - resolution: {integrity: sha512-Xutvh6VjlbcHpsIIbwY8GVRbwoviWT19tFhgdA7DlenLGC/mbc3lBoVb7jxj9Z+eyGqvcnSyIltYUrkKzWqSvg==} - engines: {node: '>=18'} - cpu: [s390x] - os: [linux] - - '@esbuild/linux-s390x@0.27.3': - resolution: {integrity: sha512-pBHUx9LzXWBc7MFIEEL0yD/ZVtNgLytvx60gES28GcWMqil8ElCYR4kvbV2BDqsHOvVDRrOxGySBM9Fcv744hw==} - engines: {node: '>=18'} - cpu: [s390x] - os: [linux] - '@esbuild/linux-s390x@0.27.7': resolution: {integrity: sha512-2k8go8Ycu1Kb46vEelhu1vqEP+UeRVj2zY1pSuPdgvbd5ykAw82Lrro28vXUrRmzEsUV0NzCf54yARIK8r0fdw==} engines: {node: '>=18'} cpu: [s390x] os: [linux] - '@esbuild/linux-x64@0.25.8': - resolution: {integrity: sha512-ASFQhgY4ElXh3nDcOMTkQero4b1lgubskNlhIfJrsH5OKZXDpUAKBlNS0Kx81jwOBp+HCeZqmoJuihTv57/jvQ==} - engines: {node: '>=18'} - cpu: [x64] - os: [linux] - - '@esbuild/linux-x64@0.27.3': - resolution: {integrity: sha512-Czi8yzXUWIQYAtL/2y6vogER8pvcsOsk5cpwL4Gk5nJqH5UZiVByIY8Eorm5R13gq+DQKYg0+JyQoytLQas4dA==} - engines: {node: '>=18'} - cpu: [x64] - os: [linux] - '@esbuild/linux-x64@0.27.7': resolution: {integrity: sha512-hzznmADPt+OmsYzw1EE33ccA+HPdIqiCRq7cQeL1Jlq2gb1+OyWBkMCrYGBJ+sxVzve2ZJEVeePbLM2iEIZSxA==} engines: {node: '>=18'} cpu: [x64] os: [linux] - '@esbuild/netbsd-arm64@0.25.8': - resolution: {integrity: sha512-d1KfruIeohqAi6SA+gENMuObDbEjn22olAR7egqnkCD9DGBG0wsEARotkLgXDu6c4ncgWTZJtN5vcgxzWRMzcw==} - engines: {node: '>=18'} - cpu: [arm64] - os: [netbsd] - - '@esbuild/netbsd-arm64@0.27.3': - resolution: {integrity: sha512-sDpk0RgmTCR/5HguIZa9n9u+HVKf40fbEUt+iTzSnCaGvY9kFP0YKBWZtJaraonFnqef5SlJ8/TiPAxzyS+UoA==} - engines: {node: '>=18'} - cpu: [arm64] - os: [netbsd] - '@esbuild/netbsd-arm64@0.27.7': resolution: {integrity: sha512-b6pqtrQdigZBwZxAn1UpazEisvwaIDvdbMbmrly7cDTMFnw/+3lVxxCTGOrkPVnsYIosJJXAsILG9XcQS+Yu6w==} engines: {node: '>=18'} cpu: [arm64] os: [netbsd] - '@esbuild/netbsd-x64@0.25.8': - resolution: {integrity: sha512-nVDCkrvx2ua+XQNyfrujIG38+YGyuy2Ru9kKVNyh5jAys6n+l44tTtToqHjino2My8VAY6Lw9H7RI73XFi66Cg==} - engines: {node: '>=18'} - cpu: [x64] - os: [netbsd] - - '@esbuild/netbsd-x64@0.27.3': - resolution: {integrity: sha512-P14lFKJl/DdaE00LItAukUdZO5iqNH7+PjoBm+fLQjtxfcfFE20Xf5CrLsmZdq5LFFZzb5JMZ9grUwvtVYzjiA==} - engines: {node: '>=18'} - cpu: [x64] - os: [netbsd] - '@esbuild/netbsd-x64@0.27.7': resolution: {integrity: sha512-OfatkLojr6U+WN5EDYuoQhtM+1xco+/6FSzJJnuWiUw5eVcicbyK3dq5EeV/QHT1uy6GoDhGbFpprUiHUYggrw==} engines: {node: '>=18'} cpu: [x64] os: [netbsd] - '@esbuild/openbsd-arm64@0.25.8': - resolution: {integrity: sha512-j8HgrDuSJFAujkivSMSfPQSAa5Fxbvk4rgNAS5i3K+r8s1X0p1uOO2Hl2xNsGFppOeHOLAVgYwDVlmxhq5h+SQ==} - engines: {node: '>=18'} - cpu: [arm64] - os: [openbsd] - - '@esbuild/openbsd-arm64@0.27.3': - resolution: {integrity: sha512-AIcMP77AvirGbRl/UZFTq5hjXK+2wC7qFRGoHSDrZ5v5b8DK/GYpXW3CPRL53NkvDqb9D+alBiC/dV0Fb7eJcw==} - engines: {node: '>=18'} - cpu: [arm64] - os: [openbsd] - '@esbuild/openbsd-arm64@0.27.7': resolution: {integrity: sha512-AFuojMQTxAz75Fo8idVcqoQWEHIXFRbOc1TrVcFSgCZtQfSdc1RXgB3tjOn/krRHENUB4j00bfGjyl2mJrU37A==} engines: {node: '>=18'} cpu: [arm64] os: [openbsd] - '@esbuild/openbsd-x64@0.25.8': - resolution: {integrity: sha512-1h8MUAwa0VhNCDp6Af0HToI2TJFAn1uqT9Al6DJVzdIBAd21m/G0Yfc77KDM3uF3T/YaOgQq3qTJHPbTOInaIQ==} - engines: {node: '>=18'} - cpu: [x64] - os: [openbsd] - - '@esbuild/openbsd-x64@0.27.3': - resolution: {integrity: sha512-DnW2sRrBzA+YnE70LKqnM3P+z8vehfJWHXECbwBmH/CU51z6FiqTQTHFenPlHmo3a8UgpLyH3PT+87OViOh1AQ==} - engines: {node: '>=18'} - cpu: [x64] - os: [openbsd] - '@esbuild/openbsd-x64@0.27.7': resolution: {integrity: sha512-+A1NJmfM8WNDv5CLVQYJ5PshuRm/4cI6WMZRg1by1GwPIQPCTs1GLEUHwiiQGT5zDdyLiRM/l1G0Pv54gvtKIg==} engines: {node: '>=18'} cpu: [x64] os: [openbsd] - '@esbuild/openharmony-arm64@0.25.8': - resolution: {integrity: sha512-r2nVa5SIK9tSWd0kJd9HCffnDHKchTGikb//9c7HX+r+wHYCpQrSgxhlY6KWV1nFo1l4KFbsMlHk+L6fekLsUg==} - engines: {node: '>=18'} - cpu: [arm64] - os: [openharmony] - - '@esbuild/openharmony-arm64@0.27.3': - resolution: {integrity: sha512-NinAEgr/etERPTsZJ7aEZQvvg/A6IsZG/LgZy+81wON2huV7SrK3e63dU0XhyZP4RKGyTm7aOgmQk0bGp0fy2g==} - engines: {node: '>=18'} - cpu: [arm64] - os: [openharmony] - '@esbuild/openharmony-arm64@0.27.7': resolution: {integrity: sha512-+KrvYb/C8zA9CU/g0sR6w2RBw7IGc5J2BPnc3dYc5VJxHCSF1yNMxTV5LQ7GuKteQXZtspjFbiuW5/dOj7H4Yw==} engines: {node: '>=18'} cpu: [arm64] os: [openharmony] - '@esbuild/sunos-x64@0.25.8': - resolution: {integrity: sha512-zUlaP2S12YhQ2UzUfcCuMDHQFJyKABkAjvO5YSndMiIkMimPmxA+BYSBikWgsRpvyxuRnow4nS5NPnf9fpv41w==} - engines: {node: '>=18'} - cpu: [x64] - os: [sunos] - - '@esbuild/sunos-x64@0.27.3': - resolution: {integrity: sha512-PanZ+nEz+eWoBJ8/f8HKxTTD172SKwdXebZ0ndd953gt1HRBbhMsaNqjTyYLGLPdoWHy4zLU7bDVJztF5f3BHA==} - engines: {node: '>=18'} - cpu: [x64] - os: [sunos] - '@esbuild/sunos-x64@0.27.7': resolution: {integrity: sha512-ikktIhFBzQNt/QDyOL580ti9+5mL/YZeUPKU2ivGtGjdTYoqz6jObj6nOMfhASpS4GU4Q/Clh1QtxWAvcYKamA==} engines: {node: '>=18'} cpu: [x64] os: [sunos] - '@esbuild/win32-arm64@0.25.8': - resolution: {integrity: sha512-YEGFFWESlPva8hGL+zvj2z/SaK+pH0SwOM0Nc/d+rVnW7GSTFlLBGzZkuSU9kFIGIo8q9X3ucpZhu8PDN5A2sQ==} - engines: {node: '>=18'} - cpu: [arm64] - os: [win32] - - '@esbuild/win32-arm64@0.27.3': - resolution: {integrity: sha512-B2t59lWWYrbRDw/tjiWOuzSsFh1Y/E95ofKz7rIVYSQkUYBjfSgf6oeYPNWHToFRr2zx52JKApIcAS/D5TUBnA==} - engines: {node: '>=18'} - cpu: [arm64] - os: [win32] - '@esbuild/win32-arm64@0.27.7': resolution: {integrity: sha512-7yRhbHvPqSpRUV7Q20VuDwbjW5kIMwTHpptuUzV+AA46kiPze5Z7qgt6CLCK3pWFrHeNfDd1VKgyP4O+ng17CA==} engines: {node: '>=18'} cpu: [arm64] os: [win32] - '@esbuild/win32-ia32@0.25.8': - resolution: {integrity: sha512-hiGgGC6KZ5LZz58OL/+qVVoZiuZlUYlYHNAmczOm7bs2oE1XriPFi5ZHHrS8ACpV5EjySrnoCKmcbQMN+ojnHg==} - engines: {node: '>=18'} - cpu: [ia32] - os: [win32] - - '@esbuild/win32-ia32@0.27.3': - resolution: {integrity: sha512-QLKSFeXNS8+tHW7tZpMtjlNb7HKau0QDpwm49u0vUp9y1WOF+PEzkU84y9GqYaAVW8aH8f3GcBck26jh54cX4Q==} - engines: {node: '>=18'} - cpu: [ia32] - os: [win32] - '@esbuild/win32-ia32@0.27.7': resolution: {integrity: sha512-SmwKXe6VHIyZYbBLJrhOoCJRB/Z1tckzmgTLfFYOfpMAx63BJEaL9ExI8x7v0oAO3Zh6D/Oi1gVxEYr5oUCFhw==} engines: {node: '>=18'} cpu: [ia32] os: [win32] - '@esbuild/win32-x64@0.25.8': - resolution: {integrity: sha512-cn3Yr7+OaaZq1c+2pe+8yxC8E144SReCQjN6/2ynubzYjvyqZjTXfQJpAcQpsdJq3My7XADANiYGHoFC69pLQw==} - engines: {node: '>=18'} - cpu: [x64] - os: [win32] - - '@esbuild/win32-x64@0.27.3': - resolution: {integrity: sha512-4uJGhsxuptu3OcpVAzli+/gWusVGwZZHTlS63hh++ehExkVT8SgiEf7/uC/PclrPPkLhZqGgCTjd0VWLo6xMqA==} - engines: {node: '>=18'} - cpu: [x64] - os: [win32] - '@esbuild/win32-x64@0.27.7': resolution: {integrity: sha512-56hiAJPhwQ1R4i+21FVF7V8kSD5zZTdHcVuRFMW0hn753vVfQN8xlx4uOPT4xoGH0Z/oVATuR82AiqSTDIpaHg==} engines: {node: '>=18'} @@ -1554,8 +1256,8 @@ packages: resolution: {integrity: sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==} engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} - '@eslint/config-array@0.21.1': - resolution: {integrity: sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA==} + '@eslint/config-array@0.21.2': + resolution: {integrity: sha512-nJl2KGTlrf9GjLimgIru+V/mzgSK0ABCDQRvxw5BjURL7WfH5uoWmizbH7QB6MmnMBd8cIC9uceWnezL1VZWWw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@eslint/config-helpers@0.4.2': @@ -1566,12 +1268,12 @@ packages: resolution: {integrity: sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/eslintrc@3.3.4': - resolution: {integrity: sha512-4h4MVF8pmBsncB60r0wSJiIeUKTSD4m7FmTFThG8RHlsg9ajqckLm9OraguFGZE4vVdpiI1Q4+hFnisopmG6gQ==} + '@eslint/eslintrc@3.3.5': + resolution: {integrity: sha512-4IlJx0X0qftVsN5E+/vGujTRIFtwuLbNsVUe7TO6zYPDR1O6nFwvwhIKEKSrl6dZchmYBITazxKoUYOjdtjlRg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/js@9.39.3': - resolution: {integrity: sha512-1B1VkCq6FuUNlQvlBYb+1jDu/gV297TIs/OeiaSR9l1H27SVW55ONE1e1Vp16NqP683+xEGzxYtv4XCiDPaQiw==} + '@eslint/js@9.39.4': + resolution: {integrity: sha512-nE7DEIchvtiFTwBw4Lfbu59PG+kCofhjsKaCWzxTpt4lfRjRMqG6uMBzKXuEcyXhOHoUp9riAm7/aWYGhXZ9cw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@eslint/object-schema@2.1.7': @@ -1582,15 +1284,6 @@ packages: resolution: {integrity: sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@exodus/bytes@1.14.1': - resolution: {integrity: sha512-OhkBFWI6GcRMUroChZiopRiSp2iAMvEBK47NhJooDqz1RERO4QuZIZnjP63TXX8GAiLABkYmX+fuQsdJ1dd2QQ==} - engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} - peerDependencies: - '@noble/hashes': ^1.8.0 || ^2.0.0 - peerDependenciesMeta: - '@noble/hashes': - optional: true - '@exodus/bytes@1.15.0': resolution: {integrity: sha512-UY0nlA+feH81UGSHv92sLEPLCeZFjXOuHhrIo0HQydScuQc8s0A7kL/UdgwgDq8g8ilksmuoF35YVTNphV2aBQ==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} @@ -1619,8 +1312,8 @@ packages: '@formatjs/intl-localematcher@0.6.2': resolution: {integrity: sha512-XOMO2Hupl0wdd172Y06h6kLpBz6Dv+J4okPLl4LPtzbr8f66WbIoy4ev98EBuZ6ZK4h5ydTN6XneT4QVpD7cdA==} - '@hono/node-server@1.19.11': - resolution: {integrity: sha512-dr8/3zEaB+p0D2n/IUrlPF1HZm586qgJNXK1a9fhg/PzdtkK7Ksd5l312tJX2yBuALqDYBlG20QEbayqPyxn+g==} + '@hono/node-server@1.19.13': + resolution: {integrity: sha512-TsQLe4i2gvoTtrHje625ngThGBySOgSK3Xo2XRYOdqGN1teR8+I7vchQC46uLJi8OF62YTYA3AhSpumtkhsaKQ==} engines: {node: '>=18.14.1'} peerDependencies: hono: '>=4.12.7' @@ -1641,8 +1334,8 @@ packages: resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==} engines: {node: '>=18.18'} - '@img/colour@1.0.0': - resolution: {integrity: sha512-A5P/LfWGFSl6nsckYtjw9da+19jB8hkJ6ACTGcDfEJ0aE+l2n2El7dsVM7UVHZQ9s2lmYMWlrS21YLy2IR1LUw==} + '@img/colour@1.1.0': + resolution: {integrity: sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ==} engines: {node: '>=18'} '@img/sharp-darwin-arm64@0.34.5': @@ -1794,12 +1487,12 @@ packages: cpu: [x64] os: [win32] - '@inquirer/ansi@2.0.3': - resolution: {integrity: sha512-g44zhR3NIKVs0zUesa4iMzExmZpLUdTLRMCStqX3GE5NT6VkPcxQGJ+uC8tDgBUC/vB1rUhUd55cOf++4NZcmw==} + '@inquirer/ansi@2.0.5': + resolution: {integrity: sha512-doc2sWgJpbFQ64UflSVd17ibMGDuxO1yKgOgLMwavzESnXjFWJqUeG8saYosqKpHp4kWiM5x1nXvEjbpx90gzw==} engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0'} - '@inquirer/checkbox@5.1.0': - resolution: {integrity: sha512-/HjF1LN0a1h4/OFsbGKHNDtWICFU/dqXCdym719HFTyJo9IG7Otr+ziGWc9S0iQuohRZllh+WprSgd5UW5Fw0g==} + '@inquirer/checkbox@5.1.3': + resolution: {integrity: sha512-+G7I8CT+EHv/hasNfUl3P37DVoMoZfpA+2FXmM54dA8MxYle1YqucxbacxHalw1iAFSdKNEDTGNV7F+j1Ldqcg==} engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0'} peerDependencies: '@types/node': ^24.1.0 @@ -1807,8 +1500,8 @@ packages: '@types/node': optional: true - '@inquirer/confirm@6.0.8': - resolution: {integrity: sha512-Di6dgmiZ9xCSUxWUReWTqDtbhXCuG2MQm2xmgSAIruzQzBqNf49b8E07/vbCYY506kDe8BiwJbegXweG8M1klw==} + '@inquirer/confirm@6.0.11': + resolution: {integrity: sha512-pTpHjg0iEIRMYV/7oCZUMf27/383E6Wyhfc/MY+AVQGEoUobffIYWOK9YLP2XFRGz/9i6WlTQh1CkFVIo2Y7XA==} engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0'} peerDependencies: '@types/node': ^24.1.0 @@ -1816,8 +1509,8 @@ packages: '@types/node': optional: true - '@inquirer/core@11.1.5': - resolution: {integrity: sha512-QQPAX+lka8GyLcZ7u7Nb1h6q72iZ/oy0blilC3IB2nSt1Qqxp7akt94Jqhi/DzARuN3Eo9QwJRvtl4tmVe4T5A==} + '@inquirer/core@11.1.8': + resolution: {integrity: sha512-/u+yJk2pOKNDOh1ZgdUH2RQaRx6OOH4I0uwL95qPvTFTIL38YBsuSC4r1yXBB3Q6JvNqFFc202gk0Ew79rrcjA==} engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0'} peerDependencies: '@types/node': ^24.1.0 @@ -1825,8 +1518,8 @@ packages: '@types/node': optional: true - '@inquirer/editor@5.0.8': - resolution: {integrity: sha512-sLcpbb9B3XqUEGrj1N66KwhDhEckzZ4nI/W6SvLXyBX8Wic3LDLENlWRvkOGpCPoserabe+MxQkpiMoI8irvyA==} + '@inquirer/editor@5.1.0': + resolution: {integrity: sha512-6wlkYl65Qfayy48gPCfU4D7li6KCAGN79mLXa/tYHZH99OfZ820yY+HA+DgE88r8YwwgeuY6PQgNqMeK6LuMmw==} engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0'} peerDependencies: '@types/node': ^24.1.0 @@ -1834,8 +1527,8 @@ packages: '@types/node': optional: true - '@inquirer/expand@5.0.8': - resolution: {integrity: sha512-QieW3F1prNw3j+hxO7/NKkG1pk3oz7pOB6+5Upwu3OIwADfPX0oZVppsqlL+Vl/uBHHDSOBY0BirLctLnXwGGg==} + '@inquirer/expand@5.0.12': + resolution: {integrity: sha512-vOfrB33b7YIZfDauXS8vNNz2Z86FozTZLIt7e+7/dCaPJ1RXZsHCuI9TlcERzEUq57vkM+UdnBgxP0rFd23JYQ==} engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0'} peerDependencies: '@types/node': ^24.1.0 @@ -1852,8 +1545,8 @@ packages: '@types/node': optional: true - '@inquirer/external-editor@2.0.3': - resolution: {integrity: sha512-LgyI7Agbda74/cL5MvA88iDpvdXI2KuMBCGRkbCl2Dg1vzHeOgs+s0SDcXV7b+WZJrv2+ERpWSM65Fpi9VfY3w==} + '@inquirer/external-editor@3.0.0': + resolution: {integrity: sha512-lDSwMgg+M5rq6JKBYaJwSX6T9e/HK2qqZ1oxmOwn4AQoJE5D+7TumsxLGC02PWS//rkIVqbZv3XA3ejsc9FYvg==} engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0'} peerDependencies: '@types/node': ^24.1.0 @@ -1861,12 +1554,12 @@ packages: '@types/node': optional: true - '@inquirer/figures@2.0.3': - resolution: {integrity: sha512-y09iGt3JKoOCBQ3w4YrSJdokcD8ciSlMIWsD+auPu+OZpfxLuyz+gICAQ6GCBOmJJt4KEQGHuZSVff2jiNOy7g==} + '@inquirer/figures@2.0.5': + resolution: {integrity: sha512-NsSs4kzfm12lNetHwAn3GEuH317IzpwrMCbOuMIVytpjnJ90YYHNwdRgYGuKmVxwuIqSgqk3M5qqQt1cDk0tGQ==} engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0'} - '@inquirer/input@5.0.8': - resolution: {integrity: sha512-p0IJslw0AmedLEkOU+yrEX3Aj2RTpQq7ZOf8nc1DIhjzaxRWrrgeuE5Kyh39fVRgtcACaMXx/9WNo8+GjgBOfw==} + '@inquirer/input@5.0.11': + resolution: {integrity: sha512-twUWidn4ocPO8qi6fRM7tNWt7W1FOnOZqQ+/+PsfLUacMR5rFLDPK9ql0nBPwxi0oELbo8T5NhRs8B2+qQEqFQ==} engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0'} peerDependencies: '@types/node': ^24.1.0 @@ -1874,8 +1567,8 @@ packages: '@types/node': optional: true - '@inquirer/number@4.0.8': - resolution: {integrity: sha512-uGLiQah9A0F9UIvJBX52m0CnqtLaym0WpT9V4YZrjZ+YRDKZdwwoEPz06N6w8ChE2lrnsdyhY9sL+Y690Kh9gQ==} + '@inquirer/number@4.0.11': + resolution: {integrity: sha512-Vscmim9TCksQsfjPtka/JwPUcbLhqWYrgfPf1cHrCm24X/F2joFwnageD50yMKsaX14oNGOyKf/RNXAFkNjWpA==} engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0'} peerDependencies: '@types/node': ^24.1.0 @@ -1883,8 +1576,8 @@ packages: '@types/node': optional: true - '@inquirer/password@5.0.8': - resolution: {integrity: sha512-zt1sF4lYLdvPqvmvHdmjOzuUUjuCQ897pdUCO8RbXMUDKXJTTyOQgtn23le+jwcb+MpHl3VAFvzIdxRAf6aPlA==} + '@inquirer/password@5.0.11': + resolution: {integrity: sha512-9KZFeRaNHIcejtPb0wN4ddFc7EvobVoAFa049eS3LrDZFxI8O7xUXiITEOinBzkZFAIwY5V4yzQae/QfO9cbbg==} engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0'} peerDependencies: '@types/node': ^24.1.0 @@ -1892,8 +1585,8 @@ packages: '@types/node': optional: true - '@inquirer/prompts@8.3.0': - resolution: {integrity: sha512-JAj66kjdH/F1+B7LCigjARbwstt3SNUOSzMdjpsvwJmzunK88gJeXmcm95L9nw1KynvFVuY4SzXh/3Y0lvtgSg==} + '@inquirer/prompts@8.4.1': + resolution: {integrity: sha512-AH5xPQ997K7e0F0vulPlteIHke2awMkFi8F0dBemrDfmvtPmHJo82mdHbONC4F/t8d1NHwrbI5cGVI+RbLWdoQ==} engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0'} peerDependencies: '@types/node': ^24.1.0 @@ -1901,8 +1594,8 @@ packages: '@types/node': optional: true - '@inquirer/rawlist@5.2.4': - resolution: {integrity: sha512-fTuJ5Cq9W286isLxwj6GGyfTjx1Zdk4qppVEPexFuA6yioCCXS4V1zfKroQqw7QdbDPN73xs2DiIAlo55+kBqg==} + '@inquirer/rawlist@5.2.7': + resolution: {integrity: sha512-AqRMiD9+uE1lskDPrdqHwrV/EUmxKEBLX44SR7uxK3vD2413AmVfE5EQaPeNzYf5Pq5SitHJDYUFVF0poIr09w==} engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0'} peerDependencies: '@types/node': ^24.1.0 @@ -1910,8 +1603,8 @@ packages: '@types/node': optional: true - '@inquirer/search@4.1.4': - resolution: {integrity: sha512-9yPTxq7LPmYjrGn3DRuaPuPbmC6u3fiWcsE9ggfLcdgO/ICHYgxq7mEy1yJ39brVvgXhtOtvDVjDh9slJxE4LQ==} + '@inquirer/search@4.1.7': + resolution: {integrity: sha512-1y7+0N65AWk5RdlXH/Kn13txf3IjIQ7OEfhCEkDTU+h5wKMLq8DUF3P6z+/kLSxDGDtQT1dRBWEUC3o/VvImsQ==} engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0'} peerDependencies: '@types/node': ^24.1.0 @@ -1919,8 +1612,8 @@ packages: '@types/node': optional: true - '@inquirer/select@5.1.0': - resolution: {integrity: sha512-OyYbKnchS1u+zRe14LpYrN8S0wH1vD0p2yKISvSsJdH2TpI87fh4eZdWnpdbrGauCRWDph3NwxRmM4Pcm/hx1Q==} + '@inquirer/select@5.1.3': + resolution: {integrity: sha512-zYyqWgGQi3NhBcNq4Isc5rB3oEdQEh1Q/EcAnOW0FK4MpnXWkvSBYgA4cYrTM4A9UB573omouZbnL9JJ74Mq3A==} engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0'} peerDependencies: '@types/node': ^24.1.0 @@ -1928,8 +1621,8 @@ packages: '@types/node': optional: true - '@inquirer/type@4.0.3': - resolution: {integrity: sha512-cKZN7qcXOpj1h+1eTTcGDVLaBIHNMT1Rz9JqJP5MnEJ0JhgVWllx7H/tahUp5YEK1qaByH2Itb8wLG/iScD5kw==} + '@inquirer/type@4.0.5': + resolution: {integrity: sha512-aetVUNeKNc/VriqXlw1NRSW0zhMBB0W4bNbWRJgzRl/3d0QNDQFfk0GO5SDdtjMZVg6o8ZKEiadd7SCCzoOn5Q==} engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0'} peerDependencies: '@types/node': ^24.1.0 @@ -1937,9 +1630,6 @@ packages: '@types/node': optional: true - '@jridgewell/gen-mapping@0.3.12': - resolution: {integrity: sha512-OuLGC46TjB5BbN1dH8JULVVZY4WTdkF7tV9Ys6wLL1rubZnCMstOhNHueU5bLCrnRuDhKPDM4g6sw4Bel5Gzqg==} - '@jridgewell/gen-mapping@0.3.13': resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} @@ -1950,15 +1640,9 @@ packages: resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} engines: {node: '>=6.0.0'} - '@jridgewell/sourcemap-codec@1.5.4': - resolution: {integrity: sha512-VT2+G1VQs/9oz078bLrYbecdZKs912zQlkelYpuf+SXF+QvZDYJlbx/LSx+meSAwdDFnF8FVXW92AVjjkVmgFw==} - '@jridgewell/sourcemap-codec@1.5.5': resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} - '@jridgewell/trace-mapping@0.3.29': - resolution: {integrity: sha512-uw6guiW/gcAGPDhLmd77/6lW8QLeiV5RUTsAX46Db6oLhGaVj4lhnPwb184s1bkc8kdVg/+h988dro8GRDpmYQ==} - '@jridgewell/trace-mapping@0.3.31': resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} @@ -1976,18 +1660,9 @@ packages: resolution: {integrity: sha512-rY0o9A5ECsTQRVhv7tL/OyDpGAoUB4tTvLiW1DSzQGq4bvTPhNw1VpSNjDJc5GFZ2XuyOtSWSVN05qOtcD71qQ==} engines: {node: '>= 0.6'} - '@mapbox/mapbox-gl-supported@3.0.0': - resolution: {integrity: sha512-2XghOwu16ZwPJLOFVuIOaLbN0iKMn867evzXFyf0P22dqugezfJwLmdanAgU25ITvz1TvOfVP4jsDImlDJzcWg==} - '@mapbox/point-geometry@0.1.0': resolution: {integrity: sha512-6j56HdLTwWGO0fJPlrZtdU/B13q8Uwmo18Ck2GnGgN9PCFyKTZ3UbXeEdRFh18i9XQ92eH2VdtpJHpBD3aripQ==} - '@mapbox/point-geometry@1.1.0': - resolution: {integrity: sha512-YGcBz1cg4ATXDCM/71L9xveh4dynfGmcLDqufR+nQQy3fKwsAZsWd/x4621/6uJaeB9mwOHE6hPeDgXz9uViUQ==} - - '@mapbox/tiny-sdf@2.0.7': - resolution: {integrity: sha512-25gQLQMcpivjOSA40g3gO6qgiFPDpWRoMfd+G/GoppPIeP6JDaMMkMrEJnMZhKyyS6iKwVt5YKu02vCUyJM3Ug==} - '@mapbox/tiny-sdf@2.1.0': resolution: {integrity: sha512-uFJhNh36BR4OCuWIEiWaEix9CA2WzT6CAIcqVjWYpnx8+QDtS+oC4QehRrx5cX4mgWs37MmKnwUejeHxVymzNg==} @@ -1997,9 +1672,6 @@ packages: '@mapbox/vector-tile@1.3.1': resolution: {integrity: sha512-MCEddb8u44/xfQ3oD+Srl/tNcQoqTw3goGk2oLsrFxOTc3dUp+kAnby3PvAeeBYSMSjSPD1nd1AJA6W49WnoUw==} - '@mapbox/vector-tile@2.0.4': - resolution: {integrity: sha512-AkOLcbgGTdXScosBWwmmD7cDlvOjkg/DetGva26pIRiZPdeJYjYKarIlb4uxVzi6bwHO6EWH82eZ5Nuv4T5DUg==} - '@mapbox/whoots-js@3.1.0': resolution: {integrity: sha512-Es6WcD0nO5l+2BOQS4uLfNPYQaNDfbot3X1XUoloz+x0mPDS3eeORZJl06HXjwBG1fOGwCRnzK88LMdxKRrd6Q==} engines: {node: '>=6.0.0'} @@ -2012,8 +1684,8 @@ packages: resolution: {integrity: sha512-AzBy3095fTFPjDjmWpR2w6HVRAZJ6hQZUCwk5Plz6EyfnfuQW1odeW5i2Ai47Y6TBA2hQnC+azscjBSALpaWgw==} hasBin: true - '@modelcontextprotocol/sdk@1.27.1': - resolution: {integrity: sha512-sr6GbP+4edBwFndLbM60gf07z0FQ79gaExpnsjMGePXqFcSSb7t6iscpjk9DhFhwd+mTEQrzNafGP8/iGGFYaA==} + '@modelcontextprotocol/sdk@1.29.0': + resolution: {integrity: sha512-zo37mZA9hJWpULgkRpowewez1y6ML5GsXJPY8FI0tBBCd77HEvza4jDqRKOXgHNn867PVGCyTdzqpz0izu5ZjQ==} engines: {node: '>=18'} peerDependencies: '@cfworker/json-schema': ^4.1.1 @@ -2025,60 +1697,60 @@ packages: '@napi-rs/wasm-runtime@0.2.12': resolution: {integrity: sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ==} - '@next/env@16.2.2': - resolution: {integrity: sha512-LqSGz5+xGk9EL/iBDr2yo/CgNQV6cFsNhRR2xhSXYh7B/hb4nePCxlmDvGEKG30NMHDFf0raqSyOZiQrO7BkHQ==} + '@next/env@16.2.3': + resolution: {integrity: sha512-ZWXyj4uNu4GCWQw9cjRxWlbD+33mcDszIo9iQxFnBX3Wmgq9ulaSJcl6VhuWx5pCWqqD+9W6Wfz7N0lM5lYPMA==} - '@next/eslint-plugin-next@16.2.2': - resolution: {integrity: sha512-IOPbWzDQ+76AtjZioaCjpIY72xNSDMnarZ2GMQ4wjNLvnJEJHqxQwGFhgnIWLV9klb4g/+amg88Tk5OXVpyLTw==} + '@next/eslint-plugin-next@16.2.3': + resolution: {integrity: sha512-nE/b9mht28XJxjTwKs/yk7w4XTaU3t40UHVAky6cjiijdP/SEy3hGsnQMPxmXPTpC7W4/97okm6fngKnvCqVaA==} - '@next/swc-darwin-arm64@16.2.2': - resolution: {integrity: sha512-B92G3ulrwmkDSEJEp9+XzGLex5wC1knrmCSIylyVeiAtCIfvEJYiN3v5kXPlYt5R4RFlsfO/v++aKV63Acrugg==} + '@next/swc-darwin-arm64@16.2.3': + resolution: {integrity: sha512-u37KDKTKQ+OQLvY+z7SNXixwo4Q2/IAJFDzU1fYe66IbCE51aDSAzkNDkWmLN0yjTUh4BKBd+hb69jYn6qqqSg==} engines: {node: '>= 10'} cpu: [arm64] os: [darwin] - '@next/swc-darwin-x64@16.2.2': - resolution: {integrity: sha512-7ZwSgNKJNQiwW0CKhNm9B1WS2L1Olc4B2XY0hPYCAL3epFnugMhuw5TMWzMilQ3QCZcCHoYm9NGWTHbr5REFxw==} + '@next/swc-darwin-x64@16.2.3': + resolution: {integrity: sha512-gHjL/qy6Q6CG3176FWbAKyKh9IfntKZTB3RY/YOJdDFpHGsUDXVH38U4mMNpHVGXmeYW4wj22dMp1lTfmu/bTQ==} engines: {node: '>= 10'} cpu: [x64] os: [darwin] - '@next/swc-linux-arm64-gnu@16.2.2': - resolution: {integrity: sha512-c3m8kBHMziMgo2fICOP/cd/5YlrxDU5YYjAJeQLyFsCqVF8xjOTH/QYG4a2u48CvvZZSj1eHQfBCbyh7kBr30Q==} + '@next/swc-linux-arm64-gnu@16.2.3': + resolution: {integrity: sha512-U6vtblPtU/P14Y/b/n9ZY0GOxbbIhTFuaFR7F4/uMBidCi2nSdaOFhA0Go81L61Zd6527+yvuX44T4ksnf8T+Q==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] libc: [glibc] - '@next/swc-linux-arm64-musl@16.2.2': - resolution: {integrity: sha512-VKLuscm0P/mIfzt+SDdn2+8TNNJ7f0qfEkA+az7OqQbjzKdBxAHs0UvuiVoCtbwX+dqMEL9U54b5wQ/aN3dHeg==} + '@next/swc-linux-arm64-musl@16.2.3': + resolution: {integrity: sha512-/YV0LgjHUmfhQpn9bVoGc4x4nan64pkhWR5wyEV8yCOfwwrH630KpvRg86olQHTwHIn1z59uh6JwKvHq1h4QEw==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] libc: [musl] - '@next/swc-linux-x64-gnu@16.2.2': - resolution: {integrity: sha512-kU3OPHJq6sBUjOk7wc5zJ7/lipn8yGldMoAv4z67j6ov6Xo/JvzA7L7LCsyzzsXmgLEhk3Qkpwqaq/1+XpNR3g==} + '@next/swc-linux-x64-gnu@16.2.3': + resolution: {integrity: sha512-/HiWEcp+WMZ7VajuiMEFGZ6cg0+aYZPqCJD3YJEfpVWQsKYSjXQG06vJP6F1rdA03COD9Fef4aODs3YxKx+RDQ==} engines: {node: '>= 10'} cpu: [x64] os: [linux] libc: [glibc] - '@next/swc-linux-x64-musl@16.2.2': - resolution: {integrity: sha512-CKXRILyErMtUftp+coGcZ38ZwE/Aqq45VMCcRLr2I4OXKrgxIBDXHnBgeX/UMil0S09i2JXaDL3Q+TN8D/cKmg==} + '@next/swc-linux-x64-musl@16.2.3': + resolution: {integrity: sha512-Kt44hGJfZSefebhk/7nIdivoDr3Ugp5+oNz9VvF3GUtfxutucUIHfIO0ZYO8QlOPDQloUVQn4NVC/9JvHRk9hw==} engines: {node: '>= 10'} cpu: [x64] os: [linux] libc: [musl] - '@next/swc-win32-arm64-msvc@16.2.2': - resolution: {integrity: sha512-sS/jSk5VUoShUqINJFvNjVT7JfR5ORYj/+/ZpOYbbIohv/lQfduWnGAycq2wlknbOql2xOR0DoV0s6Xfcy49+g==} + '@next/swc-win32-arm64-msvc@16.2.3': + resolution: {integrity: sha512-O2NZ9ie3Tq6xj5Z5CSwBT3+aWAMW2PIZ4egUi9MaWLkwaehgtB7YZjPm+UpcNpKOme0IQuqDcor7BsW6QBiQBw==} engines: {node: '>= 10'} cpu: [arm64] os: [win32] - '@next/swc-win32-x64-msvc@16.2.2': - resolution: {integrity: sha512-aHaKceJgdySReT7qeck5oShucxWRiiEuwCGK8HHALe6yZga8uyFpLkPgaRw3kkF04U7ROogL/suYCNt/+CuXGA==} + '@next/swc-win32-x64-msvc@16.2.3': + resolution: {integrity: sha512-Ibm29/GgB/ab5n7XKqlStkm54qqZE8v2FnijUPBgrd67FWrac45o/RsNlaOWjme/B5UqeWt/8KM4aWBwA1D2Kw==} engines: {node: '>= 10'} cpu: [x64] os: [win32] @@ -2378,8 +2050,8 @@ packages: '@paulirish/trace_engine@0.0.61': resolution: {integrity: sha512-/O08DwmUqIlJjUSPSZbNF8lWnlxaMsIOV6sS+uDKCxBd5i1psAmjEoG3JAqR6+nHD8X+YY474NW7SxUH/K+/kQ==} - '@playwright/browser-chromium@1.58.2': - resolution: {integrity: sha512-/HHFVVP2pzEtqBVOFRoWlDtBoO8bzIWOqS62APdm3OhYbE2+kVUs/ALpkevvAKKfY0DMDVezYN22bDBh77UQ9Q==} + '@playwright/browser-chromium@1.59.1': + resolution: {integrity: sha512-XDwr0qOrzLXAuBAzg4WO/xctVMb+ldJ54yz9KCpFu8G8MVJzUVFO6BvK1tBtBl4DIoFcoFRKHgUGZT+8wOC8BQ==} engines: {node: '>=18'} '@playwright/test@1.59.1': @@ -2584,277 +2256,139 @@ packages: '@types/react': optional: true - '@rollup/rollup-android-arm-eabi@4.59.0': - resolution: {integrity: sha512-upnNBkA6ZH2VKGcBj9Fyl9IGNPULcjXRlg0LLeaioQWueH30p6IXtJEbKAgvyv+mJaMxSm1l6xwDXYjpEMiLMg==} - cpu: [arm] - os: [android] - '@rollup/rollup-android-arm-eabi@4.60.1': resolution: {integrity: sha512-d6FinEBLdIiK+1uACUttJKfgZREXrF0Qc2SmLII7W2AD8FfiZ9Wjd+rD/iRuf5s5dWrr1GgwXCvPqOuDquOowA==} cpu: [arm] os: [android] - '@rollup/rollup-android-arm64@4.59.0': - resolution: {integrity: sha512-hZ+Zxj3SySm4A/DylsDKZAeVg0mvi++0PYVceVyX7hemkw7OreKdCvW2oQ3T1FMZvCaQXqOTHb8qmBShoqk69Q==} - cpu: [arm64] - os: [android] - '@rollup/rollup-android-arm64@4.60.1': resolution: {integrity: sha512-YjG/EwIDvvYI1YvYbHvDz/BYHtkY4ygUIXHnTdLhG+hKIQFBiosfWiACWortsKPKU/+dUwQQCKQM3qrDe8c9BA==} cpu: [arm64] os: [android] - '@rollup/rollup-darwin-arm64@4.59.0': - resolution: {integrity: sha512-W2Psnbh1J8ZJw0xKAd8zdNgF9HRLkdWwwdWqubSVk0pUuQkoHnv7rx4GiF9rT4t5DIZGAsConRE3AxCdJ4m8rg==} - cpu: [arm64] - os: [darwin] - '@rollup/rollup-darwin-arm64@4.60.1': resolution: {integrity: sha512-mjCpF7GmkRtSJwon+Rq1N8+pI+8l7w5g9Z3vWj4T7abguC4Czwi3Yu/pFaLvA3TTeMVjnu3ctigusqWUfjZzvw==} cpu: [arm64] os: [darwin] - '@rollup/rollup-darwin-x64@4.59.0': - resolution: {integrity: sha512-ZW2KkwlS4lwTv7ZVsYDiARfFCnSGhzYPdiOU4IM2fDbL+QGlyAbjgSFuqNRbSthybLbIJ915UtZBtmuLrQAT/w==} - cpu: [x64] - os: [darwin] - '@rollup/rollup-darwin-x64@4.60.1': resolution: {integrity: sha512-haZ7hJ1JT4e9hqkoT9R/19XW2QKqjfJVv+i5AGg57S+nLk9lQnJ1F/eZloRO3o9Scy9CM3wQ9l+dkXtcBgN5Ew==} cpu: [x64] os: [darwin] - '@rollup/rollup-freebsd-arm64@4.59.0': - resolution: {integrity: sha512-EsKaJ5ytAu9jI3lonzn3BgG8iRBjV4LxZexygcQbpiU0wU0ATxhNVEpXKfUa0pS05gTcSDMKpn3Sx+QB9RlTTA==} - cpu: [arm64] - os: [freebsd] - '@rollup/rollup-freebsd-arm64@4.60.1': resolution: {integrity: sha512-czw90wpQq3ZsAVBlinZjAYTKduOjTywlG7fEeWKUA7oCmpA8xdTkxZZlwNJKWqILlq0wehoZcJYfBvOyhPTQ6w==} cpu: [arm64] os: [freebsd] - '@rollup/rollup-freebsd-x64@4.59.0': - resolution: {integrity: sha512-d3DuZi2KzTMjImrxoHIAODUZYoUUMsuUiY4SRRcJy6NJoZ6iIqWnJu9IScV9jXysyGMVuW+KNzZvBLOcpdl3Vg==} - cpu: [x64] - os: [freebsd] - '@rollup/rollup-freebsd-x64@4.60.1': resolution: {integrity: sha512-KVB2rqsxTHuBtfOeySEyzEOB7ltlB/ux38iu2rBQzkjbwRVlkhAGIEDiiYnO2kFOkJp+Z7pUXKyrRRFuFUKt+g==} cpu: [x64] os: [freebsd] - '@rollup/rollup-linux-arm-gnueabihf@4.59.0': - resolution: {integrity: sha512-t4ONHboXi/3E0rT6OZl1pKbl2Vgxf9vJfWgmUoCEVQVxhW6Cw/c8I6hbbu7DAvgp82RKiH7TpLwxnJeKv2pbsw==} - cpu: [arm] - os: [linux] - libc: [glibc] - '@rollup/rollup-linux-arm-gnueabihf@4.60.1': resolution: {integrity: sha512-L+34Qqil+v5uC0zEubW7uByo78WOCIrBvci69E7sFASRl0X7b/MB6Cqd1lky/CtcSVTydWa2WZwFuWexjS5o6g==} cpu: [arm] os: [linux] libc: [glibc] - '@rollup/rollup-linux-arm-musleabihf@4.59.0': - resolution: {integrity: sha512-CikFT7aYPA2ufMD086cVORBYGHffBo4K8MQ4uPS/ZnY54GKj36i196u8U+aDVT2LX4eSMbyHtyOh7D7Zvk2VvA==} - cpu: [arm] - os: [linux] - libc: [musl] - '@rollup/rollup-linux-arm-musleabihf@4.60.1': resolution: {integrity: sha512-n83O8rt4v34hgFzlkb1ycniJh7IR5RCIqt6mz1VRJD6pmhRi0CXdmfnLu9dIUS6buzh60IvACM842Ffb3xd6Gg==} cpu: [arm] os: [linux] libc: [musl] - '@rollup/rollup-linux-arm64-gnu@4.59.0': - resolution: {integrity: sha512-jYgUGk5aLd1nUb1CtQ8E+t5JhLc9x5WdBKew9ZgAXg7DBk0ZHErLHdXM24rfX+bKrFe+Xp5YuJo54I5HFjGDAA==} - cpu: [arm64] - os: [linux] - libc: [glibc] - '@rollup/rollup-linux-arm64-gnu@4.60.1': resolution: {integrity: sha512-Nql7sTeAzhTAja3QXeAI48+/+GjBJ+QmAH13snn0AJSNL50JsDqotyudHyMbO2RbJkskbMbFJfIJKWA6R1LCJQ==} cpu: [arm64] os: [linux] libc: [glibc] - '@rollup/rollup-linux-arm64-musl@4.59.0': - resolution: {integrity: sha512-peZRVEdnFWZ5Bh2KeumKG9ty7aCXzzEsHShOZEFiCQlDEepP1dpUl/SrUNXNg13UmZl+gzVDPsiCwnV1uI0RUA==} - cpu: [arm64] - os: [linux] - libc: [musl] - '@rollup/rollup-linux-arm64-musl@4.60.1': resolution: {integrity: sha512-+pUymDhd0ys9GcKZPPWlFiZ67sTWV5UU6zOJat02M1+PiuSGDziyRuI/pPue3hoUwm2uGfxdL+trT6Z9rxnlMA==} cpu: [arm64] os: [linux] libc: [musl] - '@rollup/rollup-linux-loong64-gnu@4.59.0': - resolution: {integrity: sha512-gbUSW/97f7+r4gHy3Jlup8zDG190AuodsWnNiXErp9mT90iCy9NKKU0Xwx5k8VlRAIV2uU9CsMnEFg/xXaOfXg==} - cpu: [loong64] - os: [linux] - libc: [glibc] - '@rollup/rollup-linux-loong64-gnu@4.60.1': resolution: {integrity: sha512-VSvgvQeIcsEvY4bKDHEDWcpW4Yw7BtlKG1GUT4FzBUlEKQK0rWHYBqQt6Fm2taXS+1bXvJT6kICu5ZwqKCnvlQ==} cpu: [loong64] os: [linux] libc: [glibc] - '@rollup/rollup-linux-loong64-musl@4.59.0': - resolution: {integrity: sha512-yTRONe79E+o0FWFijasoTjtzG9EBedFXJMl888NBEDCDV9I2wGbFFfJQQe63OijbFCUZqxpHz1GzpbtSFikJ4Q==} - cpu: [loong64] - os: [linux] - libc: [musl] - '@rollup/rollup-linux-loong64-musl@4.60.1': resolution: {integrity: sha512-4LqhUomJqwe641gsPp6xLfhqWMbQV04KtPp7/dIp0nzPxAkNY1AbwL5W0MQpcalLYk07vaW9Kp1PBhdpZYYcEw==} cpu: [loong64] os: [linux] libc: [musl] - '@rollup/rollup-linux-ppc64-gnu@4.59.0': - resolution: {integrity: sha512-sw1o3tfyk12k3OEpRddF68a1unZ5VCN7zoTNtSn2KndUE+ea3m3ROOKRCZxEpmT9nsGnogpFP9x6mnLTCaoLkA==} - cpu: [ppc64] - os: [linux] - libc: [glibc] - '@rollup/rollup-linux-ppc64-gnu@4.60.1': resolution: {integrity: sha512-tLQQ9aPvkBxOc/EUT6j3pyeMD6Hb8QF2BTBnCQWP/uu1lhc9AIrIjKnLYMEroIz/JvtGYgI9dF3AxHZNaEH0rw==} cpu: [ppc64] os: [linux] libc: [glibc] - '@rollup/rollup-linux-ppc64-musl@4.59.0': - resolution: {integrity: sha512-+2kLtQ4xT3AiIxkzFVFXfsmlZiG5FXYW7ZyIIvGA7Bdeuh9Z0aN4hVyXS/G1E9bTP/vqszNIN/pUKCk/BTHsKA==} - cpu: [ppc64] - os: [linux] - libc: [musl] - '@rollup/rollup-linux-ppc64-musl@4.60.1': resolution: {integrity: sha512-RMxFhJwc9fSXP6PqmAz4cbv3kAyvD1etJFjTx4ONqFP9DkTkXsAMU4v3Vyc5BgzC+anz7nS/9tp4obsKfqkDHg==} cpu: [ppc64] os: [linux] libc: [musl] - '@rollup/rollup-linux-riscv64-gnu@4.59.0': - resolution: {integrity: sha512-NDYMpsXYJJaj+I7UdwIuHHNxXZ/b/N2hR15NyH3m2qAtb/hHPA4g4SuuvrdxetTdndfj9b1WOmy73kcPRoERUg==} - cpu: [riscv64] - os: [linux] - libc: [glibc] - '@rollup/rollup-linux-riscv64-gnu@4.60.1': resolution: {integrity: sha512-QKgFl+Yc1eEk6MmOBfRHYF6lTxiiiV3/z/BRrbSiW2I7AFTXoBFvdMEyglohPj//2mZS4hDOqeB0H1ACh3sBbg==} cpu: [riscv64] os: [linux] libc: [glibc] - '@rollup/rollup-linux-riscv64-musl@4.59.0': - resolution: {integrity: sha512-nLckB8WOqHIf1bhymk+oHxvM9D3tyPndZH8i8+35p/1YiVoVswPid2yLzgX7ZJP0KQvnkhM4H6QZ5m0LzbyIAg==} - cpu: [riscv64] - os: [linux] - libc: [musl] - '@rollup/rollup-linux-riscv64-musl@4.60.1': resolution: {integrity: sha512-RAjXjP/8c6ZtzatZcA1RaQr6O1TRhzC+adn8YZDnChliZHviqIjmvFwHcxi4JKPSDAt6Uhf/7vqcBzQJy0PDJg==} cpu: [riscv64] os: [linux] libc: [musl] - '@rollup/rollup-linux-s390x-gnu@4.59.0': - resolution: {integrity: sha512-oF87Ie3uAIvORFBpwnCvUzdeYUqi2wY6jRFWJAy1qus/udHFYIkplYRW+wo+GRUP4sKzYdmE1Y3+rY5Gc4ZO+w==} - cpu: [s390x] - os: [linux] - libc: [glibc] - '@rollup/rollup-linux-s390x-gnu@4.60.1': resolution: {integrity: sha512-wcuocpaOlaL1COBYiA89O6yfjlp3RwKDeTIA0hM7OpmhR1Bjo9j31G1uQVpDlTvwxGn2nQs65fBFL5UFd76FcQ==} cpu: [s390x] os: [linux] libc: [glibc] - '@rollup/rollup-linux-x64-gnu@4.59.0': - resolution: {integrity: sha512-3AHmtQq/ppNuUspKAlvA8HtLybkDflkMuLK4DPo77DfthRb71V84/c4MlWJXixZz4uruIH4uaa07IqoAkG64fg==} - cpu: [x64] - os: [linux] - libc: [glibc] - '@rollup/rollup-linux-x64-gnu@4.60.1': resolution: {integrity: sha512-77PpsFQUCOiZR9+LQEFg9GClyfkNXj1MP6wRnzYs0EeWbPcHs02AXu4xuUbM1zhwn3wqaizle3AEYg5aeoohhg==} cpu: [x64] os: [linux] libc: [glibc] - '@rollup/rollup-linux-x64-musl@4.59.0': - resolution: {integrity: sha512-2UdiwS/9cTAx7qIUZB/fWtToJwvt0Vbo0zmnYt7ED35KPg13Q0ym1g442THLC7VyI6JfYTP4PiSOWyoMdV2/xg==} - cpu: [x64] - os: [linux] - libc: [musl] - '@rollup/rollup-linux-x64-musl@4.60.1': resolution: {integrity: sha512-5cIATbk5vynAjqqmyBjlciMJl1+R/CwX9oLk/EyiFXDWd95KpHdrOJT//rnUl4cUcskrd0jCCw3wpZnhIHdD9w==} cpu: [x64] os: [linux] libc: [musl] - '@rollup/rollup-openbsd-x64@4.59.0': - resolution: {integrity: sha512-M3bLRAVk6GOwFlPTIxVBSYKUaqfLrn8l0psKinkCFxl4lQvOSz8ZrKDz2gxcBwHFpci0B6rttydI4IpS4IS/jQ==} - cpu: [x64] - os: [openbsd] - '@rollup/rollup-openbsd-x64@4.60.1': resolution: {integrity: sha512-cl0w09WsCi17mcmWqqglez9Gk8isgeWvoUZ3WiJFYSR3zjBQc2J5/ihSjpl+VLjPqjQ/1hJRcqBfLjssREQILw==} cpu: [x64] os: [openbsd] - '@rollup/rollup-openharmony-arm64@4.59.0': - resolution: {integrity: sha512-tt9KBJqaqp5i5HUZzoafHZX8b5Q2Fe7UjYERADll83O4fGqJ49O1FsL6LpdzVFQcpwvnyd0i+K/VSwu/o/nWlA==} - cpu: [arm64] - os: [openharmony] - '@rollup/rollup-openharmony-arm64@4.60.1': resolution: {integrity: sha512-4Cv23ZrONRbNtbZa37mLSueXUCtN7MXccChtKpUnQNgF010rjrjfHx3QxkS2PI7LqGT5xXyYs1a7LbzAwT0iCA==} cpu: [arm64] os: [openharmony] - '@rollup/rollup-win32-arm64-msvc@4.59.0': - resolution: {integrity: sha512-V5B6mG7OrGTwnxaNUzZTDTjDS7F75PO1ae6MJYdiMu60sq0CqN5CVeVsbhPxalupvTX8gXVSU9gq+Rx1/hvu6A==} - cpu: [arm64] - os: [win32] - '@rollup/rollup-win32-arm64-msvc@4.60.1': resolution: {integrity: sha512-i1okWYkA4FJICtr7KpYzFpRTHgy5jdDbZiWfvny21iIKky5YExiDXP+zbXzm3dUcFpkEeYNHgQ5fuG236JPq0g==} cpu: [arm64] os: [win32] - '@rollup/rollup-win32-ia32-msvc@4.59.0': - resolution: {integrity: sha512-UKFMHPuM9R0iBegwzKF4y0C4J9u8C6MEJgFuXTBerMk7EJ92GFVFYBfOZaSGLu6COf7FxpQNqhNS4c4icUPqxA==} - cpu: [ia32] - os: [win32] - '@rollup/rollup-win32-ia32-msvc@4.60.1': resolution: {integrity: sha512-u09m3CuwLzShA0EYKMNiFgcjjzwqtUMLmuCJLeZWjjOYA3IT2Di09KaxGBTP9xVztWyIWjVdsB2E9goMjZvTQg==} cpu: [ia32] os: [win32] - '@rollup/rollup-win32-x64-gnu@4.59.0': - resolution: {integrity: sha512-laBkYlSS1n2L8fSo1thDNGrCTQMmxjYY5G0WFWjFFYZkKPjsMBsgJfGf4TLxXrF6RyhI60L8TMOjBMvXiTcxeA==} - cpu: [x64] - os: [win32] - '@rollup/rollup-win32-x64-gnu@4.60.1': resolution: {integrity: sha512-k+600V9Zl1CM7eZxJgMyTUzmrmhB/0XZnF4pRypKAlAgxmedUA+1v9R+XOFv56W4SlHEzfeMtzujLJD22Uz5zg==} cpu: [x64] os: [win32] - '@rollup/rollup-win32-x64-msvc@4.59.0': - resolution: {integrity: sha512-2HRCml6OztYXyJXAvdDXPKcawukWY2GpR5/nxKp4iBgiO3wcoEGkAaqctIbZcNB6KlUQBIqt8VYkNSj2397EfA==} - cpu: [x64] - os: [win32] - '@rollup/rollup-win32-x64-msvc@4.60.1': resolution: {integrity: sha512-lWMnixq/QzxyhTV6NjQJ4SFo1J6PvOX8vUx5Wb4bBPsEb+8xZ89Bz6kOXpfXj9ak9AHTQVQzlgzBEc1SyM27xQ==} cpu: [x64] @@ -2906,6 +2440,20 @@ packages: engines: {node: '>=20.0.0'} hasBin: true + '@stackwright/build-scripts@0.4.0-alpha.7': + resolution: {integrity: sha512-54WaQGI/dhcps35UpxWo7jmZLkdE3Arz0ANEcOvsqD3NhTWoiKFujvEptDjvgMt0OJ+Pgo5HxoHCLOa2RAxGhg==} + engines: {node: '>=20.0.0'} + hasBin: true + + '@stackwright/cli@0.7.0-alpha.11': + resolution: {integrity: sha512-x+lAo+89F9gEzTkns/gCPROAqBUgpDf4eAEi10FSfWlGC3wN4q0mm2elU11Gj+4o1iOGrFq4CcqMUYZiA4LXtQ==} + hasBin: true + peerDependencies: + playwright: ^1.52.0 + peerDependenciesMeta: + playwright: + optional: true + '@stackwright/collections@0.1.0': resolution: {integrity: sha512-jKlS0sHJDfnmtWEHt86OQL1LLgrsAPdBFD8zlDiQXyE4yQ1ZGHYNAvvAkQp/GKGgQmLMEhWK5/5Ek67EolC+tA==} @@ -2920,6 +2468,10 @@ packages: peerDependencies: react: 19.2.4 + '@stackwright/mcp@0.3.0-alpha.11': + resolution: {integrity: sha512-eONzJPvnWyR8lKcoLFhQTYFeI39dDpkHvWwUY0YeLdQWcwgYSq/u7xVjnwYYl2lzdHFGwqIubdrDhPWx2DD9zw==} + hasBin: true + '@stackwright/nextjs@0.3.1-alpha.7': resolution: {integrity: sha512-u9ql3yBEidShEhLpcoygcCl/KNM7MtekP1SxBiOfXtEDJY9RS2GE68TH0T+HMLAlreCybNrRnaiyMDTFSzg50w==} peerDependencies: @@ -2930,6 +2482,14 @@ packages: '@stackwright/otters@0.2.0': resolution: {integrity: sha512-SCcD3kam5niwL9N9Jw+rWaZR5TrBM+J3F6GI3oG+ANBMCmLiS074tb0zjQOvT4WuaQBbg5ejedHXLccFuKNNbQ==} + '@stackwright/sbom-generator@0.1.0-alpha.0': + resolution: {integrity: sha512-Jq/sKpW5i22Gn3x7kEMqnwux0xRWKJA5R4a+7ovLGfIfGyrbudOJ02zbxptZ/GMx2GcX4KJu1XOG0S0HD0h1ZA==} + engines: {node: '>=20.0.0'} + + '@stackwright/scaffold-core@0.1.0-alpha.1': + resolution: {integrity: sha512-rMtavpBneLBTFvV0NLKbnJyT99gR+5d51ABDmE/F769LXtTjsDwLd7zZK1xpFaxri/KETtVa/7ZZ8TmRyGwFVQ==} + engines: {node: '>=20.0.0'} + '@stackwright/themes@0.5.1-alpha.0': resolution: {integrity: sha512-nqShTWeEbm03Z7NAQXaw9pK06d9Wkusjn9K4QT4GQVDiVJW4Zje6Q0QopqFVEAlKYK0pkWfz9smAjzQ6BgcryQ==} peerDependencies: @@ -2947,72 +2507,86 @@ packages: '@standard-schema/spec@1.1.0': resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==} - '@swc/core-darwin-arm64@1.15.18': - resolution: {integrity: sha512-+mIv7uBuSaywN3C9LNuWaX1jJJ3SKfiJuE6Lr3bd+/1Iv8oMU7oLBjYMluX1UrEPzwN2qCdY6Io0yVicABoCwQ==} + '@swc/core-darwin-arm64@1.15.24': + resolution: {integrity: sha512-uM5ZGfFXjtvtJ+fe448PVBEbn/CSxS3UAyLj3O9xOqKIWy3S6hPTXSPbszxkSsGDYKi+YFhzAsR4r/eXLxEQ0g==} engines: {node: '>=10'} cpu: [arm64] os: [darwin] - '@swc/core-darwin-x64@1.15.18': - resolution: {integrity: sha512-wZle0eaQhnzxWX5V/2kEOI6Z9vl/lTFEC6V4EWcn+5pDjhemCpQv9e/TDJ0GIoiClX8EDWRvuZwh+Z3dhL1NAg==} + '@swc/core-darwin-x64@1.15.24': + resolution: {integrity: sha512-fMIb/Zfn929pw25VMBhV7Ji2Dl+lCWtUPNdYJQYOke+00E5fcQ9ynxtP8+qhUo/HZc+mYQb1gJxwHM9vty+lXg==} engines: {node: '>=10'} cpu: [x64] os: [darwin] - '@swc/core-linux-arm-gnueabihf@1.15.18': - resolution: {integrity: sha512-ao61HGXVqrJFHAcPtF4/DegmwEkVCo4HApnotLU8ognfmU8x589z7+tcf3hU+qBiU1WOXV5fQX6W9Nzs6hjxDw==} + '@swc/core-linux-arm-gnueabihf@1.15.24': + resolution: {integrity: sha512-vOkjsyjjxnoYx3hMEWcGxQrMgnNrRm6WAegBXrN8foHtDAR+zpdhpGF5a4lj1bNPgXAvmysjui8cM1ov/Clkaw==} engines: {node: '>=10'} cpu: [arm] os: [linux] - '@swc/core-linux-arm64-gnu@1.15.18': - resolution: {integrity: sha512-3xnctOBLIq3kj8PxOCgPrGjBLP/kNOddr6f5gukYt/1IZxsITQaU9TDyjeX6jG+FiCIHjCuWuffsyQDL5Ew1bg==} + '@swc/core-linux-arm64-gnu@1.15.24': + resolution: {integrity: sha512-h/oNu+upkXJ6Cicnq7YGVj9PkdfarLCdQa8l/FlHYvfv8CEiMaeeTnpLU7gSBH/rGxosM6Qkfa/J9mThGF9CLA==} engines: {node: '>=10'} cpu: [arm64] os: [linux] libc: [glibc] - '@swc/core-linux-arm64-musl@1.15.18': - resolution: {integrity: sha512-0a+Lix+FSSHBSBOA0XznCcHo5/1nA6oLLjcnocvzXeqtdjnPb+SvchItHI+lfeiuj1sClYPDvPMLSLyXFaiIKw==} + '@swc/core-linux-arm64-musl@1.15.24': + resolution: {integrity: sha512-ZpF/pRe1guk6sKzQI9D1jAORtjTdNlyeXn9GDz8ophof/w2WhojRblvSDJaGe7rJjcPN8AaOkhwdRUh7q8oYIg==} engines: {node: '>=10'} cpu: [arm64] os: [linux] libc: [musl] - '@swc/core-linux-x64-gnu@1.15.18': - resolution: {integrity: sha512-wG9J8vReUlpaHz4KOD/5UE1AUgirimU4UFT9oZmupUDEofxJKYb1mTA/DrMj0s78bkBiNI+7Fo2EgPuvOJfuAA==} + '@swc/core-linux-ppc64-gnu@1.15.24': + resolution: {integrity: sha512-QZEsZfisHTSJlmyChgDFNmKPb3W6Lhbfo/O76HhIngfEdnQNmukS38/VSe1feho+xkV5A5hETyCbx3sALBZKAQ==} + engines: {node: '>=10'} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@swc/core-linux-s390x-gnu@1.15.24': + resolution: {integrity: sha512-DLdJKVsJgglqQrJBuoUYNmzm3leI7kUZhLbZGHv42onfKsGf6JDS3+bzCUQfte/XOqDjh/tmmn1DR/CF/tCJFw==} + engines: {node: '>=10'} + cpu: [s390x] + os: [linux] + libc: [glibc] + + '@swc/core-linux-x64-gnu@1.15.24': + resolution: {integrity: sha512-IpLYfposPA/XLxYOKpRfeccl1p5dDa3+okZDHHTchBkXEaVCnq5MADPmIWwIYj1tudt7hORsEHccG5no6IUQRw==} engines: {node: '>=10'} cpu: [x64] os: [linux] libc: [glibc] - '@swc/core-linux-x64-musl@1.15.18': - resolution: {integrity: sha512-4nwbVvCphKzicwNWRmvD5iBaZj8JYsRGa4xOxJmOyHlMDpsvvJ2OR2cODlvWyGFH6BYL1MfIAK3qph3hp0Az6g==} + '@swc/core-linux-x64-musl@1.15.24': + resolution: {integrity: sha512-JHy3fMSc0t/EPWgo74+OK5TGr51aElnzqfUPaiRf2qJ/BfX5CUCfMiWVBuhI7qmVMBnk1jTRnL/xZnOSHDPLYg==} engines: {node: '>=10'} cpu: [x64] os: [linux] libc: [musl] - '@swc/core-win32-arm64-msvc@1.15.18': - resolution: {integrity: sha512-zk0RYO+LjiBCat2RTMHzAWaMky0cra9loH4oRrLKLLNuL+jarxKLFDA8xTZWEkCPLjUTwlRN7d28eDLLMgtUcQ==} + '@swc/core-win32-arm64-msvc@1.15.24': + resolution: {integrity: sha512-Txj+qUH1z2bUd1P3JvwByfjKFti3cptlAxhWgmunBUUxy/IW3CXLZ6l6Gk4liANadKkU71nIU1X30Z5vpMT3BA==} engines: {node: '>=10'} cpu: [arm64] os: [win32] - '@swc/core-win32-ia32-msvc@1.15.18': - resolution: {integrity: sha512-yVuTrZ0RccD5+PEkpcLOBAuPbYBXS6rslENvIXfvJGXSdX5QGi1ehC4BjAMl5FkKLiam4kJECUI0l7Hq7T1vwg==} + '@swc/core-win32-ia32-msvc@1.15.24': + resolution: {integrity: sha512-15D/nl3XwrhFpMv+MADFOiVwv3FvH9j8c6Rf8EXBT3Q5LoMh8YnDnSgPYqw1JzPnksvsBX6QPXLiPqmcR/Z4qQ==} engines: {node: '>=10'} cpu: [ia32] os: [win32] - '@swc/core-win32-x64-msvc@1.15.18': - resolution: {integrity: sha512-7NRmE4hmUQNCbYU3Hn9Tz57mK9Qq4c97ZS+YlamlK6qG9Fb5g/BB3gPDe0iLlJkns/sYv2VWSkm8c3NmbEGjbg==} + '@swc/core-win32-x64-msvc@1.15.24': + resolution: {integrity: sha512-PR0PlTlPra2JbaDphrOAzm6s0v9rA0F17YzB+XbWD95B4g2cWcZY9LAeTa4xll70VLw9Jr7xBrlohqlQmelMFQ==} engines: {node: '>=10'} cpu: [x64] os: [win32] - '@swc/core@1.15.18': - resolution: {integrity: sha512-z87aF9GphWp//fnkRsqvtY+inMVPgYW3zSlXH1kJFvRT5H/wiAn+G32qW5l3oEk63KSF1x3Ov0BfHCObAmT8RA==} + '@swc/core@1.15.24': + resolution: {integrity: sha512-5Hj8aNasue7yusUt8LGCUe/AjM7RMAce8ZoyDyiFwx7Al+GbYKL+yE7g4sJk8vEr1dKIkTRARkNIJENc4CjkBQ==} engines: {node: '>=10'} peerDependencies: '@swc/helpers': '>=0.5.17' @@ -3026,8 +2600,8 @@ packages: '@swc/helpers@0.5.15': resolution: {integrity: sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==} - '@swc/types@0.1.25': - resolution: {integrity: sha512-iAoY/qRhNH8a/hBvm3zKj9qQ4oc2+3w1unPJa2XvTK3XjeLXtzcCingVPw/9e5mn1+0yPqxcBGp9Jf0pkfMb1g==} + '@swc/types@0.1.26': + resolution: {integrity: sha512-lyMwd7WGgG79RS7EERZV3T8wMdmPq3xwyg+1nmAM64kIhx5yl+juO2PYIHb7vTiPgPCj8LYjsNV2T5wiQHUEaw==} '@tailwindcss/cli@4.2.2': resolution: {integrity: sha512-iJS+8kAFZ8HPqnh0O5DHCLjo4L6dD97DBQEkrhfSO4V96xeefUus2jqsBs1dUMt3OU9Ks4qIkiY0mpL5UW+4LQ==} @@ -3126,16 +2700,12 @@ packages: resolution: {integrity: sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg==} engines: {node: '>=18'} - '@testing-library/jest-dom@6.6.4': - resolution: {integrity: sha512-xDXgLjVunjHqczScfkCJ9iyjdNOVHvvCdqHSSxwM9L0l/wHkTRum67SDc020uAlCoqktJplgO2AAQeLP1wgqDQ==} - engines: {node: '>=14', npm: '>=6', yarn: '>=1'} - '@testing-library/jest-dom@6.9.1': resolution: {integrity: sha512-zIcONa+hVtVSSep9UT3jZ5rizo2BsxgyDYU7WFD5eICBE7no3881HGeb/QkGfsJs6JTkY1aQhT7rIPC7e+0nnA==} engines: {node: '>=14', npm: '>=6', yarn: '>=1'} - '@testing-library/react@16.3.0': - resolution: {integrity: sha512-kFSyxiEDwv1WLl2fgsq6pPBbw5aWKrsY2/noi1Id0TK0UParSF62oFQFGHXIyaG4pp2tEub/Zlel+fjjZILDsw==} + '@testing-library/react@16.3.2': + resolution: {integrity: sha512-XU5/SytQM+ykqMnAnvB2umaJNIOsLF3PVv//1Ew4CTcpz0/BRyy/af40qqrt7SjKpDdT1saBMc42CUok5gaw+g==} engines: {node: '>=18'} peerDependencies: '@testing-library/dom': ^10.0.0 @@ -3152,33 +2722,33 @@ packages: '@tootallnate/quickjs-emscripten@0.23.0': resolution: {integrity: sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==} - '@turbo/darwin-64@2.9.5': - resolution: {integrity: sha512-qPxhKsLMQP+9+dsmPgAGidi5uNifD4AoAOnEnljab3Qgn0QZRR31Hp+/CgW3Ia5AanWj6JuLLTBYvuQj4mqTWg==} + '@turbo/darwin-64@2.9.6': + resolution: {integrity: sha512-X/56SnVXIQZBLKwniGTwEQTGmtE5brSACnKMBWpY3YafuxVYefrC2acamfjgxP7BG5w3I+6jf0UrLoSzgPcSJg==} cpu: [x64] os: [darwin] - '@turbo/darwin-arm64@2.9.5': - resolution: {integrity: sha512-vkF/9F/l3aWd4bHxTui5Hh0F5xrTZ4e3rbBsc57zA6O8gNbmHN3B6eZ5psAIP2CnJRZ8ZxRjV3WZHeNXMXkPBw==} + '@turbo/darwin-arm64@2.9.6': + resolution: {integrity: sha512-aalBeSl4agT/QtYGDyf/XLajedWzUC9Vg/pm/YO6QQ93vkQ91Vz5uK1ta5RbVRDozQSz4njxUNqRNmOXDzW+qw==} cpu: [arm64] os: [darwin] - '@turbo/linux-64@2.9.5': - resolution: {integrity: sha512-z/Get5NUaUxm5HSGFqVMICDRjFNsCUhSc4wnFa/PP1QD0NXCjr7bu9a2EM6md/KMCBW0Qe393Ac+UM7/ryDDTw==} + '@turbo/linux-64@2.9.6': + resolution: {integrity: sha512-YKi05jnNHaD7vevgYwahpzGwbsNNTwzU2c7VZdmdFm7+cGDP4oREUWSsainiMfRqjRuolQxBwRn8wf1jmu+YZA==} cpu: [x64] os: [linux] - '@turbo/linux-arm64@2.9.5': - resolution: {integrity: sha512-jyBifaNoI5/NheyswomiZXJvjdAdvT7hDRYzQ4meP0DKGvpXUjnqsD+4/J2YSDQ34OHxFkL30FnSCUIVOh2PHw==} + '@turbo/linux-arm64@2.9.6': + resolution: {integrity: sha512-02o/ZS69cOYEDczXvOB2xmyrtzjQ2hVFtWZK1iqxXUfzMmTjZK4UumrfNnjckSg+gqeBfnPRHa0NstA173Ik3g==} cpu: [arm64] os: [linux] - '@turbo/windows-64@2.9.5': - resolution: {integrity: sha512-ph24K5uPtvo7UfuyDXnBiB/8XvrO+RQWbbw5zkA/bVNoy9HDiNoIJJj3s62MxT9tjEb6DnPje5PXSz1UR7QAyg==} + '@turbo/windows-64@2.9.6': + resolution: {integrity: sha512-wVdQjvnBI15wB6JrA+43CtUtagjIMmX6XYO758oZHAsCNSxqRlJtdyujih0D8OCnwCRWiGWGI63zAxR0hO6s9g==} cpu: [x64] os: [win32] - '@turbo/windows-arm64@2.9.5': - resolution: {integrity: sha512-6c5RccT/+iR39SdT1G5HyZaD2n57W77o+l0TTfxG/cVlhV94Acyg2gTQW7zUOhW1BeQpBjHzu9x8yVBZwrHh7g==} + '@turbo/windows-arm64@2.9.6': + resolution: {integrity: sha512-1XUUyWW0W6FTSqGEhU8RHVqb2wP1SPkr7hIvBlMEwH9jr+sJQK5kqeosLJ/QaUv4ecSAd1ZhIrLoW7qslAzT4A==} cpu: [arm64] os: [win32] @@ -3188,8 +2758,8 @@ packages: '@types/aria-query@5.0.4': resolution: {integrity: sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==} - '@types/chai@5.2.2': - resolution: {integrity: sha512-8kB30R7Hwqf40JPiKhVzodJs2Qc1ZJ5zuT3uzw5Hq/dhNCl3G3l83jfpdI1e20BP348+fV7VIL/+FxaXkqBmWg==} + '@types/chai@5.2.3': + resolution: {integrity: sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==} '@types/connect@3.4.38': resolution: {integrity: sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==} @@ -3230,14 +2800,8 @@ packages: '@types/mysql@2.15.26': resolution: {integrity: sha512-DSLCOXhkvfS5WNNPbfn2KdICAmk8lLc+/PNvnPnF7gOdMZCxopXduqv0OQ13y/yA/zXTSikZZqVgybUxOEg6YQ==} - '@types/node@24.10.13': - resolution: {integrity: sha512-oH72nZRfDv9lADUBSo104Aq7gPHpQZc4BTx38r9xf9pg5LfP6EzSyH2n7qFmmxRQXh7YlUXODcYsg6PuTDSxGg==} - - '@types/node@24.11.0': - resolution: {integrity: sha512-fPxQqz4VTgPI/IQ+lj9r0h+fDR66bzoeMGHp8ASee+32OSGIkeASsoZuJixsQoVef1QJbeubcPBxKk22QVoWdw==} - - '@types/node@24.2.1': - resolution: {integrity: sha512-DRh5K+ka5eJic8CjH7td8QpYEV6Zo10gfRkjHCO3weqZHWDtAaSTFtl4+VMqOJ4N5jcuhZ9/l+yy8rVgw7BQeQ==} + '@types/node@24.12.2': + resolution: {integrity: sha512-A1sre26ke7HDIuY/M23nd9gfB+nrmhtYyMINbjI1zHJxYteKR6qSMX56FsmjMcDb3SMcjJg5BiRRgOCC/yBD0g==} '@types/pbf@3.0.5': resolution: {integrity: sha512-j3pOPiEcWZ34R6a6mN07mUkM4o4Lwf6hPNt8eilOeZhTFbxFXmKhvXl9Y28jotFPaI1bpPDJsbCprUoNke6OrA==} @@ -3436,8 +3000,8 @@ packages: cpu: [x64] os: [win32] - '@vis.gl/react-mapbox@8.1.0': - resolution: {integrity: sha512-FwvH822oxEjWYOr+pP2L8hpv+7cZB2UsQbHHHT0ryrkvvqzmTgt7qHDhamv0EobKw86e1I+B4ojENdJ5G5BkyQ==} + '@vis.gl/react-mapbox@8.1.1': + resolution: {integrity: sha512-KMDTjtWESXxHS4uqWxjsvgQUHvuL3Z6SdKe68o7Nxma2qUfuyH3x4TCkIqGn3FQTrFvZLWvTnSAbGvtm+Kd13A==} peerDependencies: mapbox-gl: '>=3.5.0' react: 19.2.4 @@ -3446,8 +3010,8 @@ packages: mapbox-gl: optional: true - '@vis.gl/react-maplibre@8.1.0': - resolution: {integrity: sha512-PkAK/gp3mUfhCLhUuc+4gc3PN9zCtVGxTF2hB6R5R5yYUw+hdg84OZ770U5MU4tPMTCG6fbduExuIW6RRKN6qQ==} + '@vis.gl/react-maplibre@8.1.1': + resolution: {integrity: sha512-iUOfzJAhFAJwEZp1644tQb7LOTFgi5/GzdaztkhzNgFVuoF2Ez7guvwZjQAKB9CN2TlHTgNuYH8UW85kO7cVhw==} peerDependencies: maplibre-gl: '>=4.0.0' react: 19.2.4 @@ -3456,20 +3020,20 @@ packages: maplibre-gl: optional: true - '@vitest/coverage-v8@4.1.3': - resolution: {integrity: sha512-/MBdrkA8t6hbdCWFKs09dPik774xvs4Z6L4bycdCxYNLHM8oZuRyosumQMG19LUlBsB6GeVpL1q4kFFazvyKGA==} + '@vitest/coverage-v8@4.1.4': + resolution: {integrity: sha512-x7FptB5oDruxNPDNY2+S8tCh0pcq7ymCe1gTHcsp733jYjrJl8V1gMUlVysuCD9Kz46Xz9t1akkv08dPcYDs1w==} peerDependencies: - '@vitest/browser': 4.1.3 + '@vitest/browser': 4.1.4 vitest: ^4.1.3 peerDependenciesMeta: '@vitest/browser': optional: true - '@vitest/expect@4.1.3': - resolution: {integrity: sha512-CW8Q9KMtXDGHj0vCsqui0M5KqRsu0zm0GNDW7Gd3U7nZ2RFpPKSCpeCXoT+/+5zr1TNlsoQRDEz+LzZUyq6gnQ==} + '@vitest/expect@4.1.4': + resolution: {integrity: sha512-iPBpra+VDuXmBFI3FMKHSFXp3Gx5HfmSCE8X67Dn+bwephCnQCaB7qWK2ldHa+8ncN8hJU8VTMcxjPpyMkUjww==} - '@vitest/mocker@4.1.3': - resolution: {integrity: sha512-XN3TrycitDQSzGRnec/YWgoofkYRhouyVQj4YNsJ5r/STCUFqMrP4+oxEv3e7ZbLi4og5kIHrZwekDJgw6hcjw==} + '@vitest/mocker@4.1.4': + resolution: {integrity: sha512-R9HTZBhW6yCSGbGQnDnH3QHfJxokKN4KB+Yvk9Q1le7eQNYwiCyKxmLmurSpFy6BzJanSLuEUDrD+j97Q+ZLPg==} peerDependencies: msw: ^2.4.9 vite: 7.3.2 @@ -3479,31 +3043,25 @@ packages: vite: optional: true - '@vitest/pretty-format@4.0.18': - resolution: {integrity: sha512-P24GK3GulZWC5tz87ux0m8OADrQIUVDPIjjj65vBXYG17ZeU3qD7r+MNZ1RNv4l8CGU2vtTRqixrOi9fYk/yKw==} - - '@vitest/pretty-format@4.1.3': - resolution: {integrity: sha512-hYqqwuMbpkkBodpRh4k4cQSOELxXky1NfMmQvOfKvV8zQHz8x8Dla+2wzElkMkBvSAJX5TRGHJAQvK0TcOafwg==} + '@vitest/pretty-format@4.1.4': + resolution: {integrity: sha512-ddmDHU0gjEUyEVLxtZa7xamrpIefdEETu3nZjWtHeZX4QxqJ7tRxSteHVXJOcr8jhiLoGAhkK4WJ3WqBpjx42A==} - '@vitest/runner@4.1.3': - resolution: {integrity: sha512-VwgOz5MmT0KhlUj40h02LWDpUBVpflZ/b7xZFA25F29AJzIrE+SMuwzFf0b7t4EXdwRNX61C3B6auIXQTR3ttA==} + '@vitest/runner@4.1.4': + resolution: {integrity: sha512-xTp7VZ5aXP5ZJrn15UtJUWlx6qXLnGtF6jNxHepdPHpMfz/aVPx+htHtgcAL2mDXJgKhpoo2e9/hVJsIeFbytQ==} - '@vitest/snapshot@4.1.3': - resolution: {integrity: sha512-9l+k/J9KG5wPJDX9BcFFzhhwNjwkRb8RsnYhaT1vPY7OufxmQFc9sZzScRCPTiETzl37mrIWVY9zxzmdVeJwDQ==} + '@vitest/snapshot@4.1.4': + resolution: {integrity: sha512-MCjCFgaS8aZz+m5nTcEcgk/xhWv0rEH4Yl53PPlMXOZ1/Ka2VcZU6CJ+MgYCZbcJvzGhQRjVrGQNZqkGPttIKw==} - '@vitest/spy@4.1.3': - resolution: {integrity: sha512-ujj5Uwxagg4XUIfAUyRQxAg631BP6e9joRiN99mr48Bg9fRs+5mdUElhOoZ6rP5mBr8Bs3lmrREnkrQWkrsTCw==} + '@vitest/spy@4.1.4': + resolution: {integrity: sha512-XxNdAsKW7C+FLydqFJLb5KhJtl3PGCMmYwFRfhvIgxJvLSXhhVI1zM8f1qD3Zg7RCjTSzDVyct6sghs9UEgBEQ==} - '@vitest/ui@4.0.18': - resolution: {integrity: sha512-CGJ25bc8fRi8Lod/3GHSvXRKi7nBo3kxh0ApW4yCjmrWmRmlT53B5E08XRSZRliygG0aVNxLrBEqPYdz/KcCtQ==} + '@vitest/ui@4.1.4': + resolution: {integrity: sha512-EgFR7nlj5iTDYZYCvavjFokNYwr3c3ry0sFiCg+N7B233Nwp+NNx7eoF/XvMWDCKY71xXAG3kFkt97ZHBJVL8A==} peerDependencies: vitest: ^4.1.3 - '@vitest/utils@4.0.18': - resolution: {integrity: sha512-msMRKLMVLWygpK3u2Hybgi4MNjcYJvwTb0Ru09+fOyCXIgT5raYP041DRRdiJiI3k/2U6SEbAETB3YtBrUkCFA==} - - '@vitest/utils@4.1.3': - resolution: {integrity: sha512-Pc/Oexse/khOWsGB+w3q4yzA4te7W4gpZZAvk+fr8qXfTURZUMj5i7kuxsNK5mP/dEB6ao3jfr0rs17fHhbHdw==} + '@vitest/utils@4.1.4': + resolution: {integrity: sha512-13QMT+eysM5uVGa1rG4kegGYNp6cnQcsTc67ELFbhNLQO+vgsygtYJx2khvdt4gVQqSSpC/KT5FZZxUpP3Oatw==} accepts@2.0.0: resolution: {integrity: sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==} @@ -3519,11 +3077,6 @@ packages: peerDependencies: acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 - acorn@8.15.0: - resolution: {integrity: sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==} - engines: {node: '>=0.4.0'} - hasBin: true - acorn@8.16.0: resolution: {integrity: sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==} engines: {node: '>=0.4.0'} @@ -3634,6 +3187,10 @@ packages: resolution: {integrity: sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==} engines: {node: '>= 0.4'} + assertion-error@2.0.1: + resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} + engines: {node: '>=12'} + assign-symbols@1.0.0: resolution: {integrity: sha512-Q+JC7Whu8HhmTdBph/Tq59IoRtoy6KAm5zzPv00WdujX82lbAL8K7WVjne7vdCsAmbF4AYaDOPyO3k0kl8qIrw==} engines: {node: '>=0.10.0'} @@ -3659,10 +3216,6 @@ packages: resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} engines: {node: '>= 0.4'} - axe-core@4.11.1: - resolution: {integrity: sha512-BASOg+YwO2C+346x3LZOeoovTIoTrRqEsqMa6fmfAV0P+U9mFr9NsyOEpiYvFjbc64NMrSswhV50WdXzdb/Z5A==} - engines: {node: '>=4'} - axe-core@4.11.2: resolution: {integrity: sha512-byD6KPdvo72y/wj2T/4zGEvvlis+PsZsn/yPS3pEO+sFpcrqRpX/TJCxvVaEsNeMrfQbCr7w163YqoD9IYwHXw==} engines: {node: '>=4'} @@ -3691,8 +3244,8 @@ packages: bare-abort-controller: optional: true - bare-fs@4.5.6: - resolution: {integrity: sha512-1QovqDrR80Pmt5HPAsMsXTCFcDYr+NSUKW6nd6WO5v0JBmnItc/irNRzm2KOQ5oZ69P37y+AMujNyNtG+1Rggw==} + bare-fs@4.7.0: + resolution: {integrity: sha512-xzqKsCFxAek9aezYhjJuJRXBIaYlg/0OGDTZp+T8eYmYMlm66cs6cYko02drIyjN2CBbi+I6L7YfXyqpqtKRXA==} engines: {bare: '>=1.16.0'} peerDependencies: bare-buffer: '*' @@ -3700,15 +3253,15 @@ packages: bare-buffer: optional: true - bare-os@3.8.0: - resolution: {integrity: sha512-Dc9/SlwfxkXIGYhvMQNUtKaXCaGkZYGcd1vuNUUADVqzu4/vQfvnMkYYOUnt2VwQ2AqKr/8qAVFRtwETljgeFg==} + bare-os@3.8.7: + resolution: {integrity: sha512-G4Gr1UsGeEy2qtDTZwL7JFLo2wapUarz7iTMcYcMFdS89AIQuBoyjgXZz0Utv7uHs3xA9LckhVbeBi8lEQrC+w==} engines: {bare: '>=1.14.0'} bare-path@3.0.0: resolution: {integrity: sha512-tyfW2cQcB5NN8Saijrhqn0Zh7AnFNsnczRcuWODH0eYAXBsJ5gVxAUuNr7tsHSC6IZ77cA0SitzT+s47kot8Mw==} - bare-stream@2.11.0: - resolution: {integrity: sha512-Y/+iQ49fL3rIn6w/AVxI/2+BRrpmzJvdWt5Jv8Za6Ngqc6V227c+pYjYYgLdpR3MwQ9ObVXD0ZrqoBztakM0rw==} + bare-stream@2.13.0: + resolution: {integrity: sha512-3zAJRZMDFGjdn+RVnNpF9kuELw+0Fl3lpndM4NcEOhb9zwtSo/deETfuIwMSE5BXanA0FrN1qVjffGwAg2Y7EA==} peerDependencies: bare-abort-controller: '*' bare-buffer: '*' @@ -3724,13 +3277,13 @@ packages: bare-url@2.4.0: resolution: {integrity: sha512-NSTU5WN+fy/L0DDenfE8SXQna4voXuW0FHM7wH8i3/q9khUSchfPbPezO4zSFMnDGIf9YE+mt/RWhZgNRKRIXA==} - baseline-browser-mapping@2.10.16: - resolution: {integrity: sha512-Lyf3aK28zpsD1yQMiiHD4RvVb6UdMoo8xzG2XzFIfR9luPzOpcBlAsT/qfB1XWS1bxWT+UtE4WmQgsp297FYOA==} + baseline-browser-mapping@2.10.18: + resolution: {integrity: sha512-VSnGQAOLtP5mib/DPyg2/t+Tlv65NTBz83BJBJvmLVHHuKJVaDOBvJJykiT5TR++em5nfAySPccDZDa4oSrn8A==} engines: {node: '>=6.0.0'} hasBin: true - basic-ftp@5.2.0: - resolution: {integrity: sha512-VoMINM2rqJwJgfdHq6RiUudKt2BV+FY5ZFezP/ypmwayk68+NzzAQy4XXLlqsGD4MCzq3DrmNFD/uUmBJuGoXw==} + basic-ftp@5.2.2: + resolution: {integrity: sha512-1tDrzKsdCg70WGvbFss/ulVAxupNauGnOlgpyjKzeQxzyllBLS0CGLV7tjIXTK3ZQA9/FBEm9qyFFN1bciA6pw==} engines: {node: '>=10.0.0'} better-path-resolve@1.0.0: @@ -3752,8 +3305,8 @@ packages: resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} engines: {node: '>=8'} - browserslist@4.28.1: - resolution: {integrity: sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==} + browserslist@4.28.2: + resolution: {integrity: sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg==} engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true @@ -3784,8 +3337,8 @@ packages: resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} engines: {node: '>= 0.4'} - call-bind@1.0.8: - resolution: {integrity: sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==} + call-bind@1.0.9: + resolution: {integrity: sha512-a/hy+pNsFUTR+Iz8TCJvXudKVLAnz/DyeSUo10I5yvFDQJBFU2s9uqQpoSrJlroHUKoKqzg+epxyP9lqFdzfBQ==} engines: {node: '>= 0.4'} call-bound@1.0.4: @@ -3796,8 +3349,8 @@ packages: resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} engines: {node: '>=6'} - caniuse-lite@1.0.30001786: - resolution: {integrity: sha512-4oxTZEvqmLLrERwxO76yfKM7acZo310U+v4kqexI2TL1DkkUEMT8UijrxxcnVdxR3qkVf5awGRX+4Z6aPHVKrA==} + caniuse-lite@1.0.30001787: + resolution: {integrity: sha512-mNcrMN9KeI68u7muanUpEejSLghOKlVhRqS/Za2IeyGllJ9I9otGpR9g3nsw7n4W378TE/LyIteA0+/FOZm4Kg==} chai@6.2.2: resolution: {integrity: sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==} @@ -3814,9 +3367,6 @@ packages: chardet@2.1.1: resolution: {integrity: sha512-PsezH1rqdV9VvyNhxxOW32/d75r01NY7TQCmOqomRo15ZSOKbpTFVsfjghxo6JloQUCGnH4k1LGu0R4yCLlWQQ==} - cheap-ruler@4.0.0: - resolution: {integrity: sha512-0BJa8f4t141BYKQyn9NSQt1PguFQXMXwZiA5shfoaBYHAb2fFk2RAX+tiWMoQU+Agtzt3mdt0JtuyshAXqZ+Vw==} - chokidar@4.0.3: resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==} engines: {node: '>= 14.16.0'} @@ -3889,8 +3439,8 @@ packages: resolution: {integrity: sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==} engines: {node: ^14.18.0 || >=16.10.0} - content-disposition@1.0.1: - resolution: {integrity: sha512-oIXISMynqSqm241k6kcQ5UwttDILMK4BiurCfGEREw6+X9jkkpEe5T9FZaApyLGGOnFuyMWZpdolTXMtvEJ08Q==} + content-disposition@1.1.0: + resolution: {integrity: sha512-5jRCH9Z/+DRP7rkvY83B+yGIGX96OYdJmzngqnw2SBSxqCFPd0w2km3s5iawpGX8krnwSGmF0FW5Nhr0Hfai3g==} engines: {node: '>=18'} content-type@1.0.5: @@ -3925,8 +3475,8 @@ packages: resolution: {integrity: sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw==} engines: {node: '>= 0.10'} - cosmiconfig-typescript-loader@6.2.0: - resolution: {integrity: sha512-GEN39v7TgdxgIoNcdkRE3uiAzQt3UXLyHbRHD6YoL048XAeOomyxaP+Hh/+2C6C2wYjxJ2onhJcsQp+L4YEkVQ==} + cosmiconfig-typescript-loader@6.3.0: + resolution: {integrity: sha512-Akr82WH1Wfqatyiqpj8HDkO2o2KmJRu1FhKfSNJP3K4IdXwHfEyL7MOb62i1AGQVLtIQM+iCE9CGOtrfhR+mmA==} engines: {node: '>=v18'} peerDependencies: '@types/node': ^24.1.0 @@ -3949,10 +3499,6 @@ packages: csp_evaluator@1.1.5: resolution: {integrity: sha512-EL/iN9etCTzw/fBnp0/uj0f5BOOGvZut2mzsiiBZ/FdT6gFQCKRO/tmcKOxn5drWZ2Ndm/xBb1SI4zwWbGtmIw==} - css-tree@3.1.0: - resolution: {integrity: sha512-0eW44TGN5SQXU1mWSkKwFstI/22X2bG1nYzZTYMAWjylYURhse752YgbE4Cx46AC+bAvI+/dYTPRk1LqSUnu6w==} - engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0} - css-tree@3.2.1: resolution: {integrity: sha512-X7sjQzceUhu1u7Y/ylrRZFU2FS6LRiFVp6rKLPg23y3x3c3DOKAwuXGDp+PAGjh6CSnCjYeAul8pcT8bAl+lSA==} engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0} @@ -3960,11 +3506,8 @@ packages: css.escape@1.5.1: resolution: {integrity: sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==} - csscolorparser@1.0.3: - resolution: {integrity: sha512-umPSgYwZkdFoUrH5hIq5kf0wPSXiro51nPw0j2K/c83KflkPSTBGMz6NJvMB+07VlL0y7VPo6QJcDjcgKTTm3w==} - - cssstyle@6.1.0: - resolution: {integrity: sha512-Ml4fP2UT2K3CUBQnVlbdV/8aFDdlY69E+YnwJM+3VUWl08S3J8c8aRuJqCkD9Py8DHZ7zNNvsfKl8psocHZEFg==} + cssstyle@6.2.0: + resolution: {integrity: sha512-Fm5NvhYathRnXNVndkUsCCuR63DCLVVwGOOwQw782coXFi5HhkXdu289l59HlXZBawsyNccXfWRYvLzcDCdDig==} engines: {node: '>=20'} csstype@3.2.3: @@ -4001,15 +3544,6 @@ packages: supports-color: optional: true - debug@4.4.1: - resolution: {integrity: sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==} - engines: {node: '>=6.0'} - peerDependencies: - supports-color: '*' - peerDependenciesMeta: - supports-color: - optional: true - debug@4.4.3: resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} engines: {node: '>=6.0'} @@ -4057,12 +3591,12 @@ packages: resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} engines: {node: '>=8'} - devtools-protocol@0.0.1527314: - resolution: {integrity: sha512-UohCFOlzpPPD/IcsxM0k4lVZp/GfhPVJ6l2No5XX+LknpGisPWJe17oOHQhZTHf6ThUFIMwHO6bSEZUq/6oP7w==} - devtools-protocol@0.0.1581282: resolution: {integrity: sha512-nv7iKtNZQshSW2hKzYNr46nM/Cfh5SEvE2oV0/SEGgc9XupIY5ggf84Cz8eJIkBce7S3bmTAauFD6aysMpnqsQ==} + devtools-protocol@0.0.1608973: + resolution: {integrity: sha512-Tpm17fxYzt+J7VrGdc1k8YdRqS3YV7se/M6KeemEqvUbq/n7At1rWVuXMxQgpWkdwSdIEKYbU//Bve+Shm4YNQ==} + dir-glob@3.0.1: resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} engines: {node: '>=8'} @@ -4095,8 +3629,8 @@ packages: ee-first@1.1.1: resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} - electron-to-chromium@1.5.286: - resolution: {integrity: sha512-9tfDXhJ4RKFNerfjdCcZfufu49vg620741MNs26a9+bhLThdB+plgMeou98CAaHu/WATj2iHOOHTp1hWtABj2A==} + electron-to-chromium@1.5.335: + resolution: {integrity: sha512-q9n5T4BR4Xwa2cwbrwcsDJtHD/enpQ5S1xF1IAtdqf5AAgqDFmR/aakqH3ChFdqd/QXJhS3rnnXFtexU7rax6Q==} emoji-regex@10.6.0: resolution: {integrity: sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==} @@ -4149,8 +3683,8 @@ packages: resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} engines: {node: '>= 0.4'} - es-iterator-helpers@1.2.2: - resolution: {integrity: sha512-BrUQ0cPTB/IwXj23HtwHjS9n7O4h9FX94b4xc5zlTHxeLgTAdzYUDyy6KdExAl9lbN5rtfe44xpjpmj9grxs5w==} + es-iterator-helpers@1.3.2: + resolution: {integrity: sha512-HVLACW1TppGYjJ8H6/jqH/pqOtKRw6wMlrB23xfExmFWxFquAIWCmwoLsOyN96K4a5KbmOf5At9ZUO3GZbetAw==} engines: {node: '>= 0.4'} es-module-lexer@2.0.0: @@ -4172,16 +3706,6 @@ packages: resolution: {integrity: sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==} engines: {node: '>= 0.4'} - esbuild@0.25.8: - resolution: {integrity: sha512-vVC0USHGtMi8+R4Kz8rt6JhEWLxsv9Rnu/lGYbPR8u47B+DCBksq9JarW0zOO7bs37hyOK1l2/oqtbciutL5+Q==} - engines: {node: '>=18'} - hasBin: true - - esbuild@0.27.3: - resolution: {integrity: sha512-8VwMnyGCONIs6cWue2IdpHxHnAjzxnw2Zr7MkVxB2vjmQ2ivqGFb4LEG3SMnv0Gb2F/G/2yA8zUaiL1gywDCCg==} - engines: {node: '>=18'} - hasBin: true - esbuild@0.27.7: resolution: {integrity: sha512-IxpibTjyVnmrIQo5aqNpCgoACA/dTKLTlhMHihVHhdkxKyPO1uBBthumT0rdHmcsk9uMonIWS0m4FljWzILh3w==} engines: {node: '>=18'} @@ -4203,8 +3727,8 @@ packages: engines: {node: '>=6.0'} hasBin: true - eslint-config-next@16.2.2: - resolution: {integrity: sha512-6VlvEhwoug2JpVgjZDhyXrJXUEuPY++TddzIpTaIRvlvlXXFgvQUtm3+Zr84IjFm0lXtJt73w19JA08tOaZVwg==} + eslint-config-next@16.2.3: + resolution: {integrity: sha512-Dnkrylzjof/Az7iNoIQJqD18zTxQZcngir19KJaiRsMnnjpQSVoa6aEg/1Q4hQC+cW90uTlgQYadwL1CYNwFWA==} peerDependencies: eslint: '>=9.0.0' typescript: ^5.8.3 @@ -4299,8 +3823,8 @@ packages: resolution: {integrity: sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==} engines: {node: ^20.19.0 || ^22.13.0 || >=24} - eslint@9.39.3: - resolution: {integrity: sha512-VmQ+sifHUbI/IcSopBCF/HO3YiHQx/AVd3UVyYL6weuwW+HvON9VYn5l6Zl1WZzPWXPNZrSQpxwkkZ/VuvJZzg==} + eslint@9.39.4: + resolution: {integrity: sha512-XoMjdBOwe/esVgEvLmNsD3IRHkm7fbKIUGvrleloJXUZgDHig2IPWNniv+GwjyJXzuNqVjlr5+4yVUZjycJwfQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} hasBin: true peerDependencies: @@ -4359,8 +3883,8 @@ packages: resolution: {integrity: sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==} engines: {node: '>=12.0.0'} - express-rate-limit@8.3.1: - resolution: {integrity: sha512-D1dKN+cmyPWuvB+G2SREQDzPY1agpBIcTa9sJxOPMCNeH3gwzhqJRDWCXW3gg0y//+LQ/8j52JbMROWyrKdMdw==} + express-rate-limit@8.3.2: + resolution: {integrity: sha512-77VmFeJkO0/rvimEDuUC5H30oqUC4EyOhyGccfqoLebB0oiEYfM7nwPrsDsBL1gsTpwfzX8SFy2MT3TDyRq+bg==} engines: {node: '>= 16'} peerDependencies: express: '>= 4.11' @@ -4419,14 +3943,6 @@ packages: fd-slicer@1.1.0: resolution: {integrity: sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==} - fdir@6.4.6: - resolution: {integrity: sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w==} - peerDependencies: - picomatch: '>=4.0.4' - peerDependenciesMeta: - picomatch: - optional: true - fdir@6.5.0: resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} engines: {node: '>=12.0.0'} @@ -4484,10 +4000,6 @@ packages: resolution: {integrity: sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==} engines: {node: '>= 0.8'} - fs-extra@11.3.1: - resolution: {integrity: sha512-eXvGGwZ5CL17ZSwHWd3bbgk7UUpF6IFHtP57NYYakPvHOs8GDgDe5KJI36jIJzDkJ6eJjuzRA8eBQb6SkKue0g==} - engines: {node: '>=14.14'} - fs-extra@11.3.4: resolution: {integrity: sha512-CTXd6rk/M3/ULNQj8FBqBWHYBVYybQ3VPBw0xGKFe3tuH7ytT6ACnvzpIQ3UZtB8yvUKC2cXn1a+x+5EVQLovA==} engines: {node: '>=14.14'} @@ -4520,8 +4032,8 @@ packages: functions-have-names@1.2.3: resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==} - fuse.js@7.1.0: - resolution: {integrity: sha512-trLf4SzuuUxfusZADLINj+dE8clK1frKdmqiJNb1Es75fmI5oY6X2mxLVUciLLjxqw/xr72Dhy+lER6dGd02FQ==} + fuse.js@7.3.0: + resolution: {integrity: sha512-plz8RVjfcDedTGfVngWH1jmJvBvAwi1v2jecfDerbEnMcmOYUEEwKFTHbNoCiYyzaK2Ws8lABkTCcRSqCY1q4w==} engines: {node: '>=10'} generator-function@2.0.1: @@ -4563,9 +4075,6 @@ packages: resolution: {integrity: sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==} engines: {node: '>= 0.4'} - get-tsconfig@4.10.1: - resolution: {integrity: sha512-auHyJ4AgMz7vgS8Hp3N6HXSmlMdUyhSUrfBF16w153rxtLIEOE+HGqaBppczZvnHLqQJfiHotCYpNhl0lUROFQ==} - get-tsconfig@4.13.7: resolution: {integrity: sha512-7tN6rFgBlMgpBML5j8typ92BKFi2sFQvIdpAqLA2beia5avZDrMs0FLZiM5etShWq5irVyGcGMEA1jcDaK7A/Q==} @@ -4593,10 +4102,6 @@ packages: resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} engines: {node: '>=10.13.0'} - glob@13.0.6: - resolution: {integrity: sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==} - engines: {node: 18 || 20 || >=22} - global-directory@4.0.1: resolution: {integrity: sha512-wHTUcDUoZ1H5/0iVqEudYW4/kAlN5cZ3j/bXn0Dpbizl9iaUVeWSHqiOjsgk6OW2bkLclbBjzewBz6weQ1zA2Q==} engines: {node: '>=18'} @@ -4628,9 +4133,6 @@ packages: graceful-fs@4.2.11: resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} - grid-index@1.1.0: - resolution: {integrity: sha512-HZRwumpOGUrHyxO5bqKZL0B0GlUpwtCAzZ42sgxUPniu33R1LSFH5yrIcBCHjkctCAh3mtWKcKd9J4vDDdeVHA==} - has-bigints@1.1.0: resolution: {integrity: sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==} engines: {node: '>= 0.4'} @@ -4664,8 +4166,8 @@ packages: hermes-parser@0.25.1: resolution: {integrity: sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA==} - hono@4.12.8: - resolution: {integrity: sha512-VJCEvtrezO1IAR+kqEYnxUOoStaQPGrCmX3j4wDTNOcD1uRPFpGlwQUIW8niPuvHXaTUxeOUl5MMDGrl+tmO9A==} + hono@4.12.12: + resolution: {integrity: sha512-p1JfQMKaceuCbpJKAPKVqyqviZdS0eUxH9v82oWo1kb9xjQ5wA6iP3FNVAPDFlz5/p7d45lO+BpSk1tuSZMF4Q==} engines: {node: '>=16.9.0'} html-encoding-sniffer@6.0.0: @@ -4953,8 +4455,8 @@ packages: resolution: {integrity: sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==} hasBin: true - jose@6.1.3: - resolution: {integrity: sha512-0TpaTfihd4QMNwrz/ob2Bp7X04yuxJkjRGi4aKmOqwhov54i6u79oCv7T+C7lo70MKH6BesI3vscD1yb/yzKXQ==} + jose@6.2.2: + resolution: {integrity: sha512-d7kPDd34KO/YnzaDOlikGpOurfF0ByC2sEV4cANCtdqLlTfBlw2p14O/5d/zv40gJPbIQxfES3nSx1/oYNyuZQ==} joycon@3.1.1: resolution: {integrity: sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==} @@ -5040,8 +4542,8 @@ packages: jsonfile@4.0.0: resolution: {integrity: sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==} - jsonfile@6.1.0: - resolution: {integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==} + jsonfile@6.2.0: + resolution: {integrity: sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==} jsx-ast-utils@3.3.5: resolution: {integrity: sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==} @@ -5081,8 +4583,8 @@ packages: lighthouse-stack-packs@1.12.3: resolution: {integrity: sha512-d8IsOpE83kbANgnM+Tp8+x6HcMpX9o2ITBiUERssgzAIFdZCQzs/f4k6D0DLQTE59enml9mbAOU52Wu35exWtg==} - lighthouse@13.0.3: - resolution: {integrity: sha512-mEHAQV3nn4fB+3FDapye+KGeeE4V8FERgbCFegKT7HxqDVGWVOM/BoH9Qof71fzVYVMLIiGnDsnWRrH0sQ9o4Q==} + lighthouse@13.1.0: + resolution: {integrity: sha512-H3Qi4fJBXbaCTdE3XzdONq6kH5wMoG4v5sv+1BgG4H+0nivSo35eTp/yryHEU7G4xepUJmmlvjS10OWGHFwU+Q==} engines: {node: '>=22.19'} hasBin: true @@ -5206,18 +4708,12 @@ packages: lodash.snakecase@4.1.1: resolution: {integrity: sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw==} - lodash.sortby@4.7.0: - resolution: {integrity: sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==} - lodash.startcase@4.4.0: resolution: {integrity: sha512-+WKqsK294HMSc2jEbNgpHpd0JfIBhp7rEV4aqXWqFr6AlXov+SlcgB1Fv01y2kGe3Gc8nMW7VA0SrGuSkRfIEg==} lodash.upperfirst@4.3.1: resolution: {integrity: sha512-sReKOYJIJf74dhJONhU4e0/shzi1trVbSWDOhKYE5XV2O+H7Sb2Dihwuc7xWxVl+DgFPyTqIN3zMfT9cq5iWDg==} - lodash@4.18.1: - resolution: {integrity: sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==} - log-update@6.1.0: resolution: {integrity: sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w==} engines: {node: '>=18'} @@ -5229,10 +4725,6 @@ packages: resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} hasBin: true - lru-cache@11.2.6: - resolution: {integrity: sha512-ESL2CrkS/2wTPfuend7Zhkzo2u0daGJ/A2VucJOgQ/C48S/zB8MMeMHSGKYpXhIjbPxfuezITkaBH1wqv00DDQ==} - engines: {node: 20 || >=22} - lru-cache@11.3.3: resolution: {integrity: sha512-JvNw9Y81y33E+BEYPr0U7omo+U9AySnsMsEiXgwT6yqd31VQWTLNQqmT4ou5eqPFUrTfIDFta2wKhB1hyohtAQ==} engines: {node: 20 || >=22} @@ -5259,8 +4751,8 @@ packages: peerDependencies: react: 19.2.4 - lucide-react@1.7.0: - resolution: {integrity: sha512-yI7BeItCLZJTXikmK4KNUGCKoGzSvbKlfCvw44bU4fXAL6v3gYS4uHD1jzsLkfwODYwI6Drw5Tu9Z5ulDe0TSg==} + lucide-react@1.8.0: + resolution: {integrity: sha512-WuvlsjngSk7TnTBJ1hsCy3ql9V9VOdcPkd3PKcSmM34vJD8KG6molxz7m7zbYFgICwsanQWmJ13JlYs4Zp7Arw==} peerDependencies: react: 19.2.4 @@ -5268,9 +4760,6 @@ packages: resolution: {integrity: sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==} hasBin: true - magic-string@0.30.17: - resolution: {integrity: sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==} - magic-string@0.30.21: resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} @@ -5281,9 +4770,6 @@ packages: resolution: {integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==} engines: {node: '>=10'} - mapbox-gl@3.20.0: - resolution: {integrity: sha512-+rVQkf6ymUlAEJiQBZy0OiamJvQN4Uk15mRHI98PRUSmRS40GOoLJyEZEG39LEUtvmzc7qGh+4ygZfJ//O5VnQ==} - maplibre-gl@4.7.1: resolution: {integrity: sha512-lgL7XpIwsgICiL82ITplfS7IGwrB1OJIw/pCvprDp2dhmSSEBgmPzYRvwYYYvJGJD7fxUv1Tvpih4nZ6VrLuaA==} engines: {node: '>=16.14.0', npm: '>=8.1.0'} @@ -5291,16 +4777,10 @@ packages: marky@1.3.0: resolution: {integrity: sha512-ocnPZQLNpvbedwTy9kNrQEsknEfgvcLMvOtz3sFeWApDq1MXH1TqkCIx58xlpESsfwQOnuBO9beyQuNGzVvuhQ==} - martinez-polygon-clipping@0.8.1: - resolution: {integrity: sha512-9PLLMzMPI6ihHox4Ns6LpVBLpRc7sbhULybZ/wyaY8sY3ECNe2+hxm1hA2/9bEEpRrdpjoeduBuZLg2aq1cSIQ==} - math-intrinsics@1.1.0: resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} engines: {node: '>= 0.4'} - mdn-data@2.12.2: - resolution: {integrity: sha512-IEn+pegP1aManZuckezWCO+XZQDplx1366JoVhTpMpBB1sPey/SbveZQUosKiKiGYjg1wH4pMlNgXbCiYgihQA==} - mdn-data@2.27.1: resolution: {integrity: sha512-9Yubnt3e8A0OKwxYSXyhLymGW4sCufcLG6VdiDdUGVkPhpqLxlvP5vl1983gQjJl3tqbrM731mjaZaP68AgosQ==} @@ -5338,11 +4818,7 @@ packages: min-indent@1.0.1: resolution: {integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==} - engines: {node: '>=4'} - - minimatch@10.2.4: - resolution: {integrity: sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==} - engines: {node: 18 || 20 || >=22} + engines: {node: '>=4'} minimatch@10.2.5: resolution: {integrity: sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==} @@ -5351,15 +4827,11 @@ packages: minimist@1.2.8: resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} - minipass@7.1.3: - resolution: {integrity: sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==} - engines: {node: '>=16 || 14 >=14.17'} - mitt@3.0.1: resolution: {integrity: sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==} - mlly@1.7.4: - resolution: {integrity: sha512-qmdSIPC4bDJXgZTCR7XosJiNKySV7O215tsPtDN9iEO/7q/76b/ijtgRu/+epFXSJhijtTCCGp3DWS549P3xKw==} + mlly@1.8.2: + resolution: {integrity: sha512-d+ObxMQFmbt10sretNDytwt85VrbkhhUA/JBGm1MPaWJ65Cl4wOgLaB1NYvJSZ0Ef03MMEU/0xpPMXUIQ29UfA==} module-details-from-path@1.0.4: resolution: {integrity: sha512-EGWKgxALGMgzvxYF1UyGTy0HXX/2vHLkw6+NvDKW2jypWbHpjQuj4UMcqQWXHERJhVGKikolT06G3bcKe4fi7w==} @@ -5402,12 +4874,12 @@ packages: resolution: {integrity: sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==} engines: {node: '>= 0.6'} - netmask@2.0.2: - resolution: {integrity: sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg==} + netmask@2.1.1: + resolution: {integrity: sha512-eonl3sLUha+S1GzTPxychyhnUzKyeQkZ7jLjKrBagJgPla13F+uQ71HgpFefyHgqrjEbCPkDArxYsjY8/+gLKA==} engines: {node: '>= 0.4.0'} - next@16.2.2: - resolution: {integrity: sha512-i6AJdyVa4oQjyvX/6GeER8dpY/xlIV+4NMv/svykcLtURJSy/WzDnnUk/TM4d0uewFHK7xSQz4TbIwPgjky+3A==} + next@16.2.3: + resolution: {integrity: sha512-9V3zV4oZFza3PVev5/poB9g0dEafVcgNyQ8eTRop8GvxZjV2G15FC5ARuG1eFD42QgeYkzJBJzHghNP8Ad9xtA==} engines: {node: '>=20.9.0'} hasBin: true peerDependencies: @@ -5434,8 +4906,8 @@ packages: resolution: {integrity: sha512-pyFS63ptit/P5WqUkt+UUfe+4oevH+bFeIiPPdfb0pFeYEu/1ELnJu5l+5EcTKYL5M7zaAa7S8ddywgXypqKCw==} engines: {node: '>= 0.4'} - node-releases@2.0.27: - resolution: {integrity: sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==} + node-releases@2.0.37: + resolution: {integrity: sha512-1h5gKZCF+pO/o3Iqt5Jp7wc9rH3eJJ0+nh/CIoiRwjRxde/hAHyLPXYN4V3CqKAbiZPSeJFSWHmJsbkicta0Eg==} object-assign@4.1.1: resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} @@ -5563,10 +5035,6 @@ packages: path-parse@1.0.7: resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} - path-scurry@2.0.2: - resolution: {integrity: sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==} - engines: {node: 18 || 20 || >=22} - path-to-regexp@8.4.2: resolution: {integrity: sha512-qRcuIdP69NPm4qbACK+aDogI5CBDMi1jKe0ry5rSQJz8JVLsC7jV8XpiJjGRLLol3N+R5ihGYcrPLTno6pAdBA==} @@ -5581,10 +5049,6 @@ packages: resolution: {integrity: sha512-XDF38WCH3z5OV/OVa8GKUNtLAyneuzbCisx7QUCF8Q6Nutx0WnJrQe5O+kOtBlLfRNUws98Y58Lblp+NJG5T4Q==} hasBin: true - pbf@4.0.1: - resolution: {integrity: sha512-SuLdBvS42z33m8ejRbInMapQe8n0D3vN/Xd5fmWM3tufNgRQFBpaW2YVJxQZV4iPNqb0vEFvssMEo5w9c6BTIA==} - hasBin: true - pend@1.2.0: resolution: {integrity: sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==} @@ -5621,21 +5085,11 @@ packages: pkg-types@1.3.1: resolution: {integrity: sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==} - playwright-core@1.58.2: - resolution: {integrity: sha512-yZkEtftgwS8CsfYo7nm0KE8jsvm6i/PTgVtB8DL726wNf6H2IMsDuxCpJj59KDaxCtSnrWan2AeDqM7JBaultg==} - engines: {node: '>=18'} - hasBin: true - playwright-core@1.59.1: resolution: {integrity: sha512-HBV/RJg81z5BiiZ9yPzIiClYV/QMsDCKUyogwH9p3MCP6IYjUFu/MActgYAvK0oWyV9NlwM3GLBjADyWgydVyg==} engines: {node: '>=18'} hasBin: true - playwright@1.58.2: - resolution: {integrity: sha512-vA30H8Nvkq/cPBnNw4Q8TWz1EJyqgpuinBcHET0YVJVFldr8JDNiU9LaWAE1KqSkRYazuaBhTpB5ZzShOezQ6A==} - engines: {node: '>=18'} - hasBin: true - playwright@1.59.1: resolution: {integrity: sha512-C8oWjPR3F81yljW9o5OxcWzfh6avkVwDD2VYdwIGqTkl+OGFISgypqzfu7dOe4QNLL2aqcWBmI3PMtLIK233lw==} engines: {node: '>=18'} @@ -5699,8 +5153,8 @@ packages: engines: {node: '>=10.13.0'} hasBin: true - prettier@3.8.1: - resolution: {integrity: sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg==} + prettier@3.8.2: + resolution: {integrity: sha512-8c3mgTe0ASwWAJK+78dpviD+A8EqhndQPUBpNUIPt6+xWlIigCwfN01lWr9MAede4uqXGTEKeQWTvzb3vjia0Q==} engines: {node: '>=14'} hasBin: true @@ -5723,8 +5177,8 @@ packages: prop-types@15.8.1: resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==} - protocol-buffers-schema@3.6.0: - resolution: {integrity: sha512-TdDRD+/QNdrCGCE7v8340QyuXd4kIWIgapsE2+n/SaGiSSbomYl4TjHlvIoCWRpE7wFt02EpB35VVA2ImcBVqw==} + protocol-buffers-schema@3.6.1: + resolution: {integrity: sha512-VG2K63Igkiv9p76tk1lilczEK1cT+kCjKtkdhw1dQZV3k3IXJbd3o6Ho8b9zJZaHSnT2hKe4I+ObmX9w6m5SmQ==} proxy-addr@2.0.7: resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==} @@ -5748,8 +5202,8 @@ packages: resolution: {integrity: sha512-MWL3XbUCfVgGR0gRsidzT6oKJT2QydPLhMITU6HoVWiiv4gkb6gJi3pcdAa8q4HwjBTbqISOWVP4aJiiyUJvag==} engines: {node: '>=18'} - qs@6.15.0: - resolution: {integrity: sha512-mAZTtNCeetKMH+pSjrb76NAM8V9a05I9aBZOHztWy/UqcJdQYNsf59vrRKWnojAT9Y+GbIvoTBC++CPHqpDBhQ==} + qs@6.15.1: + resolution: {integrity: sha512-6YHEFRL9mfgcAvql/XhwTvf5jKcOiiupt2FiJxHkiX1z4j7WL8J/jRHYLluORvc1XxB5rV20KoeK00gVJamspg==} engines: {node: '>=0.6'} quansync@0.2.11: @@ -5783,8 +5237,8 @@ packages: react-is@17.0.2: resolution: {integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==} - react-map-gl@8.1.0: - resolution: {integrity: sha512-vDx/QXR3Tb+8/ap/z6gdMjJQ8ZEyaZf6+uMSPz7jhWF5VZeIsKsGfPvwHVPPwGF43Ryn+YR4bd09uEFNR5OPdg==} + react-map-gl@8.1.1: + resolution: {integrity: sha512-aSqFAFoxvY7wxbGI93Dz0E41171mkAb3GcNbnkFIotmu88OFw495os6mIDZSi7irYNT/PZEIOEHUxhun4ToGuQ==} peerDependencies: mapbox-gl: '>=1.13.0' maplibre-gl: '>=1.13.0' @@ -5846,8 +5300,8 @@ packages: resolve-protobuf-schema@2.1.0: resolution: {integrity: sha512-kI5ffTiZWmJaS/huM8wZfEMer1eRd7oJQhDuxeCLe3t7N7mX3z94CN0xPxBQxFYQTSNz9T0i+v6inKqSdK8xrQ==} - resolve@1.22.11: - resolution: {integrity: sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==} + resolve@1.22.12: + resolution: {integrity: sha512-TyeJ1zif53BPfHootBGwPRYT1RUt6oGWsaQr8UyZW/eAm9bKoijtvruSDEmZHm92CwS9nj7/fWttqPCgzep8CA==} engines: {node: '>= 0.4'} hasBin: true @@ -5871,14 +5325,6 @@ packages: resolution: {integrity: sha512-s+pyvQeIKIZ0dx5iJiQk1tPLJAWln39+MI5jtM8wnyws+G5azk+dMnMX0qfbqNetKKNgcWWOdi0sfm+FbQbgdQ==} engines: {node: '>=10.0.0'} - robust-predicates@2.0.4: - resolution: {integrity: sha512-l4NwboJM74Ilm4VKfbAtFeGq7aEjWL+5kVFcmgFA2MrdnQWx9iE/tUGvxY5HyMI7o/WpSIUFLbC5fbeaHgSCYg==} - - rollup@4.59.0: - resolution: {integrity: sha512-2oMpl67a3zCH9H79LeMcbDhXW/UmWG/y2zuqnF2jQq5uq9TbM9TVyXvA4+t+ne2IIkBdrLpAaRQAvo7YI/Yyeg==} - engines: {node: '>=18.0.0', npm: '>=8.0.0'} - hasBin: true - rollup@4.60.1: resolution: {integrity: sha512-VmtB2rFU/GroZ4oL8+ZqXgSA38O6GR8KSIvWmEFv63pQ0G6KaBH9s07PO8XTXP4vI+3UJUEypOfjkGfmSBBR0w==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} @@ -5967,8 +5413,8 @@ packages: shimmer@1.2.1: resolution: {integrity: sha512-sQTKC1Re/rM6XyFM6fIAGHRPVGvyXfgzIDvzoq608vM+jeyVD0Tu1E6Np0Kc2zAIFWIj963V2800iF/9LPieQw==} - side-channel-list@1.0.0: - resolution: {integrity: sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==} + side-channel-list@1.0.1: + resolution: {integrity: sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==} engines: {node: '>= 0.4'} side-channel-map@1.0.1: @@ -6041,10 +5487,9 @@ packages: resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} engines: {node: '>=0.10.0'} - source-map@0.8.0-beta.0: - resolution: {integrity: sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA==} - engines: {node: '>= 8'} - deprecated: The work that was done in this beta branch won't be included in future versions + source-map@0.7.6: + resolution: {integrity: sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==} + engines: {node: '>= 12'} spawndamnit@3.0.1: resolution: {integrity: sha512-MmnduQUuHCoFckZoWnXsTg7JaiLBJrKFj9UI2MbRPGaJeVpsLcVBu6P/IGZovziM/YBsellCmsprgNA+w0CzVg==} @@ -6053,9 +5498,6 @@ packages: resolution: {integrity: sha512-DI7/OuAUD+GMpR6dmu8lliO2Wg5zfeh+/xsdyJZCzd8o5JgFUjCeLsBDuZjIQJdwXS3J0L/uZYrELKYqx+PXog==} engines: {node: '>=8.0'} - splaytree@0.1.4: - resolution: {integrity: sha512-D50hKrjZgBzqD3FT2Ek53f2dcDLAQT8SSGrzj3vidNH5ISRgceeGVJ2dQIthKOuayqFXfFjXheHNo4bbt9LhRQ==} - split-string@3.1.0: resolution: {integrity: sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==} engines: {node: '>=0.10.0'} @@ -6161,8 +5603,8 @@ packages: babel-plugin-macros: optional: true - sucrase@3.35.0: - resolution: {integrity: sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==} + sucrase@3.35.1: + resolution: {integrity: sha512-DhuTmvZWux4H1UOnWMB3sk0sbaCVOoQZjv8u1rDoTV0HTdGem9hkAZtl4JZy8P2z4Bg0nT+YMeOFyVr4zcG5Tw==} engines: {node: '>=16 || 14 >=14.17'} hasBin: true @@ -6183,9 +5625,6 @@ packages: tailwind-merge@3.5.0: resolution: {integrity: sha512-I8K9wewnVDkL1NTGoqWmVEIlUcB9gFriAEkXkfCjX5ib8ezGxtR3xD7iZIxrfArjEsH7F1CHD4RFUtxefdqV/A==} - tailwindcss@4.2.1: - resolution: {integrity: sha512-/tBrSQ36vCleJkAOsy9kbNTgaxvGbyOamC30PRePTQe/o1MFwEKHQk4Cn7BNGaPtjp+PuUrByJehM1hgxfq4sw==} - tailwindcss@4.2.2: resolution: {integrity: sha512-KWBIxs1Xb6NoLdMVqhbhgwZf2PGBpPEiwOqgI4pFIYbNTfBXiKYyWoTsXgBQ9WFg/OlhnvHaY+AEpW7wSmFo2Q==} @@ -6216,9 +5655,6 @@ packages: thenify@3.3.1: resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==} - third-party-web@0.27.0: - resolution: {integrity: sha512-h0JYX+dO2Zr3abCQpS6/uFjujaOjA1DyDzGQ41+oFn9VW/ARiq9g5ln7qEP9+BTzDpOMyIfsfj4OvfgXAsMUSA==} - third-party-web@0.29.0: resolution: {integrity: sha512-nBDSJw5B7Sl1YfsATG2XkW5qgUPODbJhXw++BKygi9w6O/NKS98/uY/nR/DxDq2axEjL6halHW1v+jhm/j1DBQ==} @@ -6228,18 +5664,10 @@ packages: tinyexec@0.3.2: resolution: {integrity: sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==} - tinyexec@1.0.4: - resolution: {integrity: sha512-u9r3uZC0bdpGOXtlxUIdwf9pkmvhqJdrVCH9fapQtgy/OeTTMZ1nqH7agtvEfmGui6e1XxjcdrlxvxJvc3sMqw==} + tinyexec@1.1.1: + resolution: {integrity: sha512-VKS/ZaQhhkKFMANmAOhhXVoIfBXblQxGX1myCQ2faQrfmobMftXeJPcZGp0gS07ocvGJWDLZGyOZDadDBqYIJg==} engines: {node: '>=18'} - tinyglobby@0.2.14: - resolution: {integrity: sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==} - engines: {node: '>=12.0.0'} - - tinyglobby@0.2.15: - resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==} - engines: {node: '>=12.0.0'} - tinyglobby@0.2.16: resolution: {integrity: sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==} engines: {node: '>=12.0.0'} @@ -6247,25 +5675,18 @@ packages: tinyqueue@3.0.0: resolution: {integrity: sha512-gRa9gwYU3ECmQYv3lslts5hxuIa90veaEcxDYuu3QGOIAEM2mOZkVHp48ANJuu1CURtRdHKUBY5Lm1tHV+sD4g==} - tinyrainbow@3.0.3: - resolution: {integrity: sha512-PSkbLUoxOFRzJYjjxHJt9xro7D+iilgMX/C9lawzVuYiIdcihh9DXmVibBe8lmcFrRi/VzlPjBxbN7rH24q8/Q==} - engines: {node: '>=14.0.0'} - tinyrainbow@3.1.0: resolution: {integrity: sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw==} engines: {node: '>=14.0.0'} - tldts-core@7.0.23: - resolution: {integrity: sha512-0g9vrtDQLrNIiCj22HSe9d4mLVG3g5ph5DZ8zCKBr4OtrspmNB6ss7hVyzArAeE88ceZocIEGkyW1Ime7fxPtQ==} + tldts-core@7.0.28: + resolution: {integrity: sha512-7W5Efjhsc3chVdFhqtaU0KtK32J37Zcr9RKtID54nG+tIpcY79CQK/veYPODxtD/LJ4Lue66jvrQzIX2Z2/pUQ==} - tldts-core@7.0.27: - resolution: {integrity: sha512-YQ7uPjgWUibIK6DW5lrKujGwUKhLevU4hcGbP5O6TcIUb+oTjJYJVWPS4nZsIHrEEEG6myk/oqAJUEQmpZrHsg==} + tldts-icann@7.0.28: + resolution: {integrity: sha512-brkN3yIgYTzBpSxB71XYBwUMDgutmKmA+6TWzgGD/EPcvCc6LHMTRaYj9ik1u3BxhSW53qIK/7cgjA2rF7BgbA==} - tldts-icann@7.0.27: - resolution: {integrity: sha512-eSnMd1x/x/E/kRNyjP0wLmzxg0lcfBqPhKLAxtQ6Kd3pqZ0CM0Ty6K8HSFWXSSKokiGFzzdrypxgWOYiXDLwFA==} - - tldts@7.0.23: - resolution: {integrity: sha512-ASdhgQIBSay0R/eXggAkQ53G4nTJqTXqC2kbaBbdDwM7SkjyZyO0OaaN1/FH7U/yCeqOHDwFO5j8+Os/IS1dXw==} + tldts@7.0.28: + resolution: {integrity: sha512-+Zg3vWhRUv8B1maGSTFdev9mjoo8Etn2Ayfs4cnjlD3CsGkxXX4QyW3j2WJ0wdjYcYmy7Lx2RDsZMhgCWafKIw==} hasBin: true to-regex-range@5.0.1: @@ -6280,17 +5701,10 @@ packages: resolution: {integrity: sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==} engines: {node: '>=6'} - tough-cookie@6.0.0: - resolution: {integrity: sha512-kXuRi1mtaKMrsLUxz3sQYvVl37B0Ns6MzfrtV5DvJceE9bPyspOqk9xxv7XbZWcfLWbFmm997vl83qUWVJA64w==} - engines: {node: '>=16'} - tough-cookie@6.0.1: resolution: {integrity: sha512-LktZQb3IeoUWB9lqR5EWTHgW/VTITCXg4D21M+lvybRVdylLrRMnqaIONLVb5mav8vM19m44HIcGq4qASeu2Qw==} engines: {node: '>=16'} - tr46@1.0.1: - resolution: {integrity: sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA==} - tr46@6.0.0: resolution: {integrity: sha512-bLVMLPtstlZ4iMQHpFHTR7GAGj2jxi8Dg0s2h2MafAE4uSWF98FC/3MomU51iQAMf8/qDUbKWf5GxuvvVcXEhw==} engines: {node: '>=20'} @@ -6314,8 +5728,8 @@ packages: tslib@2.8.1: resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} - tsup@8.5.0: - resolution: {integrity: sha512-VmBp77lWNQq6PfuMqCHD3xWl22vEoWsKajkF8t+yMBawlUS8JzEI+vOVMeuNZIuMML8qXRizFKi9oD5glKQVcQ==} + tsup@8.5.1: + resolution: {integrity: sha512-xtgkqwdhpKWr3tKPmCkvYmS9xnQK3m3XgxZHwSUjvfTjp7YfXe5tT3GgWi0F2N+ZSMsOeWeZFh7ZZFg5iPhing==} engines: {node: '>=18'} hasBin: true peerDependencies: @@ -6338,8 +5752,8 @@ packages: engines: {node: '>=18.0.0'} hasBin: true - turbo@2.9.5: - resolution: {integrity: sha512-JXNkRe6H6MjSlk5UQRTjyoKX5YN2zlc2632xcSlSFBao5yvbMWTpv9SNolOZlZmUlcDOHuszPLItbKrvcXnnZA==} + turbo@2.9.6: + resolution: {integrity: sha512-+v2QJey7ZUeUiuigkU+uFfklvNUyPI2VO2vBpMYJA+a1hKFLFiKtUYlRHdb3P9CrAvMzi0upbjI4WT+zKtqkBg==} hasBin: true type-check@0.4.0: @@ -6380,11 +5794,6 @@ packages: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: ^5.8.3 - typescript@5.9.2: - resolution: {integrity: sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==} - engines: {node: '>=14.17'} - hasBin: true - typescript@5.9.3: resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} engines: {node: '>=14.17'} @@ -6396,21 +5805,18 @@ packages: typewise@1.0.3: resolution: {integrity: sha512-aXofE06xGhaQSPzt8hlTY+/YWQhm9P0jYUp1f2XtmW/3Bk0qzXcyFWAtPoo2uTGQj1ZwbDuSyuxicq+aDo8lCQ==} - ufo@1.6.1: - resolution: {integrity: sha512-9a4/uxlTWJ4+a5i0ooc1rU7C7YOw3wT+UGqdeNNHWnOF9qcMBgLRS+4IYUqbczewFx4mLEig6gawh7X6mFlEkA==} + ufo@1.6.3: + resolution: {integrity: sha512-yDJTmhydvl5lJzBmy/hyOAA0d+aqCBuwl818haVdYCRrWV84o7YyeVm4QlVHStqNrrJSTb6jKuFAVqAFsr+K3Q==} unbox-primitive@1.1.0: resolution: {integrity: sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==} engines: {node: '>= 0.4'} - undici-types@7.10.0: - resolution: {integrity: sha512-t5Fy/nfn+14LuOc2KNYg75vZqClpAiqscVvMygNnlsHBFpSXdJaYtXMcdNLpl/Qvc3P2cB3s6lOV51nqsFq4ag==} - undici-types@7.16.0: resolution: {integrity: sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==} - undici@7.24.4: - resolution: {integrity: sha512-BM/JzwwaRXxrLdElV2Uo6cTLEjhSb3WXboncJamZ15NgUURmvlXvxa6xkwIOILIjPNo9i8ku136ZvWV0Uly8+w==} + undici@7.24.7: + resolution: {integrity: sha512-H/nlJ/h0ggGC+uRL3ovD+G0i4bqhvsDOpbDv7At5eFLlj2b41L8QliGbnl2H7SnDiYhENphh1tQFJZf+MyfLsQ==} engines: {node: '>=20.18.1'} union-value@1.0.1: @@ -6489,20 +5895,20 @@ packages: yaml: optional: true - vitest@4.1.3: - resolution: {integrity: sha512-DBc4Tx0MPNsqb9isoyOq00lHftVx/KIU44QOm2q59npZyLUkENn8TMFsuzuO+4U2FUa9rgbbPt3udrP25GcjXw==} + vitest@4.1.4: + resolution: {integrity: sha512-tFuJqTxKb8AvfyqMfnavXdzfy3h3sWZRWwfluGbkeR7n0HUev+FmNgZ8SDrRBTVrVCjgH5cA21qGbCffMNtWvg==} engines: {node: ^20.0.0 || ^22.0.0 || >=24.0.0} hasBin: true peerDependencies: '@edge-runtime/vm': '*' '@opentelemetry/api': ^1.9.0 '@types/node': ^24.1.0 - '@vitest/browser-playwright': 4.1.3 - '@vitest/browser-preview': 4.1.3 - '@vitest/browser-webdriverio': 4.1.3 - '@vitest/coverage-istanbul': 4.1.3 - '@vitest/coverage-v8': 4.1.3 - '@vitest/ui': 4.1.3 + '@vitest/browser-playwright': 4.1.4 + '@vitest/browser-preview': 4.1.4 + '@vitest/browser-webdriverio': 4.1.4 + '@vitest/coverage-istanbul': 4.1.4 + '@vitest/coverage-v8': 4.1.4 + '@vitest/ui': 4.1.4 happy-dom: '*' jsdom: '*' vite: 7.3.2 @@ -6537,12 +5943,12 @@ packages: resolution: {integrity: sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==} engines: {node: '>=18'} + web-features@3.23.0: + resolution: {integrity: sha512-StLwngU3vc0DKGgwO+j7mQz1FhxaLPC1vR7v8zBsHgmqWIOqfE1+KMaD6GPMNhfnrpIyasKc9oNSLBIMlHpkww==} + webdriver-bidi-protocol@0.4.1: resolution: {integrity: sha512-ARrjNjtWRRs2w4Tk7nqrf2gBI0QXWuOmMCx2hU+1jUt6d00MjMxURrhxhGbrsoiZKJrhTSTzbIrc554iKI10qw==} - webidl-conversions@4.0.2: - resolution: {integrity: sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==} - webidl-conversions@8.0.1: resolution: {integrity: sha512-BMhLD/Sw+GbJC21C/UgyaZX41nPt8bUTg+jWyDeg7e7YN4xOM05YPSIXceACnXVtqyEw/LMClUQMtMZ+PGGpqQ==} engines: {node: '>=20'} @@ -6555,9 +5961,6 @@ packages: resolution: {integrity: sha512-1to4zXBxmXHV3IiSSEInrreIlu02vUOvrhxJJH5vcxYTBDAx51cqZiKdyTxlecdKNSjj8EcxGBxNf6Vg+945gw==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} - whatwg-url@7.1.0: - resolution: {integrity: sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==} - when-exit@2.1.5: resolution: {integrity: sha512-VGkKJ564kzt6Ms1dbgPP/yuIoQCrsFAnRbptpC5wOEsDaNsbCB2bnfnaA8i/vRs5tjUSEOtIuvl9/MyVsvQZCg==} @@ -6673,10 +6076,10 @@ packages: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} - zod-to-json-schema@3.25.1: - resolution: {integrity: sha512-pM/SU9d3YAggzi6MtR4h7ruuQlqKtad8e9S0fmxcMi+ueAK5Korys/aWcV9LIIHTVbj01NdzxcnXSN+O74ZIVA==} + zod-to-json-schema@3.25.2: + resolution: {integrity: sha512-O/PgfnpT1xKSDeQYSCfRI5Gy3hPf91mKVDuYLUHZJMiDFptvP41MSnWofm8dnCm0256ZNfZIM7DSzuSMAFnjHA==} peerDependencies: - zod: ^3.25 || ^4 + zod: ^3.25.28 || ^4 zod-validation-error@4.0.2: resolution: {integrity: sha512-Q6/nZLe6jxuU80qb/4uJ4t5v2VEZ44lzQjPDhYJNztRQ4wyWc6VF3D3Kb/fAuPetZQnhS3hnajCf9CsWesghLQ==} @@ -6694,20 +6097,12 @@ snapshots: '@acemir/cssom@0.9.31': {} - '@adobe/css-tools@4.4.3': {} - - '@asamuzakjp/css-color@5.0.1': - dependencies: - '@csstools/css-calc': 3.1.1(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) - '@csstools/css-color-parser': 4.0.2(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) - '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) - '@csstools/css-tokenizer': 4.0.0 - lru-cache: 11.2.6 + '@adobe/css-tools@4.4.4': {} - '@asamuzakjp/css-color@5.1.8': + '@asamuzakjp/css-color@5.1.10': dependencies: - '@csstools/css-calc': 3.1.1(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) - '@csstools/css-color-parser': 4.0.2(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) + '@csstools/css-calc': 3.2.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) + '@csstools/css-color-parser': 4.1.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) '@csstools/css-tokenizer': 4.0.0 @@ -6715,11 +6110,11 @@ snapshots: dependencies: '@asamuzakjp/nwsapi': 2.3.9 bidi-js: 1.0.3 - css-tree: 3.1.0 + css-tree: 3.2.1 is-potential-custom-element-name: 1.0.1 - lru-cache: 11.2.6 + lru-cache: 11.3.3 - '@asamuzakjp/dom-selector@7.0.8': + '@asamuzakjp/dom-selector@7.0.9': dependencies: '@asamuzakjp/nwsapi': 2.3.9 bidi-js: 1.0.3 @@ -6730,7 +6125,7 @@ snapshots: '@axe-core/playwright@4.11.1(playwright-core@1.59.1)': dependencies: - axe-core: 4.11.1 + axe-core: 4.11.2 playwright-core: 1.59.1 '@babel/code-frame@7.29.0': @@ -6747,8 +6142,8 @@ snapshots: '@babel/generator': 7.29.1 '@babel/helper-compilation-targets': 7.28.6 '@babel/helper-module-transforms': 7.28.6(@babel/core@7.29.0) - '@babel/helpers': 7.28.6 - '@babel/parser': 7.29.0 + '@babel/helpers': 7.29.2 + '@babel/parser': 7.29.2 '@babel/template': 7.28.6 '@babel/traverse': 7.29.0 '@babel/types': 7.29.0 @@ -6763,7 +6158,7 @@ snapshots: '@babel/generator@7.29.1': dependencies: - '@babel/parser': 7.29.0 + '@babel/parser': 7.29.2 '@babel/types': 7.29.0 '@jridgewell/gen-mapping': 0.3.13 '@jridgewell/trace-mapping': 0.3.31 @@ -6773,7 +6168,7 @@ snapshots: dependencies: '@babel/compat-data': 7.29.0 '@babel/helper-validator-option': 7.27.1 - browserslist: 4.28.1 + browserslist: 4.28.2 lru-cache: 5.1.1 semver: 6.3.1 @@ -6801,23 +6196,21 @@ snapshots: '@babel/helper-validator-option@7.27.1': {} - '@babel/helpers@7.28.6': + '@babel/helpers@7.29.2': dependencies: '@babel/template': 7.28.6 '@babel/types': 7.29.0 - '@babel/parser@7.29.0': + '@babel/parser@7.29.2': dependencies: '@babel/types': 7.29.0 - '@babel/runtime@7.28.2': {} - '@babel/runtime@7.29.2': {} '@babel/template@7.28.6': dependencies: '@babel/code-frame': 7.29.0 - '@babel/parser': 7.29.0 + '@babel/parser': 7.29.2 '@babel/types': 7.29.0 '@babel/traverse@7.29.0': @@ -6825,7 +6218,7 @@ snapshots: '@babel/code-frame': 7.29.0 '@babel/generator': 7.29.1 '@babel/helper-globals': 7.28.0 - '@babel/parser': 7.29.0 + '@babel/parser': 7.29.2 '@babel/template': 7.28.6 '@babel/types': 7.29.0 debug: 4.4.3 @@ -6872,7 +6265,7 @@ snapshots: dependencies: '@changesets/types': 6.1.0 - '@changesets/cli@2.30.0(@types/node@24.11.0)': + '@changesets/cli@2.30.0(@types/node@24.12.2)': dependencies: '@changesets/apply-release-plan': 7.1.0 '@changesets/assemble-release-plan': 6.0.9 @@ -6888,7 +6281,7 @@ snapshots: '@changesets/should-skip-package': 0.1.2 '@changesets/types': 6.1.0 '@changesets/write': 0.4.0 - '@inquirer/external-editor': 1.0.3(@types/node@24.11.0) + '@inquirer/external-editor': 1.0.3(@types/node@24.12.2) '@manypkg/get-packages': 1.1.3 ansi-colors: 4.1.3 enquirer: 2.4.1 @@ -6986,14 +6379,14 @@ snapshots: human-id: 4.1.3 prettier: 2.8.8 - '@commitlint/cli@20.5.0(@types/node@24.11.0)(conventional-commits-parser@6.4.0)(typescript@5.9.3)': + '@commitlint/cli@20.5.0(@types/node@24.12.2)(conventional-commits-parser@6.4.0)(typescript@5.9.3)': dependencies: '@commitlint/format': 20.5.0 '@commitlint/lint': 20.5.0 - '@commitlint/load': 20.5.0(@types/node@24.11.0)(typescript@5.9.3) + '@commitlint/load': 20.5.0(@types/node@24.12.2)(typescript@5.9.3) '@commitlint/read': 20.5.0(conventional-commits-parser@6.4.0) '@commitlint/types': 20.5.0 - tinyexec: 1.0.4 + tinyexec: 1.1.1 yargs: 17.7.2 transitivePeerDependencies: - '@types/node' @@ -7039,14 +6432,14 @@ snapshots: '@commitlint/rules': 20.5.0 '@commitlint/types': 20.5.0 - '@commitlint/load@20.5.0(@types/node@24.11.0)(typescript@5.9.3)': + '@commitlint/load@20.5.0(@types/node@24.12.2)(typescript@5.9.3)': dependencies: '@commitlint/config-validator': 20.5.0 '@commitlint/execute-rule': 20.0.0 '@commitlint/resolve-extends': 20.5.0 '@commitlint/types': 20.5.0 cosmiconfig: 9.0.1(typescript@5.9.3) - cosmiconfig-typescript-loader: 6.2.0(@types/node@24.11.0)(cosmiconfig@9.0.1(typescript@5.9.3))(typescript@5.9.3) + cosmiconfig-typescript-loader: 6.3.0(@types/node@24.12.2)(cosmiconfig@9.0.1(typescript@5.9.3))(typescript@5.9.3) is-plain-obj: 4.1.0 lodash.mergewith: 4.6.2 picocolors: 1.1.1 @@ -7068,7 +6461,7 @@ snapshots: '@commitlint/types': 20.5.0 git-raw-commits: 5.0.1(conventional-commits-parser@6.4.0) minimist: 1.2.8 - tinyexec: 1.0.4 + tinyexec: 1.1.1 transitivePeerDependencies: - conventional-commits-filter - conventional-commits-parser @@ -7110,15 +6503,15 @@ snapshots: '@csstools/color-helpers@6.0.2': {} - '@csstools/css-calc@3.1.1(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)': + '@csstools/css-calc@3.2.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)': dependencies: '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) '@csstools/css-tokenizer': 4.0.0 - '@csstools/css-color-parser@4.0.2(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)': + '@csstools/css-color-parser@4.1.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)': dependencies: '@csstools/color-helpers': 6.0.2 - '@csstools/css-calc': 3.1.1(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) + '@csstools/css-calc': 3.2.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) '@csstools/css-tokenizer': 4.0.0 @@ -7126,9 +6519,7 @@ snapshots: dependencies: '@csstools/css-tokenizer': 4.0.0 - '@csstools/css-syntax-patches-for-csstree@1.0.28': {} - - '@csstools/css-syntax-patches-for-csstree@1.1.2(css-tree@3.2.1)': + '@csstools/css-syntax-patches-for-csstree@1.1.3(css-tree@3.2.1)': optionalDependencies: css-tree: 3.2.1 @@ -7150,248 +6541,92 @@ snapshots: tslib: 2.8.1 optional: true - '@esbuild/aix-ppc64@0.25.8': - optional: true - - '@esbuild/aix-ppc64@0.27.3': - optional: true - '@esbuild/aix-ppc64@0.27.7': optional: true - '@esbuild/android-arm64@0.25.8': - optional: true - - '@esbuild/android-arm64@0.27.3': - optional: true - '@esbuild/android-arm64@0.27.7': optional: true - '@esbuild/android-arm@0.25.8': - optional: true - - '@esbuild/android-arm@0.27.3': - optional: true - '@esbuild/android-arm@0.27.7': optional: true - '@esbuild/android-x64@0.25.8': - optional: true - - '@esbuild/android-x64@0.27.3': - optional: true - '@esbuild/android-x64@0.27.7': optional: true - '@esbuild/darwin-arm64@0.25.8': - optional: true - - '@esbuild/darwin-arm64@0.27.3': - optional: true - '@esbuild/darwin-arm64@0.27.7': optional: true - '@esbuild/darwin-x64@0.25.8': - optional: true - - '@esbuild/darwin-x64@0.27.3': - optional: true - '@esbuild/darwin-x64@0.27.7': optional: true - '@esbuild/freebsd-arm64@0.25.8': - optional: true - - '@esbuild/freebsd-arm64@0.27.3': - optional: true - '@esbuild/freebsd-arm64@0.27.7': optional: true - '@esbuild/freebsd-x64@0.25.8': - optional: true - - '@esbuild/freebsd-x64@0.27.3': - optional: true - '@esbuild/freebsd-x64@0.27.7': optional: true - '@esbuild/linux-arm64@0.25.8': - optional: true - - '@esbuild/linux-arm64@0.27.3': - optional: true - '@esbuild/linux-arm64@0.27.7': optional: true - '@esbuild/linux-arm@0.25.8': - optional: true - - '@esbuild/linux-arm@0.27.3': - optional: true - '@esbuild/linux-arm@0.27.7': optional: true - '@esbuild/linux-ia32@0.25.8': - optional: true - - '@esbuild/linux-ia32@0.27.3': - optional: true - '@esbuild/linux-ia32@0.27.7': optional: true - '@esbuild/linux-loong64@0.25.8': - optional: true - - '@esbuild/linux-loong64@0.27.3': - optional: true - '@esbuild/linux-loong64@0.27.7': optional: true - '@esbuild/linux-mips64el@0.25.8': - optional: true - - '@esbuild/linux-mips64el@0.27.3': - optional: true - '@esbuild/linux-mips64el@0.27.7': optional: true - '@esbuild/linux-ppc64@0.25.8': - optional: true - - '@esbuild/linux-ppc64@0.27.3': - optional: true - '@esbuild/linux-ppc64@0.27.7': optional: true - '@esbuild/linux-riscv64@0.25.8': - optional: true - - '@esbuild/linux-riscv64@0.27.3': - optional: true - '@esbuild/linux-riscv64@0.27.7': optional: true - '@esbuild/linux-s390x@0.25.8': - optional: true - - '@esbuild/linux-s390x@0.27.3': - optional: true - '@esbuild/linux-s390x@0.27.7': optional: true - '@esbuild/linux-x64@0.25.8': - optional: true - - '@esbuild/linux-x64@0.27.3': - optional: true - '@esbuild/linux-x64@0.27.7': optional: true - '@esbuild/netbsd-arm64@0.25.8': - optional: true - - '@esbuild/netbsd-arm64@0.27.3': - optional: true - '@esbuild/netbsd-arm64@0.27.7': optional: true - '@esbuild/netbsd-x64@0.25.8': - optional: true - - '@esbuild/netbsd-x64@0.27.3': - optional: true - '@esbuild/netbsd-x64@0.27.7': optional: true - '@esbuild/openbsd-arm64@0.25.8': - optional: true - - '@esbuild/openbsd-arm64@0.27.3': - optional: true - '@esbuild/openbsd-arm64@0.27.7': optional: true - '@esbuild/openbsd-x64@0.25.8': - optional: true - - '@esbuild/openbsd-x64@0.27.3': - optional: true - '@esbuild/openbsd-x64@0.27.7': optional: true - '@esbuild/openharmony-arm64@0.25.8': - optional: true - - '@esbuild/openharmony-arm64@0.27.3': - optional: true - '@esbuild/openharmony-arm64@0.27.7': optional: true - '@esbuild/sunos-x64@0.25.8': - optional: true - - '@esbuild/sunos-x64@0.27.3': - optional: true - '@esbuild/sunos-x64@0.27.7': optional: true - '@esbuild/win32-arm64@0.25.8': - optional: true - - '@esbuild/win32-arm64@0.27.3': - optional: true - '@esbuild/win32-arm64@0.27.7': optional: true - '@esbuild/win32-ia32@0.25.8': - optional: true - - '@esbuild/win32-ia32@0.27.3': - optional: true - '@esbuild/win32-ia32@0.27.7': optional: true - '@esbuild/win32-x64@0.25.8': - optional: true - - '@esbuild/win32-x64@0.27.3': - optional: true - '@esbuild/win32-x64@0.27.7': optional: true - '@eslint-community/eslint-utils@4.9.1(eslint@9.39.3(jiti@2.6.1))': + '@eslint-community/eslint-utils@4.9.1(eslint@9.39.4(jiti@2.6.1))': dependencies: - eslint: 9.39.3(jiti@2.6.1) + eslint: 9.39.4(jiti@2.6.1) eslint-visitor-keys: 3.4.3 '@eslint-community/regexpp@4.12.2': {} - '@eslint/config-array@0.21.1': + '@eslint/config-array@0.21.2': dependencies: '@eslint/object-schema': 2.1.7 debug: 4.4.3 @@ -7407,7 +6642,7 @@ snapshots: dependencies: '@types/json-schema': 7.0.15 - '@eslint/eslintrc@3.3.4': + '@eslint/eslintrc@3.3.5': dependencies: ajv: 6.14.0 debug: 4.4.3 @@ -7421,7 +6656,7 @@ snapshots: transitivePeerDependencies: - supports-color - '@eslint/js@9.39.3': {} + '@eslint/js@9.39.4': {} '@eslint/object-schema@2.1.7': {} @@ -7430,8 +6665,6 @@ snapshots: '@eslint/core': 0.17.0 levn: 0.4.1 - '@exodus/bytes@1.14.1': {} - '@exodus/bytes@1.15.0': {} '@faker-js/faker@10.4.0': {} @@ -7462,9 +6695,9 @@ snapshots: dependencies: tslib: 2.8.1 - '@hono/node-server@1.19.11(hono@4.12.8)': + '@hono/node-server@1.19.13(hono@4.12.12)': dependencies: - hono: 4.12.8 + hono: 4.12.12 '@humanfs/core@0.19.1': {} @@ -7477,7 +6710,7 @@ snapshots: '@humanwhocodes/retry@0.4.3': {} - '@img/colour@1.0.0': + '@img/colour@1.1.0': optional: true '@img/sharp-darwin-arm64@0.34.5': @@ -7574,136 +6807,131 @@ snapshots: '@img/sharp-win32-x64@0.34.5': optional: true - '@inquirer/ansi@2.0.3': {} + '@inquirer/ansi@2.0.5': {} - '@inquirer/checkbox@5.1.0(@types/node@24.2.1)': + '@inquirer/checkbox@5.1.3(@types/node@24.12.2)': dependencies: - '@inquirer/ansi': 2.0.3 - '@inquirer/core': 11.1.5(@types/node@24.2.1) - '@inquirer/figures': 2.0.3 - '@inquirer/type': 4.0.3(@types/node@24.2.1) + '@inquirer/ansi': 2.0.5 + '@inquirer/core': 11.1.8(@types/node@24.12.2) + '@inquirer/figures': 2.0.5 + '@inquirer/type': 4.0.5(@types/node@24.12.2) optionalDependencies: - '@types/node': 24.2.1 + '@types/node': 24.12.2 - '@inquirer/confirm@6.0.8(@types/node@24.2.1)': + '@inquirer/confirm@6.0.11(@types/node@24.12.2)': dependencies: - '@inquirer/core': 11.1.5(@types/node@24.2.1) - '@inquirer/type': 4.0.3(@types/node@24.2.1) + '@inquirer/core': 11.1.8(@types/node@24.12.2) + '@inquirer/type': 4.0.5(@types/node@24.12.2) optionalDependencies: - '@types/node': 24.2.1 + '@types/node': 24.12.2 - '@inquirer/core@11.1.5(@types/node@24.2.1)': + '@inquirer/core@11.1.8(@types/node@24.12.2)': dependencies: - '@inquirer/ansi': 2.0.3 - '@inquirer/figures': 2.0.3 - '@inquirer/type': 4.0.3(@types/node@24.2.1) + '@inquirer/ansi': 2.0.5 + '@inquirer/figures': 2.0.5 + '@inquirer/type': 4.0.5(@types/node@24.12.2) cli-width: 4.1.0 fast-wrap-ansi: 0.2.0 mute-stream: 3.0.0 signal-exit: 4.1.0 optionalDependencies: - '@types/node': 24.2.1 + '@types/node': 24.12.2 - '@inquirer/editor@5.0.8(@types/node@24.2.1)': + '@inquirer/editor@5.1.0(@types/node@24.12.2)': dependencies: - '@inquirer/core': 11.1.5(@types/node@24.2.1) - '@inquirer/external-editor': 2.0.3(@types/node@24.2.1) - '@inquirer/type': 4.0.3(@types/node@24.2.1) + '@inquirer/core': 11.1.8(@types/node@24.12.2) + '@inquirer/external-editor': 3.0.0(@types/node@24.12.2) + '@inquirer/type': 4.0.5(@types/node@24.12.2) optionalDependencies: - '@types/node': 24.2.1 + '@types/node': 24.12.2 - '@inquirer/expand@5.0.8(@types/node@24.2.1)': + '@inquirer/expand@5.0.12(@types/node@24.12.2)': dependencies: - '@inquirer/core': 11.1.5(@types/node@24.2.1) - '@inquirer/type': 4.0.3(@types/node@24.2.1) + '@inquirer/core': 11.1.8(@types/node@24.12.2) + '@inquirer/type': 4.0.5(@types/node@24.12.2) optionalDependencies: - '@types/node': 24.2.1 + '@types/node': 24.12.2 - '@inquirer/external-editor@1.0.3(@types/node@24.11.0)': + '@inquirer/external-editor@1.0.3(@types/node@24.12.2)': dependencies: chardet: 2.1.1 iconv-lite: 0.7.2 optionalDependencies: - '@types/node': 24.11.0 + '@types/node': 24.12.2 - '@inquirer/external-editor@2.0.3(@types/node@24.2.1)': + '@inquirer/external-editor@3.0.0(@types/node@24.12.2)': dependencies: chardet: 2.1.1 iconv-lite: 0.7.2 optionalDependencies: - '@types/node': 24.2.1 + '@types/node': 24.12.2 - '@inquirer/figures@2.0.3': {} + '@inquirer/figures@2.0.5': {} - '@inquirer/input@5.0.8(@types/node@24.2.1)': + '@inquirer/input@5.0.11(@types/node@24.12.2)': dependencies: - '@inquirer/core': 11.1.5(@types/node@24.2.1) - '@inquirer/type': 4.0.3(@types/node@24.2.1) + '@inquirer/core': 11.1.8(@types/node@24.12.2) + '@inquirer/type': 4.0.5(@types/node@24.12.2) optionalDependencies: - '@types/node': 24.2.1 + '@types/node': 24.12.2 - '@inquirer/number@4.0.8(@types/node@24.2.1)': + '@inquirer/number@4.0.11(@types/node@24.12.2)': dependencies: - '@inquirer/core': 11.1.5(@types/node@24.2.1) - '@inquirer/type': 4.0.3(@types/node@24.2.1) + '@inquirer/core': 11.1.8(@types/node@24.12.2) + '@inquirer/type': 4.0.5(@types/node@24.12.2) optionalDependencies: - '@types/node': 24.2.1 + '@types/node': 24.12.2 - '@inquirer/password@5.0.8(@types/node@24.2.1)': + '@inquirer/password@5.0.11(@types/node@24.12.2)': dependencies: - '@inquirer/ansi': 2.0.3 - '@inquirer/core': 11.1.5(@types/node@24.2.1) - '@inquirer/type': 4.0.3(@types/node@24.2.1) + '@inquirer/ansi': 2.0.5 + '@inquirer/core': 11.1.8(@types/node@24.12.2) + '@inquirer/type': 4.0.5(@types/node@24.12.2) optionalDependencies: - '@types/node': 24.2.1 - - '@inquirer/prompts@8.3.0(@types/node@24.2.1)': - dependencies: - '@inquirer/checkbox': 5.1.0(@types/node@24.2.1) - '@inquirer/confirm': 6.0.8(@types/node@24.2.1) - '@inquirer/editor': 5.0.8(@types/node@24.2.1) - '@inquirer/expand': 5.0.8(@types/node@24.2.1) - '@inquirer/input': 5.0.8(@types/node@24.2.1) - '@inquirer/number': 4.0.8(@types/node@24.2.1) - '@inquirer/password': 5.0.8(@types/node@24.2.1) - '@inquirer/rawlist': 5.2.4(@types/node@24.2.1) - '@inquirer/search': 4.1.4(@types/node@24.2.1) - '@inquirer/select': 5.1.0(@types/node@24.2.1) + '@types/node': 24.12.2 + + '@inquirer/prompts@8.4.1(@types/node@24.12.2)': + dependencies: + '@inquirer/checkbox': 5.1.3(@types/node@24.12.2) + '@inquirer/confirm': 6.0.11(@types/node@24.12.2) + '@inquirer/editor': 5.1.0(@types/node@24.12.2) + '@inquirer/expand': 5.0.12(@types/node@24.12.2) + '@inquirer/input': 5.0.11(@types/node@24.12.2) + '@inquirer/number': 4.0.11(@types/node@24.12.2) + '@inquirer/password': 5.0.11(@types/node@24.12.2) + '@inquirer/rawlist': 5.2.7(@types/node@24.12.2) + '@inquirer/search': 4.1.7(@types/node@24.12.2) + '@inquirer/select': 5.1.3(@types/node@24.12.2) optionalDependencies: - '@types/node': 24.2.1 + '@types/node': 24.12.2 - '@inquirer/rawlist@5.2.4(@types/node@24.2.1)': + '@inquirer/rawlist@5.2.7(@types/node@24.12.2)': dependencies: - '@inquirer/core': 11.1.5(@types/node@24.2.1) - '@inquirer/type': 4.0.3(@types/node@24.2.1) + '@inquirer/core': 11.1.8(@types/node@24.12.2) + '@inquirer/type': 4.0.5(@types/node@24.12.2) optionalDependencies: - '@types/node': 24.2.1 + '@types/node': 24.12.2 - '@inquirer/search@4.1.4(@types/node@24.2.1)': + '@inquirer/search@4.1.7(@types/node@24.12.2)': dependencies: - '@inquirer/core': 11.1.5(@types/node@24.2.1) - '@inquirer/figures': 2.0.3 - '@inquirer/type': 4.0.3(@types/node@24.2.1) + '@inquirer/core': 11.1.8(@types/node@24.12.2) + '@inquirer/figures': 2.0.5 + '@inquirer/type': 4.0.5(@types/node@24.12.2) optionalDependencies: - '@types/node': 24.2.1 + '@types/node': 24.12.2 - '@inquirer/select@5.1.0(@types/node@24.2.1)': + '@inquirer/select@5.1.3(@types/node@24.12.2)': dependencies: - '@inquirer/ansi': 2.0.3 - '@inquirer/core': 11.1.5(@types/node@24.2.1) - '@inquirer/figures': 2.0.3 - '@inquirer/type': 4.0.3(@types/node@24.2.1) + '@inquirer/ansi': 2.0.5 + '@inquirer/core': 11.1.8(@types/node@24.12.2) + '@inquirer/figures': 2.0.5 + '@inquirer/type': 4.0.5(@types/node@24.12.2) optionalDependencies: - '@types/node': 24.2.1 + '@types/node': 24.12.2 - '@inquirer/type@4.0.3(@types/node@24.2.1)': + '@inquirer/type@4.0.5(@types/node@24.12.2)': optionalDependencies: - '@types/node': 24.2.1 - - '@jridgewell/gen-mapping@0.3.12': - dependencies: - '@jridgewell/sourcemap-codec': 1.5.4 - '@jridgewell/trace-mapping': 0.3.29 + '@types/node': 24.12.2 '@jridgewell/gen-mapping@0.3.13': dependencies: @@ -7717,15 +6945,8 @@ snapshots: '@jridgewell/resolve-uri@3.1.2': {} - '@jridgewell/sourcemap-codec@1.5.4': {} - '@jridgewell/sourcemap-codec@1.5.5': {} - '@jridgewell/trace-mapping@0.3.29': - dependencies: - '@jridgewell/resolve-uri': 3.1.2 - '@jridgewell/sourcemap-codec': 1.5.4 - '@jridgewell/trace-mapping@0.3.31': dependencies: '@jridgewell/resolve-uri': 3.1.2 @@ -7734,7 +6955,7 @@ snapshots: '@manypkg/find-root@1.1.0': dependencies: '@babel/runtime': 7.29.2 - '@types/node': 24.11.0 + '@types/node': 24.12.2 find-up: 4.1.0 fs-extra: 8.1.0 @@ -7754,18 +6975,9 @@ snapshots: '@mapbox/jsonlint-lines-primitives@2.0.2': {} - '@mapbox/mapbox-gl-supported@3.0.0': - optional: true - '@mapbox/point-geometry@0.1.0': {} - '@mapbox/point-geometry@1.1.0': - optional: true - - '@mapbox/tiny-sdf@2.0.7': {} - - '@mapbox/tiny-sdf@2.1.0': - optional: true + '@mapbox/tiny-sdf@2.1.0': {} '@mapbox/unitbezier@0.0.1': {} @@ -7773,13 +6985,6 @@ snapshots: dependencies: '@mapbox/point-geometry': 0.1.0 - '@mapbox/vector-tile@2.0.4': - dependencies: - '@mapbox/point-geometry': 1.1.0 - '@types/geojson': 7946.0.16 - pbf: 4.0.1 - optional: true - '@mapbox/whoots-js@3.1.0': {} '@maplibre/maplibre-gl-style-spec@19.3.3': @@ -7801,9 +7006,9 @@ snapshots: rw: 1.3.3 tinyqueue: 3.0.0 - '@modelcontextprotocol/sdk@1.27.1(zod@4.3.6)': + '@modelcontextprotocol/sdk@1.29.0(zod@4.3.6)': dependencies: - '@hono/node-server': 1.19.11(hono@4.12.8) + '@hono/node-server': 1.19.13(hono@4.12.12) ajv: 8.18.0 ajv-formats: 3.0.1(ajv@8.18.0) content-type: 1.0.5 @@ -7812,14 +7017,14 @@ snapshots: eventsource: 3.0.7 eventsource-parser: 3.0.6 express: 5.2.1 - express-rate-limit: 8.3.1(express@5.2.1) - hono: 4.12.8 - jose: 6.1.3 + express-rate-limit: 8.3.2(express@5.2.1) + hono: 4.12.12 + jose: 6.2.2 json-schema-typed: 8.0.2 pkce-challenge: 5.0.1 raw-body: 3.0.2 zod: 4.3.6 - zod-to-json-schema: 3.25.1(zod@4.3.6) + zod-to-json-schema: 3.25.2(zod@4.3.6) transitivePeerDependencies: - supports-color @@ -7830,34 +7035,34 @@ snapshots: '@tybys/wasm-util': 0.10.1 optional: true - '@next/env@16.2.2': {} + '@next/env@16.2.3': {} - '@next/eslint-plugin-next@16.2.2': + '@next/eslint-plugin-next@16.2.3': dependencies: fast-glob: 3.3.1 - '@next/swc-darwin-arm64@16.2.2': + '@next/swc-darwin-arm64@16.2.3': optional: true - '@next/swc-darwin-x64@16.2.2': + '@next/swc-darwin-x64@16.2.3': optional: true - '@next/swc-linux-arm64-gnu@16.2.2': + '@next/swc-linux-arm64-gnu@16.2.3': optional: true - '@next/swc-linux-arm64-musl@16.2.2': + '@next/swc-linux-arm64-musl@16.2.3': optional: true - '@next/swc-linux-x64-gnu@16.2.2': + '@next/swc-linux-x64-gnu@16.2.3': optional: true - '@next/swc-linux-x64-musl@16.2.2': + '@next/swc-linux-x64-musl@16.2.3': optional: true - '@next/swc-win32-arm64-msvc@16.2.2': + '@next/swc-win32-arm64-msvc@16.2.3': optional: true - '@next/swc-win32-x64-msvc@16.2.2': + '@next/swc-win32-x64-msvc@16.2.3': optional: true '@nodelib/fs.scandir@2.1.5': @@ -8181,9 +7386,9 @@ snapshots: legacy-javascript: 0.0.1 third-party-web: 0.29.0 - '@playwright/browser-chromium@1.58.2': + '@playwright/browser-chromium@1.59.1': dependencies: - playwright-core: 1.58.2 + playwright-core: 1.59.1 '@playwright/test@1.59.1': dependencies: @@ -8378,153 +7583,78 @@ snapshots: optionalDependencies: '@types/react': 19.2.14 - '@rollup/rollup-android-arm-eabi@4.59.0': - optional: true - '@rollup/rollup-android-arm-eabi@4.60.1': optional: true - '@rollup/rollup-android-arm64@4.59.0': - optional: true - '@rollup/rollup-android-arm64@4.60.1': optional: true - '@rollup/rollup-darwin-arm64@4.59.0': - optional: true - '@rollup/rollup-darwin-arm64@4.60.1': optional: true - '@rollup/rollup-darwin-x64@4.59.0': - optional: true - '@rollup/rollup-darwin-x64@4.60.1': optional: true - '@rollup/rollup-freebsd-arm64@4.59.0': - optional: true - '@rollup/rollup-freebsd-arm64@4.60.1': optional: true - '@rollup/rollup-freebsd-x64@4.59.0': - optional: true - '@rollup/rollup-freebsd-x64@4.60.1': optional: true - '@rollup/rollup-linux-arm-gnueabihf@4.59.0': - optional: true - '@rollup/rollup-linux-arm-gnueabihf@4.60.1': optional: true - '@rollup/rollup-linux-arm-musleabihf@4.59.0': - optional: true - '@rollup/rollup-linux-arm-musleabihf@4.60.1': optional: true - '@rollup/rollup-linux-arm64-gnu@4.59.0': - optional: true - '@rollup/rollup-linux-arm64-gnu@4.60.1': optional: true - '@rollup/rollup-linux-arm64-musl@4.59.0': - optional: true - '@rollup/rollup-linux-arm64-musl@4.60.1': optional: true - '@rollup/rollup-linux-loong64-gnu@4.59.0': - optional: true - '@rollup/rollup-linux-loong64-gnu@4.60.1': optional: true - '@rollup/rollup-linux-loong64-musl@4.59.0': - optional: true - '@rollup/rollup-linux-loong64-musl@4.60.1': optional: true - '@rollup/rollup-linux-ppc64-gnu@4.59.0': - optional: true - '@rollup/rollup-linux-ppc64-gnu@4.60.1': optional: true - '@rollup/rollup-linux-ppc64-musl@4.59.0': - optional: true - '@rollup/rollup-linux-ppc64-musl@4.60.1': optional: true - '@rollup/rollup-linux-riscv64-gnu@4.59.0': - optional: true - '@rollup/rollup-linux-riscv64-gnu@4.60.1': optional: true - '@rollup/rollup-linux-riscv64-musl@4.59.0': - optional: true - '@rollup/rollup-linux-riscv64-musl@4.60.1': optional: true - '@rollup/rollup-linux-s390x-gnu@4.59.0': - optional: true - '@rollup/rollup-linux-s390x-gnu@4.60.1': optional: true - '@rollup/rollup-linux-x64-gnu@4.59.0': - optional: true - '@rollup/rollup-linux-x64-gnu@4.60.1': optional: true - '@rollup/rollup-linux-x64-musl@4.59.0': - optional: true - '@rollup/rollup-linux-x64-musl@4.60.1': optional: true - '@rollup/rollup-openbsd-x64@4.59.0': - optional: true - '@rollup/rollup-openbsd-x64@4.60.1': optional: true - '@rollup/rollup-openharmony-arm64@4.59.0': - optional: true - '@rollup/rollup-openharmony-arm64@4.60.1': optional: true - '@rollup/rollup-win32-arm64-msvc@4.59.0': - optional: true - '@rollup/rollup-win32-arm64-msvc@4.60.1': optional: true - '@rollup/rollup-win32-ia32-msvc@4.59.0': - optional: true - '@rollup/rollup-win32-ia32-msvc@4.60.1': optional: true - '@rollup/rollup-win32-x64-gnu@4.59.0': - optional: true - '@rollup/rollup-win32-x64-gnu@4.60.1': optional: true - '@rollup/rollup-win32-x64-msvc@4.59.0': - optional: true - '@rollup/rollup-win32-x64-msvc@4.60.1': optional: true @@ -8581,7 +7711,7 @@ snapshots: '@sentry/node-core': 9.47.1(@opentelemetry/api@1.9.1)(@opentelemetry/context-async-hooks@1.30.1(@opentelemetry/api@1.9.1))(@opentelemetry/core@1.30.1(@opentelemetry/api@1.9.1))(@opentelemetry/instrumentation@0.57.2(@opentelemetry/api@1.9.1))(@opentelemetry/resources@1.30.1(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@1.30.1(@opentelemetry/api@1.9.1))(@opentelemetry/semantic-conventions@1.40.0) '@sentry/opentelemetry': 9.47.1(@opentelemetry/api@1.9.1)(@opentelemetry/context-async-hooks@1.30.1(@opentelemetry/api@1.9.1))(@opentelemetry/core@1.30.1(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@1.30.1(@opentelemetry/api@1.9.1))(@opentelemetry/semantic-conventions@1.40.0) import-in-the-middle: 1.15.0 - minimatch: 10.2.4 + minimatch: 10.2.5 transitivePeerDependencies: - supports-color @@ -8607,6 +7737,33 @@ snapshots: transitivePeerDependencies: - react + '@stackwright/build-scripts@0.4.0-alpha.7(react@19.2.4)': + dependencies: + '@stackwright/sbom-generator': 0.1.0-alpha.0 + '@stackwright/types': 1.1.0-alpha.6(react@19.2.4) + js-yaml: 4.1.1 + transitivePeerDependencies: + - react + + '@stackwright/cli@0.7.0-alpha.11(@types/node@24.12.2)(playwright@1.59.1)(react@19.2.4)': + dependencies: + '@inquirer/prompts': 8.4.1(@types/node@24.12.2) + '@stackwright/build-scripts': 0.4.0-alpha.7(react@19.2.4) + '@stackwright/sbom-generator': 0.1.0-alpha.0 + '@stackwright/scaffold-core': 0.1.0-alpha.1 + '@stackwright/themes': 0.5.1-alpha.0(react@19.2.4) + '@stackwright/types': 1.1.0-alpha.6(react@19.2.4) + chalk: 5.6.2 + commander: 14.0.3 + fs-extra: 11.3.4 + js-yaml: 4.1.1 + zod: 4.3.6 + optionalDependencies: + playwright: 1.59.1 + transitivePeerDependencies: + - '@types/node' + - react + '@stackwright/collections@0.1.0': {} '@stackwright/core@0.7.0-alpha.7(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': @@ -8614,7 +7771,7 @@ snapshots: '@stackwright/collections': 0.1.0 '@stackwright/themes': 0.5.1-alpha.0(react@19.2.4) '@stackwright/types': 1.1.0-alpha.6(react@19.2.4) - fuse.js: 7.1.0 + fuse.js: 7.3.0 js-yaml: 4.1.1 prismjs: 1.30.0 react: 19.2.4 @@ -8627,17 +7784,37 @@ snapshots: lucide-react: 0.577.0(react@19.2.4) react: 19.2.4 - '@stackwright/nextjs@0.3.1-alpha.7(next@16.2.2(@babel/core@7.29.0)(@opentelemetry/api@1.9.1)(@playwright/test@1.59.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': + '@stackwright/mcp@0.3.0-alpha.11(@types/node@24.12.2)(react@19.2.4)': + dependencies: + '@modelcontextprotocol/sdk': 1.29.0(zod@4.3.6) + '@stackwright/cli': 0.7.0-alpha.11(@types/node@24.12.2)(playwright@1.59.1)(react@19.2.4) + '@stackwright/types': 1.1.0-alpha.6(react@19.2.4) + playwright: 1.59.1 + zod: 4.3.6 + transitivePeerDependencies: + - '@cfworker/json-schema' + - '@types/node' + - react + - supports-color + + '@stackwright/nextjs@0.3.1-alpha.7(next@16.2.3(@babel/core@7.29.0)(@opentelemetry/api@1.9.1)(@playwright/test@1.59.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': dependencies: '@stackwright/core': 0.7.0-alpha.7(react-dom@19.2.4(react@19.2.4))(react@19.2.4) '@stackwright/themes': 0.5.1-alpha.0(react@19.2.4) '@stackwright/types': 1.1.0-alpha.6(react@19.2.4) js-yaml: 4.1.1 - next: 16.2.2(@babel/core@7.29.0)(@opentelemetry/api@1.9.1)(@playwright/test@1.59.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) - react: 19.2.4 - react-dom: 19.2.4(react@19.2.4) + next: 16.2.3(@babel/core@7.29.0)(@opentelemetry/api@1.9.1)(@playwright/test@1.59.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) + + '@stackwright/otters@0.2.0': {} + + '@stackwright/sbom-generator@0.1.0-alpha.0': + dependencies: + js-yaml: 4.1.1 + zod: 4.3.6 - '@stackwright/otters@0.2.0': {} + '@stackwright/scaffold-core@0.1.0-alpha.1': {} '@stackwright/themes@0.5.1-alpha.0(react@19.2.4)': dependencies: @@ -8669,51 +7846,59 @@ snapshots: '@standard-schema/spec@1.1.0': {} - '@swc/core-darwin-arm64@1.15.18': + '@swc/core-darwin-arm64@1.15.24': + optional: true + + '@swc/core-darwin-x64@1.15.24': + optional: true + + '@swc/core-linux-arm-gnueabihf@1.15.24': optional: true - '@swc/core-darwin-x64@1.15.18': + '@swc/core-linux-arm64-gnu@1.15.24': optional: true - '@swc/core-linux-arm-gnueabihf@1.15.18': + '@swc/core-linux-arm64-musl@1.15.24': optional: true - '@swc/core-linux-arm64-gnu@1.15.18': + '@swc/core-linux-ppc64-gnu@1.15.24': optional: true - '@swc/core-linux-arm64-musl@1.15.18': + '@swc/core-linux-s390x-gnu@1.15.24': optional: true - '@swc/core-linux-x64-gnu@1.15.18': + '@swc/core-linux-x64-gnu@1.15.24': optional: true - '@swc/core-linux-x64-musl@1.15.18': + '@swc/core-linux-x64-musl@1.15.24': optional: true - '@swc/core-win32-arm64-msvc@1.15.18': + '@swc/core-win32-arm64-msvc@1.15.24': optional: true - '@swc/core-win32-ia32-msvc@1.15.18': + '@swc/core-win32-ia32-msvc@1.15.24': optional: true - '@swc/core-win32-x64-msvc@1.15.18': + '@swc/core-win32-x64-msvc@1.15.24': optional: true - '@swc/core@1.15.18': + '@swc/core@1.15.24': dependencies: '@swc/counter': 0.1.3 - '@swc/types': 0.1.25 + '@swc/types': 0.1.26 optionalDependencies: - '@swc/core-darwin-arm64': 1.15.18 - '@swc/core-darwin-x64': 1.15.18 - '@swc/core-linux-arm-gnueabihf': 1.15.18 - '@swc/core-linux-arm64-gnu': 1.15.18 - '@swc/core-linux-arm64-musl': 1.15.18 - '@swc/core-linux-x64-gnu': 1.15.18 - '@swc/core-linux-x64-musl': 1.15.18 - '@swc/core-win32-arm64-msvc': 1.15.18 - '@swc/core-win32-ia32-msvc': 1.15.18 - '@swc/core-win32-x64-msvc': 1.15.18 + '@swc/core-darwin-arm64': 1.15.24 + '@swc/core-darwin-x64': 1.15.24 + '@swc/core-linux-arm-gnueabihf': 1.15.24 + '@swc/core-linux-arm64-gnu': 1.15.24 + '@swc/core-linux-arm64-musl': 1.15.24 + '@swc/core-linux-ppc64-gnu': 1.15.24 + '@swc/core-linux-s390x-gnu': 1.15.24 + '@swc/core-linux-x64-gnu': 1.15.24 + '@swc/core-linux-x64-musl': 1.15.24 + '@swc/core-win32-arm64-msvc': 1.15.24 + '@swc/core-win32-ia32-msvc': 1.15.24 + '@swc/core-win32-x64-msvc': 1.15.24 '@swc/counter@0.1.3': {} @@ -8721,7 +7906,7 @@ snapshots: dependencies: tslib: 2.8.1 - '@swc/types@0.1.25': + '@swc/types@0.1.26': dependencies: '@swc/counter': 0.1.3 @@ -8807,28 +7992,18 @@ snapshots: picocolors: 1.1.1 pretty-format: 27.5.1 - '@testing-library/jest-dom@6.6.4': - dependencies: - '@adobe/css-tools': 4.4.3 - aria-query: 5.3.2 - css.escape: 1.5.1 - dom-accessibility-api: 0.6.3 - lodash: 4.18.1 - picocolors: 1.1.1 - redent: 3.0.0 - '@testing-library/jest-dom@6.9.1': dependencies: - '@adobe/css-tools': 4.4.3 + '@adobe/css-tools': 4.4.4 aria-query: 5.3.2 css.escape: 1.5.1 dom-accessibility-api: 0.6.3 picocolors: 1.1.1 redent: 3.0.0 - '@testing-library/react@16.3.0(@testing-library/dom@10.4.1)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': + '@testing-library/react@16.3.2(@testing-library/dom@10.4.1)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': dependencies: - '@babel/runtime': 7.28.2 + '@babel/runtime': 7.29.2 '@testing-library/dom': 10.4.1 react: 19.2.4 react-dom: 19.2.4(react@19.2.4) @@ -8838,22 +8013,22 @@ snapshots: '@tootallnate/quickjs-emscripten@0.23.0': {} - '@turbo/darwin-64@2.9.5': + '@turbo/darwin-64@2.9.6': optional: true - '@turbo/darwin-arm64@2.9.5': + '@turbo/darwin-arm64@2.9.6': optional: true - '@turbo/linux-64@2.9.5': + '@turbo/linux-64@2.9.6': optional: true - '@turbo/linux-arm64@2.9.5': + '@turbo/linux-arm64@2.9.6': optional: true - '@turbo/windows-64@2.9.5': + '@turbo/windows-64@2.9.6': optional: true - '@turbo/windows-arm64@2.9.5': + '@turbo/windows-arm64@2.9.6': optional: true '@tybys/wasm-util@0.10.1': @@ -8863,13 +8038,14 @@ snapshots: '@types/aria-query@5.0.4': {} - '@types/chai@5.2.2': + '@types/chai@5.2.3': dependencies: '@types/deep-eql': 4.0.2 + assertion-error: 2.0.1 '@types/connect@3.4.38': dependencies: - '@types/node': 24.11.0 + '@types/node': 24.12.2 '@types/deep-eql@4.0.2': {} @@ -8878,7 +8054,7 @@ snapshots: '@types/fs-extra@11.0.4': dependencies: '@types/jsonfile': 6.1.4 - '@types/node': 24.11.0 + '@types/node': 24.12.2 '@types/geojson-vt@3.2.5': dependencies: @@ -8894,7 +8070,7 @@ snapshots: '@types/jsonfile@6.1.4': dependencies: - '@types/node': 24.11.0 + '@types/node': 24.12.2 '@types/mapbox__point-geometry@0.1.4': {} @@ -8906,20 +8082,12 @@ snapshots: '@types/mysql@2.15.26': dependencies: - '@types/node': 24.11.0 + '@types/node': 24.12.2 - '@types/node@24.10.13': + '@types/node@24.12.2': dependencies: undici-types: 7.16.0 - '@types/node@24.11.0': - dependencies: - undici-types: 7.16.0 - - '@types/node@24.2.1': - dependencies: - undici-types: 7.10.0 - '@types/pbf@3.0.5': {} '@types/pg-pool@2.0.6': @@ -8928,7 +8096,7 @@ snapshots: '@types/pg@8.6.1': dependencies: - '@types/node': 24.11.0 + '@types/node': 24.12.2 pg-protocol: 1.13.0 pg-types: 2.2.0 @@ -8936,7 +8104,7 @@ snapshots: '@types/prompts@2.4.9': dependencies: - '@types/node': 24.11.0 + '@types/node': 24.12.2 kleur: 3.0.3 '@types/react-dom@19.2.3(@types/react@19.2.14)': @@ -8955,22 +8123,22 @@ snapshots: '@types/tedious@4.0.14': dependencies: - '@types/node': 24.11.0 + '@types/node': 24.12.2 '@types/yauzl@2.10.3': dependencies: - '@types/node': 24.11.0 + '@types/node': 24.12.2 optional: true - '@typescript-eslint/eslint-plugin@8.58.1(@typescript-eslint/parser@8.58.1(eslint@9.39.3(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.3(jiti@2.6.1))(typescript@5.9.3)': + '@typescript-eslint/eslint-plugin@8.58.1(@typescript-eslint/parser@8.58.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3)': dependencies: '@eslint-community/regexpp': 4.12.2 - '@typescript-eslint/parser': 8.58.1(eslint@9.39.3(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/parser': 8.58.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3) '@typescript-eslint/scope-manager': 8.58.1 - '@typescript-eslint/type-utils': 8.58.1(eslint@9.39.3(jiti@2.6.1))(typescript@5.9.3) - '@typescript-eslint/utils': 8.58.1(eslint@9.39.3(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/type-utils': 8.58.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/utils': 8.58.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3) '@typescript-eslint/visitor-keys': 8.58.1 - eslint: 9.39.3(jiti@2.6.1) + eslint: 9.39.4(jiti@2.6.1) ignore: 7.0.5 natural-compare: 1.4.0 ts-api-utils: 2.5.0(typescript@5.9.3) @@ -8978,14 +8146,14 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@8.58.1(eslint@9.39.3(jiti@2.6.1))(typescript@5.9.3)': + '@typescript-eslint/parser@8.58.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3)': dependencies: '@typescript-eslint/scope-manager': 8.58.1 '@typescript-eslint/types': 8.58.1 '@typescript-eslint/typescript-estree': 8.58.1(typescript@5.9.3) '@typescript-eslint/visitor-keys': 8.58.1 debug: 4.4.3 - eslint: 9.39.3(jiti@2.6.1) + eslint: 9.39.4(jiti@2.6.1) typescript: 5.9.3 transitivePeerDependencies: - supports-color @@ -9008,13 +8176,13 @@ snapshots: dependencies: typescript: 5.9.3 - '@typescript-eslint/type-utils@8.58.1(eslint@9.39.3(jiti@2.6.1))(typescript@5.9.3)': + '@typescript-eslint/type-utils@8.58.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3)': dependencies: '@typescript-eslint/types': 8.58.1 '@typescript-eslint/typescript-estree': 8.58.1(typescript@5.9.3) - '@typescript-eslint/utils': 8.58.1(eslint@9.39.3(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/utils': 8.58.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3) debug: 4.4.3 - eslint: 9.39.3(jiti@2.6.1) + eslint: 9.39.4(jiti@2.6.1) ts-api-utils: 2.5.0(typescript@5.9.3) typescript: 5.9.3 transitivePeerDependencies: @@ -9037,13 +8205,13 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@8.58.1(eslint@9.39.3(jiti@2.6.1))(typescript@5.9.3)': + '@typescript-eslint/utils@8.58.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3)': dependencies: - '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.3(jiti@2.6.1)) + '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.4(jiti@2.6.1)) '@typescript-eslint/scope-manager': 8.58.1 '@typescript-eslint/types': 8.58.1 '@typescript-eslint/typescript-estree': 8.58.1(typescript@5.9.3) - eslint: 9.39.3(jiti@2.6.1) + eslint: 9.39.4(jiti@2.6.1) typescript: 5.9.3 transitivePeerDependencies: - supports-color @@ -9112,14 +8280,12 @@ snapshots: '@unrs/resolver-binding-win32-x64-msvc@1.11.1': optional: true - '@vis.gl/react-mapbox@8.1.0(mapbox-gl@3.20.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': + '@vis.gl/react-mapbox@8.1.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': dependencies: react: 19.2.4 react-dom: 19.2.4(react@19.2.4) - optionalDependencies: - mapbox-gl: 3.20.0 - '@vis.gl/react-maplibre@8.1.0(maplibre-gl@4.7.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': + '@vis.gl/react-maplibre@8.1.1(maplibre-gl@4.7.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': dependencies: '@maplibre/maplibre-gl-style-spec': 19.3.3 react: 19.2.4 @@ -9127,10 +8293,10 @@ snapshots: optionalDependencies: maplibre-gl: 4.7.1 - '@vitest/coverage-v8@4.1.3(vitest@4.1.3)': + '@vitest/coverage-v8@4.1.4(vitest@4.1.4)': dependencies: '@bcoe/v8-coverage': 1.0.2 - '@vitest/utils': 4.1.3 + '@vitest/utils': 4.1.4 ast-v8-to-istanbul: 1.0.0 istanbul-lib-coverage: 3.2.2 istanbul-lib-report: 3.0.1 @@ -9139,82 +8305,57 @@ snapshots: obug: 2.1.1 std-env: 4.0.0 tinyrainbow: 3.1.0 - vitest: 4.1.3(@opentelemetry/api@1.9.1)(@types/node@24.11.0)(@vitest/coverage-v8@4.1.3)(jsdom@29.0.2)(vite@7.3.2(@types/node@24.11.0)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.3)) + vitest: 4.1.4(@opentelemetry/api@1.9.1)(@types/node@24.12.2)(@vitest/coverage-v8@4.1.4)(@vitest/ui@4.1.4)(jsdom@29.0.2)(vite@7.3.2(@types/node@24.12.2)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.3)) - '@vitest/expect@4.1.3': + '@vitest/expect@4.1.4': dependencies: '@standard-schema/spec': 1.1.0 - '@types/chai': 5.2.2 - '@vitest/spy': 4.1.3 - '@vitest/utils': 4.1.3 + '@types/chai': 5.2.3 + '@vitest/spy': 4.1.4 + '@vitest/utils': 4.1.4 chai: 6.2.2 tinyrainbow: 3.1.0 - '@vitest/mocker@4.1.3(vite@7.3.2(@types/node@24.10.13)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.3))': - dependencies: - '@vitest/spy': 4.1.3 - estree-walker: 3.0.3 - magic-string: 0.30.21 - optionalDependencies: - vite: 7.3.2(@types/node@24.10.13)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.3) - - '@vitest/mocker@4.1.3(vite@7.3.2(@types/node@24.11.0)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.3))': - dependencies: - '@vitest/spy': 4.1.3 - estree-walker: 3.0.3 - magic-string: 0.30.21 - optionalDependencies: - vite: 7.3.2(@types/node@24.11.0)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.3) - - '@vitest/mocker@4.1.3(vite@7.3.2(@types/node@24.2.1)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.3))': + '@vitest/mocker@4.1.4(vite@7.3.2(@types/node@24.12.2)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.3))': dependencies: - '@vitest/spy': 4.1.3 + '@vitest/spy': 4.1.4 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: - vite: 7.3.2(@types/node@24.2.1)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.3) + vite: 7.3.2(@types/node@24.12.2)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.3) - '@vitest/pretty-format@4.0.18': - dependencies: - tinyrainbow: 3.0.3 - - '@vitest/pretty-format@4.1.3': + '@vitest/pretty-format@4.1.4': dependencies: tinyrainbow: 3.1.0 - '@vitest/runner@4.1.3': + '@vitest/runner@4.1.4': dependencies: - '@vitest/utils': 4.1.3 + '@vitest/utils': 4.1.4 pathe: 2.0.3 - '@vitest/snapshot@4.1.3': + '@vitest/snapshot@4.1.4': dependencies: - '@vitest/pretty-format': 4.1.3 - '@vitest/utils': 4.1.3 + '@vitest/pretty-format': 4.1.4 + '@vitest/utils': 4.1.4 magic-string: 0.30.21 pathe: 2.0.3 - '@vitest/spy@4.1.3': {} + '@vitest/spy@4.1.4': {} - '@vitest/ui@4.0.18(vitest@4.1.3)': + '@vitest/ui@4.1.4(vitest@4.1.4)': dependencies: - '@vitest/utils': 4.0.18 + '@vitest/utils': 4.1.4 fflate: 0.8.2 flatted: 3.4.2 pathe: 2.0.3 sirv: 3.0.2 - tinyglobby: 0.2.15 - tinyrainbow: 3.0.3 - vitest: 4.1.3(@opentelemetry/api@1.9.1)(@types/node@24.2.1)(@vitest/coverage-v8@4.1.3)(@vitest/ui@4.0.18)(jsdom@29.0.2)(vite@7.3.2(@types/node@24.2.1)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.3)) - - '@vitest/utils@4.0.18': - dependencies: - '@vitest/pretty-format': 4.0.18 - tinyrainbow: 3.0.3 + tinyglobby: 0.2.16 + tinyrainbow: 3.1.0 + vitest: 4.1.4(@opentelemetry/api@1.9.1)(@types/node@24.12.2)(@vitest/coverage-v8@4.1.4)(@vitest/ui@4.1.4)(jsdom@29.0.2)(vite@7.3.2(@types/node@24.12.2)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.3)) - '@vitest/utils@4.1.3': + '@vitest/utils@4.1.4': dependencies: - '@vitest/pretty-format': 4.1.3 + '@vitest/pretty-format': 4.1.4 convert-source-map: 2.0.0 tinyrainbow: 3.1.0 @@ -9231,8 +8372,6 @@ snapshots: dependencies: acorn: 8.16.0 - acorn@8.15.0: {} - acorn@8.16.0: {} agent-base@7.1.4: {} @@ -9298,7 +8437,7 @@ snapshots: array-includes@3.1.9: dependencies: - call-bind: 1.0.8 + call-bind: 1.0.9 call-bound: 1.0.4 define-properties: 1.2.1 es-abstract: 1.24.2 @@ -9311,7 +8450,7 @@ snapshots: array.prototype.findlast@1.2.5: dependencies: - call-bind: 1.0.8 + call-bind: 1.0.9 define-properties: 1.2.1 es-abstract: 1.24.2 es-errors: 1.3.0 @@ -9320,7 +8459,7 @@ snapshots: array.prototype.findlastindex@1.2.6: dependencies: - call-bind: 1.0.8 + call-bind: 1.0.9 call-bound: 1.0.4 define-properties: 1.2.1 es-abstract: 1.24.2 @@ -9330,21 +8469,21 @@ snapshots: array.prototype.flat@1.3.3: dependencies: - call-bind: 1.0.8 + call-bind: 1.0.9 define-properties: 1.2.1 es-abstract: 1.24.2 es-shim-unscopables: 1.1.0 array.prototype.flatmap@1.3.3: dependencies: - call-bind: 1.0.8 + call-bind: 1.0.9 define-properties: 1.2.1 es-abstract: 1.24.2 es-shim-unscopables: 1.1.0 array.prototype.tosorted@1.1.4: dependencies: - call-bind: 1.0.8 + call-bind: 1.0.9 define-properties: 1.2.1 es-abstract: 1.24.2 es-errors: 1.3.0 @@ -9353,13 +8492,15 @@ snapshots: arraybuffer.prototype.slice@1.0.4: dependencies: array-buffer-byte-length: 1.0.2 - call-bind: 1.0.8 + call-bind: 1.0.9 define-properties: 1.2.1 es-abstract: 1.24.2 es-errors: 1.3.0 get-intrinsic: 1.3.0 is-array-buffer: 3.0.5 + assertion-error@2.0.1: {} + assign-symbols@1.0.0: {} ast-types-flow@0.0.8: {} @@ -9385,8 +8526,6 @@ snapshots: dependencies: possible-typed-array-names: 1.1.0 - axe-core@4.11.1: {} - axe-core@4.11.2: {} axobject-query@4.1.0: {} @@ -9397,24 +8536,24 @@ snapshots: bare-events@2.8.2: {} - bare-fs@4.5.6: + bare-fs@4.7.0: dependencies: bare-events: 2.8.2 bare-path: 3.0.0 - bare-stream: 2.11.0(bare-events@2.8.2) + bare-stream: 2.13.0(bare-events@2.8.2) bare-url: 2.4.0 fast-fifo: 1.3.2 transitivePeerDependencies: - bare-abort-controller - react-native-b4a - bare-os@3.8.0: {} + bare-os@3.8.7: {} bare-path@3.0.0: dependencies: - bare-os: 3.8.0 + bare-os: 3.8.7 - bare-stream@2.11.0(bare-events@2.8.2): + bare-stream@2.13.0(bare-events@2.8.2): dependencies: streamx: 2.25.0 teex: 1.0.1 @@ -9427,9 +8566,9 @@ snapshots: dependencies: bare-path: 3.0.0 - baseline-browser-mapping@2.10.16: {} + baseline-browser-mapping@2.10.18: {} - basic-ftp@5.2.0: {} + basic-ftp@5.2.2: {} better-path-resolve@1.0.0: dependencies: @@ -9447,7 +8586,7 @@ snapshots: http-errors: 2.0.1 iconv-lite: 0.7.2 on-finished: 2.4.1 - qs: 6.15.0 + qs: 6.15.1 raw-body: 3.0.2 type-is: 2.0.1 transitivePeerDependencies: @@ -9461,19 +8600,19 @@ snapshots: dependencies: fill-range: 7.1.1 - browserslist@4.28.1: + browserslist@4.28.2: dependencies: - baseline-browser-mapping: 2.10.16 - caniuse-lite: 1.0.30001786 - electron-to-chromium: 1.5.286 - node-releases: 2.0.27 - update-browserslist-db: 1.2.3(browserslist@4.28.1) + baseline-browser-mapping: 2.10.18 + caniuse-lite: 1.0.30001787 + electron-to-chromium: 1.5.335 + node-releases: 2.0.37 + update-browserslist-db: 1.2.3(browserslist@4.28.2) buffer-crc32@0.2.13: {} - bundle-require@5.1.0(esbuild@0.25.8): + bundle-require@5.1.0(esbuild@0.27.7): dependencies: - esbuild: 0.25.8 + esbuild: 0.27.7 load-tsconfig: 0.2.5 bytes@3.1.2: {} @@ -9494,7 +8633,7 @@ snapshots: es-errors: 1.3.0 function-bind: 1.1.2 - call-bind@1.0.8: + call-bind@1.0.9: dependencies: call-bind-apply-helpers: 1.0.2 es-define-property: 1.0.1 @@ -9508,7 +8647,7 @@ snapshots: callsites@3.1.0: {} - caniuse-lite@1.0.30001786: {} + caniuse-lite@1.0.30001787: {} chai@6.2.2: {} @@ -9521,16 +8660,13 @@ snapshots: chardet@2.1.1: {} - cheap-ruler@4.0.0: - optional: true - chokidar@4.0.3: dependencies: readdirp: 4.1.2 chrome-launcher@1.2.1: dependencies: - '@types/node': 24.11.0 + '@types/node': 24.12.2 escape-string-regexp: 4.0.0 is-wsl: 2.2.0 lighthouse-logger: 2.0.2 @@ -9594,7 +8730,7 @@ snapshots: consola@3.4.2: {} - content-disposition@1.0.1: {} + content-disposition@1.1.0: {} content-type@1.0.5: {} @@ -9622,9 +8758,9 @@ snapshots: object-assign: 4.1.1 vary: 1.1.2 - cosmiconfig-typescript-loader@6.2.0(@types/node@24.11.0)(cosmiconfig@9.0.1(typescript@5.9.3))(typescript@5.9.3): + cosmiconfig-typescript-loader@6.3.0(@types/node@24.12.2)(cosmiconfig@9.0.1(typescript@5.9.3))(typescript@5.9.3): dependencies: - '@types/node': 24.11.0 + '@types/node': 24.12.2 cosmiconfig: 9.0.1(typescript@5.9.3) jiti: 2.6.1 typescript: 5.9.3 @@ -9646,11 +8782,6 @@ snapshots: csp_evaluator@1.1.5: {} - css-tree@3.1.0: - dependencies: - mdn-data: 2.12.2 - source-map-js: 1.2.1 - css-tree@3.2.1: dependencies: mdn-data: 2.27.1 @@ -9658,15 +8789,12 @@ snapshots: css.escape@1.5.1: {} - csscolorparser@1.0.3: - optional: true - - cssstyle@6.1.0: + cssstyle@6.2.0: dependencies: - '@asamuzakjp/css-color': 5.0.1 - '@csstools/css-syntax-patches-for-csstree': 1.0.28 - css-tree: 3.1.0 - lru-cache: 11.2.6 + '@asamuzakjp/css-color': 5.1.10 + '@csstools/css-syntax-patches-for-csstree': 1.1.3(css-tree@3.2.1) + css-tree: 3.2.1 + lru-cache: 11.3.3 csstype@3.2.3: {} @@ -9703,10 +8831,6 @@ snapshots: dependencies: ms: 2.1.3 - debug@4.4.1: - dependencies: - ms: 2.1.3 - debug@4.4.3: dependencies: ms: 2.1.3 @@ -9743,10 +8867,10 @@ snapshots: detect-libc@2.1.2: {} - devtools-protocol@0.0.1527314: {} - devtools-protocol@0.0.1581282: {} + devtools-protocol@0.0.1608973: {} + dir-glob@3.0.1: dependencies: path-type: 4.0.0 @@ -9777,7 +8901,7 @@ snapshots: ee-first@1.1.1: {} - electron-to-chromium@1.5.286: {} + electron-to-chromium@1.5.335: {} emoji-regex@10.6.0: {} @@ -9816,7 +8940,7 @@ snapshots: array-buffer-byte-length: 1.0.2 arraybuffer.prototype.slice: 1.0.4 available-typed-arrays: 1.0.7 - call-bind: 1.0.8 + call-bind: 1.0.9 call-bound: 1.0.4 data-view-buffer: 1.0.2 data-view-byte-length: 1.0.2 @@ -9872,9 +8996,9 @@ snapshots: es-errors@1.3.0: {} - es-iterator-helpers@1.2.2: + es-iterator-helpers@1.3.2: dependencies: - call-bind: 1.0.8 + call-bind: 1.0.9 call-bound: 1.0.4 define-properties: 1.2.1 es-abstract: 1.24.2 @@ -9889,7 +9013,7 @@ snapshots: has-symbols: 1.1.0 internal-slot: 1.1.0 iterator.prototype: 1.1.5 - safe-array-concat: 1.1.3 + math-intrinsics: 1.1.0 es-module-lexer@2.0.0: {} @@ -9914,64 +9038,6 @@ snapshots: is-date-object: 1.1.0 is-symbol: 1.1.1 - esbuild@0.25.8: - optionalDependencies: - '@esbuild/aix-ppc64': 0.25.8 - '@esbuild/android-arm': 0.25.8 - '@esbuild/android-arm64': 0.25.8 - '@esbuild/android-x64': 0.25.8 - '@esbuild/darwin-arm64': 0.25.8 - '@esbuild/darwin-x64': 0.25.8 - '@esbuild/freebsd-arm64': 0.25.8 - '@esbuild/freebsd-x64': 0.25.8 - '@esbuild/linux-arm': 0.25.8 - '@esbuild/linux-arm64': 0.25.8 - '@esbuild/linux-ia32': 0.25.8 - '@esbuild/linux-loong64': 0.25.8 - '@esbuild/linux-mips64el': 0.25.8 - '@esbuild/linux-ppc64': 0.25.8 - '@esbuild/linux-riscv64': 0.25.8 - '@esbuild/linux-s390x': 0.25.8 - '@esbuild/linux-x64': 0.25.8 - '@esbuild/netbsd-arm64': 0.25.8 - '@esbuild/netbsd-x64': 0.25.8 - '@esbuild/openbsd-arm64': 0.25.8 - '@esbuild/openbsd-x64': 0.25.8 - '@esbuild/openharmony-arm64': 0.25.8 - '@esbuild/sunos-x64': 0.25.8 - '@esbuild/win32-arm64': 0.25.8 - '@esbuild/win32-ia32': 0.25.8 - '@esbuild/win32-x64': 0.25.8 - - esbuild@0.27.3: - optionalDependencies: - '@esbuild/aix-ppc64': 0.27.3 - '@esbuild/android-arm': 0.27.3 - '@esbuild/android-arm64': 0.27.3 - '@esbuild/android-x64': 0.27.3 - '@esbuild/darwin-arm64': 0.27.3 - '@esbuild/darwin-x64': 0.27.3 - '@esbuild/freebsd-arm64': 0.27.3 - '@esbuild/freebsd-x64': 0.27.3 - '@esbuild/linux-arm': 0.27.3 - '@esbuild/linux-arm64': 0.27.3 - '@esbuild/linux-ia32': 0.27.3 - '@esbuild/linux-loong64': 0.27.3 - '@esbuild/linux-mips64el': 0.27.3 - '@esbuild/linux-ppc64': 0.27.3 - '@esbuild/linux-riscv64': 0.27.3 - '@esbuild/linux-s390x': 0.27.3 - '@esbuild/linux-x64': 0.27.3 - '@esbuild/netbsd-arm64': 0.27.3 - '@esbuild/netbsd-x64': 0.27.3 - '@esbuild/openbsd-arm64': 0.27.3 - '@esbuild/openbsd-x64': 0.27.3 - '@esbuild/openharmony-arm64': 0.27.3 - '@esbuild/sunos-x64': 0.27.3 - '@esbuild/win32-arm64': 0.27.3 - '@esbuild/win32-ia32': 0.27.3 - '@esbuild/win32-x64': 0.27.3 - esbuild@0.27.7: optionalDependencies: '@esbuild/aix-ppc64': 0.27.7 @@ -10015,18 +9081,18 @@ snapshots: optionalDependencies: source-map: 0.6.1 - eslint-config-next@16.2.2(@typescript-eslint/parser@8.58.1(eslint@9.39.3(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.3(jiti@2.6.1))(typescript@5.9.3): + eslint-config-next@16.2.3(@typescript-eslint/parser@8.58.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3): dependencies: - '@next/eslint-plugin-next': 16.2.2 - eslint: 9.39.3(jiti@2.6.1) + '@next/eslint-plugin-next': 16.2.3 + eslint: 9.39.4(jiti@2.6.1) eslint-import-resolver-node: 0.3.10 - eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.58.1(eslint@9.39.3(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.3(jiti@2.6.1)))(eslint@9.39.3(jiti@2.6.1)) - eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.58.1(eslint@9.39.3(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.58.1(eslint@9.39.3(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.3(jiti@2.6.1)))(eslint@9.39.3(jiti@2.6.1)))(eslint@9.39.3(jiti@2.6.1)) - eslint-plugin-jsx-a11y: 6.10.2(eslint@9.39.3(jiti@2.6.1)) - eslint-plugin-react: 7.37.5(eslint@9.39.3(jiti@2.6.1)) - eslint-plugin-react-hooks: 7.0.1(eslint@9.39.3(jiti@2.6.1)) + eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.58.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.4(jiti@2.6.1)))(eslint@9.39.4(jiti@2.6.1)) + eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.58.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.58.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.4(jiti@2.6.1)))(eslint@9.39.4(jiti@2.6.1)))(eslint@9.39.4(jiti@2.6.1)) + eslint-plugin-jsx-a11y: 6.10.2(eslint@9.39.4(jiti@2.6.1)) + eslint-plugin-react: 7.37.5(eslint@9.39.4(jiti@2.6.1)) + eslint-plugin-react-hooks: 7.0.1(eslint@9.39.4(jiti@2.6.1)) globals: 16.4.0 - typescript-eslint: 8.58.1(eslint@9.39.3(jiti@2.6.1))(typescript@5.9.3) + typescript-eslint: 8.58.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3) optionalDependencies: typescript: 5.9.3 transitivePeerDependencies: @@ -10035,9 +9101,9 @@ snapshots: - eslint-plugin-import-x - supports-color - eslint-config-prettier@10.1.8(eslint@9.39.3(jiti@2.6.1)): + eslint-config-prettier@10.1.8(eslint@9.39.4(jiti@2.6.1)): dependencies: - eslint: 9.39.3(jiti@2.6.1) + eslint: 9.39.4(jiti@2.6.1) eslint-import-resolver-node@0.3.10: dependencies: @@ -10047,33 +9113,33 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.58.1(eslint@9.39.3(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.3(jiti@2.6.1)))(eslint@9.39.3(jiti@2.6.1)): + eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.58.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.4(jiti@2.6.1)))(eslint@9.39.4(jiti@2.6.1)): dependencies: '@nolyfill/is-core-module': 1.0.39 debug: 4.4.3 - eslint: 9.39.3(jiti@2.6.1) + eslint: 9.39.4(jiti@2.6.1) get-tsconfig: 4.13.7 is-bun-module: 2.0.0 stable-hash: 0.0.5 tinyglobby: 0.2.16 unrs-resolver: 1.11.1 optionalDependencies: - eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.58.1(eslint@9.39.3(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.58.1(eslint@9.39.3(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.3(jiti@2.6.1)))(eslint@9.39.3(jiti@2.6.1)))(eslint@9.39.3(jiti@2.6.1)) + eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.58.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.58.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.4(jiti@2.6.1)))(eslint@9.39.4(jiti@2.6.1)))(eslint@9.39.4(jiti@2.6.1)) transitivePeerDependencies: - supports-color - eslint-module-utils@2.12.1(@typescript-eslint/parser@8.58.1(eslint@9.39.3(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.10)(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.58.1(eslint@9.39.3(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.3(jiti@2.6.1)))(eslint@9.39.3(jiti@2.6.1)))(eslint@9.39.3(jiti@2.6.1)): + eslint-module-utils@2.12.1(@typescript-eslint/parser@8.58.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.10)(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.58.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.4(jiti@2.6.1)))(eslint@9.39.4(jiti@2.6.1)))(eslint@9.39.4(jiti@2.6.1)): dependencies: debug: 3.2.7 optionalDependencies: - '@typescript-eslint/parser': 8.58.1(eslint@9.39.3(jiti@2.6.1))(typescript@5.9.3) - eslint: 9.39.3(jiti@2.6.1) + '@typescript-eslint/parser': 8.58.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3) + eslint: 9.39.4(jiti@2.6.1) eslint-import-resolver-node: 0.3.10 - eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.58.1(eslint@9.39.3(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.3(jiti@2.6.1)))(eslint@9.39.3(jiti@2.6.1)) + eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.58.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.4(jiti@2.6.1)))(eslint@9.39.4(jiti@2.6.1)) transitivePeerDependencies: - supports-color - eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.58.1(eslint@9.39.3(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.58.1(eslint@9.39.3(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.3(jiti@2.6.1)))(eslint@9.39.3(jiti@2.6.1)))(eslint@9.39.3(jiti@2.6.1)): + eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.58.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.58.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.4(jiti@2.6.1)))(eslint@9.39.4(jiti@2.6.1)))(eslint@9.39.4(jiti@2.6.1)): dependencies: '@rtsao/scc': 1.1.0 array-includes: 3.1.9 @@ -10082,9 +9148,9 @@ snapshots: array.prototype.flatmap: 1.3.3 debug: 3.2.7 doctrine: 2.1.0 - eslint: 9.39.3(jiti@2.6.1) + eslint: 9.39.4(jiti@2.6.1) eslint-import-resolver-node: 0.3.10 - eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.58.1(eslint@9.39.3(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.10)(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.58.1(eslint@9.39.3(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.3(jiti@2.6.1)))(eslint@9.39.3(jiti@2.6.1)))(eslint@9.39.3(jiti@2.6.1)) + eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.58.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.10)(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.58.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.4(jiti@2.6.1)))(eslint@9.39.4(jiti@2.6.1)))(eslint@9.39.4(jiti@2.6.1)) hasown: 2.0.2 is-core-module: 2.16.1 is-glob: 4.0.3 @@ -10096,13 +9162,13 @@ snapshots: string.prototype.trimend: 1.0.9 tsconfig-paths: 3.15.0 optionalDependencies: - '@typescript-eslint/parser': 8.58.1(eslint@9.39.3(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/parser': 8.58.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3) transitivePeerDependencies: - eslint-import-resolver-typescript - eslint-import-resolver-webpack - supports-color - eslint-plugin-jsx-a11y@6.10.2(eslint@9.39.3(jiti@2.6.1)): + eslint-plugin-jsx-a11y@6.10.2(eslint@9.39.4(jiti@2.6.1)): dependencies: aria-query: 5.3.2 array-includes: 3.1.9 @@ -10112,7 +9178,7 @@ snapshots: axobject-query: 4.1.0 damerau-levenshtein: 1.0.8 emoji-regex: 9.2.2 - eslint: 9.39.3(jiti@2.6.1) + eslint: 9.39.4(jiti@2.6.1) hasown: 2.0.2 jsx-ast-utils: 3.3.5 language-tags: 1.0.9 @@ -10121,26 +9187,26 @@ snapshots: safe-regex-test: 1.1.0 string.prototype.includes: 2.0.1 - eslint-plugin-react-hooks@7.0.1(eslint@9.39.3(jiti@2.6.1)): + eslint-plugin-react-hooks@7.0.1(eslint@9.39.4(jiti@2.6.1)): dependencies: '@babel/core': 7.29.0 - '@babel/parser': 7.29.0 - eslint: 9.39.3(jiti@2.6.1) + '@babel/parser': 7.29.2 + eslint: 9.39.4(jiti@2.6.1) hermes-parser: 0.25.1 zod: 4.3.6 zod-validation-error: 4.0.2(zod@4.3.6) transitivePeerDependencies: - supports-color - eslint-plugin-react@7.37.5(eslint@9.39.3(jiti@2.6.1)): + eslint-plugin-react@7.37.5(eslint@9.39.4(jiti@2.6.1)): dependencies: array-includes: 3.1.9 array.prototype.findlast: 1.2.5 array.prototype.flatmap: 1.3.3 array.prototype.tosorted: 1.1.4 doctrine: 2.1.0 - es-iterator-helpers: 1.2.2 - eslint: 9.39.3(jiti@2.6.1) + es-iterator-helpers: 1.3.2 + eslint: 9.39.4(jiti@2.6.1) estraverse: 5.3.0 hasown: 2.0.2 jsx-ast-utils: 3.3.5 @@ -10165,15 +9231,15 @@ snapshots: eslint-visitor-keys@5.0.1: {} - eslint@9.39.3(jiti@2.6.1): + eslint@9.39.4(jiti@2.6.1): dependencies: - '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.3(jiti@2.6.1)) + '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.4(jiti@2.6.1)) '@eslint-community/regexpp': 4.12.2 - '@eslint/config-array': 0.21.1 + '@eslint/config-array': 0.21.2 '@eslint/config-helpers': 0.4.2 '@eslint/core': 0.17.0 - '@eslint/eslintrc': 3.3.4 - '@eslint/js': 9.39.3 + '@eslint/eslintrc': 3.3.5 + '@eslint/js': 9.39.4 '@eslint/plugin-kit': 0.4.1 '@humanfs/node': 0.16.7 '@humanwhocodes/module-importer': 1.0.1 @@ -10248,7 +9314,7 @@ snapshots: expect-type@1.3.0: {} - express-rate-limit@8.3.1(express@5.2.1): + express-rate-limit@8.3.2(express@5.2.1): dependencies: express: 5.2.1 ip-address: 10.1.0 @@ -10257,7 +9323,7 @@ snapshots: dependencies: accepts: 2.0.0 body-parser: 2.2.2 - content-disposition: 1.0.1 + content-disposition: 1.1.0 content-type: 1.0.5 cookie: 0.7.2 cookie-signature: 1.2.2 @@ -10275,7 +9341,7 @@ snapshots: once: 1.4.0 parseurl: 1.3.3 proxy-addr: 2.0.7 - qs: 6.15.0 + qs: 6.15.1 range-parser: 1.2.1 router: 2.2.0 send: 1.2.1 @@ -10343,10 +9409,6 @@ snapshots: dependencies: pend: 1.2.0 - fdir@6.4.6(picomatch@4.0.4): - optionalDependencies: - picomatch: 4.0.4 - fdir@6.5.0(picomatch@4.0.4): optionalDependencies: picomatch: 4.0.4 @@ -10384,9 +9446,9 @@ snapshots: fix-dts-default-cjs-exports@1.0.1: dependencies: - magic-string: 0.30.17 - mlly: 1.7.4 - rollup: 4.59.0 + magic-string: 0.30.21 + mlly: 1.8.2 + rollup: 4.60.1 flat-cache@4.0.1: dependencies: @@ -10405,16 +9467,10 @@ snapshots: fresh@2.0.0: {} - fs-extra@11.3.1: - dependencies: - graceful-fs: 4.2.11 - jsonfile: 6.1.0 - universalify: 2.0.1 - fs-extra@11.3.4: dependencies: graceful-fs: 4.2.11 - jsonfile: 6.1.0 + jsonfile: 6.2.0 universalify: 2.0.1 fs-extra@7.0.1: @@ -10439,7 +9495,7 @@ snapshots: function.prototype.name@1.1.8: dependencies: - call-bind: 1.0.8 + call-bind: 1.0.9 call-bound: 1.0.4 define-properties: 1.2.1 functions-have-names: 1.2.3 @@ -10448,7 +9504,7 @@ snapshots: functions-have-names@1.2.3: {} - fuse.js@7.1.0: {} + fuse.js@7.3.0: {} generator-function@2.0.1: {} @@ -10490,17 +9546,13 @@ snapshots: es-errors: 1.3.0 get-intrinsic: 1.3.0 - get-tsconfig@4.10.1: - dependencies: - resolve-pkg-maps: 1.0.0 - get-tsconfig@4.13.7: dependencies: resolve-pkg-maps: 1.0.0 get-uri@6.0.5: dependencies: - basic-ftp: 5.2.0 + basic-ftp: 5.2.2 data-uri-to-buffer: 6.0.2 debug: 4.4.3 transitivePeerDependencies: @@ -10526,12 +9578,6 @@ snapshots: dependencies: is-glob: 4.0.3 - glob@13.0.6: - dependencies: - minimatch: 10.2.5 - minipass: 7.1.3 - path-scurry: 2.0.2 - global-directory@4.0.1: dependencies: ini: 4.1.1 @@ -10564,9 +9610,6 @@ snapshots: graceful-fs@4.2.11: {} - grid-index@1.1.0: - optional: true - has-bigints@1.1.0: {} has-flag@4.0.0: {} @@ -10595,11 +9638,11 @@ snapshots: dependencies: hermes-estree: 0.25.1 - hono@4.12.8: {} + hono@4.12.12: {} html-encoding-sniffer@6.0.0: dependencies: - '@exodus/bytes': 1.14.1 + '@exodus/bytes': 1.15.0 transitivePeerDependencies: - '@noble/hashes' @@ -10688,7 +9731,7 @@ snapshots: is-array-buffer@3.0.5: dependencies: - call-bind: 1.0.8 + call-bind: 1.0.9 call-bound: 1.0.4 get-intrinsic: 1.3.0 @@ -10868,7 +9911,7 @@ snapshots: jiti@2.6.1: {} - jose@6.1.3: {} + jose@6.2.2: {} joycon@3.1.1: {} @@ -10894,8 +9937,8 @@ snapshots: '@acemir/cssom': 0.9.31 '@asamuzakjp/dom-selector': 6.8.1 '@bramus/specificity': 2.4.2 - '@exodus/bytes': 1.14.1 - cssstyle: 6.1.0 + '@exodus/bytes': 1.15.0 + cssstyle: 6.2.0 data-urls: 7.0.0 decimal.js: 10.6.0 html-encoding-sniffer: 6.0.0 @@ -10905,8 +9948,8 @@ snapshots: parse5: 8.0.0 saxes: 6.0.0 symbol-tree: 3.2.4 - tough-cookie: 6.0.0 - undici: 7.24.4 + tough-cookie: 6.0.1 + undici: 7.24.7 w3c-xmlserializer: 5.0.0 webidl-conversions: 8.0.1 whatwg-mimetype: 5.0.0 @@ -10918,10 +9961,10 @@ snapshots: jsdom@29.0.2: dependencies: - '@asamuzakjp/css-color': 5.1.8 - '@asamuzakjp/dom-selector': 7.0.8 + '@asamuzakjp/css-color': 5.1.10 + '@asamuzakjp/dom-selector': 7.0.9 '@bramus/specificity': 2.4.2 - '@csstools/css-syntax-patches-for-csstree': 1.1.2(css-tree@3.2.1) + '@csstools/css-syntax-patches-for-csstree': 1.1.3(css-tree@3.2.1) '@exodus/bytes': 1.15.0 css-tree: 3.2.1 data-urls: 7.0.0 @@ -10933,7 +9976,7 @@ snapshots: saxes: 6.0.0 symbol-tree: 3.2.4 tough-cookie: 6.0.1 - undici: 7.24.4 + undici: 7.24.7 w3c-xmlserializer: 5.0.0 webidl-conversions: 8.0.1 whatwg-mimetype: 5.0.0 @@ -10970,7 +10013,7 @@ snapshots: optionalDependencies: graceful-fs: 4.2.11 - jsonfile@6.1.0: + jsonfile@6.2.0: dependencies: universalify: 2.0.1 optionalDependencies: @@ -11015,15 +10058,15 @@ snapshots: lighthouse-stack-packs@1.12.3: {} - lighthouse@13.0.3: + lighthouse@13.1.0: dependencies: '@paulirish/trace_engine': 0.0.61 '@sentry/node': 9.47.1 - axe-core: 4.11.1 + axe-core: 4.11.2 chrome-launcher: 1.2.1 configstore: 7.1.0 csp_evaluator: 1.1.5 - devtools-protocol: 0.0.1527314 + devtools-protocol: 0.0.1608973 enquirer: 2.4.1 http-link-header: 1.1.3 intl-messageformat: 10.7.18 @@ -11037,8 +10080,9 @@ snapshots: puppeteer-core: 24.40.0 robots-parser: 3.0.1 speedline-core: 1.4.3 - third-party-web: 0.27.0 - tldts-icann: 7.0.27 + third-party-web: 0.29.0 + tldts-icann: 7.0.28 + web-features: 3.23.0 ws: 7.5.10 yargs: 17.7.2 yargs-parser: 21.1.1 @@ -11109,7 +10153,7 @@ snapshots: listr2: 9.0.5 picomatch: 4.0.4 string-argv: 0.3.2 - tinyexec: 1.0.4 + tinyexec: 1.1.1 yaml: 2.8.3 listr2@9.0.5: @@ -11143,14 +10187,10 @@ snapshots: lodash.snakecase@4.1.1: {} - lodash.sortby@4.7.0: {} - lodash.startcase@4.4.0: {} lodash.upperfirst@4.3.1: {} - lodash@4.18.1: {} - log-update@6.1.0: dependencies: ansi-escapes: 7.3.0 @@ -11165,8 +10205,6 @@ snapshots: dependencies: js-tokens: 4.0.0 - lru-cache@11.2.6: {} - lru-cache@11.3.3: {} lru-cache@5.1.1: @@ -11187,23 +10225,19 @@ snapshots: dependencies: react: 19.2.4 - lucide-react@1.7.0(react@19.2.4): + lucide-react@1.8.0(react@19.2.4): dependencies: react: 19.2.4 lz-string@1.5.0: {} - magic-string@0.30.17: - dependencies: - '@jridgewell/sourcemap-codec': 1.5.4 - magic-string@0.30.21: dependencies: '@jridgewell/sourcemap-codec': 1.5.5 magicast@0.5.2: dependencies: - '@babel/parser': 7.29.0 + '@babel/parser': 7.29.2 '@babel/types': 7.29.0 source-map-js: 1.2.1 @@ -11211,40 +10245,12 @@ snapshots: dependencies: semver: 7.7.4 - mapbox-gl@3.20.0: - dependencies: - '@mapbox/jsonlint-lines-primitives': 2.0.2 - '@mapbox/mapbox-gl-supported': 3.0.0 - '@mapbox/point-geometry': 1.1.0 - '@mapbox/tiny-sdf': 2.1.0 - '@mapbox/unitbezier': 0.0.1 - '@mapbox/vector-tile': 2.0.4 - '@types/geojson': 7946.0.16 - '@types/geojson-vt': 3.2.5 - '@types/pbf': 3.0.5 - '@types/supercluster': 7.1.3 - cheap-ruler: 4.0.0 - csscolorparser: 1.0.3 - earcut: 3.0.2 - geojson-vt: 4.0.2 - gl-matrix: 3.4.4 - grid-index: 1.1.0 - kdbush: 4.0.2 - martinez-polygon-clipping: 0.8.1 - murmurhash-js: 1.0.0 - pbf: 4.0.1 - potpack: 2.1.0 - quickselect: 3.0.0 - supercluster: 8.0.1 - tinyqueue: 3.0.0 - optional: true - maplibre-gl@4.7.1: dependencies: '@mapbox/geojson-rewind': 0.5.2 '@mapbox/jsonlint-lines-primitives': 2.0.2 '@mapbox/point-geometry': 0.1.0 - '@mapbox/tiny-sdf': 2.0.7 + '@mapbox/tiny-sdf': 2.1.0 '@mapbox/unitbezier': 0.0.1 '@mapbox/vector-tile': 1.3.1 '@mapbox/whoots-js': 3.1.0 @@ -11270,17 +10276,8 @@ snapshots: marky@1.3.0: {} - martinez-polygon-clipping@0.8.1: - dependencies: - robust-predicates: 2.0.4 - splaytree: 0.1.4 - tinyqueue: 3.0.0 - optional: true - math-intrinsics@1.1.0: {} - mdn-data@2.12.2: {} - mdn-data@2.27.1: {} media-typer@1.1.0: {} @@ -11306,26 +10303,20 @@ snapshots: min-indent@1.0.1: {} - minimatch@10.2.4: - dependencies: - brace-expansion: 5.0.5 - minimatch@10.2.5: dependencies: brace-expansion: 5.0.5 minimist@1.2.8: {} - minipass@7.1.3: {} - mitt@3.0.1: {} - mlly@1.7.4: + mlly@1.8.2: dependencies: - acorn: 8.15.0 + acorn: 8.16.0 pathe: 2.0.3 pkg-types: 1.3.1 - ufo: 1.6.1 + ufo: 1.6.3 module-details-from-path@1.0.4: {} @@ -11353,27 +10344,27 @@ snapshots: negotiator@1.0.0: {} - netmask@2.0.2: {} + netmask@2.1.1: {} - next@16.2.2(@babel/core@7.29.0)(@opentelemetry/api@1.9.1)(@playwright/test@1.59.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4): + next@16.2.3(@babel/core@7.29.0)(@opentelemetry/api@1.9.1)(@playwright/test@1.59.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4): dependencies: - '@next/env': 16.2.2 + '@next/env': 16.2.3 '@swc/helpers': 0.5.15 - baseline-browser-mapping: 2.10.16 - caniuse-lite: 1.0.30001786 + baseline-browser-mapping: 2.10.18 + caniuse-lite: 1.0.30001787 postcss: 8.4.31 react: 19.2.4 react-dom: 19.2.4(react@19.2.4) styled-jsx: 5.1.6(@babel/core@7.29.0)(react@19.2.4) optionalDependencies: - '@next/swc-darwin-arm64': 16.2.2 - '@next/swc-darwin-x64': 16.2.2 - '@next/swc-linux-arm64-gnu': 16.2.2 - '@next/swc-linux-arm64-musl': 16.2.2 - '@next/swc-linux-x64-gnu': 16.2.2 - '@next/swc-linux-x64-musl': 16.2.2 - '@next/swc-win32-arm64-msvc': 16.2.2 - '@next/swc-win32-x64-msvc': 16.2.2 + '@next/swc-darwin-arm64': 16.2.3 + '@next/swc-darwin-x64': 16.2.3 + '@next/swc-linux-arm64-gnu': 16.2.3 + '@next/swc-linux-arm64-musl': 16.2.3 + '@next/swc-linux-x64-gnu': 16.2.3 + '@next/swc-linux-x64-musl': 16.2.3 + '@next/swc-win32-arm64-msvc': 16.2.3 + '@next/swc-win32-x64-msvc': 16.2.3 '@opentelemetry/api': 1.9.1 '@playwright/test': 1.59.1 sharp: 0.34.5 @@ -11390,7 +10381,7 @@ snapshots: object.entries: 1.1.9 semver: 6.3.1 - node-releases@2.0.27: {} + node-releases@2.0.37: {} object-assign@4.1.1: {} @@ -11400,7 +10391,7 @@ snapshots: object.assign@4.1.7: dependencies: - call-bind: 1.0.8 + call-bind: 1.0.9 call-bound: 1.0.4 define-properties: 1.2.1 es-object-atoms: 1.1.1 @@ -11409,27 +10400,27 @@ snapshots: object.entries@1.1.9: dependencies: - call-bind: 1.0.8 + call-bind: 1.0.9 call-bound: 1.0.4 define-properties: 1.2.1 es-object-atoms: 1.1.1 object.fromentries@2.0.8: dependencies: - call-bind: 1.0.8 + call-bind: 1.0.9 define-properties: 1.2.1 es-abstract: 1.24.2 es-object-atoms: 1.1.1 object.groupby@1.0.3: dependencies: - call-bind: 1.0.8 + call-bind: 1.0.9 define-properties: 1.2.1 es-abstract: 1.24.2 object.values@1.2.1: dependencies: - call-bind: 1.0.8 + call-bind: 1.0.9 call-bound: 1.0.4 define-properties: 1.2.1 es-object-atoms: 1.1.1 @@ -11511,7 +10502,7 @@ snapshots: pac-resolver@7.0.1: dependencies: degenerator: 5.0.1 - netmask: 2.0.2 + netmask: 2.1.1 package-manager-detector@0.2.11: dependencies: @@ -11540,11 +10531,6 @@ snapshots: path-parse@1.0.7: {} - path-scurry@2.0.2: - dependencies: - lru-cache: 11.2.6 - minipass: 7.1.3 - path-to-regexp@8.4.2: {} path-type@4.0.0: {} @@ -11556,11 +10542,6 @@ snapshots: ieee754: 1.2.1 resolve-protobuf-schema: 2.1.0 - pbf@4.0.1: - dependencies: - resolve-protobuf-schema: 2.1.0 - optional: true - pend@1.2.0: {} pg-int8@1.0.1: {} @@ -11588,19 +10569,11 @@ snapshots: pkg-types@1.3.1: dependencies: confbox: 0.1.8 - mlly: 1.7.4 + mlly: 1.8.2 pathe: 2.0.3 - playwright-core@1.58.2: {} - playwright-core@1.59.1: {} - playwright@1.58.2: - dependencies: - playwright-core: 1.58.2 - optionalDependencies: - fsevents: 2.3.2 - playwright@1.59.1: dependencies: playwright-core: 1.59.1 @@ -11646,7 +10619,7 @@ snapshots: prettier@2.8.8: {} - prettier@3.8.1: {} + prettier@3.8.2: {} pretty-format@27.5.1: dependencies: @@ -11669,7 +10642,7 @@ snapshots: object-assign: 4.1.1 react-is: 16.13.1 - protocol-buffers-schema@3.6.0: {} + protocol-buffers-schema@3.6.1: {} proxy-addr@2.0.7: dependencies: @@ -11715,7 +10688,7 @@ snapshots: - supports-color - utf-8-validate - qs@6.15.0: + qs@6.15.1: dependencies: side-channel: 1.1.0 @@ -11745,14 +10718,13 @@ snapshots: react-is@17.0.2: {} - react-map-gl@8.1.0(mapbox-gl@3.20.0)(maplibre-gl@4.7.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4): + react-map-gl@8.1.1(maplibre-gl@4.7.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4): dependencies: - '@vis.gl/react-mapbox': 8.1.0(mapbox-gl@3.20.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) - '@vis.gl/react-maplibre': 8.1.0(maplibre-gl@4.7.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@vis.gl/react-mapbox': 8.1.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@vis.gl/react-maplibre': 8.1.1(maplibre-gl@4.7.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) react: 19.2.4 react-dom: 19.2.4(react@19.2.4) optionalDependencies: - mapbox-gl: 3.20.0 maplibre-gl: 4.7.1 react@19.2.4: {} @@ -11773,7 +10745,7 @@ snapshots: reflect.getprototypeof@1.0.10: dependencies: - call-bind: 1.0.8 + call-bind: 1.0.9 define-properties: 1.2.1 es-abstract: 1.24.2 es-errors: 1.3.0 @@ -11784,7 +10756,7 @@ snapshots: regexp.prototype.flags@1.5.4: dependencies: - call-bind: 1.0.8 + call-bind: 1.0.9 define-properties: 1.2.1 es-errors: 1.3.0 get-proto: 1.0.1 @@ -11799,7 +10771,7 @@ snapshots: dependencies: debug: 4.4.3 module-details-from-path: 1.0.4 - resolve: 1.22.11 + resolve: 1.22.12 transitivePeerDependencies: - supports-color @@ -11811,10 +10783,11 @@ snapshots: resolve-protobuf-schema@2.1.0: dependencies: - protocol-buffers-schema: 3.6.0 + protocol-buffers-schema: 3.6.1 - resolve@1.22.11: + resolve@1.22.12: dependencies: + es-errors: 1.3.0 is-core-module: 2.16.1 path-parse: 1.0.7 supports-preserve-symlinks-flag: 1.0.0 @@ -11839,40 +10812,6 @@ snapshots: robots-parser@3.0.1: {} - robust-predicates@2.0.4: - optional: true - - rollup@4.59.0: - dependencies: - '@types/estree': 1.0.8 - optionalDependencies: - '@rollup/rollup-android-arm-eabi': 4.59.0 - '@rollup/rollup-android-arm64': 4.59.0 - '@rollup/rollup-darwin-arm64': 4.59.0 - '@rollup/rollup-darwin-x64': 4.59.0 - '@rollup/rollup-freebsd-arm64': 4.59.0 - '@rollup/rollup-freebsd-x64': 4.59.0 - '@rollup/rollup-linux-arm-gnueabihf': 4.59.0 - '@rollup/rollup-linux-arm-musleabihf': 4.59.0 - '@rollup/rollup-linux-arm64-gnu': 4.59.0 - '@rollup/rollup-linux-arm64-musl': 4.59.0 - '@rollup/rollup-linux-loong64-gnu': 4.59.0 - '@rollup/rollup-linux-loong64-musl': 4.59.0 - '@rollup/rollup-linux-ppc64-gnu': 4.59.0 - '@rollup/rollup-linux-ppc64-musl': 4.59.0 - '@rollup/rollup-linux-riscv64-gnu': 4.59.0 - '@rollup/rollup-linux-riscv64-musl': 4.59.0 - '@rollup/rollup-linux-s390x-gnu': 4.59.0 - '@rollup/rollup-linux-x64-gnu': 4.59.0 - '@rollup/rollup-linux-x64-musl': 4.59.0 - '@rollup/rollup-openbsd-x64': 4.59.0 - '@rollup/rollup-openharmony-arm64': 4.59.0 - '@rollup/rollup-win32-arm64-msvc': 4.59.0 - '@rollup/rollup-win32-ia32-msvc': 4.59.0 - '@rollup/rollup-win32-x64-gnu': 4.59.0 - '@rollup/rollup-win32-x64-msvc': 4.59.0 - fsevents: 2.3.3 - rollup@4.60.1: dependencies: '@types/estree': 1.0.8 @@ -11902,7 +10841,7 @@ snapshots: '@rollup/rollup-win32-ia32-msvc': 4.60.1 '@rollup/rollup-win32-x64-gnu': 4.60.1 '@rollup/rollup-win32-x64-msvc': 4.60.1 - fsevents: 2.3.3 + fsevents: 2.3.2 router@2.2.0: dependencies: @@ -11922,7 +10861,7 @@ snapshots: safe-array-concat@1.1.3: dependencies: - call-bind: 1.0.8 + call-bind: 1.0.9 call-bound: 1.0.4 get-intrinsic: 1.3.0 has-symbols: 1.1.0 @@ -12009,7 +10948,7 @@ snapshots: sharp@0.34.5: dependencies: - '@img/colour': 1.0.0 + '@img/colour': 1.1.0 detect-libc: 2.1.2 semver: 7.7.4 optionalDependencies: @@ -12047,7 +10986,7 @@ snapshots: shimmer@1.2.1: {} - side-channel-list@1.0.0: + side-channel-list@1.0.1: dependencies: es-errors: 1.3.0 object-inspect: 1.13.4 @@ -12071,7 +11010,7 @@ snapshots: dependencies: es-errors: 1.3.0 object-inspect: 1.13.4 - side-channel-list: 1.0.0 + side-channel-list: 1.0.1 side-channel-map: 1.0.1 side-channel-weakmap: 1.0.2 @@ -12132,9 +11071,7 @@ snapshots: source-map@0.6.1: optional: true - source-map@0.8.0-beta.0: - dependencies: - whatwg-url: 7.1.0 + source-map@0.7.6: {} spawndamnit@3.0.1: dependencies: @@ -12143,13 +11080,10 @@ snapshots: speedline-core@1.4.3: dependencies: - '@types/node': 24.11.0 + '@types/node': 24.12.2 image-ssim: 0.2.0 jpeg-js: 0.4.4 - splaytree@0.1.4: - optional: true - split-string@3.1.0: dependencies: extend-shallow: 3.0.2 @@ -12199,13 +11133,13 @@ snapshots: string.prototype.includes@2.0.1: dependencies: - call-bind: 1.0.8 + call-bind: 1.0.9 define-properties: 1.2.1 es-abstract: 1.24.2 string.prototype.matchall@4.0.12: dependencies: - call-bind: 1.0.8 + call-bind: 1.0.9 call-bound: 1.0.4 define-properties: 1.2.1 es-abstract: 1.24.2 @@ -12226,7 +11160,7 @@ snapshots: string.prototype.trim@1.2.10: dependencies: - call-bind: 1.0.8 + call-bind: 1.0.9 call-bound: 1.0.4 define-data-property: 1.1.4 define-properties: 1.2.1 @@ -12236,14 +11170,14 @@ snapshots: string.prototype.trimend@1.0.9: dependencies: - call-bind: 1.0.8 + call-bind: 1.0.9 call-bound: 1.0.4 define-properties: 1.2.1 es-object-atoms: 1.1.1 string.prototype.trimstart@1.0.8: dependencies: - call-bind: 1.0.8 + call-bind: 1.0.9 define-properties: 1.2.1 es-object-atoms: 1.1.1 @@ -12276,14 +11210,14 @@ snapshots: optionalDependencies: '@babel/core': 7.29.0 - sucrase@3.35.0: + sucrase@3.35.1: dependencies: - '@jridgewell/gen-mapping': 0.3.12 + '@jridgewell/gen-mapping': 0.3.13 commander: 4.1.1 - glob: 13.0.6 lines-and-columns: 1.2.4 mz: 2.7.0 pirates: 4.0.7 + tinyglobby: 0.2.16 ts-interface-checker: 0.1.13 supercluster@8.0.1: @@ -12300,8 +11234,6 @@ snapshots: tailwind-merge@3.5.0: {} - tailwindcss@4.2.1: {} - tailwindcss@4.2.2: {} tapable@2.3.2: {} @@ -12311,7 +11243,7 @@ snapshots: pump: 3.0.4 tar-stream: 3.1.8 optionalDependencies: - bare-fs: 4.5.6 + bare-fs: 4.7.0 bare-path: 3.0.0 transitivePeerDependencies: - bare-abort-controller @@ -12321,7 +11253,7 @@ snapshots: tar-stream@3.1.8: dependencies: b4a: 1.8.0 - bare-fs: 4.5.6 + bare-fs: 4.7.0 fast-fifo: 1.3.2 streamx: 2.25.0 transitivePeerDependencies: @@ -12352,25 +11284,13 @@ snapshots: dependencies: any-promise: 1.3.0 - third-party-web@0.27.0: {} - third-party-web@0.29.0: {} tinybench@2.9.0: {} tinyexec@0.3.2: {} - tinyexec@1.0.4: {} - - tinyglobby@0.2.14: - dependencies: - fdir: 6.4.6(picomatch@4.0.4) - picomatch: 4.0.4 - - tinyglobby@0.2.15: - dependencies: - fdir: 6.5.0(picomatch@4.0.4) - picomatch: 4.0.4 + tinyexec@1.1.1: {} tinyglobby@0.2.16: dependencies: @@ -12379,21 +11299,17 @@ snapshots: tinyqueue@3.0.0: {} - tinyrainbow@3.0.3: {} - tinyrainbow@3.1.0: {} - tldts-core@7.0.23: {} + tldts-core@7.0.28: {} - tldts-core@7.0.27: {} - - tldts-icann@7.0.27: + tldts-icann@7.0.28: dependencies: - tldts-core: 7.0.27 + tldts-core: 7.0.28 - tldts@7.0.23: + tldts@7.0.28: dependencies: - tldts-core: 7.0.23 + tldts-core: 7.0.28 to-regex-range@5.0.1: dependencies: @@ -12403,17 +11319,9 @@ snapshots: totalist@3.0.1: {} - tough-cookie@6.0.0: - dependencies: - tldts: 7.0.23 - tough-cookie@6.0.1: dependencies: - tldts: 7.0.23 - - tr46@1.0.1: - dependencies: - punycode: 2.3.1 + tldts: 7.0.28 tr46@6.0.0: dependencies: @@ -12436,56 +11344,27 @@ snapshots: tslib@2.8.1: {} - tsup@8.5.0(@swc/core@1.15.18)(jiti@2.6.1)(postcss@8.5.9)(tsx@4.21.0)(typescript@5.9.2)(yaml@2.8.3): + tsup@8.5.1(@swc/core@1.15.24)(jiti@2.6.1)(postcss@8.5.9)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.3): dependencies: - bundle-require: 5.1.0(esbuild@0.25.8) + bundle-require: 5.1.0(esbuild@0.27.7) cac: 6.7.14 chokidar: 4.0.3 consola: 3.4.2 - debug: 4.4.1 - esbuild: 0.25.8 - fix-dts-default-cjs-exports: 1.0.1 - joycon: 3.1.1 - picocolors: 1.1.1 - postcss-load-config: 6.0.1(jiti@2.6.1)(postcss@8.5.9)(tsx@4.21.0)(yaml@2.8.3) - resolve-from: 5.0.0 - rollup: 4.59.0 - source-map: 0.8.0-beta.0 - sucrase: 3.35.0 - tinyexec: 0.3.2 - tinyglobby: 0.2.14 - tree-kill: 1.2.2 - optionalDependencies: - '@swc/core': 1.15.18 - postcss: 8.5.9 - typescript: 5.9.2 - transitivePeerDependencies: - - jiti - - supports-color - - tsx - - yaml - - tsup@8.5.0(@swc/core@1.15.18)(jiti@2.6.1)(postcss@8.5.9)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.3): - dependencies: - bundle-require: 5.1.0(esbuild@0.25.8) - cac: 6.7.14 - chokidar: 4.0.3 - consola: 3.4.2 - debug: 4.4.1 - esbuild: 0.25.8 + debug: 4.4.3 + esbuild: 0.27.7 fix-dts-default-cjs-exports: 1.0.1 joycon: 3.1.1 picocolors: 1.1.1 postcss-load-config: 6.0.1(jiti@2.6.1)(postcss@8.5.9)(tsx@4.21.0)(yaml@2.8.3) resolve-from: 5.0.0 - rollup: 4.59.0 - source-map: 0.8.0-beta.0 - sucrase: 3.35.0 + rollup: 4.60.1 + source-map: 0.7.6 + sucrase: 3.35.1 tinyexec: 0.3.2 - tinyglobby: 0.2.14 + tinyglobby: 0.2.16 tree-kill: 1.2.2 optionalDependencies: - '@swc/core': 1.15.18 + '@swc/core': 1.15.24 postcss: 8.5.9 typescript: 5.9.3 transitivePeerDependencies: @@ -12496,19 +11375,19 @@ snapshots: tsx@4.21.0: dependencies: - esbuild: 0.27.3 - get-tsconfig: 4.10.1 + esbuild: 0.27.7 + get-tsconfig: 4.13.7 optionalDependencies: fsevents: 2.3.3 - turbo@2.9.5: + turbo@2.9.6: optionalDependencies: - '@turbo/darwin-64': 2.9.5 - '@turbo/darwin-arm64': 2.9.5 - '@turbo/linux-64': 2.9.5 - '@turbo/linux-arm64': 2.9.5 - '@turbo/windows-64': 2.9.5 - '@turbo/windows-arm64': 2.9.5 + '@turbo/darwin-64': 2.9.6 + '@turbo/darwin-arm64': 2.9.6 + '@turbo/linux-64': 2.9.6 + '@turbo/linux-arm64': 2.9.6 + '@turbo/windows-64': 2.9.6 + '@turbo/windows-arm64': 2.9.6 type-check@0.4.0: dependencies: @@ -12530,7 +11409,7 @@ snapshots: typed-array-byte-length@1.0.3: dependencies: - call-bind: 1.0.8 + call-bind: 1.0.9 for-each: 0.3.5 gopd: 1.2.0 has-proto: 1.2.0 @@ -12539,7 +11418,7 @@ snapshots: typed-array-byte-offset@1.0.4: dependencies: available-typed-arrays: 1.0.7 - call-bind: 1.0.8 + call-bind: 1.0.9 for-each: 0.3.5 gopd: 1.2.0 has-proto: 1.2.0 @@ -12548,7 +11427,7 @@ snapshots: typed-array-length@1.0.7: dependencies: - call-bind: 1.0.8 + call-bind: 1.0.9 for-each: 0.3.5 gopd: 1.2.0 is-typed-array: 1.1.15 @@ -12557,19 +11436,17 @@ snapshots: typed-query-selector@2.12.1: {} - typescript-eslint@8.58.1(eslint@9.39.3(jiti@2.6.1))(typescript@5.9.3): + typescript-eslint@8.58.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3): dependencies: - '@typescript-eslint/eslint-plugin': 8.58.1(@typescript-eslint/parser@8.58.1(eslint@9.39.3(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.3(jiti@2.6.1))(typescript@5.9.3) - '@typescript-eslint/parser': 8.58.1(eslint@9.39.3(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/eslint-plugin': 8.58.1(@typescript-eslint/parser@8.58.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/parser': 8.58.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3) '@typescript-eslint/typescript-estree': 8.58.1(typescript@5.9.3) - '@typescript-eslint/utils': 8.58.1(eslint@9.39.3(jiti@2.6.1))(typescript@5.9.3) - eslint: 9.39.3(jiti@2.6.1) + '@typescript-eslint/utils': 8.58.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3) + eslint: 9.39.4(jiti@2.6.1) typescript: 5.9.3 transitivePeerDependencies: - supports-color - typescript@5.9.2: {} - typescript@5.9.3: {} typewise-core@1.2.0: {} @@ -12578,7 +11455,7 @@ snapshots: dependencies: typewise-core: 1.2.0 - ufo@1.6.1: {} + ufo@1.6.3: {} unbox-primitive@1.1.0: dependencies: @@ -12587,11 +11464,9 @@ snapshots: has-symbols: 1.1.0 which-boxed-primitive: 1.1.1 - undici-types@7.10.0: {} - undici-types@7.16.0: {} - undici@7.24.4: {} + undici@7.24.7: {} union-value@1.0.1: dependencies: @@ -12630,9 +11505,9 @@ snapshots: '@unrs/resolver-binding-win32-ia32-msvc': 1.11.1 '@unrs/resolver-binding-win32-x64-msvc': 1.11.1 - update-browserslist-db@1.2.3(browserslist@4.28.1): + update-browserslist-db@1.2.3(browserslist@4.28.2): dependencies: - browserslist: 4.28.1 + browserslist: 4.28.2 escalade: 3.2.0 picocolors: 1.1.1 @@ -12644,39 +11519,7 @@ snapshots: vary@1.1.2: {} - vite@7.3.2(@types/node@24.10.13)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.3): - dependencies: - esbuild: 0.27.7 - fdir: 6.5.0(picomatch@4.0.4) - picomatch: 4.0.4 - postcss: 8.5.9 - rollup: 4.60.1 - tinyglobby: 0.2.16 - optionalDependencies: - '@types/node': 24.10.13 - fsevents: 2.3.3 - jiti: 2.6.1 - lightningcss: 1.32.0 - tsx: 4.21.0 - yaml: 2.8.3 - - vite@7.3.2(@types/node@24.11.0)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.3): - dependencies: - esbuild: 0.27.7 - fdir: 6.5.0(picomatch@4.0.4) - picomatch: 4.0.4 - postcss: 8.5.9 - rollup: 4.60.1 - tinyglobby: 0.2.16 - optionalDependencies: - '@types/node': 24.11.0 - fsevents: 2.3.3 - jiti: 2.6.1 - lightningcss: 1.32.0 - tsx: 4.21.0 - yaml: 2.8.3 - - vite@7.3.2(@types/node@24.2.1)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.3): + vite@7.3.2(@types/node@24.12.2)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.3): dependencies: esbuild: 0.27.7 fdir: 6.5.0(picomatch@4.0.4) @@ -12685,52 +11528,22 @@ snapshots: rollup: 4.60.1 tinyglobby: 0.2.16 optionalDependencies: - '@types/node': 24.2.1 + '@types/node': 24.12.2 fsevents: 2.3.3 jiti: 2.6.1 lightningcss: 1.32.0 tsx: 4.21.0 yaml: 2.8.3 - vitest@4.1.3(@opentelemetry/api@1.9.1)(@types/node@24.10.13)(@vitest/coverage-v8@4.1.3)(jsdom@29.0.2)(vite@7.3.2(@types/node@24.10.13)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.3)): - dependencies: - '@vitest/expect': 4.1.3 - '@vitest/mocker': 4.1.3(vite@7.3.2(@types/node@24.10.13)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.3)) - '@vitest/pretty-format': 4.1.3 - '@vitest/runner': 4.1.3 - '@vitest/snapshot': 4.1.3 - '@vitest/spy': 4.1.3 - '@vitest/utils': 4.1.3 - es-module-lexer: 2.0.0 - expect-type: 1.3.0 - magic-string: 0.30.21 - obug: 2.1.1 - pathe: 2.0.3 - picomatch: 4.0.4 - std-env: 4.0.0 - tinybench: 2.9.0 - tinyexec: 1.0.4 - tinyglobby: 0.2.15 - tinyrainbow: 3.1.0 - vite: 7.3.2(@types/node@24.10.13)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.3) - why-is-node-running: 2.3.0 - optionalDependencies: - '@opentelemetry/api': 1.9.1 - '@types/node': 24.10.13 - '@vitest/coverage-v8': 4.1.3(vitest@4.1.3) - jsdom: 29.0.2 - transitivePeerDependencies: - - msw - - vitest@4.1.3(@opentelemetry/api@1.9.1)(@types/node@24.11.0)(@vitest/coverage-v8@4.1.3)(jsdom@28.1.0)(vite@7.3.2(@types/node@24.11.0)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.3)): + vitest@4.1.4(@opentelemetry/api@1.9.1)(@types/node@24.12.2)(@vitest/coverage-v8@4.1.4)(@vitest/ui@4.1.4)(jsdom@28.1.0)(vite@7.3.2(@types/node@24.12.2)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.3)): dependencies: - '@vitest/expect': 4.1.3 - '@vitest/mocker': 4.1.3(vite@7.3.2(@types/node@24.11.0)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.3)) - '@vitest/pretty-format': 4.1.3 - '@vitest/runner': 4.1.3 - '@vitest/snapshot': 4.1.3 - '@vitest/spy': 4.1.3 - '@vitest/utils': 4.1.3 + '@vitest/expect': 4.1.4 + '@vitest/mocker': 4.1.4(vite@7.3.2(@types/node@24.12.2)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.3)) + '@vitest/pretty-format': 4.1.4 + '@vitest/runner': 4.1.4 + '@vitest/snapshot': 4.1.4 + '@vitest/spy': 4.1.4 + '@vitest/utils': 4.1.4 es-module-lexer: 2.0.0 expect-type: 1.3.0 magic-string: 0.30.21 @@ -12739,58 +11552,29 @@ snapshots: picomatch: 4.0.4 std-env: 4.0.0 tinybench: 2.9.0 - tinyexec: 1.0.4 - tinyglobby: 0.2.15 + tinyexec: 1.1.1 + tinyglobby: 0.2.16 tinyrainbow: 3.1.0 - vite: 7.3.2(@types/node@24.11.0)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.3) + vite: 7.3.2(@types/node@24.12.2)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.3) why-is-node-running: 2.3.0 optionalDependencies: '@opentelemetry/api': 1.9.1 - '@types/node': 24.11.0 - '@vitest/coverage-v8': 4.1.3(vitest@4.1.3) + '@types/node': 24.12.2 + '@vitest/coverage-v8': 4.1.4(vitest@4.1.4) + '@vitest/ui': 4.1.4(vitest@4.1.4) jsdom: 28.1.0 transitivePeerDependencies: - msw - vitest@4.1.3(@opentelemetry/api@1.9.1)(@types/node@24.11.0)(@vitest/coverage-v8@4.1.3)(jsdom@29.0.2)(vite@7.3.2(@types/node@24.11.0)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.3)): - dependencies: - '@vitest/expect': 4.1.3 - '@vitest/mocker': 4.1.3(vite@7.3.2(@types/node@24.11.0)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.3)) - '@vitest/pretty-format': 4.1.3 - '@vitest/runner': 4.1.3 - '@vitest/snapshot': 4.1.3 - '@vitest/spy': 4.1.3 - '@vitest/utils': 4.1.3 - es-module-lexer: 2.0.0 - expect-type: 1.3.0 - magic-string: 0.30.21 - obug: 2.1.1 - pathe: 2.0.3 - picomatch: 4.0.4 - std-env: 4.0.0 - tinybench: 2.9.0 - tinyexec: 1.0.4 - tinyglobby: 0.2.15 - tinyrainbow: 3.1.0 - vite: 7.3.2(@types/node@24.11.0)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.3) - why-is-node-running: 2.3.0 - optionalDependencies: - '@opentelemetry/api': 1.9.1 - '@types/node': 24.11.0 - '@vitest/coverage-v8': 4.1.3(vitest@4.1.3) - jsdom: 29.0.2 - transitivePeerDependencies: - - msw - - vitest@4.1.3(@opentelemetry/api@1.9.1)(@types/node@24.2.1)(@vitest/coverage-v8@4.1.3)(@vitest/ui@4.0.18)(jsdom@29.0.2)(vite@7.3.2(@types/node@24.2.1)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.3)): + vitest@4.1.4(@opentelemetry/api@1.9.1)(@types/node@24.12.2)(@vitest/coverage-v8@4.1.4)(@vitest/ui@4.1.4)(jsdom@29.0.2)(vite@7.3.2(@types/node@24.12.2)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.3)): dependencies: - '@vitest/expect': 4.1.3 - '@vitest/mocker': 4.1.3(vite@7.3.2(@types/node@24.2.1)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.3)) - '@vitest/pretty-format': 4.1.3 - '@vitest/runner': 4.1.3 - '@vitest/snapshot': 4.1.3 - '@vitest/spy': 4.1.3 - '@vitest/utils': 4.1.3 + '@vitest/expect': 4.1.4 + '@vitest/mocker': 4.1.4(vite@7.3.2(@types/node@24.12.2)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.3)) + '@vitest/pretty-format': 4.1.4 + '@vitest/runner': 4.1.4 + '@vitest/snapshot': 4.1.4 + '@vitest/spy': 4.1.4 + '@vitest/utils': 4.1.4 es-module-lexer: 2.0.0 expect-type: 1.3.0 magic-string: 0.30.21 @@ -12799,16 +11583,16 @@ snapshots: picomatch: 4.0.4 std-env: 4.0.0 tinybench: 2.9.0 - tinyexec: 1.0.4 - tinyglobby: 0.2.15 + tinyexec: 1.1.1 + tinyglobby: 0.2.16 tinyrainbow: 3.1.0 - vite: 7.3.2(@types/node@24.2.1)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.3) + vite: 7.3.2(@types/node@24.12.2)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.3) why-is-node-running: 2.3.0 optionalDependencies: '@opentelemetry/api': 1.9.1 - '@types/node': 24.2.1 - '@vitest/coverage-v8': 4.1.3(vitest@4.1.3) - '@vitest/ui': 4.0.18(vitest@4.1.3) + '@types/node': 24.12.2 + '@vitest/coverage-v8': 4.1.4(vitest@4.1.4) + '@vitest/ui': 4.1.4(vitest@4.1.4) jsdom: 29.0.2 transitivePeerDependencies: - msw @@ -12823,9 +11607,9 @@ snapshots: dependencies: xml-name-validator: 5.0.0 - webdriver-bidi-protocol@0.4.1: {} + web-features@3.23.0: {} - webidl-conversions@4.0.2: {} + webdriver-bidi-protocol@0.4.1: {} webidl-conversions@8.0.1: {} @@ -12833,18 +11617,12 @@ snapshots: whatwg-url@16.0.1: dependencies: - '@exodus/bytes': 1.14.1 + '@exodus/bytes': 1.15.0 tr46: 6.0.0 webidl-conversions: 8.0.1 transitivePeerDependencies: - '@noble/hashes' - whatwg-url@7.1.0: - dependencies: - lodash.sortby: 4.7.0 - tr46: 1.0.1 - webidl-conversions: 4.0.2 - when-exit@2.1.5: {} which-boxed-primitive@1.1.1: @@ -12881,7 +11659,7 @@ snapshots: which-typed-array@1.1.20: dependencies: available-typed-arrays: 1.0.7 - call-bind: 1.0.8 + call-bind: 1.0.9 call-bound: 1.0.4 for-each: 0.3.5 get-proto: 1.0.1 @@ -12954,7 +11732,7 @@ snapshots: yocto-queue@0.1.0: {} - zod-to-json-schema@3.25.1(zod@4.3.6): + zod-to-json-schema@3.25.2(zod@4.3.6): dependencies: zod: 4.3.6 From 3a364fe75aa6c9ed80a9da47cdde35437262f452 Mon Sep 17 00:00:00 2001 From: Charles Gibson <46542971+perasperaactual@users.noreply.github.com> Date: Sun, 12 Apr 2026 17:18:08 -0400 Subject: [PATCH 06/27] docs: document shared validation module architecture (fixes #338) (#342) * feat(hooks): add @stackwright/hooks-registry for cross-module singleton - Create new @stackwright/hooks-registry package using Symbol.for() pattern - Update @stackwright/scaffold-core to re-export from shared registry - Fix fallback:'blocking' + output:'export' incompatibility in template - Update E2E config to serve static out/ directory Fixes module isolation where Pro packages' hooks weren't visible to CLI. * fix(hooks): add resetForTesting export and improve singleton tests * fix: address lint warnings for PR #341 * chore: update visual regression baselines and SBOM files * fix(deps): pin undici to ^7.0.0 for jsdom compatibility * docs: add ADR 006 for shared validation module (fixes #338) --------- Co-authored-by: Stackwright Bot --- .changeset/fix-338-validation-docs.md | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 .changeset/fix-338-validation-docs.md diff --git a/.changeset/fix-338-validation-docs.md b/.changeset/fix-338-validation-docs.md new file mode 100644 index 00000000..7bc9b203 --- /dev/null +++ b/.changeset/fix-338-validation-docs.md @@ -0,0 +1,9 @@ +--- +'stackwright': patch +--- + +Document shared validation module architecture (fixes #338) + +- Add ADR 006: Shared Validation Module for Content Schema +- Document OSS→Pro bidirectional alignment for Python implementation +- Cross-reference from CLAUDE.md and CONTRIBUTING.md From eb9a86eb17219c4c5e28312b3e30fa233bae1898 Mon Sep 17 00:00:00 2001 From: Charles Gibson <46542971+perasperaactual@users.noreply.github.com> Date: Sun, 12 Apr 2026 18:47:11 -0400 Subject: [PATCH 07/27] fix(core): resolve theme tokens in icon color prop (fixes #339) (#343) * fix(core): resolve theme tokens in icon color prop (fixes #339) * chore: add changeset for #339 fix * fix(core): map background token to --sw-color-bg (fixes #343 review) --------- Co-authored-by: Stackwright Bot --- .changeset/fix-issue-339-icon-theme-tokens.md | 5 + packages/build-scripts/src/prebuild.ts | 59 +- packages/cli/src/commands/page.ts | 34 +- packages/cli/src/utils/site-validator.ts | 1 + packages/core/src/components/media/Media.tsx | 40 ++ packages/types/src/types/index.ts | 4 + packages/types/src/types/validation-hints.ts | 542 ++++++++++++++++++ packages/types/src/types/validation.ts | 435 ++++++++++++++ packages/types/test/validation.test.ts | 168 ++++++ 9 files changed, 1230 insertions(+), 58 deletions(-) create mode 100644 .changeset/fix-issue-339-icon-theme-tokens.md create mode 100644 packages/types/src/types/validation-hints.ts create mode 100644 packages/types/src/types/validation.ts create mode 100644 packages/types/test/validation.test.ts diff --git a/.changeset/fix-issue-339-icon-theme-tokens.md b/.changeset/fix-issue-339-icon-theme-tokens.md new file mode 100644 index 00000000..e5c5524f --- /dev/null +++ b/.changeset/fix-issue-339-icon-theme-tokens.md @@ -0,0 +1,5 @@ +--- +"@stackwright/core": patch +--- + +Fix icon color prop to resolve theme tokens like 'accent' to CSS variables, enabling dark mode support diff --git a/packages/build-scripts/src/prebuild.ts b/packages/build-scripts/src/prebuild.ts index b52a4675..81a34b3c 100644 --- a/packages/build-scripts/src/prebuild.ts +++ b/packages/build-scripts/src/prebuild.ts @@ -25,8 +25,7 @@ import path from 'path'; import yaml from 'js-yaml'; import { siteConfigSchema, - pageContentSchema, - KNOWN_CONTENT_TYPE_KEYS, + validatePageContent, collectionConfigSchema, VIDEO_EXTENSIONS as VIDEO_EXTENSIONS_ARRAY, } from '@stackwright/types'; @@ -362,35 +361,7 @@ function findContentFiles(dir: string, baseSlug = ''): ContentFile[] { } // -- Content type validation ------------------------------------------------ - -const knownContentKeys = new Set(KNOWN_CONTENT_TYPE_KEYS); - -/** - * Inspect raw (pre-Zod-parse) content items for unrecognized content type keys. - * Zod's default `.strip()` silently removes unknown keys, so a typo like - * `feture_list` would pass validation but render as an invisible gap. - * This check catches those typos at build time. - */ -function warnUnknownContentKeys(contentItems: Record[], filePath: string): void { - for (let i = 0; i < contentItems.length; i++) { - const item = contentItems[i]; - const itemType = item.type as string | undefined; - - if (!itemType) { - console.warn( - ` WARNING: content_items[${i}] in ${filePath} is missing required "type" field.` - ); - continue; - } - - if (!knownContentKeys.has(itemType)) { - console.warn( - ` WARNING: Unknown content type "${itemType}" in ${filePath} (content_items[${i}]). ` + - `Known types: ${KNOWN_CONTENT_TYPE_KEYS.join(', ')}` - ); - } - } -} +// NOTE: Unknown content type checking is now handled by validatePageContent() // -- Collections ------------------------------------------------------------ @@ -957,21 +928,17 @@ export async function runPrebuild(options?: string | PrebuildOptions): Promise { - const field = issue.path.length > 0 ? issue.path.join('.') : '(root)'; - return ` ${field}: ${issue.message}`; - }) - .join('\n'); - throw new Error(`Invalid content: ${filePath}\n${details}`); - } - - // Warn about unknown content type keys in the raw YAML (before Zod strips them) - const rawItems = (rawContent as any)?.content?.content_items; - if (Array.isArray(rawItems)) { - warnUnknownContentKeys(rawItems, filePath); + // Validate using shared validator (includes unknown content type checking) + const pageValidation = validatePageContent(rawContent); + if (!pageValidation.valid) { + const output = [ + `Invalid content: ${filePath}`, + ...pageValidation.errors.map( + (e) => + ` ${e.fieldPath}: ${e.hint}${e.suggestion ? ` (did you mean "${e.suggestion}"?)` : ''}` + ), + ].join('\n'); + throw new Error(output); } const slugDir = slug ?? '_root'; diff --git a/packages/cli/src/commands/page.ts b/packages/cli/src/commands/page.ts index 9ee68ed5..d04da05e 100644 --- a/packages/cli/src/commands/page.ts +++ b/packages/cli/src/commands/page.ts @@ -5,7 +5,7 @@ import fs from 'fs-extra'; import chalk from 'chalk'; import yaml from 'js-yaml'; import { detectProject } from '../utils/project-detector'; -import { pageContentSchema } from '../utils/schema-loader'; +import { validatePageContent } from '@stackwright/types'; import { outputResult, outputError, getErrorCode, formatError } from '../utils/json-output'; // --------------------------------------------------------------------------- @@ -89,11 +89,10 @@ export function validatePages(pagesDir: string, slug?: string): PageValidateResu continue; } - const result = pageContentSchema.safeParse(raw); - if (!result.success) { - for (const issue of result.error.issues) { - const fieldPath = issue.path.length > 0 ? issue.path.join('.') : '(root)'; - errors.push({ slug: page.slug, message: `${fieldPath}: ${issue.message}` }); + const result = validatePageContent(raw); + if (!result.valid) { + for (const err of result.errors) { + errors.push({ slug: page.slug, message: `${err.fieldPath}: ${err.hint}` }); } } } @@ -152,11 +151,12 @@ export function writePage(pagesDir: string, slug: string, yamlContent: string): throw err; } - const result = pageContentSchema.safeParse(raw); - if (!result.success) { - const fieldErrors = result.error.issues.map((issue) => { - const fieldPath = issue.path.length > 0 ? issue.path.join('.') : '(root)'; - return `${fieldPath}: ${issue.message}`; + // Validate using shared validator + const result = validatePageContent(raw); + if (!result.valid) { + const fieldErrors = result.errors.map((e) => { + const suggestion = e.suggestion ? ` (did you mean "${e.suggestion}"?)` : ''; + return `${e.fieldPath}: ${e.hint}${suggestion}`; }); const err = new Error(`Validation failed:\n ${fieldErrors.join('\n ')}`); (err as NodeJS.ErrnoException).code = 'VALIDATION_FAILED'; @@ -379,8 +379,18 @@ export function registerPage(program: Command): void { if (result.valid) { console.log(chalk.green('All pages are valid.')); } else { + console.log(chalk.red(`\n${result.errors.length} validation error(s):\n`)); for (const e of result.errors) { - console.log(chalk.red(` ${e.slug}: ${e.message}`)); + console.log(chalk.red(` ${e.slug}: ${e.message.split(':')[0]}`)); + const hint = e.message.split(':').slice(1).join(':').trim(); + if (hint) { + console.log(` ${hint}`); + } + // Check for suggestions in the original error + const suggestionMatch = hint.match(/Did you mean "([^"]+)"/); + if (suggestionMatch) { + console.log(chalk.yellow(` Did you mean: "${suggestionMatch[1]}"?`)); + } } } }); diff --git a/packages/cli/src/utils/site-validator.ts b/packages/cli/src/utils/site-validator.ts index 2837ccd4..c2b366ad 100644 --- a/packages/cli/src/utils/site-validator.ts +++ b/packages/cli/src/utils/site-validator.ts @@ -8,6 +8,7 @@ import yaml from 'js-yaml'; import { siteConfigSchema, pageContentSchema } from './schema-loader'; +import { suggestContentType } from '@stackwright/types'; // --------------------------------------------------------------------------- // Types diff --git a/packages/core/src/components/media/Media.tsx b/packages/core/src/components/media/Media.tsx index bffccbd9..f917946a 100644 --- a/packages/core/src/components/media/Media.tsx +++ b/packages/core/src/components/media/Media.tsx @@ -24,11 +24,51 @@ const isIconSource = (src: string): boolean => { ); }; +// Known theme token names that map to CSS custom properties +const THEME_TOKEN_COLORS = [ + 'primary', + 'secondary', + 'accent', + 'background', + 'surface', + 'text', + 'textSecondary', +] as const; + +type ThemeTokenColor = (typeof THEME_TOKEN_COLORS)[number]; + +/** Check if a color string is a known theme token */ +function isThemeToken(color: string): color is ThemeTokenColor { + return THEME_TOKEN_COLORS.includes(color as ThemeTokenColor); +} + +/** Convert theme token to CSS variable name */ +function themeTokenToCSSVar(token: ThemeTokenColor): string { + // Map theme token names to CSS variable names + // Some tokens have different names in CSS vs theme + const cssNameMap: Record = { + primary: 'primary', + secondary: 'secondary', + accent: 'accent', + background: 'bg', // background → bg + surface: 'surface', + text: 'text', + textSecondary: 'text-secondary', // textSecondary → text-secondary + }; + return `--sw-color-${cssNameMap[token]}`; +} + const renderIcon = (src: string, sizePx: number | string, color?: string) => { // Registry lookup only — no require(). See packages/icons/AGENTS.md for why. const IconComponent = getIconRegistry()?.get(src); if (IconComponent) { + // Handle theme tokens via CSS variables for proper dark mode support + if (color && isThemeToken(color)) { + const cssVar = themeTokenToCSSVar(color); + return ; + } + // Fallback to direct color or currentColor return ; } diff --git a/packages/types/src/types/index.ts b/packages/types/src/types/index.ts index 926ac4f3..e6b2f6d4 100644 --- a/packages/types/src/types/index.ts +++ b/packages/types/src/types/index.ts @@ -9,3 +9,7 @@ export * from './enums'; export * from './collection'; export * from './plugin'; export * from '../constants'; + +// Re-export validation utilities +export * from './validation'; +export * from './validation-hints'; diff --git a/packages/types/src/types/validation-hints.ts b/packages/types/src/types/validation-hints.ts new file mode 100644 index 00000000..1efe17d5 --- /dev/null +++ b/packages/types/src/types/validation-hints.ts @@ -0,0 +1,542 @@ +/** + * Validation hint database for Stackwright content types. + * + * Maps content types to their required/optional fields with hints. + * Used by the validation module to produce AI-friendly error messages. + */ + +export interface FieldHint { + field: string; + required: boolean; + description: string; + expected: string; + hint: string; +} + +export interface ContentTypeHints { + type: string; + description: string; + fields: FieldHint[]; +} + +/** + * Hint database for built-in content types. + * These hints are used when validation fails to provide actionable feedback. + */ +export const CONTENT_TYPE_HINTS: Record = { + main: { + type: 'main', + description: 'Hero/main content section with heading and body text', + fields: [ + { + field: 'heading', + required: true, + description: 'Page heading', + expected: '{ text: string, textSize: h1|h2|h3|h4|h5|h6 }', + hint: 'Use a heading object with text (the heading text) and textSize (h1-h6)', + }, + { + field: 'textBlocks', + required: true, + description: 'Body content paragraphs', + expected: 'array of { text: string, textSize: body1|body2|subtitle1|subtitle2 }', + hint: 'Wrap body text in a textBlocks array. Each item needs text and textSize.', + }, + { + field: 'media', + required: false, + description: 'Optional image or video', + expected: '{ type: "image"|"video"|"media"|"icon", src: string, ... }', + hint: 'Add an image or video with type and src fields', + }, + { + field: 'buttons', + required: false, + description: 'Call-to-action buttons', + expected: + 'array of { text: string, href?: string, variant?: "text"|"outlined"|"contained" }', + hint: 'Add buttons with text and optional href and variant', + }, + ], + }, + + grid: { + type: 'grid', + description: 'Multi-column grid layout', + fields: [ + { + field: 'label', + required: true, + description: 'Unique identifier', + expected: 'string (kebab-case recommended)', + hint: 'Add a unique label to identify this grid section', + }, + { + field: 'columns', + required: true, + description: 'Grid columns', + expected: 'array of { content_items: ContentItem[], width?: number }', + hint: 'Each column needs a content_items array containing valid content blocks', + }, + { + field: 'heading', + required: false, + description: 'Optional section heading', + expected: '{ text: string, textSize: h1|h2|h3|h4|h5|h6 }', + hint: 'Add a heading object with text and textSize', + }, + ], + }, + + tabbed_content: { + type: 'tabbed_content', + description: 'Tabbed content sections', + fields: [ + { + field: 'label', + required: true, + description: 'Unique identifier', + expected: 'string (kebab-case recommended)', + hint: 'Add a unique label to identify this tabbed section', + }, + { + field: 'heading', + required: true, + description: 'Section heading', + expected: '{ text: string, textSize: h1|h2|h3|h4|h5|h6 }', + hint: 'Use a heading object with text and textSize', + }, + { + field: 'tabs', + required: true, + description: 'Tab content items', + expected: 'array of ContentItem (any valid content type)', + hint: 'Each tab should be a valid content item like main, text_block, media, etc.', + }, + ], + }, + + text_block: { + type: 'text_block', + description: 'Rich text section with heading and body', + fields: [ + { + field: 'label', + required: true, + description: 'Unique identifier', + expected: 'string (kebab-case recommended)', + hint: 'Add a unique label to identify this text block', + }, + { + field: 'textBlocks', + required: true, + description: 'Text content', + expected: 'array of { text: string, textSize: body1|body2|subtitle1|subtitle2 }', + hint: 'Wrap text in a textBlocks array with text and textSize fields', + }, + { + field: 'heading', + required: false, + description: 'Optional heading', + expected: '{ text: string, textSize: h1|h2|h3|h4|h5|h6 }', + hint: 'Add a heading object with text and textSize', + }, + ], + }, + + carousel: { + type: 'carousel', + description: 'Sliding image carousel', + fields: [ + { + field: 'label', + required: true, + description: 'Unique identifier', + expected: 'string', + hint: 'Add a unique label for this carousel', + }, + { + field: 'heading', + required: true, + description: 'Carousel title', + expected: 'string', + hint: 'Add a heading string for the carousel title', + }, + { + field: 'items', + required: true, + description: 'Carousel slides', + expected: 'array of { title: string, text: string, media: MediaItem }', + hint: 'Each slide needs title, text, and a media item (image/video)', + }, + ], + }, + + feature_list: { + type: 'feature_list', + description: 'Feature grid with icons', + fields: [ + { + field: 'label', + required: true, + description: 'Unique identifier', + expected: 'string', + hint: 'Add a unique label for this feature list', + }, + { + field: 'items', + required: true, + description: 'Feature items', + expected: 'array of { heading: string, description: string, icon?: MediaItem }', + hint: 'Each feature needs a heading and description. Optionally add an icon.', + }, + ], + }, + + faq: { + type: 'faq', + description: 'Frequently asked questions', + fields: [ + { + field: 'label', + required: true, + description: 'Unique identifier', + expected: 'string', + hint: 'Add a unique label for this FAQ section', + }, + { + field: 'items', + required: true, + description: 'FAQ question-answer pairs', + expected: 'array of { question: string, answer: string }', + hint: 'Each FAQ item needs a question and answer', + }, + ], + }, + + pricing_table: { + type: 'pricing_table', + description: 'Pricing plan comparison', + fields: [ + { + field: 'label', + required: true, + description: 'Unique identifier', + expected: 'string', + hint: 'Add a unique label for this pricing table', + }, + { + field: 'plans', + required: true, + description: 'Pricing plans', + expected: + 'array of { name: string, price: string, features: string[], cta_text: string, cta_href: string }', + hint: 'Each plan needs name, price, features list, and CTA button', + }, + ], + }, + + alert: { + type: 'alert', + description: 'Callout/notice box', + fields: [ + { + field: 'label', + required: true, + description: 'Unique identifier', + expected: 'string', + hint: 'Add a unique label for this alert', + }, + { + field: 'variant', + required: true, + description: 'Alert style', + expected: 'info|warning|success|danger|note|tip', + hint: 'Choose a variant: info (blue), warning (yellow), success (green), danger (red), note (gray), tip (lightbulb)', + }, + { + field: 'body', + required: true, + description: 'Alert message', + expected: 'string', + hint: 'Add the alert message text', + }, + ], + }, + + contact_form_stub: { + type: 'contact_form_stub', + description: 'Contact form placeholder', + fields: [ + { + field: 'label', + required: true, + description: 'Unique identifier', + expected: 'string', + hint: 'Add a unique label for this contact form', + }, + { + field: 'email', + required: true, + description: 'Contact email', + expected: 'string (email address)', + hint: 'Add the email address where form submissions should be sent', + }, + ], + }, + + code_block: { + type: 'code_block', + description: 'Code snippet display', + fields: [ + { + field: 'label', + required: true, + description: 'Unique identifier', + expected: 'string', + hint: 'Add a unique label for this code block', + }, + { + field: 'code', + required: true, + description: 'Code content', + expected: 'string', + hint: 'Add the code to display', + }, + { + field: 'language', + required: false, + description: 'Programming language', + expected: 'javascript|typescript|python|bash|yaml|json|html|css|...', + hint: 'Specify the language for syntax highlighting', + }, + ], + }, + + timeline: { + type: 'timeline', + description: 'Timeline/changelog display', + fields: [ + { + field: 'label', + required: true, + description: 'Unique identifier', + expected: 'string', + hint: 'Add a unique label for this timeline', + }, + { + field: 'items', + required: true, + description: 'Timeline events', + expected: 'array of { year: string, event: string }', + hint: 'Each timeline item needs a year and event description', + }, + ], + }, + + icon_grid: { + type: 'icon_grid', + description: 'Grid of icons with labels', + fields: [ + { + field: 'label', + required: true, + description: 'Unique identifier', + expected: 'string', + hint: 'Add a unique label for this icon grid', + }, + { + field: 'icons', + required: true, + description: 'Icon items', + expected: + 'array of IconContent ({ type: "icon", src: string, label: string, size?: number })', + hint: 'Each icon needs type="icon", src (icon name), and label', + }, + ], + }, + + testimonial_grid: { + type: 'testimonial_grid', + description: 'Customer testimonial cards', + fields: [ + { + field: 'label', + required: true, + description: 'Unique identifier', + expected: 'string', + hint: 'Add a unique label for this testimonial grid', + }, + { + field: 'items', + required: true, + description: 'Testimonials', + expected: 'array of { quote: string, name: string, role?: string, company?: string }', + hint: 'Each testimonial needs a quote and name. Optionally add role and company.', + }, + ], + }, + + media: { + type: 'media', + description: 'Single media display (image/video)', + fields: [ + { + field: 'label', + required: true, + description: 'Unique identifier', + expected: 'string', + hint: 'Add a unique label for this media', + }, + { + field: 'src', + required: true, + description: 'Media source', + expected: 'string (URL or path)', + hint: 'Add the image or video URL/path', + }, + ], + }, + + video: { + type: 'video', + description: 'Video player', + fields: [ + { + field: 'label', + required: true, + description: 'Unique identifier', + expected: 'string', + hint: 'Add a unique label for this video', + }, + { + field: 'src', + required: true, + description: 'Video source', + expected: 'string (URL)', + hint: 'Add the video URL', + }, + ], + }, + + collection_list: { + type: 'collection_list', + description: 'Dynamic content from a collection', + fields: [ + { + field: 'label', + required: true, + description: 'Unique identifier', + expected: 'string', + hint: 'Add a unique label for this collection list', + }, + { + field: 'source', + required: true, + description: 'Collection name', + expected: 'string (defined in stackwright.yml collections)', + hint: 'Specify which collection to display (must be defined in stackwright.yml)', + }, + ], + }, + + map: { + type: 'map', + description: 'Interactive map', + fields: [ + { + field: 'label', + required: true, + description: 'Unique identifier', + expected: 'string', + hint: 'Add a unique label for this map', + }, + { + field: 'center', + required: true, + description: 'Map center coordinates', + expected: '{ lat: number, lng: number }', + hint: 'Specify latitude and longitude for map center', + }, + { + field: 'zoom', + required: true, + description: 'Zoom level', + expected: 'number (0-20)', + hint: 'Add zoom level between 0 (world) and 20 (street)', + }, + ], + }, +}; + +/** + * Get hints for a specific content type. + */ +export function getContentTypeHints(type: string): ContentTypeHints | undefined { + return CONTENT_TYPE_HINTS[type]; +} + +/** + * Check if a type string looks like a typo of a known content type. + * Returns the closest match if similarity > 0.6, otherwise null. + */ +export function suggestContentType(typo: string): string | null { + const knownTypes = Object.keys(CONTENT_TYPE_HINTS); + const lowerTypo = typo.toLowerCase(); + + // Exact match (case insensitive) + const exactMatch = knownTypes.find((t) => t.toLowerCase() === lowerTypo); + if (exactMatch) return null; // Not a typo if exact match exists + + // Find closest match using simple edit distance + let bestMatch: string | null = null; + let bestScore = 0; + + for (const known of knownTypes) { + const score = similarity(typo, known); + if (score > 0.6 && score > bestScore) { + bestScore = score; + bestMatch = known; + } + } + + return bestMatch; +} + +/** + * Simple similarity score between two strings (0-1). + * Uses Levenshtein distance relative to max length. + */ +function similarity(a: string, b: string): number { + const lenA = a.length; + const lenB = b.length; + + // Quick reject for very different lengths + if (Math.abs(lenA - lenB) > Math.max(lenA, lenB) * 0.5) { + return 0; + } + + // Create edit distance matrix + const matrix: number[][] = []; + for (let i = 0; i <= lenA; i++) { + matrix[i] = [i]; + } + for (let j = 0; j <= lenB; j++) { + matrix[0][j] = j; + } + + for (let i = 1; i <= lenA; i++) { + for (let j = 1; j <= lenB; j++) { + const cost = a[i - 1].toLowerCase() === b[j - 1].toLowerCase() ? 0 : 1; + matrix[i][j] = Math.min( + matrix[i - 1][j] + 1, + matrix[i][j - 1] + 1, + matrix[i - 1][j - 1] + cost + ); + } + } + + const distance = matrix[lenA][lenB]; + const maxLen = Math.max(lenA, lenB); + return maxLen > 0 ? 1 - distance / maxLen : 0; +} diff --git a/packages/types/src/types/validation.ts b/packages/types/src/types/validation.ts new file mode 100644 index 00000000..bd9922c0 --- /dev/null +++ b/packages/types/src/types/validation.ts @@ -0,0 +1,435 @@ +/** + * Shared validation module for Stackwright content. + * + * Used by both CLI (page validate) and prebuild to ensure consistent + * validation with AI-friendly error messages. + */ + +import { z } from 'zod'; +import { contentItemSchema, KNOWN_CONTENT_TYPE_KEYS, type GridColumn } from './content'; +import { pageContentSchema } from './layout'; +import { CONTENT_TYPE_HINTS, getContentTypeHints, suggestContentType } from './validation-hints'; + +// ============================================================================ +// Types +// ============================================================================ + +/** + * Structured validation error with semantic hints for AI agents. + */ +export interface ValidationError { + /** Zod path as array */ + path: string[]; + /** Human-readable field path */ + fieldPath: string; + /** The content type involved (if identifiable) */ + contentType?: string; + /** Index in parent array (if applicable) */ + index?: number; + /** What was expected */ + expected: string; + /** What was actually provided */ + actual?: string; + /** Actionable hint for fixing */ + hint: string; + /** Did you mean suggestion (for typos) */ + suggestion?: string; +} + +/** + * Result of page content validation. + */ +export interface ValidationResult { + /** Whether validation passed */ + valid: boolean; + /** Validation errors (empty if valid) */ + errors: ValidationError[]; + /** Human-readable summary */ + summary: string; +} + +// ============================================================================ +// Internal helpers +// ============================================================================ + +type ContentWalker = (item: Record, path: string[]) => void; + +/** + * Walk all content items recursively, including nested items in grids and tabs. + */ +function walkContentItems(root: Record, visitor: ContentWalker): void { + const walkItems = (items: unknown[], parentPath: string[]): void => { + if (!Array.isArray(items)) return; + + items.forEach((item, index) => { + if (typeof item !== 'object' || item === null) return; + const obj = item as Record; + const itemPath = [...parentPath, String(index)]; + + // Visit the item + visitor(obj, itemPath); + + // Recurse into grid columns + if (obj.type === 'grid' && Array.isArray(obj.columns)) { + for (let colIdx = 0; colIdx < (obj.columns as GridColumn[]).length; colIdx++) { + const col = (obj.columns as GridColumn[])[colIdx]; + if (Array.isArray(col.content_items)) { + walkItems(col.content_items, [...itemPath, 'columns', String(colIdx), 'content_items']); + } + } + } + + // Recurse into tabbed content tabs + if (obj.type === 'tabbed_content' && Array.isArray(obj.tabs)) { + walkItems(obj.tabs, [...itemPath, 'tabs']); + } + }); + }; + + const content = root?.content as Record | undefined; + if (!content) return; + + const items = content.content_items as unknown[] | undefined; + if (!items) return; + + walkItems(items, ['content', 'content_items']); +} + +/** + * Detect content type from a content item. + */ +function detectContentType(item: Record): string | undefined { + const type = item?.type; + if (typeof type === 'string' && KNOWN_CONTENT_TYPE_KEYS.includes(type as any)) { + return type; + } + return undefined; +} + +/** + * Get field path string from path array. + */ +function formatFieldPath(path: string[]): string { + return path.join('.'); +} + +/** + * Get content type and field from a Zod error path. + */ +function extractContextFromPath( + path: string[], + root: Record +): { contentType?: string; field?: string; index?: number } { + // Try to find content type by walking the structure + let foundType: string | undefined; + walkContentItems(root, (item, itemPath) => { + if (foundType) return; + + // Check if this path matches or is a prefix of the error path + const matches = path.length >= itemPath.length && itemPath.every((seg, i) => path[i] === seg); + + if (matches) { + foundType = detectContentType(item); + } + }); + + return { contentType: foundType }; +} + +// Zod v4 issue types - use any to handle complex discriminated union +interface ZodIssueAny { + code: string; + path: (string | number)[]; + message: string; + [key: string]: unknown; +} + +/** + * Create a hint from a Zod issue. + */ +function createHint(issue: z.ZodIssue, root: Record): ValidationError { + // Cast to any to handle Zod v4's complex discriminated union + const issueAny = issue as unknown as ZodIssueAny; + const path = issueAny.path.map(String); + const fieldPath = formatFieldPath(path); + + // Extract context from path + const context = extractContextFromPath(path, root); + + // Determine expected/actual + let expected = 'valid value'; + let actual: string | undefined; + let hint = 'Check the content type schema.'; + let suggestion: string | undefined; + + switch (issueAny.code) { + case 'invalid_type': { + const expectedType = String(issueAny.expected ?? 'unknown'); + const receivedType = String(issueAny.received ?? 'undefined'); + expected = `expected ${expectedType}, got ${receivedType}`; + actual = receivedType; + hint = `Expected ${expectedType} but received ${receivedType}.`; + break; + } + + case 'invalid_value': { + // Zod v4 uses invalid_value instead of invalid_literal + const options = issueAny.options as unknown[] | undefined; + const received = String(issueAny.received ?? ''); + expected = options ? `one of: ${options.join(', ')}` : 'valid value'; + actual = received; + hint = options ? `Invalid value. Expected one of: ${options.join(', ')}.` : 'Invalid value.'; + break; + } + + case 'unrecognized_keys': { + const keys = issueAny.keys as string[] | undefined; + expected = 'valid keys only'; + hint = keys + ? `Unrecognized keys: ${keys.join(', ')}. Remove them or check for typos.` + : 'Unrecognized keys found. Remove them or check for typos.'; + break; + } + + case 'invalid_union': { + expected = 'valid content type'; + hint = + 'Invalid content type. Check that type is one of: ' + KNOWN_CONTENT_TYPE_KEYS.join(', '); + + // Zod v4: check received field for invalid type value + const receivedValue = issueAny.received; + if (typeof receivedValue === 'string') { + const suggested = suggestContentType(receivedValue); + if (suggested) { + suggestion = suggested; + hint = `Unknown content type "${receivedValue}". Did you mean "${suggested}"?`; + } + } + break; + } + + case 'too_small': + case 'too_big': { + // For size constraints + const type = issueAny.type as string | undefined; + hint = + issueAny.code === 'too_small' + ? `Value is too small${type ? ` (expected ${type})` : ''}.` + : `Value is too large${type ? ` (expected ${type})` : ''}.`; + expected = type ?? 'valid size'; + break; + } + + case 'custom': + default: + expected = issueAny.message; + hint = issueAny.message; + } + + // Enhance hint with content-type-specific guidance + if (context.contentType) { + const typeHints = getContentTypeHints(context.contentType); + if (typeHints) { + // Find the specific field that failed + const failedField = path[path.length - 1]; + const fieldHint = typeHints.fields.find((f) => f.field === failedField); + + if (fieldHint) { + expected = fieldHint.expected; + hint = fieldHint.hint; + } else if (failedField && failedField !== 'type') { + // Generic hint for the content type + hint = `${typeHints.description}. Check that all required fields are present.`; + } + } + } + + return { + path, + fieldPath, + contentType: context.contentType, + index: context.index, + expected, + actual, + hint, + suggestion, + }; +} + +/** + * Check for unknown content type keys in content items. + */ +function checkUnknownContentTypes(root: Record): ValidationError[] { + const errors: ValidationError[] = []; + + walkContentItems(root, (item, path) => { + const type = item?.type; + + if (!type) { + errors.push({ + path, + fieldPath: formatFieldPath([...path, 'type']), + expected: 'type field (e.g., main, grid, text_block)', + hint: 'Each content item must have a "type" field specifying the content type.', + }); + return; + } + + if (typeof type !== 'string') { + errors.push({ + path, + fieldPath: formatFieldPath([...path, 'type']), + expected: 'string', + actual: typeof type, + hint: 'The type field must be a string.', + }); + return; + } + + if (!KNOWN_CONTENT_TYPE_KEYS.includes(type as any)) { + const suggested = suggestContentType(type); + errors.push({ + path, + fieldPath: formatFieldPath([...path, 'type']), + contentType: type, + expected: 'valid content type', + actual: type, + hint: suggested + ? `Unknown content type "${type}". Did you mean "${suggested}"?` + : `Unknown content type "${type}". Valid types: ${KNOWN_CONTENT_TYPE_KEYS.join(', ')}.`, + suggestion: suggested ?? undefined, + }); + } + }); + + return errors; +} + +// ============================================================================ +// Public API +// ============================================================================ + +/** + * Validate page content YAML against the schema. + * Returns structured validation result with AI-friendly errors. + */ +export function validatePageContent(yamlContent: unknown): ValidationResult { + // First pass: Zod schema validation + const result = pageContentSchema.safeParse(yamlContent); + + const errors: ValidationError[] = []; + + if (!result.success) { + // Root must be the original content for path navigation + const root = yamlContent as Record; + + // Convert Zod issues to structured errors + for (const issue of result.error.issues) { + errors.push(createHint(issue, root)); + } + } + + // Second pass: check for unknown content types (catches typos before Zod strips them) + if (typeof yamlContent === 'object' && yamlContent !== null) { + const unknownTypeErrors = checkUnknownContentTypes(yamlContent as Record); + errors.push(...unknownTypeErrors); + } + + // Deduplicate errors (Zod and unknown type check may produce duplicates) + const seen = new Set(); + const dedupedErrors = errors.filter((err) => { + const key = `${err.fieldPath}:${err.hint}`; + if (seen.has(key)) return false; + seen.add(key); + return true; + }); + + // Generate summary + const summary = + dedupedErrors.length === 0 + ? 'Valid' + : `${dedupedErrors.length} validation error${dedupedErrors.length > 1 ? 's' : ''} found`; + + return { + valid: dedupedErrors.length === 0, + errors: dedupedErrors, + summary, + }; +} + +/** + * Validate a single content item. + */ +export function validateContentItem(item: unknown): ValidationResult { + const result = contentItemSchema.safeParse(item); + + const errors: ValidationError[] = []; + + if (!result.success) { + const root = item as Record; + for (const issue of result.error.issues) { + errors.push(createHint(issue, root)); + } + } + + return { + valid: errors.length === 0, + errors, + summary: errors.length === 0 ? 'Valid' : `${errors.length} error(s)`, + }; +} + +/** + * Format validation errors for CLI output. + */ +export function formatValidationErrors(errors: ValidationError[]): string { + if (errors.length === 0) return 'āœ“ All pages are valid.'; + + const lines: string[] = []; + + for (const err of errors) { + const location = err.fieldPath; + const suggestion = err.suggestion ? ` (did you mean "${err.suggestion}"?)` : ''; + + lines.push(` āœ— ${location}${suggestion}`); + lines.push(` ${err.hint}`); + if (err.expected) { + lines.push(` Expected: ${err.expected}`); + } + } + + return lines.join('\n'); +} + +/** + * Format validation errors as JSON for agent consumption. + */ +export interface ValidationResultJson { + valid: boolean; + errorCount: number; + errors: Array<{ + path: string[]; + field: string; + contentType?: string; + expected: string; + actual?: string; + hint: string; + suggestion?: string; + }>; +} + +export function toJsonFormat(result: ValidationResult): ValidationResultJson { + return { + valid: result.valid, + errorCount: result.errors.length, + errors: result.errors.map((err) => ({ + path: err.path, + field: err.fieldPath, + contentType: err.contentType, + expected: err.expected, + actual: err.actual, + hint: err.hint, + suggestion: err.suggestion, + })), + }; +} diff --git a/packages/types/test/validation.test.ts b/packages/types/test/validation.test.ts new file mode 100644 index 00000000..b5d973da --- /dev/null +++ b/packages/types/test/validation.test.ts @@ -0,0 +1,168 @@ +import { describe, it, expect } from 'vitest'; +import { validatePageContent, formatValidationErrors } from '../src/types/validation'; +import { suggestContentType } from '../src/types/validation-hints'; + +describe('validatePageContent', () => { + it('passes valid main content', () => { + const valid = { + content: { + content_items: [ + { + type: 'main', + label: 'hero', + heading: { text: 'Hello', textSize: 'h1' }, + textBlocks: [{ text: 'Body', textSize: 'body1' }], + }, + ], + }, + }; + const result = validatePageContent(valid); + expect(result.valid).toBe(true); + expect(result.errors).toHaveLength(0); + }); + + it('fails main missing heading with hint', () => { + const invalid = { + content: { + content_items: [ + { + type: 'main', + label: 'hero', + // missing heading + textBlocks: [{ text: 'Body', textSize: 'body1' }], + }, + ], + }, + }; + const result = validatePageContent(invalid); + expect(result.valid).toBe(false); + expect(result.errors.length).toBeGreaterThan(0); + // Should have a hint + expect(result.errors[0].hint).toBeTruthy(); + }); + + it('catches typo in content type with suggestion', () => { + const invalid = { + content: { + content_items: [ + { + type: 'feture_list', // typo! + label: 'features', + }, + ], + }, + }; + const result = validatePageContent(invalid); + expect(result.valid).toBe(false); + const typoError = result.errors.find((e) => e.suggestion === 'feature_list'); + expect(typoError).toBeDefined(); + expect(typoError?.suggestion).toBe('feature_list'); + }); + + it('validates nested grid column content_items', () => { + const invalid = { + content: { + content_items: [ + { + type: 'grid', + label: 'features', + columns: [ + { + content_items: [ + { type: 'typo_type' }, // invalid nested type + ], + }, + ], + }, + ], + }, + }; + const result = validatePageContent(invalid); + expect(result.valid).toBe(false); + // Should find the nested error + expect(result.errors.some((e) => e.fieldPath.includes('columns'))).toBe(true); + }); + + it('passes valid alert with all required fields', () => { + const valid = { + content: { + content_items: [ + { + type: 'alert', + label: 'notice', + variant: 'info', + body: 'This is a notice', + }, + ], + }, + }; + const result = validatePageContent(valid); + expect(result.valid).toBe(true); + }); + + it('fails alert missing required fields', () => { + const invalid = { + content: { + content_items: [ + { + type: 'alert', + label: 'notice', + // missing variant and body + }, + ], + }, + }; + const result = validatePageContent(invalid); + expect(result.valid).toBe(false); + }); +}); + +describe('suggestContentType', () => { + it('returns null for exact match', () => { + expect(suggestContentType('main')).toBeNull(); + expect(suggestContentType('feature_list')).toBeNull(); + }); + + it('returns null for case-exact match', () => { + expect(suggestContentType('Main')).toBeNull(); + expect(suggestContentType('FEATURE_LIST')).toBeNull(); + }); + + it('suggests feature_list for feture_list typo', () => { + expect(suggestContentType('feture_list')).toBe('feature_list'); + expect(suggestContentType('fetaure_list')).toBe('feature_list'); + }); + + it('suggests text_block for textblock typo', () => { + expect(suggestContentType('textblock')).toBe('text_block'); + expect(suggestContentType('text-block')).toBe('text_block'); + }); + + it('returns null for completely different string', () => { + expect(suggestContentType('xyz123')).toBeNull(); + expect(suggestContentType('foobar')).toBeNull(); + }); +}); + +describe('formatValidationErrors', () => { + it('returns success message for no errors', () => { + const output = formatValidationErrors([]); + expect(output).toBe('āœ“ All pages are valid.'); + }); + + it('formats errors with suggestions', () => { + const errors = [ + { + path: ['content', 'content_items', '0', 'type'], + fieldPath: 'content.content_items.0.type', + expected: 'valid content type', + actual: 'feture_list', + hint: 'Unknown content type "feture_list". Did you mean "feature_list"?', + suggestion: 'feature_list', + }, + ]; + const output = formatValidationErrors(errors); + expect(output).toContain('did you mean "feature_list"'); + expect(output).toContain('Unknown content type'); + }); +}); From 79b6c0cb02b5b7156331fe959e6cb171b50cbfe2 Mon Sep 17 00:00:00 2001 From: Charles Gibson <46542971+perasperaactual@users.noreply.github.com> Date: Mon, 13 Apr 2026 09:02:34 -0400 Subject: [PATCH 08/27] feat(security): add secrets scanning and plugin security guidelines (#345) * feat(security): add secrets scanning and plugin security guidelines (fixes #244, #246) * fix(security): use gitleaks v1 (MIT) and fix workflow configuration * refactor(security): use gitleaks CLI instead of GitHub Action - Replace gitleaks-action with direct CLI invocation - CLI is MIT licensed, no license key required - Exit code 1 = leaks found (fails CI), 0 = clean (passes) - Add Go setup step to install gitleaks v9 * fix(security): use --filter for pnpm audit to avoid workspace conflicts --------- Co-authored-by: Stackwright Bot --- .github/workflows/ci.yml | 13 ++ .github/workflows/security.yml | 101 ++++++++++ .gitignore | 7 + .gitleaks.toml | 99 ++++++++++ CONTRIBUTING.md | 2 + README.md | 4 + docs/PLUGIN_SECURITY.md | 343 +++++++++++++++++++++++++++++++++ 7 files changed, 569 insertions(+) create mode 100644 .github/workflows/security.yml create mode 100644 .gitleaks.toml create mode 100644 docs/PLUGIN_SECURITY.md diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d85b2eac..7461f3d5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -38,9 +38,22 @@ jobs: restore-keys: | ${{ runner.os }}-turbo- + - name: Dependency vulnerability audit + run: pnpm audit --audit-level=high + - run: pnpm turbo:format - run: pnpm turbo:lint + audit: + runs-on: ubuntu-latest + needs: [lint-and-format] + steps: + - uses: actions/checkout@v4 + - uses: ./.github/actions/setup-stackwright + + - name: Dependency vulnerability audit + run: pnpm audit --audit-level=high + test: runs-on: ubuntu-latest needs: [lint-and-format] diff --git a/.github/workflows/security.yml b/.github/workflows/security.yml new file mode 100644 index 00000000..a1a35bba --- /dev/null +++ b/.github/workflows/security.yml @@ -0,0 +1,101 @@ +name: Security Scan + +on: + push: + branches: [main, dev] + paths-ignore: + - '**.md' + - 'docs/**' + - 'examples/stackwright-docs/content/**' + pull_request: + paths-ignore: + - '**.md' + - 'docs/**' + - 'examples/stackwright-docs/content/**' + +permissions: + contents: read + security-events: write + actions: read + +jobs: + secrets-scan: + name: Scan for Secrets (Gitleaks) + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 # Full history for gitleaks + + - name: Setup Go + uses: actions/setup-go@v5 + with: + go-version: '1.21' + + - name: Install Gitleaks + run: go install github.com/gitleaks/gitleaks/v9@latest + + - name: Run Gitleaks + run: gitleaks detect --source . --config .gitleaks.toml --verbose --log-level info + + dependency-audit: + name: Dependency Vulnerability Audit + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Setup pnpm + uses: pnpm/action-setup@v2 + with: + version: 8 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '20' + cache: 'pnpm' + + - name: Install dependencies + run: pnpm install --frozen-lockfile + + - name: Dependency vulnerability audit + run: pnpm audit --audit-level=medium --filter='@stackwright/*' + + sast-scan: + name: Static Analysis (Semgrep) + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Setup pnpm + uses: pnpm/action-setup@v2 + with: + version: 8 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '20' + cache: 'pnpm' + + - name: Install Semgrep + run: pnpm add -D semgrep + + - name: Run Semgrep + run: pnpm exec semgrep --config auto --json --output semgrep-results.json || echo "[]" > semgrep-results.json + + - name: Check and upload Semgrep results + run: | + if [ -f semgrep-results.json ] && [ $(stat -f%z semgrep-results.json 2>/dev/null || stat -c%s semgrep-results.json) -gt 10 ]; then + pnpm exec semgrep --config auto --sarif --output semgrep.sarif || true + fi + + - name: Upload Semgrep SARIF + if: always() && -f semgrep.sarif + uses: github/codeql-action/upload-sarif@v3 + with: + sarif_file: semgrep.sarif + category: semgrep diff --git a/.gitignore b/.gitignore index 3f45bccb..13d429eb 100644 --- a/.gitignore +++ b/.gitignore @@ -15,3 +15,10 @@ examples/*/build-manifest.json examples/*/cyclonedx.json examples/*/spdx.json examples/*/spdx.spdx + +# Gitleaks reports +.gitleaks_reports/ +gitleaks-report.sarif + +# Dependency audit reports +audit-report.json diff --git a/.gitleaks.toml b/.gitleaks.toml new file mode 100644 index 00000000..29edd219 --- /dev/null +++ b/.gitleaks.toml @@ -0,0 +1,99 @@ +# Gitleaks Configuration for Stackwright +# Prevents accidental credential commits in CI/CD + +[allowlist] +description = "Allowed patterns for test fixtures and documentation" +regexes = [ + # Allow env var references (strict: $VAR_NAME format, 2-31 chars) + '''\$[A-Z][A-Z0-9_]{1,30}''', + # Allow test/example credentials in fixtures + '''fake[_-]?(password|secret|key|token)\s*[:=]\s*['"]test['"]''', + '''example[_-]?(token|key|secret)''', + # Allow placeholder patterns in docs + '''your[_-]?(api[_-]?key|token|secret)''', + # Allow generic test patterns + '''test[_-]?fixture''', +] + +paths = [ + # Skip test fixtures directory + '''test/fixtures/.*''', + # Skip generated schemas + '''schemas/.*\.json''', + # Skip generated SBOMs + '''\.stackwright/sbom/.*''', + # Skip coverage reports + '''coverage/.*''', + # Skip node_modules + '''node_modules/.*''', +] + +[[rules]] +description = "AWS Access Key ID" +regex = '''AKIA[0-9A-Z]{16}''' +severity = "critical" + +[[rules]] +description = "AWS Secret Access Key" +regex = '''(?i)aws(.{0,20})?['"][0-9a-zA-Z\/+]{40}['"]''' +severity = "critical" + +[[rules]] +description = "GitHub Personal Access Token" +regex = '''ghp_[a-zA-Z0-9]{36}''' +severity = "critical" + +[[rules]] +description = "GitHub OAuth Access Token" +regex = '''gho_[a-zA-Z0-9]{36}''' +severity = "critical" + +[[rules]] +description = "GitHub App Token" +regex = '''ghu_[a-zA-Z0-9]{36}''' +severity = "critical" + +[[rules]] +description = "GitLab Personal Access Token" +regex = '''glpat-[a-zA-Z0-9\-]{20}''' +severity = "critical" + +[[rules]] +description = "Google API Key" +regex = '''AIza[a-zA-Z0-9_-]{35}''' +severity = "critical" + +[[rules]] +description = "OpenAI API Key" +regex = '''sk-[a-zA-Z0-9]{48}''' +severity = "critical" + +[[rules]] +description = "NPM Token" +regex = '''npm_[a-zA-Z0-9]{36}''' +severity = "high" + +[[rules]] +description = "Private Key (generic)" +regex = '''-----BEGIN (RSA |EC |DSA |OPENSSH )?PRIVATE KEY-----''' +severity = "critical" + +[[rules]] +description = "Generic API Key" +regex = '''(?i)(api[_-]?key|apikey|api[_-]?secret)\s*[:=]\s*['"]?[a-zA-Z0-9]{16,64}['"]?''' +severity = "high" + +[[rules]] +description = "Generic Secret" +regex = '''(?i)(secret|password|passwd|pwd|token|auth)\s*[:=]\s*['"]?[a-zA-Z0-9_\-]{8,64}['"]?''' +severity = "medium" + +[[rules]] +description = "Azure Storage Account Key" +regex = '''(?i)DefaultEndpointsProtocol=https;AccountName=[a-zA-Z0-9]+;AccountKey=[a-zA-Z0-9+/]{86}==''' +severity = "critical" + +[[rules]] +description = "Generic JWT Token" +regex = '''eyJ[a-zA-Z0-9_-]{10,}\.eyJ[a-zA-Z0-9_-]{10,}\.[a-zA-Z0-9_-]{10,}''' +severity = "high" diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index e14d8f17..43b5ce39 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -307,6 +307,8 @@ Pro packages can extend the scaffold process using the hooks system in `@stackwr ### Overview +> āš ļø **Security First:** When creating scaffold hooks, always consider the security implications. See [docs/PLUGIN_SECURITY.md](./docs/PLUGIN_SECURITY.md) for security guidelines, input validation requirements, and common vulnerability patterns to avoid. + The scaffold hooks system allows Pro packages to: - Inject enterprise dependencies into `package.json` - Configure custom MCP servers diff --git a/README.md b/README.md index f83c0416..b5daca7b 100644 --- a/README.md +++ b/README.md @@ -352,6 +352,10 @@ Stackwright constrains the YAML layer and generated clients. Custom React compon > **Why this matters for government and enterprise**: You audit the schema once. Every application built on Stackwright inherits those guarantees. This isn't "we scanned it afterward" — it's mathematical proof that invalid inputs cannot reach the runtime. +### Plugin Security + +For information about securing plugins and extensions, see [docs/PLUGIN_SECURITY.md](./docs/PLUGIN_SECURITY.md). + ## Examples See `examples/hellostackwrightnext/` for a complete working site demonstrating all content types, the theme system, co-located images, and the full prebuild pipeline. diff --git a/docs/PLUGIN_SECURITY.md b/docs/PLUGIN_SECURITY.md new file mode 100644 index 00000000..42a54a91 --- /dev/null +++ b/docs/PLUGIN_SECURITY.md @@ -0,0 +1,343 @@ +# Plugin Security Guide + +*This guide establishes security requirements and best practices for Stackwright plugin developers. For framework security architecture, see [security-model-for-docs.md](./security-model-for-docs.md).* + +--- + +## Trust Model + +Plugins are **user-controlled code** that executes during the prebuild phase with the same filesystem access as the CLI user. Stackwright provides hooks, not sandboxing. + +### Security Contract + +By registering a plugin, you acknowledge: + +- Plugins can read/write any file accessible to the CLI process +- Plugins execute before static content is generated +- Plugin failures during `beforeBuild` hooks **abort the build** +- Plugin failures during `afterBuild` hooks **do not** abort the build (non-critical) +- The framework validates plugin context but not plugin output + +--- + +## Plugin Author Responsibilities + +### 1. Input Validation + +Always validate all `PrebuildPluginContext` fields before use: + +```typescript +// āœ… DO: Validate context fields +const projectRoot = context.projectRoot; +if (!path.isAbsolute(projectRoot)) { + throw new Error('projectRoot must be an absolute path'); +} + +// āœ… DO: Validate user-provided config with Zod schemas +import { z } from 'zod'; +const ConfigSchema = z.object({ + apiUrl: z.string().url(), + timeout: z.number().positive().max(30000).default(5000), +}); +const config = ConfigSchema.parse(context.config); + +// āŒ DON'T: Use .passthrough() without plugin-specific validation +const unsafe = yaml.load(userInput); // Dangerous! +``` + +### 2. Path Safety + +Use canonical path resolution and prefix checks for all file operations: + +```typescript +import path from 'path'; +import fs from 'fs'; + +// āœ… DO: Validate paths stay within projectRoot +function safeWrite(projectRoot: string, relativePath: string, content: string) { + const resolved = path.resolve(projectRoot, relativePath); + if (!resolved.startsWith(projectRoot + path.sep)) { + throw new Error('Path traversal blocked: ' + relativePath); + } + fs.writeFileSync(resolved, content, 'utf8'); +} + +// āŒ DON'T: Arbitrary file write with user input +fs.writeFileSync(path.join(projectRoot, userInput), content); // Dangerous! +``` + +### 3. Command Execution + +Use parameterized execution only: + +```typescript +import { execFile } from 'child_process'; + +// āœ… DO: Parameterized execution +execFile('tar', ['-xzf', tarFile, '--strip-components=1'], { cwd: targetDir }, (err) => { + // Handle error +}); + +// āŒ DON'T: Shell execution with user input +exec(`tar -xzf ${tarFile}`); // Shell injection risk! +``` + +### 4. Error Handling + +Catch and log errors gracefully; don't let unhandled exceptions crash builds: + +```typescript +// āœ… DO: Graceful error handling +try { + await processUserConfig(context); +} catch (error) { + console.error(`[plugin:my-plugin] Config processing failed:`, error); + throw new PluginError('Failed to process config', { cause: error }); +} + +// āŒ DON'T: Swallow errors silently +try { ... } catch {} + +## 5. Secret Management + +- āœ… DO: Accept env var references (`$API_TOKEN`) +- āœ… DO: Read secrets from environment variables +- āŒ DON'T: Log secret values, even in debug mode +- āŒ DON'T: Write secrets to generated code or output files + +```typescript +// āœ… DO: Resolve env var references +function resolveSecret(value: string): string { + if (value.startsWith('$')) { + const envKey = value.slice(1); + const envValue = process.env[envKey]; + if (!envValue) { + throw new Error(`Environment variable ${envKey} is not set`); + } + return envValue; + } + return value; +} + +// āŒ DON'T: Log secrets +console.log('API Token:', apiToken); // Dangerous! +``` + +### 6. Generated Code Safety + +- āœ… DO: Generate TypeScript with strict typing +- āœ… DO: Use Zod schemas for runtime validation +- āŒ DON'T: Generate `eval()`, `Function()`, or inline event handlers +- āŒ DON'T: Interpolate user input into code without escaping + +--- + +## SSRF Prevention + +If your plugin fetches remote resources, implement SSRF protection: + +### URL Validation + +```typescript +function validateUrl(url: string): boolean { + try { + const parsed = new URL(url); + const host = parsed.hostname; + + // Allow localhost for development (127.x.x.x and IPv6 loopback) + if (/^(localhost|127\.\d+\.\d+\.\d+|::1)$/.test(host)) { + return true; + } + + // Block RFC 1918 private ranges + if (/^(10\.|172\.(1[6-9]|2[0-9]|3[0-1])\.|192\.168\.)/.test(host)) { + return false; // RFC 1918 — blocked + } + + // Block cloud metadata endpoints + if (host.endsWith('.metadata.google.internal') || + host === '169.254.169.254' || + host === 'metadata.azure.com') { + return false; // Cloud metadata — blocked + } + + // Enforce HTTPS in production + return parsed.protocol === 'https:'; + } catch { + return false; + } +} +``` + +### Response Size Limits + +```typescript +// āœ… DO: Limit response sizes to prevent memory exhaustion +const MAX_RESPONSE_SIZE = 10 * 1024 * 1024; // 10MB + +async function safeFetch(url: string): Promise { + const response = await fetch(url); + const contentLength = response.headers.get('content-length'); + + if (contentLength && parseInt(contentLength) > MAX_RESPONSE_SIZE) { + throw new Error('Response too large'); + } + + const text = await response.text(); + if (text.length > MAX_RESPONSE_SIZE) { + throw new Error('Response too large'); + } + + return text; +} +``` + +### Rate Limiting Guidance + +Protect against abuse and resource exhaustion with rate limiting: + +```typescript +// āœ… DO: Implement request throttling for external APIs +const rateLimiter = { + maxRequests: 100, + windowMs: 60 * 1000, // 1 minute + requests: new Map(), + + check(identifier: string): boolean { + const now = Date.now(); + const timestamps = this.requests.get(identifier) || []; + const recent = timestamps.filter(t => now - t < this.windowMs); + + if (recent.length >= this.maxRequests) { + return false; // Rate limit exceeded + } + + recent.push(now); + this.requests.set(identifier, recent); + return true; + }, + + getRetryAfter(identifier: string): number { + const timestamps = this.requests.get(identifier) || []; + const now = Date.now(); + const oldest = timestamps.find(t => now - t < this.windowMs); + return oldest ? Math.ceil((this.windowMs - (now - oldest)) / 1000) : 0; + } +}; + +// āœ… DO: Use rate limiter before making API calls +function safeApiCall(url: string, identifier: string): void { + if (!rateLimiter.check(identifier)) { + const retryAfter = rateLimiter.getRetryAfter(identifier); + throw new Error(`Rate limit exceeded. Retry after ${retryAfter}s`); + } + // Proceed with API call +} + +// āœ… DO: Set appropriate timeouts for all network requests +const DEFAULT_TIMEOUT = 10_000; // 10 seconds + +async function fetchWithTimeout(url: string, timeout = DEFAULT_TIMEOUT): Promise { + const controller = new AbortController(); + const timeoutId = setTimeout(() => controller.abort(), timeout); + + try { + return await fetch(url, { signal: controller.signal }); + } finally { + clearTimeout(timeoutId); + } +} +``` + +**Recommended rate limits:** +- External API calls: 60-100 requests/minute per identifier +- File downloads: 10-20 requests/minute per host +- Build-time fetches: 30-second timeout maximum +- Use exponential backoff for retry logic + +--- + +## Security Review Checklist + +Before releasing a plugin, verify: + +### Configuration Validation +- [ ] All config fields validated with Zod schemas +- [ ] Schema uses `.parse()` not `.passthrough()` +- [ ] Invalid config fails build with clear error message +- [ ] TypeScript types match schema validation + +### Secret Handling +- [ ] No hardcoded credentials in source code +- [ ] Secrets read from environment variables +- [ ] No secret values logged (even at debug level) +- [ ] Secrets not written to output files + +### Path Safety +- [ ] All file paths validated with prefix checks +- [ ] Symlinks are rejected or validated +- [ ] No path traversal vulnerabilities (test with `../../../etc/passwd`) +- [ ] Files written only to intended directories + +### Generated Code +- [ ] No `eval()`, `Function()`, or `setTimeout(string)` +- [ ] User input escaped when interpolated into code +- [ ] Generated TypeScript is syntactically valid +- [ ] Tests include malicious input scenarios + +### Network Security +- [ ] URLs validated against allowlist (if applicable) +- [ ] RFC 1918 addresses blocked +- [ ] Cloud metadata endpoints blocked +- [ ] Response sizes limited +- [ ] TLS/HTTPS enforced + +### Testing +- [ ] Unit tests for validation functions +- [ ] Integration tests with malicious inputs +- [ ] Path traversal test cases +- [ ] SSRF prevention test cases + +--- + +## Third-Party Plugin Auditing + +Before using a third-party plugin: + +1. **Review the source code** for filesystem/network access patterns +2. **Check for hardcoded secrets** or credential embedding +3. **Verify error handling** — no unhandled rejections +4. **Test in a sandbox project** before production use +5. **Check for dependencies** with known vulnerabilities +6. **Review maintainer reputation** and release history + +### Red Flags + +🚨 **DO NOT USE** a plugin that: +- Contains obfuscated or minified code you can't review +- Requests excessive permissions (e.g., network access when not needed) +- Has not been updated in over 6 months +- Has unresolved security issues in its dependencies +- Requires you to disable security features + +--- + +## Reporting Plugin Security Issues + +Contact security@per-aspera.dev with: +- Plugin name and version +- Vulnerable code pattern +- Proof-of-concept (if applicable) +- Steps to reproduce + +--- + +## Related Documents + +- [CONTRIBUTING.md](../CONTRIBUTING.md) — General contribution guidelines +- [AGENTS.md](../AGENTS.md) — AI agent guidance +- [security-model-for-docs.md](./security-model-for-docs.md) — Framework security architecture + +--- + +*Last updated: 2026-04-13* From 83ce57d4876e59f4320d18537297d2302ef6dee7 Mon Sep 17 00:00:00 2001 From: Stackwright Bot Date: Mon, 13 Apr 2026 10:28:11 -0400 Subject: [PATCH 09/27] feat(types,build-scripts): add env var resolution for integration secrets (#245) --- packages/build-scripts/src/prebuild.ts | 22 +- .../types/schemas/site-config-schema.json | 51 ++ packages/types/src/types/index.ts | 2 + packages/types/src/types/secret-detection.ts | 45 ++ packages/types/src/types/secrets.ts | 99 ++++ packages/types/src/types/siteConfig.ts | 3 + .../types/test/integration-security.test.ts | 475 +++++++++--------- 7 files changed, 458 insertions(+), 239 deletions(-) create mode 100644 packages/types/src/types/secret-detection.ts create mode 100644 packages/types/src/types/secrets.ts diff --git a/packages/build-scripts/src/prebuild.ts b/packages/build-scripts/src/prebuild.ts index 81a34b3c..6d936fc4 100644 --- a/packages/build-scripts/src/prebuild.ts +++ b/packages/build-scripts/src/prebuild.ts @@ -28,6 +28,7 @@ import { validatePageContent, collectionConfigSchema, VIDEO_EXTENSIONS as VIDEO_EXTENSIONS_ARRAY, + resolveEnvVarsDeep, } from '@stackwright/types'; import type { CollectionConfig, @@ -39,6 +40,14 @@ import type { PrebuildPluginContext, } from '@stackwright/types'; +/** + * Recursively resolve environment variable references in config values. + * Replaces $VAR_NAME with actual env var values at build time. + */ +function applyEnvVarResolution(obj: unknown): unknown { + return resolveEnvVarsDeep(obj); +} + // -- Config ----------------------------------------------------------------- const IMAGE_EXTENSIONS = new Set([ @@ -876,14 +885,19 @@ export async function runPrebuild(options?: string | PrebuildOptions): Promise 0) { fs.writeFileSync( path.join(contentOutDir, '_font-links.json'), @@ -898,7 +912,7 @@ export async function runPrebuild(options?: string | PrebuildOptions): Promise, + siteConfig: configWithEnvResolved as Record, contentOutDir, imagesDir, generatedDir, @@ -980,7 +994,7 @@ export async function runPrebuild(options?: string | PrebuildOptions): Promise, + siteConfig: configWithEnvResolved as Record, contentOutDir, imagesDir, generatedDir, diff --git a/packages/types/schemas/site-config-schema.json b/packages/types/schemas/site-config-schema.json index b53b8f06..6a878709 100644 --- a/packages/types/schemas/site-config-schema.json +++ b/packages/types/schemas/site-config-schema.json @@ -418,6 +418,52 @@ "minLength": 1, "maxLength": 50, "pattern": "^[a-z0-9][a-z0-9-]*[a-z0-9]$" + }, + "auth": { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "bearer", + "apiKey", + "oauth2", + "basic", + "none" + ] + }, + "token": { + "allOf": [ + { + "$ref": "#/definitions/__schema13" + } + ] + }, + "value": { + "allOf": [ + { + "$ref": "#/definitions/__schema13" + } + ] + }, + "apiKeyHeader": { + "type": "string" + }, + "username": { + "type": "string" + }, + "password": { + "allOf": [ + { + "$ref": "#/definitions/__schema13" + } + ] + } + }, + "required": [ + "type" + ], + "additionalProperties": false } }, "required": [ @@ -850,6 +896,11 @@ "button", "overline" ] + }, + "__schema13": { + "type": "string", + "minLength": 1, + "description": "Token value or env var reference ($TOKEN). Use $VAR_NAME format for env var references." } } } diff --git a/packages/types/src/types/index.ts b/packages/types/src/types/index.ts index e6b2f6d4..6b3d6616 100644 --- a/packages/types/src/types/index.ts +++ b/packages/types/src/types/index.ts @@ -8,6 +8,8 @@ export * from './layout'; export * from './enums'; export * from './collection'; export * from './plugin'; +export * from './secrets'; +export * from './secret-detection'; export * from '../constants'; // Re-export validation utilities diff --git a/packages/types/src/types/secret-detection.ts b/packages/types/src/types/secret-detection.ts new file mode 100644 index 00000000..ab72871b --- /dev/null +++ b/packages/types/src/types/secret-detection.ts @@ -0,0 +1,45 @@ +/** + * Estimate entropy of a string (bits per character). + * High entropy suggests random/generated values (real secrets). + * Low entropy suggests human-readable text (potential plaintext). + */ +export function estimateEntropy(str: string): number { + if (!str || str.length === 0) return 0; + + const freq: Record = {}; + for (const char of str) { + freq[char] = (freq[char] || 0) + 1; + } + + let entropy = 0; + for (const count of Object.values(freq)) { + const p = count / str.length; + entropy -= p * Math.log2(p); + } + return entropy; +} + +/** + * Check if a string looks like a plaintext secret (not an env var reference). + * Returns warning message if looks like plaintext, null otherwise. + */ +export function checkForPlaintextSecret(value: string, fieldName: string): string | null { + // Skip if it's an env var reference + if (value.startsWith('$')) return null; + + // Skip very short values + if (value.length < 8) return null; + + const entropy = estimateEntropy(value); + + // High entropy (>4.5 bits/char) = looks like real secret + // Low entropy (<3.5 bits/char) = looks like plaintext + if (entropy < 3.5) { + return ( + `SECURITY WARNING: "${fieldName}" appears to contain plaintext secrets. ` + + `Use environment variable references like $API_TOKEN instead.` + ); + } + + return null; +} diff --git a/packages/types/src/types/secrets.ts b/packages/types/src/types/secrets.ts new file mode 100644 index 00000000..40fe5e14 --- /dev/null +++ b/packages/types/src/types/secrets.ts @@ -0,0 +1,99 @@ +import { z } from 'zod'; + +/** + * Environment variable reference patterns. + * Supports both $VAR_NAME and ${VAR_NAME} formats. + */ +export const ENV_VAR_PATTERN = /^\$[A-Z_][A-Z0-9_]*$/; +export const BRACED_ENV_VAR_PATTERN = /^\$\{[A-Z_][A-Z0-9_]*\}$/; + +/** + * Schema for environment variable references. + * Enforces $VAR_NAME or ${VAR_NAME} pattern. + */ +export const envVarRefSchema = z + .string() + .regex(ENV_VAR_PATTERN, 'Must be env var reference like $API_TOKEN or ${API_TOKEN}') + .or(z.string().regex(BRACED_ENV_VAR_PATTERN)); + +/** + * Schema for authentication token values. + * + * This schema accepts ANY non-empty string for DX flexibility. + * Runtime validation (via checkForPlaintextSecret) warns when + * plaintext secrets are detected. + * + * Ideal flow: Use $ENV_VAR references in YAML, resolve at build time. + */ +export const authTokenSchema = z + .string() + .min(1) + .describe( + 'Token value or env var reference ($TOKEN). ' + 'Use $VAR_NAME format for env var references.' + ); + +/** + * Schema for integration authentication config. + */ +export const integrationAuthSchema = z + .object({ + type: z.enum(['bearer', 'apiKey', 'oauth2', 'basic', 'none']), + token: authTokenSchema.optional(), + value: authTokenSchema.optional(), + apiKeyHeader: z.string().optional(), + username: z.string().optional(), + password: authTokenSchema.optional(), + }) + .optional(); + +/** + * Extract variable name from env var reference. + */ +export function extractEnvVarName(ref: string): string | null { + const match = ref.match(/^\$([A-Z_][A-Z0-9_]*)$/) || ref.match(/^\$\{([A-Z_][A-Z0-9_]*)\}$/); + return match ? match[1] : null; +} + +/** + * Resolve an env var reference to its actual value. + */ +export function resolveEnvVar(ref: string): string { + const varName = extractEnvVarName(ref); + if (!varName) return ref; + + const value = process.env[varName]; + if (value === undefined) { + throw new Error( + `Environment variable ${varName} is not set. ` + `Set it with: export ${varName}=` + ); + } + if (value === '') { + throw new Error( + `Environment variable ${varName} is set but empty. ` + `Please set it to a non-empty value.` + ); + } + return value; +} + +/** + * Recursively resolve all env var references in an object. + */ +export function resolveEnvVarsDeep(obj: unknown): unknown { + if (typeof obj === 'string') { + return resolveEnvVar(obj); + } + if (Array.isArray(obj)) { + return obj.map((item) => resolveEnvVarsDeep(item)); + } + if (obj !== null && typeof obj === 'object') { + const result: Record = {}; + for (const [key, value] of Object.entries(obj as Record)) { + result[key] = resolveEnvVarsDeep(value); + } + return result; + } + return obj; +} + +export type AuthToken = z.infer; +export type IntegrationAuth = z.infer; diff --git a/packages/types/src/types/siteConfig.ts b/packages/types/src/types/siteConfig.ts index 810c41ad..6cd560d1 100644 --- a/packages/types/src/types/siteConfig.ts +++ b/packages/types/src/types/siteConfig.ts @@ -3,6 +3,7 @@ import { navigationItemSchema } from './navigation'; import { buttonContentSchema } from './base'; import { mediaItemSchema } from './media'; import { themeSchema } from '@stackwright/themes'; +import { integrationAuthSchema } from './secrets'; export const appBarConfigSchema = z.object({ titleText: z.string(), @@ -92,6 +93,8 @@ export const integrationConfigSchema = z (name) => !name.includes('..') && !name.startsWith('/') && !name.includes('\\'), 'Integration name cannot contain path traversal sequences' ), + /** Optional authentication configuration for this integration. */ + auth: integrationAuthSchema, }) .passthrough(); diff --git a/packages/types/test/integration-security.test.ts b/packages/types/test/integration-security.test.ts index 83e2c6c9..5132b690 100644 --- a/packages/types/test/integration-security.test.ts +++ b/packages/types/test/integration-security.test.ts @@ -1,239 +1,244 @@ import { describe, it, expect } from 'vitest'; -import { siteConfigSchema } from '../src/types/siteConfig'; - -describe('Integration Security: CRITICAL-01 Path Traversal Protection', () => { - // Base valid config for testing - const baseConfig = { - title: 'Test Site', - navigation: [], - appBar: { - titleText: 'Test', - }, - }; - - describe('path traversal attacks', () => { - it('should reject path traversal with ../', () => { - const config = { - ...baseConfig, - integrations: [{ type: 'openapi', name: '../../../etc/passwd' }], - }; - const result = siteConfigSchema.safeParse(config); - expect(result.success).toBe(false); - if (!result.success) { - const message = result.error.issues[0].message; - expect(message.toLowerCase()).toMatch(/path traversal|kebab-case|alphanumeric/); - } - }); - - it('should reject path traversal with ..', () => { - const config = { - ...baseConfig, - integrations: [{ type: 'openapi', name: 'bad..name' }], - }; - const result = siteConfigSchema.safeParse(config); - expect(result.success).toBe(false); - if (!result.success) { - const message = result.error.issues[0].message; - // Regex catches this first (consecutive dots aren't alphanumeric) - expect(message.toLowerCase()).toMatch(/path traversal|kebab-case/); - } - }); - - it('should reject absolute paths starting with /', () => { - const config = { - ...baseConfig, - integrations: [{ type: 'openapi', name: '/etc/shadow' }], - }; - const result = siteConfigSchema.safeParse(config); - expect(result.success).toBe(false); - if (!result.success) { - const message = result.error.issues[0].message; - expect(message.toLowerCase()).toMatch(/path traversal|kebab-case/); - } - }); - - it('should reject Windows-style paths with backslashes', () => { - const config = { - ...baseConfig, - integrations: [{ type: 'openapi', name: 'bad\\name' }], - }; - const result = siteConfigSchema.safeParse(config); - expect(result.success).toBe(false); - if (!result.success) { - const message = result.error.issues[0].message; - // Regex catches this first (backslash isn't alphanumeric) - expect(message.toLowerCase()).toMatch(/path traversal|kebab-case/); - } - }); - }); - - describe('kebab-case enforcement', () => { - it('should reject names with uppercase letters', () => { - const config = { - ...baseConfig, - integrations: [{ type: 'openapi', name: 'BadName' }], - }; - const result = siteConfigSchema.safeParse(config); - expect(result.success).toBe(false); - if (!result.success) { - const message = result.error.issues[0].message; - expect(message.toLowerCase()).toMatch(/kebab-case|lowercase/); - } - }); - - it('should reject names with underscores', () => { - const config = { - ...baseConfig, - integrations: [{ type: 'openapi', name: 'bad_name' }], - }; - const result = siteConfigSchema.safeParse(config); - expect(result.success).toBe(false); - if (!result.success) { - const message = result.error.issues[0].message; - expect(message.toLowerCase()).toMatch(/kebab-case|alphanumeric/); - } - }); - - it('should reject names with leading hyphens', () => { - const config = { - ...baseConfig, - integrations: [{ type: 'openapi', name: '-bad-name' }], - }; - const result = siteConfigSchema.safeParse(config); - expect(result.success).toBe(false); - if (!result.success) { - const message = result.error.issues[0].message; - expect(message.toLowerCase()).toMatch(/kebab-case|leading/); - } - }); - - it('should reject names with trailing hyphens', () => { - const config = { - ...baseConfig, - integrations: [{ type: 'openapi', name: 'bad-name-' }], - }; - const result = siteConfigSchema.safeParse(config); - expect(result.success).toBe(false); - if (!result.success) { - const message = result.error.issues[0].message; - expect(message.toLowerCase()).toMatch(/kebab-case|trailing/); - } - }); - - it('should reject names with special characters', () => { - const badNames = ['bad@name', 'bad.name', 'bad!name', 'bad#name']; - - badNames.forEach((name) => { - const config = { - ...baseConfig, - integrations: [{ type: 'openapi', name }], - }; - const result = siteConfigSchema.safeParse(config); - expect(result.success).toBe(false); - }); - }); - }); - - describe('length validation', () => { - it('should reject names exceeding 50 characters', () => { - const config = { - ...baseConfig, - integrations: [ - { - type: 'openapi', - name: 'a'.repeat(51), // 51 characters - }, - ], - }; - const result = siteConfigSchema.safeParse(config); - expect(result.success).toBe(false); - if (!result.success) { - const message = result.error.issues[0].message; - expect(message).toMatch(/50/); - } - }); - - it('should reject single character names (need at least 2)', () => { - const config = { - ...baseConfig, - integrations: [{ type: 'openapi', name: 'a' }], - }; - const result = siteConfigSchema.safeParse(config); - expect(result.success).toBe(false); - if (!result.success) { - const message = result.error.issues[0].message; - expect(message.toLowerCase()).toMatch(/kebab-case|alphanumeric/); - } - }); - - it('should accept name at exactly 50 characters', () => { - const config = { - ...baseConfig, - integrations: [ - { - type: 'openapi', - name: 'a'.repeat(50), // Exactly 50 characters - }, - ], - }; - const result = siteConfigSchema.safeParse(config); - expect(result.success).toBe(true); - }); - }); - - describe('valid names (positive tests)', () => { - it('should accept valid kebab-case names', () => { - const validNames = [ - 'api', - 'my-api', - 'logistics-v2', - 'user-management-api', - 'api123', - '123api', - 'a1-b2-c3', - 'api-v1', - 'graphql-api', - ]; - - validNames.forEach((name) => { - const config = { - ...baseConfig, - integrations: [{ type: 'openapi', name }], - }; - const result = siteConfigSchema.safeParse(config); - expect(result.success).toBe(true, `Expected "${name}" to be valid`); - }); - }); - - it('should accept two-character names', () => { - const config = { - ...baseConfig, - integrations: [{ type: 'openapi', name: 'ab' }], - }; - const result = siteConfigSchema.safeParse(config); - expect(result.success).toBe(true); - }); - }); - - describe('combined security scenarios', () => { - it('should reject multiple path traversal techniques combined', () => { - const maliciousNames = [ - '../secrets', - 'api/../etc', - '../../root', - 'api/../../etc/passwd', - '/var/log/app', - '\\windows\\system32', - ]; - - maliciousNames.forEach((name) => { - const config = { - ...baseConfig, - integrations: [{ type: 'openapi', name }], - }; - const result = siteConfigSchema.safeParse(config); - expect(result.success).toBe(false, `Expected "${name}" to be rejected`); - }); +import { + ENV_VAR_PATTERN, + BRACED_ENV_VAR_PATTERN, + envVarRefSchema, + authTokenSchema, + integrationAuthSchema, + extractEnvVarName, + resolveEnvVar, + resolveEnvVarsDeep, +} from '../src/types/secrets.js'; +import { estimateEntropy, checkForPlaintextSecret } from '../src/types/secret-detection.js'; + +describe('Environment Variable Patterns', () => { + describe('ENV_VAR_PATTERN', () => { + it('should match $VAR_NAME format', () => { + expect(ENV_VAR_PATTERN.test('$API_TOKEN')).toBe(true); + expect(ENV_VAR_PATTERN.test('$DATABASE_URL')).toBe(true); + expect(ENV_VAR_PATTERN.test('$MY_SECRET_123')).toBe(true); + }); + + it('should not match invalid patterns', () => { + expect(ENV_VAR_PATTERN.test('$lowercase')).toBe(false); + expect(ENV_VAR_PATTERN.test('$123NUM')).toBe(false); + expect(ENV_VAR_PATTERN.test('API_TOKEN')).toBe(false); + expect(ENV_VAR_PATTERN.test('$')).toBe(false); + }); + }); + + describe('BRACED_ENV_VAR_PATTERN', () => { + it('should match ${VAR_NAME} format', () => { + expect(BRACED_ENV_VAR_PATTERN.test('${API_TOKEN}')).toBe(true); + expect(BRACED_ENV_VAR_PATTERN.test('${DATABASE_URL}')).toBe(true); + }); + + it('should not match invalid patterns', () => { + expect(BRACED_ENV_VAR_PATTERN.test('${lowercase}')).toBe(false); + expect(BRACED_ENV_VAR_PATTERN.test('$API_TOKEN')).toBe(false); + }); + }); +}); + +describe('EnvVarRefSchema', () => { + it('should accept valid $VAR_NAME references', () => { + const result = envVarRefSchema.safeParse('$API_TOKEN'); + expect(result.success).toBe(true); + }); + + it('should accept valid ${VAR_NAME} references', () => { + const result = envVarRefSchema.safeParse('${API_TOKEN}'); + expect(result.success).toBe(true); + }); + + it('should reject invalid patterns', () => { + const result = envVarRefSchema.safeParse('invalid'); + expect(result.success).toBe(false); + }); +}); + +describe('AuthTokenSchema', () => { + it('should accept env var references', () => { + expect(authTokenSchema.safeParse('$API_TOKEN').success).toBe(true); + expect(authTokenSchema.safeParse('${API_TOKEN}').success).toBe(true); + }); + + it('should accept plaintext strings', () => { + expect(authTokenSchema.safeParse('my-secret-value').success).toBe(true); + }); + + it('should reject empty strings', () => { + const result = authTokenSchema.safeParse(''); + expect(result.success).toBe(false); + }); +}); + +describe('IntegrationAuthSchema', () => { + it('should accept valid auth config with bearer token', () => { + const result = integrationAuthSchema.safeParse({ + type: 'bearer', + token: '$API_TOKEN', + }); + expect(result.success).toBe(true); + }); + + it('should accept basic auth with username/password', () => { + const result = integrationAuthSchema.safeParse({ + type: 'basic', + username: 'admin', + password: '$DB_PASSWORD', + }); + expect(result.success).toBe(true); + }); + + it('should accept apiKey auth', () => { + const result = integrationAuthSchema.safeParse({ + type: 'apiKey', + token: '$API_KEY', + apiKeyHeader: 'X-API-Key', + }); + expect(result.success).toBe(true); + }); + + it('should accept none auth type', () => { + const result = integrationAuthSchema.safeParse({ + type: 'none', + }); + expect(result.success).toBe(true); + }); + + it('should allow auth to be undefined', () => { + const result = integrationAuthSchema.safeParse(undefined); + expect(result.success).toBe(true); + }); +}); + +describe('extractEnvVarName', () => { + it('should extract variable name from $VAR format', () => { + expect(extractEnvVarName('$API_TOKEN')).toBe('API_TOKEN'); + expect(extractEnvVarName('$MY_VAR_123')).toBe('MY_VAR_123'); + }); + + it('should extract variable name from ${VAR} format', () => { + expect(extractEnvVarName('${API_TOKEN}')).toBe('API_TOKEN'); + }); + + it('should return null for non-env-var strings', () => { + expect(extractEnvVarName('plaintext')).toBe(null); + expect(extractEnvVarName('')).toBe(null); + }); +}); + +describe('resolveEnvVar', () => { + const originalEnv = process.env; + + beforeEach(() => { + process.env = { ...originalEnv }; + }); + + afterAll(() => { + process.env = originalEnv; + }); + + it('should resolve env var reference to actual value', () => { + process.env.TEST_VAR = 'test-value'; + expect(resolveEnvVar('$TEST_VAR')).toBe('test-value'); + }); + + it('should throw error for missing env var', () => { + delete process.env.MISSING_VAR; + expect(() => resolveEnvVar('$MISSING_VAR')).toThrow('MISSING_VAR is not set'); + }); + + it('should return plaintext strings unchanged', () => { + expect(resolveEnvVar('plaintext')).toBe('plaintext'); + }); +}); + +describe('resolveEnvVarsDeep', () => { + const originalEnv = process.env; + + beforeEach(() => { + process.env = { ...originalEnv, API_TOKEN: 'secret123' }; + }); + + afterAll(() => { + process.env = originalEnv; + }); + + it('should resolve env vars in objects', () => { + const input = { token: '$API_TOKEN', name: 'api' }; + const result = resolveEnvVarsDeep(input); + expect(result).toEqual({ token: 'secret123', name: 'api' }); + }); + + it('should resolve env vars in arrays', () => { + const input = ['$API_TOKEN', 'static-value']; + const result = resolveEnvVarsDeep(input); + expect(result).toEqual(['secret123', 'static-value']); + }); + + it('should handle nested structures', () => { + const input = { + auth: { token: '$API_TOKEN' }, + integrations: [{ name: 'api', key: '$API_TOKEN' }], + }; + const result = resolveEnvVarsDeep(input); + expect(result).toEqual({ + auth: { token: 'secret123' }, + integrations: [{ name: 'api', key: 'secret123' }], + }); + }); + + it('should preserve non-string values', () => { + const input = { count: 42, enabled: true, items: [1, 2, 3] }; + const result = resolveEnvVarsDeep(input); + expect(result).toEqual(input); + }); +}); + +describe('Entropy Detection', () => { + describe('estimateEntropy', () => { + it('should return 0 for empty string', () => { + expect(estimateEntropy('')).toBe(0); + }); + + it('should return low entropy for repetitive text', () => { + const entropy = estimateEntropy('aaaaaaaaaa'); + expect(entropy).toBeLessThan(1); + }); + + it('should return higher entropy for random strings', () => { + // 18 unique chars → log2(18) ā‰ˆ 4.17 bits/char, safely above threshold of 4 + const entropy = estimateEntropy('aK8#mP2$vL5@nQ9!xZ'); + expect(entropy).toBeGreaterThan(4); + }); + + it('should return moderate entropy for normal words', () => { + const entropy = estimateEntropy('password123'); + expect(entropy).toBeGreaterThan(2); + expect(entropy).toBeLessThan(4); + }); + }); + + describe('checkForPlaintextSecret', () => { + it('should return null for env var references', () => { + expect(checkForPlaintextSecret('$API_TOKEN', 'token')).toBe(null); + }); + + it('should return warning for low entropy plaintext', () => { + const result = checkForPlaintextSecret('password123', 'token'); + expect(result).toContain('SECURITY WARNING'); + expect(result).toContain('token'); + }); + + it('should return null for short strings', () => { + expect(checkForPlaintextSecret('short', 'token')).toBe(null); + }); + + it('should return null for high entropy random strings', () => { + const result = checkForPlaintextSecret('aK8#mP2$vL5@nQ9', 'token'); + expect(result).toBe(null); }); }); }); From bbf58ef8b6be11b807da3c603197c1ea75f8c820 Mon Sep 17 00:00:00 2001 From: Stackwright Bot Date: Mon, 13 Apr 2026 10:29:32 -0400 Subject: [PATCH 10/27] chore: add changeset for #245 --- .changeset/feat-env-var-secrets-245.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 .changeset/feat-env-var-secrets-245.md diff --git a/.changeset/feat-env-var-secrets-245.md b/.changeset/feat-env-var-secrets-245.md new file mode 100644 index 00000000..4b833193 --- /dev/null +++ b/.changeset/feat-env-var-secrets-245.md @@ -0,0 +1,13 @@ +--- +"@stackwright/types": minor +"@stackwright/build-scripts": minor +--- + +Add environment variable resolution for integration secrets + +This PR introduces support for referencing secrets from environment variables in integration configurations. Key changes include: + +- New `SecretReference` type for env var secret resolution +- `SecretDetection` utilities for runtime secret validation +- Updated site config schema with integration secret support +- Prebuild script updates for env var substitution From 8fa3dcf8747822523b22ccdcc3e6b6da9ef1b467 Mon Sep 17 00:00:00 2001 From: Charles Gibson <46542971+perasperaactual@users.noreply.github.com> Date: Mon, 13 Apr 2026 13:14:47 -0400 Subject: [PATCH 11/27] feat(types,build-scripts): env var resolution for integration secrets (#245) (#347) * feat(types,build-scripts): add env var resolution for integration secrets (#245) * chore: add changeset for #245 --------- Co-authored-by: Stackwright Bot From 68bdad5f701331f911d6d7903f0d087b4f3f16fe Mon Sep 17 00:00:00 2001 From: Charles Gibson <46542971+perasperaactual@users.noreply.github.com> Date: Mon, 13 Apr 2026 15:00:36 -0400 Subject: [PATCH 12/27] fix(security): add plugin config schema validation (#242) (#349) * fix(security): add configSchema field to PrebuildPlugin interface This commit addresses security issue #242 by adding schema validation for integration configs in the Stackwright prebuild pipeline. Changes: - Add configSchema field to PrebuildPlugin interface in packages/types - Add validateIntegrationConfig() and validateIntegrations() functions to packages/build-scripts/src/prebuild.ts - Integrate validation into the prebuild pipeline after env var resolution - Add comprehensive tests for plugin config schema validation Security benefits: - Prevents prototype pollution attacks (__proto__, constructor) - Validates plugin-specific configuration options - Enforces type safety for integration configs The validation is opt-in for plugins - they declare a configSchema if they want their integration configs validated. Existing plugins without a configSchema continue to work as before (with a warning in development). fixes #242 * fix: add plugin config schema validation (#242) --------- Co-authored-by: Stackwright Bot --- .changeset/fix-plugin-config-schema-242.md | 6 + packages/build-scripts/src/prebuild.ts | 84 +++++++++++ packages/types/src/types/plugin.ts | 26 ++++ .../types/test/integration-security.test.ts | 130 ++++++++++++++++++ 4 files changed, 246 insertions(+) create mode 100644 .changeset/fix-plugin-config-schema-242.md diff --git a/.changeset/fix-plugin-config-schema-242.md b/.changeset/fix-plugin-config-schema-242.md new file mode 100644 index 00000000..88db2c67 --- /dev/null +++ b/.changeset/fix-plugin-config-schema-242.md @@ -0,0 +1,6 @@ +--- +"@stackwright/types": patch +"@stackwright/build-scripts": patch +--- + +Add configSchema field to PrebuildPlugin for plugin config validation diff --git a/packages/build-scripts/src/prebuild.ts b/packages/build-scripts/src/prebuild.ts index 6d936fc4..a79fb0d0 100644 --- a/packages/build-scripts/src/prebuild.ts +++ b/packages/build-scripts/src/prebuild.ts @@ -48,6 +48,81 @@ function applyEnvVarResolution(obj: unknown): unknown { return resolveEnvVarsDeep(obj); } +/** + * Validate an integration config against its plugin's schema (if available). + * + * Security: This function validates integration configs passed through from + * stackwright.yml against the declaring plugin's configSchema. This prevents: + * - Prototype pollution attacks (__proto__, constructor) + * - Plugin-specific malicious options + * - Config without type safety + * + * Throws if validation fails. + */ +function validateIntegrationConfig( + integration: Record, + plugins: PrebuildPlugin[] +): void { + // Integration type is formatted as "integration-{pluginName}" + const integrationType = integration.type as string | undefined; + if (!integrationType) { + // No type specified - let Zod schema handle this + return; + } + + // Look for a plugin that handles this integration type + // Plugin names are like "integration-openapi", "integration-graphql" + const pluginName = `integration-${integrationType}`; + const plugin = plugins.find((p) => p.name === pluginName); + + if (!plugin) { + // No plugin registered for this integration type - allow passthrough for now + // but warn in development + if (process.env.NODE_ENV === 'development') { + console.warn( + ` WARNING: No plugin registered for integration type "${integrationType}". Config will be passed through without validation.` + ); + } + return; + } + + if (!plugin.configSchema) { + // Plugin exists but doesn't declare a config schema + if (process.env.NODE_ENV === 'development') { + console.warn( + ` WARNING: Plugin "${pluginName}" does not declare a configSchema. Config will be passed through without validation.` + ); + } + return; + } + + // Validate the integration config against the plugin's schema + const result = plugin.configSchema.safeParse(integration); + if (!result.success) { + const details = result.error.issues + .map((issue) => ` - ${issue.path.join('.')}: ${issue.message}`) + .join('\n'); + throw new Error( + `Invalid configuration for integration "${integration.name}" (${integration.type}):\n${details}` + ); + } +} + +/** + * Validate all integrations in the site config against their respective plugin schemas. + */ +function validateIntegrations(integrations: unknown, plugins: PrebuildPlugin[]): void { + if (!Array.isArray(integrations)) { + return; + } + + for (const integration of integrations) { + if (integration && typeof integration === 'object') { + validateIntegrationConfig(integration as Record, plugins); + } + } +} + // -- Config ----------------------------------------------------------------- const IMAGE_EXTENSIONS = new Set([ @@ -890,6 +965,15 @@ export async function runPrebuild(options?: string | PrebuildOptions): Promise 0) { + const integrations = (configWithEnvResolved as Record).integrations; + if (Array.isArray(integrations)) { + validateIntegrations(integrations, plugins); + console.log(' āœ“ Validated integration configurations against plugin schemas'); + } + } + fs.writeFileSync( path.join(contentOutDir, '_site.json'), JSON.stringify(configWithEnvResolved, null, 2) diff --git a/packages/types/src/types/plugin.ts b/packages/types/src/types/plugin.ts index 4c8abffb..64f3aeda 100644 --- a/packages/types/src/types/plugin.ts +++ b/packages/types/src/types/plugin.ts @@ -8,6 +8,8 @@ * TypeScript types from OpenAPI specs during prebuild. */ +import { z } from 'zod'; + /** * Plugin context provided to plugin hooks */ @@ -35,6 +37,30 @@ export interface PrebuildPlugin { /** Plugin name (for logging) */ name: string; + /** + * Optional schema for validating integration configs. + * + * When a plugin declares a configSchema, the prebuild pipeline will validate + * any integration configs of type `integration-{plugin.name}` against this schema. + * This prevents: + * - Prototype pollution attacks (__proto__, constructor) + * - Plugin-specific malicious options + * - Config without type safety + * + * @example + * ```typescript + * const myPlugin: PrebuildPlugin = { + * name: 'openapi', + * configSchema: z.object({ + * spec: z.string(), + * timeout: z.number().optional(), + * }), + * beforeBuild: async (ctx) => { /* ... *\/ }, + * }; + * ``` + */ + configSchema?: z.ZodSchema; + /** * Called after site config is loaded but before page/collection processing * diff --git a/packages/types/test/integration-security.test.ts b/packages/types/test/integration-security.test.ts index 5132b690..5cb2b36e 100644 --- a/packages/types/test/integration-security.test.ts +++ b/packages/types/test/integration-security.test.ts @@ -1,4 +1,6 @@ +import { z } from 'zod'; import { describe, it, expect } from 'vitest'; +import type { PrebuildPlugin } from '../src/types/plugin.js'; import { ENV_VAR_PATTERN, BRACED_ENV_VAR_PATTERN, @@ -242,3 +244,131 @@ describe('Entropy Detection', () => { }); }); }); + +describe('Plugin Config Schema Validation', () => { + // Mock integration config schema for testing + const mockIntegrationSchema = z.object({ + spec: z.string(), + timeout: z.number().optional(), + }); + + describe('PrebuildPlugin.configSchema', () => { + it('should allow plugins to declare a configSchema', () => { + const plugin: PrebuildPlugin = { + name: 'integration-openapi', + configSchema: mockIntegrationSchema, + }; + expect(plugin.configSchema).toBeDefined(); + expect(plugin.configSchema?.parse).toBeDefined(); + }); + + it('should validate correct config against schema', () => { + const validConfig = { spec: './openapi.yaml', timeout: 5000 }; + const result = mockIntegrationSchema.safeParse(validConfig); + expect(result.success).toBe(true); + }); + + it('should reject config missing required fields', () => { + const invalidConfig = { timeout: 5000 }; // missing spec + const result = mockIntegrationSchema.safeParse(invalidConfig); + expect(result.success).toBe(false); + }); + + it('should reject config with wrong types', () => { + const invalidConfig = { spec: './openapi.yaml', timeout: 'not-a-number' }; + const result = mockIntegrationSchema.safeParse(invalidConfig); + expect(result.success).toBe(false); + }); + + it('should block prototype pollution attempts', () => { + const maliciousConfig = { + spec: './openapi.yaml', + __proto__: { admin: true }, + constructor: { prototype: {} }, + }; + const result = mockIntegrationSchema.safeParse(maliciousConfig); + // Zod strips unknown keys by default, so this should pass + // but the __proto__ won't be added to the parsed object + expect(result.success).toBe(true); + if (result.success) { + // Use Object.hasOwn to check for literal property (not prototype chain) + expect(Object.hasOwn(result.data, '__proto__')).toBe(false); + expect(Object.hasOwn(result.data, 'constructor')).toBe(false); + } + }); + + it('should allow undefined configSchema (backward compatibility)', () => { + const plugin: PrebuildPlugin = { + name: 'integration-legacy', + beforeBuild: async () => {}, + }; + expect(plugin.configSchema).toBeUndefined(); + }); + }); + + describe('Integration schema security', () => { + it('should strip __proto__ from parsed config', () => { + const schema = z.object({ + name: z.string(), + }); + const input = { + name: 'test', + __proto__: { pollution: true }, + }; + const result = schema.safeParse(input); + expect(result.success).toBe(true); + if (result.success) { + expect(Object.prototype.hasOwnProperty.call(result.data, '__proto__')).toBe(false); + } + }); + + it('should strip constructor from parsed config', () => { + const schema = z.object({ + name: z.string(), + }); + const input = { + name: 'test', + constructor: { prototype: {} }, + }; + const result = schema.safeParse(input); + expect(result.success).toBe(true); + if (result.success) { + expect(Object.prototype.hasOwnProperty.call(result.data, 'constructor')).toBe(false); + } + }); + + it('should strip prototype from parsed config', () => { + const schema = z.object({ + name: z.string(), + }); + const input = { + name: 'test', + prototype: {}, + }; + const result = schema.safeParse(input); + expect(result.success).toBe(true); + if (result.success) { + expect(Object.prototype.hasOwnProperty.call(result.data, 'prototype')).toBe(false); + } + }); + + it('should handle nested malicious keys', () => { + const schema = z.object({ + config: z.object({ + value: z.string(), + }), + }); + const input = { + config: { + value: 'test', + __proto__: { evil: true }, + }, + }; + const result = schema.safeParse(input); + expect(result.success).toBe(true); + if (result.success && result.data.config) { + expect(Object.prototype.hasOwnProperty.call(result.data.config, '__proto__')).toBe(false); + } + }); + }); +}); From 6326d49dc4a16610aed7294d7e3b2132acf959c1 Mon Sep 17 00:00:00 2001 From: Charles Gibson <46542971+perasperaactual@users.noreply.github.com> Date: Tue, 14 Apr 2026 08:29:02 -0400 Subject: [PATCH 13/27] feat(nextjs): add security headers - CSP, HSTS, COOP/CORP/COEP (#243) (#350) Co-authored-by: Stackwright Bot --- .changeset/feat-243-security-headers.md | 5 + docs/CSP-BEST-PRACTICES.md | 555 ++++++++++++ docs/SECURITY_HEADERS.md | 617 +++++++++++++ docs/snippets/CSP-QUICK-REF.js | 85 ++ .../src/config/NextStackwrightConfig.ts | 37 +- packages/nextjs/src/config/security.ts | 250 ++++++ packages/nextjs/src/index.ts | 12 +- packages/nextjs/test/security.test.ts | 837 ++++++++++++++++++ 8 files changed, 2391 insertions(+), 7 deletions(-) create mode 100644 .changeset/feat-243-security-headers.md create mode 100644 docs/CSP-BEST-PRACTICES.md create mode 100644 docs/SECURITY_HEADERS.md create mode 100644 docs/snippets/CSP-QUICK-REF.js create mode 100644 packages/nextjs/src/config/security.ts create mode 100644 packages/nextjs/test/security.test.ts diff --git a/.changeset/feat-243-security-headers.md b/.changeset/feat-243-security-headers.md new file mode 100644 index 00000000..76a47d9a --- /dev/null +++ b/.changeset/feat-243-security-headers.md @@ -0,0 +1,5 @@ +--- +"@stackwright/nextjs": minor +--- + +Add security headers (CSP, HSTS, COOP/CORP/COEP) to Next.js integration with customizable configuration diff --git a/docs/CSP-BEST-PRACTICES.md b/docs/CSP-BEST-PRACTICES.md new file mode 100644 index 00000000..4d336481 --- /dev/null +++ b/docs/CSP-BEST-PRACTICES.md @@ -0,0 +1,555 @@ +# Content Security Policy (CSP) Best Practices for Next.js + +*This guide provides practical, implementable patterns for securing Next.js applications with CSP headers, security headers, and Permissions-Policy directives.* + +--- + +## Table of Contents + +1. [Quick Start: Complete next.config.js Example](#quick-start-complete-nextconfigjs-example) +2. [Recommended CSP Directives](#recommended-csp-directives) +3. [Next.js 14/15 App Router Patterns](#nextjs-1415-app-router-patterns) +4. [Google Fonts Configuration](#google-fonts-configuration) +5. [Permissions-Policy Directives](#permissions-policy-directives) +6. [Common Gotchas & Mistakes](#common-gotchas--mistakes) +7. [Testing Your CSP](#testing-your-csp) +8. [Report-URI & CSP Violation Monitoring](#report-uri--csp-violation-monitoring) + +--- + +## Quick Start: Complete next.config.js Example + +```javascript +// next.config.js +const securityHeaders = [ + { + // Content Security Policy + key: 'Content-Security-Policy', + value: ` + default-src 'self'; + script-src 'self' 'unsafe-inline' 'unsafe-eval'; + style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; + font-src 'self' https://fonts.gstatic.com; + img-src 'self' data: https://images.unsplash.com https://*.unsplash.com; + connect-src 'self' https://api.example.com wss://*.example.com; + frame-src 'none'; + object-src 'none'; + base-uri 'self'; + form-action 'self'; + frame-ancestors 'none'; + upgrade-insecure-requests; + `.replace(/\s{2,}/g, ' ').trim(), + }, + { + // X-Content-Type-Options prevents MIME type sniffing + key: 'X-Content-Type-Options', + value: 'nosniff', + }, + { + // X-Frame-Options prevents clickjacking + key: 'X-Frame-Options', + value: 'DENY', + }, + { + // X-XSS-Protection (legacy browsers) + key: 'X-XSS-Protection', + value: '1; mode=block', + }, + { + // Referrer-Policy controls information in the Referer header + key: 'Referrer-Policy', + value: 'strict-origin-when-cross-origin', + }, + { + // Permissions-Policy restricts browser features + key: 'Permissions-Policy', + value: 'camera=(), microphone=(), geolocation=(), interest-cohort=()', + }, + { + // Strict-Transport-Security enforces HTTPS + key: 'Strict-Transport-Security', + value: 'max-age=31536000; includeSubDomains; preload', + }, +]; + +/** @type {import('next').NextConfig} */ +const nextConfig = { + async headers() { + return [ + { + source: '/(.*)', + headers: securityHeaders, + }, + ]; + }, +}; + +module.exports = nextConfig; +``` + +--- + +## Recommended CSP Directives + +### Core Directives (Required) + +| Directive | Value | Purpose | +|-----------|-------|---------| +| `default-src` | `'self'` | Fallback for all resource types | +| `script-src` | `'self'` | Restrict JavaScript sources | +| `style-src` | `'self' 'unsafe-inline'` | Stylesheets (unsafe-inline often needed for Next.js) | +| `img-src` | `'self' data: https:` | Images (data: for inline images, https: for external) | +| `font-src` | `'self' https://fonts.gstatic.com` | Web fonts | +| `connect-src` | `'self'` | AJAX, WebSocket, fetch sources | +| `frame-src` | `'none'` | Prevent embedding in iframes | +| `object-src` | `'none'` | Disable Flash and plugins | +| `base-uri` | `'self'` | Restrict `` element | +| `form-action` | `'self'` | Restrict form submission targets | +| `frame-ancestors` | `'none'` | Prevent clickjacking | + +### Directive Values Explained + +```javascript +// Common source values +'self' // Same origin only +'none' // Block completely +'unsafe-inline' // Allow inline scripts/styles (āš ļø weakens CSP) +'unsafe-eval' // Allow eval() (āš ļø security risk) +'nonce-abc123' // Allow specific inline script with nonce +data: // Data URLs (base64 images, etc.) +https: // All HTTPS URLs (any domain) +'domain.com' // Specific domain +'*.domain.com' // Any subdomain +``` + +### Script-Src Options for Next.js + +Next.js has specific script loading requirements: + +```javascript +// Conservative (requires work for Next.js) +'script-src': "'self'" + +// Recommended for most Next.js apps +'script-src': "'self' 'unsafe-inline' 'unsafe-eval'" + +// If using inline scripts with nonces (most secure) +// Requires nonce generation in _document.tsx +'script-src': "'self' 'nonce-{NONCE_VALUE}'" +``` + +--- + +## Next.js 14/15 App Router Patterns + +### App Router (app/) + +For Next.js 14+ with App Router, use middleware for headers: + +```typescript +// middleware.ts +import { NextResponse } from 'next/server'; +import type { NextRequest } from 'next/server'; + +const securityHeaders = { + 'Content-Security-Policy': ` + default-src 'self'; + script-src 'self' 'unsafe-inline' 'unsafe-eval'; + style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; + font-src 'self' https://fonts.gstatic.com; + img-src 'self' data: https: blob:; + connect-src 'self' https://api.example.com; + frame-ancestors 'none'; + base-uri 'self'; + form-action 'self'; + `.replace(/\s{2,}/g, ' ').trim(), + 'X-Content-Type-Options': 'nosniff', + 'X-Frame-Options': 'DENY', + 'Referrer-Policy': 'strict-origin-when-cross-origin', + 'Permissions-Policy': 'camera=(), microphone=(), geolocation=()', +}; + +export function middleware(request: NextRequest) { + const response = NextResponse.next(); + + Object.entries(securityHeaders).forEach(([key, value]) => { + response.headers.set(key, value); + }); + + return response; +} + +export const config = { + matcher: [ + // Match all paths except static files and images + '/((?!_next/static|_next/image|favicon.ico|.*\\.(?:svg|png|jpg|jpeg|gif|webp)$).*)', + ], +}; +``` + +### Pages Router (_app.tsx approach) + +```javascript +// next.config.js +module.exports = { + async headers() { + return [ + { + source: '/(.*)', + headers: [ + { + key: 'Content-Security-Policy', + value: "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline';", + }, + ], + }, + ]; + }, +}; +``` + +--- + +## Google Fonts Configuration + +### Using Next.js Font Module (Recommended) + +```javascript +// app/layout.tsx +import { Inter } from 'next/font/google'; + +const inter = Inter({ + subsets: ['latin'], + display: 'swap', + variable: '--font-inter', +}); + +export default function RootLayout({ children }) { + return ( + + {children} + + ); +} +``` + +CSP for Next.js fonts (automatically handled): +```javascript +'font-src': "'self' https://fonts.gstatic.com" +``` + +### Using Google Fonts Link Tag + +If using `` tags for Google Fonts: + +```javascript +// next.config.js +const ContentSecurityPolicy = ` + default-src 'self'; + script-src 'self' 'unsafe-inline' 'unsafe-eval'; + style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; + font-src 'self' https://fonts.gstatic.com; + img-src 'self' data: https:; + connect-src 'self'; + frame-ancestors 'none'; + base-uri 'self'; +`.replace(/\s{2,}/g, ' ').trim(); + +// Add to headers... +``` + +### Preconnect for Performance + +```html + + + +``` + +--- + +## Permissions-Policy Directives + +The `Permissions-Policy` header controls browser APIs and features. + +### Common Directives + +```javascript +// Disable all features by default +'Permissions-Policy': 'fullscreen=(self), microphone=(), camera=(), geolocation=(), payment=()' + +// More granular control +'Permissions-Policy': ` + camera=(self "https://example.com"), + microphone=(self "https://example.com"), + geolocation=(self), + payment=(), + usb=(), + interest-cohort=() +`.replace(/\s{2,}/g, ' ').trim() +``` + +### Directive Reference + +| Directive | Recommended | Notes | +|-----------|-------------|-------| +| `geolocation` | `()` | Disable location tracking | +| `microphone` | `()` | Disable microphone access | +| `camera` | `()` | Disable camera access | +| `payment` | `()` | Disable payment API unless needed | +| `usb` | `()` | Disable USB access | +| `interest-cohort` | `()` | Disable FLoC tracking | +| `accelerometer` | `()` | Disable motion sensors | +| `autoplay` | `()` | Control media autoplay | +| `fullscreen` | `(self)` | Allow only same-origin | +| `web-share` | `()` | Disable Web Share API | + +--- + +## Common Gotchas & Mistakes + +### 1. **Whitespace in CSP Values** + +```javascript +// āŒ WRONG - extra whitespace breaks CSP parsing +value: ` + default-src 'self'; + script-src 'self'; +` + +// āœ… CORRECT - normalize whitespace +value: ` + default-src 'self'; + script-src 'self'; +`.replace(/\s{2,}/g, ' ').trim() +``` + +### 2. **Forgetting upgrade-insecure-requests** + +```javascript +// āœ… Include this to auto-upgrade HTTP to HTTPS +'upgrade-insecure-requests' +``` + +### 3. **Blocking Google Fonts** + +If fonts don't load: +```javascript +'font-src': "'self' https://fonts.gstatic.com" +'style-src': "'self' 'unsafe-inline' https://fonts.googleapis.com" +``` + +### 4. **Blocking Images in Next.js Image Component** + +```javascript +// Next.js Image component needs these +'img-src': "'self' data: blob: https://images.unsplash.com https://*.unsplash.com https://*.maptiler.com" +``` + +### 5. **Blocking WebSocket Connections** + +```javascript +// If using real-time features +'connect-src': "'self' wss://realtime.example.com https://api.example.com" +``` + +### 6. **Report-Only Mode for Testing** + +```javascript +// Use Content-Security-Policy-Report-Only during testing +key: 'Content-Security-Policy-Report-Only', +value: ` + default-src 'self'; + report-uri /api/csp-violations; + report-to csp-group; +`.trim() +``` + +### 7. **nonce-* Not Working with React 18** + +React 18's streaming SSR doesn't fully support nonces yet: +- Stick with `'unsafe-inline'` for now in most cases +- Or use `report-only` mode for monitoring while developing + +--- + +## Testing Your CSP + +### Using Report-Only Mode + +```javascript +// next.config.js +async headers() { + return [ + { + source: '/(.*)', + headers: [ + // Report-only mode - doesn't block, just reports violations + { + key: 'Content-Security-Policy-Report-Only', + value: ` + default-src 'self'; + script-src 'self' 'unsafe-inline'; + report-uri /api/csp-report; + report-to csp-endpoint; + `.replace(/\s{2,}/g, ' ').trim(), + }, + ], + }, + ]; +} +``` + +### CSP Evaluator + +Use Google's [CSP Evaluator](https://csp-evaluator.withgoogle.com/) to check your CSP for weaknesses. + +### Browser DevTools + +1. Open DevTools → Console +2. CSP violations appear as red error messages +3. Network tab shows blocked resources + +### Automated Testing + +```javascript +// test/security-headers.test.ts +describe('Security Headers', () => { + it('should have CSP header', async () => { + const response = await fetch('https://yoursite.com'); + const csp = response.headers.get('Content-Security-Policy'); + expect(csp).toBeTruthy(); + expect(csp).toContain("default-src 'self'"); + }); + + it('should have X-Frame-Options set to DENY', async () => { + const response = await fetch('https://yoursite.com'); + const xfo = response.headers.get('X-Frame-Options'); + expect(xfo).toBe('DENY'); + }); +}); +``` + +--- + +## Report-URI & CSP Violation Monitoring + +### Setting Up Violation Reports + +```javascript +// next.config.js +const ContentSecurityPolicy = ` + default-src 'self'; + script-src 'self' 'unsafe-inline'; + style-src 'self' 'unsafe-inline'; + img-src 'self' data: https:; + report-uri https://your-csp-reporter.example.com/api/csp-report; + report-to csp-group; +`.trim(); +``` + +### Report-To Configuration + +```javascript +// In headers +{ + key: 'Report-To', + value: JSON.stringify({ + group: 'csp-group', + max_age: 10886400, + endpoints: [ + { url: 'https://your-reporter.example.com/csp-reports' } + ], + }), +} +``` + +### Free CSP Monitoring Services + +- **CSP Auditor** (https://csper.io) +- **Report URI** (https://report-uri.com) +- **Safetix** (https://safetix.io) + +--- + +## Stackwright Integration + +Stackwright projects can add CSP headers via `createStackwrightNextConfig()`: + +```javascript +// next.config.js +const { createStackwrightNextConfig } = require('@stackwright/nextjs'); + +const securityHeaders = [ + { + key: 'Content-Security-Policy', + value: ` + default-src 'self'; + script-src 'self' 'unsafe-inline' 'unsafe-eval'; + style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; + font-src 'self' https://fonts.gstatic.com; + img-src 'self' data: https: blob:; + connect-src 'self'; + frame-ancestors 'none'; + base-uri 'self'; + form-action 'self'; + object-src 'none'; + upgrade-insecure-requests; + `.replace(/\s{2,}/g, ' ').trim(), + }, + { + key: 'X-Content-Type-Options', + value: 'nosniff', + }, + { + key: 'X-Frame-Options', + value: 'DENY', + }, + { + key: 'Referrer-Policy', + value: 'strict-origin-when-cross-origin', + }, + { + key: 'Permissions-Policy', + value: 'camera=(), microphone=(), geolocation=(), interest-cohort=()', + }, +]; + +const nextConfig = createStackwrightNextConfig({ + async headers() { + return [ + { + source: '/(.*)', + headers: securityHeaders, + }, + ]; + }, +}); + +module.exports = nextConfig; +``` + +--- + +## Summary Checklist + +- [ ] Add `Content-Security-Policy` header +- [ ] Include `X-Content-Type-Options: nosniff` +- [ ] Add `X-Frame-Options: DENY` +- [ ] Set `Referrer-Policy` +- [ ] Configure `Permissions-Policy` to disable unused features +- [ ] Enable `Strict-Transport-Security` +- [ ] Add `upgrade-insecure-requests` directive +- [ ] Whitelist Google Fonts domains +- [ ] Test with `report-only` mode first +- [ ] Monitor violations before enforcing + +--- + +## References + +- [OWASP Secure Headers Project](https://owasp.org/www-project-secure-headers/) +- [MDN CSP Documentation](https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP) +- [Google CSP Evaluator](https://csp-evaluator.withgoogle.com/) +- [Next.js Security Headers Guide](https://nextjs.org/docs/app/guides/security-headers) +- [Mozilla Observatory](https://observatory.mozilla.org/) diff --git a/docs/SECURITY_HEADERS.md b/docs/SECURITY_HEADERS.md new file mode 100644 index 00000000..fb8676b9 --- /dev/null +++ b/docs/SECURITY_HEADERS.md @@ -0,0 +1,617 @@ +# Security Headers Guide + +*Practical guide to securing your Stackwright applications with HTTP security headers.* + +--- + +## Table of Contents + +1. [Overview](#overview) +2. [Quick Start](#quick-start) +3. [Default Headers](#default-headers) +4. [Customization](#customization) +5. [CSP Directives Explained](#csp-directives-explained) +6. [Migration Guide](#migration-guide) +7. [Troubleshooting](#troubleshooting) +8. [External Resources](#external-resources) + +--- + +## Overview + +Security headers are HTTP response headers that help protect your application against common web vulnerabilities. They're a critical component of **defense-in-depth** — adding layers of protection even when your code is secure. + +### Why Security Headers Matter + +| Threat | Without Headers | With Headers | +|--------|-----------------|--------------| +| XSS Attacks | Browser executes injected scripts | CSP blocks inline scripts | +| Clickjacking | Site can be embedded in iframe | `X-Frame-Options` blocks embedding | +| MIME Sniffing | Browser may execute wrong content type | `X-Content-Type-Options` stops sniffing | +| Man-in-the-Middle | HTTP connections vulnerable | HSTS forces HTTPS | +| Unwanted Tracking | Browser APIs available by default | `Permissions-Policy` disables unused features | + +### OWASP Alignment + +Stackwright's security headers implement recommendations from the [OWASP Secure Headers Project](https://owasp.org/www-project-secure-headers/), which identifies these headers as essential for modern web application security. + +### Defense-in-Depth + +Security headers don't replace secure coding practices — they complement them: + +``` +ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā” +│ Your Application │ +ā”œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¤ +│ Input Validation │ Output Encoding │ SQL Injection │ ← Secure Code +ā”œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¤ +│ CSP │ HSTS │ X-Frame-Options │ Permissions │ ← Security Headers +ā”œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¤ +│ WAF │ Network Firewall │ TLS │ ← Infrastructure +ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜ +``` + +--- + +## Quick Start + +Adding security headers to your Stackwright project takes just two steps: + +### 1. Update `next.config.ts` + +```typescript +// next.config.ts +const { createStackwrightNextConfig, headers } = require('@stackwright/nextjs') + +module.exports = createStackwrightNextConfig() +export { headers } +``` + +That's it! Your application now includes all default security headers. + +### 2. Verify Headers Are Applied + +Start your dev server and check the headers: + +```bash +pnpm dev +``` + +Then in another terminal: + +```bash +curl -I http://localhost:3000 +``` + +You should see headers like: + +``` +Content-Security-Policy: default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; ... +X-Content-Type-Options: nosniff +X-Frame-Options: DENY +X-XSS-Protection: 1; mode=block +Referrer-Policy: strict-origin-when-cross-origin +Permissions-Policy: camera=(), microphone=(), geolocation=(), interest-cohort=() +Strict-Transport-Security: max-age=31536000; includeSubDomains; preload +``` + +--- + +## Default Headers + +Stackwright enables ten security headers by default (including three cross-origin isolation headers): + +| Header | Default Value | Purpose | +|--------|---------------|---------| +| **Content-Security-Policy** | Complex CSP (see below) | Prevents XSS, clickjacking, and data injection | +| **X-Content-Type-Options** | `nosniff` | Prevents MIME type sniffing | +| **X-Frame-Options** | `DENY` | Prevents clickjacking via iframes | +| **X-XSS-Protection** | `1; mode=block` | Legacy XSS filtering (modern browsers prefer CSP) | +| **Referrer-Policy** | `strict-origin-when-cross-origin` | Controls information in the Referer header | +| **Permissions-Policy** | `camera=(), microphone=(), geolocation=(), interest-cohort=()` | Disables unused browser features | +| **Strict-Transport-Security** | `max-age=31536000; includeSubDomains; preload` | Forces HTTPS connections | +| **Cross-Origin-Opener-Policy** | `same-origin-allow-popups` | Controls browsing context group sharing | +| **Cross-Origin-Resource-Policy** | `same-origin` | Controls cross-origin resource loading | +| **Cross-Origin-Embedder-Policy** | `credentialless` | Controls cross-origin resource embedding | + +### Default Content Security Policy + +```javascript +default-src 'self'; +script-src 'self' 'unsafe-inline' 'unsafe-eval'; +style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; +font-src 'self' https://fonts.gstatic.com; +img-src 'self' data: https: blob:; +connect-src 'self'; +frame-ancestors 'none'; +object-src 'none'; +base-uri 'self'; +form-action 'self'; +upgrade-insecure-requests; +``` + +--- + +## Customization + +Stackwright's security headers are fully customizable through the `securityHeaders` option. + +### Basic Customization + +```typescript +// next.config.ts +const { createStackwrightNextConfig, headers } = require('@stackwright/nextjs') + +const nextConfig = createStackwrightNextConfig({ + securityHeaders: { + xFrameOptions: 'SAMEORIGIN', // Allow embedding from same origin + referrerPolicy: 'no-referrer', // Don't send referrer ever + }, +}) + +module.exports = nextConfig +export { headers } +``` + +### Advanced Configuration + +```typescript +// next.config.ts +const { createStackwrightNextConfig, headers } = require('@stackwright/nextjs') + +const nextConfig = createStackwrightNextConfig({ + securityHeaders: { + // Content Security Policy options + contentSecurityPolicy: { + reportUri: 'https://your-csp-reporter.example.com/api/csp-violations', + }, + + // Strict Transport Security + strictTransportSecurity: { + maxAge: 63072000, // 2 years (default: 1 year) + includeSubDomains: true, + preload: true, + }, + + // X-Frame-Options + xFrameOptions: 'SAMEORIGIN', + + // Referrer-Policy + referrerPolicy: 'no-referrer', + + // Permissions-Policy (control browser features) + permissionsPolicy: { + camera: ['https://example.com'], // Allow camera only for example.com + microphone: ['self'], // Allow microphone for same origin + geolocation: ['self'], + interestCohort: [], // Keep disabled + }, + }, +}) + +module.exports = nextConfig +export { headers } +``` + +### Production Configuration + +For production deployments, you may want to adjust security headers for your specific needs: + +```typescript +// next.config.ts +const { createStackwrightNextConfig, headers } = require('@stackwright/nextjs') + +// Production recommended overrides +const nextConfig = createStackwrightNextConfig({ + securityHeaders: { + contentSecurityPolicy: { + // Override for your API domains + customDirectives: { + 'connect-src': "'self' https://api.yoursite.com wss://yoursite.com", + 'script-src': "'self' 'unsafe-inline'", // Remove unsafe-eval in production + }, + reportUri: 'https://csper.io/report/your-site-id', + }, + crossOriginOpenerPolicy: 'same-origin', + crossOriginResourcePolicy: 'same-origin', + crossOriginEmbedderPolicy: 'require-corp', + }, +}) + +module.exports = nextConfig +export { headers } +``` + +**Key production changes:** +- Remove `'unsafe-eval'` from `script-src` (or use nonce-based CSP) +- Add your API domains to `connect-src` +- Enable stricter cross-origin isolation with `same-origin` COOP +- Use `require-corp` for COEP (requires CORS/CORP on all cross-origin resources) + +> āš ļø **Important**: Before deploying, test thoroughly! Cross-origin isolation (`COOP: same-origin` + `COEP: require-corp`) can break third-party integrations that don't support CORS. + + +### Referrer-Policy Reference + +| Value | Behavior | +|-------|----------| +| `no-referrer` | Never send referrer | +| `no-referrer-when-downgrade` | Default, send full URL unless going to HTTP | +| `origin` | Only send origin (scheme + host + port) | +| `origin-when-cross-origin` | Full URL for same origin, origin for cross-origin | +| `same-origin` | Only send when same origin | +| `strict-origin` | Origin only when protocol security level same | +| `strict-origin-when-cross-origin` | Full URL same origin, origin for equal security | +| `unsafe-url` | Always send full URL (āš ļø not recommended) | + +### Permissions-Policy Values + +Each feature can be: +- `()` — Completely disabled +- `(self)` — Enabled only for same-origin +- `('self')` — Same as `self` +- `('https://example.com')` — Enabled for specific origin +- `('self' 'https://example.com')` — Multiple origins + + +### Cross-Origin Isolation Headers + +Stackwright includes three cross-origin isolation headers that enhance security by controlling how your pages interact with cross-origin content. + +#### Cross-Origin-Opener-Policy (COOP) + +Controls whether the document shares its browsing context group with cross-origin documents. + +| Value | Behavior | +|-------|----------| +| `same-origin` | Full isolation — blocks cross-origin windows from accessing this document | +| `same-origin-allow-popups` | **Default** — allows popups but keeps document isolated | +| `unsafe-none` | Allows sharing with cross-origin documents (reduces isolation) | + +**Note**: Setting `same-origin` enables cross-origin isolation, which is required for: +- `SharedArrayBuffer` (threading) +- `performance.measureMemory()` API +- `Navigator.share()` with arbitrary files + +#### Cross-Origin-Resource-Policy (CORP) + +Controls which cross-origin requests can load resources from your pages. + +| Value | Behavior | +|-------|----------| +| `same-origin` | **Default** — only same-origin requests can load resources | +| `same-site` | Same-site requests (including cross-origin subdomains) can load | +| `cross-origin` | Any cross-origin request can load (use only for public resources) | + +#### Cross-Origin-Embedder-Policy (COEP) + +Controls whether the document can load cross-origin resources without explicit permission. + +| Value | Behavior | +|-------|----------| +| `credentialless` | **Default** — cross-origin resources load without credentials, or with explicit permission | +| `require-corp` | Cross-origin resources must have CORS or CORP headers to load | +| `no-cors` | Allows loading cross-origin resources without CORS (reduces security) | + +**Note**: COEP combined with COOP `same-origin` enables **cross-origin isolation**, unlocking advanced browser features. + + + +--- + +## CSP Directives Explained + +The Content Security Policy is the most powerful security header. Here's what each directive does: + +### Source Values + +| Value | Meaning | +|-------|---------| +| `'self'` | Same origin (protocol + host + port) | +| `'none'` | Block everywhere | +| `'unsafe-inline'` | Allow inline scripts/styles (āš ļø weakens CSP) | +| `'unsafe-eval'` | Allow `eval()` and similar (āš ļø security risk) | +| `data:` | Data URLs (base64 images, etc.) | +| `https:` | Any HTTPS URL | +| `'nonce-xxx'` | Allow specific inline script with nonce | +| `'strict-dynamic'` | Trust scripts loaded by already-trusted scripts | + +### Directive Reference + +| Directive | Default in Stackwright | Purpose | +|-----------|------------------------|---------| +| `default-src` | `'self'` | Fallback for unspecified types | +| `script-src` | `'self' 'unsafe-inline' 'unsafe-eval'` | JavaScript sources | +| `style-src` | `'self' 'unsafe-inline' https://fonts.googleapis.com` | Stylesheet sources | +| `font-src` | `'self' https://fonts.gstatic.com` | Font sources | +| `img-src` | `'self' data: https: blob:` | Image sources | +| `connect-src` | `'self'` | XHR, fetch, WebSocket origins | +| `frame-src` | *(inherited from frame-ancestors)* | Iframe sources | +| `frame-ancestors` | `'none'` | Who can embed this page | +| `object-src` | `'none'` | Plugin sources (Flash, Java, etc.) | +| `base-uri` | `'self'` | Valid values for `` element | +| `form-action` | `'self'` | Form submission targets | +| `upgrade-insecure-requests` | enabled | Auto-upgrade HTTP to HTTPS | + +### āš ļø Security Tradeoffs + +> **Note on `unsafe-inline` and `unsafe-eval`**: These directives are included for Next.js compatibility out of the box. For production deployments handling sensitive data, consider: +> - Using Next.js's built-in nonce support for inline scripts +> - Using hash-based CSP allowlists for known scripts +> - See the Nonce-Based CSP example below + +#### Why `unsafe-inline`? + +Next.js requires `'unsafe-inline'` for: +- Component-level styles +- Dynamic styles via CSS-in-JS +- React's internal mechanisms + +#### Why `unsafe-eval`? + +Required for: +- Some older webpack loaders +- Dynamic code execution patterns +- Certain debugging tools + +If you don't use these patterns, you can create a custom CSP without them. + +### Nonce-Based CSP (Advanced) + +For stricter CSP in production, use Next.js's built-in nonce support: + +```typescript +// next.config.ts +const { createStackwrightNextConfig, headers } = require('@stackwright/nextjs') + +const nextConfig = createStackwrightNextConfig({ + security: { + generateNonce: true, + }, +}) + +module.exports = nextConfig +export { headers } +``` + +Then in your root layout: + +```tsx +import { getCspNonce } from 'next/navigation' + +export default function RootLayout({ children }) { + const nonce = getCspNonce() + + return ( + + +