diff --git a/.codecov.yml b/.codecov.yml new file mode 100644 index 00000000..17687c0f --- /dev/null +++ b/.codecov.yml @@ -0,0 +1,27 @@ +# NOTE: Baseline coverage is ~29% (27.85% on develop as of 2026-Q2). +# The project/patch range [60%, 80%] below is aspirational for Phase 3 +# coverage push (post-fast-check + property tests + Vitest utility tests). +# CI's ci.yml intentionally runs with "no thresholds, no gates", so this +# file does NOT currently block PRs — it only affects Codecov status +# badges. Update the range after Phase 2 coverage lands. +codecov: + notify: + wait_for_ci: true + require_ci_to_pass: true +comment: + behavior: default + layout: reach,diff,flags,tree + show_carryforward_flags: false +coverage: + precision: 2 + range: + - 60.0 + - 80.0 + round: down + status: + changes: false + default_rules: + flag_coverage_not_uploaded_behavior: include + patch: true + project: true +slack_app: true diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 00000000..40a711b9 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,32 @@ +root = true + +[*] +indent_style = space +indent_size = 2 +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true + +[*.rs] +indent_size = 4 + +[*.md] +trim_trailing_whitespace = false + +[*.{yml,yaml}] +indent_size = 2 + +# Makefiles require tabs (syntax) +[Makefile] +indent_style = tab + +# Nix files: prettier handles formatting, allow 2-space indent +[*.nix] +indent_style = space +indent_size = 2 + +# JSON files: Prettier handles formatting with 2-space indent +[*.json] +indent_style = space +indent_size = 2 diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 00000000..d41b233a --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,17 @@ +# Security-sensitive routes — require review +# Auth: webauthn, passkey, MFA, OIDC, TOTP +/server/server/api/v1/auth/ @BillyOutlast +/server/server/internal/auth/ @BillyOutlast + +# Metadata providers — external HTTP integration, complex fallthrough +/server/server/internal/metadata/ @BillyOutlast + +# Nitro server core (server/ dir) +/server/server/ @BillyOutlast + +# Build, deps, CI +/server/.env.example @BillyOutlast +/.github/workflows/ @BillyOutlast +/AGENTS.md @BillyOutlast +/CLAUDE.md @BillyOutlast +/CONTRIBUTING.md @BillyOutlast diff --git a/.github/actions/rust-ci/action.yml b/.github/actions/rust-ci/action.yml new file mode 100644 index 00000000..05568561 --- /dev/null +++ b/.github/actions/rust-ci/action.yml @@ -0,0 +1,154 @@ +name: Rust CI +description: > + Reusable Rust CI steps for Drop monorepo workspaces. + Handles checkout, toolchain, cache, system deps, fmt, clippy/check, + tests, coverage (llvm-cov + Codecov), and advisory cargo-audit. + +inputs: + working-directory: + required: true + description: > + Working directory for cargo commands. + Example: libraries/droplet, cli, desktop/src-tauri + + cache-workspaces: + required: true + description: > + Workspace mapping for swatinem/rust-cache. + Example: "./libraries/droplet -> target" + + system-dependencies: + required: false + description: > + Shell commands to install system dependencies (apt-get etc.). + Omit or leave empty when no system deps are needed. + default: "" + + lint-command: + required: true + description: > + Cargo lint/build command. + Example: cargo clippy --all-targets --all-features -- -D warnings + + coverage-path: + required: true + description: > + Path to coverage.lcov relative to repo root (for Codecov upload). + Example: libraries/droplet/coverage.lcov + + test-command: + required: false + description: Cargo test command. + default: cargo test --all-features --all --verbose + + test-continue-on-error: + required: false + description: Whether to continue on test failure. + default: "false" + + lint-continue-on-error: + required: false + description: Whether to continue on lint failure. + default: "false" + + components: + required: false + description: Rust toolchain components (comma-separated). + default: rustfmt, clippy + +runs: + using: composite + steps: + # ── Setup ────────────────────────────────────────────────────── + - name: Install Rust toolchain + uses: dtolnay/rust-toolchain@4fd1da8b0805d2d2e936788875a7d65dbd677dc2 + with: + toolchain: nightly + components: ${{ inputs.components }} + + - name: Rust cache + # pinned to v2 + uses: swatinem/rust-cache@42dc69e1aa15d09112580998cf2ef0119e2e91ae + with: + workspaces: ${{ inputs.cache-workspaces }} + + # ── System dependencies ──────────────────────────────────────── + - name: Install system dependencies + if: ${{ inputs.system-dependencies != '' }} + shell: bash + run: ${{ inputs.system-dependencies }} + + # ── Format ───────────────────────────────────────────────────── + - name: Check formatting + shell: bash + working-directory: ${{ inputs.working-directory }} + run: cargo fmt --all -- --check + + # ── Lint / Build ─────────────────────────────────────────────── + - name: Lint / Build + shell: bash + working-directory: ${{ inputs.working-directory }} + continue-on-error: ${{ inputs.lint-continue-on-error == 'true' }} + run: ${{ inputs.lint-command }} + + # ── Test ─────────────────────────────────────────────────────── + - name: Run tests + shell: bash + working-directory: ${{ inputs.working-directory }} + continue-on-error: ${{ inputs.test-continue-on-error == 'true' }} + run: ${{ inputs.test-command }} + + # ── Coverage ─────────────────────────────────────────────────── + - name: Install cargo-llvm-cov + uses: taiki-e/install-action@cargo-llvm-cov + + - name: Generate code coverage + # Was continue-on-error: true — switched to false so coverage + # failures surface in CI. Codecov upload below still uses + # fail_ci_if_error: false, so generation failure is visible + # but won't block the pipeline. + continue-on-error: false + shell: bash + working-directory: ${{ inputs.working-directory }} + run: | + cargo llvm-cov --all-features --workspace \ + --codecov --output-path coverage.lcov + + - name: Upload coverage to Codecov + # pinned to v5 + uses: codecov/codecov-action@04b047e8bb82a0c002c8312c1c880fbc6a999d45 + with: + files: ${{ inputs.coverage-path }} + fail_ci_if_error: false + + # ── Security audit ───────────────────────────────────────────── + - name: Install cargo-audit + uses: taiki-e/install-action@cargo-audit + + - name: Audit dependencies + # cargo audit exits 1 on ANY advisory. Keep non-blocking here; the + # follow-up step fails only when a NEW (un-ignored) advisory is found + # that is not already documented in security/risk-register.yaml. + continue-on-error: true + shell: bash + working-directory: ${{ inputs.working-directory }} + run: cargo audit --json > /tmp/cargo-audit.json 2>/dev/null || true + + - name: Check for new Rust advisories + # Run on success or failure of the audit step, but not on cancel. + # Use --min-severity high for cargo to catch DoS-class advisories + # (RUSTSEC-2026-0194/0195 in quick-xml are severity "high"); the + # script handles missing/empty/malformed JSON and missing risk + # register gracefully (exits 0 with a warning in both cases). + # Resolve the script via $GITHUB_WORKSPACE because this composite + # action is invoked with working-directory set to a sub-crate + # (cli/, desktop/src-tauri/, libraries/droplet/), where a relative + # `scripts/check-new-vulns.cjs` would not exist. + if: success() || failure() + shell: bash + working-directory: ${{ inputs.working-directory }} + run: | + node "$GITHUB_WORKSPACE/scripts/check-new-vulns.cjs" \ + --format cargo \ + --json /tmp/cargo-audit.json \ + --min-severity high diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 00000000..9befd953 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,158 @@ +version: 2 + +registries: + # Allow Dependabot to resolve private/skipped registry hosts from lockfiles + # (e.g. buf schema registry, GitHub Packages). Public registries need no entry. + npm-pkg-github: + type: "npm-registry" + url: "https://npm.pkg.github.com" + token: "${{secrets.GITHUB_TOKEN}}" + +updates: + # ----- Node / pnpm workspace (root, server, sites/*, desktop) ----- + - package-ecosystem: "npm" + directory: "/" + schedule: + interval: "daily" + time: "06:00" + timezone: "America/New_York" + open-pull-requests-limit: 10 + groups: + # Patch + minor bumps bundled together to avoid PR spam + node-minor: + update-types: ["minor", "patch"] + # Pin major bumps separately for explicit review + node-major: + update-types: ["major"] + commit-message: + prefix: "deps" + prefix-development: "chore(deps-dev)" + labels: ["dependencies", "javascript"] + reviewers: ["BillyOutlast"] + # Keep lockfile in sync; pnpm-workspace.yaml declares onlyBuiltDependencies + # — keep Dependabot from re-enabling builds that the workspace intentionally skips. + rebase-strategy: "auto" + + # ----- Nuxt 4 desktop app (separate pnpm workspace) ----- + # desktop/main/ has its own pnpm-workspace.yaml and pnpm-lock.yaml, + # so root npm entry at "/" does not cover it. Scanned independently. + - package-ecosystem: "npm" + directory: "/desktop/main" + schedule: + interval: "weekly" + day: "monday" + time: "06:00" + timezone: "America/New_York" + open-pull-requests-limit: 5 + groups: + desktop-minor: + update-types: ["minor", "patch"] + desktop-major: + update-types: ["major"] + commit-message: + prefix: "deps(desktop)" + labels: ["dependencies", "javascript"] + rebase-strategy: "auto" + + # ----- Rust workspace: CLI ----- + - package-ecosystem: "cargo" + directory: "/cli" + schedule: + interval: "weekly" + day: "monday" + time: "06:00" + timezone: "America/New_York" + open-pull-requests-limit: 5 + groups: + rust-minor: + update-types: ["minor", "patch"] + rust-major: + update-types: ["major"] + commit-message: + prefix: "deps(cli)" + labels: ["dependencies", "rust"] + + # ----- Rust workspace: droplet library ----- + - package-ecosystem: "cargo" + directory: "/libraries/droplet" + schedule: + interval: "weekly" + day: "monday" + time: "06:00" + timezone: "America/New_York" + open-pull-requests-limit: 5 + groups: + rust-minor: + update-types: ["minor", "patch"] + rust-major: + update-types: ["major"] + commit-message: + prefix: "deps(droplet)" + labels: ["dependencies", "rust"] + + # ----- Rust workspace: native_model library ----- + - package-ecosystem: "cargo" + directory: "/libraries/native_model" + schedule: + interval: "weekly" + day: "monday" + time: "06:00" + timezone: "America/New_York" + open-pull-requests-limit: 5 + groups: + rust-minor: + update-types: ["minor", "patch"] + rust-major: + update-types: ["major"] + commit-message: + prefix: "deps(native_model)" + labels: ["dependencies", "rust"] + + # ----- Rust workspace: desktop (Tauri) ----- + - package-ecosystem: "cargo" + directory: "/desktop/src-tauri" + schedule: + interval: "weekly" + day: "monday" + time: "06:00" + timezone: "America/New_York" + open-pull-requests-limit: 5 + groups: + rust-minor: + update-types: ["minor", "patch"] + rust-major: + update-types: ["major"] + commit-message: + prefix: "deps(desktop)" + labels: ["dependencies", "rust"] + + # ----- Dockerfile (root image) ----- + - package-ecosystem: "docker" + directory: "/" + schedule: + interval: "weekly" + day: "monday" + time: "06:00" + timezone: "America/New_York" + open-pull-requests-limit: 5 + commit-message: + prefix: "deps(docker)" + labels: ["dependencies", "docker"] + + # ----- GitHub Actions ----- + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" + day: "monday" + time: "06:00" + timezone: "America/New_York" + open-pull-requests-limit: 10 + groups: + actions-minor: + update-types: ["minor", "patch"] + actions-major: + update-types: ["major"] + commit-message: + prefix: "deps(ci)" + labels: ["dependencies", "github-actions"] diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 00000000..cd9c36ea --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,305 @@ +name: CI + +on: + push: + branches: + - develop + pull_request: + branches: + - develop + +permissions: + contents: read + +env: + NODE_VERSION: "22" + +jobs: + validate: + name: Validate workflows + runs-on: ubuntu-latest + steps: + - name: Check out the repo + uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 + - name: Run actionlint + uses: reviewdog/action-actionlint@086cc5300a077f608b81d2b03801bc382e417264 # v1.65.2 + + # Enforce: every pnpm audit --ignore GHSA-... must have a risk register entry. + - name: Verify risk register coverage + run: | + IGNORED=$(grep -rh '\-\-ignore GHSA-' .github/workflows/ | grep -oE 'GHSA-[a-z0-9]{4}-[a-z0-9]{4}-[a-z0-9]{4}' | sort -u) + for ghsa in $IGNORED; do + if ! grep -q "$ghsa" security/risk-register.yaml; then + echo "ERROR: $ghsa ignored but missing from security/risk-register.yaml" + exit 1 + fi + done + echo "All $(echo "$IGNORED" | wc -w) ignored advisories have risk register entries" + + dependency-review: + name: Dependency Review + runs-on: ubuntu-latest + if: github.event_name == 'pull_request' + steps: + - name: Check out the repo + uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 + - name: Review dependencies + uses: actions/dependency-review-action@da24556b548a50705dd671f47852072ea4c105d9 # v4 + with: + fail-on-severity: critical + + secrets: + name: Secret Scan + runs-on: ubuntu-latest + steps: + - name: Check out the repo + uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 + with: + # gitleaks-action v2 needs full history to resolve base-commit + # refs on fork PRs (compares baseRef^..headRef). Without this, + # git fails with 'ambiguous argument ...^...' on shallow clones. + fetch-depth: 0 + persist-credentials: false + - name: Run gitleaks + uses: gitleaks/gitleaks-action@dcedce43c6f43de0b836d1fe38946645c9c638dc # v2 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + typecheck: + name: Typecheck + runs-on: ubuntu-latest + steps: + - name: Check out the repo + uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 + with: + submodules: true + - name: Install system dependencies + run: | + sudo apt-get update + sudo apt-get install -y libpng-dev + - name: Install pnpm + uses: pnpm/action-setup@f40ffcd9367d9f12939873eb1018b921a783ffaa # v4 + - name: Setup Node.js + uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 + with: + node-version: ${{ env.NODE_VERSION }} + cache: pnpm + - name: Install dependencies + run: pnpm install --frozen-lockfile --ignore-scripts + + - name: Generate Nuxt and Prisma artifacts + run: pnpm --filter drop run postinstall + - name: Audit dependencies + # GHSA-mp2f-45pm-3cg9 patched locally via patches/decompress@4.2.1.patch. + # Remove ignore when upstream publishes decompress@>=4.2.2. + continue-on-error: true + run: pnpm audit --audit-level=critical --ignore GHSA-mp2f-45pm-3cg9 + + - name: Check for new critical advisories + # Run on success or failure of the audit step, but not on cancel. + # Use --min-severity critical for pnpm to limit noise; the script + # handles missing/empty/malformed JSON gracefully. + if: success() || failure() + run: | + pnpm audit --audit-level=critical --json > /tmp/audit.json 2>/dev/null || true + node scripts/check-new-vulns.cjs \ + --format pnpm \ + --json /tmp/audit.json \ + --ignored GHSA-mp2f-45pm-3cg9 \ + --min-severity critical + - name: Typecheck + working-directory: server + run: pnpm run typecheck + + lint: + name: Lint & Format + runs-on: ubuntu-latest + steps: + - name: Check out the repo + uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 + with: + submodules: true + - name: Install system dependencies + run: | + sudo apt-get update + sudo apt-get install -y libpng-dev + - name: Install pnpm + uses: pnpm/action-setup@f40ffcd9367d9f12939873eb1018b921a783ffaa # v4 + - name: Setup Node.js + uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 + with: + node-version: ${{ env.NODE_VERSION }} + cache: pnpm + - name: Install dependencies + run: pnpm install --frozen-lockfile --ignore-scripts + + - name: Generate Nuxt and Prisma artifacts + run: pnpm --filter drop run postinstall + - name: Check formatting + working-directory: server + run: pnpm run format:check + - name: Lint + working-directory: server + run: pnpm run lint:eslint + + test: + name: Test + Coverage + runs-on: ubuntu-latest + # Coverage is measurement-only at 29.32% server backend baseline. + # No thresholds, no gates. Rust workspaces use reusable rust-ci action + # which also uploads per-workspace lcov to Codecov. + # Add a `coverage:check` script with thresholds once coverage matures + # past ~40% on server business-logic modules. + permissions: + contents: read + pull-requests: write + steps: + - name: Check out the repo + uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 + with: + submodules: true + - name: Install system dependencies + run: | + sudo apt-get update + sudo apt-get install -y libpng-dev + - name: Install pnpm + uses: pnpm/action-setup@f40ffcd9367d9f12939873eb1018b921a783ffaa # v4 + - name: Setup Node.js + uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 + with: + node-version: ${{ env.NODE_VERSION }} + cache: pnpm + - name: Install dependencies + run: pnpm install --frozen-lockfile --ignore-scripts + + - name: Generate Nuxt and Prisma artifacts + run: pnpm --filter drop run postinstall + - name: Run tests with coverage + working-directory: server + run: pnpm run coverage + - name: Upload coverage to Codecov + uses: codecov/codecov-action@04b047e8bb82a0c002c8312c1c880fbc6a999d45 # v5 + with: + directory: server/coverage + token: ${{ secrets.CODECOV_TOKEN }} + fail_ci_if_error: false + flags: server + - name: Post coverage gaps to PR + # Skip silently when CODECOV_TOKEN is unset — the script fails + # Only run on PRs (script posts a comment); wrap with || true so a + # missing or invalid CODECOV_TOKEN does not fail the workflow. + # Step-level env is not visible in this step's own `if:` context + # so we cannot gate on env.CODECOV_TOKEN here; rely on the script + # being tolerant instead. + if: github.event_name == 'pull_request' + continue-on-error: true + env: + CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GITHUB_PR_NUMBER: ${{ github.event.pull_request.number }} + run: bash scripts/codecov-pr-comment.sh + + sonar: + name: SonarCloud Scan + runs-on: ubuntu-latest + if: github.event_name == 'push' || github.event_name == 'pull_request' + permissions: + contents: read + steps: + - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 + with: + fetch-depth: 0 + - name: Install system dependencies + run: | + sudo apt-get update + sudo apt-get install -y libpng-dev + - name: Install pnpm + uses: pnpm/action-setup@f40ffcd9367d9f12939873eb1018b921a783ffaa # v4 + - name: Setup Node.js + uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 + with: + node-version: ${{ env.NODE_VERSION }} + cache: pnpm + - name: Install dependencies + run: pnpm install --frozen-lockfile --ignore-scripts + - name: Generate Nuxt and Prisma artifacts + run: pnpm --filter drop run postinstall + - name: Run tests with coverage + working-directory: server + run: pnpm run coverage + - name: Cache SonarQube packages + uses: actions/cache@0057852bfaa89a56745cba8e7296529d2fc39830 # v4 + with: + path: ~/.sonar/cache + key: ${{ runner.os }}-sonar + restore-keys: ${{ runner.os }}-sonar + - name: SonarQube Scan + id: sonar-scan + uses: SonarSource/sonarqube-scan-action@7006c4492b2e0ee0f816d36501671557c97f5995 # v8.1.0 + env: + SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} + + sonar-sync: + name: Sync SonarCloud findings + runs-on: ubuntu-latest + needs: sonar + if: github.event_name == 'push' && github.ref == 'refs/heads/develop' && needs.sonar.result == 'success' + permissions: + contents: read + issues: write + steps: + - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 + with: + fetch-depth: 0 + - name: Sync findings to GitHub Issues + env: + SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: bash scripts/sonarcloud-sync.sh + + sonar-pr-comment: + name: SonarCloud PR Comment + runs-on: ubuntu-latest + needs: sonar + # The scan uploads findings to SonarCloud API before quality gate check. + # Run even when quality gate fails — the API still has data to comment. + # always() overrides needs dependency failure; !cancelled() alone does not. + if: github.event_name == 'pull_request' && always() && !cancelled() + permissions: + contents: read + pull-requests: write + steps: + - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 + with: + fetch-depth: 0 + - name: Post SonarCloud findings to PR + env: + SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GITHUB_PR_NUMBER: ${{ github.event.pull_request.number }} + run: bash scripts/sonarcloud-pr-comment.sh + + dockerfile: + name: Dockerfile Lint + runs-on: ubuntu-latest + steps: + - name: Check out the repo + uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 + - name: Run hadolint + uses: hadolint/hadolint-action@54c9adbab1582c2ef04b2016b760714a4bfde3cf # v3.1.0 + with: + dockerfile: Dockerfile + failure-threshold: error + + shellcheck: + name: Shellcheck + runs-on: ubuntu-latest + steps: + - name: Check out the repo + uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 + - name: Run shellcheck + uses: ludeeus/action-shellcheck@00cae500b08a931fb5698e11e79bfbd38e612a38 # 2.0.0 + with: + scandir: "./scripts" + additional_files: ".husky/pre-commit" + ignore-paths: "node_modules,.git,.nuxt,.output,target" diff --git a/.github/workflows/cli-ci.yml b/.github/workflows/cli-ci.yml new file mode 100644 index 00000000..0ce8e018 --- /dev/null +++ b/.github/workflows/cli-ci.yml @@ -0,0 +1,41 @@ +name: CLI CI + +on: + push: + branches: [develop] + paths: + - "cli/**" + - ".github/workflows/cli-ci.yml" + - "pnpm-workspace.yaml" + - "package.json" + pull_request: + branches: [develop] + paths: + - "cli/**" + - ".github/workflows/cli-ci.yml" + - "pnpm-workspace.yaml" + - "package.json" + workflow_dispatch: + +permissions: + contents: read + +jobs: + ci: + name: Build, Test, Lint + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 + with: + persist-credentials: false + + - uses: ./.github/actions/rust-ci + with: + working-directory: cli + cache-workspaces: "./cli -> target" + system-dependencies: | + sudo apt-get update + sudo apt-get install -y libarchive-dev + lint-command: cargo clippy --all-targets --no-deps --all-features + coverage-path: cli/coverage.lcov diff --git a/.github/workflows/client-release.yml b/.github/workflows/client-release.yml index ee511a24..97019b2a 100644 --- a/.github/workflows/client-release.yml +++ b/.github/workflows/client-release.yml @@ -36,30 +36,30 @@ jobs: runs-on: ${{ matrix.platform }} steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 with: token: ${{ secrets.GITHUB_TOKEN }} - name: setup pnpm - uses: pnpm/action-setup@v4 + uses: pnpm/action-setup@f40ffcd9367d9f12939873eb1018b921a783ffaa # v4 with: run_install: false - name: setup node - uses: actions/setup-node@v4 + uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 with: node-version: lts/* cache: pnpm - name: install Rust nightly - uses: dtolnay/rust-toolchain@nightly + uses: dtolnay/rust-toolchain@4fd1da8b0805d2d2e936788875a7d65dbd677dc2 # nightly with: # Those targets are only used on macos runners so it's in an `if` to slightly speed up windows and linux builds. targets: ${{ matrix.platform == 'macos-14' && 'aarch64-apple-darwin,x86_64-apple-darwin' || '' }} - name: Rust cache - uses: swatinem/rust-cache@v2 + uses: swatinem/rust-cache@42dc69e1aa15d09112580998cf2ef0119e2e91ae # v2 with: workspaces: './desktop/src-tauri -> target' @@ -119,7 +119,7 @@ jobs: - name: install frontend dependencies run: pnpm install # change this to npm, pnpm or bun depending on which one you use. - - uses: tauri-apps/tauri-action@v0 + - uses: tauri-apps/tauri-action@fce9c6108b31ea247710505d3aaaa893ee6768d4 # v0 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Do NOT set APPLE_CERTIFICATE / APPLE_CERTIFICATE_PASSWORD here. Doing so @@ -131,7 +131,7 @@ jobs: APPLE_SIGNING_IDENTITY: ${{ env.CERT_ID }} NO_STRIP: true with: - tagName: ${{ inputs.print_tags || 'v__VERSION__' }} # the action automatically replaces \_\_VERSION\_\_ with the app version. + tagName: ${{ inputs.tagName || 'v__VERSION__' }} # the action automatically replaces \_\_VERSION\_\_ with the app version. releaseName: "Auto-release v__VERSION__" releaseBody: "See the assets to download this version and install. This release was created automatically." releaseDraft: false diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml new file mode 100644 index 00000000..ce6b2d59 --- /dev/null +++ b/.github/workflows/codeql.yml @@ -0,0 +1,93 @@ +# For most projects, this workflow file will not need changing; you simply need +# to commit it to your repository. +# +# You may wish to alter this file to override the set of languages analyzed, +# or to provide custom queries or build logic. +# +# ******** NOTE ******** +# We have attempted to detect the languages in your repository. Please check +# the `language` matrix defined below to confirm you have the correct set of +# supported CodeQL languages. +# +name: "CodeQL Advanced" + +on: + push: + branches: ["rebuild"] + pull_request: + branches: ["rebuild", "develop"] + schedule: + - cron: "39 17 * * 0" + +jobs: + analyze: + name: Analyze (${{ matrix.language }}) + # Runner size impacts CodeQL analysis time. To learn more, please see: + # - https://gh.io/recommended-hardware-resources-for-running-codeql + # - https://gh.io/supported-runners-and-hardware-resources + # - https://gh.io/using-larger-runners (GitHub.com only) + # Consider using larger runners or machines with greater resources for possible analysis time improvements. + runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-latest' }} + permissions: + # required for all workflows + security-events: write + + # required to fetch internal or private CodeQL packs + packages: read + + # only required for workflows in private repositories + actions: read + contents: read + + strategy: + fail-fast: false + matrix: + include: + - language: actions + build-mode: none + - language: go + build-mode: autobuild + - language: javascript-typescript + build-mode: none + - language: rust + build-mode: none + # No Swift project in repo — remove entry. Re-add when Swift files need analysis. + # - language: swift + # build-mode: autobuild + # CodeQL supports the following values keywords for 'language': 'actions', 'c-cpp', 'csharp', 'go', 'java-kotlin', 'javascript-typescript', 'python', 'ruby', 'rust', 'swift' + # Use `c-cpp` to analyze code written in C, C++ or both + # Use 'java-kotlin' to analyze code written in Java, Kotlin or both + # Use 'javascript-typescript' to analyze code written in JavaScript, TypeScript or both + # To learn more about changing the languages that are analyzed or customizing the build mode for your analysis, + # see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/customizing-your-advanced-setup-for-code-scanning. + # If you are analyzing a compiled language, you can modify the 'build-mode' for that language to customize how + # your codebase is analyzed, see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/codeql-code-scanning-for-compiled-languages + steps: + - name: Checkout repository + uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 + with: + persist-credentials: false + + # Add any setup steps before running the `github/codeql-action/init` action. + # This includes steps like installing compilers or runtimes (`actions/setup-node` + # or others). This is typically only required for manual builds. + # - name: Setup runtime (example) + # uses: actions/setup-example@v1 + + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@e4fba868fa4b1b91e1fdab776edc8cfbe6e9fb81 # v4.37.3 + with: + languages: ${{ matrix.language }} + build-mode: ${{ matrix.build-mode }} + # If you wish to specify custom queries, you can do so here or in a config file. + # By default, queries listed here will override any specified in a config file. + # Prefix the list here with "+" to use these queries and those in the config file. + + # For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs + # queries: security-extended,security-and-quality + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@e4fba868fa4b1b91e1fdab776edc8cfbe6e9fb81 # v4.37.3 + with: + category: "/language:${{matrix.language}}" diff --git a/.github/workflows/desktop-ci.yml b/.github/workflows/desktop-ci.yml new file mode 100644 index 00000000..fc838a95 --- /dev/null +++ b/.github/workflows/desktop-ci.yml @@ -0,0 +1,47 @@ +name: Desktop CI + +on: + push: + branches: [develop] + paths: + - "desktop/src-tauri/**" + - ".github/workflows/desktop-ci.yml" + - "pnpm-workspace.yaml" + - "package.json" + pull_request: + branches: [develop] + paths: + - "desktop/src-tauri/**" + - ".github/workflows/desktop-ci.yml" + - "pnpm-workspace.yaml" + - "package.json" + workflow_dispatch: + +permissions: + contents: read + +jobs: + ci: + name: Format, Lint + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 + with: + persist-credentials: false + + - uses: ./.github/actions/rust-ci + with: + working-directory: desktop/src-tauri + cache-workspaces: "./desktop/src-tauri -> target" + system-dependencies: | + sudo apt-get update + sudo apt-get install -y \ + libwebkit2gtk-4.1-dev \ + libappindicator3-dev \ + librsvg2-dev \ + patchelf + lint-command: cargo clippy --workspace + coverage-path: desktop/src-tauri/coverage.lcov + test-command: cargo test --workspace --no-fail-fast + components: rustfmt, clippy diff --git a/.github/workflows/droplet-ci.yml b/.github/workflows/droplet-ci.yml index c125d2e7..c7130e4f 100644 --- a/.github/workflows/droplet-ci.yml +++ b/.github/workflows/droplet-ci.yml @@ -29,15 +29,15 @@ jobs: working-directory: libraries/droplet steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 - name: Install Rust toolchain - uses: dtolnay/rust-toolchain@nightly + uses: dtolnay/rust-toolchain@4fd1da8b0805d2d2e936788875a7d65dbd677dc2 # nightly with: components: rustfmt, clippy - name: Rust cache - uses: swatinem/rust-cache@v2 + uses: swatinem/rust-cache@42dc69e1aa15d09112580998cf2ef0119e2e91ae # v2 with: workspaces: "./libraries/droplet -> target" diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml new file mode 100644 index 00000000..f04c08c5 --- /dev/null +++ b/.github/workflows/e2e.yml @@ -0,0 +1,81 @@ +name: E2E + +on: + push: + branches: [develop] + paths: + - "server/**" + - ".github/workflows/e2e.yml" + - "server/playwright.config.ts" + - "package.json" + - "pnpm-lock.yaml" + - "pnpm-workspace.yaml" + pull_request: + branches: [develop] + paths: + - "server/**" + - ".github/workflows/e2e.yml" + - "server/playwright.config.ts" + - "package.json" + - "pnpm-lock.yaml" + - "pnpm-workspace.yaml" + workflow_dispatch: + +permissions: + contents: read + +jobs: + e2e: + name: Playwright E2E + runs-on: ubuntu-latest + defaults: + run: + working-directory: server + steps: + - name: Check out the repo + uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 + with: + submodules: true + persist-credentials: false + + - name: Install system dependencies + run: | + sudo apt-get update + sudo apt-get install -y libpng-dev + + - name: Install pnpm + uses: pnpm/action-setup@f40ffcd9367d9f12939873eb1018b921a783ffaa # v4 + + - name: Setup Node.js + uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 + with: + node-version: "22.16.0" + cache: pnpm + + - name: Install dependencies + run: pnpm install --frozen-lockfile --ignore-scripts # NOSONAR + + - name: Generate Nuxt and Prisma artifacts + run: pnpm --filter drop run postinstall + + # Start test postgres before the dev server so any API-touching + # E2E spec (Prisma → postgres) doesn't 500. Scoped to the test + # compose file; uses port 5433 to avoid clashing with local dev. + - name: Start postgres test container + run: docker compose -f server/docker-compose.test.yml up -d --wait + + # Playwright config sets webServer.command = "pnpm dev", so the + # dev server is auto-spawned. No explicit start needed. + - name: Install Playwright browsers + run: pnpm exec playwright install --with-deps chromium + + - name: Run E2E tests + run: pnpm run test:e2e + + - name: Upload Playwright report on failure + if: failure() + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 + with: + name: playwright-report + path: server/playwright-report/ + retention-days: 7 diff --git a/.github/workflows/editorconfig-ci.yml b/.github/workflows/editorconfig-ci.yml new file mode 100644 index 00000000..cdc994b2 --- /dev/null +++ b/.github/workflows/editorconfig-ci.yml @@ -0,0 +1,33 @@ +name: EditorConfig CI + +on: + push: + branches: [develop] + pull_request: + branches: [develop] + workflow_dispatch: + +permissions: + contents: read + +jobs: + lint-editorconfig: + name: EditorConfig Check + runs-on: ubuntu-latest + steps: + - name: Check out the repo + uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 + + - name: Install pnpm + uses: pnpm/action-setup@f40ffcd9367d9f12939873eb1018b921a783ffaa # v4 + + - name: Setup Node.js + uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 + with: + node-version: "22" + cache: pnpm + + - name: Validate .editorconfig compliance + run: | + pnpm dlx editorconfig-checker@6.1.1 --exclude \ + '(node_modules|\.git|\.nuxt|\.output|coverage|dist|target|\.omo|\.claude|\.opencode|\.dockerignore|README\.md|LICENSE|server/prisma/migrations|server/test-results|sites/docs|sites/promo/public|desktop/libs|backend|desktop/main/(components|pages|layouts|assets|plugins|public))' diff --git a/.github/workflows/osv-scanner.yml b/.github/workflows/osv-scanner.yml new file mode 100644 index 00000000..c5854db9 --- /dev/null +++ b/.github/workflows/osv-scanner.yml @@ -0,0 +1,65 @@ +name: OSV-Scanner + +# Cancel outdated in-progress runs of the same workflow on the same PR +# when a new commit is pushed. Avoids redundant scans and stale SARIF uploads. +concurrency: + group: ${{ github.workflow }}-${{ github.event_name }}-${{ github.head_ref || github.ref }} + cancel-in-progress: true + +on: + pull_request: + branches: ["rebuild", "develop"] + merge_group: + branches: ["rebuild", "develop"] + schedule: + - cron: "26 14 * * 5" + push: + branches: ["rebuild"] + +permissions: + contents: read + +jobs: + scan-scheduled: + if: ${{ github.event_name == 'push' || github.event_name == 'schedule' }} + runs-on: ubuntu-latest + permissions: + actions: read + security-events: write + contents: read + steps: + - name: Checkout + uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 + with: + persist-credentials: false + + - name: Run OSV-Scanner + # OSV scanner exits 1 on ANY CVE in the dependency tree (including transitive). + # We keep continue-on-error: true because blocking on transitive vulns would + # create constant noise. SARIF results are still uploaded below for review + # and the scan-pr job on pull_request events catches direct deps separately. + continue-on-error: true + uses: google/osv-scanner-action/osv-scanner-action@9a498708959aeaef5ef730655706c5a1df1edbc2 # v2.3.8 + with: + scan-args: |- + -r + ./ + --format=sarif + --output-file=osv-scanner-results.sarif + + - name: Upload SARIF + uses: github/codeql-action/upload-sarif@e4fba868fa4b1b91e1fdab776edc8cfbe6e9fb81 # v4.37.3 + with: + sarif_file: osv-scanner-results.sarif + + scan-pr: + if: ${{ github.event_name == 'pull_request' || github.event_name == 'merge_group' }} + permissions: + actions: read + contents: read + security-events: write + uses: "google/osv-scanner-action/.github/workflows/osv-scanner-reusable-pr.yml@9a498708959aeaef5ef730655706c5a1df1edbc2" # v2.3.8 + with: + scan-args: |- + -r + ./ diff --git a/.github/workflows/pages.yml b/.github/workflows/pages.yml index 4b78b0c8..04095c91 100644 --- a/.github/workflows/pages.yml +++ b/.github/workflows/pages.yml @@ -31,15 +31,15 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 - name: Install pnpm - uses: pnpm/action-setup@v4 + uses: pnpm/action-setup@f40ffcd9367d9f12939873eb1018b921a783ffaa # v4 with: run_install: false - name: Install Node.js - uses: actions/setup-node@v4 + uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 with: node-version: "22" cache: "pnpm" @@ -52,10 +52,10 @@ jobs: - name: Setup Pages id: setup_pages - uses: actions/configure-pages@v5 + uses: actions/configure-pages@983d7736d9b0ae728b81ab479565c72886d7745b # v5 - name: Restore cache - uses: actions/cache@v4 + uses: actions/cache@0057852bfaa89a56745cba8e7296529d2fc39830 # v4 with: path: | sites/promo/.next/cache @@ -84,7 +84,7 @@ jobs: cp -r sites/docs/dist/. sites/promo/out/docs/ - name: Upload artifact - uses: actions/upload-pages-artifact@v3 + uses: actions/upload-pages-artifact@56afc609e74202658d3ffba0e8f6dda462b719fa # v3 with: path: sites/promo/out @@ -97,4 +97,4 @@ jobs: steps: - name: Deploy to GitHub Pages id: deployment - uses: actions/deploy-pages@v4 + uses: actions/deploy-pages@d6db90164ac5ed86f2b6aed7e0febac5b3c0c03e # v4 diff --git a/.github/workflows/server-ci.yml b/.github/workflows/server-ci.yml index 99511123..5bff574e 100644 --- a/.github/workflows/server-ci.yml +++ b/.github/workflows/server-ci.yml @@ -29,19 +29,23 @@ jobs: runs-on: ubuntu-latest steps: - name: Check out the repo - uses: actions/checkout@v4 + uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 - name: Install pnpm - uses: pnpm/action-setup@v4 + uses: pnpm/action-setup@f40ffcd9367d9f12939873eb1018b921a783ffaa # v4 - name: Setup Node.js environment - uses: actions/setup-node@v4 + uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 with: node-version: lts/* cache: "pnpm" - name: Install dependencies - run: pnpm install + run: pnpm install --frozen-lockfile --ignore-scripts + + - name: Generate Nuxt + Prisma + Buf artifacts + working-directory: server + run: pnpm run postinstall - name: Typecheck working-directory: server @@ -52,19 +56,23 @@ jobs: runs-on: ubuntu-latest steps: - name: Check out the repo - uses: actions/checkout@v4 + uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 - name: Install pnpm - uses: pnpm/action-setup@v4 + uses: pnpm/action-setup@f40ffcd9367d9f12939873eb1018b921a783ffaa # v4 - name: Setup Node.js environment - uses: actions/setup-node@v4 + uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 with: node-version: lts/* cache: "pnpm" - name: Install dependencies - run: pnpm install + run: pnpm install --frozen-lockfile --ignore-scripts + + - name: Generate Nuxt + Prisma + Buf artifacts + working-directory: server + run: pnpm run postinstall - name: Lint working-directory: server diff --git a/.github/workflows/server-release.yml b/.github/workflows/server-release.yml index 208f402f..a9c7b613 100644 --- a/.github/workflows/server-release.yml +++ b/.github/workflows/server-release.yml @@ -27,7 +27,7 @@ jobs: contents: read steps: - name: Check out the repo - uses: actions/checkout@v4 + uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 with: fetch-depth: 3 # fix for when this gets triggered by tag fetch-tags: true @@ -41,22 +41,22 @@ jobs: - name: Docker meta id: meta - uses: docker/metadata-action@v5 + uses: docker/metadata-action@c299e40c65443455700f0fdfc63efafe5b349051 # v5 with: images: ${{ env.REGISTRY_IMAGE }} - name: Login to Docker Hub - uses: docker/login-action@v3 + uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - name: Set up QEMU - uses: docker/setup-qemu-action@v3 + uses: docker/setup-qemu-action@c7c53464625b32c7a7e944ae62b3e17d2b600130 # v3 - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 + uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3 - name: Determine final version id: get_final_ver @@ -78,7 +78,7 @@ jobs: - name: Build and push by digest id: build - uses: docker/build-push-action@v6 + uses: docker/build-push-action@10e90e3645eae34f1e60eeb005ba3a3d33f178e8 # v6 with: platforms: ${{ matrix.platform }} labels: ${{ steps.meta.outputs.labels }} @@ -97,7 +97,7 @@ jobs: touch "${{ runner.temp }}/digests/${digest#sha256:}" - name: Upload digest - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 with: name: digests-${{ env.PLATFORM_PAIR }} path: ${{ runner.temp }}/digests/* @@ -113,24 +113,24 @@ jobs: contents: read steps: - name: Download digests - uses: actions/download-artifact@v4 + uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4 with: path: ${{ runner.temp }}/digests pattern: digests-* merge-multiple: true - name: Login to Docker Hub - uses: docker/login-action@v3 + uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 + uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3 - name: Extract metadata (tags, labels) for Docker id: meta - uses: docker/metadata-action@v5 + uses: docker/metadata-action@c299e40c65443455700f0fdfc63efafe5b349051 # v5 with: images: | ghcr.io/drop-OSS/drop diff --git a/.gitignore b/.gitignore index 763301fc..2c5fa590 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ dist/ -node_modules/ \ No newline at end of file +node_modules/ +.omo/ \ No newline at end of file diff --git a/.husky/pre-commit b/.husky/pre-commit new file mode 100644 index 00000000..127cb2d5 --- /dev/null +++ b/.husky/pre-commit @@ -0,0 +1,78 @@ +#!/usr/bin/env bash +# Fallow audit gate — every finding in changed files blocks the commit. +# `gate = "all"` is configured in fallow.toml so inherited findings also gate. +# Base pinned to https://github.com/BillyOutlast/drop/tree/develop. +FALLOW_AUDIT_BASE=origin/develop fallow audit --format json --quiet --explain --gate-marker agent || exit 1 + +# Prisma generate trigger — only when schema or proto files change (build-dep, +# not a content check). +changed_schema=$(git diff --cached --name-only --diff-filter=ACM -- 'server/prisma/schema.prisma' 'server/**/*.proto') +if [ -n "$changed_schema" ]; then + echo "Prisma schema or proto changed — running prisma generate..." + pnpm --filter drop exec prisma generate || exit 1 +fi + +# Staged-files style gate (prettier + cargo fmt --check + shellcheck — see +# server/lint-staged.config.js). ESLint intentionally NOT here: Nuxt3 +# type-aware rules load the full tsconfig regardless of staged subset, so +# lint-staged gives no timing win for ESLint. Full-repo lint runs via +# `pnpm --filter drop lint` only in CI (ci.yml), not on commit. +pnpm --filter drop lint-staged || exit 1 + +# Typecheck must be full-repo (Nuxt type graph). Not staged-scoped. +pnpm --filter drop typecheck || exit 1 + +# Whole-repo shellcheck — all git-tracked .sh files, not just staged ones. +if command -v shellcheck >/dev/null 2>&1; then + tracked_sh=$(git ls-files '*.sh') + if [ -n "$tracked_sh" ]; then + echo "$tracked_sh" | xargs shellcheck --severity=warning || exit 1 + fi +else + echo "shellcheck not installed — skipping shell script checks" +fi + +# Whole-repo bare-assertion scan — flag toBeDefined / .not.toBeNull() without +# a companion meaningful assertion across every test file in the repo. +tracked_tests=$(git ls-files '*.test.ts' '*.spec.ts') +if [ -n "$tracked_tests" ]; then + bare_assertions=$(echo "$tracked_tests" | xargs grep -n '\.toBeDefined()\|\.not\.toBeNull()' 2>/dev/null | grep -v '\.toEqual\|\.toMatchSnapshot\|\.toStrictEqual\|\.toBe(' || true) + if [ -n "$bare_assertions" ]; then + echo "ERROR: Bare .toBeDefined() or .not.toBeNull() without companion assertion:" + echo "$bare_assertions" + echo "Add a meaningful assertion (.toEqual, .toMatchSnapshot, etc.) or suppress with --no-verify." + exit 1 + fi +fi + +# Block focused tests leaking into CI (.only / it.only / describe.only). +# Vitest + Playwright both treat `.only` as a hard focus filter — a single +# .only in a 200-file test suite means CI runs one spec and reports green. +focused=$(echo "$tracked_tests" | xargs grep -nE '(\(it|describe|test)\.only\(|fdescribe\(|fit\(' 2>/dev/null || true) +if [ -n "$focused" ]; then + echo "BLOCKED: Focused test detected — remove .only / fit / fdescribe before committing:" + echo "$focused" + exit 1 +fi + +# Warn (non-blocking) on skipped tests and assertion-less test files. +skipped=$(echo "$tracked_tests" | xargs grep -nE '(\(it|describe|test)\.skip\(|xit\(|xdescribe\(' 2>/dev/null || true) +if [ -n "$skipped" ]; then + echo "WARNING: Skipped test(s) detected (consider removing .skip before commit):" + echo "$skipped" +fi +for tf in $tracked_tests; do + if ! grep -q 'expect\|assert' "$tf" 2>/dev/null; then + echo "WARNING: $tf has no expect/assert calls — test does not assert anything." + fi +done + +# Whole-repo cargo fmt --check across all three rust workspaces (no auto-fix; +# forces developer to format manually if any .rs file drifts). +if command -v cargo >/dev/null 2>&1; then + cargo fmt --all --manifest-path torrential/Cargo.toml -- --check || exit 1 + cargo fmt --all --manifest-path cli/Cargo.toml -- --check || exit 1 + cargo fmt --all --manifest-path desktop/src-tauri/Cargo.toml -- --check || exit 1 +else + echo "cargo not installed — skipping rust format checks" +fi \ No newline at end of file diff --git a/.husky/pre-push b/.husky/pre-push new file mode 100644 index 00000000..62306e26 --- /dev/null +++ b/.husky/pre-push @@ -0,0 +1,69 @@ +#!/usr/bin/env bash +# Pre-push gate: incremental tests, fallow audit, optional full suite, PR thread check. + +set -u + +# Incremental test run: only tests affected by pushed changes. +# Full suite still runs in CI. +if command -v pnpm >/dev/null 2>&1; then + pnpm --filter drop test:changed +else + echo ":: pnpm not found — cannot run incremental tests" >&2 + exit 1 +fi + +# Detect base from upstream tracking. If upstream matches current branch +# (PR branch self-tracking) or unset, fall back to origin/develop. +CURRENT_BRANCH="$(git rev-parse --abbrev-ref HEAD)" +UPSTREAM="$(git rev-parse --abbrev-ref '@{upstream}' 2>/dev/null || true)" +REMOTE_BASE="${UPSTREAM}" +if [ -z "${REMOTE_BASE}" ] || [ "${REMOTE_BASE##*/}" = "${CURRENT_BRANCH}" ]; then + REMOTE_BASE="origin/develop" +fi + +# fallow audit gate: block on 'fail' verdict, tolerate JSON parse errors. +if command -v fallow >/dev/null 2>&1; then + FALLOW_STDERR="$(mktemp)" + FALLOW_JSON="$(FALLOW_AUDIT_BASE="${REMOTE_BASE}" fallow audit --format json --quiet --explain --gate-marker agent 2>"${FALLOW_STDERR}" || echo '{"verdict":"error","error":true}')" + FALLOW_STDERR_CONTENT="$(cat "${FALLOW_STDERR}")" + rm -f "${FALLOW_STDERR}" + if [ -n "${FALLOW_STDERR_CONTENT}" ]; then + echo ":: fallow audit stderr: ${FALLOW_STDERR_CONTENT}" >&2 + fi + if command -v jq >/dev/null 2>&1; then + FALLOW_VERDICT="$(echo "${FALLOW_JSON}" | jq -r '.verdict // "error"' 2>/dev/null || echo "error")" + else + # Fallback: parse verdict without jq so the gate still works + FALLOW_VERDICT="$(echo "${FALLOW_JSON}" | grep -oE '"verdict":"[a-z]+"' | cut -d'"' -f4 || echo "error")" + fi + if [ "${FALLOW_VERDICT}" = "fail" ]; then + echo ":: fallow audit verdict: fail. Fix findings or use --no-verify." + exit 1 + elif [ "${FALLOW_VERDICT}" = "error" ]; then + echo ":: fallow audit returned errors (non-blocking) — continuing" + fi +else + echo ":: fallow CLI not found — skipping audit gate" +fi + +# Optional full test suite gate: verify no regressions before push. +# Disabled by default — CI runs the full suite. Set FULL_TEST=1 to enable. +if [ -n "${FULL_TEST:-}" ]; then + pnpm --filter drop test +fi + +# Check for PR review threads. +# Quick count only — use /pull-review-comments skill for full details + resolution. +if command -v gh >/dev/null 2>&1 && command -v jq >/dev/null 2>&1; then + REPO="$(git remote get-url origin | sed -nE 's#.*github.com[:/]([^/]+/[^/.]+)(\.git)?$#\1#p' 2>/dev/null)" + if [ -z "${REPO}" ]; then + echo ":: unable to parse GitHub repository from remote URL — skipping review thread check" >&2 + exit 0 + fi + PR_NUMBER="$(gh pr view --json number -q .number 2>/dev/null || true)" + if [ -n "${PR_NUMBER}" ]; then + echo ":: PR #${PR_NUMBER} open — run /pull-review-comments for unresolved review threads before pushing" + fi +else + echo ":: gh or jq not found — skipping review thread check" >&2 +fi \ No newline at end of file diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 00000000..d960dbab --- /dev/null +++ b/.prettierignore @@ -0,0 +1,25 @@ +# Build output +dist/ +.nuxt/ +.output/ +coverage/ +target/ + +# Dependencies +**/node_modules/ + +# Generated code (Prisma) +prisma/client/ +**/migrations/ + +# Proto-generated code +**/proto/ + +# External / auto-generated code (not maintained by us) +desktop/libs/appletrust/ + +# Test artifacts +**/test-results/ + +# Documentation sites (separate conventions) +sites/docs/ diff --git a/Dockerfile b/Dockerfile index ecc594e2..f7e5d0f4 100644 --- a/Dockerfile +++ b/Dockerfile @@ -33,7 +33,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ && rm -rf /var/lib/apt/lists/* WORKDIR /build COPY . . -RUN cargo build --release --manifest-path ./torrential/Cargo.toml +RUN cargo build --locked --release --manifest-path ./torrential/Cargo.toml ### BUILD APP FROM base AS build-system diff --git a/desktop/main/.prettierrc.json b/desktop/main/.prettierrc.json new file mode 100644 index 00000000..43083024 --- /dev/null +++ b/desktop/main/.prettierrc.json @@ -0,0 +1,7 @@ +{ + "semi": true, + "singleQuote": false, + "trailingComma": "all", + "tabWidth": 2, + "printWidth": 100 +} diff --git a/desktop/main/vitest.config.ts b/desktop/main/vitest.config.ts new file mode 100644 index 00000000..bf844ba0 --- /dev/null +++ b/desktop/main/vitest.config.ts @@ -0,0 +1,17 @@ +import { defineConfig } from "vitest/config"; +import { fileURLToPath } from "node:url"; + +const rootDir = fileURLToPath(new URL(".", import.meta.url)); + +export default defineConfig({ + resolve: { + alias: { + "~": rootDir, + }, + }, + test: { + environment: "node", + globals: true, + include: ["**/*.test.ts"], + }, +}); diff --git a/fallow.toml b/fallow.toml new file mode 100644 index 00000000..105dffc2 --- /dev/null +++ b/fallow.toml @@ -0,0 +1,21 @@ +# Fallow audit config — entry-point overrides for the test scaffolding +# introduced by port/quality-assets. Without these, fallow's static +# analysis can't trace Playwright testDir and Vitest setupFiles as +# entry points (both are framework-injected, not Nuxt-imported) and +# false-positives every new file as unused. +# +# Husky pre-commit invokes fallow with `gate = "new-only"` (default); +# `ignorePatterns` keeps the audit focused on the affected subdir while +# excluding tooling configuration that's known to be portable. +ignorePatterns = [ + "server/test/**", + "fallow.toml", + ".fallow/**", +] + +# @playwright/test only runs in the Playwright test runner (not loaded by +# Nuxt at runtime). Confirmed in server/package.json as devDependencies; +# fallow flags it as `unlisted_dependencies` because server/playwright.config.ts +# imports it from outside the test/ ignorePattern. Promote to ignored +# rather than promote to runtime dependencies. +ignoreDependencies = ["@playwright/test"] diff --git a/libraries/base/.prettierrc.json b/libraries/base/.prettierrc.json new file mode 100644 index 00000000..43083024 --- /dev/null +++ b/libraries/base/.prettierrc.json @@ -0,0 +1,7 @@ +{ + "semi": true, + "singleQuote": false, + "trailingComma": "all", + "tabWidth": 2, + "printWidth": 100 +} diff --git a/patches/decompress@4.2.1.patch b/patches/decompress@4.2.1.patch new file mode 100644 index 00000000..20e7c442 --- /dev/null +++ b/patches/decompress@4.2.1.patch @@ -0,0 +1,78 @@ +diff --git a/index.js b/index.js +index 6aa67ca9cda4bac1262915ca6e47e97b224d336e..17fcc21d96543efa7d1bdd4266e4c1a2b40ef635 100644 +--- a/index.js ++++ b/index.js +@@ -26,7 +26,8 @@ const safeMakeDir = (dir, realOutputPath) => { + return safeMakeDir(parent, realOutputPath); + }) + .then(realParentPath => { +- if (realParentPath.indexOf(realOutputPath) !== 0) { ++ const rel = path.relative(realOutputPath, realParentPath); ++ if (rel.startsWith('..') || path.isAbsolute(rel)) { + throw (new Error('Refusing to create a directory outside the output path.')); + } + +@@ -51,6 +52,35 @@ const preventWritingThroughSymlink = (destination, realOutputPath) => { + }); + }; + ++// Security: strip setuid, setgid, and sticky bits from extracted file modes. ++// The original code used `x.mode & ~process.umask()` which only clears bits ++// already masked by the process umask, leaving setuid/setgid/sticky intact. ++// A crafted archive could create a setuid binary on extraction, which is ++// especially dangerous when extraction runs as root (CI, containers). ++const safeMode = (mode) => (mode & ~process.umask()) & 0o777 & ~0o7000; ++ ++// Security: resolve a symlink/hardlink target to its realpath and verify ++// it stays inside realOutputPath. Prevents a crafted archive from creating ++// a link to /etc/passwd (hardlink) or a symlink to /tmp (symlink) inside ++// the output directory. ++const resolveAndCheckLinkTarget = (linkname, realOutputPath) => { ++ const resolved = path.resolve(realOutputPath, linkname); ++ return fsP.realpath(resolved) ++ .catch(_ => { ++ // Target doesn't exist yet — for hardlinks, check the parent dir; ++ // for symlinks, the target is checked at write time below. ++ return null; ++ }) ++ .then(realTarget => { ++ if (realTarget) { ++ const rel = path.relative(realOutputPath, realTarget); ++ if (rel.startsWith('..') || path.isAbsolute(rel)) { ++ throw new Error('Refusing to create a link to outside output directory: ' + realTarget); ++ } ++ } ++ }); ++}; ++ + const extractFile = (input, output, opts) => runPlugins(input, opts).then(files => { + if (opts.strip > 0) { + files = files +@@ -75,7 +105,7 @@ const extractFile = (input, output, opts) => runPlugins(input, opts).then(files + + return Promise.all(files.map(x => { + const dest = path.join(output, x.path); +- const mode = x.mode & ~process.umask(); ++ const mode = safeMode(x.mode); + const now = new Date(); + + if (x.type === 'directory') { +@@ -103,11 +133,17 @@ const extractFile = (input, output, opts) => runPlugins(input, opts).then(files + .then(realOutputPath => { + return fsP.realpath(path.dirname(dest)) + .then(realDestinationDir => { +- if (realDestinationDir.indexOf(realOutputPath) !== 0) { ++ const rel = path.relative(realOutputPath, realDestinationDir); ++ if (rel.startsWith('..') || path.isAbsolute(rel)) { + throw (new Error('Refusing to write outside output directory: ' + realDestinationDir)); + } + }); + }) ++ .then(realOutputPath => { ++ if (x.type === 'link' || x.type === 'symlink') { ++ return resolveAndCheckLinkTarget(x.linkname, realOutputPath); ++ } ++ }) + .then(() => { + if (x.type === 'link') { + return fsP.link(x.linkname, dest); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 192fd61b..d468aac4 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -53,6 +53,11 @@ overrides: tough-cookie@<4.1.3: '>=4.1.3' trim-newlines@<3.0.1: '>=3.0.1' +patchedDependencies: + decompress@4.2.1: + hash: 45158ed496346a01b560e676c853793c22191f0f6056b5633694f408fcb7372b + path: patches/decompress@4.2.1.patch + importers: .: {} @@ -84,7 +89,7 @@ importers: devDependencies: '@nuxt/eslint': specifier: latest - version: 1.16.0(@typescript-eslint/utils@8.65.0(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3))(@vue/compiler-sfc@3.5.40)(esbuild@0.28.1)(eslint-import-resolver-node@0.3.10)(eslint@9.39.4(jiti@2.7.0))(magic-string@0.30.21)(magicast@0.5.3)(rolldown@1.1.5)(rollup@4.62.3)(srvx@0.11.22)(typescript@5.9.3)(unplugin@3.3.0(esbuild@0.28.1)(rolldown@1.1.5)(rollup@4.62.3)(vite@8.1.5(@types/node@24.12.2)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.99.0)(terser@5.47.1)(yaml@2.9.0)))(vite@8.1.5(@types/node@24.12.2)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.99.0)(terser@5.47.1)(yaml@2.9.0)) + version: 1.16.0(@typescript-eslint/utils@8.65.0(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3))(@vue/compiler-sfc@3.5.40)(esbuild@0.28.1)(eslint-import-resolver-node@0.3.10)(eslint@9.39.4(jiti@2.7.0))(magic-string@1.1.0)(magicast@0.5.3)(rolldown@1.1.5)(rollup@4.62.3)(srvx@0.11.22)(typescript@5.9.3)(unplugin@3.3.0(esbuild@0.28.1)(rolldown@1.1.5)(rollup@4.62.3)(vite@8.1.5(@types/node@24.12.2)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.99.0)(terser@5.47.1)(yaml@2.9.0)))(vite@8.1.5(@types/node@24.12.2)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.99.0)(terser@5.47.1)(yaml@2.9.0)) eslint: specifier: ^9.17.0 version: 9.39.4(jiti@2.7.0) @@ -108,31 +113,31 @@ importers: version: 16.0.1 '@headlessui/vue': specifier: ^1.7.23 - version: 1.7.23(vue@3.5.40(typescript@5.9.3)) + version: 1.7.23(vue@3.5.17(typescript@5.9.3)) '@heroicons/vue': specifier: ^2.1.5 - version: 2.2.0(vue@3.5.40(typescript@5.9.3)) + version: 2.2.0(vue@3.5.17(typescript@5.9.3)) '@lobomfz/prismark': specifier: 0.0.3 version: 0.0.3 '@nuxt/fonts': specifier: ^0.11.0 - version: 0.11.4(db0@0.3.4(@electric-sql/pglite@0.4.1)(mysql2@3.15.3))(ioredis@5.10.1)(magicast@0.5.3)(vite@8.1.5(@types/node@22.19.17)(esbuild@0.28.0)(jiti@2.7.0)(sass@1.99.0)(terser@5.47.1)(yaml@2.9.0)) + version: 0.11.4(db0@0.3.4(@electric-sql/pglite@0.4.1)(mysql2@3.15.3))(ioredis@5.10.1)(magicast@0.5.2)(vite@8.1.5(@types/node@22.19.17)(esbuild@0.28.0)(jiti@2.7.0)(sass@1.99.0)(terser@5.47.1)(yaml@2.9.0)) '@nuxt/image': specifier: ^1.10.0 - version: 1.11.0(db0@0.3.4(@electric-sql/pglite@0.4.1)(mysql2@3.15.3))(ioredis@5.10.1)(magicast@0.5.3) + version: 1.11.0(db0@0.3.4(@electric-sql/pglite@0.4.1)(mysql2@3.15.3))(ioredis@5.10.1)(magicast@0.5.2) '@nuxt/kit': specifier: ^3.20.1 - version: 3.21.2(magicast@0.5.3) + version: 3.21.2(magicast@0.5.2) '@nuxtjs/i18n': specifier: ^9.5.5 - version: 9.5.6(@vue/compiler-dom@3.5.40)(eslint@9.39.4(jiti@2.7.0))(magicast@0.5.3)(rollup@4.62.3)(vue@3.5.40(typescript@5.9.3)) + version: 9.5.6(@vue/compiler-dom@3.5.40)(eslint@9.39.4(jiti@2.7.0))(magicast@0.5.2)(rollup@4.62.3)(vue@3.5.17(typescript@5.9.3)) '@prisma/adapter-pg': specifier: 7.7.0 version: 7.7.0 '@prisma/client': specifier: 7.7.0 - version: 7.7.0(prisma@7.7.0(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(magicast@0.5.3)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(typescript@5.9.3))(typescript@5.9.3) + version: 7.7.0(prisma@7.7.0(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(magicast@0.5.2)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(typescript@5.9.3))(typescript@5.9.3) '@simplewebauthn/browser': specifier: ^13.2.2 version: 13.3.0 @@ -144,7 +149,7 @@ importers: version: 4.2.2(vite@8.1.5(@types/node@22.19.17)(esbuild@0.28.0)(jiti@2.7.0)(sass@1.99.0)(terser@5.47.1)(yaml@2.9.0)) '@vueuse/nuxt': specifier: 13.6.0 - version: 13.6.0(magicast@0.5.3)(nuxt@3.21.7(@electric-sql/pglite@0.4.1)(@parcel/watcher@2.5.6)(@types/node@22.19.17)(@vue/compiler-sfc@3.5.40)(cac@6.7.14)(db0@0.3.4(@electric-sql/pglite@0.4.1)(mysql2@3.15.3))(esbuild@0.28.0)(eslint@9.39.4(jiti@2.7.0))(ioredis@5.10.1)(lightningcss@1.33.0)(magicast@0.5.3)(mysql2@3.15.3)(optionator@0.9.4)(rolldown@1.1.5)(rollup-plugin-visualizer@7.0.1(rolldown@1.1.5)(rollup@4.62.3))(rollup@4.62.3)(sass@1.99.0)(terser@5.47.1)(typescript@5.9.3)(vite@8.1.5(@types/node@22.19.17)(esbuild@0.28.0)(jiti@2.7.0)(sass@1.99.0)(terser@5.47.1)(yaml@2.9.0))(vue-tsc@3.2.6(typescript@5.9.3))(yaml@2.9.0))(vue@3.5.40(typescript@5.9.3)) + version: 13.6.0(magicast@0.5.2)(nuxt@3.21.7(@electric-sql/pglite@0.4.1)(@parcel/watcher@2.5.6)(@types/node@22.19.17)(@vue/compiler-sfc@3.5.40)(cac@6.7.14)(db0@0.3.4(@electric-sql/pglite@0.4.1)(mysql2@3.15.3))(esbuild@0.28.0)(eslint@9.39.4(jiti@2.7.0))(ioredis@5.10.1)(lightningcss@1.33.0)(magicast@0.5.2)(mysql2@3.15.3)(optionator@0.9.4)(rolldown@1.1.5)(rollup-plugin-visualizer@7.0.1(rolldown@1.1.5)(rollup@4.62.3))(rollup@4.62.3)(sass@1.99.0)(terser@5.47.1)(typescript@5.9.3)(vite@8.1.5(@types/node@22.19.17)(esbuild@0.28.0)(jiti@2.7.0)(sass@1.99.0)(terser@5.47.1)(yaml@2.9.0))(vue-tsc@3.2.6(typescript@5.9.3))(yaml@2.9.0))(vue@3.5.17(typescript@5.9.3)) argon2: specifier: ^0.43.0 version: 0.43.1 @@ -198,10 +203,10 @@ importers: version: 8.1.1 nuxt: specifier: ^3.21.7 - version: 3.21.7(@electric-sql/pglite@0.4.1)(@parcel/watcher@2.5.6)(@types/node@22.19.17)(@vue/compiler-sfc@3.5.40)(cac@6.7.14)(db0@0.3.4(@electric-sql/pglite@0.4.1)(mysql2@3.15.3))(esbuild@0.28.0)(eslint@9.39.4(jiti@2.7.0))(ioredis@5.10.1)(lightningcss@1.33.0)(magicast@0.5.3)(mysql2@3.15.3)(optionator@0.9.4)(rolldown@1.1.5)(rollup-plugin-visualizer@7.0.1(rolldown@1.1.5)(rollup@4.62.3))(rollup@4.62.3)(sass@1.99.0)(terser@5.47.1)(typescript@5.9.3)(vite@8.1.5(@types/node@22.19.17)(esbuild@0.28.0)(jiti@2.7.0)(sass@1.99.0)(terser@5.47.1)(yaml@2.9.0))(vue-tsc@3.2.6(typescript@5.9.3))(yaml@2.9.0) + version: 3.21.7(@electric-sql/pglite@0.4.1)(@parcel/watcher@2.5.6)(@types/node@22.19.17)(@vue/compiler-sfc@3.5.40)(cac@6.7.14)(db0@0.3.4(@electric-sql/pglite@0.4.1)(mysql2@3.15.3))(esbuild@0.28.0)(eslint@9.39.4(jiti@2.7.0))(ioredis@5.10.1)(lightningcss@1.33.0)(magicast@0.5.2)(mysql2@3.15.3)(optionator@0.9.4)(rolldown@1.1.5)(rollup-plugin-visualizer@7.0.1(rolldown@1.1.5)(rollup@4.62.3))(rollup@4.62.3)(sass@1.99.0)(terser@5.47.1)(typescript@5.9.3)(vite@8.1.5(@types/node@22.19.17)(esbuild@0.28.0)(jiti@2.7.0)(sass@1.99.0)(terser@5.47.1)(yaml@2.9.0))(vue-tsc@3.2.6(typescript@5.9.3))(yaml@2.9.0) nuxt-security: specifier: 2.2.0 - version: 2.2.0(magicast@0.5.3)(rollup@4.62.3) + version: 2.2.0(magicast@0.5.2)(rollup@4.62.3) otp-io: specifier: ^1.2.7 version: 1.2.7 @@ -216,7 +221,7 @@ importers: version: 13.1.3 prisma: specifier: 7.7.0 - version: 7.7.0(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(magicast@0.5.3)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(typescript@5.9.3) + version: 7.7.0(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(magicast@0.5.2)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(typescript@5.9.3) sanitize-filename: specifier: ^1.6.3 version: 1.6.4 @@ -236,20 +241,20 @@ importers: specifier: ^1.15.0 version: 1.17.5(db0@0.3.4(@electric-sql/pglite@0.4.1)(mysql2@3.15.3))(ioredis@5.10.1) vue: - specifier: latest - version: 3.5.40(typescript@5.9.3) + specifier: 3.5.17 + version: 3.5.17(typescript@5.9.3) vue-router: - specifier: latest - version: 5.2.0(@vue/compiler-sfc@3.5.40)(esbuild@0.28.0)(rolldown@1.1.5)(rollup@4.62.3)(vite@8.1.5(@types/node@22.19.17)(esbuild@0.28.0)(jiti@2.7.0)(sass@1.99.0)(terser@5.47.1)(yaml@2.9.0))(vue@3.5.40(typescript@5.9.3)) + specifier: 4.5.1 + version: 4.5.1(vue@3.5.17(typescript@5.9.3)) vue3-carousel: specifier: ^0.16.0 - version: 0.16.0(vue@3.5.40(typescript@5.9.3)) + version: 0.16.0(vue@3.5.17(typescript@5.9.3)) vue3-carousel-nuxt: specifier: ^1.1.5 - version: 1.1.6(magicast@0.5.3)(vue@3.5.40(typescript@5.9.3)) + version: 1.1.6(magicast@0.5.2)(vue@3.5.17(typescript@5.9.3)) vuedraggable: specifier: ^4.1.0 - version: 4.1.0(vue@3.5.40(typescript@5.9.3)) + version: 4.1.0(vue@3.5.17(typescript@5.9.3)) devDependencies: '@bufbuild/buf': specifier: ^1.65.0 @@ -265,7 +270,13 @@ importers: version: 4.3.0(eslint@9.39.4(jiti@2.7.0))(jsonc-eslint-parser@2.4.2)(vue-eslint-parser@10.4.1(eslint@9.39.4(jiti@2.7.0)))(yaml-eslint-parser@1.3.2) '@nuxt/eslint': specifier: ^1.3.0 - version: 1.15.2(@typescript-eslint/utils@8.58.2(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3))(@vue/compiler-sfc@3.5.40)(eslint-import-resolver-node@0.3.10)(eslint@9.39.4(jiti@2.7.0))(magic-string@0.30.21)(magicast@0.5.3)(oxc-parser@0.132.0)(rolldown@1.1.5)(typescript@5.9.3)(unplugin@3.3.0(esbuild@0.28.0)(rolldown@1.1.5)(rollup@4.62.3)(vite@8.1.5(@types/node@22.19.17)(esbuild@0.28.0)(jiti@2.7.0)(sass@1.99.0)(terser@5.47.1)(yaml@2.9.0)))(vite@8.1.5(@types/node@22.19.17)(esbuild@0.28.0)(jiti@2.7.0)(sass@1.99.0)(terser@5.47.1)(yaml@2.9.0)) + version: 1.15.2(@typescript-eslint/utils@8.58.2(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3))(@vue/compiler-sfc@3.5.40)(eslint-import-resolver-node@0.3.10)(eslint@9.39.4(jiti@2.7.0))(magicast@0.5.2)(oxc-parser@0.132.0)(rolldown@1.1.5)(typescript@5.9.3)(vite@8.1.5(@types/node@22.19.17)(esbuild@0.28.0)(jiti@2.7.0)(sass@1.99.0)(terser@5.47.1)(yaml@2.9.0)) + '@nuxt/test-utils': + specifier: ^4.0.3 + version: 4.1.0(@playwright/test@1.62.0)(@vue/test-utils@2.4.11(@vue/compiler-dom@3.5.40)(@vue/server-renderer@3.5.40)(vue@3.5.17(typescript@5.9.3)))(esbuild@0.28.0)(happy-dom@20.11.1)(magicast@0.5.2)(playwright-core@1.62.0)(rolldown@1.1.5)(rollup@4.62.3)(typescript@5.9.3)(vite@8.1.5(@types/node@22.19.17)(esbuild@0.28.0)(jiti@2.7.0)(sass@1.99.0)(terser@5.47.1)(yaml@2.9.0))(vitest@4.1.10) + '@playwright/test': + specifier: ^1.61.1 + version: 1.62.0 '@tailwindcss/forms': specifier: ^0.5.9 version: 0.5.11(tailwindcss@4.2.2) @@ -290,6 +301,12 @@ importers: '@typescript-eslint/utils': specifier: ^8.50.0 version: 8.58.2(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3) + '@vitest/coverage-v8': + specifier: ^4.1.10 + version: 4.1.10(vitest@4.1.10) + '@vue/test-utils': + specifier: ^2.4.11 + version: 2.4.11(@vue/compiler-dom@3.5.40)(@vue/server-renderer@3.5.40)(vue@3.5.17(typescript@5.9.3)) autoprefixer: specifier: ^10.4.20 version: 10.5.0(postcss@8.5.24) @@ -299,12 +316,30 @@ importers: eslint-config-prettier: specifier: ^10.1.1 version: 10.1.8(eslint@9.39.4(jiti@2.7.0)) + eslint-plugin-vuejs-accessibility: + specifier: ^2.5.0 + version: 2.5.0(eslint@9.39.4(jiti@2.7.0))(globals@17.8.0) + fast-check: + specifier: ^4.9.0 + version: 4.9.0 golar: specifier: ^0.0.13 version: 0.0.13(@golar/vue@0.0.13) h3: specifier: ^1.15.5 version: 1.15.11 + happy-dom: + specifier: ^20.11.1 + version: 20.11.1 + husky: + specifier: ^9.1.7 + version: 9.1.7 + lint-staged: + specifier: ^17.2.0 + version: 17.2.0 + msw: + specifier: ^2.15.0 + version: 2.15.0(@types/node@22.19.17)(typescript@5.9.3) nitropack: specifier: ^2.13.4 version: 2.13.4(@electric-sql/pglite@0.4.1)(mysql2@3.15.3)(oxc-parser@0.132.0)(rolldown@1.1.5)(vite@8.1.5(@types/node@22.19.17)(esbuild@0.28.0)(jiti@2.7.0)(sass@1.99.0)(terser@5.47.1)(yaml@2.9.0)) @@ -323,9 +358,15 @@ importers: tailwindcss: specifier: ^4.0.0 version: 4.2.2 + type-coverage: + specifier: ^2.30.1 + version: 2.30.1(typescript@5.9.3) typescript: specifier: ^5.8.3 version: 5.9.3 + vitest: + specifier: ^4.1.10 + version: 4.1.10(@types/node@22.19.17)(@vitest/coverage-v8@4.1.10)(happy-dom@20.11.1)(msw@2.15.0(@types/node@22.19.17)(typescript@5.9.3))(vite@8.1.5(@types/node@22.19.17)(esbuild@0.28.0)(jiti@2.7.0)(sass@1.99.0)(terser@5.47.1)(yaml@2.9.0)) vue-tsc: specifier: ^3.0.1 version: 3.2.6(typescript@5.9.3) @@ -376,7 +417,7 @@ importers: version: 12.38.0(@emotion/is-prop-valid@1.4.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) next: specifier: 15.5.21 - version: 15.5.21(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(sass@1.99.0) + version: 15.5.21(@playwright/test@1.62.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(sass@1.99.0) react: specifier: ^19 version: 19.2.5 @@ -398,7 +439,7 @@ importers: version: 0.2.2(@content-collections/core@0.11.1(typescript@5.9.3))(react-dom@19.2.5(react@19.2.5))(react@19.2.5) '@content-collections/next': specifier: ^0.2.7 - version: 0.2.11(@content-collections/core@0.11.1(typescript@5.9.3))(next@15.5.21(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(sass@1.99.0)) + version: 0.2.11(@content-collections/core@0.11.1(typescript@5.9.3))(next@15.5.21(@playwright/test@1.62.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(sass@1.99.0)) '@eslint/eslintrc': specifier: ^3 version: 3.3.5 @@ -605,10 +646,6 @@ packages: resolution: {integrity: sha512-DkXD5OJQaAQIdZ1bt3UZdEnHAn9Imd3IVBdX03UFe+ony9Ojw5pzr9YVKGDY1jt+Gcn/FnGkNf8r+Vj5NOJWtQ==} engines: {node: '>=6.9.0'} - '@babel/generator@8.0.0': - resolution: {integrity: sha512-NT9NrVwJsbSV6Y2FSstWa71EETOnzrjkL5/wX3D2mYHtKM+qvqB1DvR4D0Setb/gDBsHzRICifwEWMO8CnTF6g==} - engines: {node: ^22.18.0 || >=24.11.0} - '@babel/helper-annotate-as-pure@7.29.7': resolution: {integrity: sha512-OoK6239jHPuSQOoS0kfTVKn0b/rVTk0seKq4Gd2UMLtmOVLjDC0ki3e+c90Trqv2gMfvJFqkiljrr568+qddiw==} engines: {node: '>=6.9.0'} @@ -685,10 +722,6 @@ packages: resolution: {integrity: sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==} engines: {node: '>=6.9.0'} - '@babel/helper-string-parser@8.0.0': - resolution: {integrity: sha512-6mJgmFFFIIO82vvoLt9XtRC7/TkzXfts1t/SpRX4IHSzMgqoPYCWesVu1udUPUWioAE/2fcG6WuI8zrkE1gwrg==} - engines: {node: ^22.18.0 || >=24.11.0} - '@babel/helper-validator-identifier@7.28.5': resolution: {integrity: sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==} engines: {node: '>=6.9.0'} @@ -697,10 +730,6 @@ packages: resolution: {integrity: sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==} engines: {node: '>=6.9.0'} - '@babel/helper-validator-identifier@8.0.4': - resolution: {integrity: sha512-4wFaiLd0bVo4cIoTXI3zKI038NIWE/cr3jvBjejOVYVxV/m8Ltav1USiGzG1fmS5J2RhgEOgXNNK46cRPnRsrg==} - engines: {node: ^22.18.0 || >=24.11.0} - '@babel/helper-validator-option@7.27.1': resolution: {integrity: sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==} engines: {node: '>=6.9.0'} @@ -732,11 +761,6 @@ packages: engines: {node: '>=6.0.0'} hasBin: true - '@babel/parser@8.0.4': - resolution: {integrity: sha512-srpptsAkEbbNIC/q8nT7o+m6CQe8CJUTV/t7MYc9NnWlgYVtHOb7JH6SorxMhN0kuRJjVqXbKClG6xSbPtzz+g==} - engines: {node: ^22.18.0 || >=24.11.0} - hasBin: true - '@babel/plugin-syntax-jsx@7.29.7': resolution: {integrity: sha512-TSu8+mHCoEaaCDEZ0I3+6mvTBYR4PCxQwf2z9/r5Tbztv6NaLR3B9thGTTxX2WGuGHJqRiAbKPeGTJ5XWXVg6A==} engines: {node: '>=6.9.0'} @@ -783,9 +807,9 @@ packages: resolution: {integrity: sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==} engines: {node: '>=6.9.0'} - '@babel/types@8.0.4': - resolution: {integrity: sha512-eY+Yn3dCqTGmyiq2QRU66lA5FL8lqqqvecHt0fF3uHONIa7ToYsaCiWV8lOKqAs0Rb2SjixiKFROngnulPtt2g==} - engines: {node: ^22.18.0 || >=24.11.0} + '@bcoe/v8-coverage@1.0.2': + resolution: {integrity: sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA==} + engines: {node: '>=18'} '@bomb.sh/tab@0.0.19': resolution: {integrity: sha512-dTRfo9Q9B+lbLG3JCu8a/AGQSfD2XXcFcnakQzVjSOX+VvR/s9zpsH8TlqV3iHqazniRn1Ypwd1hcRlXcu/4BA==} @@ -2200,6 +2224,41 @@ packages: cpu: [x64] os: [win32] + '@inquirer/ansi@2.0.7': + resolution: {integrity: sha512-3eTuUO1vH2cZm2ZKHeQxnOqlTi9EfZDGgIe3BL3I4u+rJHocr9Fz86M4fjYABPvFnQG/gGK551HqDiIcETwU6Q==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} + + '@inquirer/confirm@6.1.1': + resolution: {integrity: sha512-eb8DBZcz/2qHWQda4rk2JiQk5h9QV/cVHi1yjt0f69WFZMRFn0sJTye3EAP8icut8UDMjQPsaH5KbcOogefrFQ==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/core@11.2.1': + resolution: {integrity: sha512-Qd6GJT1yVyrZZCfN8W2qKF5ApmqryXRhRKCuip8h01x2w/esJQ2XIYc6f9abMIHgKQdBfFTSOdbHRLAhuM09UA==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/figures@2.0.7': + resolution: {integrity: sha512-aJ8TBPOGB6f/2qziPfElISTCEd5XOYTFckA2SGjhNmiKzfK/u4ot3v0DUzGVdUnKjN10EqnnEPck36BkyfLnJw==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} + + '@inquirer/type@4.0.7': + resolution: {integrity: sha512-t28inv14nMQ1PhKpsJPY+kEs/c00qzeCOS2gTNRyTjG5d6qsVA2fItxW4hkvGZ5lvanGLdtCzVIx5dwdRpN1+g==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + '@internationalized/date@3.12.1': resolution: {integrity: sha512-6IedsVWXyq4P9Tj+TxuU8WGWM70hYLl12nbYU8jkikVpa6WXapFazPUcHUMDMoWftIDE2ILDkFFte6W2nFCkRQ==} @@ -2371,6 +2430,10 @@ packages: peerDependencies: rollup: '>=4.59.0' + '@mswjs/interceptors@0.41.9': + resolution: {integrity: sha512-VVPPgHyQ6ShqnrmDWuxjmUIsO9gWyOZFmuOfLd9LfBGQJwZfy0gvv9pbHSJuoFNIYC7ZDX9aoFwowjcdSC4E8w==} + engines: {node: '>=18'} + '@napi-rs/wasm-runtime@0.2.12': resolution: {integrity: sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ==} @@ -2556,6 +2619,10 @@ packages: resolution: {integrity: sha512-4kzhvb2tJfxMsa/JZeYn1sMiGbx2J/S6BQrQSdXNsHgSvywGVkFhTiQGjoP6O49EsXyAouJrer47hMeBcTcfXQ==} engines: {node: '>=18.20.6'} + '@nuxt/kit@3.21.10': + resolution: {integrity: sha512-zs1+BZlRK1VlHo37TTzaMXQ+SemS1WeGRtcjWbW1ZoXpL3/ctn2VQu6nFpu6JqsJRd0QAPNS4GYmxGwLK8EHNw==} + engines: {node: '>=18.12.0'} + '@nuxt/kit@3.21.2': resolution: {integrity: sha512-Bd6m6mrDrqpBEbX+g0rc66/ALd1sxlgdx5nfK9MAYO0yKLTOSK7McSYz1KcOYn3LQFCXOWfvXwaqih/b+REI1g==} engines: {node: '>=18.12.0'} @@ -2589,6 +2656,45 @@ packages: peerDependencies: '@nuxt/kit': '>=3.0.0' + '@nuxt/test-utils@4.1.0': + resolution: {integrity: sha512-B0CipGKVVY5qbLMXJqYPYdalNzE9VFzybEAOqHGFHPDqv/8RFRe+/F3lJJigtLFroxaBDMMyrhLcmgKIXBv8og==} + engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} + peerDependencies: + '@cucumber/cucumber': '>=11.0.0' + '@jest/globals': '>=30.0.0' + '@playwright/test': ^1.43.1 + '@testing-library/vue': ^8.0.1 + '@vitest/ui': '*' + '@vue/test-utils': ^2.4.2 + h3-next: '>=2.0.1-rc.22' + happy-dom: '>=20.0.11' + jsdom: '>=27.4.0' + playwright-core: ^1.43.1 + vitest: ^4.0.2 + peerDependenciesMeta: + '@cucumber/cucumber': + optional: true + '@jest/globals': + optional: true + '@playwright/test': + optional: true + '@testing-library/vue': + optional: true + '@vitest/ui': + optional: true + '@vue/test-utils': + optional: true + h3-next: + optional: true + happy-dom: + optional: true + jsdom: + optional: true + playwright-core: + optional: true + vitest: + optional: true + '@nuxt/vite-builder@3.21.7': resolution: {integrity: sha512-Pj6829X10pfYW+KstIBjhNj2cjFMNSxpUHcSy4c0NOE28l/6RbL9Gd4AGe5F97xtOaNb42D7DLFeq9WKKYtwiw==} engines: {node: ^20.19.0 || >=22.12.0} @@ -2637,6 +2743,21 @@ packages: '@octokit/types@16.0.0': resolution: {integrity: sha512-sKq+9r1Mm4efXW1FCk7hFSeJo4QKreL/tTbR0rz/qx/r1Oa2VV83LTA/H/MuCOX7uCIJmQVRKBcbmWoySjAnSg==} + '@one-ini/wasm@0.1.1': + resolution: {integrity: sha512-XuySG1E38YScSJoMlqovLru4KTUNSjgVTIjyh7qMX6aNN5HY5Ct5LhRJdxO79JtTzKfzV/bnWpz+zquYrISsvw==} + + '@open-draft/deferred-promise@2.2.0': + resolution: {integrity: sha512-CecwLWx3rhxVQF6V4bAgPS5t+So2sTbPgAzafKkVizyi7tlwpcFpdFqq+wqF2OwNBmqFuu6tOyouTuxgpMfzmA==} + + '@open-draft/deferred-promise@3.0.0': + resolution: {integrity: sha512-XW375UK8/9SqUVNVa6M0yEy8+iTi4QN5VZ7aZuRFQmy76LRwI9wy5F4YIBU6T+eTe2/DNDo8tqu8RHlwLHM6RA==} + + '@open-draft/logger@0.3.0': + resolution: {integrity: sha512-X2g45fzhxH238HKO4xbSr7+wBS8Fvw6ixhTDuvLd5mqh6bJJCFAPwU9mPDxbcrRtfxv4u5IHCEH77BmxvXmmxQ==} + + '@open-draft/until@2.1.0': + resolution: {integrity: sha512-U69T3ItWHvLwGg5eJ0n3I62nWuE6ilHlmz7zM0npLBRvPRd7e6NYmg54vvRtP5mZG7kZqZCFVdsTWo7BPtBujg==} + '@oslojs/encoding@1.1.0': resolution: {integrity: sha512-70wQhgYmndg4GCPxPPxPGevRKqTIJ2Nh4OkiMWmDAVYsTQ+Ta7Sq+rPevXyXGdzr30/qZBnyOalCszoMxlyldQ==} @@ -3319,6 +3440,11 @@ packages: resolution: {integrity: sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA==} engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0} + '@playwright/test@1.62.0': + resolution: {integrity: sha512-9zOJ6ZQRAena31MpOH9VSzIz8Ou3YJ/wtY/eQm5T2uhfhG7/U3COrMS8xOtUrZrp9OgdmzEnIYODye3nY1VqzA==} + engines: {node: '>=20'} + hasBin: true + '@polka/url@1.0.0-next.29': resolution: {integrity: sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==} @@ -4252,9 +4378,15 @@ packages: '@tybys/wasm-util@0.10.3': resolution: {integrity: sha512-F3fo1MYrRJYL3zER0OUOmkutjr1Vp23m7OsSgp7nq4SP6OqX6C/56XFIPAl5bt3zaBRjmW7SGz3u/6LwFpYcOg==} + '@types/chai@5.2.3': + resolution: {integrity: sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==} + '@types/debug@4.1.13': resolution: {integrity: sha512-KSVgmQmzMwPlmtljOomayoR89W4FynCAi3E8PPs7vmDVPe84hT+vGPKkJfThkmXs0x0jAaa9U8uW8bbfyS2fWw==} + '@types/deep-eql@4.0.2': + resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==} + '@types/esrecurse@4.3.1': resolution: {integrity: sha512-xJBAbDifo5hpffDBuHl0Y8ywswbiAp/Wi7Y/GtAgSlZyIABppyurxVueOPE8LUQOxdlgi6Zqce7uoEpqNTeiUw==} @@ -4282,9 +4414,6 @@ packages: '@types/js-yaml@4.0.9': resolution: {integrity: sha512-k4MGaQl5TGo/iipqb2UDG2UwjXziSWkh0uysQelTlJpX1qGlpUZYm8PnO4DxG1qBomtJUdYJ6qR6xdIah10JLg==} - '@types/jsesc@2.5.1': - resolution: {integrity: sha512-9VN+6yxLOPLOav+7PwjZbxiID2bVaeq0ED4qSQmdQTdjnXJSaCVKTR58t15oqH1H5t8Ng2ZX1SabJVoN9Q34bw==} - '@types/json-schema@7.0.15': resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} @@ -4348,6 +4477,12 @@ packages: '@types/semver@7.7.1': resolution: {integrity: sha512-FmgJfu+MOcQ370SD0ev7EI8TlCAfKYU+B4m5T3yXc1CiRN94g/SZPtsCkk506aUDtlMnFZvasDwHHUcZUEaYuA==} + '@types/set-cookie-parser@2.4.10': + resolution: {integrity: sha512-GGmQVGpQWUe5qglJozEjZV/5dyxbOOZ0LHe/lqyWssB88Y4svNfst0uqBVscdDeIKl5Jy5+aPSvy7mI9tYRguw==} + + '@types/statuses@2.0.6': + resolution: {integrity: sha512-xMAgYwceFhRA2zY+XbEA7mxYbA093wdiW8Vu6gZPGWy9cmOyU9XesH1tNcEWsKFd5Vzrqx5T3D38PWx1FIIXkA==} + '@types/turndown@5.0.6': resolution: {integrity: sha512-ru00MoyeeouE5BX4gRL+6m/BsDfbRayOskWqUvh7CLGW+UXxHQItqALa38kKnOiZPqJrtzJUgAC2+F0rL1S4Pg==} @@ -4360,6 +4495,12 @@ packages: '@types/web-bluetooth@0.0.21': resolution: {integrity: sha512-oIQLCGWtcFZy2JW77j9k8nHzAOpqMHLQejDA48XXMWH6tjCQHz5RCFz1bzsmROyL6PUm+LLnUiI4BCn221inxA==} + '@types/whatwg-mimetype@3.0.2': + resolution: {integrity: sha512-c2AKvDT8ToxLIOUlN51gTiHXflsfIFisS4pO7pDPoKouJCESkhZnEy623gwP9laCy5lnLDAw1vAzu2vM2YLOrA==} + + '@types/ws@8.18.1': + resolution: {integrity: sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==} + '@typescript-eslint/eslint-plugin@8.58.2': resolution: {integrity: sha512-aC2qc5thQahutKjP+cl8cgN9DWe3ZUqVko30CMSZHnFEHyhOYoZSzkGtAI2mcwZ38xeImDucI4dnqsHiOYuuCw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -4739,6 +4880,44 @@ packages: vite: ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 vue: ^3.2.25 + '@vitest/coverage-v8@4.1.10': + resolution: {integrity: sha512-IM49HmthevbgAO4anp1hwtoT9wYe59w0LR00gr+eagHE+ZJ5lK4sLPeO0ubgoJcwLk6dehU3R24N+FbEEKDc8g==} + peerDependencies: + '@vitest/browser': 4.1.10 + vitest: 4.1.10 + peerDependenciesMeta: + '@vitest/browser': + optional: true + + '@vitest/expect@4.1.10': + resolution: {integrity: sha512-YsCn+qAk1GWjQOWFEsEcL2gNQ0zmVmQu3T03qP6UyjhtmdtwtbuI+DASn/7iQB3HGTXkdBwGddzxPlmiql5vlA==} + + '@vitest/mocker@4.1.10': + resolution: {integrity: sha512-v0xaezt+DKEmKfaxg133ldzADrwLGd7Ze1MfQQTYfvs8OqZIwbxyxaYURivwV7sWy5fqn3rH5uOrSp07bp44Ow==} + peerDependencies: + msw: ^2.4.9 + vite: ^6.0.0 || ^7.0.0 || ^8.0.0 + peerDependenciesMeta: + msw: + optional: true + vite: + optional: true + + '@vitest/pretty-format@4.1.10': + resolution: {integrity: sha512-W1HsjSH4MXQ9YfmmhLAoIYf1HRfekQCGngeIgcei6MP5QQGWUe0gkopdZQaVCFO+JDJMrAJGwa5pRpNpvy4P8Q==} + + '@vitest/runner@4.1.10': + resolution: {integrity: sha512-IKI6kpIH+LmpROplyLwBBaCfMgOZOMsygVa6BARD6ahA04VRuJSa6OaVG7kRvSEMD870Vd91rSSw0eegtWyLGg==} + + '@vitest/snapshot@4.1.10': + resolution: {integrity: sha512-xRkfOT1qpTAi/Ti4Y1LtfRc3kEuqxGw59eN2jN9pRWMtS/XDevekhcFSqvQqjUNGksfjMJu3Y+oJ+4Ypn2OaJw==} + + '@vitest/spy@4.1.10': + resolution: {integrity: sha512-PLf/Ugvoq5wO/b4rwYCR1h2PSIdXz7wnkQFMiUpLdtM7l6pqVFcQIBEHyT1+l+cj7mNwAfZHzqXqDyjvOuwbDw==} + + '@vitest/utils@4.1.10': + resolution: {integrity: sha512-fy9am/HWxbaGt/Sawrp90vt6Y6jQwf1RX77cz3uwoJwJVMli/e1IEwRPnMNJ7vKfPTwo0diXifkpPvwH9v7nGA==} + '@volar/language-core@2.4.27': resolution: {integrity: sha512-DjmjBWZ4tJKxfNC1F6HyYERNHPYS7L7OPFyCrestykNdUZMFYzI9WTyvwPcaNaHlrEUwESHYsfEw3isInncZxQ==} @@ -4788,24 +4967,36 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 + '@vue/compiler-core@3.5.17': + resolution: {integrity: sha512-Xe+AittLbAyV0pabcN7cP7/BenRBNcteM4aSDCtRvGw0d9OL+HG1u/XHLY/kt1q4fyMeZYXyIYrsHuPSiDPosA==} + '@vue/compiler-core@3.5.32': resolution: {integrity: sha512-4x74Tbtqnda8s/NSD6e1Dr5p1c8HdMU5RWSjMSUzb8RTcUQqevDCxVAitcLBKT+ie3o0Dl9crc/S/opJM7qBGQ==} '@vue/compiler-core@3.5.40': resolution: {integrity: sha512-39E8IgOhTbVDnoJFMKc2DvYnypcZwUqgUhQkccva/0m6FUwtIKSGV7n1hpVmYcFaoRAwf9pBcwnKlCEsN63ZEQ==} + '@vue/compiler-dom@3.5.17': + resolution: {integrity: sha512-+2UgfLKoaNLhgfhV5Ihnk6wB4ljyW1/7wUIog2puUqajiC29Lp5R/IKDdkebh9jTbTogTbsgB+OY9cEWzG95JQ==} + '@vue/compiler-dom@3.5.32': resolution: {integrity: sha512-ybHAu70NtiEI1fvAUz3oXZqkUYEe5J98GjMDpTGl5iHb0T15wQYLR4wE3h9xfuTNA+Cm2f4czfe8B4s+CCH57Q==} '@vue/compiler-dom@3.5.40': resolution: {integrity: sha512-pwkx4vqlqOspFstrcmzwkKLePVMD3PT65imRzLhanU2V1Fj4K13g6OXjanOyzw3aTAuRk84BOmY8f3rEHqPaVA==} + '@vue/compiler-sfc@3.5.17': + resolution: {integrity: sha512-rQQxbRJMgTqwRugtjw0cnyQv9cP4/4BxWfTdRBkqsTfLOHWykLzbOc3C4GGzAmdMDxhzU/1Ija5bTjMVrddqww==} + '@vue/compiler-sfc@3.5.32': resolution: {integrity: sha512-8UYUYo71cP/0YHMO814TRZlPuUUw3oifHuMR7Wp9SNoRSrxRQnhMLNlCeaODNn6kNTJsjFoQ/kqIj4qGvya4Xg==} '@vue/compiler-sfc@3.5.40': resolution: {integrity: sha512-gIf497P4kpuALcvs5n3AEg1Vdn0pSY4XbjASIfHNYF1/MP3T2Mf2STERTubysBxCRxzJGJYtF/O7vwJrxFB3Vw==} + '@vue/compiler-ssr@3.5.17': + resolution: {integrity: sha512-hkDbA0Q20ZzGgpj5uZjb9rBzQtIHLS78mMilwrlpWk2Ep37DYntUz0PonQ6kr113vfOEdM+zTBuJDaceNIW0tQ==} + '@vue/compiler-ssr@3.5.32': resolution: {integrity: sha512-Gp4gTs22T3DgRotZ8aA/6m2jMR+GMztvBXUBEUOYOcST+giyGWJ4WvFd7QLHBkzTxkfOt8IELKNdpzITLbA2rw==} @@ -4815,9 +5006,6 @@ packages: '@vue/devtools-api@6.6.4': resolution: {integrity: sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==} - '@vue/devtools-api@8.2.1': - resolution: {integrity: sha512-6u4vXBlIBAC1wMplIZgpyPn7uh/s4Bf6F5bMzvLv+EdJ0aHs/+4B7Ygv864EStQSjRbsRzTko/kUG1A1IejQ3A==} - '@vue/devtools-core@8.2.1': resolution: {integrity: sha512-s/VfAY9oDTb/kFEWmy461jaFde2MIV1RO/gi1vwM+PAZBZ/Pc2Ndu3BNBdZUze8QDUuyYvElbEEGA83syjJfzA==} peerDependencies: @@ -4838,24 +5026,51 @@ packages: '@vue/language-core@3.3.8': resolution: {integrity: sha512-ieGT8jJdhhy0mGzStZhsg/qPw5bQZJg5yF+3+XU6saf4sM7yo9ZXy3h+nCwrm2+b4qS/SypkNdR2jAF3uei9tA==} + '@vue/reactivity@3.5.17': + resolution: {integrity: sha512-l/rmw2STIscWi7SNJp708FK4Kofs97zc/5aEPQh4bOsReD/8ICuBcEmS7KGwDj5ODQLYWVN2lNibKJL1z5b+Lw==} + '@vue/reactivity@3.5.40': resolution: {integrity: sha512-B7ot9UlUZOi1zbq61/LvE88ZLTV8IlajTdiZTAEiDQgrnIMIZoPr9kGw0Zw46ObW62O9+H/Be3kMbfb7kYPQZA==} + '@vue/runtime-core@3.5.17': + resolution: {integrity: sha512-QQLXa20dHg1R0ri4bjKeGFKEkJA7MMBxrKo2G+gJikmumRS7PTD4BOU9FKrDQWMKowz7frJJGqBffYMgQYS96Q==} + '@vue/runtime-core@3.5.40': resolution: {integrity: sha512-KAZLweuZ6uUJPK1PMSQPgBU5gCjgrrfjUhSglmU9NhH+Zjepa8cnwSydPWDWHDwOgY4g3VcZ+PljbiHlURNCbw==} + '@vue/runtime-dom@3.5.17': + resolution: {integrity: sha512-8El0M60TcwZ1QMz4/os2MdlQECgGoVHPuLnQBU3m9h3gdNRW9xRmI8iLS4t/22OQlOE6aJvNNlBiCzPHur4H9g==} + '@vue/runtime-dom@3.5.40': resolution: {integrity: sha512-ZfrX8ssZQds900L9pr8AuK05ddnMsR4MPMZr8cPN9GoqoPWcXLhjvvbIA2SMv+7a97sJ1vv9pj/zxK0Cq/eEFQ==} + '@vue/server-renderer@3.5.17': + resolution: {integrity: sha512-BOHhm8HalujY6lmC3DbqF6uXN/K00uWiEeF22LfEsm9Q93XeJ/plHTepGwf6tqFcF7GA5oGSSAAUock3VvzaCA==} + peerDependencies: + vue: 3.5.17 + '@vue/server-renderer@3.5.40': resolution: {integrity: sha512-XNJym9WpevhTVt1HuwOrCRJ5Q+9z4BjTMrDtjTrvx74SmUll8spNTw6whWJa9mEkO4PKn5TihI/bm/8ds2QVJw==} + '@vue/shared@3.5.17': + resolution: {integrity: sha512-CabR+UN630VnsJO/jHWYBC1YVXyMq94KKp6iF5MQgZJs5I8cmjw6oVMO1oDbtBkENSHSSn/UadWlW/OAgdmKrg==} + '@vue/shared@3.5.32': resolution: {integrity: sha512-ksNyrmRQzWJJ8n3cRDuSF7zNNontuJg1YHnmWRJd2AMu8Ij2bqwiiri2lH5rHtYPZjj4STkNcgcmiQqlOjiYGg==} '@vue/shared@3.5.40': resolution: {integrity: sha512-WxnBtruIqOoV3rA4jeKDWzrYI5h7Cp4+pjwDi8kWGHz+IslhiN+wguLVVhtv2l8VoU02rzDCVfDjgCl1lNpZVg==} + '@vue/test-utils@2.4.11': + resolution: {integrity: sha512-GDqaqZsA6m2E5vNzej0aYiIb6BX8xV9pNSbbbXKOfEYwg7ZNblVX8suyqmUBThq8VIrgAJNxn+z72hVtUeiWHA==} + peerDependencies: + '@vue/compiler-dom': 3.x + '@vue/server-renderer': 3.x + vue: 3.x + peerDependenciesMeta: + '@vue/server-renderer': + optional: true + '@vueuse/core@13.6.0': resolution: {integrity: sha512-DJbD5fV86muVmBgS9QQPddVX7d9hWYswzlf4bIyUD2dj8GC46R1uNClZhVAmsdVts4xb2jwp1PbpuiA50Qee1A==} peerDependencies: @@ -4879,6 +5094,10 @@ packages: resolution: {integrity: sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==} deprecated: Use your platform's native atob() and btoa() methods instead + abbrev@2.0.0: + resolution: {integrity: sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + abbrev@3.0.1: resolution: {integrity: sha512-AO2ac6pjRB3SJmGJo+v5/aK6Omggp6fsLrs6wN9bd35ulu4cCwaAU9+7ZhXjeqHVkaHThLuzH0nZr0YpCDhygg==} engines: {node: ^18.17.0 || >=20.5.0} @@ -5105,6 +5324,10 @@ packages: resolution: {integrity: sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==} engines: {node: '>=0.8'} + assertion-error@2.0.1: + resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} + engines: {node: '>=12'} + ast-kit@1.4.3: resolution: {integrity: sha512-MdJqjpodkS5J149zN0Po+HPshkTdUyrvF7CKTafUgv69vBSPtncrj+3IiUgqdd7ElIEkbeXCsEouBUwLrw9Ilg==} engines: {node: '>=16.14.0'} @@ -5116,6 +5339,9 @@ packages: ast-types-flow@0.0.8: resolution: {integrity: sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ==} + ast-v8-to-istanbul@1.0.5: + resolution: {integrity: sha512-UPAgKJFSEGMWSDr3LX4tqnAb4f7KGT8O40Tyx8wbYmmZ/yn58lNCm8h3svs3eXgiGd5AXxz8NDOvXWvicq+rJA==} + ast-walker-scope@0.6.2: resolution: {integrity: sha512-1UWOyC50xI3QZkRuDj6PqDtpm1oHWtYs+NQGwqL/2R11eN3Q81PHAHPM0SWW3BNQm53UDwS//Jv8L4CCVLM1bQ==} engines: {node: '>=16.14.0'} @@ -5124,10 +5350,6 @@ packages: resolution: {integrity: sha512-cbdCP0PGOBq0ASG+sjnKIoYkWMKhhz+F/h9pRexUdX2Hd38+WOlBkRKlqkGOSm0YQpcFMQBJeK4WspUAkwsEdg==} engines: {node: '>=20.19.0'} - ast-walker-scope@0.9.0: - resolution: {integrity: sha512-IJdzo2vLiElBxKzwS36VsCue/62d6IdWjnPB2v3nuPKeWGynp6FF/CYoLa5i/3jXH/z97ZDdsXz6abpgM6w07A==} - engines: {node: '>=20.19.0'} - astring@1.9.0: resolution: {integrity: sha512-LElXdjswlqjWrPpJFg1Fx4wpkOCxj1TDHlSV4PlaRxHGWko024xICaa97ZkMfs6DRKlCguiAI+rbXv5GWwXIkg==} hasBin: true @@ -5402,6 +5624,10 @@ packages: buffer-from@1.1.2: resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} + buffer-image-size@0.6.4: + resolution: {integrity: sha512-nEh+kZOPY1w+gcCMobZ6ETUp9WfibndnosbpwB1iJk/8Gt5ZF2bhS6+B6bPYz424KtwsR6Rflc3tCz1/ghX2dQ==} + engines: {node: '>=4.0'} + buffer@5.7.1: resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==} @@ -5520,6 +5746,10 @@ packages: ccount@2.0.1: resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==} + chai@6.2.2: + resolution: {integrity: sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==} + engines: {node: '>=18'} + chalk@1.1.3: resolution: {integrity: sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A==} engines: {node: '>=0.10.0'} @@ -5633,9 +5863,17 @@ packages: resolution: {integrity: sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==} engines: {node: '>= 10'} + cli-width@4.1.0: + resolution: {integrity: sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==} + engines: {node: '>= 12'} + client-only@0.0.1: resolution: {integrity: sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==} + cliui@8.0.1: + resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} + engines: {node: '>=12'} + cliui@9.0.1: resolution: {integrity: sha512-k7ndgKhwoQveBL+/1tqGJYNz097I7WOvwbmmU2AR5+magtbjPWQTS1C5vzGkBC8Ym8UWRzfKUzUUqFLypY4Q+w==} engines: {node: '>=20'} @@ -5693,6 +5931,10 @@ packages: comma-separated-tokens@2.0.3: resolution: {integrity: sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==} + commander@10.0.1: + resolution: {integrity: sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==} + engines: {node: '>=14'} + commander@11.1.0: resolution: {integrity: sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==} engines: {node: '>=16'} @@ -6201,6 +6443,11 @@ packages: ecdsa-sig-formatter@1.0.11: resolution: {integrity: sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==} + editorconfig@1.0.7: + resolution: {integrity: sha512-e0GOtq/aTQhVdNyDU9e02+wz9oDDM+SIOQxWME2QRjzRX5yyLAuHDE+0aE8vHb9XRC8XD37eO2u57+F09JqFhw==} + engines: {node: '>=14'} + hasBin: true + ee-first@1.1.1: resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} @@ -6595,6 +6842,13 @@ packages: '@typescript-eslint/parser': optional: true + eslint-plugin-vuejs-accessibility@2.5.0: + resolution: {integrity: sha512-oZ2fL4tS91Cm/ezH3BueNP+FtpbbeS627OSqqgp9/lsN//glmoPcLBT6D53xwGocLtyBybaT99tX4ThBh8+ytA==} + engines: {node: '>=16.0.0'} + peerDependencies: + eslint: ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 || ^9.0.0 || ^10.0.0 + globals: '>= 13.12.1' + eslint-processor-vue-blocks@2.0.0: resolution: {integrity: sha512-u4W0CJwGoWY3bjXAuFpc/b6eK3NQEI8MoeW7ritKj3G3z/WtHrKjkqf+wk8mPEy5rlMGS+k6AZYOw2XBoN/02Q==} peerDependencies: @@ -6749,6 +7003,10 @@ packages: resolution: {integrity: sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==} engines: {node: '>=6'} + expect-type@1.4.0: + resolution: {integrity: sha512-KfYbmpRm0VbLjEvVa9yGwCi9GI34xvi7A/HXYWQO65CSD2u3MczUJSuwXKFIxlGsgBQizV9q5J9NHj4VG0n+pA==} + engines: {node: '>=12.0.0'} + expressive-code@0.43.1: resolution: {integrity: sha512-JdOzanoU825iNvslmk6Kg8Ro61eSHmDK2Zz7BynOxObVrpIXZNzrIZOwQO2uDQcGsjSYShL/8vTrXgeWYnq3NA==} @@ -6787,10 +7045,18 @@ packages: resolution: {integrity: sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==} engines: {'0': node >=0.6.0} + fake-indexeddb@6.2.5: + resolution: {integrity: sha512-CGnyrvbhPlWYMngksqrSSUT1BAVP49dZocrHuK0SvtR0D5TMs5wP0o3j7jexDJW01KSadjBp1M/71o/KR3nD1w==} + engines: {node: '>=18'} + fast-check@3.23.2: resolution: {integrity: sha512-h5+1OzzfCC3Ef7VbtKdcv7zsstUQwUDlYpUTvjeUsJAssPgLn7QzbboPtL5ro04Mq0rPOsMzl7q5hIbRs2wD1A==} engines: {node: '>=8.0.0'} + fast-check@4.9.0: + resolution: {integrity: sha512-7ms6T7SybUev/PQITciI0yLM2pOSFy5zpG8Ty7tQofcVaQUvrMXp6CBwqF6fThLCLOrfBtuHAtwq6Yu4XPCllg==} + engines: {node: '>=12.17.0'} + fast-content-type-parse@3.0.0: resolution: {integrity: sha512-ZvLdcY8P+N8mGQJahJV5G4U88CSvT1rP8ApL6uETe88MBXrBHAkZlSEySdUlyztF7ccb+Znos3TFqaepHxdhBg==} @@ -7030,6 +7296,11 @@ packages: fs.realpath@1.0.0: resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} + fsevents@2.3.2: + resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + fsevents@2.3.3: resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} @@ -7237,6 +7508,10 @@ packages: graphmatch@1.1.1: resolution: {integrity: sha512-5ykVn/EXM1hF0XCaWh05VbYvEiOL2lY1kBxZtaYsyvjp7cmWOU1XsAdfQBwClraEofXDT197lFbXOEVMHpvQOg==} + graphql@16.14.2: + resolution: {integrity: sha512-Chq1s4CY7jmh8gO2qvLIJyfCDIN+EHLFW/9iShnp1z8FjBQMoodWP1kDC36VAMXXIvAjj4ARa7ntfAV2BrjsbA==} + engines: {node: ^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0} + gray-matter@4.0.3: resolution: {integrity: sha512-5v6yZd4JK3eMI3FqqCouswVqwugaA9r4dNZB1wwcmrD02QkV5H0y7XBQW8QwQqEaZY1pM9aqORSORhJRdNK44Q==} engines: {node: '>=6.0'} @@ -7258,6 +7533,10 @@ packages: crossws: optional: true + happy-dom@20.11.1: + resolution: {integrity: sha512-XSt8tMzbW9ymE7687xztkO1ckR7qJNQ3LywY9vlYGhGi3zXrGBHuUo2Cl1ztZaICW+1eAGdkLbj6iwVqDT33kg==} + engines: {node: '>=20.0.0'} + har-schema@2.0.0: resolution: {integrity: sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q==} engines: {node: '>=4'} @@ -7380,6 +7659,9 @@ packages: hastscript@9.0.1: resolution: {integrity: sha512-g7df9rMFX/SPi34tyGCyUBREQoKkapwdY/T04Qn9TDWfHhAYt4/I0gMVirzK5wEzeUqIjEB+LXC/ypb7Aqno5w==} + headers-polyfill@5.0.1: + resolution: {integrity: sha512-1TJ6Fih/b8h5TIcv+1+Hw0PDQWJTKDKzFZzcKOiW1wJza3XoAQlkCuXLbymPYB8+ZQyw8mHvdw560e8zVFIWyA==} + help-me@5.0.0: resolution: {integrity: sha512-7xgomUX6ADmcYzFik0HzAxh/73YlKR9bmFzf51CZwR+b6YtzU2m0u49hQCqV6SvlqIqsaxovfwdvbnsw3b/zpg==} @@ -7403,6 +7685,9 @@ packages: html-entities@2.6.0: resolution: {integrity: sha512-kig+rMn/QOVRvr7c86gQ8lWXq+Hkv6CbAH1hLu+RG338StTpE8Z0b44SDVaqVu7HGKf27frdmUYEs9hTUX/cLQ==} + html-escaper@2.0.2: + resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==} + html-escaper@3.0.3: resolution: {integrity: sha512-RuMffC89BOWQoY0WKGpIhn5gX3iI54O6nRA0yC124NYVtzjmFWBIiFd8M0x+ZdX0P9R4lADg1mgP8C7PxGOWuQ==} @@ -7459,6 +7744,11 @@ packages: resolution: {integrity: sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==} engines: {node: '>=16.17.0'} + husky@9.1.7: + resolution: {integrity: sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA==} + engines: {node: '>=18'} + hasBin: true + i18next@26.3.1: resolution: {integrity: sha512-txQqd5EULsqEh9OJqRH15aCaOuy/nLJyhw5EHCSKLKJE1aBbb3Zve2+uQIxgWhPm1QqUQoWyQBm2kfmmIrzkcQ==} peerDependencies: @@ -7735,6 +8025,9 @@ packages: resolution: {integrity: sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==} engines: {node: '>= 0.4'} + is-node-process@1.2.0: + resolution: {integrity: sha512-Vg4o6/fqPxIjtxgUH5QLJhwZ7gW5diGCVlXpuUfELC62CuxM1iHcRe51f2W1FDy04Ai4KJkagKjx3XaqyfRKXw==} + is-npm@4.0.0: resolution: {integrity: sha512-96ECIfh9xtDDlPylNPXhzjsykHsMJZ18ASpaWzQyBr4YRTcVjUvzaHayDAES2oU/3KpljhHUjtSRNiDwi0F0ig==} engines: {node: '>=8'} @@ -7888,6 +8181,18 @@ packages: isstream@0.1.2: resolution: {integrity: sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==} + istanbul-lib-coverage@3.2.2: + resolution: {integrity: sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==} + engines: {node: '>=8'} + + istanbul-lib-report@3.0.1: + resolution: {integrity: sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==} + engines: {node: '>=10'} + + istanbul-reports@3.2.0: + resolution: {integrity: sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==} + engines: {node: '>=8'} + isurl@1.0.0: resolution: {integrity: sha512-1P/yWsxPlDtn7QeRD+ULKQPaIaN6yF368GZ2vDfv0AL0NwpStafjWCDDdn0k8wgFMWpVAqG7oJhxHnlud42i9w==} engines: {node: '>= 4'} @@ -7925,6 +8230,17 @@ packages: js-base64@3.7.8: resolution: {integrity: sha512-hNngCeKxIUQiEUN3GPJOkz4wF/YvdUdbNL9hsBcMQTkKzboD7T/q3OYOuuPZLUE6dBxSGpwhk5mwuDud7JVAow==} + js-beautify@1.15.4: + resolution: {integrity: sha512-9/KXeZUKKJwqCXUdBxFJ3vPh467OCckSBmYDwSK/EtV090K+iMJ7zx2S3HLVDIWFQdqMIsZWbnaGiba18aWhaA==} + engines: {node: '>=14'} + hasBin: true + + js-cookie@3.0.8: + resolution: {integrity: sha512-yeJd4aNAdYZQjaon2bpD/Gb0B/omw7HQOsynXXcOiWVCacbBcPlgn8S/d1X6blFSaHao7ozqtW7NZW19xpCtIw==} + + js-tokens@10.0.0: + resolution: {integrity: sha512-lM/UBzQmfJRo9ABXbPWemivdCW8V2G8FHaHdypQaIy523snUjog0W71ayWXTjiR+ixeMyVHN2XcpnTd/liPg/Q==} + js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} @@ -8249,6 +8565,11 @@ packages: resolution: {integrity: sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==} engines: {node: '>=14'} + lint-staged@17.2.0: + resolution: {integrity: sha512-FchGnFe4i4B1C/a35SPU9bNGPEHSC1+1iV0plLjzBmKVe9klZrlRfSgK6Cw4VeHyqOXbJUXP0vON61uRftNQ0A==} + engines: {node: '>=22.22.1'} + hasBin: true + listhen@1.10.0: resolution: {integrity: sha512-kfz4C0OrC6IpaVMtYDJtf6PFjurxe9NBBoDAh/o2p587INryFOO4DQ9OetbCdDrWFt1m1CJKvYrzkGsuPHw8nQ==} hasBin: true @@ -8430,6 +8751,9 @@ packages: magic-string@0.30.21: resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} + magic-string@1.1.0: + resolution: {integrity: sha512-kS3VHe0nEPST2saQV4Rbkchcd3UBRkVTQHo1D3h/ZTwFDhai/mfKkmtPAtD129EOI7K3HlHIsFOt0WrI2/oU9g==} + magicast@0.5.2: resolution: {integrity: sha512-E3ZJh4J3S9KfwdjZhe2afj6R9lGIN5Pher1pF39UGrXRqq/VDaGVIGN13BjHd2u8B61hArAGOnso7nBOouW3TQ==} @@ -8444,6 +8768,10 @@ packages: resolution: {integrity: sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==} engines: {node: '>=8'} + make-dir@4.0.0: + resolution: {integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==} + engines: {node: '>=10'} + map-obj@1.0.1: resolution: {integrity: sha512-7N/q3lyZ+LVCp7PzuxrJr4KMbBE2hW7BT7YNia330OFxIf4d3r5zVpicP2650l7CPN6RM9zOJRl3NGpqSiw3Eg==} engines: {node: '>=0.10.0'} @@ -8757,12 +9085,26 @@ packages: ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + msw@2.15.0: + resolution: {integrity: sha512-2wQAmKkQKxRuXvYJxVhPGG0wZNBQyD06oJvxqw90XqLvptdqxdlHrFUfEteKkpaNORX3Xzc+HtEl/q0nfmN2wQ==} + engines: {node: '>=18'} + hasBin: true + peerDependencies: + typescript: '>= 4.8.x' + peerDependenciesMeta: + typescript: + optional: true + muggle-string@0.4.1: resolution: {integrity: sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==} mute-stream@0.0.8: resolution: {integrity: sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==} + mute-stream@3.0.0: + resolution: {integrity: sha512-dkEJPVvun4FryqBmZ5KhDo0K9iDXAwn08tMLDinNdRBNPcYEDiWYysLcc6k3mjTMlbP9KyylvRpd4wFtwrT9rw==} + engines: {node: ^20.17.0 || >=22.9.0} + mysql2@3.15.3: resolution: {integrity: sha512-FBrGau0IXmuqg4haEZRBfHNWB5mUARw6hNwPDXXGg0XzVJ50mr/9hb267lvpVMnhZ1FON3qNd4Xfcez1rbFwSg==} engines: {node: '>= 8.0'} @@ -8880,6 +9222,11 @@ packages: resolution: {integrity: sha512-l2NNj07e9afPnhAhvgVrCD/oy2Ai1yfLpuo3EpiO1jFTsB4sFz6oIfAfSZyQzVpkZQ9xS8ZS5g1jCBgq4Hwo0g==} engines: {node: '>=12.19'} + nopt@7.2.1: + resolution: {integrity: sha512-taM24ViiimT/XntxbPyJQzCG+p4EKOpgD3mxFwW38mGjVUrfERQOeY4EDHjdnptttfHuHQXFx+lTP08Q+mLa/w==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + hasBin: true + nopt@8.1.0: resolution: {integrity: sha512-ieGu42u/Qsa4TFktmaKEwM6MQH0pOWnaB3htzh0JRtx84+Mebc0cbZYN5bC+6WTZ4+77xrL9Pn5m7CV6VIkV7A==} engines: {node: ^18.17.0 || >=20.5.0} @@ -9066,6 +9413,9 @@ packages: resolution: {integrity: sha512-UhIZfOtlMBSWGSN/v1i1eY5qsW07V/ZgaSq8bZXK0nfZEcoEitRKmn1dY8Sed8rafai3Ju8BwuxttuC2DJm6sA==} engines: {node: '>=14'} + outvariant@1.4.3: + resolution: {integrity: sha512-+Sl2UErvtsoajRDKCE5/dBz4DIvHXQQnAxtQTF04OJxY0+DyZXSo5P5Bb7XYWOh81syohlYL24hbDwxedPUJCA==} + ow@0.17.0: resolution: {integrity: sha512-i3keDzDQP5lWIe4oODyDFey1qVrq2hXKTuTH2VpqwpYtzPiKZt2ziRI4NBQmgW40AnV5Euz17OyWweCb+bNEQA==} engines: {node: '>=10'} @@ -9279,6 +9629,9 @@ packages: resolution: {integrity: sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==} engines: {node: 18 || 20 || >=22} + path-to-regexp@6.3.0: + resolution: {integrity: sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==} + path-type@1.1.0: resolution: {integrity: sha512-S4eENJz1pkiQn9Znv33Q+deTOKmbl+jj1Fl+qiP/vYezj+S8x+J3Uo0ISrx/QoEvIlOaDWJhPaRd1flJ9HXZqg==} engines: {node: '>=0.10.0'} @@ -9407,6 +9760,16 @@ packages: pkg-types@2.3.1: resolution: {integrity: sha512-y+ichcgc2LrADuhLNAx8DFjVfgz91pRxfZdI3UDhxHvcVEZsenLO+7XaU5vOp0u/7V/wZ+plyuQxtrDlZJ+yeg==} + playwright-core@1.62.0: + resolution: {integrity: sha512-nsNRyq0r2zsG8AcRHWknc9QRA5XCueC7gWMrs+Gx2tlZn9hcl8zudfh00lhJPY1DE7NmZ6bDsT9g2yey8mXljA==} + engines: {node: '>=20'} + hasBin: true + + playwright@1.62.0: + resolution: {integrity: sha512-Z14dG305dgaLu6foB1TXQagFiW8JfSUIUaUuPaKQ6NtBPKF1P/qXcqfh6c6K/icPqdy37JmjbiBXf6JNg6Sylw==} + engines: {node: '>=20'} + hasBin: true + pluralize@8.0.0: resolution: {integrity: sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==} engines: {node: '>=4'} @@ -9811,6 +10174,9 @@ packages: pure-rand@6.1.0: resolution: {integrity: sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==} + pure-rand@8.4.2: + resolution: {integrity: sha512-vvuOGgcuPJAirlHvuQw1TrOiw7ptaIXXmIbNuiNOY6lNGJJH49PQ1Kj4nd783nPdQhQdicgOjVI2yI/9BD6/Ng==} + pvtsutils@1.3.6: resolution: {integrity: sha512-PLgQXQ6H2FWCaeRak8vvk1GW462lMxB5s3Jm673N82zI4vqtVUPuZdffdZbPDFRoU8kAhItWFtPCWiPpp4/EDg==} @@ -10093,6 +10459,10 @@ packages: engines: {node: '>= 6'} deprecated: request has been deprecated, see https://github.com/request/request/issues/3142 + require-directory@2.1.1: + resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} + engines: {node: '>=0.10.0'} + require-from-string@2.0.2: resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} engines: {node: '>=0.10.0'} @@ -10155,6 +10525,9 @@ packages: resolution: {integrity: sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==} engines: {node: '>= 4'} + rettime@0.11.11: + resolution: {integrity: sha512-ILJRqVWBCTlg9r42fFgwVZx1gnFAcQF8mRoMkbgQfIrjEDf9nbBFDFx00oloOa+Q869FUtaYDXZvEfnecQSCoQ==} + reusify@1.1.0: resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} engines: {iojs: '>=1.0.0', node: '>=0.10.0'} @@ -10338,6 +10711,9 @@ packages: resolution: {integrity: sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==} engines: {node: '>= 18'} + set-cookie-parser@3.1.2: + resolution: {integrity: sha512-5/r/lTwbJ3zQ+qwdUFZYeRNqda7P5HD8zQKqlSjdGt1/S0cjLAphHusj4Y58ahDtWn/g32xrIS58/ikOvwl0Lw==} + set-function-length@1.2.2: resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==} engines: {node: '>= 0.4'} @@ -10415,6 +10791,9 @@ packages: resolution: {integrity: sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==} engines: {node: '>= 0.4'} + siginfo@2.0.0: + resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} + signal-exit@3.0.7: resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} @@ -10548,6 +10927,9 @@ packages: resolution: {integrity: sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==} deprecated: 'Modern JS already guarantees Array#sort() is a stable sort, so this library is deprecated. See the compatibility table on MDN: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort#browser_compatibility' + stackback@0.0.2: + resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} + standard-as-callback@2.1.0: resolution: {integrity: sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A==} @@ -10608,6 +10990,13 @@ packages: streamx@2.25.0: resolution: {integrity: sha512-0nQuG6jf1w+wddNEEXCF4nTg3LtufWINB5eFEN+5TNZW7KWJp6x87+JFL43vaAUPyCfH1wID+mNVyW6OHtFamg==} + strict-event-emitter@0.5.1: + resolution: {integrity: sha512-vMgjE/GGEPEFnhFub6pa4FmJBRBVOLpIII2hvCZ8Kzb7K0hlHo7mQv6xYrBvCL2LtAIBwFUK8wvuJgTVSQ5MFQ==} + + string-argv@0.3.2: + resolution: {integrity: sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==} + engines: {node: '>=0.6.19'} + string-width@4.2.3: resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} engines: {node: '>=8'} @@ -10905,6 +11294,9 @@ packages: tiny-invariant@1.3.3: resolution: {integrity: sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==} + tinybench@2.9.0: + resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} + tinyclip@0.1.15: resolution: {integrity: sha512-uo33abH+Ays0xYaDysoBt494Hb3hsEczMpcC0MwFl773pazORx4fmvKhclhR1wonUbB6vvpRsvVMwnhfqeMc+A==} engines: {node: ^16.14.0 || >= 17.3.0} @@ -10921,6 +11313,10 @@ packages: resolution: {integrity: sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==} engines: {node: '>=12.0.0'} + tinyrainbow@3.1.1: + resolution: {integrity: sha512-yau8yJdTt989Mm0Bd/236QnzEiPf2xLLTqUZRUJOo/3CB078LSwzei343DgtJVmfJKJE3TMINY1u42SQsP6mXw==} + engines: {node: '>=14.0.0'} + tldts-core@7.0.28: resolution: {integrity: sha512-7W5Efjhsc3chVdFhqtaU0KtK32J37Zcr9RKtID54nG+tIpcY79CQK/veYPODxtD/LJ4Lue66jvrQzIX2Z2/pUQ==} @@ -11006,6 +11402,12 @@ packages: tslib@2.8.1: resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} + tsutils@3.21.0: + resolution: {integrity: sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==} + engines: {node: '>= 6'} + peerDependencies: + typescript: '>=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta' + tsyringe@4.10.0: resolution: {integrity: sha512-axr3IdNuVIxnaK5XGEUFTu3YmAQ6lllgrvqfEoR16g/HGnYY/6We4oWENtAnzK6/LpJ2ur9PAb80RBt7/U4ugw==} engines: {node: '>= 6.0.0'} @@ -11028,6 +11430,15 @@ packages: resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} engines: {node: '>= 0.8.0'} + type-coverage-core@2.30.1: + resolution: {integrity: sha512-TFzqUvaZW5bTpKGBj3FwN8bYLsWA/smTOslISDg9rBMrpn5KadMQy9l3q068q+dXkn27CAdyfGTDlsH0PguG3A==} + peerDependencies: + typescript: 2 || 3 || 4 || 5 || 6 || 7 + + type-coverage@2.30.1: + resolution: {integrity: sha512-+Y05UXYBaeonULHdpkc7kr0BF5Ix+IsdI/UzI2S6hnkCacTA4ktpi9EnKWMYyISW6xRaLYIEqK+4fz1GLpHQFg==} + hasBin: true + type-fest@0.11.0: resolution: {integrity: sha512-OdjXJxnCN1AvyLSzeKIgXTXxV+99ZuXl3Hpo9XpJAv9MBcHrrJOQ5kV7ypXOuQie+AmWG25hLbiKdwYTifzcfQ==} engines: {node: '>=8'} @@ -11389,6 +11800,9 @@ packages: uploadthing: optional: true + until-async@3.0.2: + resolution: {integrity: sha512-IiSk4HlzAMqTUseHHe3VhIGyuFmN90zMTpD3Z3y8jeQbzLIq500MVM7Jq2vUAnTKAFPJrqwkzr6PoTcPhGcOiw==} + untun@0.1.3: resolution: {integrity: sha512-4luGP9LMYszMRZwsvyUd9MrxgEGZdZuZgpVQHEEX0lCYFESasVRvZd0EYpCkOIbJKHMuv0LskpXc/8Un+MJzEQ==} hasBin: true @@ -11646,12 +12060,59 @@ packages: vite: optional: true + vitest-environment-nuxt@2.0.0: + resolution: {integrity: sha512-zEGFRiCAaRR3fHnqISHKMNTRvCzkQEI1XyFeqNgR2IBD0oYkfZ1rUHwi7C+h3Cns3KPykfB0av1B3MtLEbChDw==} + + vitest@4.1.10: + resolution: {integrity: sha512-R9jUTe5S4Qb0HCd4TNqpC7oGcrMssMRGXLW80ubjWsW9VH5GF8y1Y0SFLY9AbqSk6nt0PnOx4H4WNJYZ13GUPw==} + engines: {node: ^20.0.0 || ^22.0.0 || >=24.0.0} + hasBin: true + peerDependencies: + '@edge-runtime/vm': '*' + '@opentelemetry/api': ^1.9.0 + '@types/node': ^20.0.0 || ^22.0.0 || >=24.0.0 + '@vitest/browser-playwright': 4.1.10 + '@vitest/browser-preview': 4.1.10 + '@vitest/browser-webdriverio': 4.1.10 + '@vitest/coverage-istanbul': 4.1.10 + '@vitest/coverage-v8': 4.1.10 + '@vitest/ui': 4.1.10 + happy-dom: '*' + jsdom: '*' + vite: ^6.0.0 || ^7.0.0 || ^8.0.0 + peerDependenciesMeta: + '@edge-runtime/vm': + optional: true + '@opentelemetry/api': + optional: true + '@types/node': + optional: true + '@vitest/browser-playwright': + optional: true + '@vitest/browser-preview': + optional: true + '@vitest/browser-webdriverio': + optional: true + '@vitest/coverage-istanbul': + optional: true + '@vitest/coverage-v8': + optional: true + '@vitest/ui': + optional: true + happy-dom: + optional: true + jsdom: + optional: true + vscode-uri@3.1.0: resolution: {integrity: sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ==} vue-bundle-renderer@2.3.1: resolution: {integrity: sha512-7F4LNMopUw5RgYWo4zCmVUHCc6aQRC6dCKHUYkM/n+fux4AUGdL1x6m5A515WWyFysRRN7cx3hBzVqoisfRfzw==} + vue-component-type-helpers@3.3.8: + resolution: {integrity: sha512-troqCMmQodQDqUqn63NQaFi+CDSclSe7sc8VEBFqf5GFLqmGR2Ph3P2WEC7qwpRVyEWsTi/aAr4vyOe/B1hU3g==} + vue-devtools-stub@0.1.0: resolution: {integrity: sha512-RutnB7X8c5hjq39NceArgXg28WZtZpGc3+J16ljMiYnFhKvd8hITxSWQSQ5bvldxMDU6gG5mkxl1MTQLXckVSQ==} @@ -11674,29 +12135,16 @@ packages: peerDependencies: vue: ^3.0.0 + vue-router@4.5.1: + resolution: {integrity: sha512-ogAF3P97NPm8fJsE4by9dwSYtDwXIY1nFY9T6DyQnGHd1E2Da94w9JIolpe42LJGIl0DwOHBi8TcRPlPGwbTtw==} + peerDependencies: + vue: ^3.2.0 + vue-router@4.6.4: resolution: {integrity: sha512-Hz9q5sa33Yhduglwz6g9skT8OBPii+4bFn88w6J+J4MfEo4KRRpmiNG/hHHkdbRFlLBOqxN8y8gf2Fb0MTUgVg==} peerDependencies: vue: ^3.5.0 - vue-router@5.2.0: - resolution: {integrity: sha512-QAC5i0LEb1GLG0LXDQmHu8L7FX12j0KwU/JTKmLQUJMrn04gQdKP6Du+p0QwpHb3iy71vBlqnHQ8WAfOSAWhqw==} - peerDependencies: - '@pinia/colada': '>=0.21.2' - '@vue/compiler-sfc': ^3.5.34 || ^4.0.0 - pinia: ^3.0.4 || ^4.0.2 - vite: ^7.3.0 || ^8.0.0 - vue: ^3.5.34 || ^4.0.0 - peerDependenciesMeta: - '@pinia/colada': - optional: true - '@vue/compiler-sfc': - optional: true - pinia: - optional: true - vite: - optional: true - vue-tsc@3.2.6: resolution: {integrity: sha512-gYW/kWI0XrwGzd0PKc7tVB/qpdeAkIZLNZb10/InizkQjHjnT8weZ/vBarZoj4kHKbUTZT/bAVgoOr8x4NsQ/Q==} hasBin: true @@ -11716,6 +12164,14 @@ packages: peerDependencies: vue: ^3.5.0 + vue@3.5.17: + resolution: {integrity: sha512-LbHV3xPN9BeljML+Xctq4lbz2lVHCR6DtbpTf5XIO6gugpXUN49j2QQPcMj086r9+AkJ0FfUT8xjulKKBkkr9g==} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + vue@3.5.40: resolution: {integrity: sha512-+8PJ4SJXdn/cHGImF4CKdxlWHIN5Dkt7DoufRREM6h6uVCx2m7QxgcEQmmzyOK8A9mcafg7sFbJFYsdFVubTig==} peerDependencies: @@ -11773,6 +12229,10 @@ packages: whatwg-mimetype@2.3.0: resolution: {integrity: sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==} + whatwg-mimetype@3.0.0: + resolution: {integrity: sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==} + engines: {node: '>=12'} + whatwg-mimetype@4.0.0: resolution: {integrity: sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==} engines: {node: '>=18'} @@ -11815,6 +12275,11 @@ packages: engines: {node: ^22.22.2 || ^24.15.0 || >=26.0.0} hasBin: true + why-is-node-running@2.3.0: + resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==} + engines: {node: '>=8'} + hasBin: true + widest-line@3.1.0: resolution: {integrity: sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg==} engines: {node: '>=8'} @@ -11947,10 +12412,18 @@ packages: engines: {node: '>= 14.6'} hasBin: true + yargs-parser@21.1.1: + resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} + engines: {node: '>=12'} + yargs-parser@22.0.0: resolution: {integrity: sha512-rwu/ClNdSMpkSrUb+d6BRsSkLUq1fmfsY6TOpYzTwvwkg1/NRG85KBy3kq++A8LKQwX6lsu+aWad+2khvuXrqw==} engines: {node: ^20.19.0 || ^22.12.0 || >=23} + yargs@17.7.3: + resolution: {integrity: sha512-GZtjxm/J/4TSxuL3FNYjCmLktBTnIw/rVmKSIyKeYAZpmJB2ig9VauCC5xsa82GNKVKDAqpOn3KVzNt0zmrU0g==} + engines: {node: '>=12'} + yargs@18.0.0: resolution: {integrity: sha512-4UEqdc2RYGHZc7Doyqkrqiln3p9X2DZVxaGbwhn2pi7MrRagKaOcIKe8L3OxYcbhXLgLFUS3zAYuQjKBQgmuNg==} engines: {node: ^20.19.0 || ^22.12.0 || >=23} @@ -12266,15 +12739,6 @@ snapshots: '@jridgewell/trace-mapping': 0.3.31 jsesc: 3.1.0 - '@babel/generator@8.0.0': - dependencies: - '@babel/parser': 8.0.4 - '@babel/types': 8.0.4 - '@jridgewell/gen-mapping': 0.3.13 - '@jridgewell/trace-mapping': 0.3.31 - '@types/jsesc': 2.5.1 - jsesc: 3.1.0 - '@babel/helper-annotate-as-pure@7.29.7': dependencies: '@babel/types': 7.29.7 @@ -12377,14 +12841,10 @@ snapshots: '@babel/helper-string-parser@7.29.7': {} - '@babel/helper-string-parser@8.0.0': {} - '@babel/helper-validator-identifier@7.28.5': {} '@babel/helper-validator-identifier@7.29.7': {} - '@babel/helper-validator-identifier@8.0.4': {} - '@babel/helper-validator-option@7.27.1': {} '@babel/helper-validator-option@7.29.7': {} @@ -12411,10 +12871,6 @@ snapshots: dependencies: '@babel/types': 7.29.7 - '@babel/parser@8.0.4': - dependencies: - '@babel/types': 8.0.4 - '@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7)': dependencies: '@babel/core': 7.29.7 @@ -12484,10 +12940,7 @@ snapshots: '@babel/helper-string-parser': 7.29.7 '@babel/helper-validator-identifier': 7.29.7 - '@babel/types@8.0.4': - dependencies: - '@babel/helper-string-parser': 8.0.0 - '@babel/helper-validator-identifier': 8.0.4 + '@bcoe/v8-coverage@1.0.2': {} '@bomb.sh/tab@0.0.19(cac@6.7.14)(citty@0.2.2)': optionalDependencies: @@ -12635,11 +13088,11 @@ snapshots: transitivePeerDependencies: - supports-color - '@content-collections/next@0.2.11(@content-collections/core@0.11.1(typescript@5.9.3))(next@15.5.21(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(sass@1.99.0))': + '@content-collections/next@0.2.11(@content-collections/core@0.11.1(typescript@5.9.3))(next@15.5.21(@playwright/test@1.62.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(sass@1.99.0))': dependencies: '@content-collections/core': 0.11.1(typescript@5.9.3) '@content-collections/integrations': 0.5.0(@content-collections/core@0.11.1(typescript@5.9.3)) - next: 15.5.21(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(sass@1.99.0) + next: 15.5.21(@playwright/test@1.62.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(sass@1.99.0) '@cto.af/wtf8@0.0.5': {} @@ -12652,10 +13105,10 @@ snapshots: jsonfile: 5.0.0 universalify: 0.1.2 - '@dxup/nuxt@0.4.1(magic-string@0.30.21)(magicast@0.5.3)(oxc-parser@0.132.0)(rolldown@1.1.5)(typescript@5.9.3)(unplugin@3.3.0(esbuild@0.28.0)(rolldown@1.1.5)(rollup@4.62.3)(vite@8.1.5(@types/node@22.19.17)(esbuild@0.28.0)(jiti@2.7.0)(sass@1.99.0)(terser@5.47.1)(yaml@2.9.0)))': + '@dxup/nuxt@0.4.1(magic-string@0.30.21)(magicast@0.5.2)(oxc-parser@0.132.0)(rolldown@1.1.5)(typescript@5.9.3)(unplugin@3.3.0(esbuild@0.28.0)(rolldown@1.1.5)(rollup@4.62.3)(vite@8.1.5(@types/node@22.19.17)(esbuild@0.28.0)(jiti@2.7.0)(sass@1.99.0)(terser@5.47.1)(yaml@2.9.0)))': dependencies: '@dxup/unimport': 0.1.2 - '@nuxt/kit': 4.5.1(magic-string@0.30.21)(magicast@0.5.3)(oxc-parser@0.132.0)(rolldown@1.1.5)(unplugin@3.3.0(esbuild@0.28.0)(rolldown@1.1.5)(rollup@4.62.3)(vite@8.1.5(@types/node@22.19.17)(esbuild@0.28.0)(jiti@2.7.0)(sass@1.99.0)(terser@5.47.1)(yaml@2.9.0))) + '@nuxt/kit': 4.5.1(magic-string@0.30.21)(magicast@0.5.2)(oxc-parser@0.132.0)(rolldown@1.1.5)(unplugin@3.3.0(esbuild@0.28.0)(rolldown@1.1.5)(rollup@4.62.3)(vite@8.1.5(@types/node@22.19.17)(esbuild@0.28.0)(jiti@2.7.0)(sass@1.99.0)(terser@5.47.1)(yaml@2.9.0))) chokidar: 5.0.0 pathe: 2.0.3 tinyglobby: 0.2.17 @@ -13292,6 +13745,11 @@ snapshots: react-dom: 19.2.5(react@19.2.5) use-sync-external-store: 1.6.0(react@19.2.5) + '@headlessui/vue@1.7.23(vue@3.5.17(typescript@5.9.3))': + dependencies: + '@tanstack/vue-virtual': 3.13.23(vue@3.5.17(typescript@5.9.3)) + vue: 3.5.17(typescript@5.9.3) + '@headlessui/vue@1.7.23(vue@3.5.40(typescript@5.9.3))': dependencies: '@tanstack/vue-virtual': 3.13.23(vue@3.5.40(typescript@5.9.3)) @@ -13301,6 +13759,10 @@ snapshots: dependencies: react: 19.2.5 + '@heroicons/vue@2.2.0(vue@3.5.17(typescript@5.9.3))': + dependencies: + vue: 3.5.17(typescript@5.9.3) + '@heroicons/vue@2.2.0(vue@3.5.40(typescript@5.9.3))': dependencies: vue: 3.5.40(typescript@5.9.3) @@ -13522,11 +13984,38 @@ snapshots: '@img/sharp-win32-x64@0.35.3': optional: true - '@internationalized/date@3.12.1': - dependencies: - '@swc/helpers': 0.5.23 + '@inquirer/ansi@2.0.7': {} - '@internationalized/number@3.6.6': + '@inquirer/confirm@6.1.1(@types/node@22.19.17)': + dependencies: + '@inquirer/core': 11.2.1(@types/node@22.19.17) + '@inquirer/type': 4.0.7(@types/node@22.19.17) + optionalDependencies: + '@types/node': 22.19.17 + + '@inquirer/core@11.2.1(@types/node@22.19.17)': + dependencies: + '@inquirer/ansi': 2.0.7 + '@inquirer/figures': 2.0.7 + '@inquirer/type': 4.0.7(@types/node@22.19.17) + cli-width: 4.1.0 + fast-wrap-ansi: 0.2.2 + mute-stream: 3.0.0 + signal-exit: 4.1.0 + optionalDependencies: + '@types/node': 22.19.17 + + '@inquirer/figures@2.0.7': {} + + '@inquirer/type@4.0.7(@types/node@22.19.17)': + optionalDependencies: + '@types/node': 22.19.17 + + '@internationalized/date@3.12.1': + dependencies: + '@swc/helpers': 0.5.23 + + '@internationalized/number@3.6.6': dependencies: '@swc/helpers': 0.5.23 @@ -13534,7 +14023,7 @@ snapshots: dependencies: '@swc/helpers': 0.5.23 - '@intlify/bundle-utils@10.0.1(vue-i18n@10.0.8(vue@3.5.40(typescript@5.9.3)))': + '@intlify/bundle-utils@10.0.1(vue-i18n@10.0.8(vue@3.5.17(typescript@5.9.3)))': dependencies: '@intlify/message-compiler': 11.3.2 '@intlify/shared': 11.3.2 @@ -13546,7 +14035,7 @@ snapshots: source-map-js: 1.2.1 yaml-eslint-parser: 1.3.2 optionalDependencies: - vue-i18n: 10.0.8(vue@3.5.40(typescript@5.9.3)) + vue-i18n: 10.0.8(vue@3.5.17(typescript@5.9.3)) '@intlify/core-base@10.0.8': dependencies: @@ -13613,12 +14102,12 @@ snapshots: '@intlify/shared@11.3.2': {} - '@intlify/unplugin-vue-i18n@6.0.8(@vue/compiler-dom@3.5.40)(eslint@9.39.4(jiti@2.7.0))(rollup@4.62.3)(typescript@5.9.3)(vue-i18n@10.0.8(vue@3.5.40(typescript@5.9.3)))(vue@3.5.40(typescript@5.9.3))': + '@intlify/unplugin-vue-i18n@6.0.8(@vue/compiler-dom@3.5.40)(eslint@9.39.4(jiti@2.7.0))(rollup@4.62.3)(typescript@5.9.3)(vue-i18n@10.0.8(vue@3.5.17(typescript@5.9.3)))(vue@3.5.17(typescript@5.9.3))': dependencies: '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.4(jiti@2.7.0)) - '@intlify/bundle-utils': 10.0.1(vue-i18n@10.0.8(vue@3.5.40(typescript@5.9.3))) + '@intlify/bundle-utils': 10.0.1(vue-i18n@10.0.8(vue@3.5.17(typescript@5.9.3))) '@intlify/shared': 11.3.2 - '@intlify/vue-i18n-extensions': 8.0.0(@intlify/shared@11.3.2)(@vue/compiler-dom@3.5.40)(vue-i18n@10.0.8(vue@3.5.40(typescript@5.9.3)))(vue@3.5.40(typescript@5.9.3)) + '@intlify/vue-i18n-extensions': 8.0.0(@intlify/shared@11.3.2)(@vue/compiler-dom@3.5.40)(vue-i18n@10.0.8(vue@3.5.17(typescript@5.9.3)))(vue@3.5.17(typescript@5.9.3)) '@rollup/pluginutils': 5.4.0(rollup@4.62.3) '@typescript-eslint/scope-manager': 8.58.2 '@typescript-eslint/typescript-estree': 8.58.2(typescript@5.9.3) @@ -13630,9 +14119,9 @@ snapshots: picocolors: 1.1.1 source-map-js: 1.2.1 unplugin: 1.16.1 - vue: 3.5.40(typescript@5.9.3) + vue: 3.5.17(typescript@5.9.3) optionalDependencies: - vue-i18n: 10.0.8(vue@3.5.40(typescript@5.9.3)) + vue-i18n: 10.0.8(vue@3.5.17(typescript@5.9.3)) transitivePeerDependencies: - '@vue/compiler-dom' - eslint @@ -13642,14 +14131,14 @@ snapshots: '@intlify/utils@0.13.0': {} - '@intlify/vue-i18n-extensions@8.0.0(@intlify/shared@11.3.2)(@vue/compiler-dom@3.5.40)(vue-i18n@10.0.8(vue@3.5.40(typescript@5.9.3)))(vue@3.5.40(typescript@5.9.3))': + '@intlify/vue-i18n-extensions@8.0.0(@intlify/shared@11.3.2)(@vue/compiler-dom@3.5.40)(vue-i18n@10.0.8(vue@3.5.17(typescript@5.9.3)))(vue@3.5.17(typescript@5.9.3))': dependencies: '@babel/parser': 7.29.7 optionalDependencies: '@intlify/shared': 11.3.2 '@vue/compiler-dom': 3.5.40 - vue: 3.5.40(typescript@5.9.3) - vue-i18n: 10.0.8(vue@3.5.40(typescript@5.9.3)) + vue: 3.5.17(typescript@5.9.3) + vue-i18n: 10.0.8(vue@3.5.17(typescript@5.9.3)) '@ioredis/commands@1.5.1': {} @@ -13771,6 +14260,15 @@ snapshots: json5: 2.2.3 rollup: 4.62.3 + '@mswjs/interceptors@0.41.9': + dependencies: + '@open-draft/deferred-promise': 2.2.0 + '@open-draft/logger': 0.3.0 + '@open-draft/until': 2.1.0 + is-node-process: 1.2.0 + outvariant: 1.4.3 + strict-event-emitter: 0.5.1 + '@napi-rs/wasm-runtime@0.2.12': dependencies: '@emnapi/core': 1.11.3 @@ -13843,6 +14341,45 @@ snapshots: '@nolyfill/is-core-module@1.0.39': {} + '@nuxt/cli@3.37.0(@nuxt/schema@3.21.7)(@parcel/watcher@2.5.6)(cac@6.7.14)(magicast@0.5.2)': + dependencies: + '@bomb.sh/tab': 0.0.19(cac@6.7.14)(citty@0.2.2) + '@clack/prompts': 1.7.0 + c12: 3.3.4(magicast@0.5.2) + citty: 0.2.2 + confbox: 0.2.4 + consola: 3.4.2 + debug: 4.4.3 + defu: 6.1.7 + exsolve: 1.1.1 + fuse.js: 7.5.0 + fzf: 0.5.2 + giget: 3.3.1 + jiti: 2.7.0 + listhen: 1.10.1(@parcel/watcher@2.5.6)(srvx@0.11.22) + nypm: 0.6.9 + ofetch: 1.5.1 + ohash: 2.0.11 + pathe: 2.0.3 + perfect-debounce: 2.1.0 + pkg-types: 2.3.1 + scule: 1.3.0 + semver: 7.8.5 + srvx: 0.11.22 + std-env: 4.2.0 + tinyclip: 0.1.15 + tinyexec: 1.2.4 + ufo: 1.6.4 + youch: 4.1.1 + optionalDependencies: + '@nuxt/schema': 3.21.7 + transitivePeerDependencies: + - '@parcel/watcher' + - cac + - commander + - magicast + - supports-color + '@nuxt/cli@3.37.0(@nuxt/schema@3.21.7)(@parcel/watcher@2.5.6)(cac@6.7.14)(magicast@0.5.3)': dependencies: '@bomb.sh/tab': 0.0.19(cac@6.7.14)(citty@0.2.2) @@ -13884,19 +14421,19 @@ snapshots: '@nuxt/devalue@2.0.2': {} - '@nuxt/devtools-kit@2.7.0(magicast@0.5.3)(vite@8.1.5(@types/node@22.19.17)(esbuild@0.28.0)(jiti@2.7.0)(sass@1.99.0)(terser@5.47.1)(yaml@2.9.0))': + '@nuxt/devtools-kit@2.7.0(magicast@0.5.2)(vite@8.1.5(@types/node@22.19.17)(esbuild@0.28.0)(jiti@2.7.0)(sass@1.99.0)(terser@5.47.1)(yaml@2.9.0))': dependencies: - '@nuxt/kit': 3.21.7(magicast@0.5.3) + '@nuxt/kit': 3.21.7(magicast@0.5.2) execa: 8.0.1 vite: 8.1.5(@types/node@22.19.17)(esbuild@0.28.0)(jiti@2.7.0)(sass@1.99.0)(terser@5.47.1)(yaml@2.9.0) transitivePeerDependencies: - magicast - '@nuxt/devtools-kit@3.2.4(magic-string@0.30.21)(magicast@0.5.3)(oxc-parser@0.132.0)(rolldown@1.1.5)(unplugin@3.3.0(esbuild@0.28.0)(rolldown@1.1.5)(rollup@4.62.3)(vite@8.1.5(@types/node@22.19.17)(esbuild@0.28.0)(jiti@2.7.0)(sass@1.99.0)(terser@5.47.1)(yaml@2.9.0)))(vite@8.1.5(@types/node@22.19.17)(esbuild@0.28.0)(jiti@2.7.0)(sass@1.99.0)(terser@5.47.1)(yaml@2.9.0))': + '@nuxt/devtools-kit@3.2.4(magic-string@1.1.0)(magicast@0.5.3)(rolldown@1.1.5)(unplugin@3.3.0(esbuild@0.28.1)(rolldown@1.1.5)(rollup@4.62.3)(vite@8.1.5(@types/node@24.12.2)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.99.0)(terser@5.47.1)(yaml@2.9.0)))(vite@8.1.5(@types/node@24.12.2)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.99.0)(terser@5.47.1)(yaml@2.9.0))': dependencies: - '@nuxt/kit': 4.5.1(magic-string@0.30.21)(magicast@0.5.3)(oxc-parser@0.132.0)(rolldown@1.1.5)(unplugin@3.3.0(esbuild@0.28.0)(rolldown@1.1.5)(rollup@4.62.3)(vite@8.1.5(@types/node@22.19.17)(esbuild@0.28.0)(jiti@2.7.0)(sass@1.99.0)(terser@5.47.1)(yaml@2.9.0))) + '@nuxt/kit': 4.5.1(magic-string@1.1.0)(magicast@0.5.3)(rolldown@1.1.5)(unplugin@3.3.0(esbuild@0.28.1)(rolldown@1.1.5)(rollup@4.62.3)(vite@8.1.5(@types/node@24.12.2)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.99.0)(terser@5.47.1)(yaml@2.9.0))) execa: 8.0.1 - vite: 8.1.5(@types/node@22.19.17)(esbuild@0.28.0)(jiti@2.7.0)(sass@1.99.0)(terser@5.47.1)(yaml@2.9.0) + vite: 8.1.5(@types/node@24.12.2)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.99.0)(terser@5.47.1)(yaml@2.9.0) transitivePeerDependencies: - magic-string - magicast @@ -13904,11 +14441,11 @@ snapshots: - rolldown - unplugin - '@nuxt/devtools-kit@3.2.4(magic-string@0.30.21)(magicast@0.5.3)(rolldown@1.1.5)(unplugin@3.3.0(esbuild@0.28.1)(rolldown@1.1.5)(rollup@4.62.3)(vite@8.1.5(@types/node@24.12.2)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.99.0)(terser@5.47.1)(yaml@2.9.0)))(vite@8.1.5(@types/node@24.12.2)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.99.0)(terser@5.47.1)(yaml@2.9.0))': + '@nuxt/devtools-kit@3.2.4(magicast@0.5.2)(oxc-parser@0.132.0)(rolldown@1.1.5)(vite@8.1.5(@types/node@22.19.17)(esbuild@0.28.0)(jiti@2.7.0)(sass@1.99.0)(terser@5.47.1)(yaml@2.9.0))': dependencies: - '@nuxt/kit': 4.5.1(magic-string@0.30.21)(magicast@0.5.3)(rolldown@1.1.5)(unplugin@3.3.0(esbuild@0.28.1)(rolldown@1.1.5)(rollup@4.62.3)(vite@8.1.5(@types/node@24.12.2)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.99.0)(terser@5.47.1)(yaml@2.9.0))) + '@nuxt/kit': 4.5.1(magicast@0.5.2)(oxc-parser@0.132.0)(rolldown@1.1.5) execa: 8.0.1 - vite: 8.1.5(@types/node@24.12.2)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.99.0)(terser@5.47.1)(yaml@2.9.0) + vite: 8.1.5(@types/node@22.19.17)(esbuild@0.28.0)(jiti@2.7.0)(sass@1.99.0)(terser@5.47.1)(yaml@2.9.0) transitivePeerDependencies: - magic-string - magicast @@ -14161,13 +14698,13 @@ snapshots: - supports-color - typescript - '@nuxt/eslint@1.15.2(@typescript-eslint/utils@8.58.2(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3))(@vue/compiler-sfc@3.5.40)(eslint-import-resolver-node@0.3.10)(eslint@9.39.4(jiti@2.7.0))(magic-string@0.30.21)(magicast@0.5.3)(oxc-parser@0.132.0)(rolldown@1.1.5)(typescript@5.9.3)(unplugin@3.3.0(esbuild@0.28.0)(rolldown@1.1.5)(rollup@4.62.3)(vite@8.1.5(@types/node@22.19.17)(esbuild@0.28.0)(jiti@2.7.0)(sass@1.99.0)(terser@5.47.1)(yaml@2.9.0)))(vite@8.1.5(@types/node@22.19.17)(esbuild@0.28.0)(jiti@2.7.0)(sass@1.99.0)(terser@5.47.1)(yaml@2.9.0))': + '@nuxt/eslint@1.15.2(@typescript-eslint/utils@8.58.2(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3))(@vue/compiler-sfc@3.5.40)(eslint-import-resolver-node@0.3.10)(eslint@9.39.4(jiti@2.7.0))(magicast@0.5.2)(oxc-parser@0.132.0)(rolldown@1.1.5)(typescript@5.9.3)(vite@8.1.5(@types/node@22.19.17)(esbuild@0.28.0)(jiti@2.7.0)(sass@1.99.0)(terser@5.47.1)(yaml@2.9.0))': dependencies: '@eslint/config-inspector': 1.5.0(eslint@9.39.4(jiti@2.7.0)) - '@nuxt/devtools-kit': 3.2.4(magic-string@0.30.21)(magicast@0.5.3)(oxc-parser@0.132.0)(rolldown@1.1.5)(unplugin@3.3.0(esbuild@0.28.0)(rolldown@1.1.5)(rollup@4.62.3)(vite@8.1.5(@types/node@22.19.17)(esbuild@0.28.0)(jiti@2.7.0)(sass@1.99.0)(terser@5.47.1)(yaml@2.9.0)))(vite@8.1.5(@types/node@22.19.17)(esbuild@0.28.0)(jiti@2.7.0)(sass@1.99.0)(terser@5.47.1)(yaml@2.9.0)) + '@nuxt/devtools-kit': 3.2.4(magicast@0.5.2)(oxc-parser@0.132.0)(rolldown@1.1.5)(vite@8.1.5(@types/node@22.19.17)(esbuild@0.28.0)(jiti@2.7.0)(sass@1.99.0)(terser@5.47.1)(yaml@2.9.0)) '@nuxt/eslint-config': 1.15.2(@typescript-eslint/utils@8.58.2(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3))(@vue/compiler-sfc@3.5.40)(eslint-import-resolver-node@0.3.10)(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3) '@nuxt/eslint-plugin': 1.15.2(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3) - '@nuxt/kit': 4.4.2(magicast@0.5.3) + '@nuxt/kit': 4.4.2(magicast@0.5.2) chokidar: 5.0.0 eslint: 9.39.4(jiti@2.7.0) eslint-flat-config-utils: 3.1.0 @@ -14193,13 +14730,13 @@ snapshots: - utf-8-validate - vite - '@nuxt/eslint@1.16.0(@typescript-eslint/utils@8.65.0(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3))(@vue/compiler-sfc@3.5.40)(esbuild@0.28.1)(eslint-import-resolver-node@0.3.10)(eslint@9.39.4(jiti@2.7.0))(magic-string@0.30.21)(magicast@0.5.3)(rolldown@1.1.5)(rollup@4.62.3)(srvx@0.11.22)(typescript@5.9.3)(unplugin@3.3.0(esbuild@0.28.1)(rolldown@1.1.5)(rollup@4.62.3)(vite@8.1.5(@types/node@24.12.2)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.99.0)(terser@5.47.1)(yaml@2.9.0)))(vite@8.1.5(@types/node@24.12.2)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.99.0)(terser@5.47.1)(yaml@2.9.0))': + '@nuxt/eslint@1.16.0(@typescript-eslint/utils@8.65.0(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3))(@vue/compiler-sfc@3.5.40)(esbuild@0.28.1)(eslint-import-resolver-node@0.3.10)(eslint@9.39.4(jiti@2.7.0))(magic-string@1.1.0)(magicast@0.5.3)(rolldown@1.1.5)(rollup@4.62.3)(srvx@0.11.22)(typescript@5.9.3)(unplugin@3.3.0(esbuild@0.28.1)(rolldown@1.1.5)(rollup@4.62.3)(vite@8.1.5(@types/node@24.12.2)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.99.0)(terser@5.47.1)(yaml@2.9.0)))(vite@8.1.5(@types/node@24.12.2)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.99.0)(terser@5.47.1)(yaml@2.9.0))': dependencies: '@eslint/config-inspector': 3.1.0(eslint@9.39.4(jiti@2.7.0))(srvx@0.11.22)(typescript@5.9.3) - '@nuxt/devtools-kit': 3.2.4(magic-string@0.30.21)(magicast@0.5.3)(rolldown@1.1.5)(unplugin@3.3.0(esbuild@0.28.1)(rolldown@1.1.5)(rollup@4.62.3)(vite@8.1.5(@types/node@24.12.2)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.99.0)(terser@5.47.1)(yaml@2.9.0)))(vite@8.1.5(@types/node@24.12.2)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.99.0)(terser@5.47.1)(yaml@2.9.0)) + '@nuxt/devtools-kit': 3.2.4(magic-string@1.1.0)(magicast@0.5.3)(rolldown@1.1.5)(unplugin@3.3.0(esbuild@0.28.1)(rolldown@1.1.5)(rollup@4.62.3)(vite@8.1.5(@types/node@24.12.2)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.99.0)(terser@5.47.1)(yaml@2.9.0)))(vite@8.1.5(@types/node@24.12.2)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.99.0)(terser@5.47.1)(yaml@2.9.0)) '@nuxt/eslint-config': 1.16.0(@typescript-eslint/utils@8.65.0(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3))(@vue/compiler-sfc@3.5.40)(eslint-import-resolver-node@0.3.10)(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3) '@nuxt/eslint-plugin': 1.16.0(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3) - '@nuxt/kit': 4.5.1(magic-string@0.30.21)(magicast@0.5.3)(rolldown@1.1.5)(unplugin@3.3.0(esbuild@0.28.1)(rolldown@1.1.5)(rollup@4.62.3)(vite@8.1.5(@types/node@24.12.2)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.99.0)(terser@5.47.1)(yaml@2.9.0))) + '@nuxt/kit': 4.5.1(magic-string@1.1.0)(magicast@0.5.3)(rolldown@1.1.5)(unplugin@3.3.0(esbuild@0.28.1)(rolldown@1.1.5)(rollup@4.62.3)(vite@8.1.5(@types/node@24.12.2)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.99.0)(terser@5.47.1)(yaml@2.9.0))) chokidar: 5.0.0 eslint: 9.39.4(jiti@2.7.0) eslint-flat-config-utils: 3.2.0 @@ -14232,10 +14769,10 @@ snapshots: - vite - webpack - '@nuxt/fonts@0.11.4(db0@0.3.4(@electric-sql/pglite@0.4.1)(mysql2@3.15.3))(ioredis@5.10.1)(magicast@0.5.3)(vite@8.1.5(@types/node@22.19.17)(esbuild@0.28.0)(jiti@2.7.0)(sass@1.99.0)(terser@5.47.1)(yaml@2.9.0))': + '@nuxt/fonts@0.11.4(db0@0.3.4(@electric-sql/pglite@0.4.1)(mysql2@3.15.3))(ioredis@5.10.1)(magicast@0.5.2)(vite@8.1.5(@types/node@22.19.17)(esbuild@0.28.0)(jiti@2.7.0)(sass@1.99.0)(terser@5.47.1)(yaml@2.9.0))': dependencies: - '@nuxt/devtools-kit': 2.7.0(magicast@0.5.3)(vite@8.1.5(@types/node@22.19.17)(esbuild@0.28.0)(jiti@2.7.0)(sass@1.99.0)(terser@5.47.1)(yaml@2.9.0)) - '@nuxt/kit': 3.21.2(magicast@0.5.3) + '@nuxt/devtools-kit': 2.7.0(magicast@0.5.2)(vite@8.1.5(@types/node@22.19.17)(esbuild@0.28.0)(jiti@2.7.0)(sass@1.99.0)(terser@5.47.1)(yaml@2.9.0)) + '@nuxt/kit': 3.21.2(magicast@0.5.2) consola: 3.4.2 css-tree: 3.2.1 defu: 6.1.7 @@ -14278,9 +14815,9 @@ snapshots: - uploadthing - vite - '@nuxt/image@1.11.0(db0@0.3.4(@electric-sql/pglite@0.4.1)(mysql2@3.15.3))(ioredis@5.10.1)(magicast@0.5.3)': + '@nuxt/image@1.11.0(db0@0.3.4(@electric-sql/pglite@0.4.1)(mysql2@3.15.3))(ioredis@5.10.1)(magicast@0.5.2)': dependencies: - '@nuxt/kit': 3.21.2(magicast@0.5.3) + '@nuxt/kit': 3.21.2(magicast@0.5.2) consola: 3.4.2 defu: 6.1.7 h3: 1.15.11 @@ -14317,9 +14854,35 @@ snapshots: - react-native-b4a - uploadthing - '@nuxt/kit@3.21.2(magicast@0.5.3)': + '@nuxt/kit@3.21.10(magicast@0.5.2)': dependencies: - c12: 3.3.4(magicast@0.5.3) + c12: 3.3.4(magicast@0.5.2) + consola: 3.4.2 + defu: 6.1.7 + destr: 2.0.5 + errx: 0.1.2 + exsolve: 1.1.1 + ignore: 7.0.6 + jiti: 2.7.0 + klona: 2.0.6 + knitwork: 1.3.0 + mlly: 1.8.2 + ohash: 2.0.11 + pathe: 2.0.3 + pkg-types: 2.3.1 + rc9: 3.0.1 + scule: 1.3.0 + semver: 7.8.5 + tinyglobby: 0.2.17 + ufo: 1.6.4 + unctx: 2.5.0 + untyped: 2.0.0 + transitivePeerDependencies: + - magicast + + '@nuxt/kit@3.21.2(magicast@0.5.2)': + dependencies: + c12: 3.3.4(magicast@0.5.2) consola: 3.4.2 defu: 6.1.7 destr: 2.0.5 @@ -14343,6 +14906,32 @@ snapshots: transitivePeerDependencies: - magicast + '@nuxt/kit@3.21.7(magicast@0.5.2)': + dependencies: + c12: 3.3.4(magicast@0.5.2) + consola: 3.4.2 + defu: 6.1.7 + destr: 2.0.5 + errx: 0.1.2 + exsolve: 1.1.1 + ignore: 7.0.6 + jiti: 2.7.0 + klona: 2.0.6 + knitwork: 1.3.0 + mlly: 1.8.2 + ohash: 2.0.11 + pathe: 2.0.3 + pkg-types: 2.3.1 + rc9: 3.0.1 + scule: 1.3.0 + semver: 7.8.5 + tinyglobby: 0.2.17 + ufo: 1.6.4 + unctx: 2.5.0 + untyped: 2.0.0 + transitivePeerDependencies: + - magicast + '@nuxt/kit@3.21.7(magicast@0.5.3)': dependencies: c12: 3.3.4(magicast@0.5.3) @@ -14369,9 +14958,9 @@ snapshots: transitivePeerDependencies: - magicast - '@nuxt/kit@4.4.2(magicast@0.5.3)': + '@nuxt/kit@4.4.2(magicast@0.5.2)': dependencies: - c12: 3.3.4(magicast@0.5.3) + c12: 3.3.4(magicast@0.5.2) consola: 3.4.2 defu: 6.1.7 destr: 2.0.5 @@ -14394,6 +14983,36 @@ snapshots: transitivePeerDependencies: - magicast + '@nuxt/kit@4.5.1(magic-string@0.30.21)(magicast@0.5.2)(oxc-parser@0.132.0)(rolldown@1.1.5)(unplugin@3.3.0(esbuild@0.28.0)(rolldown@1.1.5)(rollup@4.62.3)(vite@8.1.5(@types/node@22.19.17)(esbuild@0.28.0)(jiti@2.7.0)(sass@1.99.0)(terser@5.47.1)(yaml@2.9.0)))': + dependencies: + c12: 3.3.4(magicast@0.5.2) + consola: 3.4.2 + defu: 6.1.7 + destr: 2.0.5 + errx: 0.1.2 + exsolve: 1.1.1 + ignore: 7.0.6 + jiti: 2.7.0 + klona: 2.0.6 + mlly: 1.8.2 + nostics: 1.2.0 + ohash: 2.0.11 + pathe: 2.0.3 + pkg-types: 2.3.1 + rc9: 3.0.1 + scule: 1.3.0 + tinyglobby: 0.2.17 + ufo: 1.6.4 + unctx: 3.0.0(magic-string@0.30.21)(oxc-parser@0.132.0)(rolldown@1.1.5)(unplugin@3.3.0(esbuild@0.28.0)(rolldown@1.1.5)(rollup@4.62.3)(vite@8.1.5(@types/node@22.19.17)(esbuild@0.28.0)(jiti@2.7.0)(sass@1.99.0)(terser@5.47.1)(yaml@2.9.0))) + untyped: 2.0.0 + verkit: 0.2.0 + transitivePeerDependencies: + - magic-string + - magicast + - oxc-parser + - rolldown + - unplugin + '@nuxt/kit@4.5.1(magic-string@0.30.21)(magicast@0.5.3)(oxc-parser@0.132.0)(rolldown@1.1.5)(unplugin@3.3.0(esbuild@0.28.0)(rolldown@1.1.5)(rollup@4.62.3)(vite@8.1.5(@types/node@22.19.17)(esbuild@0.28.0)(jiti@2.7.0)(sass@1.99.0)(terser@5.47.1)(yaml@2.9.0)))': dependencies: c12: 3.3.4(magicast@0.5.3) @@ -14454,7 +15073,7 @@ snapshots: - rolldown - unplugin - '@nuxt/kit@4.5.1(magic-string@0.30.21)(magicast@0.5.3)(rolldown@1.1.5)(unplugin@3.3.0(esbuild@0.28.1)(rolldown@1.1.5)(rollup@4.62.3)(vite@8.1.5(@types/node@24.12.2)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.99.0)(terser@5.47.1)(yaml@2.9.0)))': + '@nuxt/kit@4.5.1(magic-string@1.1.0)(magicast@0.5.3)(rolldown@1.1.5)(unplugin@3.3.0(esbuild@0.28.1)(rolldown@1.1.5)(rollup@4.62.3)(vite@8.1.5(@types/node@24.12.2)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.99.0)(terser@5.47.1)(yaml@2.9.0)))': dependencies: c12: 3.3.4(magicast@0.5.3) consola: 3.4.2 @@ -14474,6 +15093,36 @@ snapshots: scule: 1.3.0 tinyglobby: 0.2.17 ufo: 1.6.4 + unctx: 3.0.0(magic-string@1.1.0)(rolldown@1.1.5)(unplugin@3.3.0(esbuild@0.28.1)(rolldown@1.1.5)(rollup@4.62.3)(vite@8.1.5(@types/node@24.12.2)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.99.0)(terser@5.47.1)(yaml@2.9.0))) + untyped: 2.0.0 + verkit: 0.2.0 + transitivePeerDependencies: + - magic-string + - magicast + - oxc-parser + - rolldown + - unplugin + + '@nuxt/kit@4.5.1(magicast@0.5.2)(oxc-parser@0.132.0)(rolldown@1.1.5)': + dependencies: + c12: 3.3.4(magicast@0.5.2) + consola: 3.4.2 + defu: 6.1.7 + destr: 2.0.5 + errx: 0.1.2 + exsolve: 1.1.1 + ignore: 7.0.6 + jiti: 2.7.0 + klona: 2.0.6 + mlly: 1.8.2 + nostics: 1.2.0 + ohash: 2.0.11 + pathe: 2.0.3 + pkg-types: 2.3.1 + rc9: 3.0.1 + scule: 1.3.0 + tinyglobby: 0.2.17 + ufo: 1.6.4 unctx: 3.0.0(magic-string@0.30.21)(oxc-parser@0.132.0)(rolldown@1.1.5)(unplugin@3.3.0(esbuild@0.28.1)(rolldown@1.1.5)(rollup@4.62.3)(vite@8.1.5(@types/node@24.12.2)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.99.0)(terser@5.47.1)(yaml@2.9.0))) untyped: 2.0.0 verkit: 0.2.0 @@ -14484,10 +15133,10 @@ snapshots: - rolldown - unplugin - '@nuxt/nitro-server@3.21.7(@electric-sql/pglite@0.4.1)(db0@0.3.4(@electric-sql/pglite@0.4.1)(mysql2@3.15.3))(esbuild@0.28.0)(ioredis@5.10.1)(magicast@0.5.3)(mysql2@3.15.3)(nuxt@3.21.7(@electric-sql/pglite@0.4.1)(@parcel/watcher@2.5.6)(@types/node@22.19.17)(@vue/compiler-sfc@3.5.40)(cac@6.7.14)(db0@0.3.4(@electric-sql/pglite@0.4.1)(mysql2@3.15.3))(esbuild@0.28.0)(eslint@9.39.4(jiti@2.7.0))(ioredis@5.10.1)(lightningcss@1.33.0)(magicast@0.5.3)(mysql2@3.15.3)(optionator@0.9.4)(rolldown@1.1.5)(rollup-plugin-visualizer@7.0.1(rolldown@1.1.5)(rollup@4.62.3))(rollup@4.62.3)(sass@1.99.0)(terser@5.47.1)(typescript@5.9.3)(vite@8.1.5(@types/node@22.19.17)(esbuild@0.28.0)(jiti@2.7.0)(sass@1.99.0)(terser@5.47.1)(yaml@2.9.0))(vue-tsc@3.2.6(typescript@5.9.3))(yaml@2.9.0))(oxc-parser@0.132.0)(rolldown@1.1.5)(rollup@4.62.3)(typescript@5.9.3)(vite@8.1.5(@types/node@22.19.17)(esbuild@0.28.0)(jiti@2.7.0)(sass@1.99.0)(terser@5.47.1)(yaml@2.9.0))': + '@nuxt/nitro-server@3.21.7(@electric-sql/pglite@0.4.1)(db0@0.3.4(@electric-sql/pglite@0.4.1)(mysql2@3.15.3))(esbuild@0.28.0)(ioredis@5.10.1)(magicast@0.5.2)(mysql2@3.15.3)(nuxt@3.21.7(@electric-sql/pglite@0.4.1)(@parcel/watcher@2.5.6)(@types/node@22.19.17)(@vue/compiler-sfc@3.5.40)(cac@6.7.14)(db0@0.3.4(@electric-sql/pglite@0.4.1)(mysql2@3.15.3))(esbuild@0.28.0)(eslint@9.39.4(jiti@2.7.0))(ioredis@5.10.1)(lightningcss@1.33.0)(magicast@0.5.2)(mysql2@3.15.3)(optionator@0.9.4)(rolldown@1.1.5)(rollup-plugin-visualizer@7.0.1(rolldown@1.1.5)(rollup@4.62.3))(rollup@4.62.3)(sass@1.99.0)(terser@5.47.1)(typescript@5.9.3)(vite@8.1.5(@types/node@22.19.17)(esbuild@0.28.0)(jiti@2.7.0)(sass@1.99.0)(terser@5.47.1)(yaml@2.9.0))(vue-tsc@3.2.6(typescript@5.9.3))(yaml@2.9.0))(oxc-parser@0.132.0)(rolldown@1.1.5)(rollup@4.62.3)(typescript@5.9.3)(vite@8.1.5(@types/node@22.19.17)(esbuild@0.28.0)(jiti@2.7.0)(sass@1.99.0)(terser@5.47.1)(yaml@2.9.0))': dependencies: '@nuxt/devalue': 2.0.2 - '@nuxt/kit': 3.21.7(magicast@0.5.3) + '@nuxt/kit': 3.21.7(magicast@0.5.2) '@unhead/vue': 2.1.16(vue@3.5.40(typescript@5.9.3)) '@vue/shared': 3.5.40 consola: 3.4.2 @@ -14502,7 +15151,7 @@ snapshots: klona: 2.0.6 mocked-exports: 0.1.1 nitropack: 2.13.4(@electric-sql/pglite@0.4.1)(mysql2@3.15.3)(oxc-parser@0.132.0)(rolldown@1.1.5)(vite@8.1.5(@types/node@22.19.17)(esbuild@0.28.0)(jiti@2.7.0)(sass@1.99.0)(terser@5.47.1)(yaml@2.9.0)) - nuxt: 3.21.7(@electric-sql/pglite@0.4.1)(@parcel/watcher@2.5.6)(@types/node@22.19.17)(@vue/compiler-sfc@3.5.40)(cac@6.7.14)(db0@0.3.4(@electric-sql/pglite@0.4.1)(mysql2@3.15.3))(esbuild@0.28.0)(eslint@9.39.4(jiti@2.7.0))(ioredis@5.10.1)(lightningcss@1.33.0)(magicast@0.5.3)(mysql2@3.15.3)(optionator@0.9.4)(rolldown@1.1.5)(rollup-plugin-visualizer@7.0.1(rolldown@1.1.5)(rollup@4.62.3))(rollup@4.62.3)(sass@1.99.0)(terser@5.47.1)(typescript@5.9.3)(vite@8.1.5(@types/node@22.19.17)(esbuild@0.28.0)(jiti@2.7.0)(sass@1.99.0)(terser@5.47.1)(yaml@2.9.0))(vue-tsc@3.2.6(typescript@5.9.3))(yaml@2.9.0) + nuxt: 3.21.7(@electric-sql/pglite@0.4.1)(@parcel/watcher@2.5.6)(@types/node@22.19.17)(@vue/compiler-sfc@3.5.40)(cac@6.7.14)(db0@0.3.4(@electric-sql/pglite@0.4.1)(mysql2@3.15.3))(esbuild@0.28.0)(eslint@9.39.4(jiti@2.7.0))(ioredis@5.10.1)(lightningcss@1.33.0)(magicast@0.5.2)(mysql2@3.15.3)(optionator@0.9.4)(rolldown@1.1.5)(rollup-plugin-visualizer@7.0.1(rolldown@1.1.5)(rollup@4.62.3))(rollup@4.62.3)(sass@1.99.0)(terser@5.47.1)(typescript@5.9.3)(vite@8.1.5(@types/node@22.19.17)(esbuild@0.28.0)(jiti@2.7.0)(sass@1.99.0)(terser@5.47.1)(yaml@2.9.0))(vue-tsc@3.2.6(typescript@5.9.3))(yaml@2.9.0) ohash: 2.0.11 pathe: 2.0.3 pkg-types: 2.3.1 @@ -14642,6 +15291,15 @@ snapshots: pkg-types: 2.3.1 std-env: 4.2.0 + '@nuxt/telemetry@2.8.0(@nuxt/kit@3.21.7(magicast@0.5.2))': + dependencies: + '@nuxt/kit': 3.21.7(magicast@0.5.2) + citty: 0.2.2 + consola: 3.4.2 + ofetch: 2.0.0-alpha.3 + rc9: 3.0.1 + std-env: 4.2.0 + '@nuxt/telemetry@2.8.0(@nuxt/kit@3.21.7(magicast@0.5.3))': dependencies: '@nuxt/kit': 3.21.7(magicast@0.5.3) @@ -14651,9 +15309,58 @@ snapshots: rc9: 3.0.1 std-env: 4.2.0 - '@nuxt/vite-builder@3.21.7(@types/node@22.19.17)(eslint@9.39.4(jiti@2.7.0))(lightningcss@1.33.0)(magicast@0.5.3)(nuxt@3.21.7(@electric-sql/pglite@0.4.1)(@parcel/watcher@2.5.6)(@types/node@22.19.17)(@vue/compiler-sfc@3.5.40)(cac@6.7.14)(db0@0.3.4(@electric-sql/pglite@0.4.1)(mysql2@3.15.3))(esbuild@0.28.0)(eslint@9.39.4(jiti@2.7.0))(ioredis@5.10.1)(lightningcss@1.33.0)(magicast@0.5.3)(mysql2@3.15.3)(optionator@0.9.4)(rolldown@1.1.5)(rollup-plugin-visualizer@7.0.1(rolldown@1.1.5)(rollup@4.62.3))(rollup@4.62.3)(sass@1.99.0)(terser@5.47.1)(typescript@5.9.3)(vite@8.1.5(@types/node@22.19.17)(esbuild@0.28.0)(jiti@2.7.0)(sass@1.99.0)(terser@5.47.1)(yaml@2.9.0))(vue-tsc@3.2.6(typescript@5.9.3))(yaml@2.9.0))(optionator@0.9.4)(rolldown@1.1.5)(rollup-plugin-visualizer@7.0.1(rolldown@1.1.5)(rollup@4.62.3))(rollup@4.62.3)(sass@1.99.0)(terser@5.47.1)(typescript@5.9.3)(vue-tsc@3.2.6(typescript@5.9.3))(vue@3.5.40(typescript@5.9.3))(yaml@2.9.0)': + '@nuxt/test-utils@4.1.0(@playwright/test@1.62.0)(@vue/test-utils@2.4.11(@vue/compiler-dom@3.5.40)(@vue/server-renderer@3.5.40)(vue@3.5.17(typescript@5.9.3)))(esbuild@0.28.0)(happy-dom@20.11.1)(magicast@0.5.2)(playwright-core@1.62.0)(rolldown@1.1.5)(rollup@4.62.3)(typescript@5.9.3)(vite@8.1.5(@types/node@22.19.17)(esbuild@0.28.0)(jiti@2.7.0)(sass@1.99.0)(terser@5.47.1)(yaml@2.9.0))(vitest@4.1.10)': dependencies: - '@nuxt/kit': 3.21.7(magicast@0.5.3) + '@clack/prompts': 1.7.0 + '@nuxt/devtools-kit': 2.7.0(magicast@0.5.2)(vite@8.1.5(@types/node@22.19.17)(esbuild@0.28.0)(jiti@2.7.0)(sass@1.99.0)(terser@5.47.1)(yaml@2.9.0)) + '@nuxt/kit': 3.21.10(magicast@0.5.2) + c12: 3.3.4(magicast@0.5.2) + consola: 3.4.2 + defu: 6.1.7 + destr: 2.0.5 + estree-walker: 3.0.3 + exsolve: 1.1.1 + fake-indexeddb: 6.2.5 + get-port-please: 3.2.0 + h3: 1.15.11 + local-pkg: 1.2.1 + magic-string: 1.1.0 + node-fetch-native: 1.6.7 + node-mock-http: 1.0.4 + nypm: 0.6.9 + ofetch: 1.5.1 + pathe: 2.0.3 + perfect-debounce: 2.1.0 + radix3: 1.1.2 + scule: 1.3.0 + std-env: 4.2.0 + tinyexec: 1.2.4 + ufo: 1.6.4 + unplugin: 3.3.0(esbuild@0.28.0)(rolldown@1.1.5)(rollup@4.62.3)(vite@8.1.5(@types/node@22.19.17)(esbuild@0.28.0)(jiti@2.7.0)(sass@1.99.0)(terser@5.47.1)(yaml@2.9.0)) + vitest-environment-nuxt: 2.0.0(@playwright/test@1.62.0)(@vue/test-utils@2.4.11(@vue/compiler-dom@3.5.40)(@vue/server-renderer@3.5.40)(vue@3.5.17(typescript@5.9.3)))(esbuild@0.28.0)(happy-dom@20.11.1)(magicast@0.5.2)(playwright-core@1.62.0)(rolldown@1.1.5)(rollup@4.62.3)(typescript@5.9.3)(vite@8.1.5(@types/node@22.19.17)(esbuild@0.28.0)(jiti@2.7.0)(sass@1.99.0)(terser@5.47.1)(yaml@2.9.0))(vitest@4.1.10) + vue: 3.5.40(typescript@5.9.3) + optionalDependencies: + '@playwright/test': 1.62.0 + '@vue/test-utils': 2.4.11(@vue/compiler-dom@3.5.40)(@vue/server-renderer@3.5.40)(vue@3.5.17(typescript@5.9.3)) + happy-dom: 20.11.1 + playwright-core: 1.62.0 + vitest: 4.1.10(@types/node@22.19.17)(@vitest/coverage-v8@4.1.10)(happy-dom@20.11.1)(msw@2.15.0(@types/node@22.19.17)(typescript@5.9.3))(vite@8.1.5(@types/node@22.19.17)(esbuild@0.28.0)(jiti@2.7.0)(sass@1.99.0)(terser@5.47.1)(yaml@2.9.0)) + transitivePeerDependencies: + - '@farmfe/core' + - '@rspack/core' + - bun-types-no-globals + - esbuild + - magicast + - rolldown + - rollup + - typescript + - unloader + - vite + - webpack + + '@nuxt/vite-builder@3.21.7(@types/node@22.19.17)(eslint@9.39.4(jiti@2.7.0))(lightningcss@1.33.0)(magicast@0.5.2)(nuxt@3.21.7(@electric-sql/pglite@0.4.1)(@parcel/watcher@2.5.6)(@types/node@22.19.17)(@vue/compiler-sfc@3.5.40)(cac@6.7.14)(db0@0.3.4(@electric-sql/pglite@0.4.1)(mysql2@3.15.3))(esbuild@0.28.0)(eslint@9.39.4(jiti@2.7.0))(ioredis@5.10.1)(lightningcss@1.33.0)(magicast@0.5.2)(mysql2@3.15.3)(optionator@0.9.4)(rolldown@1.1.5)(rollup-plugin-visualizer@7.0.1(rolldown@1.1.5)(rollup@4.62.3))(rollup@4.62.3)(sass@1.99.0)(terser@5.47.1)(typescript@5.9.3)(vite@8.1.5(@types/node@22.19.17)(esbuild@0.28.0)(jiti@2.7.0)(sass@1.99.0)(terser@5.47.1)(yaml@2.9.0))(vue-tsc@3.2.6(typescript@5.9.3))(yaml@2.9.0))(optionator@0.9.4)(rolldown@1.1.5)(rollup-plugin-visualizer@7.0.1(rolldown@1.1.5)(rollup@4.62.3))(rollup@4.62.3)(sass@1.99.0)(terser@5.47.1)(typescript@5.9.3)(vue-tsc@3.2.6(typescript@5.9.3))(vue@3.5.40(typescript@5.9.3))(yaml@2.9.0)': + dependencies: + '@nuxt/kit': 3.21.7(magicast@0.5.2) '@rollup/plugin-replace': 6.0.3(rollup@4.62.3) '@vitejs/plugin-vue': 6.0.8(vite@7.3.6(@types/node@22.19.17)(jiti@2.7.0)(lightningcss@1.33.0)(sass@1.99.0)(terser@5.47.1)(yaml@2.9.0))(vue@3.5.40(typescript@5.9.3)) '@vitejs/plugin-vue-jsx': 5.1.6(vite@7.3.6(@types/node@22.19.17)(jiti@2.7.0)(lightningcss@1.33.0)(sass@1.99.0)(terser@5.47.1)(yaml@2.9.0))(vue@3.5.40(typescript@5.9.3)) @@ -14670,7 +15377,7 @@ snapshots: magic-string: 0.30.21 mlly: 1.8.2 mocked-exports: 0.1.1 - nuxt: 3.21.7(@electric-sql/pglite@0.4.1)(@parcel/watcher@2.5.6)(@types/node@22.19.17)(@vue/compiler-sfc@3.5.40)(cac@6.7.14)(db0@0.3.4(@electric-sql/pglite@0.4.1)(mysql2@3.15.3))(esbuild@0.28.0)(eslint@9.39.4(jiti@2.7.0))(ioredis@5.10.1)(lightningcss@1.33.0)(magicast@0.5.3)(mysql2@3.15.3)(optionator@0.9.4)(rolldown@1.1.5)(rollup-plugin-visualizer@7.0.1(rolldown@1.1.5)(rollup@4.62.3))(rollup@4.62.3)(sass@1.99.0)(terser@5.47.1)(typescript@5.9.3)(vite@8.1.5(@types/node@22.19.17)(esbuild@0.28.0)(jiti@2.7.0)(sass@1.99.0)(terser@5.47.1)(yaml@2.9.0))(vue-tsc@3.2.6(typescript@5.9.3))(yaml@2.9.0) + nuxt: 3.21.7(@electric-sql/pglite@0.4.1)(@parcel/watcher@2.5.6)(@types/node@22.19.17)(@vue/compiler-sfc@3.5.40)(cac@6.7.14)(db0@0.3.4(@electric-sql/pglite@0.4.1)(mysql2@3.15.3))(esbuild@0.28.0)(eslint@9.39.4(jiti@2.7.0))(ioredis@5.10.1)(lightningcss@1.33.0)(magicast@0.5.2)(mysql2@3.15.3)(optionator@0.9.4)(rolldown@1.1.5)(rollup-plugin-visualizer@7.0.1(rolldown@1.1.5)(rollup@4.62.3))(rollup@4.62.3)(sass@1.99.0)(terser@5.47.1)(typescript@5.9.3)(vite@8.1.5(@types/node@22.19.17)(esbuild@0.28.0)(jiti@2.7.0)(sass@1.99.0)(terser@5.47.1)(yaml@2.9.0))(vue-tsc@3.2.6(typescript@5.9.3))(yaml@2.9.0) nypm: 0.6.9 ohash: 2.0.11 pathe: 2.0.3 @@ -14777,14 +15484,14 @@ snapshots: - vue-tsc - yaml - '@nuxtjs/i18n@9.5.6(@vue/compiler-dom@3.5.40)(eslint@9.39.4(jiti@2.7.0))(magicast@0.5.3)(rollup@4.62.3)(vue@3.5.40(typescript@5.9.3))': + '@nuxtjs/i18n@9.5.6(@vue/compiler-dom@3.5.40)(eslint@9.39.4(jiti@2.7.0))(magicast@0.5.2)(rollup@4.62.3)(vue@3.5.17(typescript@5.9.3))': dependencies: '@intlify/h3': 0.6.1 '@intlify/shared': 10.0.8 - '@intlify/unplugin-vue-i18n': 6.0.8(@vue/compiler-dom@3.5.40)(eslint@9.39.4(jiti@2.7.0))(rollup@4.62.3)(typescript@5.9.3)(vue-i18n@10.0.8(vue@3.5.40(typescript@5.9.3)))(vue@3.5.40(typescript@5.9.3)) + '@intlify/unplugin-vue-i18n': 6.0.8(@vue/compiler-dom@3.5.40)(eslint@9.39.4(jiti@2.7.0))(rollup@4.62.3)(typescript@5.9.3)(vue-i18n@10.0.8(vue@3.5.17(typescript@5.9.3)))(vue@3.5.17(typescript@5.9.3)) '@intlify/utils': 0.13.0 '@miyaneee/rollup-plugin-json5': 1.2.0(rollup@4.62.3) - '@nuxt/kit': 3.21.2(magicast@0.5.3) + '@nuxt/kit': 3.21.2(magicast@0.5.2) '@oxc-parser/wasm': 0.60.0 '@rollup/plugin-yaml': 4.1.2(rollup@4.62.3) '@vue/compiler-sfc': 3.5.32 @@ -14801,9 +15508,9 @@ snapshots: typescript: 5.9.3 ufo: 1.6.3 unplugin: 2.3.11 - unplugin-vue-router: 0.12.0(vue-router@4.6.4(vue@3.5.40(typescript@5.9.3)))(vue@3.5.40(typescript@5.9.3)) - vue-i18n: 10.0.8(vue@3.5.40(typescript@5.9.3)) - vue-router: 4.6.4(vue@3.5.40(typescript@5.9.3)) + unplugin-vue-router: 0.12.0(vue-router@4.5.1(vue@3.5.17(typescript@5.9.3)))(vue@3.5.17(typescript@5.9.3)) + vue-i18n: 10.0.8(vue@3.5.17(typescript@5.9.3)) + vue-router: 4.5.1(vue@3.5.17(typescript@5.9.3)) transitivePeerDependencies: - '@vue/compiler-dom' - eslint @@ -14855,6 +15562,19 @@ snapshots: dependencies: '@octokit/openapi-types': 27.0.0 + '@one-ini/wasm@0.1.1': {} + + '@open-draft/deferred-promise@2.2.0': {} + + '@open-draft/deferred-promise@3.0.0': {} + + '@open-draft/logger@0.3.0': + dependencies: + is-node-process: 1.2.0 + outvariant: 1.4.3 + + '@open-draft/until@2.1.0': {} + '@oslojs/encoding@1.1.0': {} '@oxc-minify/binding-android-arm-eabi@0.132.0': @@ -15305,6 +16025,10 @@ snapshots: '@pkgr/core@0.2.9': {} + '@playwright/test@1.62.0': + dependencies: + playwright: 1.62.0 + '@polka/url@1.0.0-next.29': {} '@poppinss/colors@4.1.6': @@ -15330,16 +16054,16 @@ snapshots: '@prisma/client-runtime-utils@7.7.0': {} - '@prisma/client@7.7.0(prisma@7.7.0(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(magicast@0.5.3)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(typescript@5.9.3))(typescript@5.9.3)': + '@prisma/client@7.7.0(prisma@7.7.0(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(magicast@0.5.2)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(typescript@5.9.3))(typescript@5.9.3)': dependencies: '@prisma/client-runtime-utils': 7.7.0 optionalDependencies: - prisma: 7.7.0(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(magicast@0.5.3)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(typescript@5.9.3) + prisma: 7.7.0(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(magicast@0.5.2)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(typescript@5.9.3) typescript: 5.9.3 - '@prisma/config@7.7.0(magicast@0.5.3)': + '@prisma/config@7.7.0(magicast@0.5.2)': dependencies: - c12: 3.1.0(magicast@0.5.3) + c12: 3.1.0(magicast@0.5.2) deepmerge-ts: 7.1.5 effect: 3.20.0 empathic: 2.0.0 @@ -15979,6 +16703,11 @@ snapshots: '@tanstack/virtual-core@3.13.23': {} + '@tanstack/vue-virtual@3.13.23(vue@3.5.17(typescript@5.9.3))': + dependencies: + '@tanstack/virtual-core': 3.13.23 + vue: 3.5.17(typescript@5.9.3) + '@tanstack/vue-virtual@3.13.23(vue@3.5.40(typescript@5.9.3))': dependencies: '@tanstack/virtual-core': 3.13.23 @@ -16069,10 +16798,17 @@ snapshots: tslib: 2.8.1 optional: true + '@types/chai@5.2.3': + dependencies: + '@types/deep-eql': 4.0.2 + assertion-error: 2.0.1 + '@types/debug@4.1.13': dependencies: '@types/ms': 2.1.0 + '@types/deep-eql@4.0.2': {} + '@types/esrecurse@4.3.1': {} '@types/estree-jsx@1.0.5': @@ -16100,8 +16836,6 @@ snapshots: '@types/js-yaml@4.0.9': {} - '@types/jsesc@2.5.1': {} - '@types/json-schema@7.0.15': {} '@types/json5@0.0.29': {} @@ -16165,6 +16899,12 @@ snapshots: '@types/semver@7.7.1': {} + '@types/set-cookie-parser@2.4.10': + dependencies: + '@types/node': 24.12.2 + + '@types/statuses@2.0.6': {} + '@types/turndown@5.0.6': {} '@types/unist@2.0.11': {} @@ -16173,6 +16913,12 @@ snapshots: '@types/web-bluetooth@0.0.21': {} + '@types/whatwg-mimetype@3.0.2': {} + + '@types/ws@8.18.1': + dependencies: + '@types/node': 24.12.2 + '@typescript-eslint/eslint-plugin@8.58.2(@typescript-eslint/parser@8.58.2(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3))(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3)': dependencies: '@eslint-community/regexpp': 4.12.2 @@ -16558,6 +17304,62 @@ snapshots: vite: 7.3.6(@types/node@24.12.2)(jiti@2.7.0)(lightningcss@1.33.0)(sass@1.99.0)(terser@5.47.1)(yaml@2.9.0) vue: 3.5.40(typescript@5.9.3) + '@vitest/coverage-v8@4.1.10(vitest@4.1.10)': + dependencies: + '@bcoe/v8-coverage': 1.0.2 + '@vitest/utils': 4.1.10 + ast-v8-to-istanbul: 1.0.5 + istanbul-lib-coverage: 3.2.2 + istanbul-lib-report: 3.0.1 + istanbul-reports: 3.2.0 + magicast: 0.5.3 + obug: 2.1.4 + std-env: 4.2.0 + tinyrainbow: 3.1.1 + vitest: 4.1.10(@types/node@22.19.17)(@vitest/coverage-v8@4.1.10)(happy-dom@20.11.1)(msw@2.15.0(@types/node@22.19.17)(typescript@5.9.3))(vite@8.1.5(@types/node@22.19.17)(esbuild@0.28.0)(jiti@2.7.0)(sass@1.99.0)(terser@5.47.1)(yaml@2.9.0)) + + '@vitest/expect@4.1.10': + dependencies: + '@standard-schema/spec': 1.1.0 + '@types/chai': 5.2.3 + '@vitest/spy': 4.1.10 + '@vitest/utils': 4.1.10 + chai: 6.2.2 + tinyrainbow: 3.1.1 + + '@vitest/mocker@4.1.10(msw@2.15.0(@types/node@22.19.17)(typescript@5.9.3))(vite@8.1.5(@types/node@22.19.17)(esbuild@0.28.0)(jiti@2.7.0)(sass@1.99.0)(terser@5.47.1)(yaml@2.9.0))': + dependencies: + '@vitest/spy': 4.1.10 + estree-walker: 3.0.3 + magic-string: 0.30.21 + optionalDependencies: + msw: 2.15.0(@types/node@22.19.17)(typescript@5.9.3) + vite: 8.1.5(@types/node@22.19.17)(esbuild@0.28.0)(jiti@2.7.0)(sass@1.99.0)(terser@5.47.1)(yaml@2.9.0) + + '@vitest/pretty-format@4.1.10': + dependencies: + tinyrainbow: 3.1.1 + + '@vitest/runner@4.1.10': + dependencies: + '@vitest/utils': 4.1.10 + pathe: 2.0.3 + + '@vitest/snapshot@4.1.10': + dependencies: + '@vitest/pretty-format': 4.1.10 + '@vitest/utils': 4.1.10 + magic-string: 0.30.21 + pathe: 2.0.3 + + '@vitest/spy@4.1.10': {} + + '@vitest/utils@4.1.10': + dependencies: + '@vitest/pretty-format': 4.1.10 + convert-source-map: 2.0.0 + tinyrainbow: 3.1.1 + '@volar/language-core@2.4.27': dependencies: '@volar/source-map': 2.4.27 @@ -16576,16 +17378,16 @@ snapshots: path-browserify: 1.0.1 vscode-uri: 3.1.0 - '@vue-macros/common@1.16.1(vue@3.5.40(typescript@5.9.3))': + '@vue-macros/common@1.16.1(vue@3.5.17(typescript@5.9.3))': dependencies: - '@vue/compiler-sfc': 3.5.32 + '@vue/compiler-sfc': 3.5.40 ast-kit: 1.4.3 local-pkg: 1.2.1 magic-string-ast: 0.7.1 pathe: 2.0.3 picomatch: 4.0.5 optionalDependencies: - vue: 3.5.40(typescript@5.9.3) + vue: 3.5.17(typescript@5.9.3) '@vue-macros/common@3.1.4(vue@3.5.40(typescript@5.9.3))': dependencies: @@ -16626,6 +17428,14 @@ snapshots: transitivePeerDependencies: - supports-color + '@vue/compiler-core@3.5.17': + dependencies: + '@babel/parser': 7.29.7 + '@vue/shared': 3.5.17 + entities: 4.5.0 + estree-walker: 2.0.2 + source-map-js: 1.2.1 + '@vue/compiler-core@3.5.32': dependencies: '@babel/parser': 7.29.7 @@ -16642,6 +17452,11 @@ snapshots: estree-walker: 2.0.2 source-map-js: 1.2.1 + '@vue/compiler-dom@3.5.17': + dependencies: + '@vue/compiler-core': 3.5.17 + '@vue/shared': 3.5.17 + '@vue/compiler-dom@3.5.32': dependencies: '@vue/compiler-core': 3.5.32 @@ -16652,9 +17467,21 @@ snapshots: '@vue/compiler-core': 3.5.40 '@vue/shared': 3.5.40 + '@vue/compiler-sfc@3.5.17': + dependencies: + '@babel/parser': 7.29.7 + '@vue/compiler-core': 3.5.17 + '@vue/compiler-dom': 3.5.17 + '@vue/compiler-ssr': 3.5.17 + '@vue/shared': 3.5.17 + estree-walker: 2.0.2 + magic-string: 0.30.21 + postcss: 8.5.24 + source-map-js: 1.2.1 + '@vue/compiler-sfc@3.5.32': dependencies: - '@babel/parser': 7.29.2 + '@babel/parser': 7.29.7 '@vue/compiler-core': 3.5.32 '@vue/compiler-dom': 3.5.32 '@vue/compiler-ssr': 3.5.32 @@ -16676,6 +17503,11 @@ snapshots: postcss: 8.5.24 source-map-js: 1.2.1 + '@vue/compiler-ssr@3.5.17': + dependencies: + '@vue/compiler-dom': 3.5.17 + '@vue/shared': 3.5.17 + '@vue/compiler-ssr@3.5.32': dependencies: '@vue/compiler-dom': 3.5.32 @@ -16688,10 +17520,6 @@ snapshots: '@vue/devtools-api@6.6.4': {} - '@vue/devtools-api@8.2.1': - dependencies: - '@vue/devtools-kit': 8.2.1 - '@vue/devtools-core@8.2.1(vue@3.5.40(typescript@5.9.3))': dependencies: '@vue/devtools-kit': 8.2.1 @@ -16737,15 +17565,31 @@ snapshots: path-browserify: 1.0.1 picomatch: 4.0.5 + '@vue/reactivity@3.5.17': + dependencies: + '@vue/shared': 3.5.17 + '@vue/reactivity@3.5.40': dependencies: '@vue/shared': 3.5.40 + '@vue/runtime-core@3.5.17': + dependencies: + '@vue/reactivity': 3.5.17 + '@vue/shared': 3.5.17 + '@vue/runtime-core@3.5.40': dependencies: '@vue/reactivity': 3.5.40 '@vue/shared': 3.5.40 + '@vue/runtime-dom@3.5.17': + dependencies: + '@vue/reactivity': 3.5.17 + '@vue/runtime-core': 3.5.17 + '@vue/shared': 3.5.17 + csstype: 3.2.3 + '@vue/runtime-dom@3.5.40': dependencies: '@vue/reactivity': 3.5.40 @@ -16753,42 +17597,61 @@ snapshots: '@vue/shared': 3.5.40 csstype: 3.2.3 + '@vue/server-renderer@3.5.17(vue@3.5.17(typescript@5.9.3))': + dependencies: + '@vue/compiler-ssr': 3.5.17 + '@vue/shared': 3.5.17 + vue: 3.5.17(typescript@5.9.3) + '@vue/server-renderer@3.5.40': dependencies: '@vue/compiler-ssr': 3.5.40 '@vue/runtime-dom': 3.5.40 '@vue/shared': 3.5.40 + '@vue/shared@3.5.17': {} + '@vue/shared@3.5.32': {} '@vue/shared@3.5.40': {} - '@vueuse/core@13.6.0(vue@3.5.40(typescript@5.9.3))': + '@vue/test-utils@2.4.11(@vue/compiler-dom@3.5.40)(@vue/server-renderer@3.5.40)(vue@3.5.17(typescript@5.9.3))': + dependencies: + '@vue/compiler-dom': 3.5.40 + js-beautify: 1.15.4 + vue: 3.5.17(typescript@5.9.3) + vue-component-type-helpers: 3.3.8 + optionalDependencies: + '@vue/server-renderer': 3.5.40 + + '@vueuse/core@13.6.0(vue@3.5.17(typescript@5.9.3))': dependencies: '@types/web-bluetooth': 0.0.21 '@vueuse/metadata': 13.6.0 - '@vueuse/shared': 13.6.0(vue@3.5.40(typescript@5.9.3)) - vue: 3.5.40(typescript@5.9.3) + '@vueuse/shared': 13.6.0(vue@3.5.17(typescript@5.9.3)) + vue: 3.5.17(typescript@5.9.3) '@vueuse/metadata@13.6.0': {} - '@vueuse/nuxt@13.6.0(magicast@0.5.3)(nuxt@3.21.7(@electric-sql/pglite@0.4.1)(@parcel/watcher@2.5.6)(@types/node@22.19.17)(@vue/compiler-sfc@3.5.40)(cac@6.7.14)(db0@0.3.4(@electric-sql/pglite@0.4.1)(mysql2@3.15.3))(esbuild@0.28.0)(eslint@9.39.4(jiti@2.7.0))(ioredis@5.10.1)(lightningcss@1.33.0)(magicast@0.5.3)(mysql2@3.15.3)(optionator@0.9.4)(rolldown@1.1.5)(rollup-plugin-visualizer@7.0.1(rolldown@1.1.5)(rollup@4.62.3))(rollup@4.62.3)(sass@1.99.0)(terser@5.47.1)(typescript@5.9.3)(vite@8.1.5(@types/node@22.19.17)(esbuild@0.28.0)(jiti@2.7.0)(sass@1.99.0)(terser@5.47.1)(yaml@2.9.0))(vue-tsc@3.2.6(typescript@5.9.3))(yaml@2.9.0))(vue@3.5.40(typescript@5.9.3))': + '@vueuse/nuxt@13.6.0(magicast@0.5.2)(nuxt@3.21.7(@electric-sql/pglite@0.4.1)(@parcel/watcher@2.5.6)(@types/node@22.19.17)(@vue/compiler-sfc@3.5.40)(cac@6.7.14)(db0@0.3.4(@electric-sql/pglite@0.4.1)(mysql2@3.15.3))(esbuild@0.28.0)(eslint@9.39.4(jiti@2.7.0))(ioredis@5.10.1)(lightningcss@1.33.0)(magicast@0.5.2)(mysql2@3.15.3)(optionator@0.9.4)(rolldown@1.1.5)(rollup-plugin-visualizer@7.0.1(rolldown@1.1.5)(rollup@4.62.3))(rollup@4.62.3)(sass@1.99.0)(terser@5.47.1)(typescript@5.9.3)(vite@8.1.5(@types/node@22.19.17)(esbuild@0.28.0)(jiti@2.7.0)(sass@1.99.0)(terser@5.47.1)(yaml@2.9.0))(vue-tsc@3.2.6(typescript@5.9.3))(yaml@2.9.0))(vue@3.5.17(typescript@5.9.3))': dependencies: - '@nuxt/kit': 4.4.2(magicast@0.5.3) - '@vueuse/core': 13.6.0(vue@3.5.40(typescript@5.9.3)) + '@nuxt/kit': 4.4.2(magicast@0.5.2) + '@vueuse/core': 13.6.0(vue@3.5.17(typescript@5.9.3)) '@vueuse/metadata': 13.6.0 local-pkg: 1.1.2 - nuxt: 3.21.7(@electric-sql/pglite@0.4.1)(@parcel/watcher@2.5.6)(@types/node@22.19.17)(@vue/compiler-sfc@3.5.40)(cac@6.7.14)(db0@0.3.4(@electric-sql/pglite@0.4.1)(mysql2@3.15.3))(esbuild@0.28.0)(eslint@9.39.4(jiti@2.7.0))(ioredis@5.10.1)(lightningcss@1.33.0)(magicast@0.5.3)(mysql2@3.15.3)(optionator@0.9.4)(rolldown@1.1.5)(rollup-plugin-visualizer@7.0.1(rolldown@1.1.5)(rollup@4.62.3))(rollup@4.62.3)(sass@1.99.0)(terser@5.47.1)(typescript@5.9.3)(vite@8.1.5(@types/node@22.19.17)(esbuild@0.28.0)(jiti@2.7.0)(sass@1.99.0)(terser@5.47.1)(yaml@2.9.0))(vue-tsc@3.2.6(typescript@5.9.3))(yaml@2.9.0) - vue: 3.5.40(typescript@5.9.3) + nuxt: 3.21.7(@electric-sql/pglite@0.4.1)(@parcel/watcher@2.5.6)(@types/node@22.19.17)(@vue/compiler-sfc@3.5.40)(cac@6.7.14)(db0@0.3.4(@electric-sql/pglite@0.4.1)(mysql2@3.15.3))(esbuild@0.28.0)(eslint@9.39.4(jiti@2.7.0))(ioredis@5.10.1)(lightningcss@1.33.0)(magicast@0.5.2)(mysql2@3.15.3)(optionator@0.9.4)(rolldown@1.1.5)(rollup-plugin-visualizer@7.0.1(rolldown@1.1.5)(rollup@4.62.3))(rollup@4.62.3)(sass@1.99.0)(terser@5.47.1)(typescript@5.9.3)(vite@8.1.5(@types/node@22.19.17)(esbuild@0.28.0)(jiti@2.7.0)(sass@1.99.0)(terser@5.47.1)(yaml@2.9.0))(vue-tsc@3.2.6(typescript@5.9.3))(yaml@2.9.0) + vue: 3.5.17(typescript@5.9.3) transitivePeerDependencies: - magicast - '@vueuse/shared@13.6.0(vue@3.5.40(typescript@5.9.3))': + '@vueuse/shared@13.6.0(vue@3.5.17(typescript@5.9.3))': dependencies: - vue: 3.5.40(typescript@5.9.3) + vue: 3.5.17(typescript@5.9.3) abab@2.0.6: {} + abbrev@2.0.0: {} + abbrev@3.0.1: {} abort-controller@3.0.0: @@ -17050,6 +17913,8 @@ snapshots: assert-plus@1.0.0: {} + assertion-error@2.0.1: {} + ast-kit@1.4.3: dependencies: '@babel/parser': 7.29.7 @@ -17062,6 +17927,12 @@ snapshots: ast-types-flow@0.0.8: {} + ast-v8-to-istanbul@1.0.5: + dependencies: + '@jridgewell/trace-mapping': 0.3.31 + estree-walker: 3.0.3 + js-tokens: 10.0.0 + ast-walker-scope@0.6.2: dependencies: '@babel/parser': 7.29.7 @@ -17072,12 +17943,6 @@ snapshots: '@babel/parser': 7.29.7 ast-kit: 2.2.0 - ast-walker-scope@0.9.0: - dependencies: - '@babel/parser': 7.29.7 - '@babel/types': 7.29.7 - ast-kit: 2.2.0 - astring@1.9.0: {} astro-expressive-code@0.43.1(astro@7.1.1(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)(@types/node@24.12.2)(db0@0.3.4(@electric-sql/pglite@0.4.1)(mysql2@3.15.3))(ioredis@5.10.1)(jiti@2.7.0)(rollup@4.62.3)(sass@1.99.0)(terser@5.47.1)(yaml@2.9.0)): @@ -17284,7 +18149,7 @@ snapshots: bin-build@3.0.0: dependencies: - decompress: 4.2.1 + decompress: 4.2.1(patch_hash=45158ed496346a01b560e676c853793c22191f0f6056b5633694f408fcb7372b) download: 6.2.5 execa: 0.7.0 p-map-series: 1.0.0 @@ -17410,6 +18275,10 @@ snapshots: buffer-from@1.1.2: {} + buffer-image-size@0.6.4: + dependencies: + '@types/node': 24.12.2 + buffer@5.7.1: dependencies: base64-js: 1.5.1 @@ -17433,7 +18302,7 @@ snapshots: byte-counter@0.1.0: {} - c12@3.1.0(magicast@0.5.3): + c12@3.1.0(magicast@0.5.2): dependencies: chokidar: 4.0.3 confbox: 0.2.4 @@ -17448,7 +18317,7 @@ snapshots: pkg-types: 2.3.1 rc9: 2.1.2 optionalDependencies: - magicast: 0.5.3 + magicast: 0.5.2 c12@3.3.4(magicast@0.5.2): dependencies: @@ -17564,6 +18433,8 @@ snapshots: ccount@2.0.1: {} + chai@6.2.2: {} + chalk@1.1.3: dependencies: ansi-styles: 2.2.1 @@ -17704,8 +18575,16 @@ snapshots: cli-width@3.0.0: {} + cli-width@4.1.0: {} + client-only@0.0.1: {} + cliui@8.0.1: + dependencies: + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 7.0.0 + cliui@9.0.1: dependencies: string-width: 7.2.0 @@ -17764,6 +18643,8 @@ snapshots: comma-separated-tokens@2.0.3: {} + commander@10.0.1: {} + commander@11.1.0: {} commander@2.20.3: {} @@ -18089,7 +18970,7 @@ snapshots: pify: 2.3.0 yauzl: 2.10.0 - decompress@4.2.1: + decompress@4.2.1(patch_hash=45158ed496346a01b560e676c853793c22191f0f6056b5633694f408fcb7372b): dependencies: decompress-tar: 4.1.1 decompress-tarbz2: 4.1.1 @@ -18246,7 +19127,7 @@ snapshots: dependencies: caw: 2.0.1 content-disposition: 0.5.4 - decompress: 4.2.1 + decompress: 4.2.1(patch_hash=45158ed496346a01b560e676c853793c22191f0f6056b5633694f408fcb7372b) ext-name: 5.0.0 file-type: 5.2.0 filenamify: 2.1.0 @@ -18261,7 +19142,7 @@ snapshots: archive-type: 4.0.0 caw: 2.0.1 content-disposition: 0.5.4 - decompress: 4.2.1 + decompress: 4.2.1(patch_hash=45158ed496346a01b560e676c853793c22191f0f6056b5633694f408fcb7372b) ext-name: 5.0.0 file-type: 8.1.0 filenamify: 2.1.0 @@ -18292,6 +19173,13 @@ snapshots: dependencies: safe-buffer: 5.2.1 + editorconfig@1.0.7: + dependencies: + '@one-ini/wasm': 0.1.1 + commander: 10.0.1 + minimatch: 10.2.6 + semver: 7.8.5 + ee-first@1.1.1: {} effect@3.20.0: @@ -18640,7 +19528,7 @@ snapshots: '@typescript-eslint/parser': 8.58.2(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3) eslint: 9.39.4(jiti@2.7.0) eslint-import-resolver-node: 0.3.10 - eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import-x@4.17.1(@typescript-eslint/utils@8.65.0(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3))(eslint-import-resolver-node@0.3.10)(eslint@9.39.4(jiti@2.7.0)))(eslint-plugin-import@2.32.0)(eslint@9.39.4(jiti@2.7.0)) + eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import-x@4.17.1(@typescript-eslint/utils@8.65.0(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3))(eslint-import-resolver-node@0.3.10)(eslint@9.39.4(jiti@2.7.0)))(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.58.2(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3))(eslint@9.39.4(jiti@2.7.0)))(eslint@9.39.4(jiti@2.7.0)) eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.58.2(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.4(jiti@2.7.0)) eslint-plugin-jsx-a11y: 6.10.2(eslint@9.39.4(jiti@2.7.0)) eslint-plugin-react: 7.37.5(eslint@9.39.4(jiti@2.7.0)) @@ -18688,7 +19576,7 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-import-resolver-typescript@3.10.1(eslint-plugin-import-x@4.17.1(@typescript-eslint/utils@8.65.0(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3))(eslint-import-resolver-node@0.3.10)(eslint@9.39.4(jiti@2.7.0)))(eslint-plugin-import@2.32.0)(eslint@9.39.4(jiti@2.7.0)): + eslint-import-resolver-typescript@3.10.1(eslint-plugin-import-x@4.17.1(@typescript-eslint/utils@8.65.0(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3))(eslint-import-resolver-node@0.3.10)(eslint@9.39.4(jiti@2.7.0)))(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.58.2(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3))(eslint@9.39.4(jiti@2.7.0)))(eslint@9.39.4(jiti@2.7.0)): dependencies: '@nolyfill/is-core-module': 1.0.39 debug: 4.4.3 @@ -18715,7 +19603,7 @@ snapshots: '@typescript-eslint/parser': 8.58.2(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3) eslint: 9.39.4(jiti@2.7.0) eslint-import-resolver-node: 0.3.10 - eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import-x@4.17.1(@typescript-eslint/utils@8.65.0(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3))(eslint-import-resolver-node@0.3.10)(eslint@9.39.4(jiti@2.7.0)))(eslint-plugin-import@2.32.0)(eslint@9.39.4(jiti@2.7.0)) + eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import-x@4.17.1(@typescript-eslint/utils@8.65.0(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3))(eslint-import-resolver-node@0.3.10)(eslint@9.39.4(jiti@2.7.0)))(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.58.2(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3))(eslint@9.39.4(jiti@2.7.0)))(eslint@9.39.4(jiti@2.7.0)) transitivePeerDependencies: - supports-color @@ -18967,6 +19855,15 @@ snapshots: '@stylistic/eslint-plugin': 5.10.0(eslint@9.39.4(jiti@2.7.0)) '@typescript-eslint/parser': 8.58.2(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3) + eslint-plugin-vuejs-accessibility@2.5.0(eslint@9.39.4(jiti@2.7.0))(globals@17.8.0): + dependencies: + aria-query: 5.3.2 + eslint: 9.39.4(jiti@2.7.0) + globals: 17.8.0 + vue-eslint-parser: 10.4.1(eslint@9.39.4(jiti@2.7.0)) + transitivePeerDependencies: + - supports-color + eslint-processor-vue-blocks@2.0.0(@vue/compiler-sfc@3.5.40)(eslint@9.39.4(jiti@2.7.0)): dependencies: '@vue/compiler-sfc': 3.5.40 @@ -19185,6 +20082,8 @@ snapshots: expand-template@2.0.3: optional: true + expect-type@1.4.0: {} + expressive-code@0.43.1: dependencies: '@expressive-code/core': 0.43.1 @@ -19228,10 +20127,16 @@ snapshots: extsprintf@1.3.0: {} + fake-indexeddb@6.2.5: {} + fast-check@3.23.2: dependencies: pure-rand: 6.1.0 + fast-check@4.9.0: + dependencies: + pure-rand: 8.4.2 + fast-content-type-parse@3.0.0: {} fast-copy@4.0.3: {} @@ -19473,6 +20378,9 @@ snapshots: fs.realpath@1.0.0: {} + fsevents@2.3.2: + optional: true + fsevents@2.3.3: optional: true @@ -19698,6 +20606,8 @@ snapshots: graphmatch@1.1.1: {} + graphql@16.14.2: {} + gray-matter@4.0.3: dependencies: js-yaml: 3.15.0 @@ -19728,6 +20638,19 @@ snapshots: optionalDependencies: crossws: 0.4.10(srvx@0.11.22) + happy-dom@20.11.1: + dependencies: + '@types/node': 24.12.2 + '@types/whatwg-mimetype': 3.0.2 + '@types/ws': 8.18.1 + buffer-image-size: 0.6.4 + entities: 7.0.1 + whatwg-mimetype: 3.0.0 + ws: 8.21.1 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + har-schema@2.0.0: {} har-validator@5.1.5: @@ -19966,6 +20889,11 @@ snapshots: property-information: 7.1.0 space-separated-tokens: 2.0.2 + headers-polyfill@5.0.1: + dependencies: + '@types/set-cookie-parser': 2.4.10 + set-cookie-parser: 3.1.2 + help-me@5.0.0: {} hono@4.12.14: {} @@ -19982,6 +20910,8 @@ snapshots: html-entities@2.6.0: {} + html-escaper@2.0.2: {} + html-escaper@3.0.3: {} html-void-elements@3.0.0: {} @@ -20050,6 +20980,8 @@ snapshots: human-signals@5.0.0: {} + husky@9.1.7: {} + i18next@26.3.1(typescript@5.9.3): optionalDependencies: typescript: 5.9.3 @@ -20397,6 +21329,8 @@ snapshots: is-negative-zero@2.0.3: {} + is-node-process@1.2.0: {} + is-npm@4.0.0: {} is-number-object@1.1.1: @@ -20519,6 +21453,19 @@ snapshots: isstream@0.1.2: {} + istanbul-lib-coverage@3.2.2: {} + + istanbul-lib-report@3.0.1: + dependencies: + istanbul-lib-coverage: 3.2.2 + make-dir: 4.0.0 + supports-color: 7.2.0 + + istanbul-reports@3.2.0: + dependencies: + html-escaper: 2.0.2 + istanbul-lib-report: 3.0.1 + isurl@1.0.0: dependencies: has-to-string-tag-x: 1.4.1 @@ -20555,6 +21502,18 @@ snapshots: js-base64@3.7.8: {} + js-beautify@1.15.4: + dependencies: + config-chain: 1.1.13 + editorconfig: 1.0.7 + glob: 10.5.0 + js-cookie: 3.0.8 + nopt: 7.2.1 + + js-cookie@3.0.8: {} + + js-tokens@10.0.0: {} + js-tokens@4.0.0: {} js-tokens@9.0.1: {} @@ -20853,6 +21812,14 @@ snapshots: lilconfig@3.1.3: {} + lint-staged@17.2.0: + dependencies: + picomatch: 4.0.5 + string-argv: 0.3.2 + tinyexec: 1.2.4 + optionalDependencies: + yaml: 2.9.0 + listhen@1.10.0: dependencies: '@parcel/watcher': 2.5.6 @@ -21074,6 +22041,10 @@ snapshots: dependencies: '@jridgewell/sourcemap-codec': 1.5.5 + magic-string@1.1.0: + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + magicast@0.5.2: dependencies: '@babel/parser': 7.29.3 @@ -21094,6 +22065,10 @@ snapshots: dependencies: semver: 6.3.1 + make-dir@4.0.0: + dependencies: + semver: 7.8.5 + map-obj@1.0.1: {} markdown-extensions@2.0.0: {} @@ -21698,10 +22673,37 @@ snapshots: ms@2.1.3: {} + msw@2.15.0(@types/node@22.19.17)(typescript@5.9.3): + dependencies: + '@inquirer/confirm': 6.1.1(@types/node@22.19.17) + '@mswjs/interceptors': 0.41.9 + '@open-draft/deferred-promise': 3.0.0 + '@types/statuses': 2.0.6 + cookie: 1.1.1 + graphql: 16.14.2 + headers-polyfill: 5.0.1 + is-node-process: 1.2.0 + outvariant: 1.4.3 + path-to-regexp: 6.3.0 + picocolors: 1.1.1 + rettime: 0.11.11 + statuses: 2.0.2 + strict-event-emitter: 0.5.1 + tough-cookie: 6.0.1 + type-fest: 5.6.0 + until-async: 3.0.2 + yargs: 17.7.3 + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - '@types/node' + muggle-string@0.4.1: {} mute-stream@0.0.8: {} + mute-stream@3.0.0: {} + mysql2@3.15.3: dependencies: aws-ssl-profiles: 1.1.2 @@ -21731,7 +22733,7 @@ snapshots: neotraverse@0.6.18: {} - next@15.5.21(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(sass@1.99.0): + next@15.5.21(@playwright/test@1.62.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(sass@1.99.0): dependencies: '@next/env': 15.5.21 '@swc/helpers': 0.5.15 @@ -21749,6 +22751,7 @@ snapshots: '@next/swc-linux-x64-musl': 15.5.21 '@next/swc-win32-arm64-msvc': 15.5.21 '@next/swc-win32-x64-msvc': 15.5.21 + '@playwright/test': 1.62.0 sass: 1.99.0 sharp: 0.34.5 transitivePeerDependencies: @@ -22016,6 +23019,10 @@ snapshots: nofilter@3.1.0: {} + nopt@7.2.1: + dependencies: + abbrev: 2.0.0 + nopt@8.1.0: dependencies: abbrev: 3.0.1 @@ -22063,20 +23070,20 @@ snapshots: dependencies: boolbase: 2.0.0 - nuxt-csurf@1.6.5(magicast@0.5.3): + nuxt-csurf@1.6.5(magicast@0.5.2): dependencies: - '@nuxt/kit': 3.21.7(magicast@0.5.3) + '@nuxt/kit': 3.21.7(magicast@0.5.2) defu: 6.1.7 uncsrf: 1.2.0 transitivePeerDependencies: - magicast - nuxt-security@2.2.0(magicast@0.5.3)(rollup@4.62.3): + nuxt-security@2.2.0(magicast@0.5.2)(rollup@4.62.3): dependencies: - '@nuxt/kit': 3.21.2(magicast@0.5.3) + '@nuxt/kit': 3.21.2(magicast@0.5.2) basic-auth: 2.0.1 defu: 6.1.7 - nuxt-csurf: 1.6.5(magicast@0.5.3) + nuxt-csurf: 1.6.5(magicast@0.5.2) pathe: 1.1.2 unplugin-remove: 1.0.3(rollup@4.62.3) xss: 1.0.15 @@ -22085,19 +23092,19 @@ snapshots: - rollup - supports-color - nuxt@3.21.7(@electric-sql/pglite@0.4.1)(@parcel/watcher@2.5.6)(@types/node@22.19.17)(@vue/compiler-sfc@3.5.40)(cac@6.7.14)(db0@0.3.4(@electric-sql/pglite@0.4.1)(mysql2@3.15.3))(esbuild@0.28.0)(eslint@9.39.4(jiti@2.7.0))(ioredis@5.10.1)(lightningcss@1.33.0)(magicast@0.5.3)(mysql2@3.15.3)(optionator@0.9.4)(rolldown@1.1.5)(rollup-plugin-visualizer@7.0.1(rolldown@1.1.5)(rollup@4.62.3))(rollup@4.62.3)(sass@1.99.0)(terser@5.47.1)(typescript@5.9.3)(vite@8.1.5(@types/node@22.19.17)(esbuild@0.28.0)(jiti@2.7.0)(sass@1.99.0)(terser@5.47.1)(yaml@2.9.0))(vue-tsc@3.2.6(typescript@5.9.3))(yaml@2.9.0): + nuxt@3.21.7(@electric-sql/pglite@0.4.1)(@parcel/watcher@2.5.6)(@types/node@22.19.17)(@vue/compiler-sfc@3.5.40)(cac@6.7.14)(db0@0.3.4(@electric-sql/pglite@0.4.1)(mysql2@3.15.3))(esbuild@0.28.0)(eslint@9.39.4(jiti@2.7.0))(ioredis@5.10.1)(lightningcss@1.33.0)(magicast@0.5.2)(mysql2@3.15.3)(optionator@0.9.4)(rolldown@1.1.5)(rollup-plugin-visualizer@7.0.1(rolldown@1.1.5)(rollup@4.62.3))(rollup@4.62.3)(sass@1.99.0)(terser@5.47.1)(typescript@5.9.3)(vite@8.1.5(@types/node@22.19.17)(esbuild@0.28.0)(jiti@2.7.0)(sass@1.99.0)(terser@5.47.1)(yaml@2.9.0))(vue-tsc@3.2.6(typescript@5.9.3))(yaml@2.9.0): dependencies: - '@dxup/nuxt': 0.4.1(magic-string@0.30.21)(magicast@0.5.3)(oxc-parser@0.132.0)(rolldown@1.1.5)(typescript@5.9.3)(unplugin@3.3.0(esbuild@0.28.0)(rolldown@1.1.5)(rollup@4.62.3)(vite@8.1.5(@types/node@22.19.17)(esbuild@0.28.0)(jiti@2.7.0)(sass@1.99.0)(terser@5.47.1)(yaml@2.9.0))) - '@nuxt/cli': 3.37.0(@nuxt/schema@3.21.7)(@parcel/watcher@2.5.6)(cac@6.7.14)(magicast@0.5.3) + '@dxup/nuxt': 0.4.1(magic-string@0.30.21)(magicast@0.5.2)(oxc-parser@0.132.0)(rolldown@1.1.5)(typescript@5.9.3)(unplugin@3.3.0(esbuild@0.28.0)(rolldown@1.1.5)(rollup@4.62.3)(vite@8.1.5(@types/node@22.19.17)(esbuild@0.28.0)(jiti@2.7.0)(sass@1.99.0)(terser@5.47.1)(yaml@2.9.0))) + '@nuxt/cli': 3.37.0(@nuxt/schema@3.21.7)(@parcel/watcher@2.5.6)(cac@6.7.14)(magicast@0.5.2) '@nuxt/devtools': 3.4.0(db0@0.3.4(@electric-sql/pglite@0.4.1)(mysql2@3.15.3))(ioredis@5.10.1)(magic-string@0.30.21)(oxc-parser@0.132.0)(rolldown@1.1.5)(unplugin@3.3.0(esbuild@0.28.0)(rolldown@1.1.5)(rollup@4.62.3)(vite@8.1.5(@types/node@22.19.17)(esbuild@0.28.0)(jiti@2.7.0)(sass@1.99.0)(terser@5.47.1)(yaml@2.9.0)))(vite@8.1.5(@types/node@22.19.17)(esbuild@0.28.0)(jiti@2.7.0)(sass@1.99.0)(terser@5.47.1)(yaml@2.9.0))(vue@3.5.40(typescript@5.9.3)) - '@nuxt/kit': 3.21.7(magicast@0.5.3) - '@nuxt/nitro-server': 3.21.7(@electric-sql/pglite@0.4.1)(db0@0.3.4(@electric-sql/pglite@0.4.1)(mysql2@3.15.3))(esbuild@0.28.0)(ioredis@5.10.1)(magicast@0.5.3)(mysql2@3.15.3)(nuxt@3.21.7(@electric-sql/pglite@0.4.1)(@parcel/watcher@2.5.6)(@types/node@22.19.17)(@vue/compiler-sfc@3.5.40)(cac@6.7.14)(db0@0.3.4(@electric-sql/pglite@0.4.1)(mysql2@3.15.3))(esbuild@0.28.0)(eslint@9.39.4(jiti@2.7.0))(ioredis@5.10.1)(lightningcss@1.33.0)(magicast@0.5.3)(mysql2@3.15.3)(optionator@0.9.4)(rolldown@1.1.5)(rollup-plugin-visualizer@7.0.1(rolldown@1.1.5)(rollup@4.62.3))(rollup@4.62.3)(sass@1.99.0)(terser@5.47.1)(typescript@5.9.3)(vite@8.1.5(@types/node@22.19.17)(esbuild@0.28.0)(jiti@2.7.0)(sass@1.99.0)(terser@5.47.1)(yaml@2.9.0))(vue-tsc@3.2.6(typescript@5.9.3))(yaml@2.9.0))(oxc-parser@0.132.0)(rolldown@1.1.5)(rollup@4.62.3)(typescript@5.9.3)(vite@8.1.5(@types/node@22.19.17)(esbuild@0.28.0)(jiti@2.7.0)(sass@1.99.0)(terser@5.47.1)(yaml@2.9.0)) + '@nuxt/kit': 3.21.7(magicast@0.5.2) + '@nuxt/nitro-server': 3.21.7(@electric-sql/pglite@0.4.1)(db0@0.3.4(@electric-sql/pglite@0.4.1)(mysql2@3.15.3))(esbuild@0.28.0)(ioredis@5.10.1)(magicast@0.5.2)(mysql2@3.15.3)(nuxt@3.21.7(@electric-sql/pglite@0.4.1)(@parcel/watcher@2.5.6)(@types/node@22.19.17)(@vue/compiler-sfc@3.5.40)(cac@6.7.14)(db0@0.3.4(@electric-sql/pglite@0.4.1)(mysql2@3.15.3))(esbuild@0.28.0)(eslint@9.39.4(jiti@2.7.0))(ioredis@5.10.1)(lightningcss@1.33.0)(magicast@0.5.2)(mysql2@3.15.3)(optionator@0.9.4)(rolldown@1.1.5)(rollup-plugin-visualizer@7.0.1(rolldown@1.1.5)(rollup@4.62.3))(rollup@4.62.3)(sass@1.99.0)(terser@5.47.1)(typescript@5.9.3)(vite@8.1.5(@types/node@22.19.17)(esbuild@0.28.0)(jiti@2.7.0)(sass@1.99.0)(terser@5.47.1)(yaml@2.9.0))(vue-tsc@3.2.6(typescript@5.9.3))(yaml@2.9.0))(oxc-parser@0.132.0)(rolldown@1.1.5)(rollup@4.62.3)(typescript@5.9.3)(vite@8.1.5(@types/node@22.19.17)(esbuild@0.28.0)(jiti@2.7.0)(sass@1.99.0)(terser@5.47.1)(yaml@2.9.0)) '@nuxt/schema': 3.21.7 - '@nuxt/telemetry': 2.8.0(@nuxt/kit@3.21.7(magicast@0.5.3)) - '@nuxt/vite-builder': 3.21.7(@types/node@22.19.17)(eslint@9.39.4(jiti@2.7.0))(lightningcss@1.33.0)(magicast@0.5.3)(nuxt@3.21.7(@electric-sql/pglite@0.4.1)(@parcel/watcher@2.5.6)(@types/node@22.19.17)(@vue/compiler-sfc@3.5.40)(cac@6.7.14)(db0@0.3.4(@electric-sql/pglite@0.4.1)(mysql2@3.15.3))(esbuild@0.28.0)(eslint@9.39.4(jiti@2.7.0))(ioredis@5.10.1)(lightningcss@1.33.0)(magicast@0.5.3)(mysql2@3.15.3)(optionator@0.9.4)(rolldown@1.1.5)(rollup-plugin-visualizer@7.0.1(rolldown@1.1.5)(rollup@4.62.3))(rollup@4.62.3)(sass@1.99.0)(terser@5.47.1)(typescript@5.9.3)(vite@8.1.5(@types/node@22.19.17)(esbuild@0.28.0)(jiti@2.7.0)(sass@1.99.0)(terser@5.47.1)(yaml@2.9.0))(vue-tsc@3.2.6(typescript@5.9.3))(yaml@2.9.0))(optionator@0.9.4)(rolldown@1.1.5)(rollup-plugin-visualizer@7.0.1(rolldown@1.1.5)(rollup@4.62.3))(rollup@4.62.3)(sass@1.99.0)(terser@5.47.1)(typescript@5.9.3)(vue-tsc@3.2.6(typescript@5.9.3))(vue@3.5.40(typescript@5.9.3))(yaml@2.9.0) + '@nuxt/telemetry': 2.8.0(@nuxt/kit@3.21.7(magicast@0.5.2)) + '@nuxt/vite-builder': 3.21.7(@types/node@22.19.17)(eslint@9.39.4(jiti@2.7.0))(lightningcss@1.33.0)(magicast@0.5.2)(nuxt@3.21.7(@electric-sql/pglite@0.4.1)(@parcel/watcher@2.5.6)(@types/node@22.19.17)(@vue/compiler-sfc@3.5.40)(cac@6.7.14)(db0@0.3.4(@electric-sql/pglite@0.4.1)(mysql2@3.15.3))(esbuild@0.28.0)(eslint@9.39.4(jiti@2.7.0))(ioredis@5.10.1)(lightningcss@1.33.0)(magicast@0.5.2)(mysql2@3.15.3)(optionator@0.9.4)(rolldown@1.1.5)(rollup-plugin-visualizer@7.0.1(rolldown@1.1.5)(rollup@4.62.3))(rollup@4.62.3)(sass@1.99.0)(terser@5.47.1)(typescript@5.9.3)(vite@8.1.5(@types/node@22.19.17)(esbuild@0.28.0)(jiti@2.7.0)(sass@1.99.0)(terser@5.47.1)(yaml@2.9.0))(vue-tsc@3.2.6(typescript@5.9.3))(yaml@2.9.0))(optionator@0.9.4)(rolldown@1.1.5)(rollup-plugin-visualizer@7.0.1(rolldown@1.1.5)(rollup@4.62.3))(rollup@4.62.3)(sass@1.99.0)(terser@5.47.1)(typescript@5.9.3)(vue-tsc@3.2.6(typescript@5.9.3))(vue@3.5.40(typescript@5.9.3))(yaml@2.9.0) '@unhead/vue': 2.1.16(vue@3.5.40(typescript@5.9.3)) '@vue/shared': 3.5.40 - c12: 3.3.4(magicast@0.5.3) + c12: 3.3.4(magicast@0.5.2) chokidar: 5.0.0 compatx: 0.2.0 consola: 3.4.2 @@ -22491,6 +23498,8 @@ snapshots: otp-io@1.2.7: {} + outvariant@1.4.3: {} + ow@0.17.0: dependencies: type-fest: 0.11.0 @@ -22806,6 +23815,8 @@ snapshots: lru-cache: 11.3.6 minipass: 7.1.3 + path-to-regexp@6.3.0: {} + path-type@1.1.0: dependencies: graceful-fs: 4.2.11 @@ -22943,6 +23954,14 @@ snapshots: exsolve: 1.1.1 pathe: 2.0.3 + playwright-core@1.62.0: {} + + playwright@1.62.0: + dependencies: + playwright-core: 1.62.0 + optionalDependencies: + fsevents: 2.3.2 + pluralize@8.0.0: {} png2icons@2.0.1: {} @@ -23204,9 +24223,9 @@ snapshots: pretty-bytes@7.1.0: {} - prisma@7.7.0(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(magicast@0.5.3)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(typescript@5.9.3): + prisma@7.7.0(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(magicast@0.5.2)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(typescript@5.9.3): dependencies: - '@prisma/config': 7.7.0(magicast@0.5.3) + '@prisma/config': 7.7.0(magicast@0.5.2) '@prisma/dev': 0.24.3(typescript@5.9.3) '@prisma/engines': 7.7.0 '@prisma/studio-core': 0.27.3(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) @@ -23264,6 +24283,8 @@ snapshots: pure-rand@6.1.0: {} + pure-rand@8.4.2: {} + pvtsutils@1.3.6: dependencies: tslib: 2.8.1 @@ -23663,6 +24684,8 @@ snapshots: tunnel-agent: 0.6.0 uuid: 3.4.0 + require-directory@2.1.1: {} + require-from-string@2.0.2: {} requires-port@1.0.0: {} @@ -23731,6 +24754,8 @@ snapshots: retry@0.12.0: {} + rettime@0.11.11: {} + reusify@1.1.0: {} rimraf@2.7.1: @@ -23997,6 +25022,8 @@ snapshots: transitivePeerDependencies: - supports-color + set-cookie-parser@3.1.2: {} + set-function-length@1.2.2: dependencies: define-data-property: 1.1.4 @@ -24158,6 +25185,8 @@ snapshots: side-channel-map: 1.0.1 side-channel-weakmap: 1.0.2 + siginfo@2.0.0: {} + signal-exit@3.0.7: {} signal-exit@4.1.0: {} @@ -24293,6 +25322,8 @@ snapshots: stable@0.1.8: {} + stackback@0.0.2: {} + standard-as-callback@2.1.0: {} starlight-image-zoom@0.14.2(@astrojs/starlight@0.40.0(astro@7.1.1(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)(@types/node@24.12.2)(db0@0.3.4(@electric-sql/pglite@0.4.1)(mysql2@3.15.3))(ioredis@5.10.1)(jiti@2.7.0)(rollup@4.62.3)(sass@1.99.0)(terser@5.47.1)(yaml@2.9.0))(typescript@5.9.3)): @@ -24366,6 +25397,10 @@ snapshots: - bare-abort-controller - react-native-b4a + strict-event-emitter@0.5.1: {} + + string-argv@0.3.2: {} + string-width@4.2.3: dependencies: emoji-regex: 8.0.0 @@ -24752,6 +25787,8 @@ snapshots: tiny-invariant@1.3.3: {} + tinybench@2.9.0: {} + tinyclip@0.1.15: {} tinyexec@1.2.4: {} @@ -24766,6 +25803,8 @@ snapshots: fdir: 6.5.0(picomatch@4.0.5) picomatch: 4.0.5 + tinyrainbow@3.1.1: {} + tldts-core@7.0.28: {} tldts@7.0.28: @@ -24841,6 +25880,11 @@ snapshots: tslib@2.8.1: {} + tsutils@3.21.0(typescript@5.9.3): + dependencies: + tslib: 1.14.1 + typescript: 5.9.3 + tsyringe@4.10.0: dependencies: tslib: 1.14.1 @@ -24863,6 +25907,23 @@ snapshots: dependencies: prelude-ls: 1.2.1 + type-coverage-core@2.30.1(typescript@5.9.3): + dependencies: + fast-glob: 3.3.3 + minimatch: 10.2.6 + normalize-path: 3.0.0 + tslib: 2.8.1 + tsutils: 3.21.0(typescript@5.9.3) + typescript: 5.9.3 + + type-coverage@2.30.1(typescript@5.9.3): + dependencies: + chalk: 4.1.2 + minimist: 1.2.8 + type-coverage-core: 2.30.1(typescript@5.9.3) + transitivePeerDependencies: + - typescript + type-fest@0.11.0: {} type-fest@0.21.3: {} @@ -24965,6 +26026,12 @@ snapshots: rolldown: 1.1.5 unplugin: 3.3.0(esbuild@0.28.1)(rolldown@1.1.5)(rollup@4.62.3)(vite@8.1.5(@types/node@24.12.2)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.99.0)(terser@5.47.1)(yaml@2.9.0)) + unctx@3.0.0(magic-string@1.1.0)(rolldown@1.1.5)(unplugin@3.3.0(esbuild@0.28.1)(rolldown@1.1.5)(rollup@4.62.3)(vite@8.1.5(@types/node@24.12.2)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.99.0)(terser@5.47.1)(yaml@2.9.0))): + optionalDependencies: + magic-string: 1.1.0 + rolldown: 1.1.5 + unplugin: 3.3.0(esbuild@0.28.1)(rolldown@1.1.5)(rollup@4.62.3)(vite@8.1.5(@types/node@24.12.2)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.99.0)(terser@5.47.1)(yaml@2.9.0)) + undici-types@6.21.0: {} undici-types@7.16.0: {} @@ -25233,10 +26300,10 @@ snapshots: pathe: 2.0.3 picomatch: 4.0.5 - unplugin-vue-router@0.12.0(vue-router@4.6.4(vue@3.5.40(typescript@5.9.3)))(vue@3.5.40(typescript@5.9.3)): + unplugin-vue-router@0.12.0(vue-router@4.5.1(vue@3.5.17(typescript@5.9.3)))(vue@3.5.17(typescript@5.9.3)): dependencies: '@babel/types': 7.29.0 - '@vue-macros/common': 1.16.1(vue@3.5.40(typescript@5.9.3)) + '@vue-macros/common': 1.16.1(vue@3.5.17(typescript@5.9.3)) ast-walker-scope: 0.6.2 chokidar: 4.0.3 fast-glob: 3.3.3 @@ -25251,7 +26318,7 @@ snapshots: unplugin-utils: 0.2.5 yaml: 2.8.3 optionalDependencies: - vue-router: 4.6.4(vue@3.5.40(typescript@5.9.3)) + vue-router: 4.5.1(vue@3.5.17(typescript@5.9.3)) transitivePeerDependencies: - vue @@ -25403,6 +26470,8 @@ snapshots: db0: 0.3.4(@electric-sql/pglite@0.4.1)(mysql2@3.15.3) ioredis: 5.10.1 + until-async@3.0.2: {} + untun@0.1.3: dependencies: citty: 0.1.6 @@ -25739,12 +26808,70 @@ snapshots: optionalDependencies: vite: 8.1.5(@types/node@24.12.2)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.99.0)(terser@5.47.1)(yaml@2.9.0) + vitest-environment-nuxt@2.0.0(@playwright/test@1.62.0)(@vue/test-utils@2.4.11(@vue/compiler-dom@3.5.40)(@vue/server-renderer@3.5.40)(vue@3.5.17(typescript@5.9.3)))(esbuild@0.28.0)(happy-dom@20.11.1)(magicast@0.5.2)(playwright-core@1.62.0)(rolldown@1.1.5)(rollup@4.62.3)(typescript@5.9.3)(vite@8.1.5(@types/node@22.19.17)(esbuild@0.28.0)(jiti@2.7.0)(sass@1.99.0)(terser@5.47.1)(yaml@2.9.0))(vitest@4.1.10): + dependencies: + '@nuxt/test-utils': 4.1.0(@playwright/test@1.62.0)(@vue/test-utils@2.4.11(@vue/compiler-dom@3.5.40)(@vue/server-renderer@3.5.40)(vue@3.5.17(typescript@5.9.3)))(esbuild@0.28.0)(happy-dom@20.11.1)(magicast@0.5.2)(playwright-core@1.62.0)(rolldown@1.1.5)(rollup@4.62.3)(typescript@5.9.3)(vite@8.1.5(@types/node@22.19.17)(esbuild@0.28.0)(jiti@2.7.0)(sass@1.99.0)(terser@5.47.1)(yaml@2.9.0))(vitest@4.1.10) + transitivePeerDependencies: + - '@cucumber/cucumber' + - '@farmfe/core' + - '@jest/globals' + - '@playwright/test' + - '@rspack/core' + - '@testing-library/vue' + - '@vitest/ui' + - '@vue/test-utils' + - bun-types-no-globals + - esbuild + - h3-next + - happy-dom + - jsdom + - magicast + - playwright-core + - rolldown + - rollup + - typescript + - unloader + - vite + - vitest + - webpack + + vitest@4.1.10(@types/node@22.19.17)(@vitest/coverage-v8@4.1.10)(happy-dom@20.11.1)(msw@2.15.0(@types/node@22.19.17)(typescript@5.9.3))(vite@8.1.5(@types/node@22.19.17)(esbuild@0.28.0)(jiti@2.7.0)(sass@1.99.0)(terser@5.47.1)(yaml@2.9.0)): + dependencies: + '@vitest/expect': 4.1.10 + '@vitest/mocker': 4.1.10(msw@2.15.0(@types/node@22.19.17)(typescript@5.9.3))(vite@8.1.5(@types/node@22.19.17)(esbuild@0.28.0)(jiti@2.7.0)(sass@1.99.0)(terser@5.47.1)(yaml@2.9.0)) + '@vitest/pretty-format': 4.1.10 + '@vitest/runner': 4.1.10 + '@vitest/snapshot': 4.1.10 + '@vitest/spy': 4.1.10 + '@vitest/utils': 4.1.10 + es-module-lexer: 2.3.1 + expect-type: 1.4.0 + magic-string: 0.30.21 + obug: 2.1.4 + pathe: 2.0.3 + picomatch: 4.0.5 + std-env: 4.2.0 + tinybench: 2.9.0 + tinyexec: 1.2.4 + tinyglobby: 0.2.17 + tinyrainbow: 3.1.1 + vite: 8.1.5(@types/node@22.19.17)(esbuild@0.28.0)(jiti@2.7.0)(sass@1.99.0)(terser@5.47.1)(yaml@2.9.0) + why-is-node-running: 2.3.0 + optionalDependencies: + '@types/node': 22.19.17 + '@vitest/coverage-v8': 4.1.10(vitest@4.1.10) + happy-dom: 20.11.1 + transitivePeerDependencies: + - msw + vscode-uri@3.1.0: {} vue-bundle-renderer@2.3.1: dependencies: ufo: 1.6.4 + vue-component-type-helpers@3.3.8: {} + vue-devtools-stub@0.1.0: {} vue-eslint-parser@10.4.0(eslint@9.39.4(jiti@2.7.0)): @@ -25771,51 +26898,22 @@ snapshots: transitivePeerDependencies: - supports-color - vue-i18n@10.0.8(vue@3.5.40(typescript@5.9.3)): + vue-i18n@10.0.8(vue@3.5.17(typescript@5.9.3)): dependencies: '@intlify/core-base': 10.0.8 '@intlify/shared': 10.0.8 '@vue/devtools-api': 6.6.4 - vue: 3.5.40(typescript@5.9.3) + vue: 3.5.17(typescript@5.9.3) - vue-router@4.6.4(vue@3.5.40(typescript@5.9.3)): + vue-router@4.5.1(vue@3.5.17(typescript@5.9.3)): dependencies: '@vue/devtools-api': 6.6.4 - vue: 3.5.40(typescript@5.9.3) + vue: 3.5.17(typescript@5.9.3) - vue-router@5.2.0(@vue/compiler-sfc@3.5.40)(esbuild@0.28.0)(rolldown@1.1.5)(rollup@4.62.3)(vite@8.1.5(@types/node@22.19.17)(esbuild@0.28.0)(jiti@2.7.0)(sass@1.99.0)(terser@5.47.1)(yaml@2.9.0))(vue@3.5.40(typescript@5.9.3)): + vue-router@4.6.4(vue@3.5.40(typescript@5.9.3)): dependencies: - '@babel/generator': 8.0.0 - '@vue-macros/common': 3.1.4(vue@3.5.40(typescript@5.9.3)) - '@vue/devtools-api': 8.2.1 - ast-walker-scope: 0.9.0 - chokidar: 5.0.0 - json5: 2.2.3 - local-pkg: 1.2.1 - magic-string: 0.30.21 - mlly: 1.8.2 - muggle-string: 0.4.1 - nostics: 1.2.0 - pathe: 2.0.3 - picomatch: 4.0.5 - scule: 1.3.0 - tinyglobby: 0.2.17 - unplugin: 3.3.0(esbuild@0.28.0)(rolldown@1.1.5)(rollup@4.62.3)(vite@8.1.5(@types/node@22.19.17)(esbuild@0.28.0)(jiti@2.7.0)(sass@1.99.0)(terser@5.47.1)(yaml@2.9.0)) - unplugin-utils: 0.3.2 + '@vue/devtools-api': 6.6.4 vue: 3.5.40(typescript@5.9.3) - yaml: 2.9.0 - optionalDependencies: - '@vue/compiler-sfc': 3.5.40 - vite: 8.1.5(@types/node@22.19.17)(esbuild@0.28.0)(jiti@2.7.0)(sass@1.99.0)(terser@5.47.1)(yaml@2.9.0) - transitivePeerDependencies: - - '@farmfe/core' - - '@rspack/core' - - bun-types-no-globals - - esbuild - - rolldown - - rollup - - unloader - - webpack vue-tsc@3.2.6(typescript@5.9.3): dependencies: @@ -25823,21 +26921,31 @@ snapshots: '@vue/language-core': 3.2.6 typescript: 5.9.3 - vue3-carousel-nuxt@1.1.6(magicast@0.5.3)(vue@3.5.40(typescript@5.9.3)): + vue3-carousel-nuxt@1.1.6(magicast@0.5.2)(vue@3.5.17(typescript@5.9.3)): dependencies: - '@nuxt/kit': 3.21.2(magicast@0.5.3) - vue3-carousel: 0.15.1(vue@3.5.40(typescript@5.9.3)) + '@nuxt/kit': 3.21.2(magicast@0.5.2) + vue3-carousel: 0.15.1(vue@3.5.17(typescript@5.9.3)) transitivePeerDependencies: - magicast - vue - vue3-carousel@0.15.1(vue@3.5.40(typescript@5.9.3)): + vue3-carousel@0.15.1(vue@3.5.17(typescript@5.9.3)): dependencies: - vue: 3.5.40(typescript@5.9.3) + vue: 3.5.17(typescript@5.9.3) - vue3-carousel@0.16.0(vue@3.5.40(typescript@5.9.3)): + vue3-carousel@0.16.0(vue@3.5.17(typescript@5.9.3)): dependencies: - vue: 3.5.40(typescript@5.9.3) + vue: 3.5.17(typescript@5.9.3) + + vue@3.5.17(typescript@5.9.3): + dependencies: + '@vue/compiler-dom': 3.5.17 + '@vue/compiler-sfc': 3.5.17 + '@vue/runtime-dom': 3.5.17 + '@vue/server-renderer': 3.5.17(vue@3.5.17(typescript@5.9.3)) + '@vue/shared': 3.5.17 + optionalDependencies: + typescript: 5.9.3 vue@3.5.40(typescript@5.9.3): dependencies: @@ -25849,10 +26957,10 @@ snapshots: optionalDependencies: typescript: 5.9.3 - vuedraggable@4.1.0(vue@3.5.40(typescript@5.9.3)): + vuedraggable@4.1.0(vue@3.5.17(typescript@5.9.3)): dependencies: sortablejs: 1.14.0 - vue: 3.5.40(typescript@5.9.3) + vue: 3.5.17(typescript@5.9.3) w3c-hr-time@1.0.2: dependencies: @@ -25889,6 +26997,8 @@ snapshots: whatwg-mimetype@2.3.0: {} + whatwg-mimetype@3.0.0: {} + whatwg-mimetype@4.0.0: {} whatwg-url@5.0.0: @@ -25955,6 +27065,11 @@ snapshots: dependencies: isexe: 4.0.0 + why-is-node-running@2.3.0: + dependencies: + siginfo: 2.0.0 + stackback: 0.0.2 + widest-line@3.1.0: dependencies: string-width: 4.2.3 @@ -26045,8 +27160,20 @@ snapshots: yaml@2.9.0: {} + yargs-parser@21.1.1: {} + yargs-parser@22.0.0: {} + yargs@17.7.3: + dependencies: + cliui: 8.0.1 + escalade: 3.2.0 + get-caller-file: 2.0.5 + require-directory: 2.1.1 + string-width: 4.2.3 + y18n: 5.0.8 + yargs-parser: 21.1.1 + yargs@18.0.0: dependencies: cliui: 9.0.1 diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 209ed10c..bbdf03c1 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -70,3 +70,6 @@ overrides: trim-newlines@<3.0.1: ">=3.0.1" shamefullyHoist: true + +patchedDependencies: + decompress@4.2.1: patches/decompress@4.2.1.patch diff --git a/scripts/check-new-vulns.cjs b/scripts/check-new-vulns.cjs new file mode 100644 index 00000000..dd33c7a2 --- /dev/null +++ b/scripts/check-new-vulns.cjs @@ -0,0 +1,232 @@ +#!/usr/bin/env node +// Detect NEW (un-accepted) advisories in pnpm/cargo audit JSON output, +// comparing against entries in security/risk-register.yaml. +// +// Behavior: +// - Missing or unparseable audit JSON → exit 0 (assume tool flaked, do +// not fail the workflow on infra noise). A noisy log line is emitted. +// - Risk register missing or unparseable → exit 0 (same reason). This +// avoids the failure mode where an empty `known` set causes every +// advisory to be flagged as new. +// - New (un-registered) advisory found → exit 1, log each one. +// - All advisories in register or no advisories → exit 0. +// +// Usage: +// check-new-vulns.cjs --format pnpm --json /tmp/audit.json +// check-new-vulns.cjs --format cargo --json /tmp/cargo-audit.json +// +// Flags: +// --format {pnpm|cargo} audit JSON shape to parse +// --json path to audit JSON output +// --register path to risk-register.yaml (default: security/risk-register.yaml) +// --ignored comma-separated advisory IDs to ignore unconditionally +// (e.g. for pnpm audit --ignore GHSA-...) +// --min-severity minimum severity to report (pnpm: critical|high|moderate|low; +// cargo: critical|high|medium|low|informational). Default: critical. + +"use strict"; + +const fs = require("node:fs"); +const path = require("node:path"); + +const HELP_TEXT = + "Usage: check-new-vulns.cjs --format {pnpm|cargo} --json " + + "[--register ] [--ignored ] [--min-severity ]\n\n" + + "Compares pnpm/cargo audit JSON output against entries in\n" + + "security/risk-register.yaml and exits 1 only when a new (un-accepted)\n" + + "advisory is detected. Missing/empty/malformed audit JSON or register\n" + + "exits 0 (treated as infra noise)."; + +// fallow-ignore-next-line complexity +function parseArgs(argv) { + const args = {}; + // Reject values that look like another flag (start with `--`) or are + // missing entirely. Catches typos like `--format --json file.json` where + // `--json` would be silently consumed as the format value. + const nextArg = (i, flagName) => { + if (i >= argv.length) { + console.error(`[check-new-vulns] missing value for ${flagName}`); + process.exit(2); + } + const v = argv[i]; + if (v.startsWith("--")) { + console.error( + `[check-new-vulns] ${flagName} requires a value (got another flag '${v}')`, + ); + process.exit(2); + } + return v; + }; + for (let i = 0; i < argv.length; i++) { + const a = argv[i]; + if (a === "--format") args.format = nextArg(++i, "--format"); + else if (a === "--json") args.json = nextArg(++i, "--json"); + else if (a === "--register") args.register = nextArg(++i, "--register"); + else if (a === "--ignored") { + const val = nextArg(++i, "--ignored"); + args.ignored = val + .split(",") + .map((s) => s.trim()) + .filter(Boolean); + } else if (a === "--min-severity") + args.minSeverity = nextArg(++i, "--min-severity"); + else if (a === "--help" || a === "-h") { + console.log(HELP_TEXT); + process.exit(0); + } + } + return args; +} + +function readJson(path) { + try { + if (!fs.existsSync(path)) return { ok: false, reason: "file missing" }; + const raw = fs.readFileSync(path, "utf8").trim(); + if (!raw) return { ok: false, reason: "file empty" }; + return { ok: true, data: JSON.parse(raw) }; + } catch (e) { + return { ok: false, reason: e.message }; + } +} + +// Parse security/risk-register.yaml by line-scanning for `advisory:` fields. +// We deliberately avoid a full YAML parser (no extra deps in CI). Format is +// stable: each entry has `advisory: GHSA-...` or `advisory: RUSTSEC-...` on +// its own line. Comment lines and unrelated fields are ignored. +// +// Returns { known, loaded } where `loaded` is false when the file is +// missing OR unparseable. A loaded-but-empty-known (file exists but no +// `advisory:` entries matched) is treated as loaded: true — the caller can +// still proceed with an empty known set and will correctly flag every +// advisory as new. +function readKnownAdvisories(path) { + let known = new Set(); + let loaded = false; + try { + if (!fs.existsSync(path)) return { known, loaded: false }; + const text = fs.readFileSync(path, "utf8"); + // Lenient: ignore any trailing content (e.g. inline `# comment`). + // Avoids super-linear backtracking and future-proofs against trailing comments. + const re = /^\s*advisory:\s*(\S+)/gm; + let m; + while ((m = re.exec(text)) !== null) { + known.add(m[1]); + } + loaded = true; + } catch (e) { + return { known: new Set(), loaded: false }; + } + return { known, loaded }; +} + +function severityRank(s) { + // Unknown / missing severity is treated as critical (highest rank) so + // we never silently filter out a vulnerability because its severity + // string was unrecognized or absent — false negatives are worse than + // false positives here. + return ( + { critical: 4, high: 3, moderate: 2, medium: 2, low: 1, informational: 0 }[ + (s || "").toLowerCase() + ] ?? 4 + ); +} + +function extractPnpm(data, minSeverity) { + const advisories = data.advisories ? Object.values(data.advisories) : []; + const minRank = severityRank(minSeverity); + return advisories + .filter((a) => severityRank(a.severity) >= minRank) + .map((a) => ({ + id: a.github_advisory_id, + module: a.module_name, + severity: a.severity, + title: a.title, + })); +} + +function extractCargo(data, minSeverity) { + const vulns = (data.vulnerabilities && data.vulnerabilities.list) || []; + const minRank = severityRank(minSeverity); + return ( + vulns + .filter((v) => v.advisory && severityRank(v.advisory.severity) >= minRank) + // fallow-ignore-next-line complexity + .map((v) => ({ + id: v.advisory?.id ?? "unknown", + module: v.package?.name ?? "unknown", + severity: v.advisory?.severity ?? "unknown", + title: v.advisory?.title ?? "unknown", + })) + ); +} + +// fallow-ignore-next-line complexity +function main() { + const args = parseArgs(process.argv.slice(2)); + if (!args.format || !args.json) { + console.error( + "Usage: check-new-vulns.cjs --format {pnpm|cargo} --json [--register ] [--ignored ] [--min-severity ]", + ); + process.exit(2); + } + if (!["pnpm", "cargo"].includes(args.format)) { + console.error(`Unsupported --format: ${args.format}`); + process.exit(2); + } + + // Default register path: GITHUB_WORKSPACE (repo root) when available, + // falling back to process.cwd()/security/. Composite actions like + // rust-ci set working-directory to a sub-crate (cli/, desktop/src-tauri/), + // so process.cwd() alone would silently miss the register and flag every + // advisory as new. + const registerPath = + args.register || + path.join( + process.env.GITHUB_WORKSPACE || process.cwd(), + "security", + "risk-register.yaml", + ); + const ignored = new Set(args.ignored || []); + + const loaded = readJson(args.json); + if (!loaded.ok) { + console.warn( + `[check-new-vulns] ${args.format} audit JSON unavailable (${loaded.reason}); treating as no advisories.`, + ); + process.exit(0); + } + + const minSeverity = args.minSeverity || "critical"; + const all = + args.format === "pnpm" + ? extractPnpm(loaded.data, minSeverity) + : extractCargo(loaded.data, minSeverity); + + // Distinguish "register loaded but empty" from "register could not be + // loaded" so we don't flag every advisory as new when the file is + // genuinely missing or unparseable (infra noise). + const { known, loaded: registerAvailable } = + readKnownAdvisories(registerPath); + + if (!registerAvailable) { + console.warn( + `[check-new-vulns] risk register unavailable at ${registerPath}; treating as no known advisories (infra noise, not a failure).`, + ); + process.exit(0); + } + + const newOnes = all.filter((a) => !ignored.has(a.id) && !known.has(a.id)); + + if (newOnes.length > 0) { + for (const a of newOnes) { + console.error( + `NEW ${args.format.toUpperCase()} ADVISORY: ${a.id} ${a.module} (${a.severity}) — ${a.title}`, + ); + } + process.exit(1); + } + + process.exit(0); +} + +main(); diff --git a/scripts/codecov-pr-comment.sh b/scripts/codecov-pr-comment.sh new file mode 100644 index 00000000..051b7611 --- /dev/null +++ b/scripts/codecov-pr-comment.sh @@ -0,0 +1,236 @@ +#!/usr/bin/env bash +# ============================================================================== +# codecov-pr-comment.sh — Post Codecov coverage gaps as PR comment +# ============================================================================== +# +# Queries Codecov's compare API for per-file, per-line coverage on a PR, +# groups uncovered new lines by file, and posts a structured markdown comment. +# +# Usage: +# ./scripts/codecov-pr-comment.sh +# +# Environment: +# CODECOV_TOKEN Required. Codecov API token (read-only scope) +# GH_TOKEN GitHub API token (falls back to GITHUB_TOKEN) +# GITHUB_REPOSITORY GitHub repo (default: BillyOutlast/drop) +# GITHUB_PR_NUMBER PR number (auto-detected from GitHub context) +# MAX_FILES Max files to report (default: 10) +# ============================================================================== + +set -euo pipefail + +# ---- Configuration ----------------------------------------------------------- + +GITHUB_REPOSITORY="${GITHUB_REPOSITORY:-BillyOutlast/drop}" +GH_TOKEN="${GH_TOKEN:-${GITHUB_TOKEN:-}}" +MAX_FILES="${MAX_FILES:-10}" + +# Auto-detect PR number from GitHub context +if [[ -z "${GITHUB_PR_NUMBER:-}" ]]; then + if [[ -n "${GITHUB_REF:-}" && "$GITHUB_REF" =~ ^refs/pull/([0-9]+)/merge$ ]]; then + GITHUB_PR_NUMBER="${BASH_REMATCH[1]}" + elif [[ -n "${GITHUB_EVENT_PULL_REQUEST_NUMBER:-}" ]]; then + GITHUB_PR_NUMBER="${GITHUB_EVENT_PULL_REQUEST_NUMBER}" + else + echo "ERROR: GITHUB_PR_NUMBER not set and cannot auto-detect" >&2 + exit 1 + fi +fi + +OWNER="${GITHUB_REPOSITORY%%/*}" +REPO="${GITHUB_REPOSITORY##*/}" + +CODE_COV_API="https://api.codecov.io/api/v2/github/${OWNER}/${REPO}/compare" +CODE_COV_APP_URL="https://app.codecov.io/gh/${GITHUB_REPOSITORY}/pull/${GITHUB_PR_NUMBER}" + +# --- Validation --------------------------------------------------------------- + +if [[ -z "${CODECOV_TOKEN:-}" ]]; then + echo "FATAL: CODECOV_TOKEN is not set" >&2 + echo "Generate a token at https://app.codecov.io/gh/${GITHUB_REPOSITORY}/settings/access" >&2 + exit 1 +fi + +if [[ -z "$GH_TOKEN" ]]; then + echo "FATAL: GH_TOKEN or GITHUB_TOKEN is not set" >&2 + exit 1 +fi + +if ! command -v jq &>/dev/null; then + echo "FATAL: jq is required but not installed" >&2 + exit 1 +fi + +if ! command -v gh &>/dev/null; then + echo "FATAL: gh (GitHub CLI) is required but not installed" >&2 + exit 1 +fi + +# --- Helpers ------------------------------------------------------------------ + +log() { echo "[$(date '+%H:%M:%S')] $*"; } + +# Compact consecutive line numbers into ranges: 1,2,3,5,7,8,9 → "1-3,5,7-9" +format_line_ranges() { + local nums="$1" + if [[ -z "$nums" ]]; then + echo "" + return + fi + + # Sort and deduplicate + local sorted + sorted=$(echo "$nums" | tr ',' '\n' | sort -n | uniq | tr '\n' ',' | sed 's/,$//') + IFS=',' read -ra arr <<< "$sorted" + + local result="" + local start="${arr[0]}" + local prev="${arr[0]}" + + for ((i=1; i < ${#arr[@]}; i++)); do + local curr="${arr[i]}" + if (( curr == prev + 1 )); then + prev="$curr" + continue + fi + if [[ "$start" == "$prev" ]]; then + result="${result}${start}," + else + result="${result}${start}-${prev}," + fi + start="$curr" + prev="$curr" + done + + # Flush last range + if [[ "$start" == "$prev" ]]; then + result="${result}${start}" + else + result="${result}${start}-${prev}" + fi + + echo "$result" +} + +# --- Step 1: Fetch Codecov comparison data ----------------------------------- + +log "Fetching Codecov comparison for PR #${GITHUB_PR_NUMBER}..." + +COMPARE_RESPONSE=$(curl -sS -f \ + -H "Authorization: Bearer ${CODECOV_TOKEN}" \ + "${CODE_COV_API}/?pullid=${GITHUB_PR_NUMBER}" 2>&1) || { + echo "WARN: Codecov API request failed: ${COMPARE_RESPONSE}" >&2 + echo "WARN: Coverage may not be uploaded yet for this PR." >&2 + echo "WARN: Try waiting 2-3 minutes for Codecov to process the upload." >&2 + exit 0 + } + +# Check for error response +if echo "$COMPARE_RESPONSE" | jq -e '.error' >/dev/null 2>&1; then + error_msg=$(echo "$COMPARE_RESPONSE" | jq -r '.error // "unknown error"') + echo "WARN: Codecov API returned error: ${error_msg}" >&2 + exit 0 +fi + +# Check if comparison is ready +STATE=$(echo "$COMPARE_RESPONSE" | jq -r '.state // "unknown"') +if [[ "$STATE" == "pending" ]]; then + echo "WARN: Codecov comparison is still processing. Try again later." >&2 + exit 0 +fi + +# --- Step 2: Parse files with uncovered new lines ----------------------------- + +log "Parsing coverage gaps..." + +# Extract files that have diff and uncovered lines +FILE_DATA=$(echo "$COMPARE_RESPONSE" | jq -c ' + [.files[]? | select(.has_diff == true) | + { + file: (.name.head // .name.base // "unknown"), + patch_coverage: (.totals.patch.coverage // 0), + patch_hits: (.totals.patch.hits // 0), + patch_misses: (.totals.patch.misses // 0), + uncovered_lines: [ + .lines[]? | + select(.added == true and (.coverage.head // 1) == 0) | + (.number.head // .number.base // 0) + ] + } | + select(.uncovered_lines | length > 0)] +' 2>/dev/null || echo "[]") + +FILE_COUNT=$(echo "$FILE_DATA" | jq 'length') +log "Found ${FILE_COUNT} changed files with uncovered new lines" + +# --- Step 3: Get aggregate patch totals --------------------------------------- + +PATCH_TOTALS=$(echo "$COMPARE_RESPONSE" | jq '{ + coverage: (.totals.patch.coverage // 0), + hits: (.totals.patch.hits // 0), + misses: (.totals.patch.misses // 0), + lines: (.totals.patch.lines // 0) +}') +PATCH_COV=$(echo "$PATCH_TOTALS" | jq -r '.coverage') +PATCH_HITS=$(echo "$PATCH_TOTALS" | jq -r '.hits') +PATCH_MISSES=$(echo "$PATCH_TOTALS" | jq -r '.misses') +PATCH_LINES=$(echo "$PATCH_TOTALS" | jq -r '.lines') + +# --- Step 4: Build PR comment ------------------------------------------------- + +log "Building PR comment..." + +COMMENT_BODY="## 📊 Codecov Coverage\n\n" + +# Summary line +if [[ "$PATCH_LINES" -gt 0 ]]; then + COMMENT_BODY+="**Patch coverage**: ${PATCH_COV}% (${PATCH_HITS} hits / ${PATCH_MISSES} misses / ${PATCH_LINES} lines)\n\n" +elif [[ "$FILE_COUNT" -eq 0 ]]; then + COMMENT_BODY+="✅ All new lines in changed files are covered by tests.\n" +else + COMMENT_BODY+="⚠️ Coverage data not yet available for this PR.\n" +fi + +# File breakdown +if [[ "$FILE_COUNT" -gt 0 ]]; then + COMMENT_BODY+="### Files Missing Coverage\n\n" + COMMENT_BODY+="| File | Patch Cov | Misses | Uncovered Lines |\n" + COMMENT_BODY+="|------|-----------|--------|-----------------|\n" + + # Sort by patch coverage (worst first), take top MAX_FILES + count=0 + while IFS= read -r row; do + if [[ $count -ge $MAX_FILES ]]; then + break + fi + file=$(echo "$row" | jq -r '.file') + cov=$(echo "$row" | jq -r '.patch_coverage') + misses=$(echo "$row" | jq -r '.patch_misses') + uncovered_raw=$(echo "$row" | jq -r '.uncovered_lines | join(",")') + ranges=$(format_line_ranges "$uncovered_raw") + + COMMENT_BODY+="| \`$file\` | ${cov}% | ${misses} | ${ranges} |\n" + count=$((count + 1)) + done < <(echo "$FILE_DATA" | jq -c 'sort_by(.patch_coverage) | .[]') + + if [[ "$FILE_COUNT" -gt "$MAX_FILES" ]]; then + COMMENT_BODY+="\n*... and $((FILE_COUNT - MAX_FILES)) more file(s)*\n" + fi +fi + +COMMENT_BODY+="\n---\n\n" +COMMENT_BODY+="*[Codecov Dashboard](${CODE_COV_APP_URL})* | " +COMMENT_BODY+="*Generated by \`scripts/codecov-pr-comment.sh\`*\n" + +# --- Step 5: Post comment ----------------------------------------------------- + +log "Posting comment to PR #${GITHUB_PR_NUMBER}..." + +echo -e "$COMMENT_BODY" | gh pr comment "$GITHUB_PR_NUMBER" \ + --repo "$GITHUB_REPOSITORY" \ + --body-file - 2>/dev/null || { + echo "ERROR: Failed to post comment to PR" >&2 + exit 1 + } + +log "Comment posted successfully" diff --git a/scripts/gen-coverage-report.sh b/scripts/gen-coverage-report.sh new file mode 100644 index 00000000..578f66d9 --- /dev/null +++ b/scripts/gen-coverage-report.sh @@ -0,0 +1,67 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Generate a dated coverage baseline markdown report from vitest coverage output. +# Usage: bash scripts/gen-coverage-report.sh [date] +# Output: docs/coverage-baseline-YYYY-MM-DD.md + +DATE="${1:-$(date +%Y-%m-%d)}" +OUTPUT="docs/coverage-baseline-${DATE}.md" +TMPFILE=$(mktemp) + +cleanup() { rm -f "$TMPFILE"; } +trap cleanup EXIT + +echo "Running vitest coverage..." +pnpm --filter drop coverage 2>&1 | tee "$TMPFILE" > /dev/null || { + echo "ERROR: coverage run failed" >&2 + exit 1 +} + +# vitest text reporter prints a table; the "All files" row is the summary. +SUMMARY=$(grep "All files" "$TMPFILE" | tail -1 || true) +if [[ -z "$SUMMARY" ]]; then + echo "ERROR: could not find 'All files' row in coverage output" >&2 + exit 1 +fi + +STMTS=$(echo "$SUMMARY" | awk -F'|' '{gsub(/ /,"",$2); print $2}') +BRANCHES=$(echo "$SUMMARY" | awk -F'|' '{gsub(/ /,"",$3); print $3}') +FUNCS=$(echo "$SUMMARY" | awk -F'|' '{gsub(/ /,"",$4); print $4}') +LINES=$(echo "$SUMMARY" | awk -F'|' '{gsub(/ /,"",$5); print $5}') + +DETAIL=$(grep -A20 "Coverage summary" "$TMPFILE" | grep -E "(Statements|Branches|Functions|Lines)" | sed 's/.*coverage: //' || true) + +cat > "$OUTPUT" << EOF +# Coverage Baseline — ${DATE} + +Measured on branch \`$(git rev-parse --abbrev-ref HEAD)\` at commit \`$(git rev-parse --short HEAD)\`. +Snapshot only — no gates, no thresholds. + +## Summary (server — \`server/server/\` backend only) + +| Metric | Value | +|---|---| +| Statements | ${STMTS}% | +| Branches | ${BRANCHES}% | +| Functions | ${FUNCS}% | +| Lines | ${LINES}% | + +## Detail + +\`\`\` +${DETAIL} +\`\`\` + +## How to reproduce + +\`\`\`bash +pnpm --filter drop coverage +\`\`\` + +Output: \`server/coverage/lcov.info\`. +CI uploads to Codecov via \`codecov/codecov-action@v5\` with flag \`server\`. +EOF + +echo "Coverage report written: $OUTPUT" +echo "Summary: Statements=${STMTS}% Branches=${BRANCHES}% Functions=${FUNCS}% Lines=${LINES}%" diff --git a/scripts/sonarcloud-pr-comment.sh b/scripts/sonarcloud-pr-comment.sh new file mode 100644 index 00000000..f482b646 --- /dev/null +++ b/scripts/sonarcloud-pr-comment.sh @@ -0,0 +1,377 @@ +#!/usr/bin/env bash +# ============================================================================== +# sonarcloud-pr-comment.sh — Post SonarCloud findings as PR comment +# ============================================================================== +# +# Queries SonarCloud for unresolved issues on the current PR and posts a +# summary comment with links to existing GitHub issues. +# +# Usage: +# ./scripts/sonarcloud-pr-comment.sh +# +# Environment: +# SONAR_TOKEN Required. SonarCloud API token +# GH_TOKEN GitHub API token (falls back to GITHUB_TOKEN) +# SONAR_PROJECT_KEY SonarCloud project key (default: BillyOutlast_drop) +# SONAR_MAX_LINES Max lines to fetch per file for line-level data (default: 10000) +# GITHUB_REPOSITORY GitHub repo (default: BillyOutlast/drop) +# GITHUB_PR_NUMBER PR number (auto-detected from GitHub context) +# ============================================================================== + +set -euo pipefail + +# ---- Configuration ----------------------------------------------------------- + +SONAR_PROJECT_KEY="${SONAR_PROJECT_KEY:-BillyOutlast_drop}" +SONAR_MAX_LINES="${SONAR_MAX_LINES:-10000}" +log "Using SONAR_MAX_LINES=${SONAR_MAX_LINES} — files exceeding this limit may have incomplete line data" +GITHUB_REPOSITORY="${GITHUB_REPOSITORY:-BillyOutlast/drop}" +GH_TOKEN="${GH_TOKEN:-${GITHUB_TOKEN:-}}" + +# Auto-detect PR number from GitHub context +if [[ -z "${GITHUB_PR_NUMBER:-}" ]]; then + if [[ -n "${GITHUB_REF:-}" && "$GITHUB_REF" =~ ^refs/pull/([0-9]+)/merge$ ]]; then + GITHUB_PR_NUMBER="${BASH_REMATCH[1]}" + else + echo "ERROR: GITHUB_PR_NUMBER not set and cannot auto-detect from GITHUB_REF" >&2 + exit 1 + fi +fi + +SONAR_API="https://sonarcloud.io/api/issues/search" +SEVERITIES="BLOCKER,CRITICAL,MAJOR" +PAGE_SIZE=100 + +# --- Validation --------------------------------------------------------------- + +if [[ -z "${SONAR_TOKEN:-}" ]]; then + echo "FATAL: SONAR_TOKEN is not set" >&2 + exit 1 +fi + +if [[ -z "$GH_TOKEN" ]]; then + echo "FATAL: GH_TOKEN or GITHUB_TOKEN is not set" >&2 + exit 1 +fi + +if ! command -v jq &>/dev/null; then + echo "FATAL: jq is required but not installed" >&2 + exit 1 +fi + +if ! command -v gh &>/dev/null; then + echo "FATAL: gh (GitHub CLI) is required but not installed" >&2 + exit 1 +fi + +# --- Helpers ------------------------------------------------------------------ + +log() { echo "[$(date '+%H:%M:%S')] $*"; } + +# --- Step 1: Fetch unresolved issues from SonarCloud ------------------------- + +log "Fetching unresolved issues from SonarCloud (project: ${SONAR_PROJECT_KEY})..." + +SONAR_RESPONSE=$(curl -sS -f \ + -H "Authorization: Bearer ${SONAR_TOKEN}" \ + "${SONAR_API}?componentKeys=${SONAR_PROJECT_KEY}&resolved=false&severities=${SEVERITIES}&ps=${PAGE_SIZE}&p=1&pullRequest=${GITHUB_PR_NUMBER}") || { + log "SonarCloud API request failed, skipping comment" + exit 0 + } + +TOTAL=$(echo "$SONAR_RESPONSE" | jq -r '.total // 0') +TOTAL_PAGES=$(( (TOTAL + PAGE_SIZE - 1) / PAGE_SIZE )) +log "Found ${TOTAL} unresolved issues across ${TOTAL_PAGES} page(s) (BLOCKER/CRITICAL/MAJOR)" + +if [[ "$TOTAL" -eq 0 ]]; then + log "No unresolved issues — building coverage-only comment" + COMMENT_BODY="## SonarCloud Analysis ✅\n\nNo BLOCKER, CRITICAL, or MAJOR issues found.\n\n" +else + COMMENT_BODY="## SonarCloud Analysis\n\n" + +# Fetch remaining pages if needed +if [[ "$TOTAL_PAGES" -gt 1 ]]; then + ALL_ISSUES=$(echo "$SONAR_RESPONSE" | jq '.issues') + PAGE=2 + while [[ "$PAGE" -le "$TOTAL_PAGES" ]]; do + log "Fetching page ${PAGE}/${TOTAL_PAGES}..." + PAGE_RESPONSE=$(curl -sS -f \ + -H "Authorization: Bearer ${SONAR_TOKEN}" \ + "${SONAR_API}?componentKeys=${SONAR_PROJECT_KEY}&resolved=false&severities=${SEVERITIES}&ps=${PAGE_SIZE}&p=${PAGE}&pullRequest=${GITHUB_PR_NUMBER}") || { + log "SonarCloud API request failed on page ${PAGE}, skipping remaining pages" + break + } + ALL_ISSUES=$(echo "$ALL_ISSUES $(echo "$PAGE_RESPONSE" | jq '.issues')" | jq -s 'add') + PAGE=$((PAGE + 1)) + done + SONAR_RESPONSE=$(echo "$SONAR_RESPONSE" | jq --argjson issues "$ALL_ISSUES" '.issues = $issues') +fi + +# --- Step 2: Group issues by severity ---------------------------------------- + +BLOCKER_COUNT=$(echo "$SONAR_RESPONSE" | jq '[.issues[] | select(.severity == "BLOCKER")] | length') +CRITICAL_COUNT=$(echo "$SONAR_RESPONSE" | jq '[.issues[] | select(.severity == "CRITICAL")] | length') +MAJOR_COUNT=$(echo "$SONAR_RESPONSE" | jq '[.issues[] | select(.severity == "MAJOR")] | length') + +# --- Step 3: Build current finding keys set ----------------------------------- + +CURRENT_KEYS=$(echo "$SONAR_RESPONSE" | jq -c '[.issues[].key] | unique') +log "Current finding keys: $(echo "$CURRENT_KEYS" | jq 'length') unique keys" + +# --- Step 4: Fetch existing GitHub issues and match to current findings ------- + +log "Fetching existing GitHub issues with 'sonarcloud' label..." +EXISTING_ISSUES=$(gh issue list \ + --repo "$GITHUB_REPOSITORY" \ + --label sonarcloud \ + --state open \ + --json number,title,body \ + --limit 100 2>/dev/null || echo "[]") + +EXISTING_COUNT=$(echo "$EXISTING_ISSUES" | jq 'length') +log "Found ${EXISTING_COUNT} existing sonarcloud issues" + +MATCHED_ISSUES=$(echo "$EXISTING_ISSUES" | jq -c --argjson currentKeys "$CURRENT_KEYS" ' + [.[] | select( + (.body // "") as $b | + ($b | capture("sonarcloud-keys:\\s*(?[A-Za-z0-9,._-]+)"; "i").keys // "" | split(",") | map(gsub("^\\s+|\\s+$"; ""))) as $issueKeys | + ($currentKeys - ($currentKeys - $issueKeys)) | length > 0 + ) | {number: .number, title: .title}] +') + +MATCHED_COUNT=$(echo "$MATCHED_ISSUES" | jq 'length') +log "Matched ${MATCHED_COUNT} issues to current findings" + +# --- Step 4: Build PR comment ------------------------------------------------ + +COMMENT_BODY="## SonarCloud Analysis\n\n" +COMMENT_BODY+="### Summary\n\n" +COMMENT_BODY+="| Severity | Count |\n" +COMMENT_BODY+="|----------|-------|\n" + +if [[ "$BLOCKER_COUNT" -gt 0 ]]; then + COMMENT_BODY+="| 🔴 BLOCKER | ${BLOCKER_COUNT} |\n" +fi +if [[ "$CRITICAL_COUNT" -gt 0 ]]; then + COMMENT_BODY+="| 🟠 CRITICAL | ${CRITICAL_COUNT} |\n" +fi +if [[ "$MAJOR_COUNT" -gt 0 ]]; then + COMMENT_BODY+="| 🟡 MAJOR | ${MAJOR_COUNT} |\n" +fi + +COMMENT_BODY+="\n**Total**: ${TOTAL} issues\n\n" + +# Add top 5 issues by severity +COMMENT_BODY+="### Top Issues\n\n" +COMMENT_BODY+="| File | Line | Rule | Severity |\n" +COMMENT_BODY+="|------|------|------|----------|\n" + +TOP_ISSUES=$(echo "$SONAR_RESPONSE" | jq -r ' + .issues + | sort_by( + if .severity == "BLOCKER" then 0 + elif .severity == "CRITICAL" then 1 + elif .severity == "MAJOR" then 2 + else 3 end + ) + | .[0:5] + | .[] + | "| \(.component | split(":") | last) | \(.line // "-") | \(.rule | split(":") | last) | \(.severity) |" +' 2>/dev/null || echo "") + +if [[ -n "$TOP_ISSUES" ]]; then + COMMENT_BODY+="${TOP_ISSUES}\n" +else + COMMENT_BODY+="| No issues found | - | - | - |\n" +fi + +COMMENT_BODY+="\n### Tracking\n\n" + +if [[ "$MATCHED_COUNT" -gt 0 ]]; then + COMMENT_BODY+="Existing GitHub issues tracking these findings:\n\n" + while IFS= read -r line; do + COMMENT_BODY+="${line}\n" + done < <(echo "$MATCHED_ISSUES" | jq -r '.[] | "- #\(.number): \(.title)"' | head -10) + + if [[ "$MATCHED_COUNT" -gt 10 ]]; then + COMMENT_BODY+="- ... and $((MATCHED_COUNT - 10)) more\n" + fi +else + COMMENT_BODY+="No existing GitHub issues found for these findings. Run \`./scripts/sonarcloud-sync.sh\` to create tracking issues.\n" +fi +fi + +COMMENT_BODY+="\n---\n\n" +COMMENT_BODY+="*Full analysis: [SonarCloud Dashboard](https://sonarcloud.io/project/overview?id=${SONAR_PROJECT_KEY})*\n" +COMMENT_BODY+="*To create tracking issues: \`./scripts/sonarcloud-sync.sh --backfill\`*\n\n" + +log "Fetching quality gate status..." +QG_RESPONSE=$(curl -sS -f \ + -H "Authorization: Bearer ${SONAR_TOKEN}" \ + "https://sonarcloud.io/api/qualitygates/project_status?projectKey=${SONAR_PROJECT_KEY}&pullRequest=${GITHUB_PR_NUMBER}" 2>/dev/null || echo '{"projectStatus":{"status":"UNKNOWN","conditions":[]}}') + +log "Fetching files needing coverage..." +COVERAGE_RESPONSE=$(curl -sS -f \ + -H "Authorization: Bearer ${SONAR_TOKEN}" \ + "https://sonarcloud.io/api/measures/component_tree?component=${SONAR_PROJECT_KEY}&metricKeys=new_coverage,new_uncovered_lines&qualifiers=FIL&ps=500&p=1&pullRequest=${GITHUB_PR_NUMBER}" 2>/dev/null || echo '{"components":[]}') + +# Check for more pages and fetch them +TOTAL_COMPONENTS=$(echo "$COVERAGE_RESPONSE" | jq -r '.paging.total // 0') +if [[ "$TOTAL_COMPONENTS" -gt 500 ]]; then + TOTAL_COV_PAGES=$(( (TOTAL_COMPONENTS + 500 - 1) / 500 )) + for ((p = 2; p <= TOTAL_COV_PAGES; p++)); do + PAGE_RESPONSE=$(curl -sS -f \ + -H "Authorization: Bearer ${SONAR_TOKEN}" \ + "https://sonarcloud.io/api/measures/component_tree?component=${SONAR_PROJECT_KEY}&metricKeys=new_coverage,new_uncovered_lines&qualifiers=FIL&ps=500&p=${p}&pullRequest=${GITHUB_PR_NUMBER}" 2>/dev/null || echo '{"components":[]}') + COVERAGE_RESPONSE=$(printf '%s %s' "$COVERAGE_RESPONSE" "$PAGE_RESPONSE" | jq -s '{components: [.[].components[]]}') + done +fi + +# --- Step 4b: Build human-readable coverage gaps table ------------------------ + +log "Building coverage gaps table..." +# PR-scoped measures nest values under .periods[0].value (branch analyses use .value) +UNCOVERED_FILES=$(echo "$COVERAGE_RESPONSE" | jq -c ' + [.components[]? + | { + key: .key, + path: (.path // "unknown"), + uncovered: (((.measures[]? | select(.metric == "new_uncovered_lines") | .periods[0].value // .value) // "0") | tonumber), + coverage: (((.measures[]? | select(.metric == "new_coverage") | .periods[0].value // .value)) // "0.0") + } + | select(.uncovered > 0) + ] | sort_by(.uncovered) | reverse | .[0:5]') + +if echo "$UNCOVERED_FILES" | jq -e 'length > 0' >/dev/null 2>&1; then + COMMENT_BODY+="### 📊 Lines Needing Coverage\n\n" + COMMENT_BODY+="| File | Coverage | Uncovered Lines | Lines Needing Tests |\n" + COMMENT_BODY+="|------|----------|----------------|--------------------|\n" + + TEMP_DIR=$(mktemp -d) + # shellcheck disable=SC2064 + trap 'rm -rf "$TEMP_DIR"' EXIT + + file_index=0 + while read -r file_entry; do + FILE_KEY=$(echo "$file_entry" | jq -r '.key') + FILE_PATH=$(echo "$file_entry" | jq -r '.path') + FILE_COV=$(echo "$file_entry" | jq -r '.coverage') + FILE_UNC=$(echo "$file_entry" | jq -r '.uncovered') + + # Stash metadata as JSON line so file paths containing | are safe + jq -c -n \ + --arg key "$FILE_KEY" \ + --arg path "$FILE_PATH" \ + --arg cov "$FILE_COV" \ + --arg unc "$FILE_UNC" \ + '{key:$key, path:$path, coverage:$cov, uncovered:$unc}' > "${TEMP_DIR}/meta_${file_index}" + + # URL-encode FILE_KEY for the SonarCloud sources/lines API + ENCODED_KEY=$(jq -rn --arg k "$FILE_KEY" '$k | @uri') + + # Fetch line-level data in background — all files run concurrently + { + curl -sS -f --connect-timeout 10 --max-time 30 \ + -H "Authorization: Bearer ${SONAR_TOKEN}" \ + "https://sonarcloud.io/api/sources/lines?key=${ENCODED_KEY}&from=1&to=${SONAR_MAX_LINES}&pullRequest=${GITHUB_PR_NUMBER}" \ + 2>/dev/null || echo '{"sources":[]}' + } > "${TEMP_DIR}/lines_${file_index}" & + + file_index=$((file_index + 1)) + done < <(echo "$UNCOVERED_FILES" | jq -c '.[]') + + # Wait for all background fetches to complete + wait + + # Process results in order + for ((i = 0; i < file_index; i++)); do + META=$(<"${TEMP_DIR}/meta_${i}") + FILE_KEY=$(echo "$META" | jq -r '.key') + FILE_PATH=$(echo "$META" | jq -r '.path') + FILE_COV=$(echo "$META" | jq -r '.coverage') + FILE_UNC=$(echo "$META" | jq -r '.uncovered') + # shellcheck disable=SC2188 + LINES_RESPONSE=$(<"${TEMP_DIR}/lines_${i}" 2>/dev/null || echo '{"sources":[]}') + + if ! echo "$LINES_RESPONSE" | jq empty 2>/dev/null; then + LINES_RESPONSE='{"sources":[]}' + fi + + # Dedupe line numbers before sort — SonarCloud may return duplicates + NEW_LINES=$(echo "$LINES_RESPONSE" | jq -r ' + [.sources[] | select(.isNew == true and (.lineHits // -1) == 0) | .line] | unique | sort' + ) + + if echo "$NEW_LINES" | jq -e 'length > 0' >/dev/null 2>&1; then + LINE_RANGES=$(echo "$NEW_LINES" | jq -r ' + reduce .[] as $l ( + {ranges: [], current: null}; + if .current == null then + {ranges: [[$l, $l]], current: [$l, $l]} + elif $l == .current[1] + 1 then + {ranges: .ranges[:-1] + [[.current[0], $l]], current: [.current[0], $l]} + else + {ranges: .ranges + [[$l, $l]], current: [$l, $l]} + end + ) | .ranges | map( + if .[0] == .[1] then "\(.[0])" + else "\(.[0])-\(.[1])" + end + ) | join(", ")') + +SAFE_PATH="${FILE_PATH//|/\\|}" +SAFE_RANGES="${LINE_RANGES//|/\\|}" +COMMENT_BODY+="| \`${SAFE_PATH}\` | ${FILE_COV}% | ${FILE_UNC} | ${SAFE_RANGES} |\n" + fi + done + COMMENT_BODY+="\n" +else + COMMENT_BODY+="### 📊 Lines Needing Coverage\n\nNo uncovered lines found in new code.\n\n" +fi + +COMMENT_BODY+="
\n📋 JSON Summary (for AI agents)\n\n" +COMMENT_BODY+="\`\`\`json\n" + +JSON_SUMMARY=$(echo "$SONAR_RESPONSE" | jq \ + --arg project "$SONAR_PROJECT_KEY" \ + --arg pr "$GITHUB_PR_NUMBER" \ + --argjson qg "$(echo "$QG_RESPONSE" | jq '{gateStatus: .projectStatus.status, failedConditions: [.projectStatus.conditions[]? | select(.status == "ERROR") | {metric: .metricKey, actual: .actualValue, threshold: .errorThreshold}]}')" \ + --argjson coverage "$(echo "$COVERAGE_RESPONSE" | jq '[.components[]? | {file: (.path // .name), coverage: (((.measures[]? | select(.metric == "new_coverage") | .periods[0].value // .value)) // null), uncovered: (((.measures[]? | select(.metric == "new_uncovered_lines") | .periods[0].value // .value) // "0") | tonumber)} | select(.uncovered > 0 or .coverage != null)] | sort_by(.uncovered) | reverse')" \ + '{ + project: $project, + pullRequest: ($pr | tonumber), + qualityGate: $qg, + filesNeedingCoverage: $coverage, + totalIssues: .total, + summary: { + blocker: [.issues[] | select(.severity == "BLOCKER")] | length, + critical: [.issues[] | select(.severity == "CRITICAL")] | length, + major: [.issues[] | select(.severity == "MAJOR")] | length + }, + issues: [.issues[] | { + key: .key, + rule: .rule, + severity: .severity, + message: .message, + component: (.component | split(":") | last), + line: .line, + url: ("https://sonarcloud.io/project/issues?id=" + $project + "&issues=" + .key + "&open=" + .key) + }] +}') + +COMMENT_BODY+="${JSON_SUMMARY}\n" +COMMENT_BODY+="\`\`\`\n\n" +COMMENT_BODY+="
" + +# --- Step 5: Post comment to PR ---------------------------------------------- + +log "Posting comment to PR #${GITHUB_PR_NUMBER}..." + +echo -e "$COMMENT_BODY" | gh pr comment "$GITHUB_PR_NUMBER" \ + --repo "$GITHUB_REPOSITORY" \ + --body-file - 2>/dev/null || { + log "Failed to post comment to PR" + exit 1 + } + +log "Comment posted successfully to PR #${GITHUB_PR_NUMBER}" diff --git a/scripts/sonarcloud-sync.sh b/scripts/sonarcloud-sync.sh new file mode 100644 index 00000000..8703e147 --- /dev/null +++ b/scripts/sonarcloud-sync.sh @@ -0,0 +1,471 @@ +#!/usr/bin/env bash +# ============================================================================== +# sonarcloud-sync.sh — Sync SonarCloud findings to GitHub Issues +# ============================================================================== +# +# Queries SonarCloud for unresolved BLOCKER/CRITICAL/MAJOR issues, groups them +# by (severity, rule), and creates/updates GitHub issues to track them. +# +# Usage: +# ./scripts/sonarcloud-sync.sh # Delta: new findings only +# ./scripts/sonarcloud-sync.sh --backfill # Full: all unresolved +# ./scripts/sonarcloud-sync.sh --dry-run # Preview, no changes +# ./scripts/sonarcloud-sync.sh --backfill --dry-run +# +# Environment: +# SONAR_TOKEN Required. SonarCloud API token +# GH_TOKEN GitHub API token (falls back to GITHUB_TOKEN) +# SONAR_PROJECT_KEY SonarCloud project key (default: BillyOutlast_drop) +# GITHUB_REPOSITORY GitHub repo (default: BillyOutlast/drop) +# ============================================================================== + +set -euo pipefail + +# ---- Configuration ----------------------------------------------------------- + +SONAR_PROJECT_KEY="${SONAR_PROJECT_KEY:-BillyOutlast_drop}" +GITHUB_REPOSITORY="${GITHUB_REPOSITORY:-BillyOutlast/drop}" +GH_TOKEN="${GH_TOKEN:-${GITHUB_TOKEN:-}}" + +SONAR_API="https://sonarcloud.io/api/issues/search" +SEVERITIES="BLOCKER,CRITICAL,MAJOR" +PAGE_SIZE=500 + +# --- Flags -------------------------------------------------------------------- + +BACKFILL=false +DRY_RUN=false + +while [[ $# -gt 0 ]]; do + case "$1" in + --backfill) BACKFILL=true; shift ;; + --dry-run) DRY_RUN=true; shift ;; + *) echo "Unknown option: $1" >&2; exit 1 ;; + esac +done + +# --- Validation --------------------------------------------------------------- + +if [[ -z "${SONAR_TOKEN:-}" ]]; then + echo "FATAL: SONAR_TOKEN is not set" >&2 + exit 1 +fi + +if [[ -z "$GH_TOKEN" ]]; then + echo "FATAL: GH_TOKEN or GITHUB_TOKEN is not set" >&2 + exit 1 +fi + +if ! command -v jq &>/dev/null; then + echo "FATAL: jq is required but not installed" >&2 + exit 1 +fi + +if ! command -v gh &>/dev/null; then + echo "FATAL: gh (GitHub CLI) is required but not installed" >&2 + exit 1 +fi + +# --- Helpers ------------------------------------------------------------------ + +log() { echo "[$(date '+%H:%M:%S')] $*"; } +warn() { echo "[WARN] $*" >&2; } +dry() { $DRY_RUN && echo "[DRY-RUN] $*" || echo "$*"; } + +# Build a stable group identifier: / +group_id() { + local severity="$1" rule="$2" + # Strip language prefix (e.g. "javascript:S8786" -> "S8786") + local short_rule="${rule##*:}" + echo "${severity}/${short_rule}" +} + +# Build a GitHub issue title +issue_title() { + local severity="$1" rule="$2" message="$3" + local short_rule="${rule##*:}" + # Truncate message to 60 chars for title + local msg="${message:0:60}" + echo "sonar: ${severity} — ${short_rule} ${msg}" +} + +# Build catch-all issue title per severity +catchall_title() { + local severity="$1" + echo "sonar: ${severity} — Various unresolved issues" +} + +# --- Step 1: Fetch unresolved issues from SonarCloud (with pagination) -------- + +log "Fetching unresolved issues from SonarCloud (project: ${SONAR_PROJECT_KEY})..." + +SONAR_RESPONSE=$(curl -sS -f \ + -H "Authorization: Bearer ${SONAR_TOKEN}" \ + "${SONAR_API}?componentKeys=${SONAR_PROJECT_KEY}&resolved=false&severities=${SEVERITIES}&ps=${PAGE_SIZE}&p=1") || { + warn "SonarCloud API request failed (exit code $?)" + exit 1 + } + +TOTAL=$(echo "$SONAR_RESPONSE" | jq -r '.total // 0') +TOTAL_PAGES=$(( (TOTAL + PAGE_SIZE - 1) / PAGE_SIZE )) +log "Found ${TOTAL} unresolved issues across ${TOTAL_PAGES} page(s) (BLOCKER/CRITICAL/MAJOR)" + +if [[ "$TOTAL" -eq 0 ]]; then + log "No unresolved issues — nothing to sync." + exit 0 +fi + +if [[ "$TOTAL_PAGES" -gt 1 ]]; then + ALL_ISSUES=$(echo "$SONAR_RESPONSE" | jq '.issues') + PAGE=2 + while [[ "$PAGE" -le "$TOTAL_PAGES" ]]; do + log "Fetching page ${PAGE}/${TOTAL_PAGES}..." + PAGE_RESPONSE=$(curl -sS -f \ + -H "Authorization: Bearer ${SONAR_TOKEN}" \ + "${SONAR_API}?componentKeys=${SONAR_PROJECT_KEY}&resolved=false&severities=${SEVERITIES}&ps=${PAGE_SIZE}&p=${PAGE}") || { + warn "SonarCloud API request failed on page ${PAGE} (exit code $?)" + exit 1 + } + ALL_ISSUES=$(echo "$ALL_ISSUES $(echo "$PAGE_RESPONSE" | jq '.issues')" | jq -s 'add') + PAGE=$((PAGE + 1)) + done + SONAR_RESPONSE=$(echo "$SONAR_RESPONSE" | jq --argjson issues "$ALL_ISSUES" '.issues = $issues') +fi + +# --- Step 2: Group issues by (severity, rule) --------------------------------- + +log "Grouping issues by (severity, rule)..." + +# Build array of deduplicated groups +declare -A GROUP_KEYS # group_id -> 1 (track which groups exist) + +while IFS=$'\t' read -r rule severity message; do + gid=$(group_id "$severity" "$rule") + GROUP_KEYS["$gid"]=1 +done < <( + echo "$SONAR_RESPONSE" | jq -r '.issues[] | [.rule, .severity, .message] | @tsv' +) + +# For each group, extract the full list of issues +declare -A GROUP_ISSUES # group_id -> JSON array of issue objects +declare -A GROUP_SEVERITY # group_id -> severity +declare -A GROUP_RULE # group_id -> rule +declare -A GROUP_MESSAGE # group_id -> first issue message + +for gid in "${!GROUP_KEYS[@]}"; do + severity="${gid%%/*}" + rule_short="${gid#*/}" + + # Reconstruct the full rule name (SonarCloud may prefix with language) + ISSUES_JSON=$(echo "$SONAR_RESPONSE" | jq -c \ + --arg rule_match "${rule_short}" \ + --arg sev "${severity}" \ + '[.issues[] | select(.severity == $sev and (.rule | endswith($rule_match)))]') + + count=$(echo "$ISSUES_JSON" | jq 'length') + GROUP_ISSUES["$gid"]="$ISSUES_JSON" + GROUP_SEVERITY["$gid"]="$severity" + + # Extract full rule name from first issue + full_rule=$(echo "$ISSUES_JSON" | jq -r '.[0].rule // ""') + GROUP_RULE["$gid"]="$full_rule" + + msg=$(echo "$ISSUES_JSON" | jq -r '.[0].message // ""') + GROUP_MESSAGE["$gid"]="$msg" + + log " Group ${gid}: ${count} issues" +done + +# --- Step 3: Separate large and small groups ---------------------------------- + +declare -A LARGE_GROUPS # group_id -> 1 (count >= 3) +CATCHALL_CRITICAL_JSON="[]" +CATCHALL_MAJOR_JSON="[]" + +for gid in "${!GROUP_KEYS[@]}"; do + count=$(echo "${GROUP_ISSUES[$gid]}" | jq 'length') + + if [[ "$count" -ge 3 ]]; then + LARGE_GROUPS["$gid"]=1 + fi +done + +# Collect small-group issues (<3) into severity catch-all arrays + +for gid in "${!GROUP_KEYS[@]}"; do + count=$(echo "${GROUP_ISSUES[$gid]}" | jq 'length') + severity="${gid%%/*}" + + if [[ "$count" -ge 3 ]]; then + continue + fi + + issues="${GROUP_ISSUES[$gid]}" + if [[ "$severity" == "BLOCKER" || "$severity" == "CRITICAL" ]]; then + CATCHALL_CRITICAL_JSON=$(echo "$CATCHALL_CRITICAL_JSON $issues" | jq -s 'add') + elif [[ "$severity" == "MAJOR" ]]; then + CATCHALL_MAJOR_JSON=$(echo "$CATCHALL_MAJOR_JSON $issues" | jq -s 'add') + fi +done + +log "Large groups (>=3 findings): ${#LARGE_GROUPS[@]}" +critical_catchall_count=$(echo "$CATCHALL_CRITICAL_JSON" | jq 'length') +major_catchall_count=$(echo "$CATCHALL_MAJOR_JSON" | jq 'length') +log "Catch-all critical: ${critical_catchall_count} issues" +log "Catch-all major: ${major_catchall_count} issues" + +# --- Step 4: Fetch existing GitHub issues ------------------------------------- + +log "Fetching existing GitHub issues with 'sonarcloud' label..." +GH_ISSUES=$(gh issue list \ + --repo "$GITHUB_REPOSITORY" \ + --label sonarcloud \ + --state open \ + --json number,title,body \ + --limit 500) + +EXISTING_ISSUES=$(echo "$GH_ISSUES" | jq -c '.' 2>/dev/null || echo "[]") +log "Found $(echo "$EXISTING_ISSUES" | jq 'length') existing open sonarcloud issues" + +# Build a map: title -> issue number +declare -A TITLE_TO_NUMBER +while IFS=$'\t' read -r number title; do + TITLE_TO_NUMBER["$title"]="$number" +done < <( + echo "$EXISTING_ISSUES" | jq -r '.[] | [.number, .title] | @tsv' +) + +# --- Step 5: Create issues for new groups (or update existing) ----------------- + +CREATED=0 +CLOSED=0 +SKIPPED=0 + +# Build issue bodies +build_issue_body() { + local severity="$1" rule="$2" message="$3" issues_json="$4" + local short_rule="${rule##*:}" + local count + count=$(echo "$issues_json" | jq 'length') + + # Collect all SonarCloud issue keys + local keys + keys=$(echo "$issues_json" | jq -r '[.[].key] | join(",")') + + local body="" + body+="## SonarCloud Finding — ${short_rule}\n\n" + body+="${message}\n\n" + body+="sonarcloud-keys: ${keys}\n\n" + body+="| File | Line |\n" + body+="|------|------|\n" + + while IFS=$'\t' read -r component line; do + # Strip project prefix from component: "BillyOutlast_drop:server/file.ts" -> "server/file.ts" + local file="${component#*:}" + body+="| \`${file}\` | ${line} |\n" + done < <( + echo "$issues_json" | jq -r '.[] | [.component, .line] | @tsv' + ) + + body+="\n### Fix\n\nFollow SonarCloud rule guidance for \`${short_rule}\`." + printf '%b' "$body" +} + +build_catchall_body() { + local severity="$1" issues_json="$2" title="$3" + local count + count=$(echo "$issues_json" | jq 'length') + + local keys + keys=$(echo "$issues_json" | jq -r '[.[].key] | join(",")') + + local body="" + body+="## SonarCloud Finding — Various ${severity} Issues\n\n" + body+="sonarcloud-keys: ${keys}\n\n" + body+="| File | Line | Rule | Issue |\n" + body+="|------|------|------|-------|\n" + + while IFS=$'\t' read -r component line rule message; do + local file="${component#*:}" + local short_rule="${rule##*:}" + # Escape pipe characters in message + local safe_msg="${message//|/\\|}" + body+="| \`${file}\` | ${line} | ${short_rule} | ${safe_msg} |\n" + done < <( + echo "$issues_json" | jq -r '.[] | [.component, .line, .rule, .message] | @tsv' + ) + + body+="\n### Fix\n\nAddress each issue according to its rule guidance." + printf '%b' "$body" +} + +determine_labels() { + local severity="$1" + local labels="sonarcloud" + + case "$severity" in + BLOCKER|CRITICAL) labels="${labels},critical" ;; + MAJOR) labels="${labels},major" ;; + *) labels="${labels},other" ;; + esac + + echo "$labels" +} + +# Process large groups (>= 3 findings each) +for gid in "${!LARGE_GROUPS[@]}"; do + severity="${GROUP_SEVERITY[$gid]}" + rule="${GROUP_RULE[$gid]}" + message="${GROUP_MESSAGE[$gid]}" + issues="${GROUP_ISSUES[$gid]}" + title=$(issue_title "$severity" "$rule" "$message") + labels=$(determine_labels "$severity") + + if [[ -n "${TITLE_TO_NUMBER[$title]:-}" ]]; then + # Issue already exists — check if it needs updating + existing_num="${TITLE_TO_NUMBER[$title]}" + log "Issue #${existing_num} already exists for: ${title}" + + # In backfill mode, update the body + if $BACKFILL; then + body=$(build_issue_body "$severity" "$rule" "$message" "$issues") + dry "gh issue edit ${existing_num} --body ..." + if ! $DRY_RUN; then + echo "$body" | gh issue edit "$existing_num" \ + --repo "$GITHUB_REPOSITORY" \ + --body-file - || warn "Failed to update issue #${existing_num}" + fi + else + (( ++SKIPPED )) + fi + else + # Create new issue + body=$(build_issue_body "$severity" "$rule" "$message" "$issues") + dry "Creating issue: ${title}" + if ! $DRY_RUN; then + created_url=$(echo "$body" | gh issue create \ + --repo "$GITHUB_REPOSITORY" \ + --title "$title" \ + --label "$labels" \ + --body-file - 2>/dev/null) || { + warn "Failed to create issue: ${title}" + continue + } + log "Created issue: ${title} → ${created_url}" + fi + (( ++CREATED )) + fi +done + +# Process catch-all groups +process_catchall() { + local severity="$1" issues_json="$2" severity_label="$3" + local count + count=$(echo "$issues_json" | jq 'length') + + [[ "$count" -eq 0 ]] && return + + local title + title=$(catchall_title "$severity") + local labels="sonarcloud,${severity_label}" + local body + body=$(build_catchall_body "$severity" "$issues_json" "$title") + + if [[ -n "${TITLE_TO_NUMBER[$title]:-}" ]]; then + existing_num="${TITLE_TO_NUMBER[$title]}" + log "Catch-all issue #${existing_num} already exists for: ${title}" + if $BACKFILL; then + dry "gh issue edit ${existing_num} --body ..." + if ! $DRY_RUN; then + echo "$body" | gh issue edit "$existing_num" \ + --repo "$GITHUB_REPOSITORY" \ + --body-file - || warn "Failed to update catch-all issue #${existing_num}" + fi + else + (( ++SKIPPED )) + fi + else + dry "Creating issue: ${title}" + if ! $DRY_RUN; then + created_url=$(echo "$body" | gh issue create \ + --repo "$GITHUB_REPOSITORY" \ + --title "$title" \ + --label "$labels" \ + --body-file - 2>/dev/null) || { + warn "Failed to create catch-all issue: ${title}" + return + } + log "Created issue: ${title} → ${created_url}" + fi + (( ++CREATED )) + fi +} + +process_catchall "CRITICAL" "$CATCHALL_CRITICAL_JSON" "critical" +process_catchall "MAJOR" "$CATCHALL_MAJOR_JSON" "major" + +# --- Step 6: Close issues whose findings are all resolved ---------------------- + +log "Checking for resolved findings to close..." + +if [[ "$(echo "$EXISTING_ISSUES" | jq 'length')" -gt 0 ]]; then + # Get all currently unresolved SonarCloud issue keys + ALL_UNRESOLVED_KEYS_JSON=$(echo "$SONAR_RESPONSE" | jq '[.issues[].key]') + + while IFS= read -r issue; do + issue_num=$(echo "$issue" | jq -r '.number') + issue_title=$(echo "$issue" | jq -r '.title') + issue_body=$(echo "$issue" | jq -r '.body // ""') + + # Extract SonarCloud keys from body + if [[ "$issue_body" =~ sonarcloud-keys:\ ([A-Za-z0-9,._-]+) ]]; then + keys="${BASH_REMATCH[1]}" + # Remove whitespace, split by comma + IFS=',' read -ra key_array <<< "$keys" + + all_resolved=true + for key in "${key_array[@]}"; do + key=$(echo "$key" | xargs) # trim + [[ -z "$key" ]] && continue + # Check if this key still appears in unresolved results + if echo "$ALL_UNRESOLVED_KEYS_JSON" | jq -e --arg k "$key" 'index($k)' >/dev/null; then + all_resolved=false + break + fi + done + + # Also skip catch-all issues — they need fuzzy matching, safer to let them persist + if [[ "$issue_title" == *"Various"* ]]; then + all_resolved=false + fi + + if $all_resolved && [[ ${#key_array[@]} -gt 0 ]]; then + dry "Closing issue #${issue_num}: All SonarCloud findings resolved" + if ! $DRY_RUN; then + if gh issue close "$issue_num" \ + --repo "$GITHUB_REPOSITORY" \ + --comment "All constituent SonarCloud findings have been resolved." 2>/dev/null; then + log "Closed issue #${issue_num}: ${issue_title}" + (( ++CLOSED )) + else + warn "Failed to close issue #${issue_num}" + fi + fi + fi + fi + done < <(echo "$EXISTING_ISSUES" | jq -c '.[]') +fi + +# --- Summary ------------------------------------------------------------------ + +echo "" +log "=== Sync Summary ===" +log " Created: ${CREATED}" +log " Closed: ${CLOSED}" +log " Skipped: ${SKIPPED}" +$DRY_RUN && log " (dry-run — no changes were made)" + +# Handle case where nothing was done +if [[ "$CREATED" -eq 0 && "$CLOSED" -eq 0 ]]; then + log " Everything up to date." +fi diff --git a/security/risk-register.yaml b/security/risk-register.yaml new file mode 100644 index 00000000..71a08a72 --- /dev/null +++ b/security/risk-register.yaml @@ -0,0 +1,212 @@ +# Accepted security risks. +# +# Each entry documents a known vulnerability with accepted mitigation. +# `review_by`: when this entry must be re-evaluated (auto-flagged if past). +# `ci_ignore: true` means the vuln is in `pnpm audit --ignore GHSA-...` and +# this entry is the authoritative rationale. +# +# When a new vuln is added to `pnpm audit --ignore`, add a corresponding +# entry here. CI check (ci: verify risk register covers all ignored advisories) +# enforces coverage. + +risks: + - id: RISK-001 + title: "decompress: Archive extraction can create files and links outside the target directory" + package: "decompress@<=4.2.1" + advisory: GHSA-mp2f-45pm-3cg9 + severity: critical + affected_paths: + - "desktop>tauri>imagemin-optipng>optipng-bin>bin-build>decompress" + - "desktop>tauri>imagemin-optipng>optipng-bin>bin-build>download>decompress" + - "desktop>tauri>imagemin-optipng>optipng-bin>bin-wrapper>download>decompress" + mitigation: "Local patch applied via patches/decompress@4.2.1.patch (path containment + mode bit stripping + link target validation). Only used in Tauri build pipeline, not exposed to runtime. Patched in commit 2be06d7a." + accepted_by: "BillyOutlast" + accepted_date: "2025-07-24" + review_by: "2025-10-24" + ci_ignore: true + + - id: RISK-002 + title: "Prototype Pollution in lodash" + package: "lodash.pick@>=4.0.0 <=4.4.0" + advisory: GHSA-p6mc-m468-83gw + severity: high + affected_paths: + - "desktop>tauri>@tauri-apps/tauri-inliner>cheerio>lodash.pick" + mitigation: "Dead-end transitive dep. tauri-inliner pins lodash.pick 4.x. No upstream fix available. Desktop build pipeline only, not exposed to runtime." + accepted_by: "BillyOutlast" + accepted_date: "2025-07-24" + review_by: "2025-10-24" + ci_ignore: true + + - id: RISK-003 + title: "SVGO removeScripts plugin leaves some executable code intact" + package: "svgo@1.3.2" + advisory: GHSA-2p49-hgcm-8545 + severity: high + affected_paths: + - "desktop>tauri>@tauri-apps/tauri-inliner>svgo" + mitigation: "Dead-end transitive dep. tauri-inliner pins svgo 1.x. No upstream fix. Desktop build pipeline only." + accepted_by: "BillyOutlast" + accepted_date: "2025-07-24" + review_by: "2025-10-24" + ci_ignore: true + + - id: RISK-004 + title: "Server-Side Request Forgery in Request" + package: "request@2.88.2" + advisory: GHSA-c2qf-rxjj-qqgw + severity: moderate + affected_paths: + - "desktop>tauri>@tauri-apps/tauri-inliner>request" + mitigation: "Deprecated since 2020. No fix. Desktop build pipeline only." + accepted_by: "BillyOutlast" + accepted_date: "2025-07-24" + review_by: "2025-10-24" + ci_ignore: true + + - id: RISK-005 + title: "file-type affected by infinite loop in ASF parser" + package: "file-type@18.7.0" + advisory: GHSA-cjm9-3pvw-9wxf + severity: moderate + affected_paths: + - "server>stream-mime-type>file-type" + mitigation: "Server uses file-type only for stream MIME detection. Affected path requires crafted ASF file input. No upstream fix yet." + accepted_by: "BillyOutlast" + accepted_date: "2025-07-24" + review_by: "2025-10-24" + ci_ignore: true + + - id: RISK-006 + title: "@hono/node-server: Middleware bypass via repeated X-Powered-By headers" + package: "@hono/node-server<1.13.10" + advisory: GHSA-7g6w-hpgm-66vp + severity: moderate + affected_paths: + - "server>prisma>@prisma/dev>@hono/node-server" + mitigation: "Dev-only dep. Not bundled in production. Prisma update would resolve." + accepted_by: "BillyOutlast" + accepted_date: "2025-07-24" + review_by: "2025-10-24" + ci_ignore: true + + - id: RISK-007 + title: "uuid: Missing buffer bounds check" + package: "uuid@<11.0.0" + advisory: GHSA-cf7w-9wvq-3f9r + severity: moderate + affected_paths: + - "desktop>tauri>@tauri-apps/tauri-inliner>request>uuid" + mitigation: "Transitive through deprecated request. Desktop build pipeline only. Normal uuid usage unaffected (CVE requires explicit buf arg)." + accepted_by: "BillyOutlast" + accepted_date: "2025-07-24" + review_by: "2025-10-24" + ci_ignore: true + + - id: RISK-008 + title: "Astro: Reflected XSS via unescaped View Transition" + package: "astro@<7.0.10" + advisory: GHSA-vfqw-2gh8-h2hg + severity: moderate + affected_paths: + - "sites/docs>astro" + mitigation: "Fix requires astro 7.x (MAJOR version jump). sites/docs uses astro 6.4.8. Migration deferred. sites/docs is content-only — no user input rendering." + accepted_by: "BillyOutlast" + accepted_date: "2025-07-24" + review_by: "2025-10-24" + ci_ignore: true + + - id: RISK-009 + title: "Astro: XSS via unescaped spread attribute names in components" + package: "astro@<7.0.10" + advisory: GHSA-fv6c-7r8v-qpqv + severity: moderate + affected_paths: + - "sites/docs>astro" + mitigation: "Same as RISK-008 — requires astro 7.x. Deferred." + accepted_by: "BillyOutlast" + accepted_date: "2025-07-24" + review_by: "2025-10-24" + ci_ignore: true + + - id: RISK-010 + title: "Node.js Adapter for Hono: Path traversal in serveStatic" + package: "@hono/node-server<1.13.10" + advisory: GHSA-3qpp-9r2h-9wj2 + severity: moderate + affected_paths: + - "server>prisma>@prisma/dev>@hono/node-server" + mitigation: "Dev-only dep. Not in production. Prisma update would resolve." + accepted_by: "BillyOutlast" + accepted_date: "2025-07-24" + review_by: "2025-10-24" + ci_ignore: true + + - id: RISK-011 + title: "Valibot: record() issue paths can make flatten() throw" + package: "valibot@<1.2.0" + advisory: GHSA-vfqw-2gh8-h3gv + severity: moderate + affected_paths: + - "server>prisma>@prisma/dev>valibot" + mitigation: "Dev-only dep (prisma devtools). Not in production. Prisma update would resolve." + accepted_by: "BillyOutlast" + accepted_date: "2025-07-24" + review_by: "2025-10-24" + ci_ignore: true + + - id: RISK-012 + title: "esbuild allows arbitrary file read when running the dev server" + package: "esbuild@<=0.25.0" + advisory: GHSA-67mh-4wv8-7f7v + severity: low + affected_paths: + - "sites/docs>astro>esbuild" + mitigation: "Windows-only exploit. Linux dev server immune. sites/docs is content-only, no untrusted file access." + accepted_by: "BillyOutlast" + accepted_date: "2025-07-24" + review_by: "2025-10-24" + ci_ignore: true + + - id: RISK-013 + title: "Astro: Cross-site scripting via unescaped transition:* props" + package: "astro@<7.0.10" + advisory: GHSA-fv6c-7r8v-qpg3 + severity: low + affected_paths: + - "sites/docs>astro" + mitigation: "Same as RISK-008 — requires astro 7.x. sites/docs uses astro 6.4.8. Deferred." + accepted_by: "BillyOutlast" + accepted_date: "2025-07-24" + review_by: "2025-10-24" + ci_ignore: true + + - id: RISK-014 + title: "quick-xml: Quadratic duplicate attribute check DoS" + package: "quick-xml@<0.41.0" + advisory: RUSTSEC-2026-0194 + severity: high + affected_paths: + - "cli>opendal>reqsign>quick-xml 0.37.5" + - "cli>opendal>quick-xml 0.38.4" + - "desktop>tauri>tauri-utils>plist>quick-xml 0.38.4" + mitigation: "Desktop build-time path: tauri-utils/plist parses only its own Info.plist — fully trusted. CLI path via opendal: the XML endpoint is user-configured cloud storage. While normal usage targets trusted providers, a user could configure a malicious endpoint returning crafted XML. Keep CLI advisory open; recommend limiting deployment to trusted endpoints. Upstream crate bump in opendal will resolve when available." + accepted_by: "BillyOutlast" + accepted_date: "2026-07-28" + review_by: "2026-10-28" + ci_ignore: false + + - id: RISK-015 + title: "quick-xml: Unbounded namespace allocation OOM" + package: "quick-xml@<0.41.0" + advisory: RUSTSEC-2026-0195 + severity: high + affected_paths: + - "cli>opendal>reqsign>quick-xml 0.37.5" + - "cli>opendal>quick-xml 0.38.4" + - "desktop>tauri>tauri-utils>plist>quick-xml 0.38.4" + mitigation: "Same as RISK-014. Desktop build-time path is trusted; CLI opendal path has untrusted XML surface from user-configured endpoints. NsReader path unused in all affected crates' usage patterns." + accepted_by: "BillyOutlast" + accepted_date: "2026-07-28" + review_by: "2026-10-28" + ci_ignore: false diff --git a/server/.prettierignore b/server/.prettierignore index ed27b4da..6df296cc 100644 --- a/server/.prettierignore +++ b/server/.prettierignore @@ -1,7 +1,8 @@ -drop-base/ -# file is fully managed by pnpm, no reason to break it -pnpm-lock.yaml - -/torrential/ +node_modules/ +.nuxt/ +.output/ +dist/ +coverage/ .data/** **/.data/** +pnpm-lock.yaml diff --git a/server/docker-compose.test.yml b/server/docker-compose.test.yml new file mode 100644 index 00000000..6a1704c7 --- /dev/null +++ b/server/docker-compose.test.yml @@ -0,0 +1,15 @@ +services: + postgres-test: + image: postgres:14-alpine + environment: + POSTGRES_DB: drop_test + POSTGRES_USER: drop_test + POSTGRES_PASSWORD: drop_test + ports: + - "5433:5432" + tmpfs: /var/lib/postgresql/data + healthcheck: + test: ["CMD-SHELL", "pg_isready -U drop_test"] + interval: 5s + timeout: 5s + retries: 5 diff --git a/server/lint-staged.config.js b/server/lint-staged.config.js new file mode 100644 index 00000000..66e9ebaa --- /dev/null +++ b/server/lint-staged.config.js @@ -0,0 +1,25 @@ +// lint-staged config — polyglot; runs only on staged files at pre-commit. +// +// Scope rule: prettier for web/JS/TS, cargo fmt --check for Rust (no auto-fix +// on commit; dev runs `cargo fmt` manually), shellcheck for shell. ESLint is +// intentionally NOT here — Nuxt3 type-aware rules load the full tsconfig +// regardless of staged subset, so lint-staged offers no timing win for eslint +// (the full-repo pnpm --filter drop lint command remains in pre-commit). +// +// Husky swap (see .husky/pre-commit): `pnpm --filter drop lint` replaced with +// `pnpm --filter drop lint-staged` so this config runs on staged files only. +export default { + "*.{ts,js,vue,mjs,cjs,tsx,jsx}": "prettier --write", + "*.{yml,yaml,json,md}": "prettier --write", + "*.rs": (filepath) => { + // File-level passthrough so cargo fmt --check runs on the changed crate only. + // lint-staged passes the absolute path; derive `--manifest-path` from it. + const normalized = filepath.replace(/\\/g, "/"); + const match = normalized.match( + /^(.*\/)(torrential|cli|desktop\/src-tauri)(\/|$)/, + ); + const crate = match ? match[2] : "torrential"; + return `cargo fmt --all --manifest-path ${crate}/Cargo.toml -- --check`; + }, + "*.sh": "shellcheck --severity=warning", +}; diff --git a/server/package.json b/server/package.json index 3cd70422..ab4112ee 100644 --- a/server/package.json +++ b/server/package.json @@ -17,7 +17,16 @@ "lint": "pnpm run lint:eslint && pnpm run lint:prettier", "lint:eslint": "eslint .", "lint:prettier": "prettier . --check", - "lint:fix": "eslint . --fix && prettier --write --list-different ." + "lint:fix": "eslint . --fix && prettier --write --list-different .", + "format:check": "prettier --check .", + "test:e2e": "playwright test", + "test": "vitest run --passWithNoTests", + "test:changed": "vitest run --changed", + "test:watch": "vitest", + "coverage": "vitest run --coverage --passWithNoTests", + "coverage:type": "NODE_OPTIONS=\"--max-old-space-size=8192\" type-coverage --strict --cache --ignore-nested --ignore-catch", + "prepare": "husky", + "lint-staged": "lint-staged" }, "dependencies": { "@bufbuild/protobuf": "^2.11.0", @@ -65,8 +74,8 @@ "stream-mime-type": "^2.0.0", "turndown": "^7.2.0", "unstorage": "^1.15.0", - "vue": "latest", - "vue-router": "latest", + "vue": "3.5.17", + "vue-router": "4.5.1", "vue3-carousel": "^0.16.0", "vue3-carousel-nuxt": "^1.1.5", "vuedraggable": "^4.1.0" @@ -97,11 +106,23 @@ "sass": "^1.79.4", "tailwindcss": "^4.0.0", "typescript": "^5.8.3", - "vue-tsc": "^3.0.1" + "vue-tsc": "^3.0.1", + "@nuxt/test-utils": "^4.0.3", + "@playwright/test": "^1.61.1", + "@vitest/coverage-v8": "^4.1.10", + "@vue/test-utils": "^2.4.11", + "eslint-plugin-vuejs-accessibility": "^2.5.0", + "fast-check": "^4.9.0", + "happy-dom": "^20.11.1", + "husky": "^9.1.7", + "lint-staged": "^17.2.0", + "msw": "^2.15.0", + "type-coverage": "^2.30.1", + "vitest": "^4.1.10" }, "overrides": { "vue3-carousel-nuxt": { "vue3-carousel": "^0.16.0" } } -} +} \ No newline at end of file diff --git a/server/playwright.config.ts b/server/playwright.config.ts new file mode 100644 index 00000000..a3514ceb --- /dev/null +++ b/server/playwright.config.ts @@ -0,0 +1,16 @@ +import { defineConfig } from "@playwright/test"; + +export default defineConfig({ + testDir: "./test/e2e", + fullyParallel: true, + retries: 2, + use: { + baseURL: "http://localhost:4000", + trace: "on-first-retry", + }, + webServer: { + command: "E2E=true pnpm dev", + port: 4000, + reuseExistingServer: !process.env.CI, + }, +}); diff --git a/server/test/e2e/smoke.spec.ts b/server/test/e2e/smoke.spec.ts new file mode 100644 index 00000000..2f46e2bf --- /dev/null +++ b/server/test/e2e/smoke.spec.ts @@ -0,0 +1,30 @@ +import { test, expect } from "@playwright/test"; + +// Smoke spec — exists to unblock CI. Playwright's test runner exits 1 when +// no tests match the testDir glob; this single spec keeps the green-rail +// stable for port/quality-assets. Phase 2 (separate PR) adds the real +// auth-flow + game-library E2E suite (see hyperplan bundle I3 — desktop +// is out of scope, web is the target). +// +// What this covers: +// - Nuxt SSR Nitro stack reachable (port 4000, dev server) +// - Root route resolves to either 200 (auth'd landing) or 3xx redirect +// to /auth/login — the auth-gated baseline. No DB deps required +// for routing itself; DB deps hit the integration suite, not smoke. +test("app root loads or redirects to /auth/login", async ({ page }) => { + const response = await page.goto("/"); + expect(response, "navigated to / should produce a response").not.toBeNull(); + const status = response!.status(); + expect(status, "expected 2xx / 3xx from root route").toBeGreaterThanOrEqual( + 200, + ); + expect(status, "expected < 400 from root route").toBeLessThan(400); + // After load, URL should be either root (auth'd) or /auth/login (unauth'd). + const finalUrl = page.url(); + const isRoot = new URL(finalUrl).pathname === "/"; + const isLogin = new URL(finalUrl).pathname.startsWith("/auth"); + expect( + isRoot || isLogin, + `expected root or /auth/* path, got ${finalUrl}`, + ).toBe(true); +}); diff --git a/server/test/setup.ts b/server/test/setup.ts new file mode 100644 index 00000000..99614979 --- /dev/null +++ b/server/test/setup.ts @@ -0,0 +1,11 @@ +// Vitest setup file — referenced by vitest.config.ts as setupFiles. +// Exists to unblock `pnpm --filter drop test` even when there are zero +// test files in the repo (Vitest still loads setup before --passWithNoTests). +// +// Sets DATABASE_URL to the docker-compose.test.yml postgres (port 5433) +// so any Phase-2 test that hits Prisma has a datasource to connect to. +// Vitest's env-isolation: this file runs per-worker due to `pool: "forks"`. + +process.env.DATABASE_URL ??= + "postgresql://drop_test:drop_test@localhost:5433/drop_test"; +process.env.NODE_ENV ??= "test"; diff --git a/server/vitest.config.ts b/server/vitest.config.ts new file mode 100644 index 00000000..e106b986 --- /dev/null +++ b/server/vitest.config.ts @@ -0,0 +1,33 @@ +import { defineVitestConfig } from "@nuxt/test-utils/config"; +import { fileURLToPath } from "node:url"; + +const serverDir = fileURLToPath(new URL(".", import.meta.url)); + +export default defineVitestConfig({ + resolve: { + alias: { + "~": serverDir, + "~/*": `${serverDir}*`, + }, + }, + test: { + environment: "nuxt", + globals: true, + include: ["**/*.test.ts", "**/*.test.tsx"], + // Nuxt env init is slow; default 5s is too short + testTimeout: 30000, + // Isolate Nuxt env + Prisma singleton per-worker to prevent cross-test state leak + pool: "forks", + coverage: { + provider: "v8", + reporter: ["text", "lcov"], + reportsDirectory: "./coverage", + // Scope to Nitro backend only. Excludes server/pages/, + // server/components/, server/composables/, server/layouts/ + // (frontend code that needs Playwright, not vitest). + include: ["server/server/**/*.ts"], + exclude: ["server/test/**", "server/**/*.test.ts"], + }, + setupFiles: ["./test/setup.ts"], + }, +}); diff --git a/sites/docs/.prettierrc.json b/sites/docs/.prettierrc.json new file mode 100644 index 00000000..92685b51 --- /dev/null +++ b/sites/docs/.prettierrc.json @@ -0,0 +1,8 @@ +{ + "semi": true, + "singleQuote": false, + "trailingComma": "all", + "tabWidth": 2, + "printWidth": 100, + "plugins": ["prettier-plugin-astro"] +} diff --git a/sites/promo/.prettierrc.json b/sites/promo/.prettierrc.json new file mode 100644 index 00000000..e8f91818 --- /dev/null +++ b/sites/promo/.prettierrc.json @@ -0,0 +1,8 @@ +{ + "semi": true, + "singleQuote": false, + "trailingComma": "all", + "tabWidth": 2, + "printWidth": 100, + "plugins": ["prettier-plugin-organize-imports", "prettier-plugin-tailwindcss"] +} diff --git a/sites/promo/prettier.config.js b/sites/promo/prettier.config.js index a9e1a7b1..cad30134 100644 --- a/sites/promo/prettier.config.js +++ b/sites/promo/prettier.config.js @@ -2,7 +2,7 @@ module.exports = { singleQuote: true, semi: false, - plugins: ['prettier-plugin-organize-imports', 'prettier-plugin-tailwindcss'], - tailwindFunctions: ['clsx'], - tailwindStylesheet: './src/styles/tailwind.css', -} + plugins: ["prettier-plugin-organize-imports", "prettier-plugin-tailwindcss"], + tailwindFunctions: ["clsx"], + tailwindStylesheet: "./src/styles/tailwind.css", +}; diff --git a/sonar-project.properties b/sonar-project.properties new file mode 100644 index 00000000..d7e4cd70 --- /dev/null +++ b/sonar-project.properties @@ -0,0 +1,39 @@ +# SonarCloud project — BillyOutlast / drop (main monolith) +sonar.projectKey=BillyOutlast_drop +sonar.organization=billyoutlast +sonar.projectName=BillyOutlast / drop +sonar.projectVersion=1.0 + +# Enable in-code issue resolution (SONAR-RESOLVE) +sonar.issues.issueResolution.enabled=true + +# Fail CI on quality gate failure +sonar.qualitygate.wait=true +sonar.qualitygate.timeout=600 + +# Paths +sonar.sources=. +sonar.exclusions=**/node_modules/**,**/dist/**,**/build/**,**/.next/**,**/target/**,**/coverage/**,**/__pycache__/**,**/.venv/**,**/*.d.ts,**/migrations/**,**/.nuxt/**,**/.output/**,**/test/**,**/tests/** + +# Files outside vitest coverage scope (Swift, Go, Prisma singleton). +# Excluded from coverage analysis only — still scanned for issues. +# `server/server/internal/db/database.ts` is excluded because it is the +# Prisma singleton (~3 lines), derives its type from the Prisma schema, +# and unit-testing it requires a live DB connection — exercised by the +# docker-compose integration tests in Phase 3, not by vitest coverage. +sonar.coverage.exclusions=**/*.swift,**/backend/**,**/server/server/internal/db/database.ts +sonar.newCode.exclusions=desktop/libs/appletrust/**,**/backend/**,**/server/server/internal/db/database.ts + +# JavaScript / TypeScript / Vue +sonar.javascript.file.suffixes=.js,.jsx,.cjs,.mjs,.vue +sonar.typescript.file.suffixes=.ts,.tsx +sonar.javascript.lcov.reportPaths=server/coverage/lcov.info + +# Rust native addons +sonar.rust.file.suffixes=.rs + +# Python utility scripts +sonar.python.file.suffixes=.py + +# Source encoding +sonar.sourceEncoding=UTF-8 diff --git a/vitest.config.ts b/vitest.config.ts new file mode 100644 index 00000000..b36cfd48 --- /dev/null +++ b/vitest.config.ts @@ -0,0 +1,7 @@ +import { defineConfig } from "vitest/config"; + +export default defineConfig({ + test: { + projects: ["./server"], + }, +});