From eaaeb3bf30ef196922caa856a75c8d4ce74b97fa Mon Sep 17 00:00:00 2001 From: Anand Pant Date: Sat, 10 Oct 2026 00:07:20 -0500 Subject: [PATCH 1/7] chore: refresh toolchain and restore reliable maintenance checks --- .github/workflows/cd-publish-release.yml | 4 +- .github/workflows/cd-release-pr.yml | 4 +- .github/workflows/ci.yml | 195 ++----- .github/workflows/codeql.yml | 2 +- .github/workflows/org-required-checks.yml | 50 +- .github/workflows/pr-auto-merge.yml | 2 +- .github/workflows/preview-seed.yml | 14 +- .github/workflows/tui-release.yml | 46 +- README.md | 15 +- apps/tui/bunfig.toml | 1 - apps/tui/package.json | 19 +- apps/tui/src/{index.tsx => index.ts} | 4 +- apps/tui/tsconfig.json | 2 - apps/web/package.json | 28 +- apps/web/src/lib/capability.test.ts | 6 + apps/web/src/lib/capability.ts | 3 +- bun.lock | 505 ++++++------------ mise.toml | 3 + package.json | 33 +- packages/backend/convex/config.test.ts | 86 ++- .../convex/manualInferenceLogic.test.ts | 19 +- .../backend/convex/manualInferenceLogic.ts | 3 +- packages/backend/package.json | 7 +- packages/shopify-cable-source/package.json | 1 + scripts/browser/agent-browser-smoke.sh | 76 ++- scripts/browser/run-local-browser-qa.sh | 25 +- scripts/ci/merge-gate-context.js | 23 + scripts/ci/merge-gate-context.test.ts | 56 ++ vercel.json | 2 +- 29 files changed, 500 insertions(+), 734 deletions(-) delete mode 100644 apps/tui/bunfig.toml rename apps/tui/src/{index.tsx => index.ts} (99%) create mode 100644 mise.toml create mode 100644 scripts/ci/merge-gate-context.js create mode 100644 scripts/ci/merge-gate-context.test.ts diff --git a/.github/workflows/cd-publish-release.yml b/.github/workflows/cd-publish-release.yml index 59b554f..bd69ac1 100644 --- a/.github/workflows/cd-publish-release.yml +++ b/.github/workflows/cd-publish-release.yml @@ -21,10 +21,10 @@ jobs: if: | github.event_name == 'workflow_dispatch' || startsWith(github.event.head_commit.message, 'chore: bump version to ') - runs-on: blacksmith-2vcpu-ubuntu-2404 + runs-on: ubuntu-24.04 steps: - name: Checkout - uses: actions/checkout@v6 + uses: actions/checkout@v7 with: fetch-depth: 1 diff --git a/.github/workflows/cd-release-pr.yml b/.github/workflows/cd-release-pr.yml index d138df7..d7f19b8 100644 --- a/.github/workflows/cd-release-pr.yml +++ b/.github/workflows/cd-release-pr.yml @@ -23,13 +23,13 @@ concurrency: jobs: create-release-pr: - runs-on: blacksmith-2vcpu-ubuntu-2404 + runs-on: ubuntu-24.04 if: | !contains(github.event.head_commit.message, '[skip-release]') && !contains(github.event.head_commit.message, 'chore: bump version') steps: - name: Checkout - uses: actions/checkout@v6 + uses: actions/checkout@v7 with: token: ${{ secrets.GITHUB_TOKEN }} fetch-depth: 0 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f6585fd..427a0bf 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -9,8 +9,7 @@ on: - ".codex/**" - ".opencode/**" push: - branches: - - main + branches: [main] paths-ignore: - "docs/**" - "README.md" @@ -26,187 +25,63 @@ concurrency: cancel-in-progress: true jobs: - stage-1-quality: - name: Stage 1 - Quality - if: github.event_name != 'pull_request' || !startsWith(github.head_ref, 'chore/bump-version-') - runs-on: blacksmith-2vcpu-ubuntu-2404 + checks: + name: checks + runs-on: ubuntu-24.04 + timeout-minutes: 20 env: - PUBLIC_CONVEX_URL: "https://example.convex.cloud" + PUBLIC_CONVEX_URL: "https://placeholder.convex.cloud" steps: - name: Checkout - uses: actions/checkout@v6 - - - name: Setup Bun - uses: oven-sh/setup-bun@v2 - with: - bun-version: 1.3.11 + uses: actions/checkout@v7 - - name: Cache Bun + Turbo - uses: actions/cache@v5 - with: - path: | - ~/.bun/install/cache - .turbo - key: ${{ runner.os }}-bun-turbo-${{ hashFiles('**/bun.lock', '**/bun.lockb', 'package.json', 'apps/**/package.json', 'packages/**/package.json') }} - restore-keys: | - ${{ runner.os }}-bun-turbo- + - name: Setup toolchain + uses: jdx/mise-action@v5 - - name: Install dependencies - run: bun install --frozen-lockfile - - - name: Ultracite check - run: bun x ultracite check - - - name: Core manual inference tests + - name: Verify toolchain run: | - bun test \ - packages/backend/convex/manualInferenceLogic.test.ts \ - apps/web/src/lib/capability.test.ts \ - apps/web/src/lib/mappers.test.ts + node --version + bun --version + test "$(bun --version)" = "$(node -p "require('./package.json').packageManager.split('@')[1]")" - - name: Publish summary - if: always() - run: | - echo "## Stage 1 - Quality" >> "$GITHUB_STEP_SUMMARY" - echo "" >> "$GITHUB_STEP_SUMMARY" - echo "- ultracite check" >> "$GITHUB_STEP_SUMMARY" - echo "- deterministic manual inference + mapping tests" >> "$GITHUB_STEP_SUMMARY" - - stage-2-browser-qa: - name: Stage 2 - Browser QA - needs: stage-1-quality - if: github.event_name != 'pull_request' || !startsWith(github.head_ref, 'chore/bump-version-') - runs-on: blacksmith-2vcpu-ubuntu-2404 - env: - PUBLIC_CONVEX_URL: "https://example.convex.cloud" - steps: - - name: Checkout - uses: actions/checkout@v6 - - - name: Setup Bun - uses: oven-sh/setup-bun@v2 - with: - bun-version: 1.3.11 - - - name: Cache Bun + Turbo - uses: actions/cache@v5 + - name: Cache dependencies and build + uses: actions/cache@v6 with: path: | ~/.bun/install/cache .turbo - key: ${{ runner.os }}-bun-turbo-${{ hashFiles('**/bun.lock', '**/bun.lockb', 'package.json', 'apps/**/package.json', 'packages/**/package.json') }} - restore-keys: | - ${{ runner.os }}-bun-turbo- - - - name: Cache Playwright browsers - uses: actions/cache@v5 - with: - path: ~/.cache/ms-playwright - key: ${{ runner.os }}-playwright-${{ hashFiles('**/bun.lock', '**/bun.lockb', 'package.json', 'apps/**/package.json', 'packages/**/package.json') }} - restore-keys: | - ${{ runner.os }}-playwright- + key: ${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('mise.toml', 'bun.lock') }} - name: Install dependencies run: bun install --frozen-lockfile - - name: Install agent-browser CLI - run: npm install -g agent-browser@latest - - - name: Install Playwright Chromium - run: npx playwright install --with-deps chromium - - - name: Run local browser QA smoke - run: bun run qa:browser - - - name: Upload browser QA artifacts - if: always() - uses: actions/upload-artifact@v7 - with: - name: browser-qa-local-${{ github.run_id }}-${{ github.run_attempt }} - path: artifacts/browser-qa/** - if-no-files-found: error - - - name: Publish summary - if: always() - run: | - echo "## Stage 2 - Browser QA" >> "$GITHUB_STEP_SUMMARY" - echo "" >> "$GITHUB_STEP_SUMMARY" - echo "- local agent-browser smoke flow" >> "$GITHUB_STEP_SUMMARY" - echo "- screenshots uploaded as workflow artifact" >> "$GITHUB_STEP_SUMMARY" - echo "- Browserbase replay runs on the preview-validation workflow" >> "$GITHUB_STEP_SUMMARY" - - stage-3-build: - name: Stage 3 - Build - needs: stage-2-browser-qa - if: github.event_name != 'pull_request' || !startsWith(github.head_ref, 'chore/bump-version-') - runs-on: blacksmith-4vcpu-ubuntu-2404 - env: - PUBLIC_CONVEX_URL: "https://example.convex.cloud" - steps: - - name: Checkout - uses: actions/checkout@v6 - - - name: Setup Bun - uses: oven-sh/setup-bun@v2 - with: - bun-version: 1.3.11 + - name: Lint + run: bun x ultracite check - - name: Cache Bun + Turbo - uses: actions/cache@v5 - with: - path: | - ~/.bun/install/cache - .turbo - key: ${{ runner.os }}-bun-turbo-${{ hashFiles('**/bun.lock', '**/bun.lockb', 'package.json', 'apps/**/package.json', 'packages/**/package.json') }} - restore-keys: | - ${{ runner.os }}-bun-turbo- + - name: Types + run: bun run check-types - - name: Install dependencies - run: bun install --frozen-lockfile + - name: Unit tests + run: bun run test:unit - name: Build run: bun run build - - name: TUI compile smoke test + - name: TUI compile smoke run: | - mkdir -p apps/tui/dist - bun build apps/tui/src/index.tsx --compile --outfile apps/tui/dist/cable-intel-tui + bun run --cwd apps/tui build:bin ./apps/tui/dist/cable-intel-tui --help - - name: Publish summary - if: always() - run: | - echo "## Stage 3 - Build" >> "$GITHUB_STEP_SUMMARY" - echo "" >> "$GITHUB_STEP_SUMMARY" - echo "- workspace build" >> "$GITHUB_STEP_SUMMARY" - echo "- TUI compile smoke test" >> "$GITHUB_STEP_SUMMARY" + - name: Install browser + run: bunx --no-install agent-browser install --with-deps - checks: - name: checks - if: always() - needs: - - stage-1-quality - - stage-2-browser-qa - - stage-3-build - runs-on: blacksmith-2vcpu-ubuntu-2404 - steps: - - name: Skip staged CI for automated release PRs - if: github.event_name == 'pull_request' && startsWith(github.head_ref, 'chore/bump-version-') - run: echo "Automated version-bump PR; staged CI is intentionally skipped." + - name: Browser smoke + run: bun run qa:browser -- --skip-build - - name: Finalize staged CI result - if: github.event_name != 'pull_request' || !startsWith(github.head_ref, 'chore/bump-version-') - run: | - if [ "${{ needs.stage-1-quality.result }}" != "success" ]; then - echo "Stage 1 failed." - exit 1 - fi - if [ "${{ needs.stage-2-browser-qa.result }}" != "success" ]; then - echo "Stage 2 failed." - exit 1 - fi - if [ "${{ needs.stage-3-build.result }}" != "success" ]; then - echo "Stage 3 failed." - exit 1 - fi - echo "All CI stages passed." + - name: Upload browser evidence + if: always() + uses: actions/upload-artifact@v7 + with: + name: browser-qa-local-${{ github.run_id }}-${{ github.run_attempt }} + path: artifacts/browser-qa/** + if-no-files-found: warn diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 8291b9d..343a513 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -23,7 +23,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v6 + uses: actions/checkout@v7 - name: Initialize CodeQL uses: github/codeql-action/init@v4 diff --git a/.github/workflows/org-required-checks.yml b/.github/workflows/org-required-checks.yml index d83eeee..9d42c6a 100644 --- a/.github/workflows/org-required-checks.yml +++ b/.github/workflows/org-required-checks.yml @@ -23,43 +23,25 @@ jobs: github.event_name == 'pull_request' || (github.event_name == 'deployment_status' && github.event.deployment_status.state == 'success') runs-on: ubuntu-latest + timeout-minutes: 25 steps: + - name: Checkout + uses: actions/checkout@v7 + - name: Wait for merge gates uses: actions/github-script@v9 with: script: | const owner = context.repo.owner; const repo = context.repo.repo; - let pr = context.payload.pull_request; - let pull_number; - let headSha; - - if (pr) { - pull_number = pr.number; - headSha = pr.head.sha; - } else { - const deploymentStatus = context.payload.deployment_status; - const deploymentSha = deploymentStatus?.deployment?.sha; - if (!deploymentSha) { - core.info("No pull request or deployment SHA context. Passing check."); - return; - } - - headSha = deploymentSha; - const associatedPrs = await github.rest.repos.listPullRequestsAssociatedWithCommit({ - owner, - repo, - commit_sha: headSha, - }); - - pr = - associatedPrs.data.find((candidate) => candidate.state === "open") ?? null; - if (!pr) { - core.info(`No open pull request associated with SHA ${headSha}. Passing check.`); - return; - } - pull_number = pr.number; + const { resolveMergeGateContext } = await import(`${process.env.GITHUB_WORKSPACE}/scripts/ci/merge-gate-context.js`); + const resolved = await resolveMergeGateContext(context, github); + if (!resolved) { + core.info("No current open PR head associated with this deployment."); + return; } + const { pullRequest: pr, headSha } = resolved; + const pull_number = pr.number; const files = await github.paginate(github.rest.pulls.listFiles, { owner, @@ -119,14 +101,14 @@ jobs: }; while (Date.now() < deadline) { - const [checkRunsResp, statusesResp] = await Promise.all([ - github.rest.checks.listForRef({ + const [checkRuns, statuses] = await Promise.all([ + github.paginate(github.rest.checks.listForRef, { owner, repo, ref: headSha, per_page: 100, }), - github.rest.repos.listCommitStatusesForRef({ + github.paginate(github.rest.repos.listCommitStatusesForRef, { owner, repo, ref: headSha, @@ -135,14 +117,14 @@ jobs: ]); const checkRunsByName = new Map(); - for (const checkRun of checkRunsResp.data.check_runs) { + for (const checkRun of checkRuns) { if (!checkRunsByName.has(checkRun.name)) { checkRunsByName.set(checkRun.name, checkRun); } } const statusesByContext = new Map(); - for (const status of statusesResp.data) { + for (const status of statuses) { if (!statusesByContext.has(status.context)) { statusesByContext.set(status.context, status); } diff --git a/.github/workflows/pr-auto-merge.yml b/.github/workflows/pr-auto-merge.yml index 3d18d7c..996ddf1 100644 --- a/.github/workflows/pr-auto-merge.yml +++ b/.github/workflows/pr-auto-merge.yml @@ -13,7 +13,7 @@ jobs: github.event.pull_request.draft == false && github.event.pull_request.base.ref == github.event.repository.default_branch && github.event.pull_request.head.repo.full_name == github.repository - runs-on: blacksmith-2vcpu-ubuntu-2404 + runs-on: ubuntu-24.04 steps: - name: Enable auto-merge env: diff --git a/.github/workflows/preview-seed.yml b/.github/workflows/preview-seed.yml index 728cbbc..7b0f59d 100644 --- a/.github/workflows/preview-seed.yml +++ b/.github/workflows/preview-seed.yml @@ -20,7 +20,7 @@ jobs: concurrency: group: preview-seed-${{ github.event.deployment.id }} cancel-in-progress: false - runs-on: blacksmith-2vcpu-ubuntu-2404 + runs-on: ubuntu-24.04 env: PREVIEW_URL: ${{ github.event.deployment_status.environment_url || github.event.deployment_status.target_url || github.event.deployment.environment_url || '' }} DEPLOYMENT_ID: ${{ github.event.deployment.id }} @@ -177,13 +177,11 @@ jobs: - name: Checkout if: steps.pr.outputs.skip != 'true' - uses: actions/checkout@v6 + uses: actions/checkout@v7 - - name: Setup Bun + - name: Setup toolchain if: steps.pr.outputs.skip != 'true' - uses: oven-sh/setup-bun@v2 - with: - bun-version: 1.3.11 + uses: jdx/mise-action@v5 - name: Install dependencies if: steps.pr.outputs.skip != 'true' @@ -191,11 +189,11 @@ jobs: - name: Install agent-browser CLI if: steps.pr.outputs.skip != 'true' - run: npm install -g agent-browser@latest + run: bunx --no-install agent-browser --version - name: Install Playwright Chromium if: steps.pr.outputs.skip != 'true' - run: npx playwright install --with-deps chromium + run: bunx --no-install agent-browser install --with-deps - name: Seed preview catalog id: seed diff --git a/.github/workflows/tui-release.yml b/.github/workflows/tui-release.yml index 0b75335..bfb875d 100644 --- a/.github/workflows/tui-release.yml +++ b/.github/workflows/tui-release.yml @@ -23,17 +23,15 @@ jobs: asset_name: cable-intel-tui-macos-arm64 exec_name: cable-intel-tui-macos-arm64 - os: windows-latest - asset_name: blacksmith-4vcpu-windows-2025 - exec_name: blacksmith-4vcpu-windows-2025 + asset_name: cable-intel-tui-windows-x64 + exec_name: cable-intel-tui-windows-x64.exe runs-on: ${{ matrix.os }} steps: - name: Checkout - uses: actions/checkout@v6 + uses: actions/checkout@v7 - - name: Setup Bun - uses: oven-sh/setup-bun@v2 - with: - bun-version: 1.3.11 + - name: Setup toolchain + uses: jdx/mise-action@v5 - name: Install dependencies (unix) if: runner.os != 'Windows' @@ -46,7 +44,7 @@ jobs: run: | set -euo pipefail mkdir -p apps/tui/dist - bun build apps/tui/src/index.tsx --compile --outfile "apps/tui/dist/${{ matrix.exec_name }}" + bun build apps/tui/src/index.ts --compile --outfile "apps/tui/dist/${{ matrix.exec_name }}" "./apps/tui/dist/${{ matrix.exec_name }}" --help shasum -a 256 "apps/tui/dist/${{ matrix.exec_name }}" > "apps/tui/dist/${{ matrix.exec_name }}.sha256" @@ -60,7 +58,7 @@ jobs: shell: pwsh run: | New-Item -ItemType Directory -Force -Path apps/tui/dist | Out-Null - bun build apps/tui/src/index.tsx --compile --outfile "apps/tui/dist/${{ matrix.exec_name }}" + bun build apps/tui/src/index.ts --compile --outfile "apps/tui/dist/${{ matrix.exec_name }}" & "apps/tui/dist/${{ matrix.exec_name }}" --help $hash = (Get-FileHash -Path "apps/tui/dist/${{ matrix.exec_name }}" -Algorithm SHA256).Hash.ToLower() "$hash *${{ matrix.exec_name }}" | Set-Content "apps/tui/dist/${{ matrix.exec_name }}.sha256" @@ -75,47 +73,41 @@ jobs: if-no-files-found: error browser-qa: - runs-on: blacksmith-2vcpu-ubuntu-2404 + runs-on: ubuntu-24.04 env: - PUBLIC_CONVEX_URL: "https://example.convex.cloud" + PUBLIC_CONVEX_URL: "https://placeholder.convex.cloud" BROWSERBASE_API_KEY: ${{ secrets.BROWSERBASE_API_KEY }} BROWSERBASE_PROJECT_ID: ${{ secrets.BROWSERBASE_PROJECT_ID }} RELEASE_QA_TARGET_URL: ${{ vars.RELEASE_QA_TARGET_URL }} steps: - name: Checkout - uses: actions/checkout@v6 + uses: actions/checkout@v7 - - name: Setup Bun - uses: oven-sh/setup-bun@v2 - with: - bun-version: 1.3.11 + - name: Setup toolchain + uses: jdx/mise-action@v5 - name: Cache Bun + Turbo - uses: actions/cache@v5 + uses: actions/cache@v6 with: path: | ~/.bun/install/cache .turbo - key: ${{ runner.os }}-bun-turbo-${{ hashFiles('**/bun.lock', '**/bun.lockb', 'package.json', 'apps/**/package.json', 'packages/**/package.json') }} - restore-keys: | - ${{ runner.os }}-bun-turbo- + key: ${{ runner.os }}-${{ runner.arch }}-bun-turbo-${{ hashFiles('mise.toml', 'bun.lock') }} - name: Cache Playwright browsers - uses: actions/cache@v5 + uses: actions/cache@v6 with: path: ~/.cache/ms-playwright - key: ${{ runner.os }}-playwright-${{ hashFiles('**/bun.lock', '**/bun.lockb', 'package.json', 'apps/**/package.json', 'packages/**/package.json') }} - restore-keys: | - ${{ runner.os }}-playwright- + key: ${{ runner.os }}-${{ runner.arch }}-playwright-${{ hashFiles('mise.toml', 'bun.lock') }} - name: Install dependencies run: bun install --frozen-lockfile - name: Install agent-browser CLI - run: npm install -g agent-browser@latest + run: bunx --no-install agent-browser --version - name: Install Playwright Chromium (local provider) - run: npx playwright install --with-deps chromium + run: bunx --no-install agent-browser install --with-deps - name: Run browser QA smoke run: | @@ -148,7 +140,7 @@ jobs: needs: - build-binaries - browser-qa - runs-on: blacksmith-2vcpu-ubuntu-2404 + runs-on: ubuntu-24.04 steps: - name: Download artifacts uses: actions/download-artifact@v8 diff --git a/README.md b/README.md index fd1654d..aac50c7 100644 --- a/README.md +++ b/README.md @@ -28,21 +28,28 @@ Cable Intel helps you identify cables quickly, label them consistently, and avoi ## Quick Start ```bash -bun install +mise install +mise exec -- bun install bun run dev:setup bun run dev ``` -Web app: `http://localhost:5173` +Web app: `http://localhost:5173`. `mise.toml` pins Node 24.21.0 and Bun 1.4.2 for local work and CI; Vercel uses the matching Bun `packageManager` pin and Node 24 engine. ## Testing And CI | Layer | Present | Tooling | Runs in CI | |---|---|---|---| -| unit | yes | `bun test` (core suites) | yes | +| unit | yes | `bun run test:unit` | yes | | integration | yes | `bun test` integration suites | no | | e2e api | yes | `bun test packages/backend/convex/ingest.e2e.test.ts` | no | -| e2e web | yes | `agent-browser` smoke scripts | yes | +| e2e web | yes | pinned `agent-browser` smoke scripts | local fields in CI; inference/reset on preview | + +`bun run check-types` checks the web, backend, source package, and TUI. For local +checks without a backend, set `PUBLIC_CONVEX_URL=https://placeholder.convex.cloud`. +The local browser check deliberately disables Convex and does not claim inference +or reset coverage. Live source/provider integration tests remain separate and need +network access and the documented provider credentials. Primary CI workflows: - `.github/workflows/ci.yml` (quality + local browser smoke + build) diff --git a/apps/tui/bunfig.toml b/apps/tui/bunfig.toml deleted file mode 100644 index 7693482..0000000 --- a/apps/tui/bunfig.toml +++ /dev/null @@ -1 +0,0 @@ -preload = ["@opentui/solid/preload"] diff --git a/apps/tui/package.json b/apps/tui/package.json index 9d31ac7..f96ac58 100644 --- a/apps/tui/package.json +++ b/apps/tui/package.json @@ -2,22 +2,15 @@ "name": "tui", "private": true, "type": "module", - "module": "src/index.tsx", + "module": "src/index.ts", "scripts": { - "start": "bun src/index.tsx", - "dev": "bun --watch src/index.tsx", - "build:bin": "bun build src/index.tsx --compile --outfile dist/cable-intel-tui" - }, - "dependencies": { - "@babel/core": "^7.29.0", - "@babel/preset-typescript": "^7.28.5", - "@opentui/core": "^0.3.0", - "@opentui/solid": "^0.3.0", - "babel-preset-solid": "1.9.12", - "solid-js": "1.9.13" + "build:bin": "bun build src/index.ts --compile --outfile dist/cable-intel-tui", + "check-types": "tsc --noEmit", + "dev": "bun --watch src/index.ts", + "start": "bun src/index.ts" }, "devDependencies": { - "@types/bun": "latest" + "@types/bun": "1.4.2" }, "peerDependencies": { "typescript": "^5" diff --git a/apps/tui/src/index.tsx b/apps/tui/src/index.ts similarity index 99% rename from apps/tui/src/index.tsx rename to apps/tui/src/index.ts index ba702f0..74e6e83 100644 --- a/apps/tui/src/index.tsx +++ b/apps/tui/src/index.ts @@ -50,7 +50,7 @@ const printUsage = (): void => { print("Cable Intel ingest TUI"); print(""); print("Usage:"); - print(" bun run src/index.tsx [options]"); + print(" bun run src/index.ts [options]"); print(""); print("Options:"); print(" --vercel-url Resolve deployment from Vercel URL"); @@ -310,7 +310,7 @@ function parseTrailingJson(text: string): T | null { for (let cursor = startIndexes.length - 1; cursor >= 0; cursor -= 1) { const start = startIndexes[cursor]; - if (start < 0) { + if (start === undefined || start < 0) { continue; } const candidate = findBalancedJsonSlice(text, start); diff --git a/apps/tui/tsconfig.json b/apps/tui/tsconfig.json index d472d4a..d4467c8 100644 --- a/apps/tui/tsconfig.json +++ b/apps/tui/tsconfig.json @@ -5,8 +5,6 @@ "target": "ESNext", "module": "Preserve", "moduleDetection": "force", - "jsx": "preserve", - "jsxImportSource": "@opentui/solid", "allowJs": true, // Bundler mode diff --git a/apps/web/package.json b/apps/web/package.json index 141dc97..3e8bcd8 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -4,34 +4,34 @@ "private": true, "type": "module", "scripts": { - "dev": "vite dev", "build": "vite build", "build:vercel": "cd ../.. && bun run build:vercel", - "preview": "vite preview", - "prepare": "svelte-kit sync || echo ''", - "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json", - "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch" + "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch", + "check-types": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json", + "dev": "vite dev", + "prepare": "svelte-kit sync", + "preview": "vite preview" }, "dependencies": { "@cable-intel/backend": "workspace:*", "@cable-intel/env": "workspace:*", + "@tabler/icons-svelte": "^3.49.0", "@vercel/analytics": "^2.0.1", - "@tabler/icons-svelte": "^3.40.0", "convex": "catalog:", - "convex-svelte": "^0.0.12", + "convex-svelte": "^0.14.0", "dotenv": "catalog:", "zod": "catalog:" }, "devDependencies": { "@cable-intel/config": "workspace:*", "@sveltejs/adapter-auto": "^7.0.1", - "@sveltejs/kit": "2.61.1", - "@sveltejs/vite-plugin-svelte": "7.1.2", - "@tailwindcss/vite": "^4.2.2", - "svelte": "5.55.10", - "svelte-check": "4.4.8", - "tailwindcss": "^4.2.2", + "@sveltejs/kit": "2.70.3", + "@sveltejs/vite-plugin-svelte": "7.3.1", + "@tailwindcss/vite": "^4.3.3", + "svelte": "5.57.2", + "svelte-check": "4.7.6", + "tailwindcss": "^4.3.3", "typescript": "catalog:", - "vite": "8.0.14" + "vite": "8.3.4" } } diff --git a/apps/web/src/lib/capability.test.ts b/apps/web/src/lib/capability.test.ts index e5925e0..5cb56a1 100644 --- a/apps/web/src/lib/capability.test.ts +++ b/apps/web/src/lib/capability.test.ts @@ -13,6 +13,12 @@ describe("parsePositiveNumber", () => { }); describe("inferMaxGbpsFromGeneration", () => { + it("does not promote USB 3.2 Gen 1 to the Gen 2 data rate", () => { + expect(inferMaxGbpsFromGeneration("USB 3.2 Gen 1")).toBe(5); + expect(inferMaxGbpsFromGeneration("USB 3.2 Gen 2")).toBe(10); + expect(inferMaxGbpsFromGeneration("USB 3.2 Gen 2x2")).toBe(20); + }); + it("returns the highest inferred class for combined generation markings", () => { expect(inferMaxGbpsFromGeneration("USB 3.2 Gen 2 / USB4 / TB4")).toBe(40); }); diff --git a/apps/web/src/lib/capability.ts b/apps/web/src/lib/capability.ts index 79a0cb8..6e875fa 100644 --- a/apps/web/src/lib/capability.ts +++ b/apps/web/src/lib/capability.ts @@ -29,7 +29,8 @@ const GENERATION_GBPS_HINTS: Array<{ gbps: number; regex: RegExp }> = [ { gbps: 20, regex: /\b(?:usb\s*)?3\.2\s*gen\s*2x2\b/i }, { gbps: 10, - regex: /\b(?:usb\s*(?:3\.2\s*gen\s*2|3\.1\s*gen\s*2)|(?:usb\s*)?3\.2)\b/i, + regex: + /\b(?:usb\s*(?:3\.2\s*gen\s*2|3\.1\s*gen\s*2)|(?:usb\s*)?3\.2(?!\s*gen\b))\b/i, }, { gbps: 5, diff --git a/bun.lock b/bun.lock index e59722a..adca065 100644 --- a/bun.lock +++ b/bun.lock @@ -12,23 +12,16 @@ "devDependencies": { "@biomejs/biome": "2.4.8", "@cable-intel/config": "workspace:*", - "turbo": "^2.8.20", + "agent-browser": "0.38.2", + "turbo": "^2.11.7", "typescript": "catalog:", "ultracite": "7.3.2", }, }, "apps/tui": { "name": "tui", - "dependencies": { - "@babel/core": "^7.29.0", - "@babel/preset-typescript": "^7.28.5", - "@opentui/core": "^0.3.0", - "@opentui/solid": "^0.3.0", - "babel-preset-solid": "1.9.12", - "solid-js": "1.9.13", - }, "devDependencies": { - "@types/bun": "latest", + "@types/bun": "1.4.2", }, "peerDependencies": { "typescript": "^5", @@ -40,24 +33,24 @@ "dependencies": { "@cable-intel/backend": "workspace:*", "@cable-intel/env": "workspace:*", - "@tabler/icons-svelte": "^3.40.0", + "@tabler/icons-svelte": "^3.49.0", "@vercel/analytics": "^2.0.1", "convex": "catalog:", - "convex-svelte": "^0.0.12", + "convex-svelte": "^0.14.0", "dotenv": "catalog:", "zod": "catalog:", }, "devDependencies": { "@cable-intel/config": "workspace:*", "@sveltejs/adapter-auto": "^7.0.1", - "@sveltejs/kit": "2.61.1", - "@sveltejs/vite-plugin-svelte": "7.1.2", - "@tailwindcss/vite": "^4.2.2", - "svelte": "5.55.10", - "svelte-check": "4.4.8", - "tailwindcss": "^4.2.2", + "@sveltejs/kit": "2.70.3", + "@sveltejs/vite-plugin-svelte": "7.3.1", + "@tailwindcss/vite": "^4.3.3", + "svelte": "5.57.2", + "svelte-check": "4.7.6", + "tailwindcss": "^4.3.3", "typescript": "catalog:", - "vite": "8.0.14", + "vite": "8.3.4", }, }, "packages/backend": { @@ -65,15 +58,15 @@ "version": "1.0.0", "dependencies": { "@cable-intel/shopify-cable-source": "workspace:*", - "ai": "^6.0.134", + "ai": "^6.0.301", "convex": "catalog:", "dotenv": "catalog:", "zod": "catalog:", }, "devDependencies": { "@cable-intel/config": "workspace:*", - "@types/node": "^25.5.0", - "convex-test": "^0.0.53", + "@types/node": "^24.19.1", + "convex-test": "^0.0.60", "typescript": "catalog:", }, }, @@ -99,80 +92,26 @@ "version": "0.0.0", "devDependencies": { "@cable-intel/config": "workspace:*", + "@types/node": "^24.19.1", "typescript": "catalog:", }, }, }, + "overrides": { + "cookie": "^0.7.2", + }, "catalog": { - "convex": "^1.34.0", - "dotenv": "^17.3.1", + "convex": "^1.46.0", + "dotenv": "^17.4.2", "typescript": "^5.9.3", - "zod": "^4.3.6", + "zod": "^4.6.5", }, "packages": { - "@ai-sdk/gateway": ["@ai-sdk/gateway@3.0.77", "", { "dependencies": { "@ai-sdk/provider": "3.0.8", "@ai-sdk/provider-utils": "4.0.21", "@vercel/oidc": "3.1.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-UdwIG2H2YMuntJQ5L+EmED5XiwnlvDT3HOmKfVFxR4Nq/RSLFA/HcchhwfNXHZ5UJjyuL2VO0huLbWSZ9ijemQ=="], - - "@ai-sdk/provider": ["@ai-sdk/provider@3.0.8", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-oGMAgGoQdBXbZqNG0Ze56CHjDZ1IDYOwGYxYjO5KLSlz5HiNQ9udIXsPZ61VWaHGZ5XW/jyjmr6t2xz2jGVwbQ=="], - - "@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@4.0.21", "", { "dependencies": { "@ai-sdk/provider": "3.0.8", "@standard-schema/spec": "^1.1.0", "eventsource-parser": "^3.0.6" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-MtFUYI1/8mgDvRmaBDjbLJPFFrMG777AvSgyIFQtZHIMzm88R/12vYBBpnk7pfiWLFE1DSZzY4WDYzGbKAcmiw=="], - - "@ampproject/remapping": ["@ampproject/remapping@2.3.0", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw=="], - - "@babel/code-frame": ["@babel/code-frame@7.29.0", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.28.5", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw=="], - - "@babel/compat-data": ["@babel/compat-data@7.29.0", "", {}, "sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg=="], - - "@babel/core": ["@babel/core@7.29.0", "", { "dependencies": { "@babel/code-frame": "^7.29.0", "@babel/generator": "^7.29.0", "@babel/helper-compilation-targets": "^7.28.6", "@babel/helper-module-transforms": "^7.28.6", "@babel/helpers": "^7.28.6", "@babel/parser": "^7.29.0", "@babel/template": "^7.28.6", "@babel/traverse": "^7.29.0", "@babel/types": "^7.29.0", "@jridgewell/remapping": "^2.3.5", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", "json5": "^2.2.3", "semver": "^6.3.1" } }, "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA=="], - - "@babel/generator": ["@babel/generator@7.29.1", "", { "dependencies": { "@babel/parser": "^7.29.0", "@babel/types": "^7.29.0", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" } }, "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw=="], - - "@babel/helper-annotate-as-pure": ["@babel/helper-annotate-as-pure@7.27.3", "", { "dependencies": { "@babel/types": "^7.27.3" } }, "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg=="], - - "@babel/helper-compilation-targets": ["@babel/helper-compilation-targets@7.28.6", "", { "dependencies": { "@babel/compat-data": "^7.28.6", "@babel/helper-validator-option": "^7.27.1", "browserslist": "^4.24.0", "lru-cache": "^5.1.1", "semver": "^6.3.1" } }, "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA=="], - - "@babel/helper-create-class-features-plugin": ["@babel/helper-create-class-features-plugin@7.28.6", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.3", "@babel/helper-member-expression-to-functions": "^7.28.5", "@babel/helper-optimise-call-expression": "^7.27.1", "@babel/helper-replace-supers": "^7.28.6", "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", "@babel/traverse": "^7.28.6", "semver": "^6.3.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-dTOdvsjnG3xNT9Y0AUg1wAl38y+4Rl4sf9caSQZOXdNqVn+H+HbbJ4IyyHaIqNR6SW9oJpA/RuRjsjCw2IdIow=="], - - "@babel/helper-globals": ["@babel/helper-globals@7.28.0", "", {}, "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw=="], - - "@babel/helper-member-expression-to-functions": ["@babel/helper-member-expression-to-functions@7.28.5", "", { "dependencies": { "@babel/traverse": "^7.28.5", "@babel/types": "^7.28.5" } }, "sha512-cwM7SBRZcPCLgl8a7cY0soT1SptSzAlMH39vwiRpOQkJlh53r5hdHwLSCZpQdVLT39sZt+CRpNwYG4Y2v77atg=="], - - "@babel/helper-module-imports": ["@babel/helper-module-imports@7.28.6", "", { "dependencies": { "@babel/traverse": "^7.28.6", "@babel/types": "^7.28.6" } }, "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw=="], - - "@babel/helper-module-transforms": ["@babel/helper-module-transforms@7.28.6", "", { "dependencies": { "@babel/helper-module-imports": "^7.28.6", "@babel/helper-validator-identifier": "^7.28.5", "@babel/traverse": "^7.28.6" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA=="], - - "@babel/helper-optimise-call-expression": ["@babel/helper-optimise-call-expression@7.27.1", "", { "dependencies": { "@babel/types": "^7.27.1" } }, "sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw=="], - - "@babel/helper-plugin-utils": ["@babel/helper-plugin-utils@7.28.6", "", {}, "sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug=="], - - "@babel/helper-replace-supers": ["@babel/helper-replace-supers@7.28.6", "", { "dependencies": { "@babel/helper-member-expression-to-functions": "^7.28.5", "@babel/helper-optimise-call-expression": "^7.27.1", "@babel/traverse": "^7.28.6" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-mq8e+laIk94/yFec3DxSjCRD2Z0TAjhVbEJY3UQrlwVo15Lmt7C2wAUbK4bjnTs4APkwsYLTahXRraQXhb1WCg=="], - - "@babel/helper-skip-transparent-expression-wrappers": ["@babel/helper-skip-transparent-expression-wrappers@7.27.1", "", { "dependencies": { "@babel/traverse": "^7.27.1", "@babel/types": "^7.27.1" } }, "sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg=="], - - "@babel/helper-string-parser": ["@babel/helper-string-parser@7.27.1", "", {}, "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA=="], - - "@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.28.5", "", {}, "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q=="], - - "@babel/helper-validator-option": ["@babel/helper-validator-option@7.27.1", "", {}, "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg=="], - - "@babel/helpers": ["@babel/helpers@7.28.6", "", { "dependencies": { "@babel/template": "^7.28.6", "@babel/types": "^7.28.6" } }, "sha512-xOBvwq86HHdB7WUDTfKfT/Vuxh7gElQ+Sfti2Cy6yIWNW05P8iUslOVcZ4/sKbE+/jQaukQAdz/gf3724kYdqw=="], - - "@babel/parser": ["@babel/parser@7.29.0", "", { "dependencies": { "@babel/types": "^7.29.0" }, "bin": "./bin/babel-parser.js" }, "sha512-IyDgFV5GeDUVX4YdF/3CPULtVGSXXMLh1xVIgdCgxApktqnQV0r7/8Nqthg+8YLGaAtdyIlo2qIdZrbCv4+7ww=="], + "@ai-sdk/gateway": ["@ai-sdk/gateway@3.0.212", "", { "dependencies": { "@ai-sdk/provider": "3.0.18", "@ai-sdk/provider-utils": "4.0.57", "@vercel/oidc": "3.2.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-iTJSim/6LuSxiG3RF0FWU7bEQ8dF6EplcWci5Km4Vc8SUcR8vxFuC7T/pfCutW00pI/3WYsHm8RCQ5141/Xykw=="], - "@babel/plugin-syntax-jsx": ["@babel/plugin-syntax-jsx@7.28.6", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.28.6" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-wgEmr06G6sIpqr8YDwA2dSRTE3bJ+V0IfpzfSY3Lfgd7YWOaAdlykvJi13ZKBt8cZHfgH1IXN+CL656W3uUa4w=="], + "@ai-sdk/provider": ["@ai-sdk/provider@3.0.18", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-IpefF5ssZVOZCD4Wu7zxt6PwZoGT2HLVjxpJ10Bjx6EAmRfLYJPjyrfxb0RYDlwvXnDb9nkQ7Do/oJVDuRFzew=="], - "@babel/plugin-syntax-typescript": ["@babel/plugin-syntax-typescript@7.28.6", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.28.6" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-+nDNmQye7nlnuuHDboPbGm00Vqg3oO8niRRL27/4LYHUsHYh0zJ1xWOz0uRwNFmM1Avzk8wZbc6rdiYhomzv/A=="], - - "@babel/plugin-transform-modules-commonjs": ["@babel/plugin-transform-modules-commonjs@7.28.6", "", { "dependencies": { "@babel/helper-module-transforms": "^7.28.6", "@babel/helper-plugin-utils": "^7.28.6" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-jppVbf8IV9iWWwWTQIxJMAJCWBuuKx71475wHwYytrRGQ2CWiDvYlADQno3tcYpS/T2UUWFQp3nVtYfK/YBQrA=="], - - "@babel/plugin-transform-typescript": ["@babel/plugin-transform-typescript@7.28.6", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.3", "@babel/helper-create-class-features-plugin": "^7.28.6", "@babel/helper-plugin-utils": "^7.28.6", "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", "@babel/plugin-syntax-typescript": "^7.28.6" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-0YWL2RFxOqEm9Efk5PvreamxPME8OyY0wM5wh5lHjF+VtVhdneCWGzZeSqzOfiobVqQaNCd2z0tQvnI9DaPWPw=="], - - "@babel/preset-typescript": ["@babel/preset-typescript@7.28.5", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", "@babel/helper-validator-option": "^7.27.1", "@babel/plugin-syntax-jsx": "^7.27.1", "@babel/plugin-transform-modules-commonjs": "^7.27.1", "@babel/plugin-transform-typescript": "^7.28.5" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-+bQy5WOI2V6LJZpPVxY+yp66XdZ2yifu0Mc1aP5CQKgjn4QM5IN2i5fAZ4xKop47pr8rpVhiAeu+nDQa12C8+g=="], - - "@babel/template": ["@babel/template@7.28.6", "", { "dependencies": { "@babel/code-frame": "^7.28.6", "@babel/parser": "^7.28.6", "@babel/types": "^7.28.6" } }, "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ=="], - - "@babel/traverse": ["@babel/traverse@7.29.0", "", { "dependencies": { "@babel/code-frame": "^7.29.0", "@babel/generator": "^7.29.0", "@babel/helper-globals": "^7.28.0", "@babel/parser": "^7.29.0", "@babel/template": "^7.28.6", "@babel/types": "^7.29.0", "debug": "^4.3.1" } }, "sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA=="], - - "@babel/types": ["@babel/types@7.29.0", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.28.5" } }, "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A=="], + "@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@4.0.57", "", { "dependencies": { "@ai-sdk/provider": "3.0.18", "@standard-schema/spec": "^1.1.0", "eventsource-parser": "^3.0.8", "undici": "^6.28.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-89a7sPvZqXsP3PigcAQLWntu/duIVEcN/RyChAB+j6p3C7NwW767sVttvL8r0+BetTg63uAXQoYHCZZueQKBSw=="], "@biomejs/biome": ["@biomejs/biome@2.4.8", "", { "optionalDependencies": { "@biomejs/cli-darwin-arm64": "2.4.8", "@biomejs/cli-darwin-x64": "2.4.8", "@biomejs/cli-linux-arm64": "2.4.8", "@biomejs/cli-linux-arm64-musl": "2.4.8", "@biomejs/cli-linux-x64": "2.4.8", "@biomejs/cli-linux-x64-musl": "2.4.8", "@biomejs/cli-win32-arm64": "2.4.8", "@biomejs/cli-win32-x64": "2.4.8" }, "bin": { "biome": "bin/biome" } }, "sha512-ponn0oKOky1oRXBV+rlSaUlixUxf1aZvWC19Z41zBfUOUesthrQqL3OtiAlSB1EjFjyWpn98Q64DHelhA6jNlA=="], @@ -200,15 +139,9 @@ "@cable-intel/shopify-cable-source": ["@cable-intel/shopify-cable-source@workspace:packages/shopify-cable-source"], - "@clack/core": ["@clack/core@1.3.1", "", { "dependencies": { "fast-wrap-ansi": "^0.2.0", "sisteransi": "^1.0.5" } }, "sha512-fT1qHVGAag4IEkrupZ6lRRbNCs1vS9P01KB/sG8zKgvUztbYtFBtQpjSITNwooDZ83tpsPzP0mRNs1/KVszCRA=="], - - "@clack/prompts": ["@clack/prompts@1.4.0", "", { "dependencies": { "@clack/core": "1.3.1", "fast-string-width": "^3.0.2", "fast-wrap-ansi": "^0.2.0", "sisteransi": "^1.0.5" } }, "sha512-S0My7XPGIgpRWMDG8uRqalbgT+a6FmCUdOW+HaIOVVpUPHOb7RrpvjTjiODadKp06fsrVDJZlIzc6yCTp4AnxA=="], + "@clack/core": ["@clack/core@1.5.1", "", { "dependencies": { "fast-wrap-ansi": "^0.2.0", "sisteransi": "^1.0.5" } }, "sha512-iHTrHA8MtVuLl2TfZySmcKv1qO2PoyC9Z7pfSDozEuV5vtY3/wcOPKJXlqJ5Oq2Cx5DDGQGAMVx6HZfRRoVEbQ=="], - "@emnapi/core": ["@emnapi/core@1.10.0", "", { "dependencies": { "@emnapi/wasi-threads": "1.2.1", "tslib": "^2.4.0" } }, "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw=="], - - "@emnapi/runtime": ["@emnapi/runtime@1.10.0", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA=="], - - "@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.2.1", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w=="], + "@clack/prompts": ["@clack/prompts@1.8.1", "", { "dependencies": { "@clack/core": "1.5.1", "fast-string-width": "^3.0.2", "fast-wrap-ansi": "^0.2.0", "sisteransi": "^1.0.5" } }, "sha512-dlT1m5e/0yUL0kRNcQn7yGLVThkgbB0Ga/1AmfDDC/8ik6AIiSf2QLQO2zPYvefsHP0aFgxO93cVLCCfDp7kzQ=="], "@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.27.0", "", { "os": "aix", "cpu": "ppc64" }, "sha512-KuZrd2hRjz01y5JK9mEBSD3Vj3mbCvemhT466rSuJYeE/hjuBrHfjjcjMdTm/sz7au+++sdbJZJmuBwQLuw68A=="], @@ -262,177 +195,141 @@ "@esbuild/win32-x64": ["@esbuild/win32-x64@0.27.0", "", { "os": "win32", "cpu": "x64" }, "sha512-aIitBcjQeyOhMTImhLZmtxfdOcuNRpwlPNmlFKPcHQYPhEssw75Cl1TSXJXpMkzaua9FUetx/4OQKq7eJul5Cg=="], - "@isaacs/cliui": ["@isaacs/cliui@9.0.0", "", {}, "sha512-AokJm4tuBHillT+FpMtxQ60n8ObyXBatq7jD2/JA9dxbDDokKQm8KMht5ibGzLVU9IJDIKK4TPKgMHEYMn3lMg=="], - "@jridgewell/gen-mapping": ["@jridgewell/gen-mapping@0.3.13", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA=="], "@jridgewell/remapping": ["@jridgewell/remapping@2.3.5", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ=="], "@jridgewell/resolve-uri": ["@jridgewell/resolve-uri@3.1.2", "", {}, "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw=="], - "@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.5", "", {}, "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og=="], + "@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.6.0", "", {}, "sha512-T7jf+5zgsZHwNJ4lvQ7/aezbyk0nNX+zJVWpmHA7VYsEx7a7qr5Rg5IbtJFqkgze5Y2sruq1RUY8Q837Od7iFw=="], "@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="], - "@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@1.1.4", "", { "dependencies": { "@tybys/wasm-util": "^0.10.1" }, "peerDependencies": { "@emnapi/core": "^1.7.1", "@emnapi/runtime": "^1.7.1" } }, "sha512-3NQNNgA1YSlJb/kMH1ildASP9HW7/7kYnRI2szWJaofaS1hWmbGI4H+d3+22aGzXXN9IJ+n+GiFVcGipJP18ow=="], - - "@opentelemetry/api": ["@opentelemetry/api@1.9.0", "", {}, "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg=="], - - "@opentui/core": ["@opentui/core@0.3.0", "", { "dependencies": { "bun-ffi-structs": "0.2.2", "diff": "9.0.0", "marked": "17.0.1", "string-width": "7.2.0", "strip-ansi": "7.1.2", "yoga-layout": "3.2.1" }, "optionalDependencies": { "@opentui/core-darwin-arm64": "0.3.0", "@opentui/core-darwin-x64": "0.3.0", "@opentui/core-linux-arm64": "0.3.0", "@opentui/core-linux-x64": "0.3.0", "@opentui/core-win32-arm64": "0.3.0", "@opentui/core-win32-x64": "0.3.0" }, "peerDependencies": { "web-tree-sitter": "0.25.10" } }, "sha512-wvNESYGYGRLuvarZ3QY4CTB+BziZ/j6Snd9qRKD4fQ7SF6G4UpYElLTFrg7uzRo1v7WJTqbquymcTvWEHMnpYA=="], - - "@opentui/core-darwin-arm64": ["@opentui/core-darwin-arm64@0.3.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-/eDfAcutAHJqR9spwHMLuo6LMqngymev/m+i6uqlk98gX1EJiJe2pJ16sKbp3RctgH/Gz/8TYOhVHpPGYJl7yQ=="], + "@opentelemetry/api": ["@opentelemetry/api@1.9.1", "", {}, "sha512-gLyJlPHPZYdAk1JENA9LeHejZe1Ti77/pTeFm/nMXmQH/HFZlcS/O2XJB+L8fkbrNSqhdtlvjBVjxwUYanNH5Q=="], - "@opentui/core-darwin-x64": ["@opentui/core-darwin-x64@0.3.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-/j6EWAvdwhz1wU/mWfXepAf3+NuMYz2Ic5ozaid5LdwIpPomIkM9yCUDm76mQhRBbjsAl/7UeSeUA0qSCMSZBg=="], - - "@opentui/core-linux-arm64": ["@opentui/core-linux-arm64@0.3.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-uUFVT3V35KkM1m8gaLmRcTV9dsJzXnxwM+dv6+NjScx0W/Y0CJKbW9wDYwnLyPnBNgaFUi171zmJra5gTtFTsw=="], - - "@opentui/core-linux-x64": ["@opentui/core-linux-x64@0.3.0", "", { "os": "linux", "cpu": "x64" }, "sha512-73bNNNU2OaqZQLIlvzDOdAzQmzBAqf+cSilmJ+Y9JnybrBn1d6VShC66+V4xxIgonq1swk7BD+SUHYbwwGilQA=="], - - "@opentui/core-win32-arm64": ["@opentui/core-win32-arm64@0.3.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-jg5KrV/4mVQ0mdkcL9CtQVtBk0NAtQ+2rCKoZ/jNHB6GxGK0ot9vDV6P3X68hZVkvpb2pdXfg6GRsZJ+Np4hZA=="], - - "@opentui/core-win32-x64": ["@opentui/core-win32-x64@0.3.0", "", { "os": "win32", "cpu": "x64" }, "sha512-kiM3C5bwQBTfrJKAOfb+L3U6MMkPSQlMhAERlLMjqSurc+llcyqygr/wbXSvfAqJtKlIpf3MKJRnVFTyfRIdng=="], - - "@opentui/solid": ["@opentui/solid@0.3.0", "", { "dependencies": { "@babel/core": "7.28.0", "@babel/preset-typescript": "7.27.1", "@opentui/core": "0.3.0", "babel-plugin-module-resolver": "5.0.2", "babel-preset-solid": "1.9.12", "entities": "7.0.1", "s-js": "^0.4.9" }, "peerDependencies": { "solid-js": "1.9.12" } }, "sha512-AUtNzvgkdW81Ftl0sahAy3tY1LIPSMzBw3APBC8jiDAzzPv4kYVdyWXryTxLbU2q+Pgtr57VwKwHgc5wsNrd2w=="], - - "@oxc-project/types": ["@oxc-project/types@0.132.0", "", {}, "sha512-FESMOxil5Se014ui/Eq8fT5uHJo6nIRwH0PfJrZJXs6Gek3ZVFOrpUv3YIZT20m+extU98Hg1Ym72U58rlsxUQ=="], + "@oxc-project/types": ["@oxc-project/types@0.153.0", "", {}, "sha512-jSNrRIyvPVLL5E1z/tS5zLAprosGz/FuI1PWwEOz59j3E4TTTzSpqKb/RDGqJhhW0QHYISGXxq582wGx6T11Tw=="], "@polka/url": ["@polka/url@1.0.0-next.29", "", {}, "sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww=="], - "@rolldown/binding-android-arm64": ["@rolldown/binding-android-arm64@1.0.2", "", { "os": "android", "cpu": "arm64" }, "sha512-ZS4D1JPGn/MYQN/SYDWftIE/nVsM8j/AFOYEzAoOE2O3NktQOZru+/vYXGbR/qtdLdIfGCP0lcoJiYVzsEz+iQ=="], + "@rolldown/binding-android-arm-eabi": ["@rolldown/binding-android-arm-eabi@1.2.13", "", { "os": "android", "cpu": "arm" }, "sha512-Vu/NICrgdRvUOeaJZIyB2FP63s6fO/4ud5l8dXmxDx9yDyn+mN0y+gsvsVviSli+T361Aqo0h46EuMoobrwawA=="], - "@rolldown/binding-darwin-arm64": ["@rolldown/binding-darwin-arm64@1.0.2", "", { "os": "darwin", "cpu": "arm64" }, "sha512-vdFA9+C/rekyGce7WqHs/xoT0ioZEWaOFyZLIV1mEeNFaFDUQrPIo8Vs2GvJ6eetb3rzDUtUBgzto3ExpXJB3w=="], + "@rolldown/binding-android-arm64": ["@rolldown/binding-android-arm64@1.2.13", "", { "os": "android", "cpu": "arm64" }, "sha512-6LEoIrGy5vvCd2boNnv2soPSUZj/PRvhp8R/t9KlX+mIZxThIE/ZqxrGXxmuNX0FuvQFvLdZr7S4HrYpYBVhEQ=="], - "@rolldown/binding-darwin-x64": ["@rolldown/binding-darwin-x64@1.0.2", "", { "os": "darwin", "cpu": "x64" }, "sha512-BewSOwTHazv77DTYiAZXSqqKZ4KP/KonFisDMVU7PImxoWfB2aepnPhd2E4SWz3zDzYgDNbs6jBmTdgNnF02GA=="], + "@rolldown/binding-darwin-arm64": ["@rolldown/binding-darwin-arm64@1.2.13", "", { "os": "darwin", "cpu": "arm64" }, "sha512-CaJSRzlQ6Q+zkDMVA9OkVxBjVoPRCDEE3v0FSJ9kIQBgRjFApA3Si4ijhbNVUji7ZJt+PfO0e46qy4nPInHUPA=="], - "@rolldown/binding-freebsd-x64": ["@rolldown/binding-freebsd-x64@1.0.2", "", { "os": "freebsd", "cpu": "x64" }, "sha512-m41o7M0YWtUdqk61Tb+jnKb2rN++iRdIASlExkUoKfIAH30DOHCB8fVLzSUpbWHHU8esmEioY62PxzexE8MBuA=="], + "@rolldown/binding-darwin-x64": ["@rolldown/binding-darwin-x64@1.2.13", "", { "os": "darwin", "cpu": "x64" }, "sha512-e4r340I5hxSzJkD3YJBxgzq/rJ3wwxwdLLb2FPt1Zpyem20nrQPT11DDEEkazwxaHumH8wpglsp/Ozgj/wT6mw=="], - "@rolldown/binding-linux-arm-gnueabihf": ["@rolldown/binding-linux-arm-gnueabihf@1.0.2", "", { "os": "linux", "cpu": "arm" }, "sha512-jcojB9H7W/jS29pMKWAK1N+fU99vXodHDTatS3b3y/XSOCiHo0kkA74pL3jJmkoQtYpOCxDvaKs1fo2Ij/1X5w=="], + "@rolldown/binding-freebsd-x64": ["@rolldown/binding-freebsd-x64@1.2.13", "", { "os": "freebsd", "cpu": "x64" }, "sha512-hoBWDZ4/K/mJKVMbsgUd1jfpA2DiOrJ54mpegObU9TBHBBrtX4FLIXJJQRDPZ/2Dn91GvPob+Z2hwKJzf4E5SQ=="], - "@rolldown/binding-linux-arm64-gnu": ["@rolldown/binding-linux-arm64-gnu@1.0.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-1jn6qDU5iiOgFgygDzKUuKP0maTi0/f1+sBLgvij/76C77Nm3ts6ufz9Bjg5q5dduxiUIxtq86JIoBvo1xQ4Ig=="], + "@rolldown/binding-linux-arm-gnueabihf": ["@rolldown/binding-linux-arm-gnueabihf@1.2.13", "", { "os": "linux", "cpu": "arm" }, "sha512-oF9fZu3vTWnTiVAkDFxU9PJLt1nh20K9RRpuHsR7ZnE2L7XookBbDBVLojCVSLGbvtSlxeUNnjAj6DbNsKyzbg=="], - "@rolldown/binding-linux-arm64-musl": ["@rolldown/binding-linux-arm64-musl@1.0.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-QVLO/czFMdoMFSqlX3bcswcJNm/23r+qoa/jgtmFc/qEp6/jXmIkDjF/XIo8dPfGaiwy1xfQn8o77L79GeXFgw=="], + "@rolldown/binding-linux-arm64-gnu": ["@rolldown/binding-linux-arm64-gnu@1.2.13", "", { "os": "linux", "cpu": "arm64" }, "sha512-DTpb/+jMiqWMPgphn4ksW1zW+XLJ6dJJ9FMvnIk9I291LhCctMPhMYJrP5t9p6C9qa3KXadDdUEZHBaPh3kNxQ=="], - "@rolldown/binding-linux-ppc64-gnu": ["@rolldown/binding-linux-ppc64-gnu@1.0.2", "", { "os": "linux", "cpu": "ppc64" }, "sha512-hgO5Abm0w5UL6FEa2iFnZqo2KlK7TQ5QhV5x09hujBf7t5KzHQ1VmfPuTpqRy/rNlSxua3eWH374xxiVrP+lcA=="], + "@rolldown/binding-linux-arm64-musl": ["@rolldown/binding-linux-arm64-musl@1.2.13", "", { "os": "linux", "cpu": "arm64" }, "sha512-xU+Kvv9N2A3SY35zFh1FWWNjxbadE/BKeqnkXe7vScU4GZsw757BlVmDLITZEq8DPpk6m7uoDYkK7P+x/uU5Ow=="], - "@rolldown/binding-linux-s390x-gnu": ["@rolldown/binding-linux-s390x-gnu@1.0.2", "", { "os": "linux", "cpu": "s390x" }, "sha512-fy8rXxuYEu602abC8MUNaPjYLIFzReOaEIEMKMUa0rFEUxNpVXhs15KSSQ4qlqSaM7B6rcj9rDZgADh/IGDzLQ=="], + "@rolldown/binding-linux-ppc64-gnu": ["@rolldown/binding-linux-ppc64-gnu@1.2.13", "", { "os": "linux", "cpu": "ppc64" }, "sha512-XMk/hnhh25oQ756Lclb1Q11h4ll0xC0Esa5w4Vzp+mAE2S3AGJgeM0SonrIgY8fHhjarb6hcoTuwoFG0VVP2ig=="], - "@rolldown/binding-linux-x64-gnu": ["@rolldown/binding-linux-x64-gnu@1.0.2", "", { "os": "linux", "cpu": "x64" }, "sha512-0+bOkiQ779+r1WpoHOWHqncvyySci0vKph+myNDYb+im6meJAzHQXay6oEgnkHuUGouM1LKTZwqKpBow6Kj7CQ=="], + "@rolldown/binding-linux-s390x-gnu": ["@rolldown/binding-linux-s390x-gnu@1.2.13", "", { "os": "linux", "cpu": "s390x" }, "sha512-U1BoTHYmlywE2n3HUWjzZ+8EQsKr+OwpxYuDWaubx42tUL8QSL7nOqRz/NVHanibbFP3M1Rxuprz/wNcbMKjqw=="], - "@rolldown/binding-linux-x64-musl": ["@rolldown/binding-linux-x64-musl@1.0.2", "", { "os": "linux", "cpu": "x64" }, "sha512-mjSkrzZK5Qsl0a9d1JgILOiuZOSDTVdKENcSXBoqbzSrspLR/4/IRVDo5wd2GgZjNss/viBFJdeq+j7qH2nypw=="], + "@rolldown/binding-linux-x64-gnu": ["@rolldown/binding-linux-x64-gnu@1.2.13", "", { "os": "linux", "cpu": "x64" }, "sha512-J4cm80Pc6wEwV5AZ42o62sIQgDk+Y7R2s646jIwuvO+4V46QB58N4vVuMPWCBaNgursXLpmUWJV//WQAkPvHBQ=="], - "@rolldown/binding-openharmony-arm64": ["@rolldown/binding-openharmony-arm64@1.0.2", "", { "os": "none", "cpu": "arm64" }, "sha512-1v5vHasdfQAZoEHakBV72LIFAC9JjnymsiKxp+GEr/ma3+NJCPSaYK+qavInOovJkgwFrs7GccX2d6IgDA3Z5w=="], + "@rolldown/binding-linux-x64-musl": ["@rolldown/binding-linux-x64-musl@1.2.13", "", { "os": "linux", "cpu": "x64" }, "sha512-W0euZS40U4V6JkmEnVCIX9mcsL6cbXQ4X8c2w+MMOIjFg68TXUW+u3RT+2e1t1pv9mg/EP3pDtK3POXdQ7Rxyw=="], - "@rolldown/binding-wasm32-wasi": ["@rolldown/binding-wasm32-wasi@1.0.2", "", { "dependencies": { "@emnapi/core": "1.10.0", "@emnapi/runtime": "1.10.0", "@napi-rs/wasm-runtime": "^1.1.4" }, "cpu": "none" }, "sha512-mb1VobWn6NheziTk5/WEaR6AKVbrwT5sOi6C7zk3gy/pD1qtJfU1j4PgTo2NJnOtbL9Dl3Aeei8w9jJ7qC2jZQ=="], + "@rolldown/binding-openharmony-arm64": ["@rolldown/binding-openharmony-arm64@1.2.13", "", { "os": "none", "cpu": "arm64" }, "sha512-do/AF/4n9MRshia3a5aXoy8xM2c6mgfT6D7H/dY6my5z+hfCIR4ab7mk4IRj++nsEQpKy39kd0DQnDq+r692tw=="], - "@rolldown/binding-win32-arm64-msvc": ["@rolldown/binding-win32-arm64-msvc@1.0.2", "", { "os": "win32", "cpu": "arm64" }, "sha512-SqKonF56vA/L2yHwHYcEp2P34URpOZ7d1fS635cTkpDnUtEGdUbhI6NzsPdqeSWvAAeGDrxjWjNmibDIdFf9/A=="], + "@rolldown/binding-win32-arm64-msvc": ["@rolldown/binding-win32-arm64-msvc@1.2.13", "", { "os": "win32", "cpu": "arm64" }, "sha512-6FtUf2cJZobutJdxnGYX8z0vtbgqU0b2v8vfBhkXb9wp2MppMFtkunE+IaOntyi01rkIQoPW2GHABtfzsC0BRQ=="], - "@rolldown/binding-win32-x64-msvc": ["@rolldown/binding-win32-x64-msvc@1.0.2", "", { "os": "win32", "cpu": "x64" }, "sha512-v7qRI7gXLRINcOGXt+7YmAZ6iFuyZVMIoXAxhd8oP+DR9dLfL9GfNIx7PLMxmhZdvq8waUJBQiWN9EKNy+TRBQ=="], + "@rolldown/binding-win32-x64-msvc": ["@rolldown/binding-win32-x64-msvc@1.2.13", "", { "os": "win32", "cpu": "x64" }, "sha512-UKhFaZTeTcbKrb3iU82m17jVcPEAoqDkCN5MBqgPyZSwp/cQmib21crTS0DdH7T1B7wZRWEI89YQAOJMet5yhg=="], "@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.1", "", {}, "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw=="], "@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="], - "@sveltejs/acorn-typescript": ["@sveltejs/acorn-typescript@1.0.10", "", { "peerDependencies": { "acorn": "^8.9.0" } }, "sha512-4WfKk68eTih+MiJD4fSbxN7E8kVBmTMPWHUPYjvl2N0rMs53YLTT8/YjKU5Dtnz5LqDjl7LEw4U7lXR2W3J5WA=="], + "@sveltejs/acorn-typescript": ["@sveltejs/acorn-typescript@1.0.13", "", { "peerDependencies": { "acorn": "^8.9.0" } }, "sha512-wgKggnhZVL9Bfx1OaKKTrYY9BFRk6C8UAkQNUcIv1+llzYrIqy+RZm5HPKzn0NpEBvTVhTqB4kQyllZywsRBRQ=="], "@sveltejs/adapter-auto": ["@sveltejs/adapter-auto@7.0.1", "", { "peerDependencies": { "@sveltejs/kit": "^2.0.0" } }, "sha512-dvuPm1E7M9NI/+canIQ6KKQDU2AkEefEZ2Dp7cY6uKoPq9Z/PhOXABe526UdW2mN986gjVkuSLkOYIBnS/M2LQ=="], - "@sveltejs/kit": ["@sveltejs/kit@2.61.1", "", { "dependencies": { "@standard-schema/spec": "^1.0.0", "@sveltejs/acorn-typescript": "^1.0.9", "@types/cookie": "^0.6.0", "acorn": "^8.16.0", "cookie": "^0.6.0", "devalue": "^5.8.1", "esm-env": "^1.2.2", "kleur": "^4.1.5", "magic-string": "^0.30.5", "mrmime": "^2.0.0", "set-cookie-parser": "^3.0.0", "sirv": "^3.0.0" }, "peerDependencies": { "@opentelemetry/api": "^1.0.0", "@sveltejs/vite-plugin-svelte": "^3.0.0 || ^4.0.0-next.1 || ^5.0.0 || ^6.0.0-next.0 || ^7.0.0", "svelte": "^4.0.0 || ^5.0.0-next.0", "typescript": "^5.3.3 || ^6.0.0", "vite": "^5.0.3 || ^6.0.0 || ^7.0.0-beta.0 || ^8.0.0" }, "optionalPeers": ["@opentelemetry/api", "typescript"], "bin": { "svelte-kit": "svelte-kit.js" } }, "sha512-Ny8s1SR1TyQS2hD2Rvw0XKzU2Nw1eUF52dTb6T2bdcgz7wSC+Nyb5IwjWYlR4b2dvbbR5NJDiQwHg3rnNseghg=="], + "@sveltejs/kit": ["@sveltejs/kit@2.70.3", "", { "dependencies": { "@standard-schema/spec": "^1.0.0", "@sveltejs/acorn-typescript": "^1.0.9", "@types/cookie": "^0.6.0", "acorn": "^8.16.0", "cookie": "^0.6.0", "devalue": "^5.8.1", "esm-env": "^1.2.2", "kleur": "^4.1.5", "magic-string": "^0.30.5", "mrmime": "^2.0.0", "set-cookie-parser": "^3.0.0", "sirv": "^3.0.0" }, "peerDependencies": { "@opentelemetry/api": "^1.0.0", "@sveltejs/vite-plugin-svelte": "^3.0.0 || ^4.0.0-next.1 || ^5.0.0 || ^6.0.0-next.0 || ^7.0.0", "svelte": "^4.0.0 || ^5.0.0-next.0", "typescript": "^5.3.3 || ^6.0.0", "vite": "^5.0.3 || ^6.0.0 || ^7.0.0-beta.0 || ^8.0.0" }, "optionalPeers": ["@opentelemetry/api", "typescript"], "bin": { "svelte-kit": "svelte-kit.js" } }, "sha512-UDvEYuZqAMbfB/oXIoqKvbKcb7YczK5zYrzmsGV1zRJk03jntwp8dXiYoIJotxAndsKvcPFtx9H1GRSKFdSHgg=="], - "@sveltejs/vite-plugin-svelte": ["@sveltejs/vite-plugin-svelte@7.1.2", "", { "dependencies": { "deepmerge": "^4.3.1", "magic-string": "^0.30.21", "obug": "^2.1.0", "vitefu": "^1.1.2" }, "peerDependencies": { "svelte": "^5.46.4", "vite": "^8.0.0-beta.7 || ^8.0.0" } }, "sha512-DrUBA2UXRfDmUX/ZTiEopd3X40yavsJF1FX2RygcuIScHL7o5YX1fMvoYnDhjeJQC4weCOklirpNWlcb2NiSeA=="], + "@sveltejs/load-config": ["@sveltejs/load-config@0.2.3", "", {}, "sha512-VT3qmUb8pRV2QrZjd8iAmtg8lf4W0TIjZbvXtz5MKei/q96teWZgGJyyidJzOjzZzvdq616eSRVeMYIQChUTAQ=="], - "@t3-oss/env-core": ["@t3-oss/env-core@0.13.11", "", { "peerDependencies": { "arktype": "^2.1.0", "typescript": ">=5.0.0", "valibot": "^1.0.0-beta.7 || ^1.0.0", "zod": "^3.24.0 || ^4.0.0" }, "optionalPeers": ["arktype", "typescript", "valibot", "zod"] }, "sha512-sM7GYY+KL7H/Hl0BE0inWfk3nRHZOLhmVn7sHGxaZt9FAR6KqREXAE+6TqKfiavfXmpRxO/OZ2QgKRd+oiBYRQ=="], + "@sveltejs/vite-plugin-svelte": ["@sveltejs/vite-plugin-svelte@7.3.1", "", { "dependencies": { "deepmerge": "^4.3.1", "magic-string": "^1.0.0", "obug": "^2.1.0", "vitefu": "^1.1.2" }, "peerDependencies": { "svelte": "^5.46.4", "vite": "^8.0.0-beta.7 || ^8.0.0" } }, "sha512-ZPsLN8B1e/En+Ak5s4V7srFDT532oS0qieLsQwu63NGKsS+iAjoO2Js1BochlHlglcU+Pt7WAO3C5Ee+4f6gVA=="], - "@tabler/icons": ["@tabler/icons@3.40.0", "", {}, "sha512-V/Q4VgNPKubRTiLdmWjV/zscYcj5IIk+euicUtaVVqF6luSC9rDngYWgST5/yh3Mrg/mYUwRv1YVTk71Jp0twQ=="], + "@t3-oss/env-core": ["@t3-oss/env-core@0.13.11", "", { "peerDependencies": { "arktype": "^2.1.0", "typescript": ">=5.0.0", "valibot": "^1.0.0-beta.7 || ^1.0.0", "zod": "^3.24.0 || ^4.0.0" }, "optionalPeers": ["arktype", "typescript", "valibot", "zod"] }, "sha512-sM7GYY+KL7H/Hl0BE0inWfk3nRHZOLhmVn7sHGxaZt9FAR6KqREXAE+6TqKfiavfXmpRxO/OZ2QgKRd+oiBYRQ=="], - "@tabler/icons-svelte": ["@tabler/icons-svelte@3.40.0", "", { "dependencies": { "@tabler/icons": "3.40.0" }, "peerDependencies": { "svelte": ">=3 <6 || >=5.0.0-next.0" } }, "sha512-7j8y7kPWGEmcTjcAry8JJK9RNnv8lLVN3c4GHRfRj/iu0+ovkaRM1lLplITYVPq/fbzhrK52P8i8JkEQBPj0hw=="], + "@tabler/icons": ["@tabler/icons@3.49.0", "", {}, "sha512-l+GnSkFETx0U26icxv1VUFnxxiTqXfCcVRiRAnyMwibupM2S2L/hSaZ9gmSgz01rkrsH/93NvLsxykqdanc6lw=="], - "@tailwindcss/node": ["@tailwindcss/node@4.2.2", "", { "dependencies": { "@jridgewell/remapping": "^2.3.5", "enhanced-resolve": "^5.19.0", "jiti": "^2.6.1", "lightningcss": "1.32.0", "magic-string": "^0.30.21", "source-map-js": "^1.2.1", "tailwindcss": "4.2.2" } }, "sha512-pXS+wJ2gZpVXqFaUEjojq7jzMpTGf8rU6ipJz5ovJV6PUGmlJ+jvIwGrzdHdQ80Sg+wmQxUFuoW1UAAwHNEdFA=="], + "@tabler/icons-svelte": ["@tabler/icons-svelte@3.49.0", "", { "dependencies": { "@tabler/icons": "3.49.0" }, "peerDependencies": { "svelte": ">=3 <6 || >=5.0.0-next.0" } }, "sha512-+omJWDCEHHfloOoMELXkdlrIpoQCN2XBx2xnb2NxqDjxsAGGTii0UQF05UoKiUO97skqe1eEIbeGjRZ9xUye0w=="], - "@tailwindcss/oxide": ["@tailwindcss/oxide@4.2.2", "", { "optionalDependencies": { "@tailwindcss/oxide-android-arm64": "4.2.2", "@tailwindcss/oxide-darwin-arm64": "4.2.2", "@tailwindcss/oxide-darwin-x64": "4.2.2", "@tailwindcss/oxide-freebsd-x64": "4.2.2", "@tailwindcss/oxide-linux-arm-gnueabihf": "4.2.2", "@tailwindcss/oxide-linux-arm64-gnu": "4.2.2", "@tailwindcss/oxide-linux-arm64-musl": "4.2.2", "@tailwindcss/oxide-linux-x64-gnu": "4.2.2", "@tailwindcss/oxide-linux-x64-musl": "4.2.2", "@tailwindcss/oxide-wasm32-wasi": "4.2.2", "@tailwindcss/oxide-win32-arm64-msvc": "4.2.2", "@tailwindcss/oxide-win32-x64-msvc": "4.2.2" } }, "sha512-qEUA07+E5kehxYp9BVMpq9E8vnJuBHfJEC0vPC5e7iL/hw7HR61aDKoVoKzrG+QKp56vhNZe4qwkRmMC0zDLvg=="], + "@tailwindcss/node": ["@tailwindcss/node@4.3.3", "", { "dependencies": { "@jridgewell/remapping": "^2.3.5", "enhanced-resolve": "^5.24.1", "jiti": "^2.7.0", "lightningcss": "1.32.0", "magic-string": "^0.30.21", "source-map-js": "^1.2.1", "tailwindcss": "4.3.3" } }, "sha512-/T8IKEsf9VTU6tLjgC7+sv2mOPtQxzE2jMw7u4Tt40Tx+QSZxpzh95/H6cMKoja9XuW7iMdLJYBB0o9G1CaAgg=="], - "@tailwindcss/oxide-android-arm64": ["@tailwindcss/oxide-android-arm64@4.2.2", "", { "os": "android", "cpu": "arm64" }, "sha512-dXGR1n+P3B6748jZO/SvHZq7qBOqqzQ+yFrXpoOWWALWndF9MoSKAT3Q0fYgAzYzGhxNYOoysRvYlpixRBBoDg=="], + "@tailwindcss/oxide": ["@tailwindcss/oxide@4.3.3", "", { "optionalDependencies": { "@tailwindcss/oxide-android-arm64": "4.3.3", "@tailwindcss/oxide-darwin-arm64": "4.3.3", "@tailwindcss/oxide-darwin-x64": "4.3.3", "@tailwindcss/oxide-freebsd-x64": "4.3.3", "@tailwindcss/oxide-linux-arm-gnueabihf": "4.3.3", "@tailwindcss/oxide-linux-arm64-gnu": "4.3.3", "@tailwindcss/oxide-linux-arm64-musl": "4.3.3", "@tailwindcss/oxide-linux-x64-gnu": "4.3.3", "@tailwindcss/oxide-linux-x64-musl": "4.3.3", "@tailwindcss/oxide-wasm32-wasi": "4.3.3", "@tailwindcss/oxide-win32-arm64-msvc": "4.3.3", "@tailwindcss/oxide-win32-x64-msvc": "4.3.3" } }, "sha512-krXjAikiaFSPaK/FkAQT5UTx3VormQaiZ5hBFlJZ9UFQGB/rwg1MZIhHAG9smMQRTdyJxP6Qt5MwMtdyU5FWrA=="], - "@tailwindcss/oxide-darwin-arm64": ["@tailwindcss/oxide-darwin-arm64@4.2.2", "", { "os": "darwin", "cpu": "arm64" }, "sha512-iq9Qjr6knfMpZHj55/37ouZeykwbDqF21gPFtfnhCCKGDcPI/21FKC9XdMO/XyBM7qKORx6UIhGgg6jLl7BZlg=="], + "@tailwindcss/oxide-android-arm64": ["@tailwindcss/oxide-android-arm64@4.3.3", "", { "os": "android", "cpu": "arm64" }, "sha512-Y85A2gmPSkl5Ve5qR86GL4HT509cFqQh1aes9p3sSkyTPwt0Pppf3GkwGe4JPACcRYjgJIEhQgM6dBClnr0NYw=="], - "@tailwindcss/oxide-darwin-x64": ["@tailwindcss/oxide-darwin-x64@4.2.2", "", { "os": "darwin", "cpu": "x64" }, "sha512-BlR+2c3nzc8f2G639LpL89YY4bdcIdUmiOOkv2GQv4/4M0vJlpXEa0JXNHhCHU7VWOKWT/CjqHdTP8aUuDJkuw=="], + "@tailwindcss/oxide-darwin-arm64": ["@tailwindcss/oxide-darwin-arm64@4.3.3", "", { "os": "darwin", "cpu": "arm64" }, "sha512-BiaWatpBcERQFDlOjRDpIVXuFK5PJez5SA4JMg6VYZdBYU+qKfV/vqjcIs+IYmtitf1xYQZTwXvU/8y4lfZUGw=="], - "@tailwindcss/oxide-freebsd-x64": ["@tailwindcss/oxide-freebsd-x64@4.2.2", "", { "os": "freebsd", "cpu": "x64" }, "sha512-YUqUgrGMSu2CDO82hzlQ5qSb5xmx3RUrke/QgnoEx7KvmRJHQuZHZmZTLSuuHwFf0DJPybFMXMYf+WJdxHy/nQ=="], + "@tailwindcss/oxide-darwin-x64": ["@tailwindcss/oxide-darwin-x64@4.3.3", "", { "os": "darwin", "cpu": "x64" }, "sha512-fAeUqfV5ndhxRwai8cXGzdLvul9utWOmeTkv69unv4ZXixjn61Z+p9lCWdwOwA3TYboG3BwdVuN/RDjhBRl0mw=="], - "@tailwindcss/oxide-linux-arm-gnueabihf": ["@tailwindcss/oxide-linux-arm-gnueabihf@4.2.2", "", { "os": "linux", "cpu": "arm" }, "sha512-FPdhvsW6g06T9BWT0qTwiVZYE2WIFo2dY5aCSpjG/S/u1tby+wXoslXS0kl3/KXnULlLr1E3NPRRw0g7t2kgaQ=="], + "@tailwindcss/oxide-freebsd-x64": ["@tailwindcss/oxide-freebsd-x64@4.3.3", "", { "os": "freebsd", "cpu": "x64" }, "sha512-iyf5bV6+wnAlflVeEy7R25dupxTNECZN5QMI0qNT6eT+EgaGdZcKhGkr5SdoaWiLJ3spLqIY9VCeSGrwmtg4kw=="], - "@tailwindcss/oxide-linux-arm64-gnu": ["@tailwindcss/oxide-linux-arm64-gnu@4.2.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-4og1V+ftEPXGttOO7eCmW7VICmzzJWgMx+QXAJRAhjrSjumCwWqMfkDrNu1LXEQzNAwz28NCUpucgQPrR4S2yw=="], + "@tailwindcss/oxide-linux-arm-gnueabihf": ["@tailwindcss/oxide-linux-arm-gnueabihf@4.3.3", "", { "os": "linux", "cpu": "arm" }, "sha512-aAYUprJAJQWWbRrPvtjdroZ56Md+JM8pMiopS6xGEwDfLhqj+2ver2p4nU4Mb3CRqcMmNBjo8KkUgcxhkzVQGQ=="], - "@tailwindcss/oxide-linux-arm64-musl": ["@tailwindcss/oxide-linux-arm64-musl@4.2.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-oCfG/mS+/+XRlwNjnsNLVwnMWYH7tn/kYPsNPh+JSOMlnt93mYNCKHYzylRhI51X+TbR+ufNhhKKzm6QkqX8ag=="], + "@tailwindcss/oxide-linux-arm64-gnu": ["@tailwindcss/oxide-linux-arm64-gnu@4.3.3", "", { "os": "linux", "cpu": "arm64" }, "sha512-nDxldcEENOxZRzC2uu9jrutZdAAQtb+8WWDCSnWL1zvBk1+FN+x6MtDViPB5AJMfttVCUhehGWus3XBPgatM/w=="], - "@tailwindcss/oxide-linux-x64-gnu": ["@tailwindcss/oxide-linux-x64-gnu@4.2.2", "", { "os": "linux", "cpu": "x64" }, "sha512-rTAGAkDgqbXHNp/xW0iugLVmX62wOp2PoE39BTCGKjv3Iocf6AFbRP/wZT/kuCxC9QBh9Pu8XPkv/zCZB2mcMg=="], + "@tailwindcss/oxide-linux-arm64-musl": ["@tailwindcss/oxide-linux-arm64-musl@4.3.3", "", { "os": "linux", "cpu": "arm64" }, "sha512-Md44bD6veX/PC5iyF8cDVnw4HBIANZepRZZ7a8DQOvkfo5WUBwcp6iAuCUz23u+4SUkhJlD3eL7hNdW8ezd/kA=="], - "@tailwindcss/oxide-linux-x64-musl": ["@tailwindcss/oxide-linux-x64-musl@4.2.2", "", { "os": "linux", "cpu": "x64" }, "sha512-XW3t3qwbIwiSyRCggeO2zxe3KWaEbM0/kW9e8+0XpBgyKU4ATYzcVSMKteZJ1iukJ3HgHBjbg9P5YPRCVUxlnQ=="], + "@tailwindcss/oxide-linux-x64-gnu": ["@tailwindcss/oxide-linux-x64-gnu@4.3.3", "", { "os": "linux", "cpu": "x64" }, "sha512-tx7us1muwOKAKWao2v/GaafFeQboE6aj88vC6ziN2NCGcRm8gWUhwjzg+YdVB1e4boAtdtma4L43onunI6NS4w=="], - "@tailwindcss/oxide-wasm32-wasi": ["@tailwindcss/oxide-wasm32-wasi@4.2.2", "", { "dependencies": { "@emnapi/core": "^1.8.1", "@emnapi/runtime": "^1.8.1", "@emnapi/wasi-threads": "^1.1.0", "@napi-rs/wasm-runtime": "^1.1.1", "@tybys/wasm-util": "^0.10.1", "tslib": "^2.8.1" }, "cpu": "none" }, "sha512-eKSztKsmEsn1O5lJ4ZAfyn41NfG7vzCg496YiGtMDV86jz1q/irhms5O0VrY6ZwTUkFy/EKG3RfWgxSI3VbZ8Q=="], + "@tailwindcss/oxide-linux-x64-musl": ["@tailwindcss/oxide-linux-x64-musl@4.3.3", "", { "os": "linux", "cpu": "x64" }, "sha512-SJxX60smvHgasZoBy11dX6YRjXJFovwWBoedhbQPOBzgFWBHGB+TVPWB9BxzR7TTxU8FQZAI2AyiNCMzFm8Img=="], - "@tailwindcss/oxide-win32-arm64-msvc": ["@tailwindcss/oxide-win32-arm64-msvc@4.2.2", "", { "os": "win32", "cpu": "arm64" }, "sha512-qPmaQM4iKu5mxpsrWZMOZRgZv1tOZpUm+zdhhQP0VhJfyGGO3aUKdbh3gDZc/dPLQwW4eSqWGrrcWNBZWUWaXQ=="], + "@tailwindcss/oxide-wasm32-wasi": ["@tailwindcss/oxide-wasm32-wasi@4.3.3", "", { "dependencies": { "@emnapi/core": "^1.11.1", "@emnapi/runtime": "^1.11.1", "@emnapi/wasi-threads": "^1.2.2", "@napi-rs/wasm-runtime": "^1.1.4", "@tybys/wasm-util": "^0.10.2", "tslib": "^2.8.1" }, "cpu": "none" }, "sha512-jx1+rPhY/5Ympkktd656HBWEBLxP7dH06losBLjjf5vgCODXvi9KhtftWcMIwTFIDqBr7cRnQkdLnAG+IOlGvQ=="], - "@tailwindcss/oxide-win32-x64-msvc": ["@tailwindcss/oxide-win32-x64-msvc@4.2.2", "", { "os": "win32", "cpu": "x64" }, "sha512-1T/37VvI7WyH66b+vqHj/cLwnCxt7Qt3WFu5Q8hk65aOvlwAhs7rAp1VkulBJw/N4tMirXjVnylTR72uI0HGcA=="], + "@tailwindcss/oxide-win32-arm64-msvc": ["@tailwindcss/oxide-win32-arm64-msvc@4.3.3", "", { "os": "win32", "cpu": "arm64" }, "sha512-3rc292Ca2ceK6Ulcc/bAVnTs/3nDtoPhyEKlgPv+yQJQi/JS/AMJlqzxvlDacL1nekbrcf6bTqp/jV4qgnPxNQ=="], - "@tailwindcss/vite": ["@tailwindcss/vite@4.2.2", "", { "dependencies": { "@tailwindcss/node": "4.2.2", "@tailwindcss/oxide": "4.2.2", "tailwindcss": "4.2.2" }, "peerDependencies": { "vite": "^5.2.0 || ^6 || ^7 || ^8" } }, "sha512-mEiF5HO1QqCLXoNEfXVA1Tzo+cYsrqV7w9Juj2wdUFyW07JRenqMG225MvPwr3ZD9N1bFQj46X7r33iHxLUW0w=="], + "@tailwindcss/oxide-win32-x64-msvc": ["@tailwindcss/oxide-win32-x64-msvc@4.3.3", "", { "os": "win32", "cpu": "x64" }, "sha512-yJ0pwIVc/nYeGoV02WtsN8KYyLQv7kyI2wDnkezyJlGGjkd4QLwDGAwl47YpPJeuI0M0ObaXGSPjvWDPeTPggw=="], - "@turbo/darwin-64": ["@turbo/darwin-64@2.8.20", "", { "os": "darwin", "cpu": "x64" }, "sha512-FQ9EX1xMU5nbwjxXxM3yU88AQQ6Sqc6S44exPRroMcx9XZHqqppl5ymJF0Ig/z3nvQNwDmz1Gsnvxubo+nXWjQ=="], + "@tailwindcss/vite": ["@tailwindcss/vite@4.3.3", "", { "dependencies": { "@tailwindcss/node": "4.3.3", "@tailwindcss/oxide": "4.3.3", "tailwindcss": "4.3.3" }, "peerDependencies": { "vite": "^5.2.0 || ^6 || ^7 || ^8" } }, "sha512-yYU8cogLeSh/ms2jh8Fj7jaba/EWa7Ja6GoUqYZaraEuCI5YS6ms6ObZgjjedm+jm6XZjdNRWBpPP6Z86oOxcw=="], - "@turbo/darwin-arm64": ["@turbo/darwin-arm64@2.8.20", "", { "os": "darwin", "cpu": "arm64" }, "sha512-Gpyh9ATFGThD6/s9L95YWY54cizg/VRWl2B67h0yofG8BpHf67DFAh9nuJVKG7bY0+SBJDAo5cMur+wOl9YOYw=="], + "@turbo/darwin-64": ["@turbo/darwin-64@2.11.7", "", { "os": "darwin", "cpu": "x64" }, "sha512-Z2LFOxwbiJueD1miByXJS/BrW21vv6YaQXZustty5jEX4vQYw6zyh101Sz1QO7WG0mqBzKagZ/DoAeeujI0/Sw=="], - "@turbo/linux-64": ["@turbo/linux-64@2.8.20", "", { "os": "linux", "cpu": "x64" }, "sha512-p2QxWUYyYUgUFG0b0kR+pPi8t7c9uaVlRtjTTI1AbCvVqkpjUfCcReBn6DgG/Hu8xrWdKLuyQFaLYFzQskZbcA=="], + "@turbo/darwin-arm64": ["@turbo/darwin-arm64@2.11.7", "", { "os": "darwin", "cpu": "arm64" }, "sha512-Vhv8ybgIflosNDSSRD9fNXHlCFA60dsGyTqQmc7l28SEX5jOaweCCD4BBYNd8/p0WGkw3e7tZbfA9w6FsujGvw=="], - "@turbo/linux-arm64": ["@turbo/linux-arm64@2.8.20", "", { "os": "linux", "cpu": "arm64" }, "sha512-Gn5yjlZGLRZWarLWqdQzv0wMqyBNIdq1QLi48F1oY5Lo9kiohuf7BPQWtWxeNVS2NgJ1+nb/DzK1JduYC4AWOA=="], + "@turbo/linux-64": ["@turbo/linux-64@2.11.7", "", { "os": [ "linux", "android", ], "cpu": "x64" }, "sha512-rAqRp3blQKk1+ylMGlZHAW0eJjVNuuGUm4ZrC6RU1Im8tLryvaO4a2zLQ/FQi1TRlmKqKV9YXBki2YeytlLbXA=="], - "@turbo/windows-64": ["@turbo/windows-64@2.8.20", "", { "os": "win32", "cpu": "x64" }, "sha512-vyaDpYk/8T6Qz5V/X+ihKvKFEZFUoC0oxYpC1sZanK6gaESJlmV3cMRT3Qhcg4D2VxvtC2Jjs9IRkrZGL+exLw=="], + "@turbo/linux-arm64": ["@turbo/linux-arm64@2.11.7", "", { "os": [ "linux", "android", ], "cpu": "arm64" }, "sha512-thVYGov7S+llXAxrl3kBb/efalav0xhrjl5NHCR6AfKx14JkbqaetvbVwyss4FvZ9rt+Pv3xsTb+8SIYix8DTg=="], - "@turbo/windows-arm64": ["@turbo/windows-arm64@2.8.20", "", { "os": "win32", "cpu": "arm64" }, "sha512-voicVULvUV5yaGXo0Iue13BcHGYW3u0VgqSbfQwBaHbpj1zLjYV4KIe+7fYIo6DO8FVUJzxFps3ODCQG/Wy2Qw=="], + "@turbo/windows-64": ["@turbo/windows-64@2.11.7", "", { "os": "win32", "cpu": "x64" }, "sha512-ZIoKT7J7KutgYl4bGxmmgeUQjG+7E0nY8BF9dxUOiMH2vdTsM4JQ2uSwTYFkTR2Llbg6ghGjSmuZ8LQtkLEYjg=="], - "@tybys/wasm-util": ["@tybys/wasm-util@0.10.1", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg=="], + "@turbo/windows-arm64": ["@turbo/windows-arm64@2.11.7", "", { "os": "win32", "cpu": "arm64" }, "sha512-88Xv2f7SLGVJ68lRRx8o375dKmnZYNdDtvlKcPA0kZpcF93eAQd8l96BKQ0/AMHG0bMJA0Z5QqoqtetxzNsC+g=="], - "@types/bun": ["@types/bun@1.3.14", "", { "dependencies": { "bun-types": "1.3.14" } }, "sha512-h1hFqFVcvAvD9j9K7ZW7vd82aSA+rTdznZa+5bwvCwqSB1jmmfLcbIWhOLx1/+boy/xmjgCs/OMUL8hRJSmnPw=="], + "@types/bun": ["@types/bun@1.4.2", "", { "dependencies": { "bun-types": "1.4.2" } }, "sha512-GimotNn7+ZV0uVArItBbriZsR1oNf0+WTzPkdcFrzShI7k2norL0uzEaJT8T33dWr7O/c9ZDuAFQrctKCi72oQ=="], "@types/cookie": ["@types/cookie@0.6.0", "", {}, "sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA=="], - "@types/estree": ["@types/estree@1.0.8", "", {}, "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="], - - "@types/node": ["@types/node@25.5.0", "", { "dependencies": { "undici-types": "~7.18.0" } }, "sha512-jp2P3tQMSxWugkCUKLRPVUpGaL5MVFwF8RDuSRztfwgN1wmqJeMSbKlnEtQqU8UrhTmzEmZdu2I6v2dpp7XIxw=="], + "@types/estree": ["@types/estree@1.0.9", "", {}, "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg=="], - "@types/trusted-types": ["@types/trusted-types@2.0.7", "", {}, "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw=="], + "@types/node": ["@types/node@24.19.2", "", { "dependencies": { "undici-types": ">=7.24.0 <7.24.7" } }, "sha512-93d49wCWJkCyN87MxLpNN6KXPCwFw7bVUg79mGUV1OS40JGWwVCr8n61DXCSqz7CV6/VfPkb7xec7uP2M+QHWQ=="], "@vercel/analytics": ["@vercel/analytics@2.0.1", "", { "peerDependencies": { "@remix-run/react": "^2", "@sveltejs/kit": "^1 || ^2", "next": ">= 13", "nuxt": ">= 3", "react": "^18 || ^19 || ^19.0.0-rc", "svelte": ">= 4", "vue": "^3", "vue-router": "^4" }, "optionalPeers": ["@remix-run/react", "@sveltejs/kit", "next", "nuxt", "react", "svelte", "vue", "vue-router"] }, "sha512-MTQG6V9qQrt1tsDeF+2Uoo5aPjqbVPys1xvnIftXSJYG2SrwXRHnqEvVoYID7BTruDz4lCd2Z7rM1BdkUehk2g=="], - "@vercel/oidc": ["@vercel/oidc@3.1.0", "", {}, "sha512-Fw28YZpRnA3cAHHDlkt7xQHiJ0fcL+NRcIqsocZQUSmbzeIKRpwttJjik5ZGanXP+vlA4SbTg+AbA3bP363l+w=="], + "@vercel/oidc": ["@vercel/oidc@3.2.0", "", {}, "sha512-UycprH3T6n3jH0k44NHMa7pnFHGu/N05MjojYr+Mc6I7obkoLIJujSWwin1pCvdy/eOxrI/l3uDLQsmcrOb4ug=="], - "acorn": ["acorn@8.16.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw=="], + "acorn": ["acorn@8.19.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-oJlA3XiRm7Cyk6qFD2Jn8ak9B7jSy0qy00ADO3+8dpT0LSjFihQYv4C02LFCSYJV3Q37xYwwRy5m+IpIiUzqWw=="], - "ai": ["ai@6.0.134", "", { "dependencies": { "@ai-sdk/gateway": "3.0.77", "@ai-sdk/provider": "3.0.8", "@ai-sdk/provider-utils": "4.0.21", "@opentelemetry/api": "1.9.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-YalNEaavld/kE444gOcsMKXdVVRGEe0SK77fAFcWYcqLg+a7xKnEet8bdfrEAJTfnMjj01rhgrIL10903w1a5Q=="], + "agent-browser": ["agent-browser@0.38.2", "", { "bin": { "agent-browser": "./bin/agent-browser.js" } }, "sha512-b/7hvo2RrqocvybjB9kwpX12kGkxkFvtdJ4XPDoott5TLK2n3aPX+lYg7ALd+2YvPi2LrTKGzQYu/rxZC2Y7ZA=="], - "ansi-regex": ["ansi-regex@6.2.2", "", {}, "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg=="], + "ai": ["ai@6.0.303", "", { "dependencies": { "@ai-sdk/gateway": "3.0.212", "@ai-sdk/provider": "3.0.18", "@ai-sdk/provider-utils": "4.0.57", "@opentelemetry/api": "^1.9.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-4HE3naLXgRLN4XOoEJaPHfIz7/hO0A21ltUG5DM3NDrGVTrqhnfs7s3ItwHtiWazCDOIuc4cCE4MgiuEvVryCw=="], "aria-query": ["aria-query@5.3.1", "", {}, "sha512-Z/ZeOgVl7bcSYZ/u/rh0fOpvEpq//LZmdbkXyc7syVzjPAhfOa9ebsdTSjEBDU4vs5nC98Kfduj1uFo0qyET3g=="], "axobject-query": ["axobject-query@4.1.0", "", {}, "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ=="], - "babel-plugin-jsx-dom-expressions": ["babel-plugin-jsx-dom-expressions@0.40.7", "", { "dependencies": { "@babel/helper-module-imports": "7.18.6", "@babel/plugin-syntax-jsx": "^7.18.6", "@babel/types": "^7.20.7", "html-entities": "2.3.3", "parse5": "^7.1.2" }, "peerDependencies": { "@babel/core": "^7.20.12" } }, "sha512-/O6JWUmjv03OI9lL2ry9bUjpD5S3PclM55RRJEyCdcFZ5W2SEA/59d+l2hNsk3gI6kiWRdRPdOtqZmsQzFN1pQ=="], - - "babel-plugin-module-resolver": ["babel-plugin-module-resolver@5.0.2", "", { "dependencies": { "find-babel-config": "^2.1.1", "glob": "^9.3.3", "pkg-up": "^3.1.0", "reselect": "^4.1.7", "resolve": "^1.22.8" } }, "sha512-9KtaCazHee2xc0ibfqsDeamwDps6FZNo5S0Q81dUqEuFzVwPhcT4J5jOqIVvgCA3Q/wO9hKYxN/Ds3tIsp5ygg=="], - - "babel-preset-solid": ["babel-preset-solid@1.9.12", "", { "dependencies": { "babel-plugin-jsx-dom-expressions": "^0.40.6" }, "peerDependencies": { "@babel/core": "^7.0.0", "solid-js": "^1.9.12" }, "optionalPeers": ["solid-js"] }, "sha512-LLqnuKVDlKpyBlMPcH6qEvs/wmS9a+NczppxJ3ryS/c0O5IiSFOIBQi9GzyiGDSbcJpx4Gr87jyFTos1MyEuWg=="], - - "balanced-match": ["balanced-match@4.0.2", "", { "dependencies": { "jackspeak": "^4.2.3" } }, "sha512-x0K50QvKQ97fdEz2kPehIerj+YTeptKF9hyYkKf6egnwmMWAkADiO0QCzSp0R5xN8FTZgYaBfSaue46Ej62nMg=="], - - "baseline-browser-mapping": ["baseline-browser-mapping@2.9.19", "", { "bin": { "baseline-browser-mapping": "dist/cli.js" } }, "sha512-ipDqC8FrAl/76p2SSWKSI+H9tFwm7vYqXQrItCuiVPt26Km0jS+NzSsBWAaBusvSbQcfJG+JitdMm+wZAgTYqg=="], + "balanced-match": ["balanced-match@4.0.4", "", {}, "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA=="], - "brace-expansion": ["brace-expansion@5.0.2", "", { "dependencies": { "balanced-match": "^4.0.2" } }, "sha512-Pdk8c9poy+YhOgVWw1JNN22/HcivgKWwpxKq04M/jTmHyCZn12WPJebZxdjSa5TmBqISrUSgNYU3eRORljfCCw=="], + "brace-expansion": ["brace-expansion@5.0.12", "", { "dependencies": { "balanced-match": "^4.0.2" } }, "sha512-YovQ3rzhaLMIrDjNDMkNS01tea93qhEhG5xy8f6+R0l+dw3Ki+5sCoIoI942iuLZTHWogWktgwVDhU09iNEimQ=="], - "browserslist": ["browserslist@4.28.1", "", { "dependencies": { "baseline-browser-mapping": "^2.9.0", "caniuse-lite": "^1.0.30001759", "electron-to-chromium": "^1.5.263", "node-releases": "^2.0.27", "update-browserslist-db": "^1.2.0" }, "bin": { "browserslist": "cli.js" } }, "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA=="], - - "bun-ffi-structs": ["bun-ffi-structs@0.2.2", "", { "peerDependencies": { "typescript": "^5" } }, "sha512-N/ZWtyN0piZlrXQT7TO0V+q952orYqkfhXRXM1Hcbb+R3QSiBH4vLnib187Mrs1H7pWIYECAmPeapGYDOMCl+w=="], - - "bun-types": ["bun-types@1.3.14", "", { "dependencies": { "@types/node": "*" } }, "sha512-4N0ig0fEomHt5R0KCFWjovxow98rIoRwKolrYdCcknNwMekCXRnWEUvgu5soYV8QXtVsrUD8B95MBOZGPvr6KQ=="], - - "caniuse-lite": ["caniuse-lite@1.0.30001770", "", {}, "sha512-x/2CLQ1jHENRbHg5PSId2sXq1CIO1CISvwWAj027ltMVG2UNgW+w9oH2+HzgEIRFembL8bUlXtfbBHR1fCg2xw=="], + "bun-types": ["bun-types@1.4.2", "", { "dependencies": { "@types/node": "*" } }, "sha512-bxV1FgK7yBIzjRe5zBozIM4Bem11ZJcCXSrjWRG3YWLt8yFDePu4cLjpebO8OvPeIE9trbyPF4fuj3Cia4Fj3w=="], "chokidar": ["chokidar@4.0.3", "", { "dependencies": { "readdirp": "^4.0.1" } }, "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA=="], @@ -442,47 +339,33 @@ "commander": ["commander@14.0.3", "", {}, "sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw=="], - "convert-source-map": ["convert-source-map@2.0.0", "", {}, "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg=="], - - "convex": ["convex@1.34.0", "", { "dependencies": { "esbuild": "0.27.0", "prettier": "^3.0.0", "ws": "8.18.0" }, "peerDependencies": { "@auth0/auth0-react": "^2.0.1", "@clerk/clerk-react": "^4.12.8 || ^5.0.0", "react": "^18.0.0 || ^19.0.0-0 || ^19.0.0" }, "optionalPeers": ["@auth0/auth0-react", "@clerk/clerk-react", "react"], "bin": { "convex": "bin/main.js" } }, "sha512-TbC509Z4urZMChZR2aLPgalQ8gMhAYSz2VMxaYsCvba8YqB0Uxma7zWnXwRn7aEGXuA8ro5/uHgD1IJ0HhYYPg=="], + "convex": ["convex@1.46.0", "", { "dependencies": { "esbuild": "0.27.0", "prettier": "^3.0.0", "ws": "8.21.0" }, "peerDependencies": { "@auth0/auth0-react": "^2.0.1", "@clerk/clerk-react": "^4.12.8 || ^5.0.0", "@clerk/react": "^6.4.3", "react": "^18.0.0 || ^19.0.0-0 || ^19.0.0" }, "optionalPeers": ["@auth0/auth0-react", "@clerk/clerk-react", "@clerk/react", "react"], "bin": { "convex": "bin/main.js" } }, "sha512-dhBwDQTZDOhi5SUGAX17EWNehCdNq6/UFrSL0eLZuGfMkdsQbCVWVRZCzbZ5CF+/pgvoGxtApbEoTSipqL4JTw=="], - "convex-svelte": ["convex-svelte@0.0.12", "", { "dependencies": { "esm-env": "^1.2.2", "runed": "^0.31.1" }, "peerDependencies": { "convex": "^1.10.0", "svelte": "^5.0.0" } }, "sha512-sUZoYp4ZsokyvKlbbg1dWYB7MkAjZn4nNG9DnADEt9L6KTKuhhnEIt6fdLj+3GnVBUGDTssm17+7ppzFc4y7Gg=="], + "convex-svelte": ["convex-svelte@0.14.0", "", { "dependencies": { "esm-env": "^1.2.2", "runed": "^0.37.1" }, "peerDependencies": { "convex": "^1.30.0", "svelte": "^5.19.0" } }, "sha512-eCaId++XnczQvEGc0sQi30BHkSEQ5AZjdDWaAqSlva2h9zKezGz8enp6MdFN7MIIMIlYZF3dKdzQ/LQ0d59KhQ=="], - "convex-test": ["convex-test@0.0.53", "", { "peerDependencies": { "convex": "^1.32.0" } }, "sha512-bouZQTnTvZi8IHljHL++yClj1vcV+/9ZxEcd8JZz7RDxOfPkRKrkMgkk/xlX4M1EAiwcEJPNiQE7VJFvDM3lCQ=="], + "convex-test": ["convex-test@0.0.60", "", { "peerDependencies": { "convex": "^1.43.0" } }, "sha512-qCAFx+CO6bmpXNc9s1jZvw8NJPS4yM074ib87PUY675FsiaRRW4ALotEB+RbVCYDRMYxh623KMnYZBlrMJIv1Q=="], - "cookie": ["cookie@0.6.0", "", {}, "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw=="], - - "csstype": ["csstype@3.2.3", "", {}, "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ=="], - - "debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="], + "cookie": ["cookie@0.7.2", "", {}, "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w=="], "deepmerge": ["deepmerge@4.3.1", "", {}, "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A=="], - "detect-libc": ["detect-libc@2.1.2", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="], + "dequal": ["dequal@2.0.3", "", {}, "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA=="], - "devalue": ["devalue@5.8.1", "", {}, "sha512-4CXDYRBGqN+57wVJkuXBYmpAVUSg3L6JAQa/DFqm238G73E1wuyc/JhGQJzN7vUf/CMphYau2zXbfWzDR5aTEw=="], - - "diff": ["diff@9.0.0", "", {}, "sha512-svtcdpS8CgJyqAjEQIXdb3OjhFVVYjzGAPO8WGCmRbrml64SPw/jJD4GoE98aR7r25A0XcgrK3F02yw9R/vhQw=="], - - "dotenv": ["dotenv@17.3.1", "", {}, "sha512-IO8C/dzEb6O3F9/twg6ZLXz164a2fhTnEWb95H23Dm4OuN+92NmEAlTrupP9VW6Jm3sO26tQlqyvyi4CsnY9GA=="], - - "electron-to-chromium": ["electron-to-chromium@1.5.286", "", {}, "sha512-9tfDXhJ4RKFNerfjdCcZfufu49vg620741MNs26a9+bhLThdB+plgMeou98CAaHu/WATj2iHOOHTp1hWtABj2A=="], + "detect-libc": ["detect-libc@2.1.2", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="], - "emoji-regex": ["emoji-regex@10.6.0", "", {}, "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A=="], + "devalue": ["devalue@5.9.4", "", {}, "sha512-sPAT4pztbu6586/hrhOnMKS17IJrvg12mXiSPSS3W5qDeN2RGgvZ0diZCm31dBbnevfVmujNO3IM2wrS4Y2Rhg=="], - "enhanced-resolve": ["enhanced-resolve@5.19.0", "", { "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.3.0" } }, "sha512-phv3E1Xl4tQOShqSte26C7Fl84EwUdZsyOuSSk9qtAGyyQs2s3jJzComh+Abf4g187lUUAvH+H26omrqia2aGg=="], + "dotenv": ["dotenv@17.4.2", "", {}, "sha512-nI4U3TottKAcAD9LLud4Cb7b2QztQMUEfHbvhTH09bqXTxnSie8WnjPALV/WMCrJZ6UV/qHJ6L03OqO3LcdYZw=="], - "entities": ["entities@7.0.1", "", {}, "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA=="], + "enhanced-resolve": ["enhanced-resolve@5.26.0", "", { "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.3.3" } }, "sha512-9vhedylFonb2YGogzUKX6+Ja72gOJbN1QHAqdrvqLwhdl/QWbKopzoUC9EbQNVsAns/bx/4uyqalrQAoy1IByw=="], "esbuild": ["esbuild@0.27.0", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.27.0", "@esbuild/android-arm": "0.27.0", "@esbuild/android-arm64": "0.27.0", "@esbuild/android-x64": "0.27.0", "@esbuild/darwin-arm64": "0.27.0", "@esbuild/darwin-x64": "0.27.0", "@esbuild/freebsd-arm64": "0.27.0", "@esbuild/freebsd-x64": "0.27.0", "@esbuild/linux-arm": "0.27.0", "@esbuild/linux-arm64": "0.27.0", "@esbuild/linux-ia32": "0.27.0", "@esbuild/linux-loong64": "0.27.0", "@esbuild/linux-mips64el": "0.27.0", "@esbuild/linux-ppc64": "0.27.0", "@esbuild/linux-riscv64": "0.27.0", "@esbuild/linux-s390x": "0.27.0", "@esbuild/linux-x64": "0.27.0", "@esbuild/netbsd-arm64": "0.27.0", "@esbuild/netbsd-x64": "0.27.0", "@esbuild/openbsd-arm64": "0.27.0", "@esbuild/openbsd-x64": "0.27.0", "@esbuild/openharmony-arm64": "0.27.0", "@esbuild/sunos-x64": "0.27.0", "@esbuild/win32-arm64": "0.27.0", "@esbuild/win32-ia32": "0.27.0", "@esbuild/win32-x64": "0.27.0" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-jd0f4NHbD6cALCyGElNpGAOtWxSq46l9X/sWB0Nzd5er4Kz2YTm+Vl0qKFT9KUJvD8+fiO8AvoHhFvEatfVixA=="], - "escalade": ["escalade@3.2.0", "", {}, "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="], - "esm-env": ["esm-env@1.2.2", "", {}, "sha512-Epxrv+Nr/CaL4ZcFGPJIYLWFom+YeV1DqMLHJoEd9SYRxNbaFruBwfEX/kkHUJf55j2+TUbmDcmuilbP1TmXHA=="], - "esrap": ["esrap@2.2.9", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.4.15" }, "peerDependencies": { "@typescript-eslint/types": "^8.2.0" }, "optionalPeers": ["@typescript-eslint/types"] }, "sha512-4KijP+NxCWthMCUC3qHbE6n4vCjqgJS1uAYKhuT/GWfFTf1Qyive2TgOjep+gzbSzRfnNyaN/UU9YmdOt8Eg0A=="], + "esrap": ["esrap@2.4.0", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.4.15" }, "peerDependencies": { "@typescript-eslint/types": "^8.2.0" }, "optionalPeers": ["@typescript-eslint/types"] }, "sha512-CQPcneEHw/czj32uwQC52nYnax8H4SXt25FCM+LIwGZ49cMIxpOj3bP8wmzcI+Z0HIy5KkClaPhRLfCySzwHPA=="], - "eventsource-parser": ["eventsource-parser@3.0.6", "", {}, "sha512-Vo1ab+QXPzZ4tCa8SwIHJFaSzy4R6SHf7BY79rFBDf0idraZWAkYrDjDj8uWaSm3S2TK+hJ7/t1CEmZ7jXw+pg=="], + "eventsource-parser": ["eventsource-parser@3.1.1", "", {}, "sha512-EKN1vKAMcZ8MlYMpaNuxN6R9yakzH6uajHcHVTqWJzvu5pWw9DyhbP35HH8MVBQ+dZjAfDxk+A8NiR9KWaXiyQ=="], "fast-string-truncated-width": ["fast-string-truncated-width@3.0.3", "", {}, "sha512-0jjjIEL6+0jag3l2XWWizO64/aZVtpiGE3t0Zgqxv0DPuxiMjvB3M24fCyhZUO4KomJQPj3LTSUnDP3GpdwC0g=="], @@ -492,83 +375,55 @@ "fdir": ["fdir@6.5.0", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="], - "find-babel-config": ["find-babel-config@2.1.2", "", { "dependencies": { "json5": "^2.2.3" } }, "sha512-ZfZp1rQyp4gyuxqt1ZqjFGVeVBvmpURMqdIWXbPRfB97Bf6BzdK/xSIbylEINzQ0kB5tlDQfn9HkNXXWsqTqLg=="], - - "find-up": ["find-up@3.0.0", "", { "dependencies": { "locate-path": "^3.0.0" } }, "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg=="], - - "fs.realpath": ["fs.realpath@1.0.0", "", {}, "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw=="], - "fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="], - "function-bind": ["function-bind@1.1.2", "", {}, "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="], - - "gensync": ["gensync@1.0.0-beta.2", "", {}, "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg=="], - - "get-east-asian-width": ["get-east-asian-width@1.6.0", "", {}, "sha512-QRbvDIbx6YklUe6RxeTeleMR0yv3cYH6PsPZHcnVn7xv7zO1BHN8r0XETu8n6Ye3Q+ahtSarc3WgtNWmehIBfA=="], - "glob": ["glob@13.0.6", "", { "dependencies": { "minimatch": "^10.2.2", "minipass": "^7.1.3", "path-scurry": "^2.0.2" } }, "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw=="], "graceful-fs": ["graceful-fs@4.2.11", "", {}, "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="], - "hasown": ["hasown@2.0.2", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ=="], - - "html-entities": ["html-entities@2.3.3", "", {}, "sha512-DV5Ln36z34NNTDgnz0EWGBLZENelNAtkiFA4kyNOG2tDI6Mz1uSWiq1wAKdyjnJwyDiDO7Fa2SO1CTxPXL8VxA=="], - - "is-core-module": ["is-core-module@2.16.1", "", { "dependencies": { "hasown": "^2.0.2" } }, "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w=="], - "is-reference": ["is-reference@3.0.3", "", { "dependencies": { "@types/estree": "^1.0.6" } }, "sha512-ixkJoqQvAP88E6wLydLGGqCJsrFUnqoH6HnaczB8XmDH1oaWU+xxdptvikTgaEhtZ53Ky6YXiBuUI2WXLMCwjw=="], - "jackspeak": ["jackspeak@4.2.3", "", { "dependencies": { "@isaacs/cliui": "^9.0.0" } }, "sha512-ykkVRwrYvFm1nb2AJfKKYPr0emF6IiXDYUaFx4Zn9ZuIH7MrzEZ3sD5RlqGXNRpHtvUHJyOnCEFxOlNDtGo7wg=="], - - "jiti": ["jiti@2.6.1", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ=="], - - "js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="], - - "jsesc": ["jsesc@3.1.0", "", { "bin": { "jsesc": "bin/jsesc" } }, "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA=="], + "jiti": ["jiti@2.7.0", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ=="], "json-schema": ["json-schema@0.4.0", "", {}, "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA=="], - "json5": ["json5@2.2.3", "", { "bin": { "json5": "lib/cli.js" } }, "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg=="], - "jsonc-parser": ["jsonc-parser@3.3.1", "", {}, "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ=="], "kleur": ["kleur@4.1.5", "", {}, "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ=="], - "lightningcss": ["lightningcss@1.32.0", "", { "dependencies": { "detect-libc": "^2.0.3" }, "optionalDependencies": { "lightningcss-android-arm64": "1.32.0", "lightningcss-darwin-arm64": "1.32.0", "lightningcss-darwin-x64": "1.32.0", "lightningcss-freebsd-x64": "1.32.0", "lightningcss-linux-arm-gnueabihf": "1.32.0", "lightningcss-linux-arm64-gnu": "1.32.0", "lightningcss-linux-arm64-musl": "1.32.0", "lightningcss-linux-x64-gnu": "1.32.0", "lightningcss-linux-x64-musl": "1.32.0", "lightningcss-win32-arm64-msvc": "1.32.0", "lightningcss-win32-x64-msvc": "1.32.0" } }, "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ=="], + "lightningcss": ["lightningcss@1.33.0", "", { "dependencies": { "detect-libc": "^2.0.3" }, "optionalDependencies": { "lightningcss-android-arm64": "1.33.0", "lightningcss-darwin-arm64": "1.33.0", "lightningcss-darwin-x64": "1.33.0", "lightningcss-freebsd-x64": "1.33.0", "lightningcss-linux-arm-gnueabihf": "1.33.0", "lightningcss-linux-arm64-gnu": "1.33.0", "lightningcss-linux-arm64-musl": "1.33.0", "lightningcss-linux-x64-gnu": "1.33.0", "lightningcss-linux-x64-musl": "1.33.0", "lightningcss-win32-arm64-msvc": "1.33.0", "lightningcss-win32-x64-msvc": "1.33.0" } }, "sha512-WkUDrojuJs0xkgGf2udWxa3yGBRxPtxUkB79i6aCZLRgc7PM8fZe9TosfPDcvEpQZbuFASnHYmRLBLUbmLOIIA=="], - "lightningcss-android-arm64": ["lightningcss-android-arm64@1.32.0", "", { "os": "android", "cpu": "arm64" }, "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg=="], + "lightningcss-android-arm64": ["lightningcss-android-arm64@1.33.0", "", { "os": "android", "cpu": "arm64" }, "sha512-gEpRTalKdosp4Bb8qWtc2iOgE5SeIHlpS1up9bFq2wAyYhl1UdTObYiHe98zEM9SQvSoqQZ1IQD0JNpg3Ml5pg=="], - "lightningcss-darwin-arm64": ["lightningcss-darwin-arm64@1.32.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ=="], + "lightningcss-darwin-arm64": ["lightningcss-darwin-arm64@1.33.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-Sciaz8eenNTKn9b3t7+xr0ipTp9YxKQY4npwQ3mrRuL0BAVHBLyZxofhaKBAVtzmtRZ/zTyo0/to4B1uWG/Djg=="], - "lightningcss-darwin-x64": ["lightningcss-darwin-x64@1.32.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w=="], + "lightningcss-darwin-x64": ["lightningcss-darwin-x64@1.33.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-Z5UPAxzrjlWNNyGy6i65cJzzvgJ5D3T6wMvs+gWpY9d7qRhANrxqAp6LhxIgZhWEw18RfJTGcRxjuLIBr+m8XQ=="], - "lightningcss-freebsd-x64": ["lightningcss-freebsd-x64@1.32.0", "", { "os": "freebsd", "cpu": "x64" }, "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig=="], + "lightningcss-freebsd-x64": ["lightningcss-freebsd-x64@1.33.0", "", { "os": "freebsd", "cpu": "x64" }, "sha512-QQM/Ti/hQajJwCY+RiWuCZ9sdtI/XQk7nDK5vC8kkdwixezOlDgvDx7+RT+QjK6FcFT4MpsuoBnHIo/O3StRRg=="], - "lightningcss-linux-arm-gnueabihf": ["lightningcss-linux-arm-gnueabihf@1.32.0", "", { "os": "linux", "cpu": "arm" }, "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw=="], + "lightningcss-linux-arm-gnueabihf": ["lightningcss-linux-arm-gnueabihf@1.33.0", "", { "os": "linux", "cpu": "arm" }, "sha512-N7FVBe6iS24MlM6R/4RBTxGhQheZGs7tiQ9U32UtF75NzP5Q7xWPRqLBCKxlRQRk3rY1jCIPLzx7WzOhuUIRLQ=="], - "lightningcss-linux-arm64-gnu": ["lightningcss-linux-arm64-gnu@1.32.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ=="], + "lightningcss-linux-arm64-gnu": ["lightningcss-linux-arm64-gnu@1.33.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-j2v/itmy4HlNxlc6voKXYgBqNi0Ng2LShg4z7GufpEgs05P+2suBVyi9I6YHq5uoVFx9ETin3eCEhLVyXGQnKg=="], - "lightningcss-linux-arm64-musl": ["lightningcss-linux-arm64-musl@1.32.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg=="], + "lightningcss-linux-arm64-musl": ["lightningcss-linux-arm64-musl@1.33.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-yiO5ROMuYQgXbC60yjZU5CYSFZGKXL0HFATXt9mHJn1+zW55oCtMI9NfcVhYLMFDL7gV7oBPon/EmMMGg2OvtQ=="], - "lightningcss-linux-x64-gnu": ["lightningcss-linux-x64-gnu@1.32.0", "", { "os": "linux", "cpu": "x64" }, "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA=="], + "lightningcss-linux-x64-gnu": ["lightningcss-linux-x64-gnu@1.33.0", "", { "os": "linux", "cpu": "x64" }, "sha512-ar+Ju7LmcN0Jo4FpL4hpFybwNG9/3A/Br5KW2n2jyODg3MEZXaDYADdemoNS+BDNfMgKvylJLj4S5tyRActuAg=="], - "lightningcss-linux-x64-musl": ["lightningcss-linux-x64-musl@1.32.0", "", { "os": "linux", "cpu": "x64" }, "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg=="], + "lightningcss-linux-x64-musl": ["lightningcss-linux-x64-musl@1.33.0", "", { "os": "linux", "cpu": "x64" }, "sha512-RYiYbkokw0trfKqqzfF55lginwEPrD3OJDfTuJzFs1MK6iFnDenaz1fqLLtX4ITG3OktJQXOeTaw1awrBAlZPw=="], - "lightningcss-win32-arm64-msvc": ["lightningcss-win32-arm64-msvc@1.32.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw=="], + "lightningcss-win32-arm64-msvc": ["lightningcss-win32-arm64-msvc@1.33.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-1K+MPfLSFVpphzpdbfkhlWk6wBrTObBzS2T6db10PNOZgR9GoVsAWzwNyuhUYYbTp23j+4RrncfujZ4uAzXvwA=="], - "lightningcss-win32-x64-msvc": ["lightningcss-win32-x64-msvc@1.32.0", "", { "os": "win32", "cpu": "x64" }, "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q=="], + "lightningcss-win32-x64-msvc": ["lightningcss-win32-x64-msvc@1.33.0", "", { "os": "win32", "cpu": "x64" }, "sha512-OlEICDx/Xl0FqSp4bry8zFnCvGpig3Gl4gCquvYwHuqJKEC1+n9NgDniFvqHGmMv1ZkqDJrDqKKSykTDX+ehuA=="], "locate-character": ["locate-character@3.0.0", "", {}, "sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA=="], - "locate-path": ["locate-path@3.0.0", "", { "dependencies": { "p-locate": "^3.0.0", "path-exists": "^3.0.0" } }, "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A=="], + "lru-cache": ["lru-cache@11.5.3", "", {}, "sha512-U4N8FgzmWxc8k1VH8Kr6lQg18U7Fjvby6wXHVRX/ZZ7IwWbRMgrRbP0Wrb5q5NVinryp4SQampHKdvtecItxUg=="], - "lru-cache": ["lru-cache@5.1.1", "", { "dependencies": { "yallist": "^3.0.2" } }, "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w=="], + "lz-string": ["lz-string@1.5.0", "", { "bin": { "lz-string": "bin/bin.js" } }, "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ=="], "magic-string": ["magic-string@0.30.21", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" } }, "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ=="], - "marked": ["marked@17.0.1", "", { "bin": { "marked": "bin/marked.js" } }, "sha512-boeBdiS0ghpWcSwoNm/jJBwdpFaMnZWRzjA6SkUMYb40SVaN1x7mmfGKp0jvexGcx+7y2La5zRZsYFZI6Qpypg=="], - - "minimatch": ["minimatch@10.2.4", "", { "dependencies": { "brace-expansion": "^5.0.2" } }, "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg=="], + "minimatch": ["minimatch@10.2.6", "", { "dependencies": { "brace-expansion": "^5.0.8" } }, "sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A=="], "minipass": ["minipass@7.1.3", "", {}, "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A=="], @@ -576,27 +431,11 @@ "mrmime": ["mrmime@2.0.1", "", {}, "sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ=="], - "ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], + "nanoid": ["nanoid@3.3.20", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-uKdg2G3GNCKQn9byYOpxbGqrT2fGO5KRt5J/8b3pok8rT6qxGWF6hxMyJiEYtAf+FVyYuD9hRaDqX5uPFYJ4ZQ=="], - "nanoid": ["nanoid@3.3.12", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ=="], + "nypm": ["nypm@0.6.10", "", { "dependencies": { "citty": "^0.2.2", "pathe": "^2.0.3", "tinyexec": "^1.3.1" }, "bin": { "nypm": "./dist/cli.mjs" } }, "sha512-W72Hrj1petq+b3Hk2aAC+9zswetlIFTnDW4s5djseZh2nYqBbyQLOtj472HwcbcWykPBUW1WpWpjOd4nK6gMRw=="], - "node-releases": ["node-releases@2.0.27", "", {}, "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA=="], - - "nypm": ["nypm@0.6.6", "", { "dependencies": { "citty": "^0.2.2", "pathe": "^2.0.3", "tinyexec": "^1.1.1" }, "bin": { "nypm": "dist/cli.mjs" } }, "sha512-vRyr0r4cbBapw07Xw8xrj9Teq3o7MUD35rSaTcanDbW+aK2XHDgJFiU6ZTj2GBw7Q12ysdsyFss+Vdz4hQ0Y6Q=="], - - "obug": ["obug@2.1.1", "", {}, "sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ=="], - - "p-limit": ["p-limit@2.3.0", "", { "dependencies": { "p-try": "^2.0.0" } }, "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w=="], - - "p-locate": ["p-locate@3.0.0", "", { "dependencies": { "p-limit": "^2.0.0" } }, "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ=="], - - "p-try": ["p-try@2.2.0", "", {}, "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ=="], - - "parse5": ["parse5@7.3.0", "", { "dependencies": { "entities": "^6.0.0" } }, "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw=="], - - "path-exists": ["path-exists@3.0.0", "", {}, "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ=="], - - "path-parse": ["path-parse@1.0.7", "", {}, "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="], + "obug": ["obug@2.2.1", "", {}, "sha512-XrsrhT5sybtKI6wakr2SPOlGZWWYbUXZ7a0jT8/QOeAPau+1X/bSegNe5YR75oJmEZQbKningirmGOEJCIk61Q=="], "path-scurry": ["path-scurry@2.0.2", "", { "dependencies": { "lru-cache": "^11.0.0", "minipass": "^7.1.2" } }, "sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg=="], @@ -604,142 +443,106 @@ "picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="], - "picomatch": ["picomatch@4.0.4", "", {}, "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A=="], - - "pkg-up": ["pkg-up@3.1.0", "", { "dependencies": { "find-up": "^3.0.0" } }, "sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA=="], + "picomatch": ["picomatch@4.0.7", "", {}, "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA=="], - "postcss": ["postcss@8.5.15", "", { "dependencies": { "nanoid": "^3.3.12", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A=="], + "postcss": ["postcss@8.5.29", "", { "dependencies": { "nanoid": "^3.3.19", "picocolors": "^1.1.1", "source-map-js": "^1.2.2" } }, "sha512-49cGhUbXj8Qenv0iTMxA1cFBzxXoctpC9Ujd77t1WcbJIr6nF/eI7g/8MgxrYldFRuAXvja7xQRwavoW7kgrxQ=="], - "prettier": ["prettier@3.8.1", "", { "bin": { "prettier": "bin/prettier.cjs" } }, "sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg=="], + "prettier": ["prettier@3.9.9", "", { "bin": { "prettier": "bin/prettier.cjs" } }, "sha512-Z/CJHIkdujO/OtN7nXUii0Rf3VT5SRuhjBA82Xvu2XhBUgX3nhP67T0LHceBdQLex7OOFGTox+Q5Yg8Jk2Qivg=="], "readdirp": ["readdirp@4.1.2", "", {}, "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg=="], - "reselect": ["reselect@4.1.8", "", {}, "sha512-ab9EmR80F/zQTMNeneUr4cv+jSwPJgIlvEmVwLerwrWVbpLlBuls9XHzIeTFy4cegU2NHBp3va0LKOzU5qFEYQ=="], - - "resolve": ["resolve@1.22.11", "", { "dependencies": { "is-core-module": "^2.16.1", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" } }, "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ=="], + "rolldown": ["rolldown@1.2.13", "", { "dependencies": { "@oxc-project/types": "=0.153.0", "@rolldown/pluginutils": "^1.0.0" }, "optionalDependencies": { "@rolldown/binding-android-arm-eabi": "1.2.13", "@rolldown/binding-android-arm64": "1.2.13", "@rolldown/binding-darwin-arm64": "1.2.13", "@rolldown/binding-darwin-x64": "1.2.13", "@rolldown/binding-freebsd-x64": "1.2.13", "@rolldown/binding-linux-arm-gnueabihf": "1.2.13", "@rolldown/binding-linux-arm64-gnu": "1.2.13", "@rolldown/binding-linux-arm64-musl": "1.2.13", "@rolldown/binding-linux-ppc64-gnu": "1.2.13", "@rolldown/binding-linux-s390x-gnu": "1.2.13", "@rolldown/binding-linux-x64-gnu": "1.2.13", "@rolldown/binding-linux-x64-musl": "1.2.13", "@rolldown/binding-openharmony-arm64": "1.2.13", "@rolldown/binding-win32-arm64-msvc": "1.2.13", "@rolldown/binding-win32-x64-msvc": "1.2.13" }, "bin": { "rolldown": "./bin/cli.mjs" } }, "sha512-/5apOkwWxtACcQtZ5WlrAsKS4JwXVThQPJZ3RqIruRY9UG9M8e0VlXN068xum/JRKA4aIUoJ13ghe/kGCvidiQ=="], - "rolldown": ["rolldown@1.0.2", "", { "dependencies": { "@oxc-project/types": "=0.132.0", "@rolldown/pluginutils": "^1.0.0" }, "optionalDependencies": { "@rolldown/binding-android-arm64": "1.0.2", "@rolldown/binding-darwin-arm64": "1.0.2", "@rolldown/binding-darwin-x64": "1.0.2", "@rolldown/binding-freebsd-x64": "1.0.2", "@rolldown/binding-linux-arm-gnueabihf": "1.0.2", "@rolldown/binding-linux-arm64-gnu": "1.0.2", "@rolldown/binding-linux-arm64-musl": "1.0.2", "@rolldown/binding-linux-ppc64-gnu": "1.0.2", "@rolldown/binding-linux-s390x-gnu": "1.0.2", "@rolldown/binding-linux-x64-gnu": "1.0.2", "@rolldown/binding-linux-x64-musl": "1.0.2", "@rolldown/binding-openharmony-arm64": "1.0.2", "@rolldown/binding-wasm32-wasi": "1.0.2", "@rolldown/binding-win32-arm64-msvc": "1.0.2", "@rolldown/binding-win32-x64-msvc": "1.0.2" }, "bin": { "rolldown": "./bin/cli.mjs" } }, "sha512-oZx5zVDtVB44AW3eaifgDml1gWRDZGvjcfdxonE4swNPG98PrrXjaO/KrnUjzlMnztCCRVlUueA1kCXhARGk6g=="], - - "runed": ["runed@0.31.1", "", { "dependencies": { "esm-env": "^1.0.0" }, "peerDependencies": { "svelte": "^5.7.0" } }, "sha512-v3czcTnO+EJjiPvD4dwIqfTdHLZ8oH0zJheKqAHh9QMViY7Qb29UlAMRpX7ZtHh7AFqV60KmfxaJ9QMy+L1igQ=="], - - "s-js": ["s-js@0.4.9", "", {}, "sha512-RtpOm+cM6O0sHg6IA70wH+UC3FZcND+rccBZpBAHzlUgNO2Bm5BN+FnM8+OBxzXdwpKWFwX11JGF0MFRkhSoIQ=="], + "runed": ["runed@0.37.1", "", { "dependencies": { "dequal": "^2.0.3", "esm-env": "^1.0.0", "lz-string": "^1.5.0" }, "peerDependencies": { "@sveltejs/kit": "^2.21.0", "svelte": "^5.7.0", "zod": "^4.1.0" }, "optionalPeers": ["@sveltejs/kit", "zod"] }, "sha512-MeFY73xBW8IueWBm012nNFIGy19WUGPLtknavyUPMpnyt350M47PhGSGrGoSLbidwn+Zlt/O0cp8/OZE3LASWA=="], "sade": ["sade@1.8.1", "", { "dependencies": { "mri": "^1.1.0" } }, "sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A=="], - "semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="], - - "seroval": ["seroval@1.5.0", "", {}, "sha512-OE4cvmJ1uSPrKorFIH9/w/Qwuvi/IMcGbv5RKgcJ/zjA/IohDLU6SVaxFN9FwajbP7nsX0dQqMDes1whk3y+yw=="], - - "seroval-plugins": ["seroval-plugins@1.5.0", "", { "peerDependencies": { "seroval": "^1.0" } }, "sha512-EAHqADIQondwRZIdeW2I636zgsODzoBDwb3PT/+7TLDWyw1Dy/Xv7iGUIEXXav7usHDE9HVhOU61irI3EnyyHA=="], - - "set-cookie-parser": ["set-cookie-parser@3.0.1", "", {}, "sha512-n7Z7dXZhJbwuAHhNzkTti6Aw9QDDjZtm3JTpTGATIdNzdQz5GuFs22w90BcvF4INfnrL5xrX3oGsuqO5Dx3A1Q=="], + "set-cookie-parser": ["set-cookie-parser@3.1.3", "", {}, "sha512-xletSdAI5efyVJjsatBbzDkXl3M/Zmad3xfnQe7ZXuBB0yNwW7C2WFsNGjdRNfZunmNHQuX79xs/ACMTLCdcew=="], "sirv": ["sirv@3.0.2", "", { "dependencies": { "@polka/url": "^1.0.0-next.24", "mrmime": "^2.0.0", "totalist": "^3.0.0" } }, "sha512-2wcC/oGxHis/BoHkkPwldgiPSYcpZK3JU28WoMVv55yHJgcZ8rlXvuG9iZggz+sU1d4bRgIGASwyWqjxu3FM0g=="], "sisteransi": ["sisteransi@1.0.5", "", {}, "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg=="], - "solid-js": ["solid-js@1.9.13", "", { "dependencies": { "csstype": "^3.1.0", "seroval": "~1.5.0", "seroval-plugins": "~1.5.0" } }, "sha512-6hJeJMOcEX8ktqjpDoJZEmld3ijvcvWBDtiXBm7f4332SiFN66QeAQI1REQshvyUoISsSeJ4PHDauKYbwao9JQ=="], - - "source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="], - - "string-width": ["string-width@7.2.0", "", { "dependencies": { "emoji-regex": "^10.3.0", "get-east-asian-width": "^1.0.0", "strip-ansi": "^7.1.0" } }, "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ=="], + "source-map-js": ["source-map-js@1.2.2", "", {}, "sha512-KGj/8Y43x35aZVDtt+J4mK1hoLGHULMYfSkODJNQjNDC3oW1PqPoxMwo0pLUsWM/UEGzON/NxeHywEfNXNP3Vw=="], - "strip-ansi": ["strip-ansi@7.1.2", "", { "dependencies": { "ansi-regex": "^6.0.1" } }, "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA=="], + "svelte": ["svelte@5.57.2", "", { "dependencies": { "@jridgewell/remapping": "^2.3.5", "@jridgewell/sourcemap-codec": "^1.6.0", "@sveltejs/acorn-typescript": "^1.0.13", "@types/estree": "^1.0.9", "acorn": "^8.18.0", "aria-query": "5.3.1", "axobject-query": "^4.1.0", "clsx": "^2.1.1", "devalue": "^5.9.2", "esm-env": "^1.2.1", "esrap": "^2.3.6", "is-reference": "^3.0.3", "locate-character": "^3.0.0", "magic-string": "^0.30.11", "zimmerframe": "^1.1.2" } }, "sha512-1YiPCAXT0Ct6g1vOMmSLa0Nsc3QRMvtDF32ihgglGVrugIuIK38RyDiFNXVDhtQ81TJjSO57NdMw6DyYLqSGDQ=="], - "supports-preserve-symlinks-flag": ["supports-preserve-symlinks-flag@1.0.0", "", {}, "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w=="], + "svelte-check": ["svelte-check@4.7.6", "", { "dependencies": { "@jridgewell/trace-mapping": "^0.3.25", "@sveltejs/load-config": "^0.2.3", "chokidar": "^4.0.1", "fdir": "^6.2.0", "picocolors": "^1.0.0", "sade": "^1.7.4" }, "peerDependencies": { "svelte": "^4.0.0 || ^5.0.0-next.0", "typescript": "^5.0.0 || ^6.0.0" }, "bin": { "svelte-check": "bin/svelte-check" } }, "sha512-t2scM//ZuVbSY/T2w6FSBw1v9s2NEmh/g+sy1lqtosW5ylBV5AF4wFb1Ts9Kf3MbfPDUDJDZ9L436YT0SPTdvw=="], - "svelte": ["svelte@5.55.10", "", { "dependencies": { "@jridgewell/remapping": "^2.3.4", "@jridgewell/sourcemap-codec": "^1.5.0", "@sveltejs/acorn-typescript": "^1.0.10", "@types/estree": "^1.0.5", "@types/trusted-types": "^2.0.7", "acorn": "^8.12.1", "aria-query": "5.3.1", "axobject-query": "^4.1.0", "clsx": "^2.1.1", "devalue": "^5.8.1", "esm-env": "^1.2.1", "esrap": "^2.2.9", "is-reference": "^3.0.3", "locate-character": "^3.0.0", "magic-string": "^0.30.11", "zimmerframe": "^1.1.2" } }, "sha512-v9mFVBY1USosyIWdXE7Cg4AN0ywyKCMcAhONvli8doMowEhFhMdNLKD1j7O/UnsrdVTHaUOk/jv8hD/HClVy+g=="], + "tailwindcss": ["tailwindcss@4.3.3", "", {}, "sha512-gOhV3P7ufE62QDGg1zVaTgCR+EtPv92k2nIhVcVKcLmxT1sUBsQGhnZj175j+MqRt4zLF7ic+sCYjfhxMxj7YQ=="], - "svelte-check": ["svelte-check@4.4.8", "", { "dependencies": { "@jridgewell/trace-mapping": "^0.3.25", "chokidar": "^4.0.1", "fdir": "^6.2.0", "picocolors": "^1.0.0", "sade": "^1.7.4" }, "peerDependencies": { "svelte": "^4.0.0 || ^5.0.0-next.0", "typescript": ">=5.0.0" }, "bin": { "svelte-check": "bin/svelte-check" } }, "sha512-67adfgBox5eNSNIvIIwgFizKGdcRrGpiMoNO2obHcYuLz7iTa8Xgm/NGU3ntMFnNm8K1grFOIG6HhMLX/vcN8w=="], + "tapable": ["tapable@2.3.3", "", {}, "sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A=="], - "tailwindcss": ["tailwindcss@4.2.2", "", {}, "sha512-KWBIxs1Xb6NoLdMVqhbhgwZf2PGBpPEiwOqgI4pFIYbNTfBXiKYyWoTsXgBQ9WFg/OlhnvHaY+AEpW7wSmFo2Q=="], + "tinyexec": ["tinyexec@1.3.1", "", {}, "sha512-GCvB3aoys96IuDFBMcTB46JOR6mdMtAToqwiW8JlWhsoh1mhHi/xn9ss/Dg7N555GiJyEt2qzoG/NHCwM6h1EA=="], - "tapable": ["tapable@2.3.0", "", {}, "sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg=="], - - "tinyexec": ["tinyexec@1.2.2", "", {}, "sha512-M/Q0B2cp4K7kynaT/vnED1j8TlLY+Pp7C6Wl2bl/7u/F0mUVwdyOpwomQb8JpYLitHUssAJRmLZdMCGsrx7i+g=="], - - "tinyglobby": ["tinyglobby@0.2.16", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.4" } }, "sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg=="], + "tinyglobby": ["tinyglobby@0.2.17", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.4" } }, "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g=="], "totalist": ["totalist@3.0.1", "", {}, "sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ=="], - "tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], - "tui": ["tui@workspace:apps/tui"], - "turbo": ["turbo@2.8.20", "", { "optionalDependencies": { "@turbo/darwin-64": "2.8.20", "@turbo/darwin-arm64": "2.8.20", "@turbo/linux-64": "2.8.20", "@turbo/linux-arm64": "2.8.20", "@turbo/windows-64": "2.8.20", "@turbo/windows-arm64": "2.8.20" }, "bin": { "turbo": "bin/turbo" } }, "sha512-Rb4qk5YT8RUwwdXtkLpkVhNEe/lor6+WV7S5tTlLpxSz6MjV5Qi8jGNn4gS6NAvrYGA/rNrE6YUQM85sCZUDbQ=="], + "turbo": ["turbo@2.11.7", "", { "optionalDependencies": { "@turbo/darwin-64": "2.11.7", "@turbo/darwin-arm64": "2.11.7", "@turbo/linux-64": "2.11.7", "@turbo/linux-arm64": "2.11.7", "@turbo/windows-64": "2.11.7", "@turbo/windows-arm64": "2.11.7" }, "bin": { "turbo": "bin/turbo" } }, "sha512-RhXHbqfvY6o3xt/zSHCwSNwLrZlXiqHzewi4eAj9PtZ1T1CqgMJ2yIDb7SKPTG9IikTm925mfzL6hldQVdyNWw=="], "typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="], "ultracite": ["ultracite@7.3.2", "", { "dependencies": { "@clack/prompts": "^1.1.0", "commander": "^14.0.3", "deepmerge": "^4.3.1", "glob": "^13.0.6", "jsonc-parser": "^3.3.1", "nypm": "^0.6.5" }, "peerDependencies": { "oxlint": "^1.0.0" }, "optionalPeers": ["oxlint"], "bin": { "ultracite": "dist/index.js" } }, "sha512-JV2TdeBDc1aaj3wFzXLjRcBlUZkXBdShMLAjHh9YLK0rXPSK42CzHqfWXUfibFUAUiUGNwoOFnIpS45EaJAcdg=="], - "undici-types": ["undici-types@7.18.2", "", {}, "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w=="], + "undici": ["undici@6.29.0", "", {}, "sha512-R+RODBqp6i2pPflGdq+xIOUkl+RNfGgHwoinecKu/JCuf2uO06cOKoDbI2P7Dn6KcswdKwrczbU6IYJ6K8X+wg=="], - "update-browserslist-db": ["update-browserslist-db@1.2.3", "", { "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" }, "peerDependencies": { "browserslist": ">= 4.21.0" }, "bin": { "update-browserslist-db": "cli.js" } }, "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w=="], + "undici-types": ["undici-types@7.24.6", "", {}, "sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg=="], - "vite": ["vite@8.0.14", "", { "dependencies": { "lightningcss": "^1.32.0", "picomatch": "^4.0.4", "postcss": "^8.5.15", "rolldown": "1.0.2", "tinyglobby": "^0.2.16" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "@vitejs/devtools": "^0.1.18", "esbuild": "^0.27.0 || ^0.28.0", "jiti": ">=1.21.0", "less": "^4.0.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "@vitejs/devtools", "esbuild", "jiti", "less", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-s4BJJ+5y1pYL6Otw51FHhVJQhPnuRinKig64g/1+EUNaJsd3gCKdD31IPFvswUgW9/60QT9oFHbZHbQK5imcxw=="], + "vite": ["vite@8.3.4", "", { "dependencies": { "lightningcss": "^1.33.0", "picomatch": "^4.0.7", "postcss": "^8.5.29", "rolldown": "~1.2.12", "tinyglobby": "^0.2.17" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "@vitejs/devtools": "^0.7.1", "esbuild": "^0.27.0 || ^0.28.0", "jiti": ">=1.21.0", "less": "^4.0.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "@vitejs/devtools", "esbuild", "jiti", "less", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-IftLE8phJhdNRIGNEV4JCCvfNScoZQVfcXaN8TQropWGJtOHcu+iAB9pHcuGvmEb/kddw0LBe/jS8ztdC6xdiQ=="], - "vitefu": ["vitefu@1.1.2", "", { "peerDependencies": { "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-beta.0" }, "optionalPeers": ["vite"] }, "sha512-zpKATdUbzbsycPFBN71nS2uzBUQiVnFoOrr2rvqv34S1lcAgMKKkjWleLGeiJlZ8lwCXvtWaRn7R3ZC16SYRuw=="], + "vitefu": ["vitefu@1.1.3", "", { "peerDependencies": { "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0" }, "optionalPeers": ["vite"] }, "sha512-ub4okH7Z5KLjb6hDyjqrGXqWtWvoYdU3IGm/NorpgHncKoLTCfRIbvlhBm7r0YstIaQRYlp4yEbFqDcKSzXSSg=="], "web": ["web@workspace:apps/web"], - "web-tree-sitter": ["web-tree-sitter@0.25.10", "", { "peerDependencies": { "@types/emscripten": "^1.40.0" }, "optionalPeers": ["@types/emscripten"] }, "sha512-Y09sF44/13XvgVKgO2cNDw5rGk6s26MgoZPXLESvMXeefBf7i6/73eFurre0IsTW6E14Y0ArIzhUMmjoc7xyzA=="], - - "ws": ["ws@8.18.0", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw=="], - - "yallist": ["yallist@3.1.1", "", {}, "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="], - - "yaml": ["yaml@2.9.0", "", { "bin": { "yaml": "bin.mjs" } }, "sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA=="], - - "yoga-layout": ["yoga-layout@3.2.1", "", {}, "sha512-0LPOt3AxKqMdFBZA3HBAt/t/8vIKq7VaQYbuA8WxCgung+p9TVyKRYdpvCb80HcdTN2NkbIKbhNwKUfm3tQywQ=="], - - "zimmerframe": ["zimmerframe@1.1.4", "", {}, "sha512-B58NGBEoc8Y9MWWCQGl/gq9xBCe4IiKM0a2x7GZdQKOW5Exr8S1W24J6OgM1njK8xCRGvAJIL/MxXHf6SkmQKQ=="], - - "zod": ["zod@4.3.6", "", {}, "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg=="], + "ws": ["ws@8.21.0", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g=="], - "@cable-intel/backend/zod": ["zod@4.4.3", "", {}, "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ=="], + "zimmerframe": ["zimmerframe@1.1.5", "", {}, "sha512-msJxIvYDYcoNL+PJsu+7qmpDWsYmAxTY+2TNYXXF0hzBzBk0BMecOqDOG/EckUoKCuKwObfbugIl8QpqHDXeFA=="], - "@cable-intel/env/zod": ["zod@4.4.3", "", {}, "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ=="], + "zod": ["zod@4.6.5", "", {}, "sha512-v5l/aFXZQeai4awLbOpSoHecE9UiMrnfx75tEXLjNonXVARxQ5mOeipTjROUchszUNCqnE+hqAMujRsRHsut2Q=="], - "@opentui/solid/@babel/core": ["@babel/core@7.28.0", "", { "dependencies": { "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.0", "@babel/helper-compilation-targets": "^7.27.2", "@babel/helper-module-transforms": "^7.27.3", "@babel/helpers": "^7.27.6", "@babel/parser": "^7.28.0", "@babel/template": "^7.27.2", "@babel/traverse": "^7.28.0", "@babel/types": "^7.28.0", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", "json5": "^2.2.3", "semver": "^6.3.1" } }, "sha512-UlLAnTPrFdNGoFtbSXwcGFQBtQZJCNjaN6hQNP3UPvuNXT1i82N26KL3dZeIpNalWywr9IuQuncaAfUaS1g6sQ=="], + "@sveltejs/vite-plugin-svelte/magic-string": ["magic-string@1.4.3", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.6.0" } }, "sha512-z12OxmaPGE0F4xlpGdjuAUckipLpQlTAuAmyGhNoD7ODpYUzvDfvtxUbjM/jwznyP178oju/KDdyi220wNJ0RQ=="], - "@opentui/solid/@babel/preset-typescript": ["@babel/preset-typescript@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", "@babel/helper-validator-option": "^7.27.1", "@babel/plugin-syntax-jsx": "^7.27.1", "@babel/plugin-transform-modules-commonjs": "^7.27.1", "@babel/plugin-transform-typescript": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-l7WfQfX0WK4M0v2RudjuQK4u99BS6yLHYEmdtVPP7lKV013zr9DygFuWNlnbvQ9LR+LS0Egz/XAvGx5U9MX0fQ=="], + "@tailwindcss/node/lightningcss": ["lightningcss@1.32.0", "", { "dependencies": { "detect-libc": "^2.0.3" }, "optionalDependencies": { "lightningcss-android-arm64": "1.32.0", "lightningcss-darwin-arm64": "1.32.0", "lightningcss-darwin-x64": "1.32.0", "lightningcss-freebsd-x64": "1.32.0", "lightningcss-linux-arm-gnueabihf": "1.32.0", "lightningcss-linux-arm64-gnu": "1.32.0", "lightningcss-linux-arm64-musl": "1.32.0", "lightningcss-linux-x64-gnu": "1.32.0", "lightningcss-linux-x64-musl": "1.32.0", "lightningcss-win32-arm64-msvc": "1.32.0", "lightningcss-win32-x64-msvc": "1.32.0" } }, "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ=="], - "@tailwindcss/oxide-wasm32-wasi/@emnapi/core": ["@emnapi/core@1.8.1", "", { "dependencies": { "@emnapi/wasi-threads": "1.1.0", "tslib": "^2.4.0" }, "bundled": true }, "sha512-AvT9QFpxK0Zd8J0jopedNm+w/2fIzvtPKPjqyw9jwvBaReTTqPBk9Hixaz7KbjimP+QNz605/XnjFcDAL2pqBg=="], + "@tailwindcss/oxide-wasm32-wasi/@emnapi/core": ["@emnapi/core@1.11.3", "", { "dependencies": { "@emnapi/wasi-threads": "1.2.3", "tslib": "^2.4.0" }, "bundled": true }, "sha512-zLpS5asjEb7lq8jYLq37N6XKaE41DIexlY1rF/z4/tIl3wo13Sqm28fRyfIsKZD+NZ8mM5RoKkpW/rBcuoSZSg=="], - "@tailwindcss/oxide-wasm32-wasi/@emnapi/runtime": ["@emnapi/runtime@1.8.1", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-mehfKSMWjjNol8659Z8KxEMrdSJDDot5SXMq00dM8BN4o+CLNXQ0xH2V7EchNHV4RmbZLmmPdEaXZc5H2FXmDg=="], + "@tailwindcss/oxide-wasm32-wasi/@emnapi/runtime": ["@emnapi/runtime@1.11.3", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-Xz4Tpyki7XyrpbUK1jR1AhdAdaXyhhY4lZ3neLodmhpuWfy2PAQN5B46sAiU4liOXGLkHypn/qU+jvfWSCYYLA=="], - "@tailwindcss/oxide-wasm32-wasi/@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.1.0", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ=="], + "@tailwindcss/oxide-wasm32-wasi/@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.2.3", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-ELEBe8PsLvvJ6QMr0zLt8ffvOHW/dc1m3CEzNMg7aJUv3bMaoDtw2TXyDAwkYBuroxxuHEwhRTLJSe5sya547g=="], - "@tailwindcss/oxide-wasm32-wasi/@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@1.1.1", "", { "dependencies": { "@emnapi/core": "^1.7.1", "@emnapi/runtime": "^1.7.1", "@tybys/wasm-util": "^0.10.1" }, "bundled": true }, "sha512-p64ah1M1ld8xjWv3qbvFwHiFVWrq1yFvV4f7w+mzaqiR4IlSgkqhcRdHwsGgomwzBH51sRY4NEowLxnaBjcW/A=="], + "@tailwindcss/oxide-wasm32-wasi/@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@1.1.4", "", { "dependencies": { "@tybys/wasm-util": "^0.10.1" }, "peerDependencies": { "@emnapi/core": "^1.7.1", "@emnapi/runtime": "^1.7.1" }, "bundled": true }, "sha512-3NQNNgA1YSlJb/kMH1ildASP9HW7/7kYnRI2szWJaofaS1hWmbGI4H+d3+22aGzXXN9IJ+n+GiFVcGipJP18ow=="], - "@tailwindcss/oxide-wasm32-wasi/@tybys/wasm-util": ["@tybys/wasm-util@0.10.1", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg=="], + "@tailwindcss/oxide-wasm32-wasi/@tybys/wasm-util": ["@tybys/wasm-util@0.10.4", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-W3c4gRigFS0T/Ma4qIYF3GDAc5AQdHb1yL5znJT1Zv1YaD9Kitx656wBjvr19qbiosmZT8lWDM5BEMynUqX65A=="], "@tailwindcss/oxide-wasm32-wasi/tslib": ["tslib@2.8.1", "", { "bundled": true }, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], - "babel-plugin-jsx-dom-expressions/@babel/helper-module-imports": ["@babel/helper-module-imports@7.18.6", "", { "dependencies": { "@babel/types": "^7.18.6" } }, "sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA=="], + "bun-types/@types/node": ["@types/node@26.6.5", "", { "dependencies": { "undici-types": "~8.9.0" } }, "sha512-uMzsv9stDdofIZBx4UGh4Z9ZgXnGC9seRDscilBcTHAeOFk67MZg/k9bWVfO+bk4qZmG+l52hRnLwUiH2LnJTg=="], - "babel-plugin-module-resolver/glob": ["glob@9.3.5", "", { "dependencies": { "fs.realpath": "^1.0.0", "minimatch": "^8.0.2", "minipass": "^4.2.4", "path-scurry": "^1.6.1" } }, "sha512-e1LleDykUz2Iu+MTYdkSsuWX8lvAjAcs0Xef0lNIu0S2wOAzuTxCJtcd9S3cijlwYF18EsU3rzb8jPVobxDh9Q=="], + "@tailwindcss/node/lightningcss/lightningcss-android-arm64": ["lightningcss-android-arm64@1.32.0", "", { "os": "android", "cpu": "arm64" }, "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg=="], - "parse5/entities": ["entities@6.0.1", "", {}, "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g=="], + "@tailwindcss/node/lightningcss/lightningcss-darwin-arm64": ["lightningcss-darwin-arm64@1.32.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ=="], - "path-scurry/lru-cache": ["lru-cache@11.2.6", "", {}, "sha512-ESL2CrkS/2wTPfuend7Zhkzo2u0daGJ/A2VucJOgQ/C48S/zB8MMeMHSGKYpXhIjbPxfuezITkaBH1wqv00DDQ=="], + "@tailwindcss/node/lightningcss/lightningcss-darwin-x64": ["lightningcss-darwin-x64@1.32.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w=="], - "svelte/acorn": ["acorn@8.15.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg=="], + "@tailwindcss/node/lightningcss/lightningcss-freebsd-x64": ["lightningcss-freebsd-x64@1.32.0", "", { "os": "freebsd", "cpu": "x64" }, "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig=="], - "web/zod": ["zod@4.4.3", "", {}, "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ=="], + "@tailwindcss/node/lightningcss/lightningcss-linux-arm-gnueabihf": ["lightningcss-linux-arm-gnueabihf@1.32.0", "", { "os": "linux", "cpu": "arm" }, "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw=="], - "babel-plugin-module-resolver/glob/minimatch": ["minimatch@8.0.4", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-W0Wvr9HyFXZRGIDgCicunpQ299OKXs9RgZfaukz4qAW/pJhcpUfupc9c+OObPOFueNy8VSrZgEmDtk6Kh4WzDA=="], + "@tailwindcss/node/lightningcss/lightningcss-linux-arm64-gnu": ["lightningcss-linux-arm64-gnu@1.32.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ=="], - "babel-plugin-module-resolver/glob/minipass": ["minipass@4.2.8", "", {}, "sha512-fNzuVyifolSLFL4NzpF+wEF4qrgqaaKX0haXPQEdQ7NKAN+WecoKMHV09YcuL/DHxrUsYQOK3MiuDf7Ip2OXfQ=="], + "@tailwindcss/node/lightningcss/lightningcss-linux-arm64-musl": ["lightningcss-linux-arm64-musl@1.32.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg=="], - "babel-plugin-module-resolver/glob/path-scurry": ["path-scurry@1.11.1", "", { "dependencies": { "lru-cache": "^10.2.0", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" } }, "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA=="], + "@tailwindcss/node/lightningcss/lightningcss-linux-x64-gnu": ["lightningcss-linux-x64-gnu@1.32.0", "", { "os": "linux", "cpu": "x64" }, "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA=="], - "babel-plugin-module-resolver/glob/minimatch/brace-expansion": ["brace-expansion@2.0.2", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ=="], + "@tailwindcss/node/lightningcss/lightningcss-linux-x64-musl": ["lightningcss-linux-x64-musl@1.32.0", "", { "os": "linux", "cpu": "x64" }, "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg=="], - "babel-plugin-module-resolver/glob/path-scurry/lru-cache": ["lru-cache@10.4.3", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="], + "@tailwindcss/node/lightningcss/lightningcss-win32-arm64-msvc": ["lightningcss-win32-arm64-msvc@1.32.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw=="], - "babel-plugin-module-resolver/glob/path-scurry/minipass": ["minipass@7.1.2", "", {}, "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw=="], + "@tailwindcss/node/lightningcss/lightningcss-win32-x64-msvc": ["lightningcss-win32-x64-msvc@1.32.0", "", { "os": "win32", "cpu": "x64" }, "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q=="], - "babel-plugin-module-resolver/glob/minimatch/brace-expansion/balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="], + "bun-types/@types/node/undici-types": ["undici-types@8.9.0", "", {}, "sha512-KTDyRTYX8sWmKXAikPHHSyc63CRPETMctyjKFupcC6OBLXT3xsN0e9aF7m+mIXutFWpUXuedtowG7iLOzp0kQg=="], } } diff --git a/mise.toml b/mise.toml new file mode 100644 index 0000000..dfbca09 --- /dev/null +++ b/mise.toml @@ -0,0 +1,3 @@ +[tools] +node = "24.21.0" +bun = "1.4.2" diff --git a/package.json b/package.json index 0b01a84..57f11bb 100644 --- a/package.json +++ b/package.json @@ -2,34 +2,37 @@ "name": "cable-intel", "version": "0.1.28", "private": true, + "type": "module", "workspaces": { "packages": [ "apps/*", "packages/*" ], "catalog": { - "dotenv": "^17.3.1", - "zod": "^4.3.6", + "dotenv": "^17.4.2", + "zod": "^4.6.5", "typescript": "^5.9.3", - "convex": "^1.34.0" + "convex": "^1.46.0" } }, - "type": "module", "scripts": { - "dev": "turbo -F web -F @cable-intel/backend dev", "build": "turbo build", "build:vercel": "bash ./scripts/vercel-build.sh", - "seed:vercel-deployment": "bun run scripts/seed-vercel-deployment.ts", - "seed:realistic": "bun run scripts/seed-realistic-catalog.ts", + "check": "ultracite check", "check-types": "turbo check-types", - "dev:native": "turbo -F native dev", - "dev:web": "turbo -F web dev", + "dev": "turbo -F web -F @cable-intel/backend dev", "dev:server": "turbo -F @cable-intel/backend dev", "dev:setup": "turbo -F @cable-intel/backend dev:setup", - "check": "ultracite check", + "dev:web": "turbo -F web dev", "fix": "ultracite fix", "qa:browser": "bash ./scripts/browser/run-local-browser-qa.sh", - "qa:browser:smoke": "bash ./scripts/browser/agent-browser-smoke.sh" + "qa:browser:smoke": "bash ./scripts/browser/agent-browser-smoke.sh", + "seed:realistic": "bun run scripts/seed-realistic-catalog.ts", + "seed:vercel-deployment": "bun run scripts/seed-vercel-deployment.ts", + "test:unit": "bun test apps/web/src/lib packages/backend/convex/manualInferenceLogic.test.ts packages/backend/convex/catalogQuality.test.ts packages/backend/convex/config.test.ts packages/backend/convex/contracts packages/backend/convex/shopifyJsonEnrichment.test.ts packages/shopify-cable-source/src/text.test.ts packages/shopify-cable-source/src/connector-text.test.ts scripts/ci" + }, + "overrides": { + "cookie": "^0.7.2" }, "dependencies": { "@cable-intel/env": "workspace:*", @@ -39,9 +42,13 @@ "devDependencies": { "@biomejs/biome": "2.4.8", "@cable-intel/config": "workspace:*", - "turbo": "^2.8.20", + "agent-browser": "0.38.2", + "turbo": "^2.11.7", "typescript": "catalog:", "ultracite": "7.3.2" }, - "packageManager": "bun@1.3.11" + "packageManager": "bun@1.4.2", + "engines": { + "node": "24.x" + } } diff --git a/packages/backend/convex/config.test.ts b/packages/backend/convex/config.test.ts index 52229a4..98cce31 100644 --- a/packages/backend/convex/config.test.ts +++ b/packages/backend/convex/config.test.ts @@ -6,59 +6,35 @@ import { manualInferenceDefaults, } from "./config"; -const ORIGINAL_AI_GATEWAY_API_KEY = process.env.AI_GATEWAY_API_KEY; -const ORIGINAL_FIRECRAWL_API_KEY = process.env.FIRECRAWL_API_KEY; -const ORIGINAL_AI_SDK_TELEMETRY_ENABLED = process.env.AI_SDK_TELEMETRY_ENABLED; -const ORIGINAL_AI_SDK_TELEMETRY_RECORD_INPUTS = - process.env.AI_SDK_TELEMETRY_RECORD_INPUTS; -const ORIGINAL_AI_SDK_TELEMETRY_RECORD_OUTPUTS = - process.env.AI_SDK_TELEMETRY_RECORD_OUTPUTS; -const ORIGINAL_MANUAL_INFERENCE_MODEL = process.env.MANUAL_INFERENCE_MODEL; +const ENV_KEYS = [ + "AI_GATEWAY_API_KEY", + "FIRECRAWL_API_KEY", + "AI_SDK_TELEMETRY_ENABLED", + "AI_SDK_TELEMETRY_RECORD_INPUTS", + "AI_SDK_TELEMETRY_RECORD_OUTPUTS", + "MANUAL_INFERENCE_MODEL", +] as const; +const originalEnv = new Map(ENV_KEYS.map((key) => [key, process.env[key]])); + +function unsetEnv(key: (typeof ENV_KEYS)[number]): void { + // process.env stringifies undefined; absence requires deletion. + delete process.env[key]; +} afterEach(() => { - if (ORIGINAL_AI_GATEWAY_API_KEY === undefined) { - process.env.AI_GATEWAY_API_KEY = undefined; - } else { - process.env.AI_GATEWAY_API_KEY = ORIGINAL_AI_GATEWAY_API_KEY; - } - - if (ORIGINAL_FIRECRAWL_API_KEY === undefined) { - process.env.FIRECRAWL_API_KEY = undefined; - } else { - process.env.FIRECRAWL_API_KEY = ORIGINAL_FIRECRAWL_API_KEY; - } - - if (ORIGINAL_AI_SDK_TELEMETRY_ENABLED === undefined) { - process.env.AI_SDK_TELEMETRY_ENABLED = undefined; - } else { - process.env.AI_SDK_TELEMETRY_ENABLED = ORIGINAL_AI_SDK_TELEMETRY_ENABLED; - } - - if (ORIGINAL_AI_SDK_TELEMETRY_RECORD_INPUTS === undefined) { - process.env.AI_SDK_TELEMETRY_RECORD_INPUTS = undefined; - } else { - process.env.AI_SDK_TELEMETRY_RECORD_INPUTS = - ORIGINAL_AI_SDK_TELEMETRY_RECORD_INPUTS; - } - - if (ORIGINAL_AI_SDK_TELEMETRY_RECORD_OUTPUTS === undefined) { - process.env.AI_SDK_TELEMETRY_RECORD_OUTPUTS = undefined; - } else { - process.env.AI_SDK_TELEMETRY_RECORD_OUTPUTS = - ORIGINAL_AI_SDK_TELEMETRY_RECORD_OUTPUTS; - } - - if (ORIGINAL_MANUAL_INFERENCE_MODEL === undefined) { - process.env.MANUAL_INFERENCE_MODEL = undefined; - } else { - process.env.MANUAL_INFERENCE_MODEL = ORIGINAL_MANUAL_INFERENCE_MODEL; + for (const [key, value] of originalEnv) { + if (value === undefined) { + unsetEnv(key); + } else { + process.env[key] = value; + } } }); describe("getIngestConfig", () => { it("throws when required secrets are missing", () => { - process.env.AI_GATEWAY_API_KEY = undefined; - process.env.FIRECRAWL_API_KEY = undefined; + unsetEnv("AI_GATEWAY_API_KEY"); + unsetEnv("FIRECRAWL_API_KEY"); expect(() => getIngestConfig()).toThrow( "Missing required environment variable: AI_GATEWAY_API_KEY" @@ -68,9 +44,9 @@ describe("getIngestConfig", () => { it("returns required secrets with code defaults", () => { process.env.AI_GATEWAY_API_KEY = "test-ai-key"; process.env.FIRECRAWL_API_KEY = "test-firecrawl-key"; - process.env.AI_SDK_TELEMETRY_ENABLED = undefined; - process.env.AI_SDK_TELEMETRY_RECORD_INPUTS = undefined; - process.env.AI_SDK_TELEMETRY_RECORD_OUTPUTS = undefined; + unsetEnv("AI_SDK_TELEMETRY_ENABLED"); + unsetEnv("AI_SDK_TELEMETRY_RECORD_INPUTS"); + unsetEnv("AI_SDK_TELEMETRY_RECORD_OUTPUTS"); expect(getIngestConfig()).toEqual({ aiTelemetryEnabled: ingestDefaults.aiTelemetryEnabled, @@ -112,8 +88,8 @@ describe("getIngestConfig", () => { describe("getManualInferenceConfig", () => { it("requires AI gateway key", () => { - process.env.AI_GATEWAY_API_KEY = undefined; - process.env.MANUAL_INFERENCE_MODEL = undefined; + unsetEnv("AI_GATEWAY_API_KEY"); + unsetEnv("MANUAL_INFERENCE_MODEL"); expect(() => getManualInferenceConfig()).toThrow( "Missing required environment variable: AI_GATEWAY_API_KEY" @@ -122,10 +98,10 @@ describe("getManualInferenceConfig", () => { it("returns manual defaults when optional values are absent", () => { process.env.AI_GATEWAY_API_KEY = "manual-ai-key"; - process.env.MANUAL_INFERENCE_MODEL = undefined; - process.env.AI_SDK_TELEMETRY_ENABLED = undefined; - process.env.AI_SDK_TELEMETRY_RECORD_INPUTS = undefined; - process.env.AI_SDK_TELEMETRY_RECORD_OUTPUTS = undefined; + unsetEnv("MANUAL_INFERENCE_MODEL"); + unsetEnv("AI_SDK_TELEMETRY_ENABLED"); + unsetEnv("AI_SDK_TELEMETRY_RECORD_INPUTS"); + unsetEnv("AI_SDK_TELEMETRY_RECORD_OUTPUTS"); expect(getManualInferenceConfig()).toEqual({ aiGatewayApiKey: "manual-ai-key", diff --git a/packages/backend/convex/manualInferenceLogic.test.ts b/packages/backend/convex/manualInferenceLogic.test.ts index 1c217bc..c29a509 100644 --- a/packages/backend/convex/manualInferenceLogic.test.ts +++ b/packages/backend/convex/manualInferenceLogic.test.ts @@ -8,6 +8,11 @@ import { } from "./manualInferenceLogic"; describe("manualInferenceLogic", () => { + it("preserves the USB 3.2 Gen 1 data rate when inferring a cable", () => { + const result = inferDeterministic("USB-C to USB-C, USB 3.2 Gen 1"); + expect(result.patch.gbps).toBe("5"); + }); + it("extracts deterministic cable hints from free text", () => { const deterministic = inferDeterministic( "USB-C to USB-C braided cable, 240W PD, USB4, 8K 120Hz" @@ -52,8 +57,8 @@ describe("manualInferenceLogic", () => { usbGeneration: "USB4 / Thunderbolt 4", watts: "100", }, - uncertainties: ["video"], notes: "Likely USB4-class cable but video support unclear.", + uncertainties: ["video"], }); const merged = mergeInferenceSignals({ @@ -86,14 +91,14 @@ describe("manualInferenceLogic", () => { it("applies follow-up answer patch to the draft", () => { const question = { - id: "q-video-1", + applyIfNo: { videoSupport: "no" as const }, + applyIfSkip: {}, + applyIfYes: { videoSupport: "yes" as const }, category: "video" as const, - prompt: "Does packaging mention video output?", detail: "", + id: "q-video-1", + prompt: "Does packaging mention video output?", status: "pending" as const, - applyIfYes: { videoSupport: "yes" as const }, - applyIfNo: { videoSupport: "no" as const }, - applyIfSkip: {}, }; const answered = applyQuestionAnswerPatch( @@ -115,9 +120,9 @@ describe("manualInferenceLogic", () => { usbGeneration: " usb4 ", watts: 240, }, + extra: "ignored", notes: 12_345, uncertainties: ["Display", "charging"], - extra: "ignored", }); }).toThrow(); }); diff --git a/packages/backend/convex/manualInferenceLogic.ts b/packages/backend/convex/manualInferenceLogic.ts index db1f535..6c44c99 100644 --- a/packages/backend/convex/manualInferenceLogic.ts +++ b/packages/backend/convex/manualInferenceLogic.ts @@ -238,7 +238,8 @@ const GENERATION_HINTS: Array<{ { impliedGbps: "10", label: "USB 3.2 Gen 2", - regex: /\b(?:usb\s*(?:3\.2\s*gen\s*2|3\.1\s*gen\s*2)|(?:usb\s*)?3\.2)\b/i, + regex: + /\b(?:usb\s*(?:3\.2\s*gen\s*2|3\.1\s*gen\s*2)|(?:usb\s*)?3\.2(?!\s*gen\b))\b/i, }, { impliedGbps: "5", diff --git a/packages/backend/package.json b/packages/backend/package.json index fd16529..737cc00 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -5,20 +5,21 @@ "license": "ISC", "author": "", "scripts": { + "check-types": "tsc --noEmit -p convex/tsconfig.json", "dev": "convex dev", "dev:setup": "convex dev --configure --until-success" }, "dependencies": { "@cable-intel/shopify-cable-source": "workspace:*", - "ai": "^6.0.134", + "ai": "^6.0.301", "convex": "catalog:", "dotenv": "catalog:", "zod": "catalog:" }, "devDependencies": { "@cable-intel/config": "workspace:*", - "@types/node": "^25.5.0", - "convex-test": "^0.0.53", + "@types/node": "^24.19.1", + "convex-test": "^0.0.60", "typescript": "catalog:" } } diff --git a/packages/shopify-cable-source/package.json b/packages/shopify-cable-source/package.json index ada5060..0536375 100644 --- a/packages/shopify-cable-source/package.json +++ b/packages/shopify-cable-source/package.json @@ -12,6 +12,7 @@ }, "devDependencies": { "@cable-intel/config": "workspace:*", + "@types/node": "^24.19.1", "typescript": "catalog:" } } diff --git a/scripts/browser/agent-browser-smoke.sh b/scripts/browser/agent-browser-smoke.sh index c848d4e..f40cdad 100755 --- a/scripts/browser/agent-browser-smoke.sh +++ b/scripts/browser/agent-browser-smoke.sh @@ -6,9 +6,14 @@ BASE_URL="http://127.0.0.1:4173" OUT_DIR="" SESSION_NAME="cable-intel-qa-$(date +%s)" USE_BROWSERBASE="${USE_BROWSERBASE:-0}" +LOCAL_ONLY="0" while [[ $# -gt 0 ]]; do case "$1" in + --local-only) + LOCAL_ONLY="1" + shift + ;; --base-url) BASE_URL="$2" shift 2 @@ -43,11 +48,11 @@ BB_CONNECT_URL="" BB_REPLAY_URL="" ab() { - agent-browser --session "$SESSION_NAME" "$@" + bunx --no-install agent-browser --session "$SESSION_NAME" "$@" } ab_json() { - agent-browser --session "$SESSION_NAME" --json "$@" + bunx --no-install agent-browser --session "$SESSION_NAME" --json "$@" } cleanup() { @@ -107,44 +112,67 @@ if [[ "$USE_BROWSERBASE" == "1" ]]; then ab connect "$BB_CONNECT_URL" fi -ab open "$BASE_URL" -ab screenshot --full "$OUT_DIR/01-home.png" +ab open "$BASE_URL" >/dev/null +ab wait --text "Catalog Results" +ab screenshot --full body "$OUT_DIR/01-home.png" ab find role button click --name "Manual entry" -ab wait 900 -ab screenshot --full "$OUT_DIR/02-manual-entry.png" - -ab find label "Cable description" fill "usb c to usb c 240 3.2, 240w" -ab find role button click --name "Enter" -ab wait 1600 -ab screenshot --full "$OUT_DIR/03-after-enter.png" +ab wait --text "Live Cable Profile" +ab screenshot --full body "$OUT_DIR/02-manual-entry.png" + +if [[ "$LOCAL_ONLY" != "1" ]]; then + ab wait --fn "!document.body.innerText.includes('workspace data is reconnecting')" + ab find label "Cable description" fill "usb c to usb c 240w USB 3.2 Gen 1" + ab find role button click --name "Enter" --exact + ab wait --fn "document.querySelector('.manual-error') || document.body.innerText.includes('5Gbps')" + ab wait --fn "!Array.from(document.querySelectorAll('button')).some(b => b.textContent.trim() === 'Inferring...')" + ab_json eval "document.querySelector('.manual-error')?.textContent ?? ''" > "$OUT_DIR/inference-error.json" + jq -e '.data.result == ""' "$OUT_DIR/inference-error.json" >/dev/null +fi +ab screenshot --full body "$OUT_DIR/03-after-enter.png" +ab find label "USB generation marking" fill "USB 3.2 Gen 1" +ab wait --text "USB 3.2 Gen 1 (5Gbps)" ab find label "Printed wattage" fill "60, 100, 240W" -ab wait 350 -ab screenshot --full "$OUT_DIR/04-wattage-edit.png" - -ab find role button click --name "Reset" -ab wait 900 -ab screenshot --full "$OUT_DIR/05-after-reset.png" +ab wait --text "240W" +ab screenshot --full body "$OUT_DIR/04-wattage-edit.png" + +if [[ "$LOCAL_ONLY" != "1" ]]; then + ab find role button click --name "Reset" --exact + ab wait --fn "document.querySelector('textarea')?.value === ''" +else + # No backend exists in the local shell check; don't claim inference/reset proof. + ab find role button click --name "Catalog" --exact + ab wait --text "Catalog Results" + ab find role button click --name "Manual entry" --exact + ab wait --text "USB 3.2 Gen 1 (5Gbps)" +fi +ab screenshot --full body "$OUT_DIR/05-after-reset.png" ab_json snapshot -i >"$OUT_DIR/final-snapshot.json" -ab_json errors --clear >"$OUT_DIR/errors.json" -ab_json console --clear >"$OUT_DIR/console.json" +ab_json errors >"$OUT_DIR/errors.json" +ab_json console >"$OUT_DIR/console.json" ab_json get title >"$OUT_DIR/title.json" -ab_json get url >"$OUT_DIR/url.json" +# Never persist Vercel bypass query parameters in downloadable artifacts. +ab_json eval "location.origin + location.pathname" >"$OUT_DIR/url.json" +jq -e '.data.errors | length == 0' "$OUT_DIR/errors.json" >/dev/null +jq -e '[.data.messages[] | select(.type == "error")] | length == 0' "$OUT_DIR/console.json" >/dev/null +SAFE_BASE_URL="$(node -e 'const u = new URL(process.argv[1]); console.log(u.origin + u.pathname)' "$BASE_URL")" jq -n \ - --arg baseUrl "$BASE_URL" \ + --arg baseUrl "$SAFE_BASE_URL" \ --arg outDir "$OUT_DIR" \ --arg sessionName "$SESSION_NAME" \ + --arg localOnly "$LOCAL_ONLY" \ --arg browserbaseSessionId "$BB_SESSION_ID" \ --arg browserbaseReplayUrl "$BB_REPLAY_URL" \ '{ baseUrl: $baseUrl, + scope: (if $localOnly == "1" then "local manual fields; inference/reset not run" else "hosted inference and reset" end), outputDirectory: $outDir, sessionName: $sessionName, - browserbaseSessionId: ($browserbaseSessionId | select(length > 0)), - browserbaseReplayUrl: ($browserbaseReplayUrl | select(length > 0)), + browserbaseSessionId: (if $browserbaseSessionId == "" then null else $browserbaseSessionId end), + browserbaseReplayUrl: (if $browserbaseReplayUrl == "" then null else $browserbaseReplayUrl end), screenshots: [ "01-home.png", "02-manual-entry.png", @@ -157,7 +185,7 @@ jq -n \ cat >"$OUT_DIR/summary.md" </tmp/cable-intel-web-preview.log 2>&1 & + bunx --no-install vite preview --host "$HOST" --port "$PORT" --strictPort + ) >"$OUT_DIR/server.log" 2>&1 & PREVIEW_PID="$!" wait_for_url "$BASE_URL" fi @@ -110,6 +118,9 @@ SMOKE_ARGS=( --base-url "$BASE_URL" --out-dir "$OUT_DIR" ) +if [[ "$START_PREVIEW" == "1" ]]; then + SMOKE_ARGS+=(--local-only) +fi SMOKE_USE_BROWSERBASE="${USE_BROWSERBASE:-0}" if [[ "$SMOKE_USE_BROWSERBASE" == "1" ]]; then if is_local_base_url "$BASE_URL"; then diff --git a/scripts/ci/merge-gate-context.js b/scripts/ci/merge-gate-context.js new file mode 100644 index 0000000..b8b8bd5 --- /dev/null +++ b/scripts/ci/merge-gate-context.js @@ -0,0 +1,23 @@ +/** Resolve only the current PR head; deployments carry their SHA at the event root. */ +export async function resolveMergeGateContext(context, github) { + const pullRequest = context.payload.pull_request; + if (pullRequest) { + return { pullRequest, headSha: pullRequest.head.sha }; + } + + const headSha = context.payload.deployment?.sha; + if (!headSha) { + throw new Error("Deployment event is missing its commit SHA."); + } + const { data: associated } = + await github.rest.repos.listPullRequestsAssociatedWithCommit({ + ...context.repo, + commit_sha: headSha, + }); + const currentPullRequest = associated.find( + (candidate) => candidate.state === "open" && candidate.head.sha === headSha + ); + return currentPullRequest + ? { pullRequest: currentPullRequest, headSha } + : null; +} diff --git a/scripts/ci/merge-gate-context.test.ts b/scripts/ci/merge-gate-context.test.ts new file mode 100644 index 0000000..c8c7cbf --- /dev/null +++ b/scripts/ci/merge-gate-context.test.ts @@ -0,0 +1,56 @@ +import { expect, test } from "bun:test"; +import { resolveMergeGateContext } from "./merge-gate-context"; + +const pr = { number: 42, state: "open", head: { sha: "current-head" } }; +const repo = { owner: "owner", repo: "repo" }; + +function githubFor(pullRequests: (typeof pr)[]) { + return { + rest: { + repos: { + listPullRequestsAssociatedWithCommit: async () => ({ + data: pullRequests, + }), + }, + }, + }; +} + +test("uses the PR head for pull_request events", async () => { + expect( + await resolveMergeGateContext( + { repo, payload: { pull_request: pr } }, + githubFor([]) + ) + ).toEqual({ pullRequest: pr, headSha: "current-head" }); +}); + +test("deployment_status events must evaluate the deployed PR head", async () => { + expect( + await resolveMergeGateContext( + { + repo, + payload: { + deployment: { sha: "current-head" }, + deployment_status: { state: "success" }, + }, + }, + githubFor([pr]) + ) + ).toEqual({ pullRequest: pr, headSha: "current-head" }); +}); + +test("an old deployment cannot evaluate a newer PR head", async () => { + expect( + await resolveMergeGateContext( + { repo, payload: { deployment: { sha: "old-head" } } }, + githubFor([pr]) + ) + ).toBeNull(); +}); + +test("a malformed deployment event fails instead of passing the gate", async () => { + await expect( + resolveMergeGateContext({ repo, payload: {} }, githubFor([])) + ).rejects.toThrow("missing its commit SHA"); +}); diff --git a/vercel.json b/vercel.json index 35a3461..90c9316 100644 --- a/vercel.json +++ b/vercel.json @@ -1,5 +1,5 @@ { "$schema": "https://openapi.vercel.sh/vercel.json", - "installCommand": "bun install", + "installCommand": "bun install --frozen-lockfile", "buildCommand": "bun run build:vercel" } From 506b38f1d7e81c18e30fe391dd8d7093f718c518 Mon Sep 17 00:00:00 2001 From: Anand Pant Date: Sat, 10 Oct 2026 00:10:52 -0500 Subject: [PATCH 2/7] fix: bind gateway credentials and pin hosted Bun execution --- README.md | 2 +- package.json | 2 +- packages/backend/convex/gatewayModel.test.ts | 30 ++++++++++++++++++++ packages/backend/convex/gatewayModel.ts | 8 ++++++ packages/backend/convex/ingest.ts | 14 +++++---- packages/backend/convex/manualInference.ts | 5 ++-- scripts/vercel-build.sh | 8 ++++++ vercel.json | 4 +-- 8 files changed, 61 insertions(+), 12 deletions(-) create mode 100644 packages/backend/convex/gatewayModel.test.ts create mode 100644 packages/backend/convex/gatewayModel.ts diff --git a/README.md b/README.md index aac50c7..0847024 100644 --- a/README.md +++ b/README.md @@ -34,7 +34,7 @@ bun run dev:setup bun run dev ``` -Web app: `http://localhost:5173`. `mise.toml` pins Node 24.21.0 and Bun 1.4.2 for local work and CI; Vercel uses the matching Bun `packageManager` pin and Node 24 engine. +Web app: `http://localhost:5173`. `mise.toml` pins Node 24.21.0 and Bun 1.4.2 for local work and CI; Vercel uses an explicit `bunx bun@1.4.2` install/build command and the Node 24 engine. Its build script verifies the actual Bun version against `packageManager`. ## Testing And CI diff --git a/package.json b/package.json index 57f11bb..5c6fcb3 100644 --- a/package.json +++ b/package.json @@ -29,7 +29,7 @@ "qa:browser:smoke": "bash ./scripts/browser/agent-browser-smoke.sh", "seed:realistic": "bun run scripts/seed-realistic-catalog.ts", "seed:vercel-deployment": "bun run scripts/seed-vercel-deployment.ts", - "test:unit": "bun test apps/web/src/lib packages/backend/convex/manualInferenceLogic.test.ts packages/backend/convex/catalogQuality.test.ts packages/backend/convex/config.test.ts packages/backend/convex/contracts packages/backend/convex/shopifyJsonEnrichment.test.ts packages/shopify-cable-source/src/text.test.ts packages/shopify-cable-source/src/connector-text.test.ts scripts/ci" + "test:unit": "bun test apps/web/src/lib packages/backend/convex/manualInferenceLogic.test.ts packages/backend/convex/catalogQuality.test.ts packages/backend/convex/config.test.ts packages/backend/convex/contracts packages/backend/convex/shopifyJsonEnrichment.test.ts packages/shopify-cable-source/src/text.test.ts packages/shopify-cable-source/src/connector-text.test.ts scripts/ci packages/backend/convex/gatewayModel.test.ts" }, "overrides": { "cookie": "^0.7.2" diff --git a/packages/backend/convex/gatewayModel.test.ts b/packages/backend/convex/gatewayModel.test.ts new file mode 100644 index 0000000..89aedbc --- /dev/null +++ b/packages/backend/convex/gatewayModel.test.ts @@ -0,0 +1,30 @@ +import { expect, spyOn, test } from "bun:test"; +import { createGatewayModel } from "./gatewayModel"; + +test("passes the validated Convex credential to the gateway request", async () => { + const authorization: (string | null)[] = []; + const request = spyOn(globalThis, "fetch").mockImplementation( + (_url, init) => { + authorization.push(new Headers(init?.headers).get("authorization")); + return Promise.resolve( + new Response( + JSON.stringify({ error: { message: "test request rejected" } }), + { + status: 400, + headers: { "content-type": "application/json" }, + } + ) + ); + } + ); + try { + const model = createGatewayModel({ + aiGatewayApiKey: "test-convex-credential", + model: "openai/gpt-oss-120b", + }); + await expect(model.doGenerate({ prompt: [] })).rejects.toThrow(); + expect(authorization).toEqual(["Bearer test-convex-credential"]); + } finally { + request.mockRestore(); + } +}); diff --git a/packages/backend/convex/gatewayModel.ts b/packages/backend/convex/gatewayModel.ts new file mode 100644 index 0000000..a8c62a3 --- /dev/null +++ b/packages/backend/convex/gatewayModel.ts @@ -0,0 +1,8 @@ +import { createGateway } from "ai"; +import type { IngestConfig } from "./config"; + +export function createGatewayModel( + config: Pick +) { + return createGateway({ apiKey: config.aiGatewayApiKey })(config.model); +} diff --git a/packages/backend/convex/ingest.ts b/packages/backend/convex/ingest.ts index 097412c..8ba76f3 100644 --- a/packages/backend/convex/ingest.ts +++ b/packages/backend/convex/ingest.ts @@ -1,4 +1,6 @@ -"use node"; +import { createGatewayModel } from "./gatewayModel"; + +("use node"); import { createHash } from "node:crypto"; import { @@ -8,7 +10,7 @@ import { matchShopifyTemplateForUrl, type ShopifyExtractedCableSpec, } from "@cable-intel/shopify-cable-source"; -import { gateway, generateObject } from "ai"; +import { generateObject } from "ai"; import { v } from "convex/values"; import { api, internal } from "./_generated/api"; import type { Doc, Id } from "./_generated/dataModel"; @@ -415,7 +417,7 @@ const enrichShopifyCablesFromProductJson = async ( if (needsLlm) { const providerConfig = getProviderConfig(); const { object } = await generateObject({ - model: gateway(providerConfig.model), + model: createGatewayModel(providerConfig), schema: shopifyJsonLlmEnrichmentSchema, system: SHOPIFY_JSON_ENRICHMENT_SYSTEM_PROMPT, prompt: buildShopifyJsonEnrichmentPrompt(canonicalUrl, inputForPrompt), @@ -492,7 +494,7 @@ const extractCablesFromShopifySource = async ( }; const extractCableFromSnapshot = async ( - model: string, + providerConfig: ReturnType, snapshot: ScrapedSnapshot, contentHash: string, telemetry: { @@ -504,7 +506,7 @@ const extractCableFromSnapshot = async ( ) => { const prompt = buildExtractionPrompt(snapshot, contentHash); const { object } = await generateObject({ - model: gateway(model), + model: createGatewayModel(providerConfig), schema: extractionOutputSchema, system: EXTRACTION_SYSTEM_PROMPT, prompt, @@ -623,7 +625,7 @@ const processWorkflowItem = async ( ) .digest("hex"); const parsed = await extractCableFromSnapshot( - providerConfig.model, + providerConfig, snapshot, contentHash, { diff --git a/packages/backend/convex/manualInference.ts b/packages/backend/convex/manualInference.ts index 960b621..9c755e4 100644 --- a/packages/backend/convex/manualInference.ts +++ b/packages/backend/convex/manualInference.ts @@ -1,4 +1,4 @@ -import { gateway, generateObject } from "ai"; +import { generateObject } from "ai"; import { v } from "convex/values"; import { internal } from "./_generated/api"; import type { Doc } from "./_generated/dataModel"; @@ -12,6 +12,7 @@ import { query, } from "./_generated/server"; import { getManualInferenceConfig, manualInferenceDefaults } from "./config"; +import { createGatewayModel } from "./gatewayModel"; import { applyDraftPatch, applyQuestionAnswerPatch, @@ -483,7 +484,7 @@ const runLlmInference = async (args: { const { object } = await generateObject({ abortSignal: abortController.signal, maxRetries: 1, - model: gateway(providerConfig.model), + model: createGatewayModel(providerConfig), schema: manualInferenceLlmOutputSchema, system: MANUAL_INFERENCE_SYSTEM_PROMPT, prompt: buildManualInferencePrompt(args), diff --git a/scripts/vercel-build.sh b/scripts/vercel-build.sh index eef8ffa..937c343 100644 --- a/scripts/vercel-build.sh +++ b/scripts/vercel-build.sh @@ -4,6 +4,14 @@ set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" REPO_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)" +expected_bun="$(node -p "require('${REPO_ROOT}/package.json').packageManager.split('@')[1]")" +actual_bun="$(bun --version)" +echo "Build toolchain: Bun ${actual_bun}; Node $(node --version)" +if [[ "$actual_bun" != "$expected_bun" ]]; then + echo "Expected Bun ${expected_bun}, received ${actual_bun}." >&2 + exit 1 +fi + # Derive a deterministic preview deployment name so we reuse a bounded pool of Convex # previews and avoid hitting the 40-deployment quota on busy repos. derive_preview_slot_name() { diff --git a/vercel.json b/vercel.json index 90c9316..bc7690a 100644 --- a/vercel.json +++ b/vercel.json @@ -1,5 +1,5 @@ { "$schema": "https://openapi.vercel.sh/vercel.json", - "installCommand": "bun install --frozen-lockfile", - "buildCommand": "bun run build:vercel" + "installCommand": "bunx bun@1.4.2 install --frozen-lockfile", + "buildCommand": "bunx bun@1.4.2 run build:vercel" } From 11e6f987cccde193b990674902c7323a31e7da8f Mon Sep 17 00:00:00 2001 From: Anand Pant Date: Sat, 10 Oct 2026 00:12:07 -0500 Subject: [PATCH 3/7] fix: preserve Convex Node runtime directive --- packages/backend/convex/ingest.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/backend/convex/ingest.ts b/packages/backend/convex/ingest.ts index 8ba76f3..53b739c 100644 --- a/packages/backend/convex/ingest.ts +++ b/packages/backend/convex/ingest.ts @@ -1,6 +1,4 @@ -import { createGatewayModel } from "./gatewayModel"; - -("use node"); +"use node"; import { createHash } from "node:crypto"; import { @@ -20,6 +18,7 @@ import { extractionOutputSchema, parseExtractionOutput, } from "./contracts/extraction"; +import { createGatewayModel } from "./gatewayModel"; import { applyShopifyJsonLlmEnrichment, applyShopifyJsonPowerSignals, From fa21a11768b3905cb91c4eeb66723999c7870024 Mon Sep 17 00:00:00 2001 From: Anand Pant Date: Sat, 10 Oct 2026 00:19:22 -0500 Subject: [PATCH 4/7] fix: infer USB generations without an explicit prefix --- apps/web/src/lib/capability.test.ts | 9 +++++++++ apps/web/src/lib/capability.ts | 5 ++--- apps/web/src/lib/mappers.test.ts | 16 ++++++++++++++++ .../backend/convex/manualInferenceLogic.test.ts | 10 ++++++++++ packages/backend/convex/manualInferenceLogic.ts | 4 ++-- 5 files changed, 39 insertions(+), 5 deletions(-) diff --git a/apps/web/src/lib/capability.test.ts b/apps/web/src/lib/capability.test.ts index 5cb56a1..1c0b72c 100644 --- a/apps/web/src/lib/capability.test.ts +++ b/apps/web/src/lib/capability.test.ts @@ -19,6 +19,15 @@ describe("inferMaxGbpsFromGeneration", () => { expect(inferMaxGbpsFromGeneration("USB 3.2 Gen 2x2")).toBe(20); }); + it.each([ + ["3.2 Gen 1", 5], + ["3.2 Gen 2", 10], + ["3.1 Gen 1", 5], + ["3.1 Gen 2", 10], + ])("infers %s without a USB prefix", (generation, gbps) => { + expect(inferMaxGbpsFromGeneration(generation)).toBe(gbps); + }); + it("returns the highest inferred class for combined generation markings", () => { expect(inferMaxGbpsFromGeneration("USB 3.2 Gen 2 / USB4 / TB4")).toBe(40); }); diff --git a/apps/web/src/lib/capability.ts b/apps/web/src/lib/capability.ts index 6e875fa..dc7cec9 100644 --- a/apps/web/src/lib/capability.ts +++ b/apps/web/src/lib/capability.ts @@ -30,12 +30,11 @@ const GENERATION_GBPS_HINTS: Array<{ gbps: number; regex: RegExp }> = [ { gbps: 10, regex: - /\b(?:usb\s*(?:3\.2\s*gen\s*2|3\.1\s*gen\s*2)|(?:usb\s*)?3\.2(?!\s*gen\b))\b/i, + /\b(?:(?:usb\s*)?(?:3\.2\s*gen\s*2|3\.1\s*gen\s*2)|(?:usb\s*)?3\.2(?!\s*gen\b))\b/i, }, { gbps: 5, - regex: - /\b(?:usb\s*(?:3\.2\s*gen\s*1|3\.1\s*gen\s*1|3\.0)|(?:usb\s*)?3\.0)\b/i, + regex: /\b(?:usb\s*)?(?:3\.2\s*gen\s*1|3\.1\s*gen\s*1|3\.0)\b/i, }, { gbps: LIGHTNING_MAX_GBPS, regex: /\b(?:usb\s*2(?:\.0)?|480\s*mbps)\b/i }, ]; diff --git a/apps/web/src/lib/mappers.test.ts b/apps/web/src/lib/mappers.test.ts index acc9d86..ca16e39 100644 --- a/apps/web/src/lib/mappers.test.ts +++ b/apps/web/src/lib/mappers.test.ts @@ -1,6 +1,8 @@ import { describe, expect, it } from "bun:test"; +import { recommendLabels } from "$lib/labeling"; import { buildProfileFromMarkings, mapCatalogRowToProfile } from "$lib/mappers"; import type { CatalogCableRow, MarkingsDraft } from "$lib/types"; +import { DEFAULT_MARKINGS_DRAFT } from "$lib/types"; describe("mapCatalogRowToProfile", () => { it("maps Convex row shape into a normalized cable profile", () => { @@ -119,6 +121,20 @@ describe("mapCatalogRowToProfile", () => { }); describe("buildProfileFromMarkings", () => { + it.each([ + ["3.2 Gen 1", 5, "Black"], + ["3.2 Gen 2", 10, "Blue"], + ["USB 3.2 Gen 1", 5, "Black"], + ])("derives throughput and label from %s with blank throughput", (generation, gbps, color) => { + const profile = buildProfileFromMarkings({ + ...DEFAULT_MARKINGS_DRAFT, + usbGeneration: generation, + gbps: "", + }); + expect(profile.data.maxGbps).toBe(gbps); + expect(recommendLabels(profile).velcroColor).toBe(color); + }); + it("builds a markings profile and parses numeric fields", () => { const draft: MarkingsDraft = { connectorFrom: "USB-C", diff --git a/packages/backend/convex/manualInferenceLogic.test.ts b/packages/backend/convex/manualInferenceLogic.test.ts index c29a509..f91a6b4 100644 --- a/packages/backend/convex/manualInferenceLogic.test.ts +++ b/packages/backend/convex/manualInferenceLogic.test.ts @@ -13,6 +13,16 @@ describe("manualInferenceLogic", () => { expect(result.patch.gbps).toBe("5"); }); + it.each([ + ["3.2 Gen 1", "5"], + ["3.2 Gen 2", "10"], + ["3.1 Gen 1", "5"], + ["3.1 Gen 2", "10"], + ])("infers %s without a USB prefix", (generation, gbps) => { + const result = inferDeterministic(`USB-C to USB-C, ${generation}`); + expect(result.patch.gbps).toBe(gbps); + }); + it("extracts deterministic cable hints from free text", () => { const deterministic = inferDeterministic( "USB-C to USB-C braided cable, 240W PD, USB4, 8K 120Hz" diff --git a/packages/backend/convex/manualInferenceLogic.ts b/packages/backend/convex/manualInferenceLogic.ts index 6c44c99..eceee93 100644 --- a/packages/backend/convex/manualInferenceLogic.ts +++ b/packages/backend/convex/manualInferenceLogic.ts @@ -239,12 +239,12 @@ const GENERATION_HINTS: Array<{ impliedGbps: "10", label: "USB 3.2 Gen 2", regex: - /\b(?:usb\s*(?:3\.2\s*gen\s*2|3\.1\s*gen\s*2)|(?:usb\s*)?3\.2(?!\s*gen\b))\b/i, + /\b(?:(?:usb\s*)?(?:3\.2\s*gen\s*2|3\.1\s*gen\s*2)|(?:usb\s*)?3\.2(?!\s*gen\b))\b/i, }, { impliedGbps: "5", label: "USB 3.x", - regex: /\busb\s*(?:3\.2\s*gen\s*1|3\.1\s*gen\s*1|3\.0)\b/i, + regex: /\b(?:usb\s*)?(?:3\.2\s*gen\s*1|3\.1\s*gen\s*1|3\.0)\b/i, }, { impliedGbps: "0.48", From b0fbed357b967921ad0be58e44742fd136a48e2d Mon Sep 17 00:00:00 2001 From: Anand Pant Date: Sat, 10 Oct 2026 00:46:42 -0500 Subject: [PATCH 5/7] test: refresh retired Shopify source assumptions --- package.json | 2 +- .../convex/shopify.ingest.integration.test.ts | 22 ++--- .../src/fixtures/anker-a80e7.json | 57 +++++++++++++ .../src/source.integration.test.ts | 26 +++--- .../shopify-cable-source/src/source.test.ts | 81 +++++++++++++++++++ 5 files changed, 163 insertions(+), 25 deletions(-) create mode 100644 packages/shopify-cable-source/src/fixtures/anker-a80e7.json create mode 100644 packages/shopify-cable-source/src/source.test.ts diff --git a/package.json b/package.json index 5c6fcb3..cd81eac 100644 --- a/package.json +++ b/package.json @@ -29,7 +29,7 @@ "qa:browser:smoke": "bash ./scripts/browser/agent-browser-smoke.sh", "seed:realistic": "bun run scripts/seed-realistic-catalog.ts", "seed:vercel-deployment": "bun run scripts/seed-vercel-deployment.ts", - "test:unit": "bun test apps/web/src/lib packages/backend/convex/manualInferenceLogic.test.ts packages/backend/convex/catalogQuality.test.ts packages/backend/convex/config.test.ts packages/backend/convex/contracts packages/backend/convex/shopifyJsonEnrichment.test.ts packages/shopify-cable-source/src/text.test.ts packages/shopify-cable-source/src/connector-text.test.ts scripts/ci packages/backend/convex/gatewayModel.test.ts" + "test:unit": "bun test apps/web/src/lib packages/backend/convex/manualInferenceLogic.test.ts packages/backend/convex/catalogQuality.test.ts packages/backend/convex/config.test.ts packages/backend/convex/contracts packages/backend/convex/shopifyJsonEnrichment.test.ts packages/shopify-cable-source/src/source.test.ts packages/shopify-cable-source/src/text.test.ts packages/shopify-cable-source/src/connector-text.test.ts scripts/ci packages/backend/convex/gatewayModel.test.ts" }, "overrides": { "cookie": "^0.7.2" diff --git a/packages/backend/convex/shopify.ingest.integration.test.ts b/packages/backend/convex/shopify.ingest.integration.test.ts index 5a1b6a4..8ea9ae8 100644 --- a/packages/backend/convex/shopify.ingest.integration.test.ts +++ b/packages/backend/convex/shopify.ingest.integration.test.ts @@ -15,9 +15,9 @@ const DEDUPE_TEST_TOP_CABLE_LIMIT = 80; const TARGET_PRODUCT_SLUG = "a82e2-240w-usb-c-to-usb-c-cable"; const MIN_ANKER_ROWS = 4; const EXPECTED_SKU = "A82E2011"; -const A80E6_PRODUCT_SLUG = "a80e6"; -const MIN_A80E6_ROWS = 6; -const EXPECTED_A80E6_MAX_WATTS = 240; +const A80E7_PRODUCT_SLUG = "a80e7"; +const EXPECTED_A80E7_ROWS = 1; +const EXPECTED_A80E7_MAX_WATTS = 240; const OVERLAP_PRODUCT_SLUG_ONE = "a8662"; const OVERLAP_PRODUCT_SLUG_TWO = "a8663"; const SATECHI_USB4_PRODUCT_SLUG = "satechi-usb4-c-to-c-cable"; @@ -115,14 +115,14 @@ describe("shopify ingest integration", () => { ); it( - "keeps Anker A80E6 USB-C rows quality-ready with 240W evidence", + "keeps Anker A80E7 USB-C rows quality-ready with 240W evidence", async () => { requireEnv("AI_GATEWAY_API_KEY"); requireEnv("FIRECRAWL_API_KEY"); const t = convexTest(schema, modules); const ingestResult = await t.action(api.ingest.runSeedIngest, { - seedUrls: [`https://www.anker.com/products/${A80E6_PRODUCT_SLUG}`], + seedUrls: [`https://www.anker.com/products/${A80E7_PRODUCT_SLUG}`], }); expect(ingestResult.totalItems).toBe(EXPECT_TOTAL_ITEMS); @@ -137,14 +137,14 @@ describe("shopify ingest integration", () => { } ); const workflowRows = workflowReport.cables.filter((row) => { - return row.productUrl?.includes(A80E6_PRODUCT_SLUG); + return row.productUrl?.includes(A80E7_PRODUCT_SLUG); }); - expect(workflowRows.length).toBeGreaterThan(MIN_A80E6_ROWS); + expect(workflowRows.length).toBe(EXPECTED_A80E7_ROWS); for (const row of workflowRows) { expect(row.connectorFrom).toBe("USB-C"); expect(row.connectorTo).toBe("USB-C"); - expect(row.power.maxWatts).toBe(EXPECTED_A80E6_MAX_WATTS); + expect(row.power.maxWatts).toBe(EXPECTED_A80E7_MAX_WATTS); expect(row.qualityState).toBe("ready"); expect(row.qualityIssues).toEqual([]); expect( @@ -158,12 +158,12 @@ describe("shopify ingest integration", () => { limit: TOP_CABLE_LIMIT, }); const topCableRows = topCables.filter((row) => { - return row.productUrl?.includes(A80E6_PRODUCT_SLUG); + return row.productUrl?.includes(A80E7_PRODUCT_SLUG); }); - expect(topCableRows.length).toBeGreaterThan(MIN_A80E6_ROWS); + expect(topCableRows.length).toBe(EXPECTED_A80E7_ROWS); for (const row of topCableRows) { - expect(row.power.maxWatts).toBe(EXPECTED_A80E6_MAX_WATTS); + expect(row.power.maxWatts).toBe(EXPECTED_A80E7_MAX_WATTS); expect(row.qualityState).toBe("ready"); expect(row.qualityIssues).toEqual([]); expect( diff --git a/packages/shopify-cable-source/src/fixtures/anker-a80e7.json b/packages/shopify-cable-source/src/fixtures/anker-a80e7.json new file mode 100644 index 0000000..02220f7 --- /dev/null +++ b/packages/shopify-cable-source/src/fixtures/anker-a80e7.json @@ -0,0 +1,57 @@ +{ + "sourceUrl": "https://www.anker.com/products/a80e7", + "fetchedAt": "2026-10-10T05:43:36.047Z", + "product": { + "name": "Anker 543 USB C to USB C Cable (240W, 10 ft)", + "title": "Anker 543 USB C to USB C Cable (240W, 10 ft)", + "vendor": "Anker", + "handle": "a80e7", + "description": "Built with Bio-Based Nylon: 46% of the exterior of the cable body and the connector casing is made from bio-based nylon. Using bio-based nylon in our cables allows us to cut down on petroleum-based plastic such as TPE. Fast Charging: Charge a Samsung Galaxy S22 Ultra to 65% in just 30 minutes. Built to Last: Designed to withstand up to 10,000 bends, and proven to resist fraying and kinking even after being tested 10,000 times. Wide Compatibility: Meets USB-IF safety standards for flawless compatibility with virtually any USB-C device, including phones, tablets, laptops, and more. What You Get: Anker 543 USB-C to USB-C Cable (Bio-Nylon 10ft), our worry-free 24-month warranty, and friendly customer service.", + "images": [ + { + "url": "https://cdn.shopify.com/s/files/1/0493/9834/9974/files/A80E7011_TD01_V1_c9531cc6-0f0b-4556-b321-7aaa97600533.png?v=1730775410", + "altText": "event: pingAnker USB-C cable with bio-based cover for durable charging.", + "width": 2864, + "height": 3001 + } + ], + "variants": [ + { + "name": "Black / 10ft", + "sku": "A80E7011", + "options": [ + { + "__typename": "MultipleChoiceOption", + "id": "gid://shopify/ProductVariant/42700220924054", + "displayName": "color", + "name": "Color", + "values": [ + { + "label": "Black", + "hexColors": ["#000000"] + } + ] + }, + { + "__typename": "MultipleChoiceOption", + "id": "gid://shopify/ProductVariant/42700220924054", + "displayName": "size", + "name": "Size", + "values": [ + { + "label": "10ft" + } + ] + } + ], + "image": { + "altText": "event: pingAnker USB-C cable with bio-based cover for durable charging.", + "url": "https://cdn.shopify.com/s/files/1/0493/9834/9974/files/A80E7011_TD01_V1_c9531cc6-0f0b-4556-b321-7aaa97600533.png?v=1730775410", + "width": 2864, + "height": 3001 + } + } + ] + }, + "projection": "Selected product fields from props.pageProps.product in the source page __NEXT_DATA__. Values unchanged; prices, inventory and unrelated metadata omitted. Only the first gallery image is retained; it contains no power rating." +} diff --git a/packages/shopify-cable-source/src/source.integration.test.ts b/packages/shopify-cable-source/src/source.integration.test.ts index e4a8cfc..0fca0d2 100644 --- a/packages/shopify-cable-source/src/source.integration.test.ts +++ b/packages/shopify-cable-source/src/source.integration.test.ts @@ -83,7 +83,7 @@ describe("shopify cable source integration", () => { url.includes("/products/a82e2-240w-usb-c-to-usb-c-cable") ) ).toBe(true); - expect(urls.some((url) => url.includes("/products/a80e6"))).toBe(true); + expect(urls.some((url) => url.includes("/products/a80e7"))).toBe(true); expect(urls.some((url) => url.includes("/products/a8552"))).toBe(true); }, 120_000); @@ -101,7 +101,6 @@ describe("shopify cable source integration", () => { expect(result.cables.length).toBeGreaterThan(4); const skus = new Set(); - let has240w = false; for (const cable of result.cables) { expect(cable.brand.length).toBeGreaterThan(0); @@ -120,19 +119,17 @@ describe("shopify cable source integration", () => { skus.add(cable.sku); } - if (cable.power.maxWatts === 240) { - has240w = true; - } + expect(cable.power.maxWatts).toBe(240); + expect(fieldPaths.has("power.maxWatts")).toBe(true); } expect(skus.size).toBeGreaterThan(4); expect(skus.has("A82E2011")).toBe(true); - expect(has240w).toBe(true); }, 120_000); it("does not classify Lightning cables as Thunderbolt/USB4 class", async () => { const result = await extractAnkerProduct( - "https://www.anker.com/products/a8633" + "https://www.anker.com/products/a8623" ); expect(result.cables.length).toBeGreaterThan(0); @@ -173,12 +170,14 @@ describe("shopify cable source integration", () => { } }, 120_000); - it("uses model hints before SKU when a single variant is 'Default Title'", async () => { + it("extracts a named single-variant cable with a bundle SKU", async () => { const result = await extractAnkerProduct( - "https://www.anker.com/products/b81a2" + "https://www.anker.com/products/a82g2012-2" ); - expect(result.cables.length).toBeGreaterThan(0); + expect(result.cables.length).toBe(1); + expect(result.cables[0]?.sku).toBe("BUNDLE-A82G2012-2"); + expect(result.cables[0]?.variant).toBe("Black / 6 ft"); for (const cable of result.cables) { expect(cable.sku?.length ?? 0).toBeGreaterThan(0); expect(cable.variant?.length ?? 0).toBeGreaterThan(0); @@ -210,12 +209,13 @@ describe("shopify cable source integration", () => { } }, 120_000); - it("derives 240W power for all Anker A80E6 USB-C variants", async () => { + it("derives 240W power from the current Anker A80E7 title", async () => { const result = await extractAnkerProduct( - "https://www.anker.com/products/a80e6" + "https://www.anker.com/products/a80e7" ); - expect(result.cables.length).toBe(7); + expect(result.cables.length).toBe(1); + expect(result.cables[0]?.sku).toBe("A80E7011"); for (const cable of result.cables) { expect(cable.connectorPair.from).toBe("USB-C"); expect(cable.connectorPair.to).toBe("USB-C"); diff --git a/packages/shopify-cable-source/src/source.test.ts b/packages/shopify-cable-source/src/source.test.ts new file mode 100644 index 0000000..dc476df --- /dev/null +++ b/packages/shopify-cable-source/src/source.test.ts @@ -0,0 +1,81 @@ +import { describe, expect, it, spyOn } from "bun:test"; +import fixture from "./fixtures/anker-a80e7.json"; +import { createShopifyCableSource } from "./source"; +import { ankerShopifyTemplate } from "./templates/anker"; + +const extractFixture = async (product: typeof fixture.product) => { + const requestedPaths: string[] = []; + const request = spyOn(globalThis, "fetch").mockImplementation((input) => { + const url = new URL(input instanceof Request ? input.url : String(input)); + requestedPaths.push(url.pathname); + if (url.origin !== ankerShopifyTemplate.baseUrl) { + throw new Error(`Unexpected fixture origin: ${url.origin}`); + } + if (url.pathname === "/search") { + return Promise.resolve( + new Response( + '' + ) + ); + } + if (url.pathname === "/_next/data/fixture/products/a80e7.json") { + return Promise.resolve(Response.json({ pageProps: { product } })); + } + // The Anker suggest endpoint returned 404 at fixture capture time. + if ( + url.pathname === "/search/suggest.json" && + url.searchParams.get("q") === "a80e7" + ) { + return Promise.resolve(new Response(null, { status: 404 })); + } + throw new Error(`Unexpected fixture request: ${url}`); + }); + try { + const result = await createShopifyCableSource( + ankerShopifyTemplate + ).extractFromProductUrl(fixture.sourceUrl); + return { result, requestedPaths }; + } finally { + request.mockRestore(); + } +}; + +describe("Shopify source payload regression", () => { + it("keeps title-derived 240W, source identity and the original variant label", async () => { + const { result, requestedPaths } = await extractFixture(fixture.product); + expect(requestedPaths).toEqual([ + "/search", + "/_next/data/fixture/products/a80e7.json", + "/search/suggest.json", + ]); + expect(result?.source.url).toBe(fixture.sourceUrl); + expect(result?.source.fetchedAt).toBeGreaterThan(0); + expect(result?.cables).toHaveLength(1); + const cable = result?.cables[0]; + expect(cable?.sku).toBe("A80E7011"); + expect(cable?.variant).toBe("Black / 10ft"); + expect(cable?.connectorPair).toEqual({ from: "USB-C", to: "USB-C" }); + expect(cable?.power.maxWatts).toBe(240); + expect(cable?.evidence).toContainEqual({ + fieldPath: "power.maxWatts", + sourceUrl: fixture.sourceUrl, + snippet: expect.stringContaining("240W"), + }); + }); + + // Synthetic edge cases derived from the captured single-variant product. + // B81A2 no longer serves a product; do not depend on that retired URL to + // exercise Shopify's default-title shape or pretend this is current A80E7 data. + it.each([ + "Default Title", + "", + ])("uses the model hint before SKU for variant name %j", async (name) => { + const product = structuredClone(fixture.product); + product.variants[0].name = name; + product.variants[0].options = []; + const { result } = await extractFixture(product); + expect(result?.cables).toHaveLength(1); + expect(result?.cables[0]?.sku).toBe("A80E7011"); + expect(result?.cables[0]?.variant).toBe("240W, 10 ft"); + }); +}); From 64b4a92cea77742b50b79b8219ae494ab57cf6fb Mon Sep 17 00:00:00 2001 From: Anand Pant Date: Sat, 10 Oct 2026 01:20:12 -0500 Subject: [PATCH 6/7] fix: preserve negative PD support and normalize Mbps rates --- .../convex/shopifyJsonEnrichment.test.ts | 93 +++++++++++++++ .../backend/convex/shopifyJsonEnrichment.ts | 27 ++++- packages/shopify-cable-source/package.json | 3 +- .../src/fixtures/anker-b8173.json | 70 ++++++++++++ .../src/power-delivery.ts | 16 +++ .../src/source.integration.test.ts | 16 +++ .../shopify-cable-source/src/source.test.ts | 106 +++++++++++++++++- packages/shopify-cable-source/src/source.ts | 37 ++++-- 8 files changed, 346 insertions(+), 22 deletions(-) create mode 100644 packages/shopify-cable-source/src/fixtures/anker-b8173.json create mode 100644 packages/shopify-cable-source/src/power-delivery.ts diff --git a/packages/backend/convex/shopifyJsonEnrichment.test.ts b/packages/backend/convex/shopifyJsonEnrichment.test.ts index f85fc36..e4ffe4b 100644 --- a/packages/backend/convex/shopifyJsonEnrichment.test.ts +++ b/packages/backend/convex/shopifyJsonEnrichment.test.ts @@ -48,6 +48,99 @@ const baseParsedCable = () => { }; describe("shopifyJsonEnrichment", () => { + it.each([ + ["Not compatible with Power Delivery. PD cable.", false], + ["Supports Power Delivery.", true], + ["Durable cable.", undefined], + ])("preserves PD polarity from Shopify JSON: %s", (description, expected) => { + const input = buildShopifyJsonEnrichmentInput( + { title: "USB-C cable", description }, + {} + ); + expect(input).not.toBeNull(); + if (!input) { + throw new Error("Expected valid Shopify input"); + } + const signals = deriveDeterministicPowerSignals(input); + expect(signals.pdSupported).toBe(expected); + const result = applyShopifyJsonPowerSignals( + baseParsedCable(), + SOURCE_URL, + signals + ); + expect(result.power.pdSupported).toBe(expected); + if (expected === false) { + expect(result.evidence).toContainEqual({ + fieldPath: "power.pdSupported", + sourceUrl: SOURCE_URL, + snippet: "Not compatible with Power Delivery", + }); + } + }); + + it("replaces incorrect positive PD evidence with explicit incompatibility", () => { + const parsed = baseParsedCable(); + parsed.power.pdSupported = true; + parsed.evidence.push({ + fieldPath: "power.pdSupported", + snippet: "PD", + sourceUrl: SOURCE_URL, + }); + const result = applyShopifyJsonPowerSignals(parsed, SOURCE_URL, { + pdSupported: false, + pdSnippet: "Not compatible with Power Delivery", + }); + expect(result.power.pdSupported).toBe(false); + expect( + result.evidence.filter((item) => item.fieldPath === "power.pdSupported") + ).toEqual([ + { + fieldPath: "power.pdSupported", + snippet: "Not compatible with Power Delivery", + sourceUrl: SOURCE_URL, + }, + ]); + expect(parsed.power.pdSupported).toBe(true); + }); + + it("does not promote an explicit PD negative during deterministic or LLM enrichment", () => { + const parsed = baseParsedCable(); + parsed.power.pdSupported = false; + parsed.evidence.push({ + fieldPath: "power.pdSupported", + snippet: "Not compatible with Power Delivery", + sourceUrl: SOURCE_URL, + }); + const deterministic = applyShopifyJsonPowerSignals(parsed, SOURCE_URL, { + pdSupported: true, + pdSnippet: "PD", + }); + const enriched = applyShopifyJsonLlmEnrichment( + deterministic, + SOURCE_URL, + { + power: { pdSupported: true }, + evidence: [{ fieldPath: "power.pdSupported", snippet: "PD" }], + }, + "PD" + ); + expect(enriched.power.pdSupported).toBe(false); + expect(enriched.evidence).toEqual(parsed.evidence); + }); + + it("still fills unknown PD support from positive LLM enrichment", () => { + const enriched = applyShopifyJsonLlmEnrichment( + baseParsedCable(), + SOURCE_URL, + { + power: { pdSupported: true }, + evidence: [{ fieldPath: "power.pdSupported", snippet: "Supports PD" }], + }, + "Supports PD" + ); + expect(enriched.power.pdSupported).toBe(true); + }); + it("derives deterministic watts from stable Shopify JSON fields", () => { const input = buildShopifyJsonEnrichmentInput( { diff --git a/packages/backend/convex/shopifyJsonEnrichment.ts b/packages/backend/convex/shopifyJsonEnrichment.ts index 29ddcec..d0105d2 100644 --- a/packages/backend/convex/shopifyJsonEnrichment.ts +++ b/packages/backend/convex/shopifyJsonEnrichment.ts @@ -1,8 +1,8 @@ +import { parsePowerDeliverySupport } from "@cable-intel/shopify-cable-source/power-delivery"; import { z } from "zod"; import type { ExtractionOutput } from "./contracts/extraction"; const POWER_REGEX = /(\d{1,3}(?:\.\d+)?)\s*W\b/gi; -const PD_REGEX = /\bPD\b|Power Delivery/i; const EPR_REGEX = /\bEPR\b/i; const MAX_WATTAGE = 500; @@ -133,6 +133,7 @@ export interface ShopifyJsonEnrichmentInput { export interface DeterministicPowerSignals { eprSupported?: boolean; maxWatts?: number; + pdSnippet?: string; pdSupported?: boolean; snippet?: string; } @@ -333,11 +334,13 @@ export const deriveDeterministicPowerSignals = ( const segments = collectPowerSignalSegments(input); const { maxWatts, snippet } = getMaxWattsFromSegments(segments); const combined = segments.join("\n"); + const pd = parsePowerDeliverySupport(combined); return { eprSupported: EPR_REGEX.test(combined) ? true : undefined, maxWatts, - pdSupported: PD_REGEX.test(combined) ? true : undefined, + pdSupported: pd?.supported, + pdSnippet: pd?.snippet, snippet, }; }; @@ -399,9 +402,21 @@ export const applyShopifyJsonPowerSignals = ( ); } - if (signals.pdSupported === true && next.power.pdSupported !== true) { - next.power.pdSupported = true; - addEvidenceIfMissing(next.evidence, "power.pdSupported", sourceUrl, "PD"); + if ( + typeof signals.pdSupported === "boolean" && + signals.pdSupported !== next.power.pdSupported && + (signals.pdSupported === false || next.power.pdSupported === undefined) + ) { + next.power.pdSupported = signals.pdSupported; + next.evidence = next.evidence.filter( + (item) => item.fieldPath !== "power.pdSupported" + ); + addEvidenceIfMissing( + next.evidence, + "power.pdSupported", + sourceUrl, + signals.pdSnippet + ); } if (signals.eprSupported === true && next.power.eprSupported !== true) { @@ -491,7 +506,7 @@ const applyLlmPowerSignals = ( if ( enrichment.power?.pdSupported === true && - target.power.pdSupported !== true + target.power.pdSupported === undefined ) { target.power.pdSupported = true; addEvidenceIfMissing( diff --git a/packages/shopify-cable-source/package.json b/packages/shopify-cable-source/package.json index 0536375..5d8e20f 100644 --- a/packages/shopify-cable-source/package.json +++ b/packages/shopify-cable-source/package.json @@ -4,7 +4,8 @@ "private": true, "type": "module", "exports": { - ".": "./src/index.ts" + ".": "./src/index.ts", + "./power-delivery": "./src/power-delivery.ts" }, "scripts": { "check-types": "tsc --noEmit -p tsconfig.json", diff --git a/packages/shopify-cable-source/src/fixtures/anker-b8173.json b/packages/shopify-cable-source/src/fixtures/anker-b8173.json new file mode 100644 index 0000000..7b5bb4a --- /dev/null +++ b/packages/shopify-cable-source/src/fixtures/anker-b8173.json @@ -0,0 +1,70 @@ +{ + "sourceUrl": "https://www.anker.com/products/b8173", + "fetchedAt": "2026-10-10T05:43:42.327Z", + "product": { + "name": "Anker Premium Nylon USB-C to USB-A Cable (2-Pack)", + "title": "Anker Premium Nylon USB-C to USB-A Cable (2-Pack)", + "vendor": "Anker", + "handle": "b8173", + "description": "The Anker Advantage: Join the 50 million+ powered by our leading technology. Enhanced Durability: Improved construction techniques and materials make a cable that lasts 12\u00d7 longer. Universal Compatibility: Designed to work flawlessly with any device that uses a USB-C port. Fast Sync & Charge: Supports fast charging up to 15W (3A/5V) and data transfer speeds up to 480Mbps. (Not compatible with Power Delivery) What You Get: 2 \u00d7 Premium Nylon-Braided USB-A to USB-C Charger Cable (6ft), welcome guide, a lifetime warranty, and our friendly customer service", + "images": [ + { + "url": "https://cdn.shopify.com/s/files/1/0493/9834/9974/files/B8173011_TD01_V1_729d194f-fac9-44e3-8608-4048b9637f2d.png?v=1730775300", + "altText": "Anker charging cable with USB-C for durability", + "width": 2400, + "height": 3000 + } + ], + "variants": [ + { + "name": "Black", + "sku": "B8173011", + "options": [ + { + "__typename": "MultipleChoiceOption", + "id": "gid://shopify/ProductVariant/43756200591510", + "displayName": "color", + "name": "Color", + "values": [ + { + "label": "Black", + "hexColors": ["#000000"] + } + ] + } + ], + "image": { + "altText": "Anker charging cable with USB-C for durability", + "url": "https://cdn.shopify.com/s/files/1/0493/9834/9974/files/B8173011_TD01_V1_729d194f-fac9-44e3-8608-4048b9637f2d.png?v=1730775300", + "width": 2400, + "height": 3000 + } + }, + { + "name": "Silver", + "sku": "B8173041", + "options": [ + { + "__typename": "MultipleChoiceOption", + "id": "gid://shopify/ProductVariant/37437990142102", + "displayName": "color", + "name": "Color", + "values": [ + { + "label": "Silver", + "hexColors": ["#C0C0C0"] + } + ] + } + ], + "image": { + "altText": "Anker charging cable with USB-C for durability", + "url": "https://cdn.shopify.com/s/files/1/0493/9834/9974/files/B8173041_TD01_V1_a2065b77-4d16-4398-b625-fb72c76ed6f4.png?v=1761706315", + "width": 2400, + "height": 3000 + } + } + ] + }, + "projection": "Selected props.pageProps.product fields from vendor __NEXT_DATA__; values unchanged. Only first gallery image retained; unrelated price/inventory/metadata omitted." +} diff --git a/packages/shopify-cable-source/src/power-delivery.ts b/packages/shopify-cable-source/src/power-delivery.ts new file mode 100644 index 0000000..8a5bdd2 --- /dev/null +++ b/packages/shopify-cable-source/src/power-delivery.ts @@ -0,0 +1,16 @@ +const PD_MENTION_REGEX = /\b(?:PD|Power\s+Delivery)\b/i; +const PD_NEGATIVE_REGEX = + /\b(?:(?:not\s+compatible\s+with|does\s+not\s+support)\s+(?:USB[\s-]+)?(?:Power\s+Delivery|PD)|(?:Power\s+Delivery|PD)\s+(?:is\s+)?not\s+supported)\b/i; + +// An explicit incompatibility takes precedence over incidental PD mentions. +// Absence of a PD statement remains unknown, not false. +export const parsePowerDeliverySupport = ( + text: string +): { supported: boolean; snippet: string } | undefined => { + const negative = text.match(PD_NEGATIVE_REGEX); + if (negative) { + return { supported: false, snippet: negative[0] }; + } + const mention = text.match(PD_MENTION_REGEX); + return mention ? { supported: true, snippet: mention[0] } : undefined; +}; diff --git a/packages/shopify-cable-source/src/source.integration.test.ts b/packages/shopify-cable-source/src/source.integration.test.ts index 0fca0d2..caa4000 100644 --- a/packages/shopify-cable-source/src/source.integration.test.ts +++ b/packages/shopify-cable-source/src/source.integration.test.ts @@ -145,6 +145,22 @@ describe("shopify cable source integration", () => { } }, 120_000); + it("preserves B8173 PD incompatibility and Mbps transfer speed", async () => { + const result = await extractAnkerProduct( + "https://www.anker.com/products/b8173" + ); + expect(result.cables.length).toBeGreaterThan(0); + for (const cable of result.cables) { + expect(cable.power.pdSupported).toBe(false); + expect(cable.data.maxGbps).toBe(0.48); + expect(cable.evidence).toContainEqual({ + fieldPath: "power.pdSupported", + sourceUrl: result.source.url, + snippet: "Not compatible with Power Delivery", + }); + } + }, 120_000); + it("normalizes beta-like vendor aliases to the canonical template brand", async () => { const result = await extractAnkerProduct( "https://www.anker.com/products/a84n1" diff --git a/packages/shopify-cable-source/src/source.test.ts b/packages/shopify-cable-source/src/source.test.ts index dc476df..c91b1b2 100644 --- a/packages/shopify-cable-source/src/source.test.ts +++ b/packages/shopify-cable-source/src/source.test.ts @@ -1,9 +1,13 @@ import { describe, expect, it, spyOn } from "bun:test"; import fixture from "./fixtures/anker-a80e7.json"; +import b8173 from "./fixtures/anker-b8173.json"; import { createShopifyCableSource } from "./source"; import { ankerShopifyTemplate } from "./templates/anker"; -const extractFixture = async (product: typeof fixture.product) => { +const extractFixture = async ( + product: typeof fixture.product | typeof b8173.product, + sourceUrl = fixture.sourceUrl +) => { const requestedPaths: string[] = []; const request = spyOn(globalThis, "fetch").mockImplementation((input) => { const url = new URL(input instanceof Request ? input.url : String(input)); @@ -18,22 +22,25 @@ const extractFixture = async (product: typeof fixture.product) => { ) ); } - if (url.pathname === "/_next/data/fixture/products/a80e7.json") { + if ( + url.pathname === `/_next/data/fixture/products/${product.handle}.json` + ) { return Promise.resolve(Response.json({ pageProps: { product } })); } // The Anker suggest endpoint returned 404 at fixture capture time. if ( url.pathname === "/search/suggest.json" && - url.searchParams.get("q") === "a80e7" + url.searchParams.get("q") === product.handle ) { return Promise.resolve(new Response(null, { status: 404 })); } throw new Error(`Unexpected fixture request: ${url}`); }); try { - const result = await createShopifyCableSource( - ankerShopifyTemplate - ).extractFromProductUrl(fixture.sourceUrl); + const result = + await createShopifyCableSource( + ankerShopifyTemplate + ).extractFromProductUrl(sourceUrl); return { result, requestedPaths }; } finally { request.mockRestore(); @@ -79,3 +86,90 @@ describe("Shopify source payload regression", () => { expect(result?.cables[0]?.variant).toBe("240W, 10 ft"); }); }); + +describe("B8173 capability extraction", () => { + it("preserves explicit negative Power Delivery and its vendor evidence", async () => { + const { result } = await extractFixture(b8173.product, b8173.sourceUrl); + expect(result?.cables.map((cable) => cable.sku)).toEqual([ + "B8173011", + "B8173041", + ]); + for (const cable of result?.cables ?? []) { + expect(cable.power.pdSupported).toBe(false); + expect(cable.power.maxWatts).toBe(15); + expect(cable.evidence).toContainEqual({ + fieldPath: "power.pdSupported", + sourceUrl: b8173.sourceUrl, + snippet: expect.stringContaining("Not compatible with Power Delivery"), + }); + } + }); + + it("converts the vendor's 480Mbps to 0.48Gbps with original unit evidence", async () => { + const { result } = await extractFixture(b8173.product, b8173.sourceUrl); + expect(result?.cables).toHaveLength(2); + for (const cable of result?.cables ?? []) { + expect(cable.data.maxGbps).toBe(0.48); + expect(cable.evidence).toContainEqual({ + fieldPath: "data.maxGbps", + sourceUrl: b8173.sourceUrl, + snippet: expect.stringContaining("480Mbps"), + }); + } + }); + + // Explicit synthetic descriptions isolate presence, negation and unknown semantics. + it.each([ + ["Supports Power Delivery.", true], + ["PD compatible.", true], + ["Not compatible with Power Delivery. PD charging.", false], + ["Does not support PD.", false], + ["Power Delivery is not supported.", false], + ["Durable charging cable.", undefined], + ])("interprets PD support in %s", async (description, expected) => { + const { result } = await extractFixture( + { ...b8173.product, description }, + b8173.sourceUrl + ); + expect(result?.cables).toHaveLength(2); + expect(result?.cables[0]?.power.pdSupported).toBe(expected); + if (expected === undefined) { + expect( + result?.cables[0]?.evidence.some( + (item) => item.fieldPath === "power.pdSupported" + ) + ).toBe(false); + } + }); + + it("keeps evidence for the highest normalized Mbps rate", async () => { + const description = `480Mbps. ${"Product details. ".repeat(15)}Up to 1000Mbps.`; + const { result } = await extractFixture( + { ...b8173.product, description }, + b8173.sourceUrl + ); + expect(result?.cables[0]?.data.maxGbps).toBe(1); + const evidence = result?.cables[0]?.evidence.find( + (item) => item.fieldPath === "data.maxGbps" + ); + expect(evidence?.snippet).toContain("1000Mbps"); + expect(evidence?.snippet).not.toContain("480Mbps"); + }); + + it.each([ + ["480 Mbps", 0.48], + ["1000Mbps", 1], + ["480Mbps or 1000Mbps", 1], + ["480Mbps and 5Gbps", 5], + ["0.48Gbps", 0.48], + ["40 Gbps", 40], + ["Transfer speed not listed", undefined], + ])("normalizes data units in %s", async (description, expected) => { + const { result } = await extractFixture( + { ...b8173.product, description }, + b8173.sourceUrl + ); + expect(result?.cables).toHaveLength(2); + expect(result?.cables[0]?.data.maxGbps).toBe(expected); + }); +}); diff --git a/packages/shopify-cable-source/src/source.ts b/packages/shopify-cable-source/src/source.ts index c9d025e..5e009ff 100644 --- a/packages/shopify-cable-source/src/source.ts +++ b/packages/shopify-cable-source/src/source.ts @@ -2,6 +2,7 @@ import { collectNormalizedConnectors, extractConnectorPairFromText, } from "./connector-text"; +import { parsePowerDeliverySupport } from "./power-delivery"; import { cleanText, normalizeWhitespace } from "./text"; import type { ShopifyCableSourceTemplate, @@ -135,6 +136,7 @@ interface ProductExtractionContext { imageUrls: string[]; markdown: string; model: string; + pdSnippet?: string; power: { eprSupported?: boolean; maxWatts?: number; @@ -164,11 +166,10 @@ const THUNDERBOLT_WORD_REGEX = /thunderbolt/i; const CABLE_WORD_REGEX = /cable/i; const POWER_REGEX = /(\d{1,3}(?:\.\d+)?)\s*W\b/gi; const POWER_HINT_REGEX = /(\d{1,3}(?:\.\d+)?)\s*W\b/i; -const DATA_RATE_REGEX = /(\d{1,3}(?:\.\d+)?)\s*Gbps\b/gi; -const DATA_RATE_HINT_REGEX = /(\d{1,3}(?:\.\d+)?)\s*Gbps\b/i; +const DATA_RATE_REGEX = /\b(\d+(?:\.\d+)?)\s*([GM])bps\b/gi; +const DATA_RATE_HINT_REGEX = /\b\d+(?:\.\d+)?\s*[GM]bps\b/i; const THUNDERBOLT_REGEX = /Thunderbolt\s*(\d+)/i; const USB_GENERATION_REGEX = /USB\s*(\d(?:\.\d)?)/i; -const PD_REGEX = /\bPD\b|Power Delivery/i; const EPR_REGEX = /\bEPR\b/i; const VIDEO_NEGATIVE_REGEX = /does\s+not\s+support\s+screen\s+mirroring|not\s+support\s+screen\s+mirroring/i; @@ -302,7 +303,7 @@ const hasCapabilitySignals = (text: string): boolean => { return ( POWER_HINT_REGEX.test(text) || DATA_RATE_HINT_REGEX.test(text) || - PD_REGEX.test(text) || + parsePowerDeliverySupport(text) !== undefined || VIDEO_POSITIVE_REGEX.test(text) || VIDEO_NEGATIVE_REGEX.test(text) ); @@ -584,11 +585,16 @@ const getPowerCapability = (text: string) => { return { maxWatts: watts.length > 0 ? Math.max(...watts) : undefined, - pdSupported: PD_REGEX.test(text) ? true : undefined, + pdSupported: parsePowerDeliverySupport(text)?.supported, eprSupported: EPR_REGEX.test(text) ? true : undefined, }; }; +const MEGABITS_PER_GIGABIT = 1000; +const dataRateGbps = (match: RegExpMatchArray): number => + Number(match[1]) / + (match[2]?.toUpperCase() === "M" ? MEGABITS_PER_GIGABIT : 1); + const getDataCapability = ( text: string, connectorPair: { from: string; to: string } @@ -596,7 +602,7 @@ const getDataCapability = ( const isLightningCable = connectorPair.from === "Lightning" || connectorPair.to === "Lightning"; const speeds = [...text.matchAll(DATA_RATE_REGEX)] - .map((match) => Number(match[1])) + .map(dataRateGbps) .filter((value) => Number.isFinite(value) && value > 0); const thunderbolt = isLightningCable ? null : text.match(THUNDERBOLT_REGEX); @@ -832,6 +838,7 @@ const buildProductExtractionContext = ( const connectorPair = parseConnectorPair(model, contextText); const data = getDataCapability(contextText, connectorPair); const power = getPowerCapability(powerContextText); + const pdSnippet = parsePowerDeliverySupport(powerContextText)?.snippet; const variants = Array.isArray(product.variants) ? product.variants : ([{}] as ShopifyVariant[]); @@ -869,6 +876,7 @@ const buildProductExtractionContext = ( imageUrls, markdown, model, + pdSnippet, power, sourceText, variants, @@ -925,10 +933,21 @@ const buildEvidence = ( } } + if (context.pdSnippet && typeof effectivePower.pdSupported === "boolean") { + evidence.push({ + fieldPath: "power.pdSupported", + snippet: context.pdSnippet, + sourceUrl: context.canonicalUrl, + }); + } + if (typeof context.data.maxGbps === "number") { - const dataSnippet = - getSnippet(context.sourceText, `${context.data.maxGbps}Gbps`) ?? - getSnippet(context.sourceText, DATA_RATE_REGEX); + const rateToken = [...context.sourceText.matchAll(DATA_RATE_REGEX)].find( + (match) => dataRateGbps(match) === context.data.maxGbps + )?.[0]; + const dataSnippet = rateToken + ? getSnippet(context.sourceText, rateToken) + : undefined; if (dataSnippet) { evidence.push({ fieldPath: "data.maxGbps", From 5236e3b53b050d25a5da6eb069fe3e82fb2b48af Mon Sep 17 00:00:00 2001 From: Anand Pant Date: Sat, 10 Oct 2026 01:22:29 -0500 Subject: [PATCH 7/7] fix: distinguish byte units from cable bit rates --- packages/shopify-cable-source/src/source.test.ts | 3 +++ packages/shopify-cable-source/src/source.ts | 4 ++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/shopify-cable-source/src/source.test.ts b/packages/shopify-cable-source/src/source.test.ts index c91b1b2..c170f5a 100644 --- a/packages/shopify-cable-source/src/source.test.ts +++ b/packages/shopify-cable-source/src/source.test.ts @@ -158,6 +158,9 @@ describe("B8173 capability extraction", () => { it.each([ ["480 Mbps", 0.48], + ["480 mbps", 0.48], + ["480MBps", undefined], + ["5GBps", undefined], ["1000Mbps", 1], ["480Mbps or 1000Mbps", 1], ["480Mbps and 5Gbps", 5], diff --git a/packages/shopify-cable-source/src/source.ts b/packages/shopify-cable-source/src/source.ts index 5e009ff..cb82577 100644 --- a/packages/shopify-cable-source/src/source.ts +++ b/packages/shopify-cable-source/src/source.ts @@ -166,8 +166,8 @@ const THUNDERBOLT_WORD_REGEX = /thunderbolt/i; const CABLE_WORD_REGEX = /cable/i; const POWER_REGEX = /(\d{1,3}(?:\.\d+)?)\s*W\b/gi; const POWER_HINT_REGEX = /(\d{1,3}(?:\.\d+)?)\s*W\b/i; -const DATA_RATE_REGEX = /\b(\d+(?:\.\d+)?)\s*([GM])bps\b/gi; -const DATA_RATE_HINT_REGEX = /\b\d+(?:\.\d+)?\s*[GM]bps\b/i; +const DATA_RATE_REGEX = /\b(\d+(?:\.\d+)?)\s*([GMgm])bps\b/g; +const DATA_RATE_HINT_REGEX = /\b\d+(?:\.\d+)?\s*[GMgm]bps\b/; const THUNDERBOLT_REGEX = /Thunderbolt\s*(\d+)/i; const USB_GENERATION_REGEX = /USB\s*(\d(?:\.\d)?)/i; const EPR_REGEX = /\bEPR\b/i;