diff --git a/.github/workflows/quality.yml b/.github/workflows/quality.yml index c1243fab..6a8772b1 100644 --- a/.github/workflows/quality.yml +++ b/.github/workflows/quality.yml @@ -24,7 +24,7 @@ jobs: cache: npm - name: Set up Bun - uses: oven-sh/setup-bun@v2 + uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2 - name: Install frontend dependencies run: npm ci --ignore-scripts @@ -71,3 +71,42 @@ jobs: name: grimoire-demo-dist path: dist/ if-no-files-found: error + + homebrew: + name: Homebrew tap and install smoke test + runs-on: macos-latest + env: + HOMEBREW_NO_AUTO_UPDATE: "1" + HOMEBREW_NO_ENV_HINTS: "1" + HOMEBREW_NO_INSTALL_CLEANUP: "1" + steps: + - name: Check out repository + uses: actions/checkout@v4 + + - name: Set up Node.js + uses: actions/setup-node@v4 + with: + node-version: 22 + cache: npm + + - name: Set up Bun + uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2 + + - name: Build candidate archive + run: | + npm ci --ignore-scripts + npm run package:release -- --platform macos + python3 - <<'PYTHON' + import json, pathlib, re + artifact = json.loads(pathlib.Path("release/release-manifest.json").read_text())["artifacts"][0] + formula = pathlib.Path("Formula/grimoire.rb") + text, count = re.subn(r'(sha256 ")[a-f0-9]{64}(")', lambda match: match[1] + artifact["sha256"] + match[2], formula.read_text(), count=1) + assert count == 1 + formula.write_text(text) + PYTHON + + - name: Validate local tap and install formula + env: + HOMEBREW_TEST_ARCHIVE_DIR: ${{ github.workspace }}/release + HOMEBREW_TEST_EXPECTED_VERSION: 1.2.0 + run: bash scripts/homebrew-smoke.sh diff --git a/.github/workflows/trivy.yml b/.github/workflows/trivy.yml new file mode 100644 index 00000000..42318b4c --- /dev/null +++ b/.github/workflows/trivy.yml @@ -0,0 +1,50 @@ +name: Trivy scan + +# Fork-only CI gate: this repo's purpose is to track upstream dependency bumps +# and verify them against Trivy before redeploying. Kept separate from +# quality.yml (upstream-owned) so upstream syncs never conflict here. + +on: + pull_request: + push: + branches: + - main + workflow_dispatch: {} + +permissions: + contents: read + +jobs: + fs-scan: + name: Trivy filesystem scan (dependencies) + runs-on: ubuntu-latest + steps: + - name: Check out repository + uses: actions/checkout@v4 + + - name: Trivy fs scan + uses: aquasecurity/trivy-action@ed142fd0673e97e23eac54620cfb913e5ce36c25 # v0.36.0 + with: + scan-type: fs + scan-ref: . + severity: HIGH,CRITICAL + ignore-unfixed: true + exit-code: "1" + + image-scan: + name: Build image and Trivy scan + runs-on: ubuntu-latest + steps: + - name: Check out repository + uses: actions/checkout@v4 + + - name: Build image + run: docker build -t grimoire:ci . + + - name: Trivy image scan + uses: aquasecurity/trivy-action@ed142fd0673e97e23eac54620cfb913e5ce36c25 # v0.36.0 + with: + image-ref: grimoire:ci + severity: HIGH,CRITICAL + ignore-unfixed: true + exit-code: "1" diff --git a/.github/workflows/upstream-sync.yml b/.github/workflows/upstream-sync.yml new file mode 100644 index 00000000..61275896 --- /dev/null +++ b/.github/workflows/upstream-sync.yml @@ -0,0 +1,73 @@ +name: Upstream sync check + +# Watches goniszewski/grimoire (the upstream this repo is forked from) and opens/ +# updates a PR whenever upstream/main gets commits this fork doesn't have yet. +# The PR is a signal, not an auto-merge: reconcile it manually following +# CATCHUP-v1.2.md (re-apply the local Trivy CVE patch branch on top). + +on: + schedule: + - cron: "0 7 * * 1" # every Monday 07:00 UTC + workflow_dispatch: {} + +permissions: + contents: write + pull-requests: write + +jobs: + sync: + if: github.repository == 'bbossa/grimoire' + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + ref: main + + - name: Configure git identity + run: | + git config user.name "github-actions[bot]" + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + + - name: Fetch upstream + run: | + git remote add upstream https://github.com/goniszewski/grimoire.git + git fetch upstream main --tags + + - name: Check for new upstream commits + id: diff + run: | + UPSTREAM_SHA=$(git rev-parse upstream/main) + AHEAD=$(git rev-list --count origin/main..upstream/main) + echo "upstream_sha=$UPSTREAM_SHA" >> "$GITHUB_OUTPUT" + echo "ahead=$AHEAD" >> "$GITHUB_OUTPUT" + echo "$AHEAD commit(s) on upstream/main not yet in main (upstream @ $UPSTREAM_SHA)" + + - name: Update sync branch and PR + if: steps.diff.outputs.ahead != '0' + env: + GH_TOKEN: ${{ github.token }} + AHEAD: ${{ steps.diff.outputs.ahead }} + UPSTREAM_SHA: ${{ steps.diff.outputs.upstream_sha }} + run: | + git branch -f sync/upstream-main upstream/main + git push origin sync/upstream-main --force + + BODY="upstream/main (goniszewski/grimoire) is ${AHEAD} commit(s) ahead of main, at \`${UPSTREAM_SHA}\`. + + Do not merge this PR directly: it is a notification branch, not a reconciled one. Follow \`CATCHUP-v1.2.md\` to merge upstream/main and re-apply the local Trivy CVE patch branch (Dockerfile \`apt-get upgrade\`, dependency overrides) on top, then re-scan with Trivy before redeploying." + + if gh pr list --head sync/upstream-main --state open --json number -q '.[0].number' | grep -q .; then + echo "Open PR already exists for sync/upstream-main; branch push above updated it." + else + gh pr create \ + --base main \ + --head sync/upstream-main \ + --title "sync: upstream is ${AHEAD} commit(s) ahead" \ + --body "$BODY" + fi + + - name: Nothing to sync + if: steps.diff.outputs.ahead == '0' + run: echo "main is up to date with upstream/main, nothing to do." diff --git a/API.md b/API.md index f7933f8b..255638f3 100644 --- a/API.md +++ b/API.md @@ -2401,6 +2401,8 @@ Request body: | `category_id` | string \| null | no | Existing category ID to assign | | `category` | string | no | Root category name to resolve or create when category_id is omitted | | `notes` | string \| null | no | Personal notes, or null to leave empty | +| `is_pinned` | boolean | no | Whether the new bookmark should be pinned | +| `read_later` | boolean | no | Whether the new bookmark should be added to Read Later | | `source` | CaptureSource | no | | | `source.client` | string \| null | no | Optional local integration client label | | `source.source_url` | string \| null | no | Optional public HTTP or HTTPS page/context URL | @@ -2481,6 +2483,43 @@ Content-Type: application/json } ``` +#### GET /integrations/browser/v1/capabilities + +Negotiate the packaged browser extension capture protocol. + +Requires a managed integration bearer token. Packaged Chrome and Firefox clients use this endpoint to distinguish current Grimoire from the legacy account API before sending capture data. The response describes stable paths and enforced field limits; clients must not infer compatibility from the daemon marketing version alone. + +Responses: + +| Status | Content type | Schema | Description | +|---|---|---|---| +| `200` | application/json | `BrowserIntegrationCapabilitiesResponse` | Supported browser integration capabilities | +| `401` | application/problem+json | `ProblemDetails` | Missing, invalid, rotated, or revoked integration token | + +Examples: + +**Negotiate browser capture support** + +Request: + +```bash +curl http://127.0.0.1:3210/integrations/browser/v1/capabilities \ + -H "Authorization: Bearer limp_it_example" +``` + +#### GET /integrations/browser/v1/taxonomy + +List the categories and tags available to the packaged browser extension. + +Requires a managed integration bearer token. This route keeps extension-origin reads inside the versioned browser integration surface instead of exposing unrelated local-library GET routes. + +Responses: + +| Status | Content type | Schema | Description | +|---|---|---|---| +| `200` | application/json | `BrowserIntegrationTaxonomyResponse` | Browser capture categories and tags | +| `401` | application/problem+json | `ProblemDetails` | Missing, invalid, rotated, or revoked integration token | + #### GET /capture/bookmarklet Bookmarklet capture bridge page. @@ -3138,6 +3177,8 @@ Protected one-click capture request for explicit local integrations | `category_id` | string \| null | no | Existing category ID to assign | | `category` | string | no | Root category name to resolve or create when category_id is omitted | | `notes` | string \| null | no | Personal notes, or null to leave empty | +| `is_pinned` | boolean | no | Whether the new bookmark should be pinned | +| `read_later` | boolean | no | Whether the new bookmark should be added to Read Later | | `source` | CaptureSource | no | | | `source.client` | string \| null | no | Optional local integration client label | | `source.source_url` | string \| null | no | Optional public HTTP or HTTPS page/context URL | @@ -4518,6 +4559,70 @@ Response data | `uptime` | integer | yes | Process uptime in milliseconds | | `queueSize` | integer | yes | Queued background jobs | +### BrowserIntegrationCapabilities + +Authenticated capabilities advertised to packaged browser extensions + +| Field | Type | Required | Description | +|---|---|---:|---| +| `protocol` | "grimoire-browser-capture" | yes | Stable browser integration protocol identifier | +| `protocol_version` | integer | yes | Browser integration protocol version | +| `grimoire_version` | string | yes | Daemon package version | +| `endpoints` | object | yes | | +| `endpoints.capture` | string | yes | Authenticated bookmark capture path | +| `endpoints.taxonomy` | string | yes | Authenticated category and tag discovery path | +| `capture_fields` | object | no | Optional capture fields supported by this daemon | +| `capture_fields.is_pinned` | boolean | yes | Capture accepts an initial pinned state | +| `capture_fields.read_later` | boolean | yes | Capture accepts an initial Read Later state | +| `limits` | object | yes | | +| `limits.request_bytes` | integer | yes | Maximum capture request bytes | +| `limits.title_characters` | integer | yes | Maximum title characters | +| `limits.notes_characters` | integer | yes | Maximum notes characters | +| `limits.selected_text_characters` | integer | yes | Maximum selected text characters | +| `limits.tag_characters` | integer | yes | Maximum characters per tag | + +### BrowserIntegrationCapabilitiesResponse + +Browser integration capability response + +| Field | Type | Required | Description | +|---|---|---:|---| +| `data` | BrowserIntegrationCapabilities | yes | | +| `data.protocol` | "grimoire-browser-capture" | yes | Stable browser integration protocol identifier | +| `data.protocol_version` | integer | yes | Browser integration protocol version | +| `data.grimoire_version` | string | yes | Daemon package version | +| `data.endpoints` | object | yes | | +| `data.endpoints.capture` | string | yes | Authenticated bookmark capture path | +| `data.endpoints.taxonomy` | string | yes | Authenticated category and tag discovery path | +| `data.capture_fields` | object | no | Optional capture fields supported by this daemon | +| `data.capture_fields.is_pinned` | boolean | yes | Capture accepts an initial pinned state | +| `data.capture_fields.read_later` | boolean | yes | Capture accepts an initial Read Later state | +| `data.limits` | object | yes | | +| `data.limits.request_bytes` | integer | yes | Maximum capture request bytes | +| `data.limits.title_characters` | integer | yes | Maximum title characters | +| `data.limits.notes_characters` | integer | yes | Maximum notes characters | +| `data.limits.selected_text_characters` | integer | yes | Maximum selected text characters | +| `data.limits.tag_characters` | integer | yes | Maximum characters per tag | + +### BrowserIntegrationTaxonomy + +Authenticated taxonomy subset used by the browser extension + +| Field | Type | Required | Description | +|---|---|---:|---| +| `categories` | array | yes | Category tree | +| `tags` | array | yes | Tags | + +### BrowserIntegrationTaxonomyResponse + +Browser integration taxonomy response + +| Field | Type | Required | Description | +|---|---|---:|---| +| `data` | BrowserIntegrationTaxonomy | yes | | +| `data.categories` | array | yes | Category tree | +| `data.tags` | array | yes | Tags | + ### Diagnostics Redacted local diagnostics payload for user-shared support bundles diff --git a/CATCHUP-v1.2.md b/CATCHUP-v1.2.md new file mode 100644 index 00000000..b0bd03b9 --- /dev/null +++ b/CATCHUP-v1.2.md @@ -0,0 +1,164 @@ +# Rattrapage upstream v1.2 — fork `bbossa/grimoire` + +Contexte : ce dépôt est un fork de [`goniszewski/grimoire`](https://github.com/goniszewski/grimoire), +utilisé ici pour appliquer des correctifs de vulnérabilités détectées par Trivy avant +redéploiement sur le NAS. Le upstream a publié la **v1.2** ; ce fork est encore sur +`main` = **1.1.0** (`package.json` / `daemon/package.json`). + +État constaté au moment de la rédaction de ce fichier : + +- Un seul remote configuré : `origin` → `git@github.com:bbossa/grimoire.git` (le fork). + **Pas de remote `upstream`** pointant vers `goniszewski/grimoire` — à ajouter (étape 1). +- Branche `main` : propre, à jour avec `origin/main` (dernier commit `10e68e3`). +- Branche `fix/security-cve-2026-09` (sur `origin`, **non fusionnée dans `main`**) : + contient 1 commit local, `fbc3f7f — fix: patch Debian base image and Node dependency CVEs`, + qui corrige des CVE remontées par Trivy : + - `apt-get upgrade` ajouté dans le `Dockerfile` (stage runtime, CVE OS Debian) + - overrides npm/bun : `fast-uri@^3.1.3`, `fast-xml-builder@^1.1.7`, + `fast-xml-parser@^5.5.6`, `ip-address@^10.3.1`, `path-to-regexp@^8.4.0` + - `hono` remonté à `^4.12.25` + +Objectif : intégrer la v1.2 upstream **sans perdre** le patch CVE local, revalider avec +Trivy, puis redéployer sur le NAS. + +### Diagnostic déjà fait (remote `upstream` ajouté et fetché) + +- `upstream/main` est 20 commits devant `main` (browser companion TASK-138, Homebrew/ + renommage CLI, fix recherche, `fix(deps): remediate release dependency advisories`, + préparation release 1.2.0). +- `hono`, `fast-uri`, `ip-address`, `path-to-regexp` sont déjà bumpés par upstream à des + versions **≥** celles de nos overrides locaux → **à supprimer** de + `daemon/package.json` après le merge (upstream couvre déjà ces CVE). +- `fast-xml-builder` / `fast-xml-parser` **ont disparu de l'arbre de dépendances** en + v1.2.0 → override devenu inutile, **à supprimer**. +- Le `Dockerfile` n'a **pas bougé** entre `main` et `v1.2.0` : le correctif + `apt-get upgrade` (CVE OS Debian) du commit `fbc3f7f` reste nécessaire, à conserver + tel quel après le merge. +- v1.2.0 ajoute ses propres overrides (`@hono/node-server`, `body-parser`, `qs`) — à + garder. +- Changements sensibles à retester après merge : `react-router-dom` 6→7, `vite` 5→6, + `vitest` 3→4. + +--- + +## 1. Ajouter le remote upstream (une seule fois) — ✅ déjà fait + +```bash +git remote add upstream https://github.com/goniszewski/grimoire.git +git remote -v # vérifier que origin = bbossa/grimoire et upstream = goniszewski/grimoire +``` + +## 2. Récupérer l'historique upstream et mesurer le retard + +```bash +git fetch upstream --tags +git log --oneline main..upstream/main # commits upstream absents de main +git tag -l 'v1.2*' # confirmer le tag de la 1.2 +git log --oneline main..upstream/main -- Dockerfile daemon/package.json daemon/bun.lock +``` + +Regarder en particulier si upstream a déjà bougé les mêmes dépendances +(`hono`, `fast-uri`, `fast-xml-parser`, `ip-address`, `path-to-regexp`) ou touché le +`Dockerfile` runtime stage : ça donne une idée des conflits à attendre à l'étape 4. + +## 3. Mettre à jour `main` depuis upstream + +Travailler sur une branche dédiée plutôt que directement sur `main` : + +```bash +git checkout -b sync/upstream-v1.2 main +git merge upstream/main --no-edit +``` + +En cas de conflits, les résoudre normalement (`git status`, éditer, `git add`, +`git merge --continue`). Les fichiers les plus probables : `daemon/package.json`, +`daemon/bun.lock`, `Dockerfile`, `CHANGELOG.md`, `package.json`. + +## 4. Réappliquer le correctif CVE local par-dessus la v1.2 + +```bash +git merge origin/fix/security-cve-2026-09 --no-edit +``` + +- Si upstream a déjà corrigé une des CVE listées ci-dessus avec une version égale ou + supérieure à celle du patch local, garder la version upstream (plus récente) et + laisser tomber l'override correspondant dans `daemon/package.json`. +- Si upstream n'a pas encore corrigé une CVE, garder l'override local. +- Vérifier `Dockerfile` : la ligne `apt-get update && apt-get upgrade -y && apt-get install -y` + doit être présente dans le stage runtime après le merge (upstream aurait pu la + réécrire sans le `upgrade -y`). + +## 5. Réinstaller les dépendances et vérifier le lockfile + +```bash +bun install --frozen-lockfile # à la racine +cd daemon && bun install --frozen-lockfile && cd .. +git diff --stat # s'assurer que bun.lock reflète bien le merge +``` + +## 6. Revalider avec Trivy avant de builder l'image finale + +```bash +# scan du filesystem (dépendances Node/Bun) +trivy fs --severity HIGH,CRITICAL . + +# build de l'image candidate puis scan de l'image +docker build -t grimoire:v1.2-catchup . +trivy image --severity HIGH,CRITICAL grimoire:v1.2-catchup +``` + +Objectif : confirmer que les CVE du commit `fbc3f7f` restent corrigées et qu'aucune +nouvelle vulnérabilité n'a été introduite par la v1.2 upstream. + +## 7. Tests + +```bash +bun run lint +bun test # ou la commande de test spécifique au projet, cf. CONTRIBUTING.md +``` + +## 8. Finaliser sur `main` et pousser sur le fork + +```bash +git checkout main +git merge sync/upstream-v1.2 --no-edit +git push origin main +git branch -d sync/upstream-v1.2 +# la branche fix/security-cve-2026-09 est maintenant fusionnée : possibilité de la +# supprimer côté origin une fois le déploiement validé +git push origin --delete fix/security-cve-2026-09 +``` + +## 9. Redéploiement sur le NAS + +```bash +# sur le NAS (ou poste qui buide l'image utilisée par le NAS) +git pull origin main +docker compose build --no-cache +docker compose up -d +docker compose ps +curl -f http://localhost:3210/health +docker compose logs -f grimoire # vérifier l'absence d'erreurs au démarrage +``` + +Après redémarrage, relancer un scan Trivy sur l'image effectivement déployée pour +clore la boucle : + +```bash +trivy image --severity HIGH,CRITICAL $(docker inspect --format='{{.Image}}' grimoire) +``` + +--- + +## Points de vigilance + +- **Ordre merge upstream puis merge branche CVE** (étapes 3 puis 4) plutôt que l'inverse : + ça évite de re-résoudre les mêmes conflits deux fois si upstream a touché les mêmes + fichiers. +- Si `bun.lock` génère trop de conflits texte, le régénérer après resolution des + `package.json` (`rm daemon/bun.lock && cd daemon && bun install`) plutôt que de + merger le lockfile ligne à ligne. +- Vérifier `CHANGELOG.md` et les numéros de version (`package.json`, + `daemon/package.json`) sont bien alignés sur 1.2.0 après le merge upstream. +- Garder trace des overrides CVE encore nécessaires après la 1.2 dans un commit dédié + (comme `fbc3f7f`) pour faciliter le prochain rattrapage. diff --git a/CHANGELOG.md b/CHANGELOG.md index f78d2e7f..3344629d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,33 @@ All notable changes to Grimoire will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). +## [1.2.0] - 2026-09-17 + +### Added +- Homebrew installation and managed service support, with `grimoire` as the + primary CLI and `littleimp` retained as a compatibility alias. +- Versioned Companion protocol discovery and capability negotiation for official + browser extensions. +- Authenticated single-page and bulk-tab capture with editable titles, notes, + categories, tags, selected text, pinning, and Read Later state. +- Managed integration tokens for Companion, bookmarklets, MCP clients, and + other supported local integrations. + +### Changed +- Browser Integration settings now explain how named integration tokens are + shared across supported clients and recommend separate tokens for independent + revocation. + +### Fixed +- Search defaults to relevance instead of inheriting the library date sort, + restoring meaningful ordering for AI and mixed search. Explicit search sorts + remain available without changing the saved library order. +- Model connection tests can be retried after a failed attempt in Settings. +- Native self-updates are blocked for Homebrew-managed installations; use + `brew upgrade grimoire` instead. +- Opening Browser Integration settings from an extension now loads the SPA + instead of returning the JSON settings endpoint. + ## [1.1.0] - 2026-08-12 ### Added diff --git a/Dockerfile b/Dockerfile index 24514614..a05e4070 100644 --- a/Dockerfile +++ b/Dockerfile @@ -27,7 +27,7 @@ FROM oven/bun:1-slim AS runtime RUN groupadd -r littleimp && useradd -r -g littleimp littleimp # Install necessary system dependencies -RUN apt-get update && apt-get install -y \ +RUN apt-get update && apt-get upgrade -y && apt-get install -y \ curl \ ca-certificates \ && rm -rf /var/lib/apt/lists/* diff --git a/Formula/grimoire.rb b/Formula/grimoire.rb index 93b4cb39..5d271506 100644 --- a/Formula/grimoire.rb +++ b/Formula/grimoire.rb @@ -3,25 +3,38 @@ class Grimoire < Formula homepage "https://github.com/goniszewski/grimoire" license "MIT" - depends_on "oven-sh/bun/bun" + depends_on "bun" if OS.mac? - url "https://github.com/goniszewski/grimoire/releases/download/v1.1.0/little-imp-1.1.0-macos.tar.gz" - sha256 "c68bc963602a79631c76df223b9cc4a3709a0d382c0b117288f27c72da96c88f" + url "https://github.com/goniszewski/grimoire/releases/download/v1.2.0/little-imp-1.2.0-macos.tar.gz" + sha256 "5b99fc6c4d92cd4516b64c1651e847905457775112859eae6bddee40d132635b" elsif OS.linux? - url "https://github.com/goniszewski/grimoire/releases/download/v1.1.0/little-imp-1.1.0-linux.tar.gz" - sha256 "50429c64e2befeca1daca6d44a95d74f755f7be4a16ed869d68a80007809d340" + url "https://github.com/goniszewski/grimoire/releases/download/v1.2.0/little-imp-1.2.0-linux.tar.gz" + sha256 "5217a85217518ccb1e3d2e594b5e85194dccf5fa32ddd673262553f01e70e485" end def install libexec.install "daemon", "dist", "bin", "README.md", "VERSION", "RELEASE.json", "CHECKSUMS.sha256", "SIGNING.md" - system Formula["oven-sh/bun/bun"].opt_bin/"bun", "install", "--production", "--cwd", libexec/"daemon" + bun = formula_opt_bin("bun")/"bun" + system bun, "install", "--production", "--frozen-lockfile", "--ignore-scripts", "--cwd", libexec/"daemon" - (bin/"littleimp").write <<~EOS + cli_wrapper = <<~EOS #!/bin/bash set -euo pipefail - exec "#{Formula["oven-sh/bun/bun"].opt_bin}/bun" "#{opt_libexec}/daemon/src/cli.ts" "$@" + # Published archives may predate the CLI's package-manager guard. + case "${1:-} ${2:-}" in + "update install"|"update upgrade") + echo 'Homebrew-managed installations are updated with `brew upgrade grimoire`.' >&2 + exit 2 + ;; + esac + export LITTLEIMP_PACKAGE_MANAGER="homebrew" + exec "#{bun}" "#{opt_libexec}/daemon/src/cli.ts" "$@" EOS + (bin/"grimoire").write cli_wrapper + (bin/"littleimp").write cli_wrapper + (bin/"grimoire").chmod 0555 + (bin/"littleimp").chmod 0555 (bin/"littleimpd").write <<~EOS #!/bin/bash @@ -33,24 +46,34 @@ def install export LOG_FORMAT="${LOG_FORMAT:-json}" mkdir -p "${DATA_DIR}/logs" cd "#{opt_libexec}/daemon" - exec "#{Formula["oven-sh/bun/bun"].opt_bin}/bun" run "#{opt_libexec}/daemon/src/index.ts" + exec "#{bun}" run "#{opt_libexec}/daemon/src/index.ts" EOS + (bin/"littleimpd").chmod 0555 end - def post_install - (var/"little-imp/logs").mkpath + post_install_steps do + mkdir_p "little-imp/logs", base: :var + unless_path_exists "little-imp/.env", base: :var do + write_file "little-imp/.env", <<~EOS, base: :var + HOST=127.0.0.1 + PORT=3210 + DATA_DIR={{var}}/little-imp + NODE_ENV=production + LOG_FORMAT=json + EOS + set_permissions "little-imp/.env", "0600", base: :var, recursive: false + end + end - env_path = var/"little-imp/.env" - return if env_path.exist? + def caveats + <<~EOS + Start the Grimoire daemon with: + brew services start grimoire - env_path.write <<~EOS - HOST=127.0.0.1 - PORT=3210 - DATA_DIR=#{var}/little-imp - NODE_ENV=production - LOG_FORMAT=json + Use `brew upgrade grimoire` to update this Homebrew installation. + Homebrew-managed data is stored under: + #{var}/little-imp EOS - chmod 0600, env_path end service do @@ -67,6 +90,12 @@ def post_install end test do - assert_match "littleimp #{version}", shell_output("#{bin}/littleimp --help") + update_actions = %w[install upgrade] + %w[grimoire littleimp].each do |command| + assert_match version.to_s, shell_output("#{bin}/#{command} --help") + update_actions.each do |action| + assert_match "brew upgrade grimoire", shell_output("#{bin}/#{command} update #{action} 2>&1", 2) + end + end end end diff --git a/Formula/release.json b/Formula/release.json new file mode 100644 index 00000000..4e2b9f68 --- /dev/null +++ b/Formula/release.json @@ -0,0 +1,15 @@ +{ + "version": "1.2.0", + "artifacts": [ + { + "platform": "macos", + "archive": "little-imp-1.2.0-macos.tar.gz", + "sha256": "5b99fc6c4d92cd4516b64c1651e847905457775112859eae6bddee40d132635b" + }, + { + "platform": "linux", + "archive": "little-imp-1.2.0-linux.tar.gz", + "sha256": "5217a85217518ccb1e3d2e594b5e85194dccf5fa32ddd673262553f01e70e485" + } + ] +} diff --git a/README.md b/README.md index d4efee75..cb556f9b 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@
[![Quality Gates](https://github.com/goniszewski/grimoire/actions/workflows/quality.yml/badge.svg?branch=main)](https://github.com/goniszewski/grimoire/actions/workflows/quality.yml) -![Release target](https://img.shields.io/badge/release-1.1.0-7c3aed) +![Release target](https://img.shields.io/badge/release-1.2.0-7c3aed) ![Bun 1.x](https://img.shields.io/badge/Bun-1.x-black) [![License: MIT](https://img.shields.io/badge/license-MIT-green.svg)](./LICENSE) @@ -15,9 +15,13 @@ Grimoire is a local-first bookmark manager for people who save technical resourc > [!NOTE] > The current Grimoire application is a complete rewrite — a fresh start for the project. The legacy Grimoire (v0.5.x, SvelteKit-based) is preserved on the [`legacy/v0.x`](https://github.com/goniszewski/grimoire/tree/legacy/v0.x) branch. -> Coming from v0.5.x? Grimoire 1.1.0 includes an **experimental** `littleimp migrate` tool for v0.5 SQLite data. See the [migration guide](./docs/migration.md) before applying it. +> Coming from v0.5.x? Grimoire 1.1.0 includes an **experimental** `grimoire migrate` tool for v0.5 SQLite data. See the [migration guide](./docs/migration.md) before applying it. > Everything remains **local-first**, **private**, and **100% open source** under the MIT license. +The current source and repackaged builds use `grimoire` as the primary CLI +name; `littleimp` remains a compatibility alias. The already-published v1.1.0 +archive predates this rename and exposes `littleimp` when used directly. + ## Contents - [Screenshots](#screenshots) @@ -152,7 +156,8 @@ bookmark usable and expose retry/reprocess controls. The source installer copies daemon files, installs production dependencies, builds the frontend, writes a default config, registers the user service, and -starts the daemon. +starts the daemon. It installs `grimoire` under `~/.local/bin` and keeps +`littleimp` there as a compatibility alias. ```sh cd daemon @@ -180,11 +185,30 @@ cd daemon ./install.sh --uninstall --purge ``` -### Homebrew (pending live validation) +### Homebrew + +Grimoire v1.2.0 is available through Homebrew. The main Grimoire repository +serves as the tap; the explicit URL avoids requiring a separate +`homebrew-grimoire` repository. Install it with: + +Current Homebrew releases require explicit trust for non-official taps, so +trust only this formula: + +```sh +brew trust --formula goniszewski/grimoire/grimoire +brew tap goniszewski/grimoire https://github.com/goniszewski/grimoire.git +brew install grimoire +brew services start grimoire +``` + +Use `brew upgrade grimoire` for Homebrew upgrades. Homebrew-managed data is +kept under `$(brew --prefix)/var/little-imp`; it is separate from the native +`~/.local/share/littleimp` directory and is not migrated automatically. -The repository includes a Homebrew formula, but public install, service -lifecycle, and data-preservation checks have not passed against release assets. -It is not a supported installation path yet. +The public macOS Homebrew path has passed install, service startup, reinstall, +and uninstall checks with data preservation. Upgrading from v1.1 to v1.2 was +also validated. Linux Homebrew remains unverified; use the native Linux archive +or Docker Compose there. ## Data, Privacy, And Security @@ -236,8 +260,9 @@ diagnostics, and portable settings backups. ## Backups And Restore -Settings and the packaged `littleimp` CLI can create, verify, encrypt, and -restore local backup snapshots. Each normal snapshot contains: +Settings and the packaged `grimoire` CLI can create, verify, encrypt, and +restore local backup snapshots. The legacy `littleimp` command remains +available as a compatibility alias. Each normal snapshot contains: - `snapshot.db` - `manifest.json` @@ -247,20 +272,20 @@ restore local backup snapshots. Each normal snapshot contains: CLI examples: ```sh -littleimp backup create -littleimp backup list -littleimp backup verify --file ~/.local/share/littleimp/backups/BACKUP_NAME -littleimp backup restore BACKUP_NAME --yes +grimoire backup create +grimoire backup list +grimoire backup verify --file ~/.local/share/littleimp/backups/BACKUP_NAME +grimoire backup restore BACKUP_NAME --yes ``` Encrypted package examples: ```sh LITTLEIMP_BACKUP_PASSWORD='use-a-long-unique-password' \ - littleimp backup create --encrypt --output ~/Desktop/little-imp-backup.enc + grimoire backup create --encrypt --output ~/Desktop/little-imp-backup.enc LITTLEIMP_BACKUP_PASSWORD='use-a-long-unique-password' \ - littleimp backup verify --encrypted --file ~/Desktop/little-imp-backup.enc + grimoire backup verify --encrypted --file ~/Desktop/little-imp-backup.enc ``` Restores verify checksums, create a rollback directory, replace local data, and @@ -273,9 +298,9 @@ tags, and available local media into Grimoire 1.x. Start with `inspect`, review an `apply --dry-run`, then apply with `--yes`: ```sh -littleimp migrate inspect --data-dir /path/to/grimoire/data -littleimp migrate apply --data-dir /path/to/grimoire/data --owner YOUR_USERNAME --dry-run -littleimp migrate apply --data-dir /path/to/grimoire/data --owner YOUR_USERNAME --yes +grimoire migrate inspect --data-dir /path/to/grimoire/data +grimoire migrate apply --data-dir /path/to/grimoire/data --owner YOUR_USERNAME --dry-run +grimoire migrate apply --data-dir /path/to/grimoire/data --owner YOUR_USERNAME --yes ``` See the complete [migration guide](./docs/migration.md) for archive inputs, @@ -343,6 +368,16 @@ npm run test:e2e npm run build ``` +Homebrew-specific validation requires Homebrew and performs a disposable +install through a local tap: + +```sh +npm run test:homebrew +``` + +`npm run test:homebrew:published` exercises +the one-argument `brew tap goniszewski/grimoire` path. + Full local quality gate: ```sh diff --git a/SECURITY.md b/SECURITY.md index 4f40b253..57aca137 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -112,10 +112,12 @@ other sites cannot frame the UI. before extraction. - Detached `.asc` signatures are verified when published or explicitly provided. -- The packaged `littleimp update install` flow verifies checksums, verifies +- The packaged `grimoire update install` flow verifies checksums, verifies optional detached signatures, rejects unsafe archive layouts, runs the native installer in upgrade mode, and confirms `/health` reports the upgraded - version. + version. The legacy `littleimp` alias has the same behavior for native + packaged installs; Homebrew-managed installs are upgraded with + `brew upgrade grimoire`. - The Homebrew formula uses the same release archives and verifies their published SHA-256 checksums. diff --git a/bun.lock b/bun.lock index 3672bd3e..4e33421e 100644 --- a/bun.lock +++ b/bun.lock @@ -51,7 +51,7 @@ "react-hook-form": "^7.61.1", "react-markdown": "^10.1.0", "react-resizable-panels": "^2.1.9", - "react-router-dom": "^6.30.1", + "react-router-dom": "^7.18.3", "recharts": "^2.15.4", "sonner": "^1.7.4", "tailwind-merge": "^2.6.0", @@ -81,11 +81,14 @@ "tailwindcss": "^3.4.17", "typescript": "^5.8.3", "typescript-eslint": "^8.38.0", - "vite": "^5.4.19", - "vitest": "^3.2.4", + "vite": "^6.4.3", + "vitest": "^4.1.11", }, }, }, + "overrides": { + "lodash": "^4.18.0", + }, "packages": { "@adobe/css-tools": ["@adobe/css-tools@4.4.4", "", {}, "sha512-Elp+iwUx5rN5+Y8xLt5/GRoG20WGoDCQ/1Fb+1LiGtvwbDavuSk0jhD/eZdckHAuzcDzccnkv+rEjyWfRx18gg=="], @@ -105,51 +108,57 @@ "@dnd-kit/utilities": ["@dnd-kit/utilities@3.2.2", "", { "dependencies": { "tslib": "^2.0.0" }, "peerDependencies": { "react": ">=16.8.0" } }, "sha512-+MKAJEOfaBe5SmV6t34p80MMKhjvUz0vRrvVJbPT0WElzaOJ/1xs+D+KDv+tD/NE5ujfrChEcshd4fLn0wpiqg=="], - "@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.21.5", "", { "os": "aix", "cpu": "ppc64" }, "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ=="], + "@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.25.12", "", { "os": "aix", "cpu": "ppc64" }, "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA=="], + + "@esbuild/android-arm": ["@esbuild/android-arm@0.25.12", "", { "os": "android", "cpu": "arm" }, "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg=="], + + "@esbuild/android-arm64": ["@esbuild/android-arm64@0.25.12", "", { "os": "android", "cpu": "arm64" }, "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg=="], - "@esbuild/android-arm": ["@esbuild/android-arm@0.21.5", "", { "os": "android", "cpu": "arm" }, "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg=="], + "@esbuild/android-x64": ["@esbuild/android-x64@0.25.12", "", { "os": "android", "cpu": "x64" }, "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg=="], - "@esbuild/android-arm64": ["@esbuild/android-arm64@0.21.5", "", { "os": "android", "cpu": "arm64" }, "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A=="], + "@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.25.12", "", { "os": "darwin", "cpu": "arm64" }, "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg=="], - "@esbuild/android-x64": ["@esbuild/android-x64@0.21.5", "", { "os": "android", "cpu": "x64" }, "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA=="], + "@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.25.12", "", { "os": "darwin", "cpu": "x64" }, "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA=="], - "@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.21.5", "", { "os": "darwin", "cpu": "arm64" }, "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ=="], + "@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.25.12", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg=="], - "@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.21.5", "", { "os": "darwin", "cpu": "x64" }, "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw=="], + "@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.25.12", "", { "os": "freebsd", "cpu": "x64" }, "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ=="], - "@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.21.5", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g=="], + "@esbuild/linux-arm": ["@esbuild/linux-arm@0.25.12", "", { "os": "linux", "cpu": "arm" }, "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw=="], - "@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.21.5", "", { "os": "freebsd", "cpu": "x64" }, "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ=="], + "@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.25.12", "", { "os": "linux", "cpu": "arm64" }, "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ=="], - "@esbuild/linux-arm": ["@esbuild/linux-arm@0.21.5", "", { "os": "linux", "cpu": "arm" }, "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA=="], + "@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.25.12", "", { "os": "linux", "cpu": "ia32" }, "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA=="], - "@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.21.5", "", { "os": "linux", "cpu": "arm64" }, "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q=="], + "@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.25.12", "", { "os": "linux", "cpu": "none" }, "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng=="], - "@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.21.5", "", { "os": "linux", "cpu": "ia32" }, "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg=="], + "@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.25.12", "", { "os": "linux", "cpu": "none" }, "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw=="], - "@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.21.5", "", { "os": "linux", "cpu": "none" }, "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg=="], + "@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.25.12", "", { "os": "linux", "cpu": "ppc64" }, "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA=="], - "@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.21.5", "", { "os": "linux", "cpu": "none" }, "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg=="], + "@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.25.12", "", { "os": "linux", "cpu": "none" }, "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w=="], - "@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.21.5", "", { "os": "linux", "cpu": "ppc64" }, "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w=="], + "@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.25.12", "", { "os": "linux", "cpu": "s390x" }, "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg=="], - "@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.21.5", "", { "os": "linux", "cpu": "none" }, "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA=="], + "@esbuild/linux-x64": ["@esbuild/linux-x64@0.25.12", "", { "os": "linux", "cpu": "x64" }, "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw=="], - "@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.21.5", "", { "os": "linux", "cpu": "s390x" }, "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A=="], + "@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.25.12", "", { "os": "none", "cpu": "arm64" }, "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg=="], - "@esbuild/linux-x64": ["@esbuild/linux-x64@0.21.5", "", { "os": "linux", "cpu": "x64" }, "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ=="], + "@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.25.12", "", { "os": "none", "cpu": "x64" }, "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ=="], - "@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.21.5", "", { "os": "none", "cpu": "x64" }, "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg=="], + "@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.25.12", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A=="], - "@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.21.5", "", { "os": "openbsd", "cpu": "x64" }, "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow=="], + "@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.25.12", "", { "os": "openbsd", "cpu": "x64" }, "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw=="], - "@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.21.5", "", { "os": "sunos", "cpu": "x64" }, "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg=="], + "@esbuild/openharmony-arm64": ["@esbuild/openharmony-arm64@0.25.12", "", { "os": "none", "cpu": "arm64" }, "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg=="], - "@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.21.5", "", { "os": "win32", "cpu": "arm64" }, "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A=="], + "@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.25.12", "", { "os": "sunos", "cpu": "x64" }, "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w=="], - "@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.21.5", "", { "os": "win32", "cpu": "ia32" }, "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA=="], + "@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.25.12", "", { "os": "win32", "cpu": "arm64" }, "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg=="], - "@esbuild/win32-x64": ["@esbuild/win32-x64@0.21.5", "", { "os": "win32", "cpu": "x64" }, "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw=="], + "@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.25.12", "", { "os": "win32", "cpu": "ia32" }, "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ=="], + + "@esbuild/win32-x64": ["@esbuild/win32-x64@0.25.12", "", { "os": "win32", "cpu": "x64" }, "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA=="], "@eslint-community/eslint-utils": ["@eslint-community/eslint-utils@4.7.0", "", { "dependencies": { "eslint-visitor-keys": "^3.4.3" }, "peerDependencies": { "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, "sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw=="], @@ -199,12 +208,16 @@ "@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.25", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ=="], + "@napi-rs/lzma-linux-x64-gnu": ["@napi-rs/lzma-linux-x64-gnu@1.5.1", "", { "os": "linux", "cpu": "x64" }, "sha512-oTXEIha4SsuXdTA4Iyskj0kpdx2yVXdhd75c2v3xGrHFfVMsbhTPZU/nMPL4sWKo4pBHm3aucLaqGlF696dTyQ=="], + "@nodelib/fs.scandir": ["@nodelib/fs.scandir@2.1.5", "", { "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" } }, "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g=="], "@nodelib/fs.stat": ["@nodelib/fs.stat@2.0.5", "", {}, "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A=="], "@nodelib/fs.walk": ["@nodelib/fs.walk@1.2.8", "", { "dependencies": { "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" } }, "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg=="], + "@oxc-project/types": ["@oxc-project/types@0.150.0", "", {}, "sha512-rDS5/31E9HfPl/CIzGrn0DOlvBbXFseQ5URJ9sYMfstbKLD/c6Gm9vmRzRGDdAXyOIL4zmO37lc9RIwYqVruZw=="], + "@pkgjs/parseargs": ["@pkgjs/parseargs@0.11.0", "", {}, "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg=="], "@playwright/test": ["@playwright/test@1.58.2", "", { "dependencies": { "playwright": "1.58.2" }, "bin": { "playwright": "cli.js" } }, "sha512-akea+6bHYBBfA9uQqSYmlJXn61cTa+jbO87xVLCWbTqbWadRVmhxlXATaOjOgcBaWU4ePo0wB41KMFv3o35IXA=="], @@ -319,41 +332,89 @@ "@radix-ui/rect": ["@radix-ui/rect@1.1.1", "", {}, "sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw=="], - "@remix-run/router": ["@remix-run/router@1.23.0", "", {}, "sha512-O3rHJzAQKamUz1fvE0Qaw0xSFqsA/yafi2iqeE0pvdFtCO1viYx8QL6f3Ln/aCCTLxs68SLf0KPM9eSeM8yBnA=="], + "@rolldown/binding-android-arm-eabi": ["@rolldown/binding-android-arm-eabi@1.2.9", "", { "os": "android", "cpu": "arm" }, "sha512-tNISae1QEf/vkb3xkRcjV5SEdzPE97We5IVaa2Z8jSszQPZ8U60B/YCYpw4QI7VidYsBtKavczXf+DyDs9WGxw=="], + + "@rolldown/binding-android-arm64": ["@rolldown/binding-android-arm64@1.2.9", "", { "os": "android", "cpu": "arm64" }, "sha512-YC8YsI30o606GTZi0VyzYlsDKFP8W61i/QzayHDkLbNEz/IShqAmTa+hsJRj13xTHA0H+6fk4b2UmGn+Q/cMlg=="], + + "@rolldown/binding-darwin-arm64": ["@rolldown/binding-darwin-arm64@1.2.9", "", { "os": "darwin", "cpu": "arm64" }, "sha512-IwhlH3qK5urrY8hZiEgGkHKEFN901p/p2bjxCxJlr4GyNnF7wYpUvK+Y43uaRYuC4hpfjzbR3SJC3arX1jGvmw=="], + + "@rolldown/binding-darwin-x64": ["@rolldown/binding-darwin-x64@1.2.9", "", { "os": "darwin", "cpu": "x64" }, "sha512-XxpJfVzFh+jilRxIXUqcfYAYcunIc/XEzIizsOL1fcJee5Sf7H3mH8WlLmfHfluz5amqR88QQo9izKtmMlavAw=="], + + "@rolldown/binding-freebsd-x64": ["@rolldown/binding-freebsd-x64@1.2.9", "", { "os": "freebsd", "cpu": "x64" }, "sha512-kSfvhmgeWyfkbT3p/1s5vSgboogoah2zkm9fX2zjg2hHxSV7T4KhMWRUUaRk4OXNqoD3QAUeRqLcs1aZOK4U1g=="], + + "@rolldown/binding-linux-arm-gnueabihf": ["@rolldown/binding-linux-arm-gnueabihf@1.2.9", "", { "os": "linux", "cpu": "arm" }, "sha512-1RVzG17pxqbTfYLC352JlLt6kKLG+6Hr30n8DlIJqsnV5luUDd2Qdx9Ayw1Cabfyb1K9k0jXEZ7evxkRoT+uiw=="], + + "@rolldown/binding-linux-arm64-gnu": ["@rolldown/binding-linux-arm64-gnu@1.2.9", "", { "os": "linux", "cpu": "arm64" }, "sha512-BXqPvZ2drqVD+/Z8UpKwcs4Mp7grM+eGFku4CAEKrEtcbAsUpzREphK1sogCRZGreVPiMkiiBtw0n3TPteuqvw=="], + + "@rolldown/binding-linux-arm64-musl": ["@rolldown/binding-linux-arm64-musl@1.2.9", "", { "os": "linux", "cpu": "arm64" }, "sha512-11vWvo8YDwLzukt27J3aYDWU+gg2P7J+ZOmiJ0hkF5BXZDW7pVya7r40MXDy6ya0i9KamoENSVKIugvJNgFXIA=="], + + "@rolldown/binding-linux-ppc64-gnu": ["@rolldown/binding-linux-ppc64-gnu@1.2.9", "", { "os": "linux", "cpu": "ppc64" }, "sha512-a1tijMkdwsIARtc0F39ApURROkf3NwqinI6TOiSSWCTR7dT96dffNvMUtDHnq64wKNTIZOIlzKrFvvFUznJiyw=="], + + "@rolldown/binding-linux-s390x-gnu": ["@rolldown/binding-linux-s390x-gnu@1.2.9", "", { "os": "linux", "cpu": "s390x" }, "sha512-x6SQNdAvv4c3hWqTMaWuawzMX9myaCs/yEmlGsxJzkdClnHW7FbrjQuSiRDhuSYzEYoEMhsaJy9qHG/XNemJPQ=="], + + "@rolldown/binding-linux-x64-gnu": ["@rolldown/binding-linux-x64-gnu@1.2.9", "", { "os": "linux", "cpu": "x64" }, "sha512-9s0AZ8BFK5/n7B/TBoa2yJE3gI3KURrbXcPBlsAsvjU4VeJKgE90y1YtNxyEUIcHPQkg6/yfF3qihUrcM/Kf0Q=="], + + "@rolldown/binding-linux-x64-musl": ["@rolldown/binding-linux-x64-musl@1.2.9", "", { "os": "linux", "cpu": "x64" }, "sha512-P7VWAmV+WdJluH7ovnRGoiv2i8To7GAZ+kGzfGup635cyL7SyYl3lSUaA3Gp5THf0n/Co5EyEqb2zbqq+nMOHQ=="], + + "@rolldown/binding-openharmony-arm64": ["@rolldown/binding-openharmony-arm64@1.2.9", "", { "os": "none", "cpu": "arm64" }, "sha512-1qixtsE4BK8h+yS3BfmZ09UhA7O/N4IACva6YBr7EBvCJraByTuRcgOTaiA62Tm0vey3UcKXLOaoGHtYmNGEVg=="], + + "@rolldown/binding-win32-arm64-msvc": ["@rolldown/binding-win32-arm64-msvc@1.2.9", "", { "os": "win32", "cpu": "arm64" }, "sha512-ok8IQjcEPs1AKZfuEUznVBrJw+gK4soq+bx8b1X2XoMqVClarc1q5JDmVtWXY1xfr6ZuHTAsPXHTgTrqKTZeww=="], + + "@rolldown/binding-win32-x64-msvc": ["@rolldown/binding-win32-x64-msvc@1.2.9", "", { "os": "win32", "cpu": "x64" }, "sha512-Ip2mXoU0hM0boq3Rf+ekuT653OROSo6aSYcPT1VHE4q52KvyxgFkQgrgb/IEsxOuvQ2fZZbs8khJAyCEPM24/g=="], "@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.0-beta.27", "", {}, "sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA=="], - "@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.24.0", "", { "os": "android", "cpu": "arm" }, "sha512-Q6HJd7Y6xdB48x8ZNVDOqsbh2uByBhgK8PiQgPhwkIw/HC/YX5Ghq2mQY5sRMZWHb3VsFkWooUVOZHKr7DmDIA=="], + "@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.63.3", "", { "os": "android", "cpu": "arm" }, "sha512-w3Jnvi1ocaVm/c7yVPpfB98XeSRBMyzp6njL5MVVbGyXjpmUkN+s6Hp4t0PqhGCCaI1ZHMKXt/w0lA1RCaLVcw=="], + + "@rollup/rollup-android-arm64": ["@rollup/rollup-android-arm64@4.63.3", "", { "os": "android", "cpu": "arm64" }, "sha512-uI/ESiaIbbRYAEhzy8PCUWDp1hB0bjAqM06mW9flOoNO4Q8DQpeoREhBR5Hegfl+wpXiguyJv6XSPzEN7OxyHQ=="], + + "@rollup/rollup-darwin-arm64": ["@rollup/rollup-darwin-arm64@4.63.3", "", { "os": "darwin", "cpu": "arm64" }, "sha512-oxhrd1jmXLwWZ83eQYDXxuqRdkqkzrjR3JobKeuUyfdNZo11FuQIvqEOZhyIT7OBHxXoGslDDjN0cQcM6T0TqQ=="], + + "@rollup/rollup-darwin-x64": ["@rollup/rollup-darwin-x64@4.63.3", "", { "os": "darwin", "cpu": "x64" }, "sha512-7/YiIMghVE8DrxKvNdorAaJVdriOFgOIpdStnPx8ppx5zfTwC3jBCSEAIzB7JD5404m65THl6H93UTTVUvypmg=="], + + "@rollup/rollup-freebsd-arm64": ["@rollup/rollup-freebsd-arm64@4.63.3", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-GXFZRRoMAytaI5z6N3Zhfw0WL18Q0M8r95D5hlC4GqE/lGk8pbSJNUBoOWDfbm6dTciqHj2nU87tI5f6XhQiOg=="], + + "@rollup/rollup-freebsd-x64": ["@rollup/rollup-freebsd-x64@4.63.3", "", { "os": "freebsd", "cpu": "x64" }, "sha512-77W+8X3ddYgPxUpB8nZFQs2Mq+wc4HVlcSRtApXLjYBcnPMkttrSnU8VwKQjeWYhMsITHFs5cWBQ8vz1Q+5RHQ=="], - "@rollup/rollup-android-arm64": ["@rollup/rollup-android-arm64@4.24.0", "", { "os": "android", "cpu": "arm64" }, "sha512-ijLnS1qFId8xhKjT81uBHuuJp2lU4x2yxa4ctFPtG+MqEE6+C5f/+X/bStmxapgmwLwiL3ih122xv8kVARNAZA=="], + "@rollup/rollup-linux-arm-gnueabihf": ["@rollup/rollup-linux-arm-gnueabihf@4.63.3", "", { "os": "linux", "cpu": "arm" }, "sha512-FVkwK+iUC+mq+GipVK46rRVticfAPtvPUNlqlGXUDxdVk/UGjQiiiUVPUrEXdSpU2ufU0XxLGyTqDtBidDOVmg=="], - "@rollup/rollup-darwin-arm64": ["@rollup/rollup-darwin-arm64@4.24.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-bIv+X9xeSs1XCk6DVvkO+S/z8/2AMt/2lMqdQbMrmVpgFvXlmde9mLcbQpztXm1tajC3raFDqegsH18HQPMYtA=="], + "@rollup/rollup-linux-arm-musleabihf": ["@rollup/rollup-linux-arm-musleabihf@4.63.3", "", { "os": "linux", "cpu": "arm" }, "sha512-+aGU1t3398yQOVj1Bz8o3e+KtswxAPvO+mtxtNdfXYMkXIHu7XhhkCD7/DEH9q8tF8uhDnMWvfpUKI8y1sZJsg=="], - "@rollup/rollup-darwin-x64": ["@rollup/rollup-darwin-x64@4.24.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-X6/nOwoFN7RT2svEQWUsW/5C/fYMBe4fnLK9DQk4SX4mgVBiTA9h64kjUYPvGQ0F/9xwJ5U5UfTbl6BEjaQdBQ=="], + "@rollup/rollup-linux-arm64-gnu": ["@rollup/rollup-linux-arm64-gnu@4.63.3", "", { "os": "linux", "cpu": "arm64" }, "sha512-cR0kjpRXR2KJ2oQK8E2KTPtphs+b9hZ8IhTZubNryt/RsqgdOZBQ2Zq0q5UedtiIi0rs3jVhJh55RE1ZHUVGUA=="], - "@rollup/rollup-linux-arm-gnueabihf": ["@rollup/rollup-linux-arm-gnueabihf@4.24.0", "", { "os": "linux", "cpu": "arm" }, "sha512-0KXvIJQMOImLCVCz9uvvdPgfyWo93aHHp8ui3FrtOP57svqrF/roSSR5pjqL2hcMp0ljeGlU4q9o/rQaAQ3AYA=="], + "@rollup/rollup-linux-arm64-musl": ["@rollup/rollup-linux-arm64-musl@4.63.3", "", { "os": "linux", "cpu": "arm64" }, "sha512-y1RYi4Q3/9ByVWSSt9kX2ustE0B7kFYbJ6zZdVZVyqopZs3yhCTwRfrjIX4vezUJInma/Gs6BOFDJg7yZmJ0IQ=="], - "@rollup/rollup-linux-arm-musleabihf": ["@rollup/rollup-linux-arm-musleabihf@4.24.0", "", { "os": "linux", "cpu": "arm" }, "sha512-it2BW6kKFVh8xk/BnHfakEeoLPv8STIISekpoF+nBgWM4d55CZKc7T4Dx1pEbTnYm/xEKMgy1MNtYuoA8RFIWw=="], + "@rollup/rollup-linux-loong64-gnu": ["@rollup/rollup-linux-loong64-gnu@4.63.3", "", { "os": "linux", "cpu": "none" }, "sha512-DNhEA5viIj3Z5bZLE4z4oV8N5ozWqDwyt7T6KG7VdLDJ0nW+rNOYlphBl4/3HQkK75qipPLsVOfStHHOwN9WSg=="], - "@rollup/rollup-linux-arm64-gnu": ["@rollup/rollup-linux-arm64-gnu@4.24.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-i0xTLXjqap2eRfulFVlSnM5dEbTVque/3Pi4g2y7cxrs7+a9De42z4XxKLYJ7+OhE3IgxvfQM7vQc43bwTgPwA=="], + "@rollup/rollup-linux-loong64-musl": ["@rollup/rollup-linux-loong64-musl@4.63.3", "", { "os": "linux", "cpu": "none" }, "sha512-17gQCqrIpXBX2Cmi9/TygnVOqGbzsba/iaqcYSL8FY7lNugg+7AiYNs5c5nKWD+NRQha36Sa0CqkJqH4XVHwnQ=="], - "@rollup/rollup-linux-arm64-musl": ["@rollup/rollup-linux-arm64-musl@4.24.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-9E6MKUJhDuDh604Qco5yP/3qn3y7SLXYuiC0Rpr89aMScS2UAmK1wHP2b7KAa1nSjWJc/f/Lc0Wl1L47qjiyQw=="], + "@rollup/rollup-linux-ppc64-gnu": ["@rollup/rollup-linux-ppc64-gnu@4.63.3", "", { "os": "linux", "cpu": "ppc64" }, "sha512-6LwVnZRIyINpdku/yOcI8Tm9YqLmhHK5emmlOOnW9tO0SYEm1FmKPcsSAGp0NBlqR2P04xaND4jvN6sTHqhq8A=="], - "@rollup/rollup-linux-powerpc64le-gnu": ["@rollup/rollup-linux-powerpc64le-gnu@4.24.0", "", { "os": "linux", "cpu": "ppc64" }, "sha512-2XFFPJ2XMEiF5Zi2EBf4h73oR1V/lycirxZxHZNc93SqDN/IWhYYSYj8I9381ikUFXZrz2v7r2tOVk2NBwxrWw=="], + "@rollup/rollup-linux-ppc64-musl": ["@rollup/rollup-linux-ppc64-musl@4.63.3", "", { "os": "linux", "cpu": "ppc64" }, "sha512-xMUqkTXlEUtI/p5AAukMwBRr1enU3efsTeF+bskeFfk8t1C9rcC8sLREcZXmTfAXEbvRdJVSonVJez3TMlbR3w=="], - "@rollup/rollup-linux-riscv64-gnu": ["@rollup/rollup-linux-riscv64-gnu@4.24.0", "", { "os": "linux", "cpu": "none" }, "sha512-M3Dg4hlwuntUCdzU7KjYqbbd+BLq3JMAOhCKdBE3TcMGMZbKkDdJ5ivNdehOssMCIokNHFOsv7DO4rlEOfyKpg=="], + "@rollup/rollup-linux-riscv64-gnu": ["@rollup/rollup-linux-riscv64-gnu@4.63.3", "", { "os": "linux", "cpu": "none" }, "sha512-S3E94co9F9WRRqEaUoQZ38K1gCz6KiM+nL7/3ijq7fDGF3OznjS5TasgYITlvl27GQKtu4lOAOsr5MFwkijvOA=="], - "@rollup/rollup-linux-s390x-gnu": ["@rollup/rollup-linux-s390x-gnu@4.24.0", "", { "os": "linux", "cpu": "s390x" }, "sha512-mjBaoo4ocxJppTorZVKWFpy1bfFj9FeCMJqzlMQGjpNPY9JwQi7OuS1axzNIk0nMX6jSgy6ZURDZ2w0QW6D56g=="], + "@rollup/rollup-linux-riscv64-musl": ["@rollup/rollup-linux-riscv64-musl@4.63.3", "", { "os": "linux", "cpu": "none" }, "sha512-1QtRDwG42x5BJI3s9mxu5rEjDnfbSnk20HQ9/ylTAYnSwYwxMVb+Vgu34wzzTQ7ogqBybebgQNUDAvZVQ38DbA=="], - "@rollup/rollup-linux-x64-gnu": ["@rollup/rollup-linux-x64-gnu@4.24.0", "", { "os": "linux", "cpu": "x64" }, "sha512-ZXFk7M72R0YYFN5q13niV0B7G8/5dcQ9JDp8keJSfr3GoZeXEoMHP/HlvqROA3OMbMdfr19IjCeNAnPUG93b6A=="], + "@rollup/rollup-linux-s390x-gnu": ["@rollup/rollup-linux-s390x-gnu@4.63.3", "", { "os": "linux", "cpu": "s390x" }, "sha512-BQhejF6ZXOpxbngiNTP12GCGQeaDVL2QXGeBVViKIYzFHM5RKxTxwUMB1fr1BeNFphFMpnRqC5QSXFSa4z6UQw=="], - "@rollup/rollup-linux-x64-musl": ["@rollup/rollup-linux-x64-musl@4.24.0", "", { "os": "linux", "cpu": "x64" }, "sha512-w1i+L7kAXZNdYl+vFvzSZy8Y1arS7vMgIy8wusXJzRrPyof5LAb02KGr1PD2EkRcl73kHulIID0M501lN+vobQ=="], + "@rollup/rollup-linux-x64-gnu": ["@rollup/rollup-linux-x64-gnu@4.63.3", "", { "os": "linux", "cpu": "x64" }, "sha512-SXagRwnI2Wlwlitllu59UK/nGVbD1CKPcNqDplHwIC4BqJcpXFjD32d1R/RbuISa95HdQrZM3/7v4bKiowFaLA=="], - "@rollup/rollup-win32-arm64-msvc": ["@rollup/rollup-win32-arm64-msvc@4.24.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-VXBrnPWgBpVDCVY6XF3LEW0pOU51KbaHhccHw6AS6vBWIC60eqsH19DAeeObl+g8nKAz04QFdl/Cefta0xQtUQ=="], + "@rollup/rollup-linux-x64-musl": ["@rollup/rollup-linux-x64-musl@4.63.3", "", { "os": "linux", "cpu": "x64" }, "sha512-2IPozoEALRCziGqE8O9KMK60PMu5TS1huv4fwoeCexj+WjmcwFtX9CTOVbfXCUqcELAubEwRFPYlzb/WvwY2HQ=="], - "@rollup/rollup-win32-ia32-msvc": ["@rollup/rollup-win32-ia32-msvc@4.24.0", "", { "os": "win32", "cpu": "ia32" }, "sha512-xrNcGDU0OxVcPTH/8n/ShH4UevZxKIO6HJFK0e15XItZP2UcaiLFd5kiX7hJnqCbSztUF8Qot+JWBC/QXRPYWQ=="], + "@rollup/rollup-openbsd-x64": ["@rollup/rollup-openbsd-x64@4.63.3", "", { "os": "openbsd", "cpu": "x64" }, "sha512-AoxqosUHT9IX54hFn2TiN6A7d6ZKTtE6pd2bqWtqkkNJ6HJGaU6FRouGX8L1O7R/ZwsnCnpQrHzb4pDEx+UHRQ=="], - "@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.24.0", "", { "os": "win32", "cpu": "x64" }, "sha512-fbMkAF7fufku0N2dE5TBXcNlg0pt0cJue4xBRE2Qc5Vqikxr4VCgKj/ht6SMdFcOacVA9rqF70APJ8RN/4vMJw=="], + "@rollup/rollup-openharmony-arm64": ["@rollup/rollup-openharmony-arm64@4.63.3", "", { "os": "none", "cpu": "arm64" }, "sha512-d+CaftKgmkFBzCwezMqqy1d0QNNYugqLCMcYVQWBy5SS2YfeMP8Q8ripkgx9O8IyBXXLHrJ+aaCV4U96usv6Yg=="], + + "@rollup/rollup-win32-arm64-msvc": ["@rollup/rollup-win32-arm64-msvc@4.63.3", "", { "os": "win32", "cpu": "arm64" }, "sha512-xXlDF6nR1eOuXbdDy5Hl5fmtY7teUDevF/k0O7IPoZe4Tpmdv+lgdE5JRsnhQtt37ql9P0VF2kAN9a0OCZdo+Q=="], + + "@rollup/rollup-win32-ia32-msvc": ["@rollup/rollup-win32-ia32-msvc@4.63.3", "", { "os": "win32", "cpu": "ia32" }, "sha512-YtXAgLN+JP7Ay6qG3eWhc7IHMQPzLc8r3uvhAvlJIoCz/4Q32+Bl9Fmnywidh8v1GOIMmymjovfqY9ETAtysvA=="], + + "@rollup/rollup-win32-x64-gnu": ["@rollup/rollup-win32-x64-gnu@4.63.3", "", { "os": "win32", "cpu": "x64" }, "sha512-WuWtSJRNo549vzcfZyEgfqb6zeSgn1F+UE5kQ+BCjzz0W4MGCjntUHkZVc1VRuAM7+ULaSyhiPxD1spyewFvkQ=="], + + "@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.63.3", "", { "os": "win32", "cpu": "x64" }, "sha512-+lIKX7O0+IGe7WuhATaAMMeT7B76vfhXH/l9wLQL+nvyhbw2ohYCKIdWL56JfDu75CWt5oKRP4QFH/jkMtBquA=="], + + "@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="], "@swc/core": ["@swc/core@1.13.2", "", { "dependencies": { "@swc/counter": "^0.1.3", "@swc/types": "^0.1.23" }, "optionalDependencies": { "@swc/core-darwin-arm64": "1.13.2", "@swc/core-darwin-x64": "1.13.2", "@swc/core-linux-arm-gnueabihf": "1.13.2", "@swc/core-linux-arm64-gnu": "1.13.2", "@swc/core-linux-arm64-musl": "1.13.2", "@swc/core-linux-x64-gnu": "1.13.2", "@swc/core-linux-x64-musl": "1.13.2", "@swc/core-win32-arm64-msvc": "1.13.2", "@swc/core-win32-ia32-msvc": "1.13.2", "@swc/core-win32-x64-msvc": "1.13.2" }, "peerDependencies": { "@swc/helpers": ">=0.5.17" }, "optionalPeers": ["@swc/helpers"] }, "sha512-YWqn+0IKXDhqVLKoac4v2tV6hJqB/wOh8/Br8zjqeqBkKa77Qb0Kw2i7LOFzjFNZbZaPH6AlMGlBwNrxaauaAg=="], @@ -469,19 +530,19 @@ "@vitejs/plugin-react-swc": ["@vitejs/plugin-react-swc@3.11.0", "", { "dependencies": { "@rolldown/pluginutils": "1.0.0-beta.27", "@swc/core": "^1.12.11" }, "peerDependencies": { "vite": "^4 || ^5 || ^6 || ^7" } }, "sha512-YTJCGFdNMHCMfjODYtxRNVAYmTWQ1Lb8PulP/2/f/oEEtglw8oKxKIZmmRkyXrVrHfsKOaVkAc3NT9/dMutO5w=="], - "@vitest/expect": ["@vitest/expect@3.2.4", "", { "dependencies": { "@types/chai": "^5.2.2", "@vitest/spy": "3.2.4", "@vitest/utils": "3.2.4", "chai": "^5.2.0", "tinyrainbow": "^2.0.0" } }, "sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig=="], + "@vitest/expect": ["@vitest/expect@4.1.11", "", { "dependencies": { "@standard-schema/spec": "^1.1.0", "@types/chai": "^5.2.2", "@vitest/spy": "4.1.11", "@vitest/utils": "4.1.11", "chai": "^6.2.2", "tinyrainbow": "^3.1.0" } }, "sha512-VX2x5vNJXET47KAFzwERI+KRMtTTCSWTfSMKsW7JsUsXV4psq++e3DvZpuTDOpHcxytiDs6p2nhVb2tVDiiUYw=="], - "@vitest/mocker": ["@vitest/mocker@3.2.4", "", { "dependencies": { "@vitest/spy": "3.2.4", "estree-walker": "^3.0.3", "magic-string": "^0.30.17" }, "peerDependencies": { "msw": "^2.4.9", "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" }, "optionalPeers": ["msw", "vite"] }, "sha512-46ryTE9RZO/rfDd7pEqFl7etuyzekzEhUbTW3BvmeO/BcCMEgq59BKhek3dXDWgAj4oMK6OZi+vRr1wPW6qjEQ=="], + "@vitest/mocker": ["@vitest/mocker@4.1.11", "", { "dependencies": { "@vitest/spy": "4.1.11", "estree-walker": "^3.0.3", "magic-string": "^0.30.21" }, "peerDependencies": { "msw": "^2.4.9", "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" }, "optionalPeers": ["msw", "vite"] }, "sha512-2XJVD55d1o5AZous5CCGKS74g/riOj9odEt2bQpCVZeblHyHdnMeFl4jl0XjU21stf4mbjUkew2eXQZt65g5CQ=="], - "@vitest/pretty-format": ["@vitest/pretty-format@3.2.4", "", { "dependencies": { "tinyrainbow": "^2.0.0" } }, "sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA=="], + "@vitest/pretty-format": ["@vitest/pretty-format@4.1.11", "", { "dependencies": { "tinyrainbow": "^3.1.0" } }, "sha512-yiZzPbGTS9Sr/JpFl8zHrcIkAofNbFV6k21vIgQN/cY/oxZeXhJv5sc/MBJ5jFKWmWs+oJHw0UXLZjmf931+Vw=="], - "@vitest/runner": ["@vitest/runner@3.2.4", "", { "dependencies": { "@vitest/utils": "3.2.4", "pathe": "^2.0.3", "strip-literal": "^3.0.0" } }, "sha512-oukfKT9Mk41LreEW09vt45f8wx7DordoWUZMYdY/cyAk7w5TWkTRCNZYF7sX7n2wB7jyGAl74OxgwhPgKaqDMQ=="], + "@vitest/runner": ["@vitest/runner@4.1.11", "", { "dependencies": { "@vitest/utils": "4.1.11", "pathe": "^2.0.3" } }, "sha512-LztvUgdwMNJMIkj3hQnnxiC2Xy1zNxq928W/xhjCLaNCzqTZOudjwbQf6v9IntZGPw132i2Lq2rgTRZHD3JHNw=="], - "@vitest/snapshot": ["@vitest/snapshot@3.2.4", "", { "dependencies": { "@vitest/pretty-format": "3.2.4", "magic-string": "^0.30.17", "pathe": "^2.0.3" } }, "sha512-dEYtS7qQP2CjU27QBC5oUOxLE/v5eLkGqPE0ZKEIDGMs4vKWe7IjgLOeauHsR0D5YuuycGRO5oSRXnwnmA78fQ=="], + "@vitest/snapshot": ["@vitest/snapshot@4.1.11", "", { "dependencies": { "@vitest/pretty-format": "4.1.11", "@vitest/utils": "4.1.11", "magic-string": "^0.30.21", "pathe": "^2.0.3" } }, "sha512-pN7ikn1ON7h8ee4gIAp4AzyK+zBtJPzVbqOgu5LCEh4VaJVbPQcgYQYJIMGQPXVeJJq1fnfazis7a5pFNPahog=="], - "@vitest/spy": ["@vitest/spy@3.2.4", "", { "dependencies": { "tinyspy": "^4.0.3" } }, "sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw=="], + "@vitest/spy": ["@vitest/spy@4.1.11", "", {}, "sha512-apNa/prQy2qCeywhnixOHPRCgGNhvg7T4Dapfl1GahLp/R+uhBm5cPyFoNVyqsNd2h1nJxL6BqqdIjiABL60YA=="], - "@vitest/utils": ["@vitest/utils@3.2.4", "", { "dependencies": { "@vitest/pretty-format": "3.2.4", "loupe": "^3.1.4", "tinyrainbow": "^2.0.0" } }, "sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA=="], + "@vitest/utils": ["@vitest/utils@4.1.11", "", { "dependencies": { "@vitest/pretty-format": "4.1.11", "convert-source-map": "^2.0.0", "tinyrainbow": "^3.1.0" } }, "sha512-zTCVGpyFsGWBhllOyKlTw/vnr6D9qxsfSDyfbyZmTyjHw5N/VuvzHpHoQjm2ZJzn4RJgx5w4r7V0er69CmLgPQ=="], "abab": ["abab@2.0.6", "", {}, "sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA=="], @@ -531,8 +592,6 @@ "browserslist": ["browserslist@4.25.1", "", { "dependencies": { "caniuse-lite": "^1.0.30001726", "electron-to-chromium": "^1.5.173", "node-releases": "^2.0.19", "update-browserslist-db": "^1.1.3" }, "bin": "cli.js" }, "sha512-KGj0KoOMXLpSNkkEI6Z6mShmQy0bc1I+T7K9N81k4WWMrfz+6fQ6es80B/YLAeRoKvjYE1YSHHOW1qe9xIVzHw=="], - "cac": ["cac@6.7.14", "", {}, "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ=="], - "call-bind-apply-helpers": ["call-bind-apply-helpers@1.0.2", "", { "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2" } }, "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ=="], "callsites": ["callsites@3.1.0", "", {}, "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ=="], @@ -543,7 +602,7 @@ "ccount": ["ccount@2.0.1", "", {}, "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg=="], - "chai": ["chai@5.3.3", "", { "dependencies": { "assertion-error": "^2.0.1", "check-error": "^2.1.1", "deep-eql": "^5.0.1", "loupe": "^3.1.0", "pathval": "^2.0.0" } }, "sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw=="], + "chai": ["chai@6.2.2", "", {}, "sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg=="], "chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="], @@ -555,8 +614,6 @@ "character-reference-invalid": ["character-reference-invalid@2.0.1", "", {}, "sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw=="], - "check-error": ["check-error@2.1.3", "", {}, "sha512-PAJdDJusoxnwm1VwW07VWwUN1sl7smmC3OKggvndJFadxxDRyFJBX/ggnu/KE4kQAB7a3Dp8f/YXC1FlUprWmA=="], - "chokidar": ["chokidar@3.6.0", "", { "dependencies": { "anymatch": "~3.1.2", "braces": "~3.0.2", "glob-parent": "~5.1.2", "is-binary-path": "~2.1.0", "is-glob": "~4.0.1", "normalize-path": "~3.0.0", "readdirp": "~3.6.0" }, "optionalDependencies": { "fsevents": "~2.3.2" } }, "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw=="], "class-variance-authority": ["class-variance-authority@0.7.1", "", { "dependencies": { "clsx": "^2.1.1" } }, "sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg=="], @@ -577,6 +634,10 @@ "concat-map": ["concat-map@0.0.1", "", {}, "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="], + "convert-source-map": ["convert-source-map@2.0.0", "", {}, "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg=="], + + "cookie": ["cookie@1.1.1", "", {}, "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ=="], + "cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="], "css.escape": ["css.escape@1.5.1", "", {}, "sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg=="], @@ -623,14 +684,14 @@ "decode-named-character-reference": ["decode-named-character-reference@1.3.0", "", { "dependencies": { "character-entities": "^2.0.0" } }, "sha512-GtpQYB283KrPp6nRw50q3U9/VfOutZOe103qlN7BPP6Ad27xYnOIWv4lPzo8HCAL+mMZofJ9KEy30fq6MfaK6Q=="], - "deep-eql": ["deep-eql@5.0.2", "", {}, "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q=="], - "deep-is": ["deep-is@0.1.4", "", {}, "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ=="], "delayed-stream": ["delayed-stream@1.0.0", "", {}, "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ=="], "dequal": ["dequal@2.0.3", "", {}, "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA=="], + "detect-libc": ["detect-libc@2.1.2", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="], + "detect-node-es": ["detect-node-es@1.1.0", "", {}, "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ=="], "devlop": ["devlop@1.1.0", "", { "dependencies": { "dequal": "^2.0.0" } }, "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA=="], @@ -665,13 +726,13 @@ "es-errors": ["es-errors@1.3.0", "", {}, "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw=="], - "es-module-lexer": ["es-module-lexer@1.7.0", "", {}, "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA=="], + "es-module-lexer": ["es-module-lexer@2.3.2", "", {}, "sha512-poHGpORABojJJucnV9KbOavETW8lBVnphkW77ER5/BQ5Fz7oXSoCNek7IH3vR5nRjdsEz926ibFYX8KtLQmdyw=="], "es-object-atoms": ["es-object-atoms@1.1.1", "", { "dependencies": { "es-errors": "^1.3.0" } }, "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA=="], "es-set-tostringtag": ["es-set-tostringtag@2.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "get-intrinsic": "^1.2.6", "has-tostringtag": "^1.0.2", "hasown": "^2.0.2" } }, "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA=="], - "esbuild": ["esbuild@0.21.5", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.21.5", "@esbuild/android-arm": "0.21.5", "@esbuild/android-arm64": "0.21.5", "@esbuild/android-x64": "0.21.5", "@esbuild/darwin-arm64": "0.21.5", "@esbuild/darwin-x64": "0.21.5", "@esbuild/freebsd-arm64": "0.21.5", "@esbuild/freebsd-x64": "0.21.5", "@esbuild/linux-arm": "0.21.5", "@esbuild/linux-arm64": "0.21.5", "@esbuild/linux-ia32": "0.21.5", "@esbuild/linux-loong64": "0.21.5", "@esbuild/linux-mips64el": "0.21.5", "@esbuild/linux-ppc64": "0.21.5", "@esbuild/linux-riscv64": "0.21.5", "@esbuild/linux-s390x": "0.21.5", "@esbuild/linux-x64": "0.21.5", "@esbuild/netbsd-x64": "0.21.5", "@esbuild/openbsd-x64": "0.21.5", "@esbuild/sunos-x64": "0.21.5", "@esbuild/win32-arm64": "0.21.5", "@esbuild/win32-ia32": "0.21.5", "@esbuild/win32-x64": "0.21.5" }, "bin": "bin/esbuild" }, "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw=="], + "esbuild": ["esbuild@0.25.12", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.12", "@esbuild/android-arm": "0.25.12", "@esbuild/android-arm64": "0.25.12", "@esbuild/android-x64": "0.25.12", "@esbuild/darwin-arm64": "0.25.12", "@esbuild/darwin-x64": "0.25.12", "@esbuild/freebsd-arm64": "0.25.12", "@esbuild/freebsd-x64": "0.25.12", "@esbuild/linux-arm": "0.25.12", "@esbuild/linux-arm64": "0.25.12", "@esbuild/linux-ia32": "0.25.12", "@esbuild/linux-loong64": "0.25.12", "@esbuild/linux-mips64el": "0.25.12", "@esbuild/linux-ppc64": "0.25.12", "@esbuild/linux-riscv64": "0.25.12", "@esbuild/linux-s390x": "0.25.12", "@esbuild/linux-x64": "0.25.12", "@esbuild/netbsd-arm64": "0.25.12", "@esbuild/netbsd-x64": "0.25.12", "@esbuild/openbsd-arm64": "0.25.12", "@esbuild/openbsd-x64": "0.25.12", "@esbuild/openharmony-arm64": "0.25.12", "@esbuild/sunos-x64": "0.25.12", "@esbuild/win32-arm64": "0.25.12", "@esbuild/win32-ia32": "0.25.12", "@esbuild/win32-x64": "0.25.12" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg=="], "escalade": ["escalade@3.2.0", "", {}, "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="], @@ -845,13 +906,37 @@ "levn": ["levn@0.4.1", "", { "dependencies": { "prelude-ls": "^1.2.1", "type-check": "~0.4.0" } }, "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ=="], + "lightningcss": ["lightningcss@1.33.0", "", { "dependencies": { "detect-libc": "^2.0.3" }, "optionalDependencies": { "lightningcss-android-arm64": "1.33.0", "lightningcss-darwin-arm64": "1.33.0", "lightningcss-darwin-x64": "1.33.0", "lightningcss-freebsd-x64": "1.33.0", "lightningcss-linux-arm-gnueabihf": "1.33.0", "lightningcss-linux-arm64-gnu": "1.33.0", "lightningcss-linux-arm64-musl": "1.33.0", "lightningcss-linux-x64-gnu": "1.33.0", "lightningcss-linux-x64-musl": "1.33.0", "lightningcss-win32-arm64-msvc": "1.33.0", "lightningcss-win32-x64-msvc": "1.33.0" } }, "sha512-WkUDrojuJs0xkgGf2udWxa3yGBRxPtxUkB79i6aCZLRgc7PM8fZe9TosfPDcvEpQZbuFASnHYmRLBLUbmLOIIA=="], + + "lightningcss-android-arm64": ["lightningcss-android-arm64@1.33.0", "", { "os": "android", "cpu": "arm64" }, "sha512-gEpRTalKdosp4Bb8qWtc2iOgE5SeIHlpS1up9bFq2wAyYhl1UdTObYiHe98zEM9SQvSoqQZ1IQD0JNpg3Ml5pg=="], + + "lightningcss-darwin-arm64": ["lightningcss-darwin-arm64@1.33.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-Sciaz8eenNTKn9b3t7+xr0ipTp9YxKQY4npwQ3mrRuL0BAVHBLyZxofhaKBAVtzmtRZ/zTyo0/to4B1uWG/Djg=="], + + "lightningcss-darwin-x64": ["lightningcss-darwin-x64@1.33.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-Z5UPAxzrjlWNNyGy6i65cJzzvgJ5D3T6wMvs+gWpY9d7qRhANrxqAp6LhxIgZhWEw18RfJTGcRxjuLIBr+m8XQ=="], + + "lightningcss-freebsd-x64": ["lightningcss-freebsd-x64@1.33.0", "", { "os": "freebsd", "cpu": "x64" }, "sha512-QQM/Ti/hQajJwCY+RiWuCZ9sdtI/XQk7nDK5vC8kkdwixezOlDgvDx7+RT+QjK6FcFT4MpsuoBnHIo/O3StRRg=="], + + "lightningcss-linux-arm-gnueabihf": ["lightningcss-linux-arm-gnueabihf@1.33.0", "", { "os": "linux", "cpu": "arm" }, "sha512-N7FVBe6iS24MlM6R/4RBTxGhQheZGs7tiQ9U32UtF75NzP5Q7xWPRqLBCKxlRQRk3rY1jCIPLzx7WzOhuUIRLQ=="], + + "lightningcss-linux-arm64-gnu": ["lightningcss-linux-arm64-gnu@1.33.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-j2v/itmy4HlNxlc6voKXYgBqNi0Ng2LShg4z7GufpEgs05P+2suBVyi9I6YHq5uoVFx9ETin3eCEhLVyXGQnKg=="], + + "lightningcss-linux-arm64-musl": ["lightningcss-linux-arm64-musl@1.33.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-yiO5ROMuYQgXbC60yjZU5CYSFZGKXL0HFATXt9mHJn1+zW55oCtMI9NfcVhYLMFDL7gV7oBPon/EmMMGg2OvtQ=="], + + "lightningcss-linux-x64-gnu": ["lightningcss-linux-x64-gnu@1.33.0", "", { "os": "linux", "cpu": "x64" }, "sha512-ar+Ju7LmcN0Jo4FpL4hpFybwNG9/3A/Br5KW2n2jyODg3MEZXaDYADdemoNS+BDNfMgKvylJLj4S5tyRActuAg=="], + + "lightningcss-linux-x64-musl": ["lightningcss-linux-x64-musl@1.33.0", "", { "os": "linux", "cpu": "x64" }, "sha512-RYiYbkokw0trfKqqzfF55lginwEPrD3OJDfTuJzFs1MK6iFnDenaz1fqLLtX4ITG3OktJQXOeTaw1awrBAlZPw=="], + + "lightningcss-win32-arm64-msvc": ["lightningcss-win32-arm64-msvc@1.33.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-1K+MPfLSFVpphzpdbfkhlWk6wBrTObBzS2T6db10PNOZgR9GoVsAWzwNyuhUYYbTp23j+4RrncfujZ4uAzXvwA=="], + + "lightningcss-win32-x64-msvc": ["lightningcss-win32-x64-msvc@1.33.0", "", { "os": "win32", "cpu": "x64" }, "sha512-OlEICDx/Xl0FqSp4bry8zFnCvGpig3Gl4gCquvYwHuqJKEC1+n9NgDniFvqHGmMv1ZkqDJrDqKKSykTDX+ehuA=="], + "lilconfig": ["lilconfig@3.1.3", "", {}, "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw=="], "lines-and-columns": ["lines-and-columns@1.2.4", "", {}, "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg=="], "locate-path": ["locate-path@6.0.0", "", { "dependencies": { "p-locate": "^5.0.0" } }, "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw=="], - "lodash": ["lodash@4.17.21", "", {}, "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="], + "lodash": ["lodash@4.18.1", "", {}, "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q=="], "lodash.castarray": ["lodash.castarray@4.4.0", "", {}, "sha512-aVx8ztPv7/2ULbArGJ2Y42bG1mEQ5mGjpdvrbJcJFU3TbYybe+QlLS4pst9zV52ymy2in1KpFPiZnAOATxD4+Q=="], @@ -863,8 +948,6 @@ "loose-envify": ["loose-envify@1.4.0", "", { "dependencies": { "js-tokens": "^3.0.0 || ^4.0.0" }, "bin": "cli.js" }, "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q=="], - "loupe": ["loupe@3.2.1", "", {}, "sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ=="], - "lru-cache": ["lru-cache@10.4.3", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="], "lucide-react": ["lucide-react@0.462.0", "", { "peerDependencies": { "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0-rc" } }, "sha512-NTL7EbAao9IFtuSivSZgrAh4fZd09Lr+6MTkqIxuHaH2nnYiYIzXPo06cOxHg9wKLdj6LL8TByG4qpePqwgx/g=="], @@ -969,6 +1052,8 @@ "object-hash": ["object-hash@3.0.0", "", {}, "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw=="], + "obug": ["obug@2.2.1", "", {}, "sha512-XrsrhT5sybtKI6wakr2SPOlGZWWYbUXZ7a0jT8/QOeAPau+1X/bSegNe5YR75oJmEZQbKningirmGOEJCIk61Q=="], + "optionator": ["optionator@0.9.4", "", { "dependencies": { "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", "levn": "^0.4.1", "prelude-ls": "^1.2.1", "type-check": "^0.4.0", "word-wrap": "^1.2.5" } }, "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g=="], "p-limit": ["p-limit@3.1.0", "", { "dependencies": { "yocto-queue": "^0.1.0" } }, "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ=="], @@ -993,8 +1078,6 @@ "pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="], - "pathval": ["pathval@2.0.1", "", {}, "sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ=="], - "picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="], "picomatch": ["picomatch@4.0.3", "", {}, "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q=="], @@ -1055,9 +1138,9 @@ "react-resizable-panels": ["react-resizable-panels@2.1.9", "", { "peerDependencies": { "react": "^16.14.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc", "react-dom": "^16.14.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" } }, "sha512-z77+X08YDIrgAes4jl8xhnUu1LNIRp4+E7cv4xHmLOxxUPO/ML7PSrE813b90vj7xvQ1lcf7g2uA9GeMZonjhQ=="], - "react-router": ["react-router@6.30.1", "", { "dependencies": { "@remix-run/router": "1.23.0" }, "peerDependencies": { "react": ">=16.8" } }, "sha512-X1m21aEmxGXqENEPG3T6u0Th7g0aS4ZmoNynhbs+Cn+q+QGTLt+d5IQ2bHAXKzKcxGJjxACpVbnYQSCRcfxHlQ=="], + "react-router": ["react-router@7.18.4", "", { "dependencies": { "cookie": "^1.0.1", "set-cookie-parser": "^2.6.0" }, "peerDependencies": { "react": ">=18", "react-dom": ">=18" }, "optionalPeers": ["react-dom"] }, "sha512-PUPQcMhMGRAslLcvtlPz/kmzBEWPhLdgLFrL7pLNepBL6dX0lWj4WD2cUYVgYCuT3jxvghYFg81cDTj44DhetQ=="], - "react-router-dom": ["react-router-dom@6.30.1", "", { "dependencies": { "@remix-run/router": "1.23.0", "react-router": "6.30.1" }, "peerDependencies": { "react": ">=16.8", "react-dom": ">=16.8" } }, "sha512-llKsgOkZdbPU1Eg3zK8lCn+sjD9wMRZZPuzmdWWX5SUs8OFkN5HnFVC0u5KMeMaC9aoancFI/KoLuKPqN+hxHw=="], + "react-router-dom": ["react-router-dom@7.18.4", "", { "dependencies": { "react-router": "7.18.4" }, "peerDependencies": { "react": ">=18", "react-dom": ">=18" } }, "sha512-yrfmJHIpDG7taCpqKjT1G5B6q3O2K+RN8/fgNf0lTjCwiPbQ0ei6vXX9ZjQR+7ld8Tr7Z5xmyMnZ8YJrphWQUw=="], "react-smooth": ["react-smooth@4.0.4", "", { "dependencies": { "fast-equals": "^5.0.1", "prop-types": "^15.8.1", "react-transition-group": "^4.4.5" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-gnGKTpYwqL0Iii09gHobNolvX4Kiq4PKx6eWBCYYix+8cdw+cGo3do906l1NBPKkSWx1DghC1dlWG9L2uGd61Q=="], @@ -1087,7 +1170,9 @@ "reusify": ["reusify@1.0.4", "", {}, "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw=="], - "rollup": ["rollup@4.24.0", "", { "dependencies": { "@types/estree": "1.0.6" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.24.0", "@rollup/rollup-android-arm64": "4.24.0", "@rollup/rollup-darwin-arm64": "4.24.0", "@rollup/rollup-darwin-x64": "4.24.0", "@rollup/rollup-linux-arm-gnueabihf": "4.24.0", "@rollup/rollup-linux-arm-musleabihf": "4.24.0", "@rollup/rollup-linux-arm64-gnu": "4.24.0", "@rollup/rollup-linux-arm64-musl": "4.24.0", "@rollup/rollup-linux-powerpc64le-gnu": "4.24.0", "@rollup/rollup-linux-riscv64-gnu": "4.24.0", "@rollup/rollup-linux-s390x-gnu": "4.24.0", "@rollup/rollup-linux-x64-gnu": "4.24.0", "@rollup/rollup-linux-x64-musl": "4.24.0", "@rollup/rollup-win32-arm64-msvc": "4.24.0", "@rollup/rollup-win32-ia32-msvc": "4.24.0", "@rollup/rollup-win32-x64-msvc": "4.24.0", "fsevents": "~2.3.2" }, "bin": "dist/bin/rollup" }, "sha512-DOmrlGSXNk1DM0ljiQA+i+o0rSLhtii1je5wgk60j49d1jHT5YYttBv1iWOnYSTG+fZZESUOSNiAl89SIet+Cg=="], + "rolldown": ["rolldown@1.2.9", "", { "dependencies": { "@oxc-project/types": "=0.150.0", "@rolldown/pluginutils": "^1.0.0" }, "optionalDependencies": { "@rolldown/binding-android-arm-eabi": "1.2.9", "@rolldown/binding-android-arm64": "1.2.9", "@rolldown/binding-darwin-arm64": "1.2.9", "@rolldown/binding-darwin-x64": "1.2.9", "@rolldown/binding-freebsd-x64": "1.2.9", "@rolldown/binding-linux-arm-gnueabihf": "1.2.9", "@rolldown/binding-linux-arm64-gnu": "1.2.9", "@rolldown/binding-linux-arm64-musl": "1.2.9", "@rolldown/binding-linux-ppc64-gnu": "1.2.9", "@rolldown/binding-linux-s390x-gnu": "1.2.9", "@rolldown/binding-linux-x64-gnu": "1.2.9", "@rolldown/binding-linux-x64-musl": "1.2.9", "@rolldown/binding-openharmony-arm64": "1.2.9", "@rolldown/binding-win32-arm64-msvc": "1.2.9", "@rolldown/binding-win32-x64-msvc": "1.2.9" }, "bin": { "rolldown": "./bin/cli.mjs" } }, "sha512-hx/Pv0N1haXRb11qkfnK5MXB/iqr7i0yjWQqmO9uHqZpBgQSqzc8UsSnEpalsh+j1I8qQ2CkXAkJC8Br3dKSlg=="], + + "rollup": ["rollup@4.63.3", "", { "dependencies": { "@types/estree": "1.0.9" }, "optionalDependencies": { "@napi-rs/lzma-linux-x64-gnu": "1.5.1", "@rollup/rollup-android-arm-eabi": "4.63.3", "@rollup/rollup-android-arm64": "4.63.3", "@rollup/rollup-darwin-arm64": "4.63.3", "@rollup/rollup-darwin-x64": "4.63.3", "@rollup/rollup-freebsd-arm64": "4.63.3", "@rollup/rollup-freebsd-x64": "4.63.3", "@rollup/rollup-linux-arm-gnueabihf": "4.63.3", "@rollup/rollup-linux-arm-musleabihf": "4.63.3", "@rollup/rollup-linux-arm64-gnu": "4.63.3", "@rollup/rollup-linux-arm64-musl": "4.63.3", "@rollup/rollup-linux-loong64-gnu": "4.63.3", "@rollup/rollup-linux-loong64-musl": "4.63.3", "@rollup/rollup-linux-ppc64-gnu": "4.63.3", "@rollup/rollup-linux-ppc64-musl": "4.63.3", "@rollup/rollup-linux-riscv64-gnu": "4.63.3", "@rollup/rollup-linux-riscv64-musl": "4.63.3", "@rollup/rollup-linux-s390x-gnu": "4.63.3", "@rollup/rollup-linux-x64-gnu": "4.63.3", "@rollup/rollup-linux-x64-musl": "4.63.3", "@rollup/rollup-openbsd-x64": "4.63.3", "@rollup/rollup-openharmony-arm64": "4.63.3", "@rollup/rollup-win32-arm64-msvc": "4.63.3", "@rollup/rollup-win32-ia32-msvc": "4.63.3", "@rollup/rollup-win32-x64-gnu": "4.63.3", "@rollup/rollup-win32-x64-msvc": "4.63.3", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-1i2XreiAoMMXuPGD6Msj2xWrMMkHojNRKivInxGQcg7/1KuPuYlfUutLyh4drnOxUTHX9cHI4wFoat8D/NKaBw=="], "run-parallel": ["run-parallel@1.2.0", "", { "dependencies": { "queue-microtask": "^1.2.2" } }, "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA=="], @@ -1099,6 +1184,8 @@ "semver": ["semver@7.7.2", "", { "bin": "bin/semver.js" }, "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA=="], + "set-cookie-parser": ["set-cookie-parser@2.7.2", "", {}, "sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw=="], + "shebang-command": ["shebang-command@2.0.0", "", { "dependencies": { "shebang-regex": "^3.0.0" } }, "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA=="], "shebang-regex": ["shebang-regex@3.0.0", "", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="], @@ -1117,7 +1204,7 @@ "stackback": ["stackback@0.0.2", "", {}, "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw=="], - "std-env": ["std-env@3.10.0", "", {}, "sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg=="], + "std-env": ["std-env@4.2.0", "", {}, "sha512-oCUKSupKTHX53EyjDtuZQ64pjLJ6yYCtpmEw0goYxtjG9KpbRe8KAsl2tBUGU9DyMcJ0RwJ8GqJAFzMXcXW1Rw=="], "string-width": ["string-width@5.1.2", "", { "dependencies": { "eastasianwidth": "^0.2.0", "emoji-regex": "^9.2.2", "strip-ansi": "^7.0.1" } }, "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA=="], @@ -1133,8 +1220,6 @@ "strip-json-comments": ["strip-json-comments@3.1.1", "", {}, "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig=="], - "strip-literal": ["strip-literal@3.1.0", "", { "dependencies": { "js-tokens": "^9.0.1" } }, "sha512-8r3mkIM/2+PpjHoOtiAW8Rg3jJLHaV7xPwG+YRGrv6FP0wwk/toTpATxWYOW0BKdWwl82VT2tFYi5DlROa0Mxg=="], - "style-to-js": ["style-to-js@1.1.21", "", { "dependencies": { "style-to-object": "1.0.14" } }, "sha512-RjQetxJrrUJLQPHbLku6U/ocGtzyjbJMP9lCNK7Ag0CNh690nSH8woqWH9u16nMjYBAok+i7JO1NP2pOy8IsPQ=="], "style-to-object": ["style-to-object@1.0.14", "", { "dependencies": { "inline-style-parser": "0.2.7" } }, "sha512-LIN7rULI0jBscWQYaSswptyderlarFkjQ+t79nzty8tcIAceVomEVlLzH5VP4Cmsv6MtKhs7qaAiwlcp+Mgaxw=="], @@ -1161,15 +1246,11 @@ "tinybench": ["tinybench@2.9.0", "", {}, "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg=="], - "tinyexec": ["tinyexec@0.3.2", "", {}, "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA=="], + "tinyexec": ["tinyexec@1.3.1", "", {}, "sha512-GCvB3aoys96IuDFBMcTB46JOR6mdMtAToqwiW8JlWhsoh1mhHi/xn9ss/Dg7N555GiJyEt2qzoG/NHCwM6h1EA=="], "tinyglobby": ["tinyglobby@0.2.15", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.3" } }, "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ=="], - "tinypool": ["tinypool@1.1.1", "", {}, "sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg=="], - - "tinyrainbow": ["tinyrainbow@2.0.0", "", {}, "sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw=="], - - "tinyspy": ["tinyspy@4.0.4", "", {}, "sha512-azl+t0z7pw/z958Gy9svOTuzqIk6xq+NSheJzn5MMWtWTFywIacg2wUlzKFGtt3cthx0r2SxMK0yzJOR0IES7Q=="], + "tinyrainbow": ["tinyrainbow@3.1.1", "", {}, "sha512-yau8yJdTt989Mm0Bd/236QnzEiPf2xLLTqUZRUJOo/3CB078LSwzei343DgtJVmfJKJE3TMINY1u42SQsP6mXw=="], "to-regex-range": ["to-regex-range@5.0.1", "", { "dependencies": { "is-number": "^7.0.0" } }, "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ=="], @@ -1231,11 +1312,9 @@ "victory-vendor": ["victory-vendor@36.9.2", "", { "dependencies": { "@types/d3-array": "^3.0.3", "@types/d3-ease": "^3.0.0", "@types/d3-interpolate": "^3.0.1", "@types/d3-scale": "^4.0.2", "@types/d3-shape": "^3.1.0", "@types/d3-time": "^3.0.0", "@types/d3-timer": "^3.0.0", "d3-array": "^3.1.6", "d3-ease": "^3.0.1", "d3-interpolate": "^3.0.1", "d3-scale": "^4.0.2", "d3-shape": "^3.1.0", "d3-time": "^3.0.0", "d3-timer": "^3.0.1" } }, "sha512-PnpQQMuxlwYdocC8fIJqVXvkeViHYzotI+NJrCuav0ZYFoq912ZHBk3mCeuj+5/VpodOjPe1z0Fk2ihgzlXqjQ=="], - "vite": ["vite@5.4.19", "", { "dependencies": { "esbuild": "^0.21.3", "postcss": "^8.4.43", "rollup": "^4.20.0" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || >=20.0.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.4.0" }, "optionalPeers": ["less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser"], "bin": "bin/vite.js" }, "sha512-qO3aKv3HoQC8QKiNSTuUM1l9o/XX3+c+VTgLHbJWHZGeTPVAg2XwazI9UWzoxjIJCGCV2zU60uqMzjeLZuULqA=="], - - "vite-node": ["vite-node@3.2.4", "", { "dependencies": { "cac": "^6.7.14", "debug": "^4.4.1", "es-module-lexer": "^1.7.0", "pathe": "^2.0.3", "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" }, "bin": { "vite-node": "vite-node.mjs" } }, "sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg=="], + "vite": ["vite@6.4.3", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.4", "picomatch": "^4.0.2", "postcss": "^8.5.3", "rollup": "^4.34.9", "tinyglobby": "^0.2.13" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "jiti": ">=1.21.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-NTKlcQjlAK7MlQoyb6LgaqHc8sso/pVyUJYWMws3jg21uTJw/LddqIFPcPqP6PzpgbIcZyKI85sFE4HBrQDA8A=="], - "vitest": ["vitest@3.2.4", "", { "dependencies": { "@types/chai": "^5.2.2", "@vitest/expect": "3.2.4", "@vitest/mocker": "3.2.4", "@vitest/pretty-format": "^3.2.4", "@vitest/runner": "3.2.4", "@vitest/snapshot": "3.2.4", "@vitest/spy": "3.2.4", "@vitest/utils": "3.2.4", "chai": "^5.2.0", "debug": "^4.4.1", "expect-type": "^1.2.1", "magic-string": "^0.30.17", "pathe": "^2.0.3", "picomatch": "^4.0.2", "std-env": "^3.9.0", "tinybench": "^2.9.0", "tinyexec": "^0.3.2", "tinyglobby": "^0.2.14", "tinypool": "^1.1.1", "tinyrainbow": "^2.0.0", "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0", "vite-node": "3.2.4", "why-is-node-running": "^2.3.0" }, "peerDependencies": { "@edge-runtime/vm": "*", "@types/debug": "^4.1.12", "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "@vitest/browser": "3.2.4", "@vitest/ui": "3.2.4", "happy-dom": "*", "jsdom": "*" }, "optionalPeers": ["@edge-runtime/vm", "@types/debug", "@types/node", "@vitest/browser", "@vitest/ui", "happy-dom", "jsdom"], "bin": { "vitest": "vitest.mjs" } }, "sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A=="], + "vitest": ["vitest@4.1.11", "", { "dependencies": { "@vitest/expect": "4.1.11", "@vitest/mocker": "4.1.11", "@vitest/pretty-format": "4.1.11", "@vitest/runner": "4.1.11", "@vitest/snapshot": "4.1.11", "@vitest/spy": "4.1.11", "@vitest/utils": "4.1.11", "es-module-lexer": "^2.0.0", "expect-type": "^1.3.0", "magic-string": "^0.30.21", "obug": "^2.1.1", "pathe": "^2.0.3", "picomatch": "^4.0.3", "std-env": "^4.0.0-rc.1", "tinybench": "^2.9.0", "tinyexec": "^1.0.2", "tinyglobby": "^0.2.15", "tinyrainbow": "^3.1.0", "vite": "^6.0.0 || ^7.0.0 || ^8.0.0", "why-is-node-running": "^2.3.0" }, "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.11", "@vitest/browser-preview": "4.1.11", "@vitest/browser-webdriverio": "4.1.11", "@vitest/coverage-istanbul": "4.1.11", "@vitest/coverage-v8": "4.1.11", "@vitest/ui": "4.1.11", "happy-dom": "*", "jsdom": "*" }, "optionalPeers": ["@edge-runtime/vm", "@opentelemetry/api", "@types/node", "@vitest/browser-playwright", "@vitest/browser-preview", "@vitest/browser-webdriverio", "@vitest/coverage-istanbul", "@vitest/coverage-v8", "@vitest/ui", "happy-dom", "jsdom"], "bin": { "vitest": "./vitest.mjs" } }, "sha512-fhACrNXUidIbGSBr5FlbuBkO7VWC1ZyLl0DO4CU2DrQoAPxX84Ysxs+HeGQpii5lZWV1Q4gBZTTu49mF+A6Edw=="], "w3c-xmlserializer": ["w3c-xmlserializer@4.0.0", "", { "dependencies": { "xml-name-validator": "^4.0.0" } }, "sha512-d+BFHzbiCx6zGfz0HyQ6Rg69w9k19nviJspaj4yNscGjrHu94sVP+aRm75yEbCh+r2/yR+7q6hux9LVtbuTGBw=="], @@ -1323,19 +1402,19 @@ "readdirp/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], + "rolldown/@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.1", "", {}, "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw=="], + + "rollup/@types/estree": ["@types/estree@1.0.9", "", {}, "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg=="], + "string-width-cjs/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], "string-width-cjs/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], "strip-ansi/ansi-regex": ["ansi-regex@6.1.0", "", {}, "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA=="], - "strip-literal/js-tokens": ["js-tokens@9.0.1", "", {}, "sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ=="], - "tailwindcss/postcss-selector-parser": ["postcss-selector-parser@6.1.2", "", { "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" } }, "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg=="], - "vite-node/debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="], - - "vitest/debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="], + "vitest/vite": ["vite@8.3.0", "", { "dependencies": { "lightningcss": "^1.33.0", "picomatch": "^4.0.7", "postcss": "^8.5.28", "rolldown": "~1.2.6", "tinyglobby": "^0.2.17" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "@vitejs/devtools": "^0.7.1", "esbuild": "^0.27.0 || ^0.28.0", "jiti": ">=1.21.0", "less": "^4.0.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "@vitejs/devtools", "esbuild", "jiti", "less", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-lhZBVvEHefgE+HQZC9O7EBJgCU/nVzFNl7vkS4RE0APtWLP02/8QVIkQtzBxPquh7lq5/78NHipTj7ODQ6XuyQ=="], "wrap-ansi/ansi-styles": ["ansi-styles@6.2.1", "", {}, "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug=="], @@ -1347,6 +1426,14 @@ "glob/minimatch/brace-expansion": ["brace-expansion@2.0.2", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ=="], + "vitest/vite/picomatch": ["picomatch@4.0.7", "", {}, "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA=="], + + "vitest/vite/postcss": ["postcss@8.5.28", "", { "dependencies": { "nanoid": "^3.3.18", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-RRuzqDtt5Y9h3quz5hWhK+TPnsmVs6WwSU6LkJMeY4HstUEDuYTG8UJSdawMRzmzAtV+KEoG8N3Qg2qLy5vM/A=="], + + "vitest/vite/tinyglobby": ["tinyglobby@0.2.17", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.4" } }, "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g=="], + "wrap-ansi-cjs/string-width/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], + + "vitest/vite/postcss/nanoid": ["nanoid@3.3.19", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-Y2tUNy4ouw6tq5oDSKeQYGOyhkUBhNOcGV/02KC+6kd9eDGqdZd++mjMiIDilrBYvjEnCYvVtsuHCuP+okSfug=="], } } diff --git a/daemon/bun.lock b/daemon/bun.lock index 1623ce96..2f5baded 100644 --- a/daemon/bun.lock +++ b/daemon/bun.lock @@ -5,9 +5,9 @@ "": { "name": "littleimpd", "dependencies": { - "@aws-sdk/client-s3": "^3.1009.0", - "@modelcontextprotocol/sdk": "^1.27.1", - "hono": "^4.12.7", + "@aws-sdk/client-s3": "^3.1128.0", + "@modelcontextprotocol/sdk": "^1.30.0", + "hono": "^4.13.7", "pdf-parse": "^2.4.5", "sqlite-vec": "^0.1.7-alpha.2", "zod": "^4.3.6", @@ -17,90 +17,54 @@ }, }, }, + "overrides": { + "@hono/node-server": "1.19.15", + "body-parser": "2.3.0", + "fast-uri": "3.1.6", + "ip-address": "10.7.0", + "path-to-regexp": "8.4.2", + "qs": "6.16.0", + }, "packages": { - "@aws-crypto/crc32": ["@aws-crypto/crc32@5.2.0", "", { "dependencies": { "@aws-crypto/util": "^5.2.0", "@aws-sdk/types": "^3.222.0", "tslib": "^2.6.2" } }, "sha512-nLbCWqQNgUiwwtFsen1AdzAtvuLRsQS8rYgMuxCrdKf9kOssamGLuPwyTY9wyYblNr9+1XM8v6zoDTPPSIeANg=="], - - "@aws-crypto/crc32c": ["@aws-crypto/crc32c@5.2.0", "", { "dependencies": { "@aws-crypto/util": "^5.2.0", "@aws-sdk/types": "^3.222.0", "tslib": "^2.6.2" } }, "sha512-+iWb8qaHLYKrNvGRbiYRHSdKRWhto5XlZUEBwDjYNf+ly5SVYG6zEoYIdxvf5R3zyeP16w4PLBn3rH1xc74Rag=="], - - "@aws-crypto/sha1-browser": ["@aws-crypto/sha1-browser@5.2.0", "", { "dependencies": { "@aws-crypto/supports-web-crypto": "^5.2.0", "@aws-crypto/util": "^5.2.0", "@aws-sdk/types": "^3.222.0", "@aws-sdk/util-locate-window": "^3.0.0", "@smithy/util-utf8": "^2.0.0", "tslib": "^2.6.2" } }, "sha512-OH6lveCFfcDjX4dbAvCFSYUjJZjDr/3XJ3xHtjn3Oj5b9RjojQo8npoLeA/bNwkOkrSQ0wgrHzXk4tDRxGKJeg=="], - - "@aws-crypto/sha256-browser": ["@aws-crypto/sha256-browser@5.2.0", "", { "dependencies": { "@aws-crypto/sha256-js": "^5.2.0", "@aws-crypto/supports-web-crypto": "^5.2.0", "@aws-crypto/util": "^5.2.0", "@aws-sdk/types": "^3.222.0", "@aws-sdk/util-locate-window": "^3.0.0", "@smithy/util-utf8": "^2.0.0", "tslib": "^2.6.2" } }, "sha512-AXfN/lGotSQwu6HNcEsIASo7kWXZ5HYWvfOmSNKDsEqC4OashTp8alTmaz+F7TC2L083SFv5RdB+qU3Vs1kZqw=="], - - "@aws-crypto/sha256-js": ["@aws-crypto/sha256-js@5.2.0", "", { "dependencies": { "@aws-crypto/util": "^5.2.0", "@aws-sdk/types": "^3.222.0", "tslib": "^2.6.2" } }, "sha512-FFQQyu7edu4ufvIZ+OadFpHHOt+eSTBaYaki44c+akjg7qZg9oOQeLlk77F6tSYqjDAFClrHJk9tMf0HdVyOvA=="], - - "@aws-crypto/supports-web-crypto": ["@aws-crypto/supports-web-crypto@5.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-iAvUotm021kM33eCdNfwIN//F77/IADDSs58i+MDaOqFrVjZo9bAal0NK7HurRuWLLpF1iLX7gbWrjHjeo+YFg=="], - - "@aws-crypto/util": ["@aws-crypto/util@5.2.0", "", { "dependencies": { "@aws-sdk/types": "^3.222.0", "@smithy/util-utf8": "^2.0.0", "tslib": "^2.6.2" } }, "sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ=="], - - "@aws-sdk/client-s3": ["@aws-sdk/client-s3@3.1009.0", "", { "dependencies": { "@aws-crypto/sha1-browser": "5.2.0", "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "^3.973.20", "@aws-sdk/credential-provider-node": "^3.972.21", "@aws-sdk/middleware-bucket-endpoint": "^3.972.8", "@aws-sdk/middleware-expect-continue": "^3.972.8", "@aws-sdk/middleware-flexible-checksums": "^3.973.6", "@aws-sdk/middleware-host-header": "^3.972.8", "@aws-sdk/middleware-location-constraint": "^3.972.8", "@aws-sdk/middleware-logger": "^3.972.8", "@aws-sdk/middleware-recursion-detection": "^3.972.8", "@aws-sdk/middleware-sdk-s3": "^3.972.20", "@aws-sdk/middleware-ssec": "^3.972.8", "@aws-sdk/middleware-user-agent": "^3.972.21", "@aws-sdk/region-config-resolver": "^3.972.8", "@aws-sdk/signature-v4-multi-region": "^3.996.8", "@aws-sdk/types": "^3.973.6", "@aws-sdk/util-endpoints": "^3.996.5", "@aws-sdk/util-user-agent-browser": "^3.972.8", "@aws-sdk/util-user-agent-node": "^3.973.7", "@smithy/config-resolver": "^4.4.11", "@smithy/core": "^3.23.11", "@smithy/eventstream-serde-browser": "^4.2.12", "@smithy/eventstream-serde-config-resolver": "^4.3.12", "@smithy/eventstream-serde-node": "^4.2.12", "@smithy/fetch-http-handler": "^5.3.15", "@smithy/hash-blob-browser": "^4.2.13", "@smithy/hash-node": "^4.2.12", "@smithy/hash-stream-node": "^4.2.12", "@smithy/invalid-dependency": "^4.2.12", "@smithy/md5-js": "^4.2.12", "@smithy/middleware-content-length": "^4.2.12", "@smithy/middleware-endpoint": "^4.4.25", "@smithy/middleware-retry": "^4.4.42", "@smithy/middleware-serde": "^4.2.14", "@smithy/middleware-stack": "^4.2.12", "@smithy/node-config-provider": "^4.3.12", "@smithy/node-http-handler": "^4.4.16", "@smithy/protocol-http": "^5.3.12", "@smithy/smithy-client": "^4.12.5", "@smithy/types": "^4.13.1", "@smithy/url-parser": "^4.2.12", "@smithy/util-base64": "^4.3.2", "@smithy/util-body-length-browser": "^4.2.2", "@smithy/util-body-length-node": "^4.2.3", "@smithy/util-defaults-mode-browser": "^4.3.41", "@smithy/util-defaults-mode-node": "^4.2.44", "@smithy/util-endpoints": "^3.3.3", "@smithy/util-middleware": "^4.2.12", "@smithy/util-retry": "^4.2.12", "@smithy/util-stream": "^4.5.19", "@smithy/util-utf8": "^4.2.2", "@smithy/util-waiter": "^4.2.13", "tslib": "^2.6.2" } }, "sha512-luy8CxallkoiGWTqU86ca/BbvkWJjs0oala7uIIRN1JtQxMb5i4Yl/PBZVcQFhbK9kQi0PK0GfD8gIpLkI91fw=="], - - "@aws-sdk/core": ["@aws-sdk/core@3.973.20", "", { "dependencies": { "@aws-sdk/types": "^3.973.6", "@aws-sdk/xml-builder": "^3.972.11", "@smithy/core": "^3.23.11", "@smithy/node-config-provider": "^4.3.12", "@smithy/property-provider": "^4.2.12", "@smithy/protocol-http": "^5.3.12", "@smithy/signature-v4": "^5.3.12", "@smithy/smithy-client": "^4.12.5", "@smithy/types": "^4.13.1", "@smithy/util-base64": "^4.3.2", "@smithy/util-middleware": "^4.2.12", "@smithy/util-utf8": "^4.2.2", "tslib": "^2.6.2" } }, "sha512-i3GuX+lowD892F3IuJf8o6AbyDupMTdyTxQrCJGcn71ni5hTZ82L4nQhcdumxZ7XPJRJJVHS/CR3uYOIIs0PVA=="], - - "@aws-sdk/crc64-nvme": ["@aws-sdk/crc64-nvme@3.972.5", "", { "dependencies": { "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-2VbTstbjKdT+yKi8m7b3a9CiVac+pL/IY2PHJwsaGkkHmuuqkJZIErPck1h6P3T9ghQMLSdMPyW6Qp7Di5swFg=="], - - "@aws-sdk/credential-provider-env": ["@aws-sdk/credential-provider-env@3.972.18", "", { "dependencies": { "@aws-sdk/core": "^3.973.20", "@aws-sdk/types": "^3.973.6", "@smithy/property-provider": "^4.2.12", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-X0B8AlQY507i5DwjLByeU2Af4ARsl9Vr84koDcXCbAkplmU+1xBFWxEPrWRAoh56waBne/yJqEloSwvRf4x6XA=="], - - "@aws-sdk/credential-provider-http": ["@aws-sdk/credential-provider-http@3.972.20", "", { "dependencies": { "@aws-sdk/core": "^3.973.20", "@aws-sdk/types": "^3.973.6", "@smithy/fetch-http-handler": "^5.3.15", "@smithy/node-http-handler": "^4.4.16", "@smithy/property-provider": "^4.2.12", "@smithy/protocol-http": "^5.3.12", "@smithy/smithy-client": "^4.12.5", "@smithy/types": "^4.13.1", "@smithy/util-stream": "^4.5.19", "tslib": "^2.6.2" } }, "sha512-ey9Lelj001+oOfrbKmS6R2CJAiXX7QKY4Vj9VJv6L2eE6/VjD8DocHIoYqztTm70xDLR4E1jYPTKfIui+eRNDA=="], - - "@aws-sdk/credential-provider-ini": ["@aws-sdk/credential-provider-ini@3.972.20", "", { "dependencies": { "@aws-sdk/core": "^3.973.20", "@aws-sdk/credential-provider-env": "^3.972.18", "@aws-sdk/credential-provider-http": "^3.972.20", "@aws-sdk/credential-provider-login": "^3.972.20", "@aws-sdk/credential-provider-process": "^3.972.18", "@aws-sdk/credential-provider-sso": "^3.972.20", "@aws-sdk/credential-provider-web-identity": "^3.972.20", "@aws-sdk/nested-clients": "^3.996.10", "@aws-sdk/types": "^3.973.6", "@smithy/credential-provider-imds": "^4.2.12", "@smithy/property-provider": "^4.2.12", "@smithy/shared-ini-file-loader": "^4.4.7", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-5flXSnKHMloObNF+9N0cupKegnH1Z37cdVlpETVgx8/rAhCe+VNlkcZH3HDg2SDn9bI765S+rhNPXGDJJPfbtA=="], - - "@aws-sdk/credential-provider-login": ["@aws-sdk/credential-provider-login@3.972.20", "", { "dependencies": { "@aws-sdk/core": "^3.973.20", "@aws-sdk/nested-clients": "^3.996.10", "@aws-sdk/types": "^3.973.6", "@smithy/property-provider": "^4.2.12", "@smithy/protocol-http": "^5.3.12", "@smithy/shared-ini-file-loader": "^4.4.7", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-gEWo54nfqp2jABMu6HNsjVC4hDLpg9HC8IKSJnp0kqWtxIJYHTmiLSsIfI4ScQjxEwpB+jOOH8dOLax1+hy/Hw=="], - - "@aws-sdk/credential-provider-node": ["@aws-sdk/credential-provider-node@3.972.21", "", { "dependencies": { "@aws-sdk/credential-provider-env": "^3.972.18", "@aws-sdk/credential-provider-http": "^3.972.20", "@aws-sdk/credential-provider-ini": "^3.972.20", "@aws-sdk/credential-provider-process": "^3.972.18", "@aws-sdk/credential-provider-sso": "^3.972.20", "@aws-sdk/credential-provider-web-identity": "^3.972.20", "@aws-sdk/types": "^3.973.6", "@smithy/credential-provider-imds": "^4.2.12", "@smithy/property-provider": "^4.2.12", "@smithy/shared-ini-file-loader": "^4.4.7", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-hah8if3/B/Q+LBYN5FukyQ1Mym6PLPDsBOBsIgNEYD6wLyZg0UmUF/OKIVC3nX9XH8TfTPuITK+7N/jenVACWA=="], - - "@aws-sdk/credential-provider-process": ["@aws-sdk/credential-provider-process@3.972.18", "", { "dependencies": { "@aws-sdk/core": "^3.973.20", "@aws-sdk/types": "^3.973.6", "@smithy/property-provider": "^4.2.12", "@smithy/shared-ini-file-loader": "^4.4.7", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-Tpl7SRaPoOLT32jbTWchPsn52hYYgJ0kpiFgnwk8pxTANQdUymVSZkzFvv1+oOgZm1CrbQUP9MBeoMZ9IzLZjA=="], - - "@aws-sdk/credential-provider-sso": ["@aws-sdk/credential-provider-sso@3.972.20", "", { "dependencies": { "@aws-sdk/core": "^3.973.20", "@aws-sdk/nested-clients": "^3.996.10", "@aws-sdk/token-providers": "3.1009.0", "@aws-sdk/types": "^3.973.6", "@smithy/property-provider": "^4.2.12", "@smithy/shared-ini-file-loader": "^4.4.7", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-p+R+PYR5Z7Gjqf/6pvbCnzEHcqPCpLzR7Yf127HjJ6EAb4hUcD+qsNRnuww1sB/RmSeCLxyay8FMyqREw4p1RA=="], - - "@aws-sdk/credential-provider-web-identity": ["@aws-sdk/credential-provider-web-identity@3.972.20", "", { "dependencies": { "@aws-sdk/core": "^3.973.20", "@aws-sdk/nested-clients": "^3.996.10", "@aws-sdk/types": "^3.973.6", "@smithy/property-provider": "^4.2.12", "@smithy/shared-ini-file-loader": "^4.4.7", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-rWCmh8o7QY4CsUj63qopzMzkDq/yPpkrpb+CnjBEFSOg/02T/we7sSTVg4QsDiVS9uwZ8VyONhq98qt+pIh3KA=="], - - "@aws-sdk/middleware-bucket-endpoint": ["@aws-sdk/middleware-bucket-endpoint@3.972.8", "", { "dependencies": { "@aws-sdk/types": "^3.973.6", "@aws-sdk/util-arn-parser": "^3.972.3", "@smithy/node-config-provider": "^4.3.12", "@smithy/protocol-http": "^5.3.12", "@smithy/types": "^4.13.1", "@smithy/util-config-provider": "^4.2.2", "tslib": "^2.6.2" } }, "sha512-WR525Rr2QJSETa9a050isktyWi/4yIGcmY3BQ1kpHqb0LqUglQHCS8R27dTJxxWNZvQ0RVGtEZjTCbZJpyF3Aw=="], - - "@aws-sdk/middleware-expect-continue": ["@aws-sdk/middleware-expect-continue@3.972.8", "", { "dependencies": { "@aws-sdk/types": "^3.973.6", "@smithy/protocol-http": "^5.3.12", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-5DTBTiotEES1e2jOHAq//zyzCjeMB78lEHd35u15qnrid4Nxm7diqIf9fQQ3Ov0ChH1V3Vvt13thOnrACmfGVQ=="], + "@aws-sdk/checksums": ["@aws-sdk/checksums@3.1000.29", "", { "dependencies": { "@aws-sdk/core": "^3.977.9", "@aws-sdk/types": "^3.974.5", "@smithy/core": "^3.33.3", "@smithy/types": "^4.17.2", "tslib": "^2.6.2" } }, "sha512-Dtu0gr4dnATZAPwEYbpCsG+MpLM7OAliy2gTepEFQwl1vZ6DL3QMH2FveMa3HLvPsOdhJsPRB3KtxVhph9T75A=="], - "@aws-sdk/middleware-flexible-checksums": ["@aws-sdk/middleware-flexible-checksums@3.973.6", "", { "dependencies": { "@aws-crypto/crc32": "5.2.0", "@aws-crypto/crc32c": "5.2.0", "@aws-crypto/util": "5.2.0", "@aws-sdk/core": "^3.973.20", "@aws-sdk/crc64-nvme": "^3.972.5", "@aws-sdk/types": "^3.973.6", "@smithy/is-array-buffer": "^4.2.2", "@smithy/node-config-provider": "^4.3.12", "@smithy/protocol-http": "^5.3.12", "@smithy/types": "^4.13.1", "@smithy/util-middleware": "^4.2.12", "@smithy/util-stream": "^4.5.19", "@smithy/util-utf8": "^4.2.2", "tslib": "^2.6.2" } }, "sha512-0nYEgkJH7Yt9k+nZJyllTghnkKaz17TWFcr5Mi0XMVMzYlF4ytDZADQpF2/iJo36cKL5AYSzRsvlykE4M/ErTA=="], + "@aws-sdk/client-s3": ["@aws-sdk/client-s3@3.1128.0", "", { "dependencies": { "@aws-sdk/checksums": "^3.1000.29", "@aws-sdk/core": "^3.977.9", "@aws-sdk/credential-provider-node": "^3.972.82", "@aws-sdk/middleware-sdk-s3": "^3.972.75", "@aws-sdk/signature-v4-multi-region": "^3.996.46", "@aws-sdk/types": "^3.974.5", "@smithy/core": "^3.33.3", "@smithy/fetch-http-handler": "^5.7.2", "@smithy/node-http-handler": "^4.11.3", "@smithy/types": "^4.17.2", "tslib": "^2.6.2" } }, "sha512-tYEB4058LdhTiSS7sCVVSpqSAdjIc1jTaf1dDPoIRJbR/XI5A2ZOuCiV1Aebo/pria/pUJBOT0WjdZVIwaZtDA=="], - "@aws-sdk/middleware-host-header": ["@aws-sdk/middleware-host-header@3.972.8", "", { "dependencies": { "@aws-sdk/types": "^3.973.6", "@smithy/protocol-http": "^5.3.12", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-wAr2REfKsqoKQ+OkNqvOShnBoh+nkPurDKW7uAeVSu6kUECnWlSJiPvnoqxGlfousEY/v9LfS9sNc46hjSYDIQ=="], + "@aws-sdk/core": ["@aws-sdk/core@3.977.9", "", { "dependencies": { "@aws-sdk/types": "^3.974.5", "@aws-sdk/xml-builder": "^3.972.40", "@aws/lambda-invoke-store": "^0.3.0", "@smithy/core": "^3.33.3", "@smithy/signature-v4": "^5.6.12", "@smithy/types": "^4.17.2", "bowser": "^2.11.0", "tslib": "^2.6.2" } }, "sha512-reqPFEQrZxDZpeGj4PFMepBeR5LGYHRqq/L0motTzgFkCRBA4rFdaVXDSLYyGHhxVz7sT2PDnPN9CluGSfgyJA=="], - "@aws-sdk/middleware-location-constraint": ["@aws-sdk/middleware-location-constraint@3.972.8", "", { "dependencies": { "@aws-sdk/types": "^3.973.6", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-KaUoFuoFPziIa98DSQsTPeke1gvGXlc5ZGMhy+b+nLxZ4A7jmJgLzjEF95l8aOQN2T/qlPP3MrAyELm8ExXucw=="], + "@aws-sdk/credential-provider-env": ["@aws-sdk/credential-provider-env@3.972.70", "", { "dependencies": { "@aws-sdk/core": "^3.977.9", "@aws-sdk/types": "^3.974.5", "@smithy/core": "^3.33.3", "@smithy/types": "^4.17.2", "tslib": "^2.6.2" } }, "sha512-H404B7dJl2mCrBqahDEYsanB0xhdDp6tXnXcTUnXmmpy2Q3J0Ho0bUajZ2jr/RdwzCyS59Gi8xXIFwPLGBl6Uw=="], - "@aws-sdk/middleware-logger": ["@aws-sdk/middleware-logger@3.972.8", "", { "dependencies": { "@aws-sdk/types": "^3.973.6", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-CWl5UCM57WUFaFi5kB7IBY1UmOeLvNZAZ2/OZ5l20ldiJ3TiIz1pC65gYj8X0BCPWkeR1E32mpsCk1L1I4n+lA=="], + "@aws-sdk/credential-provider-http": ["@aws-sdk/credential-provider-http@3.972.72", "", { "dependencies": { "@aws-sdk/core": "^3.977.9", "@aws-sdk/types": "^3.974.5", "@smithy/core": "^3.33.3", "@smithy/fetch-http-handler": "^5.7.2", "@smithy/node-http-handler": "^4.11.3", "@smithy/types": "^4.17.2", "tslib": "^2.6.2" } }, "sha512-X98zYOrVOeuosCX+6ktf29FC2N2GHPLia7qv6mzPzTc+RPAuHWCDS++Z6JK7eGYqb/v6uaW7bAXaOvDBfol+0w=="], - "@aws-sdk/middleware-recursion-detection": ["@aws-sdk/middleware-recursion-detection@3.972.8", "", { "dependencies": { "@aws-sdk/types": "^3.973.6", "@aws/lambda-invoke-store": "^0.2.2", "@smithy/protocol-http": "^5.3.12", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-BnnvYs2ZEpdlmZ2PNlV2ZyQ8j8AEkMTjN79y/YA475ER1ByFYrkVR85qmhni8oeTaJcDqbx364wDpitDAA/wCA=="], + "@aws-sdk/credential-provider-ini": ["@aws-sdk/credential-provider-ini@3.973.15", "", { "dependencies": { "@aws-sdk/core": "^3.977.9", "@aws-sdk/credential-provider-env": "^3.972.70", "@aws-sdk/credential-provider-http": "^3.972.72", "@aws-sdk/credential-provider-login": "^3.972.77", "@aws-sdk/credential-provider-process": "^3.972.70", "@aws-sdk/credential-provider-sso": "^3.973.14", "@aws-sdk/credential-provider-web-identity": "^3.972.76", "@aws-sdk/nested-clients": "^3.997.44", "@aws-sdk/types": "^3.974.5", "@smithy/core": "^3.33.3", "@smithy/credential-provider-imds": "^4.4.16", "@smithy/types": "^4.17.2", "tslib": "^2.6.2" } }, "sha512-Rykg6s5ceBuynMOGWgoowO4N+27JfnqXAnVaSunZl0hOO1XodSrxGNz6sCEbnmS0lAfQZDKyb3fbr46gSuv6Sg=="], - "@aws-sdk/middleware-sdk-s3": ["@aws-sdk/middleware-sdk-s3@3.972.20", "", { "dependencies": { "@aws-sdk/core": "^3.973.20", "@aws-sdk/types": "^3.973.6", "@aws-sdk/util-arn-parser": "^3.972.3", "@smithy/core": "^3.23.11", "@smithy/node-config-provider": "^4.3.12", "@smithy/protocol-http": "^5.3.12", "@smithy/signature-v4": "^5.3.12", "@smithy/smithy-client": "^4.12.5", "@smithy/types": "^4.13.1", "@smithy/util-config-provider": "^4.2.2", "@smithy/util-middleware": "^4.2.12", "@smithy/util-stream": "^4.5.19", "@smithy/util-utf8": "^4.2.2", "tslib": "^2.6.2" } }, "sha512-yhva/xL5H4tWQgsBjwV+RRD0ByCzg0TcByDCLp3GXdn/wlyRNfy8zsswDtCvr1WSKQkSQYlyEzPuWkJG0f5HvQ=="], + "@aws-sdk/credential-provider-login": ["@aws-sdk/credential-provider-login@3.972.77", "", { "dependencies": { "@aws-sdk/core": "^3.977.9", "@aws-sdk/nested-clients": "^3.997.44", "@aws-sdk/types": "^3.974.5", "@smithy/core": "^3.33.3", "@smithy/types": "^4.17.2", "tslib": "^2.6.2" } }, "sha512-Jb59xfEISoN5mmbnA+HYqdtrSX3CgCtJoof+V5D8/TgUI56W63GEEd5Y58WijU3Ou6+WEgaLD1feVzaRXV5IDQ=="], - "@aws-sdk/middleware-ssec": ["@aws-sdk/middleware-ssec@3.972.8", "", { "dependencies": { "@aws-sdk/types": "^3.973.6", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-wqlK0yO/TxEC2UsY9wIlqeeutF6jjLe0f96Pbm40XscTo57nImUk9lBcw0dPgsm0sppFtAkSlDrfpK+pC30Wqw=="], + "@aws-sdk/credential-provider-node": ["@aws-sdk/credential-provider-node@3.972.82", "", { "dependencies": { "@aws-sdk/credential-provider-env": "^3.972.70", "@aws-sdk/credential-provider-http": "^3.972.72", "@aws-sdk/credential-provider-ini": "^3.973.15", "@aws-sdk/credential-provider-process": "^3.972.70", "@aws-sdk/credential-provider-sso": "^3.973.14", "@aws-sdk/credential-provider-web-identity": "^3.972.76", "@aws-sdk/types": "^3.974.5", "@smithy/core": "^3.33.3", "@smithy/credential-provider-imds": "^4.4.16", "@smithy/types": "^4.17.2", "tslib": "^2.6.2" } }, "sha512-znDkEOGXB8W3kG1LJUKP3foBZY/9qLM0eil/DxWXSp37XsdsRLQHE/d/OaCGGVgKpA6znR38h/+INk8do1FjiA=="], - "@aws-sdk/middleware-user-agent": ["@aws-sdk/middleware-user-agent@3.972.21", "", { "dependencies": { "@aws-sdk/core": "^3.973.20", "@aws-sdk/types": "^3.973.6", "@aws-sdk/util-endpoints": "^3.996.5", "@smithy/core": "^3.23.11", "@smithy/protocol-http": "^5.3.12", "@smithy/types": "^4.13.1", "@smithy/util-retry": "^4.2.12", "tslib": "^2.6.2" } }, "sha512-62XRl1GDYPpkt7cx1AX1SPy9wgNE9Iw/NPuurJu4lmhCWS7sGKO+kS53TQ8eRmIxy3skmvNInnk0ZbWrU5Dpyg=="], + "@aws-sdk/credential-provider-process": ["@aws-sdk/credential-provider-process@3.972.70", "", { "dependencies": { "@aws-sdk/core": "^3.977.9", "@aws-sdk/types": "^3.974.5", "@smithy/core": "^3.33.3", "@smithy/types": "^4.17.2", "tslib": "^2.6.2" } }, "sha512-2ry03fGRJr4sV3jI+ocjj5JqALnFD6ymM5KiNCDZMvq8bX2GSbE0vji4aM43TVCl2nXqqLRZaUxdq/KeWRAY4Q=="], - "@aws-sdk/nested-clients": ["@aws-sdk/nested-clients@3.996.10", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "^3.973.20", "@aws-sdk/middleware-host-header": "^3.972.8", "@aws-sdk/middleware-logger": "^3.972.8", "@aws-sdk/middleware-recursion-detection": "^3.972.8", "@aws-sdk/middleware-user-agent": "^3.972.21", "@aws-sdk/region-config-resolver": "^3.972.8", "@aws-sdk/types": "^3.973.6", "@aws-sdk/util-endpoints": "^3.996.5", "@aws-sdk/util-user-agent-browser": "^3.972.8", "@aws-sdk/util-user-agent-node": "^3.973.7", "@smithy/config-resolver": "^4.4.11", "@smithy/core": "^3.23.11", "@smithy/fetch-http-handler": "^5.3.15", "@smithy/hash-node": "^4.2.12", "@smithy/invalid-dependency": "^4.2.12", "@smithy/middleware-content-length": "^4.2.12", "@smithy/middleware-endpoint": "^4.4.25", "@smithy/middleware-retry": "^4.4.42", "@smithy/middleware-serde": "^4.2.14", "@smithy/middleware-stack": "^4.2.12", "@smithy/node-config-provider": "^4.3.12", "@smithy/node-http-handler": "^4.4.16", "@smithy/protocol-http": "^5.3.12", "@smithy/smithy-client": "^4.12.5", "@smithy/types": "^4.13.1", "@smithy/url-parser": "^4.2.12", "@smithy/util-base64": "^4.3.2", "@smithy/util-body-length-browser": "^4.2.2", "@smithy/util-body-length-node": "^4.2.3", "@smithy/util-defaults-mode-browser": "^4.3.41", "@smithy/util-defaults-mode-node": "^4.2.44", "@smithy/util-endpoints": "^3.3.3", "@smithy/util-middleware": "^4.2.12", "@smithy/util-retry": "^4.2.12", "@smithy/util-utf8": "^4.2.2", "tslib": "^2.6.2" } }, "sha512-SlDol5Z+C7Ivnc2rKGqiqfSUmUZzY1qHfVs9myt/nxVwswgfpjdKahyTzLTx802Zfq0NFRs7AejwKzzzl5Co2w=="], + "@aws-sdk/credential-provider-sso": ["@aws-sdk/credential-provider-sso@3.973.14", "", { "dependencies": { "@aws-sdk/core": "^3.977.9", "@aws-sdk/nested-clients": "^3.997.44", "@aws-sdk/token-providers": "3.1116.0", "@aws-sdk/types": "^3.974.5", "@smithy/core": "^3.33.3", "@smithy/types": "^4.17.2", "tslib": "^2.6.2" } }, "sha512-jkhg/8ocAAoc0RFyLMhCw+/zZh7gystQgd4F4hznNa8P4Cc501PQmxd+jGLiMHodPJ+7Zv/3znM62gZojyasmA=="], - "@aws-sdk/region-config-resolver": ["@aws-sdk/region-config-resolver@3.972.8", "", { "dependencies": { "@aws-sdk/types": "^3.973.6", "@smithy/config-resolver": "^4.4.11", "@smithy/node-config-provider": "^4.3.12", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-1eD4uhTDeambO/PNIDVG19A6+v4NdD7xzwLHDutHsUqz0B+i661MwQB2eYO4/crcCvCiQG4SRm1k81k54FEIvw=="], + "@aws-sdk/credential-provider-web-identity": ["@aws-sdk/credential-provider-web-identity@3.972.76", "", { "dependencies": { "@aws-sdk/core": "^3.977.9", "@aws-sdk/nested-clients": "^3.997.44", "@aws-sdk/types": "^3.974.5", "@smithy/core": "^3.33.3", "@smithy/types": "^4.17.2", "tslib": "^2.6.2" } }, "sha512-d3AGyVu759PGr35mEB2s22xxlNEA5rpdxtSPJthfPFJvoQ8dt357iVPECqWfUxXp1toJAvKmbtcIYVGigaGsCA=="], - "@aws-sdk/signature-v4-multi-region": ["@aws-sdk/signature-v4-multi-region@3.996.8", "", { "dependencies": { "@aws-sdk/middleware-sdk-s3": "^3.972.20", "@aws-sdk/types": "^3.973.6", "@smithy/protocol-http": "^5.3.12", "@smithy/signature-v4": "^5.3.12", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-n1qYFD+tbqZuyskVaxUE+t10AUz9g3qzDw3Tp6QZDKmqsjfDmZBd4GIk2EKJJNtcCBtE5YiUjDYA+3djFAFBBg=="], + "@aws-sdk/middleware-sdk-s3": ["@aws-sdk/middleware-sdk-s3@3.972.75", "", { "dependencies": { "@aws-sdk/core": "^3.977.9", "@aws-sdk/signature-v4-multi-region": "^3.996.46", "@aws-sdk/types": "^3.974.5", "@smithy/core": "^3.33.3", "@smithy/types": "^4.17.2", "tslib": "^2.6.2" } }, "sha512-wMIsNumRVKaNMKhvU/s9VrdEwE8S6gSzXp4RygFG5BEMnGkkXf8cjh8zf7cKJBpUDpqTWqwbz5isEgp9rH6Lng=="], - "@aws-sdk/token-providers": ["@aws-sdk/token-providers@3.1009.0", "", { "dependencies": { "@aws-sdk/core": "^3.973.20", "@aws-sdk/nested-clients": "^3.996.10", "@aws-sdk/types": "^3.973.6", "@smithy/property-provider": "^4.2.12", "@smithy/shared-ini-file-loader": "^4.4.7", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-KCPLuTqN9u0Rr38Arln78fRG9KXpzsPWmof+PZzfAHMMQq2QED6YjQrkrfiH7PDefLWEposY1o4/eGwrmKA4JA=="], + "@aws-sdk/nested-clients": ["@aws-sdk/nested-clients@3.997.44", "", { "dependencies": { "@aws-sdk/core": "^3.977.9", "@aws-sdk/signature-v4-multi-region": "^3.996.46", "@aws-sdk/types": "^3.974.5", "@smithy/core": "^3.33.3", "@smithy/fetch-http-handler": "^5.7.2", "@smithy/node-http-handler": "^4.11.3", "@smithy/types": "^4.17.2", "tslib": "^2.6.2" } }, "sha512-NhEgryjlBF9w38ZXqGymQV28IhkYa1mKhlbYnqIis57AYwWGVYfUPgg/qC2rLRqOUfblxx++irvju10kVTa8Vw=="], - "@aws-sdk/types": ["@aws-sdk/types@3.973.6", "", { "dependencies": { "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-Atfcy4E++beKtwJHiDln2Nby8W/mam64opFPTiHEqgsthqeydFS1pY+OUlN1ouNOmf8ArPU/6cDS65anOP3KQw=="], + "@aws-sdk/signature-v4-multi-region": ["@aws-sdk/signature-v4-multi-region@3.996.46", "", { "dependencies": { "@aws-sdk/types": "^3.974.5", "@smithy/signature-v4": "^5.6.12", "@smithy/types": "^4.17.2", "tslib": "^2.6.2" } }, "sha512-L+2xZTye/2T96f3lwCws0Zw6GG2JHZW9e8FpVgGBeeExSKyeoZ6CWRpBml/7DNiK/O26jrgPM9F+Ay8VkgzUWQ=="], - "@aws-sdk/util-arn-parser": ["@aws-sdk/util-arn-parser@3.972.3", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-HzSD8PMFrvgi2Kserxuff5VitNq2sgf3w9qxmskKDiDTThWfVteJxuCS9JXiPIPtmCrp+7N9asfIaVhBFORllA=="], + "@aws-sdk/token-providers": ["@aws-sdk/token-providers@3.1116.0", "", { "dependencies": { "@aws-sdk/core": "^3.977.9", "@aws-sdk/nested-clients": "^3.997.44", "@aws-sdk/types": "^3.974.5", "@smithy/core": "^3.33.3", "@smithy/types": "^4.17.2", "tslib": "^2.6.2" } }, "sha512-ygIivKqh8aHzNkucOCXHyIBgBpLPfrSI0mCqXF+vLBsPTUKqj0VSqAY0GFPe7lQl4HntjOcQ+KSyS7oUV2C54Q=="], - "@aws-sdk/util-endpoints": ["@aws-sdk/util-endpoints@3.996.5", "", { "dependencies": { "@aws-sdk/types": "^3.973.6", "@smithy/types": "^4.13.1", "@smithy/url-parser": "^4.2.12", "@smithy/util-endpoints": "^3.3.3", "tslib": "^2.6.2" } }, "sha512-Uh93L5sXFNbyR5sEPMzUU8tJ++Ku97EY4udmC01nB8Zu+xfBPwpIwJ6F7snqQeq8h2pf+8SGN5/NoytfKgYPIw=="], + "@aws-sdk/types": ["@aws-sdk/types@3.974.5", "", { "dependencies": { "@smithy/types": "^4.17.2", "tslib": "^2.6.2" } }, "sha512-LkwLL2BLbC6wNNm4JaH9mbEqBMdOZCct6VAYqhdN4U1xrWM+fUJQEfbHwQgDypapOWTRtlk25akb5afM0P8CIQ=="], - "@aws-sdk/util-locate-window": ["@aws-sdk/util-locate-window@3.965.5", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-WhlJNNINQB+9qtLtZJcpQdgZw3SCDCpXdUJP7cToGwHbCWCnRckGlc6Bx/OhWwIYFNAn+FIydY8SZ0QmVu3xTQ=="], + "@aws-sdk/xml-builder": ["@aws-sdk/xml-builder@3.972.40", "", { "dependencies": { "@smithy/types": "^4.17.2", "tslib": "^2.6.2" } }, "sha512-wlFmCIGUlwF4zx/kncw+bmxTQh1HeSJq4mYV/V5cZUSJadDP3kXvGW8Rn21cimj/7y9ju+47oYWXi97vF7czaA=="], - "@aws-sdk/util-user-agent-browser": ["@aws-sdk/util-user-agent-browser@3.972.8", "", { "dependencies": { "@aws-sdk/types": "^3.973.6", "@smithy/types": "^4.13.1", "bowser": "^2.11.0", "tslib": "^2.6.2" } }, "sha512-B3KGXJviV2u6Cdw2SDY2aDhoJkVfY/Q/Trwk2CMSkikE1Oi6gRzxhvhIfiRpHfmIsAhV4EA54TVEX8K6CbHbkA=="], + "@aws/lambda-invoke-store": ["@aws/lambda-invoke-store@0.3.0", "", {}, "sha512-sl4Bm6yiMNYrZKkqqDFWN0UfnWhlS8ivKxrYl+6t0gCLrqr8y3B2IqZZbFRkfaVVp7C/baApyh71P+LeE1A2sQ=="], - "@aws-sdk/util-user-agent-node": ["@aws-sdk/util-user-agent-node@3.973.7", "", { "dependencies": { "@aws-sdk/middleware-user-agent": "^3.972.21", "@aws-sdk/types": "^3.973.6", "@smithy/node-config-provider": "^4.3.12", "@smithy/types": "^4.13.1", "@smithy/util-config-provider": "^4.2.2", "tslib": "^2.6.2" }, "peerDependencies": { "aws-crt": ">=1.0.0" }, "optionalPeers": ["aws-crt"] }, "sha512-Hz6EZMUAEzqUd7e+vZ9LE7mn+5gMbxltXy18v+YSFY+9LBJz15wkNZvw5JqfX3z0FS9n3bgUtz3L5rAsfh4YlA=="], + "@hono/node-server": ["@hono/node-server@1.19.15", "", { "peerDependencies": { "hono": "^4" } }, "sha512-Za2ai6TLdKjUvnur+eenO6nuYYipVAEhyCAdaV8IRvmU9kK8crOZUSYvIXn72E4f8fJqyAbpcJuTsYYmZp9Deg=="], - "@aws-sdk/xml-builder": ["@aws-sdk/xml-builder@3.972.11", "", { "dependencies": { "@smithy/types": "^4.13.1", "fast-xml-parser": "5.4.1", "tslib": "^2.6.2" } }, "sha512-iitV/gZKQMvY9d7ovmyFnFuTHbBAtrmLnvaSb/3X8vOKyevwtpmEtyc8AdhVWZe0pI/1GsHxlEvQeOePFzy7KQ=="], - - "@aws/lambda-invoke-store": ["@aws/lambda-invoke-store@0.2.4", "", {}, "sha512-iY8yvjE0y651BixKNPgmv1WrQc+GZ142sb0z4gYnChDDY2YqI4P/jsSopBWrKfAt7LOJAkOXt7rC/hms+WclQQ=="], - - "@hono/node-server": ["@hono/node-server@1.19.11", "", { "peerDependencies": { "hono": "^4" } }, "sha512-dr8/3zEaB+p0D2n/IUrlPF1HZm586qgJNXK1a9fhg/PzdtkK7Ksd5l312tJX2yBuALqDYBlG20QEbayqPyxn+g=="], - - "@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.27.1", "", { "dependencies": { "@hono/node-server": "^1.19.9", "ajv": "^8.17.1", "ajv-formats": "^3.0.1", "content-type": "^1.0.5", "cors": "^2.8.5", "cross-spawn": "^7.0.5", "eventsource": "^3.0.2", "eventsource-parser": "^3.0.0", "express": "^5.2.1", "express-rate-limit": "^8.2.1", "hono": "^4.11.4", "jose": "^6.1.3", "json-schema-typed": "^8.0.2", "pkce-challenge": "^5.0.0", "raw-body": "^3.0.0", "zod": "^3.25 || ^4.0", "zod-to-json-schema": "^3.25.1" }, "peerDependencies": { "@cfworker/json-schema": "^4.1.1" }, "optionalPeers": ["@cfworker/json-schema"] }, "sha512-sr6GbP+4edBwFndLbM60gf07z0FQ79gaExpnsjMGePXqFcSSb7t6iscpjk9DhFhwd+mTEQrzNafGP8/iGGFYaA=="], + "@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.30.0", "", { "dependencies": { "@hono/node-server": "^1.19.9 || ^2.0.5", "ajv": "^8.17.1", "ajv-formats": "^3.0.1", "content-type": "^1.0.5", "cors": "^2.8.5", "cross-spawn": "^7.0.5", "eventsource": "^3.0.2", "eventsource-parser": "^3.0.0", "express": "^5.2.1", "express-rate-limit": "^8.2.1", "hono": "^4.11.4", "jose": "^6.1.3", "json-schema-typed": "^8.0.2", "pkce-challenge": "^5.0.0", "raw-body": "^3.0.0", "zod": "^3.25 || ^4.0", "zod-to-json-schema": "^3.25.1" }, "peerDependencies": { "@cfworker/json-schema": "^4.1.1" }, "optionalPeers": ["@cfworker/json-schema"] }, "sha512-xKd8OIzlqNzcqcNumGAa6g+PW2kjD5vrpcKOnfldAUPP3j7lnqMPwlTXQm8gF+UwH72z0lqaRbjr9hqGz0eITA=="], "@napi-rs/canvas": ["@napi-rs/canvas@0.1.80", "", { "optionalDependencies": { "@napi-rs/canvas-android-arm64": "0.1.80", "@napi-rs/canvas-darwin-arm64": "0.1.80", "@napi-rs/canvas-darwin-x64": "0.1.80", "@napi-rs/canvas-linux-arm-gnueabihf": "0.1.80", "@napi-rs/canvas-linux-arm64-gnu": "0.1.80", "@napi-rs/canvas-linux-arm64-musl": "0.1.80", "@napi-rs/canvas-linux-riscv64-gnu": "0.1.80", "@napi-rs/canvas-linux-x64-gnu": "0.1.80", "@napi-rs/canvas-linux-x64-musl": "0.1.80", "@napi-rs/canvas-win32-x64-msvc": "0.1.80" } }, "sha512-DxuT1ClnIPts1kQx8FBmkk4BQDTfI5kIzywAaMjQSXfNnra5UFU9PwurXrl+Je3bJ6BGsp/zmshVVFbCmyI+ww=="], @@ -124,109 +88,19 @@ "@napi-rs/canvas-win32-x64-msvc": ["@napi-rs/canvas-win32-x64-msvc@0.1.80", "", { "os": "win32", "cpu": "x64" }, "sha512-Z8jPsM6df5V8B1HrCHB05+bDiCxjE9QA//3YrkKIdVDEwn5RKaqOxCJDRJkl48cJbylcrJbW4HxZbTte8juuPg=="], - "@smithy/abort-controller": ["@smithy/abort-controller@4.2.12", "", { "dependencies": { "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-xolrFw6b+2iYGl6EcOL7IJY71vvyZ0DJ3mcKtpykqPe2uscwtzDZJa1uVQXyP7w9Dd+kGwYnPbMsJrGISKiY/Q=="], - - "@smithy/chunked-blob-reader": ["@smithy/chunked-blob-reader@5.2.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-St+kVicSyayWQca+I1rGitaOEH6uKgE8IUWoYnnEX26SWdWQcL6LvMSD19Lg+vYHKdT9B2Zuu7rd3i6Wnyb/iw=="], - - "@smithy/chunked-blob-reader-native": ["@smithy/chunked-blob-reader-native@4.2.3", "", { "dependencies": { "@smithy/util-base64": "^4.3.2", "tslib": "^2.6.2" } }, "sha512-jA5k5Udn7Y5717L86h4EIv06wIr3xn8GM1qHRi/Nf31annXcXHJjBKvgztnbn2TxH3xWrPBfgwHsOwZf0UmQWw=="], - - "@smithy/config-resolver": ["@smithy/config-resolver@4.4.11", "", { "dependencies": { "@smithy/node-config-provider": "^4.3.12", "@smithy/types": "^4.13.1", "@smithy/util-config-provider": "^4.2.2", "@smithy/util-endpoints": "^3.3.3", "@smithy/util-middleware": "^4.2.12", "tslib": "^2.6.2" } }, "sha512-YxFiiG4YDAtX7WMN7RuhHZLeTmRRAOyCbr+zB8e3AQzHPnUhS8zXjB1+cniPVQI3xbWsQPM0X2aaIkO/ME0ymw=="], - - "@smithy/core": ["@smithy/core@3.23.11", "", { "dependencies": { "@smithy/protocol-http": "^5.3.12", "@smithy/types": "^4.13.1", "@smithy/url-parser": "^4.2.12", "@smithy/util-base64": "^4.3.2", "@smithy/util-body-length-browser": "^4.2.2", "@smithy/util-middleware": "^4.2.12", "@smithy/util-stream": "^4.5.19", "@smithy/util-utf8": "^4.2.2", "@smithy/uuid": "^1.1.2", "tslib": "^2.6.2" } }, "sha512-952rGf7hBRnhUIaeLp6q4MptKW8sPFe5VvkoZ5qIzFAtx6c/QZ/54FS3yootsyUSf9gJX/NBqEBNdNR7jMIlpQ=="], - - "@smithy/credential-provider-imds": ["@smithy/credential-provider-imds@4.2.12", "", { "dependencies": { "@smithy/node-config-provider": "^4.3.12", "@smithy/property-provider": "^4.2.12", "@smithy/types": "^4.13.1", "@smithy/url-parser": "^4.2.12", "tslib": "^2.6.2" } }, "sha512-cr2lR792vNZcYMriSIj+Um3x9KWrjcu98kn234xA6reOAFMmbRpQMOv8KPgEmLLtx3eldU6c5wALKFqNOhugmg=="], - - "@smithy/eventstream-codec": ["@smithy/eventstream-codec@4.2.12", "", { "dependencies": { "@aws-crypto/crc32": "5.2.0", "@smithy/types": "^4.13.1", "@smithy/util-hex-encoding": "^4.2.2", "tslib": "^2.6.2" } }, "sha512-FE3bZdEl62ojmy8x4FHqxq2+BuOHlcxiH5vaZ6aqHJr3AIZzwF5jfx8dEiU/X0a8RboyNDjmXjlbr8AdEyLgiA=="], - - "@smithy/eventstream-serde-browser": ["@smithy/eventstream-serde-browser@4.2.12", "", { "dependencies": { "@smithy/eventstream-serde-universal": "^4.2.12", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-XUSuMxlTxV5pp4VpqZf6Sa3vT/Q75FVkLSpSSE3KkWBvAQWeuWt1msTv8fJfgA4/jcJhrbrbMzN1AC/hvPmm5A=="], - - "@smithy/eventstream-serde-config-resolver": ["@smithy/eventstream-serde-config-resolver@4.3.12", "", { "dependencies": { "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-7epsAZ3QvfHkngz6RXQYseyZYHlmWXSTPOfPmXkiS+zA6TBNo1awUaMFL9vxyXlGdoELmCZyZe1nQE+imbmV+Q=="], - - "@smithy/eventstream-serde-node": ["@smithy/eventstream-serde-node@4.2.12", "", { "dependencies": { "@smithy/eventstream-serde-universal": "^4.2.12", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-D1pFuExo31854eAvg89KMn9Oab/wEeJR6Buy32B49A9Ogdtx5fwZPqBHUlDzaCDpycTFk2+fSQgX689Qsk7UGA=="], - - "@smithy/eventstream-serde-universal": ["@smithy/eventstream-serde-universal@4.2.12", "", { "dependencies": { "@smithy/eventstream-codec": "^4.2.12", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-+yNuTiyBACxOJUTvbsNsSOfH9G9oKbaJE1lNL3YHpGcuucl6rPZMi3nrpehpVOVR2E07YqFFmtwpImtpzlouHQ=="], - - "@smithy/fetch-http-handler": ["@smithy/fetch-http-handler@5.3.15", "", { "dependencies": { "@smithy/protocol-http": "^5.3.12", "@smithy/querystring-builder": "^4.2.12", "@smithy/types": "^4.13.1", "@smithy/util-base64": "^4.3.2", "tslib": "^2.6.2" } }, "sha512-T4jFU5N/yiIfrtrsb9uOQn7RdELdM/7HbyLNr6uO/mpkj1ctiVs7CihVr51w4LyQlXWDpXFn4BElf1WmQvZu/A=="], - - "@smithy/hash-blob-browser": ["@smithy/hash-blob-browser@4.2.13", "", { "dependencies": { "@smithy/chunked-blob-reader": "^5.2.2", "@smithy/chunked-blob-reader-native": "^4.2.3", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-YrF4zWKh+ghLuquldj6e/RzE3xZYL8wIPfkt0MqCRphVICjyyjH8OwKD7LLlKpVEbk4FLizFfC1+gwK6XQdR3g=="], - - "@smithy/hash-node": ["@smithy/hash-node@4.2.12", "", { "dependencies": { "@smithy/types": "^4.13.1", "@smithy/util-buffer-from": "^4.2.2", "@smithy/util-utf8": "^4.2.2", "tslib": "^2.6.2" } }, "sha512-QhBYbGrbxTkZ43QoTPrK72DoYviDeg6YKDrHTMJbbC+A0sml3kSjzFtXP7BtbyJnXojLfTQldGdUR0RGD8dA3w=="], - - "@smithy/hash-stream-node": ["@smithy/hash-stream-node@4.2.12", "", { "dependencies": { "@smithy/types": "^4.13.1", "@smithy/util-utf8": "^4.2.2", "tslib": "^2.6.2" } }, "sha512-O3YbmGExeafuM/kP7Y8r6+1y0hIh3/zn6GROx0uNlB54K9oihAL75Qtc+jFfLNliTi6pxOAYZrRKD9A7iA6UFw=="], - - "@smithy/invalid-dependency": ["@smithy/invalid-dependency@4.2.12", "", { "dependencies": { "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-/4F1zb7Z8LOu1PalTdESFHR0RbPwHd3FcaG1sI3UEIriQTWakysgJr65lc1jj6QY5ye7aFsisajotH6UhWfm/g=="], - - "@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.2.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-n6rQ4N8Jj4YTQO3YFrlgZuwKodf4zUFs7EJIWH86pSCWBaAtAGBFfCM7Wx6D2bBJ2xqFNxGBSrUWswT3M0VJow=="], - - "@smithy/md5-js": ["@smithy/md5-js@4.2.12", "", { "dependencies": { "@smithy/types": "^4.13.1", "@smithy/util-utf8": "^4.2.2", "tslib": "^2.6.2" } }, "sha512-W/oIpHCpWU2+iAkfZYyGWE+qkpuf3vEXHLxQQDx9FPNZTTdnul0dZ2d/gUFrtQ5je1G2kp4cjG0/24YueG2LbQ=="], + "@smithy/core": ["@smithy/core@3.33.3", "", { "dependencies": { "@smithy/types": "^4.17.2", "tslib": "^2.6.2" } }, "sha512-CsOeKq/9kA3y6VJHt+/+VTCtBaxJ4OTFpgrjIUhPpDIKxBci1k2bJaQASF2h/ELWrulGp+t97DZ0mevfAD8idg=="], - "@smithy/middleware-content-length": ["@smithy/middleware-content-length@4.2.12", "", { "dependencies": { "@smithy/protocol-http": "^5.3.12", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-YE58Yz+cvFInWI/wOTrB+DbvUVz/pLn5mC5MvOV4fdRUc6qGwygyngcucRQjAhiCEbmfLOXX0gntSIcgMvAjmA=="], + "@smithy/credential-provider-imds": ["@smithy/credential-provider-imds@4.5.2", "", { "dependencies": { "@smithy/core": "^3.33.2", "@smithy/types": "^4.17.2", "tslib": "^2.6.2" } }, "sha512-A9uSdn72ozbRUSit0eib0TW7nXuNPlaeM0zcGkJ+nE6tFcSDbnmtwoxbTCFBukVQcszDAyvsd7+rTduPTXpygg=="], - "@smithy/middleware-endpoint": ["@smithy/middleware-endpoint@4.4.25", "", { "dependencies": { "@smithy/core": "^3.23.11", "@smithy/middleware-serde": "^4.2.14", "@smithy/node-config-provider": "^4.3.12", "@smithy/shared-ini-file-loader": "^4.4.7", "@smithy/types": "^4.13.1", "@smithy/url-parser": "^4.2.12", "@smithy/util-middleware": "^4.2.12", "tslib": "^2.6.2" } }, "sha512-dqjLwZs2eBxIUG6Qtw8/YZ4DvzHGIf0DA18wrgtfP6a50UIO7e2nY0FPdcbv5tVJKqWCCU5BmGMOUwT7Puan+A=="], + "@smithy/fetch-http-handler": ["@smithy/fetch-http-handler@5.8.0", "", { "dependencies": { "@smithy/core": "^3.33.3", "@smithy/types": "^4.18.0", "tslib": "^2.6.2" } }, "sha512-ycSJu3tFAQ4v04CBB0agqFMVsSQ1iG3yw+SpgxRqKfaURpQD4CZ8Wn0zPMmSnOuTpTh65Vz+EA0rMrw089wvkA=="], - "@smithy/middleware-retry": ["@smithy/middleware-retry@4.4.42", "", { "dependencies": { "@smithy/node-config-provider": "^4.3.12", "@smithy/protocol-http": "^5.3.12", "@smithy/service-error-classification": "^4.2.12", "@smithy/smithy-client": "^4.12.5", "@smithy/types": "^4.13.1", "@smithy/util-middleware": "^4.2.12", "@smithy/util-retry": "^4.2.12", "@smithy/uuid": "^1.1.2", "tslib": "^2.6.2" } }, "sha512-vbwyqHRIpIZutNXZpLAozakzamcINaRCpEy1MYmK6xBeW3xN+TyPRA123GjXnuxZIjc9848MRRCugVMTXxC4Eg=="], + "@smithy/node-http-handler": ["@smithy/node-http-handler@4.12.1", "", { "dependencies": { "@smithy/core": "^3.33.3", "@smithy/types": "^4.18.0", "tslib": "^2.6.2" } }, "sha512-ThMkboGeONWXAelq9FvGsuJC4rOi+qyC4/zhUF58xYpxUg5sQKx2VXZYJmtNjr4dSuBJ1HeJXETQILCz3wOHvw=="], - "@smithy/middleware-serde": ["@smithy/middleware-serde@4.2.14", "", { "dependencies": { "@smithy/core": "^3.23.11", "@smithy/protocol-http": "^5.3.12", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-+CcaLoLa5apzSRtloOyG7lQvkUw2ZDml3hRh4QiG9WyEPfW5Ke/3tPOPiPjUneuT59Tpn8+c3RVaUvvkkwqZwg=="], + "@smithy/signature-v4": ["@smithy/signature-v4@5.7.3", "", { "dependencies": { "@smithy/core": "^3.33.3", "@smithy/types": "^4.17.2", "tslib": "^2.6.2" } }, "sha512-7ImGm+FkHRLcBaRttIAMZ6bzJZWb2cJGoYjq46F2UjycujWzrL9GEN9h4w7eQyXJYnltrUhxbbieBAIRrdqpow=="], - "@smithy/middleware-stack": ["@smithy/middleware-stack@4.2.12", "", { "dependencies": { "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-kruC5gRHwsCOuyCd4ouQxYjgRAym2uDlCvQ5acuMtRrcdfg7mFBg6blaxcJ09STpt3ziEkis6bhg1uwrWU7txw=="], + "@smithy/types": ["@smithy/types@4.18.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-CgB6HHWer/vrKps24ulRIbpcpb7K4xAU7SkZ7YHzBPlwHsvsrCJFEXK421s+cJzX+ZrqtA/TuU5w1HzI7k9N8A=="], - "@smithy/node-config-provider": ["@smithy/node-config-provider@4.3.12", "", { "dependencies": { "@smithy/property-provider": "^4.2.12", "@smithy/shared-ini-file-loader": "^4.4.7", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-tr2oKX2xMcO+rBOjobSwVAkV05SIfUKz8iI53rzxEmgW3GOOPOv0UioSDk+J8OpRQnpnhsO3Af6IEBabQBVmiw=="], - - "@smithy/node-http-handler": ["@smithy/node-http-handler@4.4.16", "", { "dependencies": { "@smithy/abort-controller": "^4.2.12", "@smithy/protocol-http": "^5.3.12", "@smithy/querystring-builder": "^4.2.12", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-ULC8UCS/HivdCB3jhi+kLFYe4B5gxH2gi9vHBfEIiRrT2jfKiZNiETJSlzRtE6B26XbBHjPtc8iZKSNqMol9bw=="], - - "@smithy/property-provider": ["@smithy/property-provider@4.2.12", "", { "dependencies": { "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-jqve46eYU1v7pZ5BM+fmkbq3DerkSluPr5EhvOcHxygxzD05ByDRppRwRPPpFrsFo5yDtCYLKu+kreHKVrvc7A=="], - - "@smithy/protocol-http": ["@smithy/protocol-http@5.3.12", "", { "dependencies": { "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-fit0GZK9I1xoRlR4jXmbLhoN0OdEpa96ul8M65XdmXnxXkuMxM0Y8HDT0Fh0Xb4I85MBvBClOzgSrV1X2s1Hxw=="], - - "@smithy/querystring-builder": ["@smithy/querystring-builder@4.2.12", "", { "dependencies": { "@smithy/types": "^4.13.1", "@smithy/util-uri-escape": "^4.2.2", "tslib": "^2.6.2" } }, "sha512-6wTZjGABQufekycfDGMEB84BgtdOE/rCVTov+EDXQ8NHKTUNIp/j27IliwP7tjIU9LR+sSzyGBOXjeEtVgzCHg=="], - - "@smithy/querystring-parser": ["@smithy/querystring-parser@4.2.12", "", { "dependencies": { "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-P2OdvrgiAKpkPNKlKUtWbNZKB1XjPxM086NeVhK+W+wI46pIKdWBe5QyXvhUm3MEcyS/rkLvY8rZzyUdmyDZBw=="], - - "@smithy/service-error-classification": ["@smithy/service-error-classification@4.2.12", "", { "dependencies": { "@smithy/types": "^4.13.1" } }, "sha512-LlP29oSQN0Tw0b6D0Xo6BIikBswuIiGYbRACy5ujw/JgWSzTdYj46U83ssf6Ux0GyNJVivs2uReU8pt7Eu9okQ=="], - - "@smithy/shared-ini-file-loader": ["@smithy/shared-ini-file-loader@4.4.7", "", { "dependencies": { "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-HrOKWsUb+otTeo1HxVWeEb99t5ER1XrBi/xka2Wv6NVmTbuCUC1dvlrksdvxFtODLBjsC+PHK+fuy2x/7Ynyiw=="], - - "@smithy/signature-v4": ["@smithy/signature-v4@5.3.12", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.2", "@smithy/protocol-http": "^5.3.12", "@smithy/types": "^4.13.1", "@smithy/util-hex-encoding": "^4.2.2", "@smithy/util-middleware": "^4.2.12", "@smithy/util-uri-escape": "^4.2.2", "@smithy/util-utf8": "^4.2.2", "tslib": "^2.6.2" } }, "sha512-B/FBwO3MVOL00DaRSXfXfa/TRXRheagt/q5A2NM13u7q+sHS59EOVGQNfG7DkmVtdQm5m3vOosoKAXSqn/OEgw=="], - - "@smithy/smithy-client": ["@smithy/smithy-client@4.12.5", "", { "dependencies": { "@smithy/core": "^3.23.11", "@smithy/middleware-endpoint": "^4.4.25", "@smithy/middleware-stack": "^4.2.12", "@smithy/protocol-http": "^5.3.12", "@smithy/types": "^4.13.1", "@smithy/util-stream": "^4.5.19", "tslib": "^2.6.2" } }, "sha512-UqwYawyqSr/aog8mnLnfbPurS0gi4G7IYDcD28cUIBhsvWs1+rQcL2IwkUQ+QZ7dibaoRzhNF99fAQ9AUcO00w=="], - - "@smithy/types": ["@smithy/types@4.13.1", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-787F3yzE2UiJIQ+wYW1CVg2odHjmaWLGksnKQHUrK/lYZSEcy1msuLVvxaR/sI2/aDe9U+TBuLsXnr3vod1g0g=="], - - "@smithy/url-parser": ["@smithy/url-parser@4.2.12", "", { "dependencies": { "@smithy/querystring-parser": "^4.2.12", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-wOPKPEpso+doCZGIlr+e1lVI6+9VAKfL4kZWFgzVgGWY2hZxshNKod4l2LXS3PRC9otH/JRSjtEHqQ/7eLciRA=="], - - "@smithy/util-base64": ["@smithy/util-base64@4.3.2", "", { "dependencies": { "@smithy/util-buffer-from": "^4.2.2", "@smithy/util-utf8": "^4.2.2", "tslib": "^2.6.2" } }, "sha512-XRH6b0H/5A3SgblmMa5ErXQ2XKhfbQB+Fm/oyLZ2O2kCUrwgg55bU0RekmzAhuwOjA9qdN5VU2BprOvGGUkOOQ=="], - - "@smithy/util-body-length-browser": ["@smithy/util-body-length-browser@4.2.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-JKCrLNOup3OOgmzeaKQwi4ZCTWlYR5H4Gm1r2uTMVBXoemo1UEghk5vtMi1xSu2ymgKVGW631e2fp9/R610ZjQ=="], - - "@smithy/util-body-length-node": ["@smithy/util-body-length-node@4.2.3", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-ZkJGvqBzMHVHE7r/hcuCxlTY8pQr1kMtdsVPs7ex4mMU+EAbcXppfo5NmyxMYi2XU49eqaz56j2gsk4dHHPG/g=="], - - "@smithy/util-buffer-from": ["@smithy/util-buffer-from@4.2.2", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.2", "tslib": "^2.6.2" } }, "sha512-FDXD7cvUoFWwN6vtQfEta540Y/YBe5JneK3SoZg9bThSoOAC/eGeYEua6RkBgKjGa/sz6Y+DuBZj3+YEY21y4Q=="], - - "@smithy/util-config-provider": ["@smithy/util-config-provider@4.2.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-dWU03V3XUprJwaUIFVv4iOnS1FC9HnMHDfUrlNDSh4315v0cWyaIErP8KiqGVbf5z+JupoVpNM7ZB3jFiTejvQ=="], - - "@smithy/util-defaults-mode-browser": ["@smithy/util-defaults-mode-browser@4.3.41", "", { "dependencies": { "@smithy/property-provider": "^4.2.12", "@smithy/smithy-client": "^4.12.5", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-M1w1Ux0rSVvBOxIIiqbxvZvhnjQ+VUjJrugtORE90BbadSTH+jsQL279KRL3Hv0w69rE7EuYkV/4Lepz/NBW9g=="], - - "@smithy/util-defaults-mode-node": ["@smithy/util-defaults-mode-node@4.2.44", "", { "dependencies": { "@smithy/config-resolver": "^4.4.11", "@smithy/credential-provider-imds": "^4.2.12", "@smithy/node-config-provider": "^4.3.12", "@smithy/property-provider": "^4.2.12", "@smithy/smithy-client": "^4.12.5", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-YPze3/lD1KmWuZsl9JlfhcgGLX7AXhSoaCDtiPntUjNW5/YY0lOHjkcgxyE9x/h5vvS1fzDifMGjzqnNlNiqOQ=="], - - "@smithy/util-endpoints": ["@smithy/util-endpoints@3.3.3", "", { "dependencies": { "@smithy/node-config-provider": "^4.3.12", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-VACQVe50j0HZPjpwWcjyT51KUQ4AnsvEaQ2lKHOSL4mNLD0G9BjEniQ+yCt1qqfKfiAHRAts26ud7hBjamrwig=="], - - "@smithy/util-hex-encoding": ["@smithy/util-hex-encoding@4.2.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-Qcz3W5vuHK4sLQdyT93k/rfrUwdJ8/HZ+nMUOyGdpeGA1Wxt65zYwi3oEl9kOM+RswvYq90fzkNDahPS8K0OIg=="], - - "@smithy/util-middleware": ["@smithy/util-middleware@4.2.12", "", { "dependencies": { "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-Er805uFUOvgc0l8nv0e0su0VFISoxhJ/AwOn3gL2NWNY2LUEldP5WtVcRYSQBcjg0y9NfG8JYrCJaYDpupBHJQ=="], - - "@smithy/util-retry": ["@smithy/util-retry@4.2.12", "", { "dependencies": { "@smithy/service-error-classification": "^4.2.12", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-1zopLDUEOwumjcHdJ1mwBHddubYF8GMQvstVCLC54Y46rqoHwlIU+8ZzUeaBcD+WCJHyDGSeZ2ml9YSe9aqcoQ=="], - - "@smithy/util-stream": ["@smithy/util-stream@4.5.19", "", { "dependencies": { "@smithy/fetch-http-handler": "^5.3.15", "@smithy/node-http-handler": "^4.4.16", "@smithy/types": "^4.13.1", "@smithy/util-base64": "^4.3.2", "@smithy/util-buffer-from": "^4.2.2", "@smithy/util-hex-encoding": "^4.2.2", "@smithy/util-utf8": "^4.2.2", "tslib": "^2.6.2" } }, "sha512-v4sa+3xTweL1CLO2UP0p7tvIMH/Rq1X4KKOxd568mpe6LSLMQCnDHs4uv7m3ukpl3HvcN2JH6jiCS0SNRXKP/w=="], - - "@smithy/util-uri-escape": ["@smithy/util-uri-escape@4.2.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-2kAStBlvq+lTXHyAZYfJRb/DfS3rsinLiwb+69SstC9Vb0s9vNWkRwpnj918Pfi85mzi42sOqdV72OLxWAISnw=="], - - "@smithy/util-utf8": ["@smithy/util-utf8@4.2.2", "", { "dependencies": { "@smithy/util-buffer-from": "^4.2.2", "tslib": "^2.6.2" } }, "sha512-75MeYpjdWRe8M5E3AW0O4Cx3UadweS+cwdXjwYGBW5h/gxxnbeZ877sLPX/ZJA9GVTlL/qG0dXP29JWFCD1Ayw=="], - - "@smithy/util-waiter": ["@smithy/util-waiter@4.2.13", "", { "dependencies": { "@smithy/abort-controller": "^4.2.12", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-2zdZ9DTHngRtcYxJK1GUDxruNr53kv5W2Lupe0LMU+Imr6ohQg8M2T14MNkj1Y0wS3FFwpgpGQyvuaMF7CiTmQ=="], - - "@smithy/uuid": ["@smithy/uuid@1.1.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-O/IEdcCUKkubz60tFbGA7ceITTAJsty+lBjNoorP4Z6XRqaFb/OjQjZODophEcuq68nKm6/0r+6/lLQ+XVpk8g=="], - - "@types/bun": ["@types/bun@1.3.10", "", { "dependencies": { "bun-types": "1.3.10" } }, "sha512-0+rlrUrOrTSskibryHbvQkDOWRJwJZqZlxrUs1u4oOoTln8+WIXBPmAuCF35SWB2z4Zl3E84Nl/D0P7803nigQ=="], + "@types/bun": ["@types/bun@1.4.2", "", { "dependencies": { "bun-types": "1.4.2" } }, "sha512-GimotNn7+ZV0uVArItBbriZsR1oNf0+WTzPkdcFrzShI7k2norL0uzEaJT8T33dWr7O/c9ZDuAFQrctKCi72oQ=="], "@types/node": ["@types/node@25.4.0", "", { "dependencies": { "undici-types": "~7.18.0" } }, "sha512-9wLpoeWuBlcbBpOY3XmzSTG3oscB6xjBEEtn+pYXTfhyXhIxC5FsBer2KTopBlvKEiW9l13po9fq+SJY/5lkhw=="], @@ -236,11 +110,11 @@ "ajv-formats": ["ajv-formats@3.0.1", "", { "dependencies": { "ajv": "^8.0.0" } }, "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ=="], - "body-parser": ["body-parser@2.2.2", "", { "dependencies": { "bytes": "^3.1.2", "content-type": "^1.0.5", "debug": "^4.4.3", "http-errors": "^2.0.0", "iconv-lite": "^0.7.0", "on-finished": "^2.4.1", "qs": "^6.14.1", "raw-body": "^3.0.1", "type-is": "^2.0.1" } }, "sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA=="], + "body-parser": ["body-parser@2.3.0", "", { "dependencies": { "bytes": "^3.1.2", "content-type": "^2.0.0", "debug": "^4.4.3", "http-errors": "^2.0.1", "iconv-lite": "^0.7.2", "on-finished": "^2.4.1", "qs": "^6.15.2", "raw-body": "^3.0.2", "type-is": "^2.1.0" } }, "sha512-2cGmJupaNgg+QUwVLAucDuWuoMZ6EX9iHDRswZ5lsNYEmwPaRknMPCLZz07yTzVq/83p4o/wzbDZbBrTvGGTIw=="], "bowser": ["bowser@2.14.1", "", {}, "sha512-tzPjzCxygAKWFOJP011oxFHs57HzIhOEracIgAePE4pqB3LikALKnSzUyU4MGs9/iCEUuHlAJTjTc5M+u7YEGg=="], - "bun-types": ["bun-types@1.3.10", "", { "dependencies": { "@types/node": "*" } }, "sha512-tcpfCCl6XWo6nCVnpcVrxQ+9AYN1iqMIzgrSKYMB/fjLtV2eyAVEg7AxQJuCq/26R6HpKWykQXuSOq/21RYcbg=="], + "bun-types": ["bun-types@1.4.2", "", { "dependencies": { "@types/node": "*" } }, "sha512-bxV1FgK7yBIzjRe5zBozIM4Bem11ZJcCXSrjWRG3YWLt8yFDePu4cLjpebO8OvPeIE9trbyPF4fuj3Cia4Fj3w=="], "bytes": ["bytes@3.1.2", "", {}, "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg=="], @@ -290,11 +164,7 @@ "fast-deep-equal": ["fast-deep-equal@3.1.3", "", {}, "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="], - "fast-uri": ["fast-uri@3.1.0", "", {}, "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA=="], - - "fast-xml-builder": ["fast-xml-builder@1.1.3", "", { "dependencies": { "path-expression-matcher": "^1.1.3" } }, "sha512-1o60KoFw2+LWKQu3IdcfcFlGTW4dpqEWmjhYec6H82AYZU2TVBXep6tMl8Z1Y+wM+ZrzCwe3BZ9Vyd9N2rIvmg=="], - - "fast-xml-parser": ["fast-xml-parser@5.4.1", "", { "dependencies": { "fast-xml-builder": "^1.0.0", "strnum": "^2.1.2" }, "bin": { "fxparser": "src/cli/cli.js" } }, "sha512-BQ30U1mKkvXQXXkAGcuyUA/GA26oEB7NzOtsxCDtyu62sjGw5QraKFhx2Em3WQNjPw9PG6MQ9yuIIgkSDfGu5A=="], + "fast-uri": ["fast-uri@3.1.6", "", {}, "sha512-7Ical1vFEMr0onbVzEDIreM22I4khW+fzyQPwvAFWBp1iwdshSZRsL4jjRvPG9JP1uiqMHRto+YU6R2/CzDz5Q=="], "finalhandler": ["finalhandler@2.1.1", "", { "dependencies": { "debug": "^4.4.0", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "on-finished": "^2.4.1", "parseurl": "^1.3.3", "statuses": "^2.0.1" } }, "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA=="], @@ -314,7 +184,7 @@ "hasown": ["hasown@2.0.2", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ=="], - "hono": ["hono@4.12.7", "", {}, "sha512-jq9l1DM0zVIvsm3lv9Nw9nlJnMNPOcAtsbsgiUhWcFzPE99Gvo6yRTlszSLLYacMeQ6quHD6hMfId8crVHvexw=="], + "hono": ["hono@4.13.7", "", {}, "sha512-c8/gF9ac8Y78/agExVocyLevgR+JlpNB444Py0FSX8pJoPdYUfUzRcXtYEYGwt6l19qIlVZPN5Mfsw9jFShmQQ=="], "http-errors": ["http-errors@2.0.1", "", { "dependencies": { "depd": "~2.0.0", "inherits": "~2.0.4", "setprototypeof": "~1.2.0", "statuses": "~2.0.2", "toidentifier": "~1.0.1" } }, "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ=="], @@ -322,7 +192,7 @@ "inherits": ["inherits@2.0.4", "", {}, "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="], - "ip-address": ["ip-address@10.1.0", "", {}, "sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q=="], + "ip-address": ["ip-address@10.7.0", "", {}, "sha512-BGFsyJd5mpXp3rK6jIdADLNgpJUK1jnjzvYF8lK+VyDab9JAmqN0YOKDdP17HlgKb2+ehPgDc8EtnRLbGCAMhA=="], "ipaddr.js": ["ipaddr.js@1.9.1", "", {}, "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g=="], @@ -360,11 +230,9 @@ "parseurl": ["parseurl@1.3.3", "", {}, "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ=="], - "path-expression-matcher": ["path-expression-matcher@1.1.3", "", {}, "sha512-qdVgY8KXmVdJZRSS1JdEPOKPdTiEK/pi0RkcT2sw1RhXxohdujUlJFPuS1TSkevZ9vzd3ZlL7ULl1MHGTApKzQ=="], - "path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="], - "path-to-regexp": ["path-to-regexp@8.3.0", "", {}, "sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA=="], + "path-to-regexp": ["path-to-regexp@8.4.2", "", {}, "sha512-qRcuIdP69NPm4qbACK+aDogI5CBDMi1jKe0ry5rSQJz8JVLsC7jV8XpiJjGRLLol3N+R5ihGYcrPLTno6pAdBA=="], "pdf-parse": ["pdf-parse@2.4.5", "", { "dependencies": { "@napi-rs/canvas": "0.1.80", "pdfjs-dist": "5.4.296" }, "bin": { "pdf-parse": "bin/cli.mjs" } }, "sha512-mHU89HGh7v+4u2ubfnevJ03lmPgQ5WU4CxAVmTSh/sxVTEDYd1er/dKS/A6vg77NX47KTEoihq8jZBLr8Cxuwg=="], @@ -374,7 +242,7 @@ "proxy-addr": ["proxy-addr@2.0.7", "", { "dependencies": { "forwarded": "0.2.0", "ipaddr.js": "1.9.1" } }, "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg=="], - "qs": ["qs@6.15.0", "", { "dependencies": { "side-channel": "^1.1.0" } }, "sha512-mAZTtNCeetKMH+pSjrb76NAM8V9a05I9aBZOHztWy/UqcJdQYNsf59vrRKWnojAT9Y+GbIvoTBC++CPHqpDBhQ=="], + "qs": ["qs@6.16.0", "", { "dependencies": { "es-define-property": "^1.0.1", "side-channel": "^1.1.1" } }, "sha512-h6fhOIaRrID2CbEY2fqs+7t+UXZo+MLAnU5gRIq85uFtdiUPCdsApMlHhXogKVM4HM2DVbIjGNTTYH2OcmP1vA=="], "range-parser": ["range-parser@1.2.1", "", {}, "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg=="], @@ -396,9 +264,9 @@ "shebang-regex": ["shebang-regex@3.0.0", "", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="], - "side-channel": ["side-channel@1.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.3", "side-channel-list": "^1.0.0", "side-channel-map": "^1.0.1", "side-channel-weakmap": "^1.0.2" } }, "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw=="], + "side-channel": ["side-channel@1.1.1", "", { "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.4", "side-channel-list": "^1.0.1", "side-channel-map": "^1.0.1", "side-channel-weakmap": "^1.0.2" } }, "sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ=="], - "side-channel-list": ["side-channel-list@1.0.0", "", { "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.3" } }, "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA=="], + "side-channel-list": ["side-channel-list@1.0.1", "", { "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.4" } }, "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w=="], "side-channel-map": ["side-channel-map@1.0.1", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.5", "object-inspect": "^1.13.3" } }, "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA=="], @@ -418,8 +286,6 @@ "statuses": ["statuses@2.0.2", "", {}, "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw=="], - "strnum": ["strnum@2.2.0", "", {}, "sha512-Y7Bj8XyJxnPAORMZj/xltsfo55uOiyHcU2tnAVzHUnSJR/KsEX+9RoDeXEnsXtl/CX4fAcrt64gZ13aGaWPeBg=="], - "toidentifier": ["toidentifier@1.0.1", "", {}, "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA=="], "tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], @@ -436,26 +302,12 @@ "wrappy": ["wrappy@1.0.2", "", {}, "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="], - "zod": ["zod@4.3.6", "", {}, "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg=="], + "zod": ["zod@4.5.4", "", {}, "sha512-sC95tT5iHHH9gtpj6A81kh+NEaRAUFN+qlUPDUbRfOMvNf5QCBqsb3WgvnpVtK5Y+4UfA6KqufotuTvMGiTlsA=="], "zod-to-json-schema": ["zod-to-json-schema@3.25.1", "", { "peerDependencies": { "zod": "^3.25 || ^4" } }, "sha512-pM/SU9d3YAggzi6MtR4h7ruuQlqKtad8e9S0fmxcMi+ueAK5Korys/aWcV9LIIHTVbj01NdzxcnXSN+O74ZIVA=="], - "@aws-crypto/sha1-browser/@smithy/util-utf8": ["@smithy/util-utf8@2.3.0", "", { "dependencies": { "@smithy/util-buffer-from": "^2.2.0", "tslib": "^2.6.2" } }, "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A=="], - - "@aws-crypto/sha256-browser/@smithy/util-utf8": ["@smithy/util-utf8@2.3.0", "", { "dependencies": { "@smithy/util-buffer-from": "^2.2.0", "tslib": "^2.6.2" } }, "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A=="], - - "@aws-crypto/util/@smithy/util-utf8": ["@smithy/util-utf8@2.3.0", "", { "dependencies": { "@smithy/util-buffer-from": "^2.2.0", "tslib": "^2.6.2" } }, "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A=="], - - "@aws-crypto/sha1-browser/@smithy/util-utf8/@smithy/util-buffer-from": ["@smithy/util-buffer-from@2.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^2.2.0", "tslib": "^2.6.2" } }, "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA=="], - - "@aws-crypto/sha256-browser/@smithy/util-utf8/@smithy/util-buffer-from": ["@smithy/util-buffer-from@2.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^2.2.0", "tslib": "^2.6.2" } }, "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA=="], - - "@aws-crypto/util/@smithy/util-utf8/@smithy/util-buffer-from": ["@smithy/util-buffer-from@2.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^2.2.0", "tslib": "^2.6.2" } }, "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA=="], - - "@aws-crypto/sha1-browser/@smithy/util-utf8/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@2.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA=="], - - "@aws-crypto/sha256-browser/@smithy/util-utf8/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@2.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA=="], + "body-parser/content-type": ["content-type@2.1.0", "", {}, "sha512-mj7UPXE0jaqaOsukNZRUEfEi2AcL7C/vwmwcHV0O97eO1E1pxBZuyjlZrx5seTaNBg1U6+o35wpa35Qfcc+7ag=="], - "@aws-crypto/util/@smithy/util-utf8/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@2.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA=="], + "body-parser/type-is": ["type-is@2.1.0", "", { "dependencies": { "content-type": "^2.0.0", "media-typer": "^1.1.0", "mime-types": "^3.0.0" } }, "sha512-faYHw0anBbc/kWF3zFTEnxSFOAGUX9GFbOBthvDdLsIlEoWOFOtS0zgCiQYwIskL9iGXZL3kAXD8OoZ4GmMATA=="], } } diff --git a/daemon/install.sh b/daemon/install.sh index 50f003ae..7c21fe18 100755 --- a/daemon/install.sh +++ b/daemon/install.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -# install.sh — littleimpd installer +# install.sh — Grimoire installer # Usage: ./install.sh [--uninstall] [--upgrade] set -euo pipefail @@ -10,7 +10,8 @@ DATA_DIR="${HOME}/.local/share/littleimp" LOG_DIR="${DATA_DIR}/logs" FRONTEND_INSTALL_DIR="${DATA_DIR}/dist" CLI_BIN_DIR="${HOME}/.local/bin" -CLI_BIN="${CLI_BIN_DIR}/littleimp" +CLI_BIN="${CLI_BIN_DIR}/grimoire" +LEGACY_CLI_BIN="${CLI_BIN_DIR}/littleimp" HEALTH_URL="http://127.0.0.1:3210/health" HEALTH_TIMEOUT=15 BUN_MIN_MAJOR=1 @@ -95,7 +96,8 @@ install_daemon_files() { rm -f "${INSTALL_DIR}/.env" 2>/dev/null || true fi info "Installing dependencies…" - (cd "${INSTALL_DIR}" && bun install --production) + # Never execute dependency lifecycle scripts from an installed archive. + (cd "${INSTALL_DIR}" && bun install --production --frozen-lockfile --ignore-scripts) info "Daemon files installed" } @@ -137,20 +139,34 @@ install_frontend_files() { info "Frontend files installed" } -install_cli() { - info "Installing CLI command to ${CLI_BIN}…" - mkdir -p "${CLI_BIN_DIR}" +write_cli_wrapper() { + local cli_path="$1" { printf '#!/usr/bin/env bash\n' printf 'exec %q %q "$@"\n' "${BUN_PATH}" "${INSTALL_DIR}/src/cli.ts" - } > "${CLI_BIN}" - chmod +x "${CLI_BIN}" + } > "${cli_path}" + chmod +x "${cli_path}" +} + +is_installer_cli_wrapper() { + local cli_path="$1" + local escaped_cli_target + # write_cli_wrapper uses printf %q, so paths containing spaces are escaped. + escaped_cli_target="$(printf '%q' "${INSTALL_DIR}/src/cli.ts")" + grep -Fq -- "${escaped_cli_target}" "${cli_path}" 2>/dev/null +} + +install_cli() { + info "Installing CLI commands to ${CLI_BIN} and ${LEGACY_CLI_BIN}…" + mkdir -p "${CLI_BIN_DIR}" + write_cli_wrapper "${CLI_BIN}" + write_cli_wrapper "${LEGACY_CLI_BIN}" case ":${PATH}:" in *":${CLI_BIN_DIR}:"*) ;; - *) warn "${CLI_BIN_DIR} is not on PATH. Add it to your shell profile to run 'littleimp' directly." ;; + *) warn "${CLI_BIN_DIR} is not on PATH. Add it to your shell profile to run 'grimoire' directly." ;; esac - info "CLI installed: ${CLI_BIN}" + info "CLI installed: ${CLI_BIN} (legacy alias: ${LEGACY_CLI_BIN})" } create_config_dir() { @@ -253,7 +269,11 @@ wait_for_health() { print_success() { local mode="${1:-install}" - printf '\n\033[32m✓ littleimpd %sd and running!\033[0m\n' "${mode}" + local status_word="installed" + if [[ "${mode}" == "upgrade" ]]; then + status_word="upgraded" + fi + printf '\n\033[32m✓ Grimoire %s and running!\033[0m\n' "${status_word}" printf ' Health: %s\n' "${HEALTH_URL}" printf ' Data: %s\n' "${DATA_DIR}" printf ' Logs: %s\n' "${LOG_DIR}" @@ -267,7 +287,7 @@ print_success() { # ---------- uninstall ---------- uninstall() { local purge="${1:-}" - info "Uninstalling littleimpd…" + info "Uninstalling Grimoire (littleimpd)…" local os os="$(detect_os)" if [[ "${os}" == "macos" ]]; then @@ -287,10 +307,13 @@ uninstall() { rm -rf "${INSTALL_DIR}" info "Daemon files removed from ${INSTALL_DIR}" fi - if [[ -f "${CLI_BIN}" ]] && grep -Fq "${INSTALL_DIR}/src/cli.ts" "${CLI_BIN}" 2>/dev/null; then - rm -f "${CLI_BIN}" - info "CLI command removed: ${CLI_BIN}" - fi + local cli_path + for cli_path in "${CLI_BIN}" "${LEGACY_CLI_BIN}"; do + if [[ -f "${cli_path}" ]] && is_installer_cli_wrapper "${cli_path}"; then + rm -f "${cli_path}" + info "CLI command removed: ${cli_path}" + fi + done if [[ "${purge}" == "--purge" ]]; then if [[ -d "${DATA_DIR}" ]]; then rm -rf "${DATA_DIR}" @@ -298,7 +321,8 @@ uninstall() { fi else info "Data preserved at: ${DATA_DIR}" - info "To also remove data, run: $0 --uninstall --purge" + info "To also remove data later, rerun the original source or release installer with:" + info " ./install.sh --uninstall --purge" fi } @@ -316,11 +340,11 @@ main() { ;; --help|-h) printf 'Usage: %s [--upgrade] [--uninstall [--purge]]\n' "$(basename "$0")" - printf ' (no flags) Fresh install of littleimpd\n' + printf ' (no flags) Fresh install of Grimoire (daemon: littleimpd)\n' printf ' --upgrade Stop daemon, update files, restart\n' printf ' --uninstall Stop and remove daemon and files (data preserved)\n' printf ' --uninstall --purge Also delete all data at %s\n' "${DATA_DIR}" - printf '\nInstalls the CLI command at %s\n' "${CLI_BIN}" + printf '\nInstalls CLI commands at %s and %s\n' "${CLI_BIN}" "${LEGACY_CLI_BIN}" exit 0 ;; "") diff --git a/daemon/package.json b/daemon/package.json index 75382de5..ba477ee1 100644 --- a/daemon/package.json +++ b/daemon/package.json @@ -1,9 +1,10 @@ { "name": "littleimpd", - "version": "1.1.0", + "version": "1.2.0", "private": true, "type": "module", "bin": { + "grimoire": "./src/cli.ts", "littleimp": "./src/cli.ts" }, "scripts": { @@ -16,14 +17,22 @@ "test:performance": "bun run src/test/performance/large-library-performance.ts" }, "dependencies": { - "@aws-sdk/client-s3": "^3.1009.0", - "@modelcontextprotocol/sdk": "^1.27.1", - "hono": "^4.12.7", + "@aws-sdk/client-s3": "^3.1128.0", + "@modelcontextprotocol/sdk": "^1.30.0", + "hono": "^4.13.7", "pdf-parse": "^2.4.5", "sqlite-vec": "^0.1.7-alpha.2", "zod": "^4.3.6" }, "devDependencies": { "@types/bun": "^1.2" + }, + "overrides": { + "@hono/node-server": "1.19.15", + "body-parser": "2.3.0", + "fast-uri": "3.1.6", + "ip-address": "10.7.0", + "path-to-regexp": "8.4.2", + "qs": "6.16.0" } } diff --git a/daemon/src/api/contract.ts b/daemon/src/api/contract.ts index 7baad38b..050c98d2 100644 --- a/daemon/src/api/contract.ts +++ b/daemon/src/api/contract.ts @@ -385,6 +385,8 @@ const schemas = { category_id: nullable(stringSchema("Existing category ID to assign")), category: stringSchema("Root category name to resolve or create when category_id is omitted", { maxLength: 100 }), notes: nullable(stringSchema("Personal notes, or null to leave empty", { maxLength: 100000 })), + is_pinned: booleanSchema("Whether the new bookmark should be pinned"), + read_later: booleanSchema("Whether the new bookmark should be added to Read Later"), source: ref("CaptureSource"), }, ["url"], @@ -1456,6 +1458,67 @@ const schemas = { }, ["status", "version", "uptime", "queueSize"] ), + BrowserIntegrationCapabilities: objectSchema( + { + protocol: stringSchema("Stable browser integration protocol identifier", { + enum: ["grimoire-browser-capture"], + }), + protocol_version: integerSchema("Browser integration protocol version", { + minimum: 1, + maximum: 1, + }), + grimoire_version: stringSchema("Daemon package version"), + endpoints: objectSchema( + { + capture: stringSchema("Authenticated bookmark capture path"), + taxonomy: stringSchema("Authenticated category and tag discovery path"), + }, + ["capture", "taxonomy"] + ), + capture_fields: objectSchema( + { + is_pinned: booleanSchema("Capture accepts an initial pinned state"), + read_later: booleanSchema("Capture accepts an initial Read Later state"), + }, + ["is_pinned", "read_later"], + "Optional capture fields supported by this daemon" + ), + limits: objectSchema( + { + request_bytes: integerSchema("Maximum capture request bytes", { minimum: 1 }), + title_characters: integerSchema("Maximum title characters", { minimum: 1 }), + notes_characters: integerSchema("Maximum notes characters", { minimum: 1 }), + selected_text_characters: integerSchema("Maximum selected text characters", { minimum: 1 }), + tag_characters: integerSchema("Maximum characters per tag", { minimum: 1 }), + }, + [ + "request_bytes", + "title_characters", + "notes_characters", + "selected_text_characters", + "tag_characters", + ] + ), + }, + ["protocol", "protocol_version", "grimoire_version", "endpoints", "limits"], + "Authenticated capabilities advertised to packaged browser extensions" + ), + BrowserIntegrationCapabilitiesResponse: envelope( + ref("BrowserIntegrationCapabilities"), + "Browser integration capability response" + ), + BrowserIntegrationTaxonomy: objectSchema( + { + categories: arrayOf(ref("CategoryNode"), "Category tree"), + tags: arrayOf(ref("TagWithCount"), "Tags"), + }, + ["categories", "tags"], + "Authenticated taxonomy subset used by the browser extension" + ), + BrowserIntegrationTaxonomyResponse: envelope( + ref("BrowserIntegrationTaxonomy"), + "Browser integration taxonomy response" + ), Diagnostics: objectSchema( { generated_at: stringSchema("Diagnostics generation timestamp", { format: "date-time" }), @@ -3233,6 +3296,43 @@ export const apiContract = { }, ], }, + { + method: "GET", + path: "/integrations/browser/v1/capabilities", + tag: "Integrations", + summary: "Negotiate the packaged browser extension capture protocol.", + description: + "Requires a managed integration bearer token. Packaged Chrome and Firefox clients use this endpoint to distinguish current Grimoire from the legacy account API before sending capture data. The response describes stable paths and enforced field limits; clients must not infer compatibility from the daemon marketing version alone.", + responses: { + "200": jsonResponse( + "Supported browser integration capabilities", + ref("BrowserIntegrationCapabilitiesResponse") + ), + "401": problemResponse("Missing, invalid, rotated, or revoked integration token"), + }, + examples: [ + { + title: "Negotiate browser capture support", + request: + "curl http://127.0.0.1:3210/integrations/browser/v1/capabilities \\\n -H \"Authorization: Bearer limp_it_example\"", + }, + ], + }, + { + method: "GET", + path: "/integrations/browser/v1/taxonomy", + tag: "Integrations", + summary: "List the categories and tags available to the packaged browser extension.", + description: + "Requires a managed integration bearer token. This route keeps extension-origin reads inside the versioned browser integration surface instead of exposing unrelated local-library GET routes.", + responses: { + "200": jsonResponse( + "Browser capture categories and tags", + ref("BrowserIntegrationTaxonomyResponse") + ), + "401": problemResponse("Missing, invalid, rotated, or revoked integration token"), + }, + }, { method: "GET", path: "/capture/bookmarklet", diff --git a/daemon/src/cli.ts b/daemon/src/cli.ts index 71c9d74c..c736c43e 100755 --- a/daemon/src/cli.ts +++ b/daemon/src/cli.ts @@ -376,30 +376,30 @@ function formatRollbackGuidance(lines: string[]): string { function usage(): string { return [ - `littleimp ${APP_VERSION}`, + `grimoire ${APP_VERSION}`, "", "Usage:", - " littleimp update check [--channel stable|beta] [--source URL] [--json]", - " littleimp update install [--version VERSION] [--release-base-url URL] [--channel stable|beta] [--source URL] [--json] [--allow-unsigned]", - " littleimp update install --archive FILE --checksum FILE [--signature FILE] [--json] [--allow-unsigned]", - " littleimp backup create [--json] [--daemon-url URL]", - " littleimp backup create --encrypt --output FILE [--json] [--daemon-url URL] [--password-file FILE]", - " littleimp backup list [--include-remote] [--json] [--daemon-url URL]", - " littleimp backup restore --yes [--json] [--daemon-url URL]", - " littleimp backup restore --remote-key --yes [--json] [--daemon-url URL]", - " littleimp backup restore --encrypted-file FILE --yes [--json] [--daemon-url URL] [--password-file FILE]", - " littleimp backup verify --file [--json]", - " littleimp backup verify --encrypted --file FILE [--json] [--password-file FILE]", - " littleimp diagnostics [--json] [--daemon-url URL]", + " grimoire update check [--channel stable|beta] [--source URL] [--json]", + " grimoire update install [--version VERSION] [--release-base-url URL] [--channel stable|beta] [--source URL] [--json] [--allow-unsigned]", + " grimoire update install --archive FILE --checksum FILE [--signature FILE] [--json] [--allow-unsigned]", + " grimoire backup create [--json] [--daemon-url URL]", + " grimoire backup create --encrypt --output FILE [--json] [--daemon-url URL] [--password-file FILE]", + " grimoire backup list [--include-remote] [--json] [--daemon-url URL]", + " grimoire backup restore --yes [--json] [--daemon-url URL]", + " grimoire backup restore --remote-key --yes [--json] [--daemon-url URL]", + " grimoire backup restore --encrypted-file FILE --yes [--json] [--daemon-url URL] [--password-file FILE]", + " grimoire backup verify --file [--json]", + " grimoire backup verify --encrypted --file FILE [--json] [--password-file FILE]", + " grimoire diagnostics [--json] [--daemon-url URL]", "", "Migration commands (experimental; v0.5 SQLite only):", - " littleimp migrate inspect --data-dir [--json] [--daemon-url URL]", - " littleimp migrate inspect --db [--uploads-dir DIR] [--json] [--daemon-url URL]", - " littleimp migrate inspect --archive [--json] [--daemon-url URL]", - " littleimp migrate apply --data-dir --yes [--owner USER] [--password|--password-file FILE] [--merge] [--json] [--daemon-url URL]", - " littleimp migrate apply --db --yes [--uploads-dir DIR] [--owner USER] [--password|--password-file FILE] [--merge] [--json] [--daemon-url URL]", - " littleimp migrate apply --archive --yes [--owner USER] [--password|--password-file FILE] [--merge] [--json] [--daemon-url URL]", - " littleimp migrate apply --data-dir --dry-run [--owner USER] [--merge] [--json] [--daemon-url URL]", + " grimoire migrate inspect --data-dir [--json] [--daemon-url URL]", + " grimoire migrate inspect --db [--uploads-dir DIR] [--json] [--daemon-url URL]", + " grimoire migrate inspect --archive [--json] [--daemon-url URL]", + " grimoire migrate apply --data-dir --yes [--owner USER] [--password|--password-file FILE] [--merge] [--json] [--daemon-url URL]", + " grimoire migrate apply --db --yes [--uploads-dir DIR] [--owner USER] [--password|--password-file FILE] [--merge] [--json] [--daemon-url URL]", + " grimoire migrate apply --archive --yes [--owner USER] [--password|--password-file FILE] [--merge] [--json] [--daemon-url URL]", + " grimoire migrate apply --data-dir --dry-run [--owner USER] [--merge] [--json] [--daemon-url URL]", "", "Environment:", " LITTLEIMP_DAEMON_URL Defaults to http://127.0.0.1:3210", @@ -671,6 +671,13 @@ async function handleUpdateInstallCommand( }); assertNoPositionals(parsed, command, "update"); + if (io.env.LITTLEIMP_PACKAGE_MANAGER === "homebrew") { + throw new CliError( + "Homebrew-managed installations are updated with `brew upgrade grimoire`; do not use the CLI self-update command.", + 2 + ); + } + const archive = parsed.values.get("--archive"); const checksum = parsed.values.get("--checksum"); const signature = parsed.values.get("--signature"); diff --git a/daemon/src/routes/browser-integration.ts b/daemon/src/routes/browser-integration.ts new file mode 100644 index 00000000..a61b1204 --- /dev/null +++ b/daemon/src/routes/browser-integration.ts @@ -0,0 +1,54 @@ +import { Hono } from "hono"; +import type { Database } from "bun:sqlite"; +import { requireIntegrationToken } from "../lib/integration-auth.js"; +import { CategoryRepository } from "../db/category-repository.js"; +import { TagRepository } from "../db/tag-repository.js"; + +interface BrowserIntegrationDeps { + db: Database; + version: string; +} + +export function createBrowserIntegrationRoute(deps: BrowserIntegrationDeps): Hono { + const router = new Hono(); + const categoryRepo = new CategoryRepository(deps.db); + const tagRepo = new TagRepository(deps.db); + + router.use("/integrations/browser/v1/*", requireIntegrationToken(deps.db)); + + router.get("/integrations/browser/v1/capabilities", (c) => { + return c.json({ + data: { + protocol: "grimoire-browser-capture", + protocol_version: 1, + grimoire_version: deps.version, + endpoints: { + capture: "/capture", + taxonomy: "/integrations/browser/v1/taxonomy", + }, + capture_fields: { + is_pinned: true, + read_later: true, + }, + limits: { + request_bytes: 256 * 1024, + title_characters: 2_000, + notes_characters: 100_000, + selected_text_characters: 10_000, + tag_characters: 50, + }, + }, + }); + }); + + router.get("/integrations/browser/v1/taxonomy", (c) => { + return c.json({ + data: { + categories: categoryRepo.listTree(), + tags: tagRepo.list(), + }, + }); + }); + + return router; +} diff --git a/daemon/src/routes/capture.ts b/daemon/src/routes/capture.ts index b4b898af..3943a9f1 100644 --- a/daemon/src/routes/capture.ts +++ b/daemon/src/routes/capture.ts @@ -18,7 +18,17 @@ interface CaptureDeps { type ProblemStatus = 400 | 404 | 409 | 422 | 500; -const captureFields = new Set(["url", "title", "tags", "category_id", "category", "notes", "source"]); +const captureFields = new Set([ + "url", + "title", + "tags", + "category_id", + "category", + "notes", + "is_pinned", + "read_later", + "source", +]); const sourceFields = new Set(["client", "source_url", "referrer_url", "selected_text"]); const tagPattern = /^[a-z0-9]+(-[a-z0-9]+)*$/; const MAX_TITLE_LENGTH = 2000; @@ -126,6 +136,14 @@ function parseTags(body: Record): string[] | undefined { return tags; } +function parseOptionalBoolean(body: Record, field: string): boolean | undefined { + if (!(field in body)) return undefined; + if (typeof body[field] !== "boolean") { + throw new ValidationError(`\`${field}\` must be a boolean`); + } + return body[field]; +} + function parseSource(body: Record): CaptureMetadataInput { if (!("source" in body) || body.source === null || body.source === undefined) return {}; if (!isRecord(body.source)) { @@ -222,6 +240,8 @@ type DoCaptureInput = { resolveCategory?: () => string | null; tags?: string[]; notes?: string | null; + isPinned?: boolean; + readLater?: boolean; source?: CaptureMetadataInput; }; @@ -271,10 +291,17 @@ function doCapture( if (!fetched) throw new Error("Captured bookmark could not be fetched"); // Apply tags and notes after creation if provided - if (input.tags !== undefined || input.notes !== undefined) { + if ( + input.tags !== undefined || + input.notes !== undefined || + input.isPinned !== undefined || + input.readLater !== undefined + ) { const updated = bookmarkRepo.update(fetched.id, { ...(input.tags !== undefined ? { tags: input.tags } : {}), ...(input.notes !== undefined ? { notes: input.notes } : {}), + ...(input.isPinned !== undefined ? { is_pinned: input.isPinned ? 1 : 0 } : {}), + ...(input.readLater !== undefined ? { read_later: input.readLater ? 1 : 0 } : {}), }); if (!updated) throw new Error("Captured bookmark could not be updated"); fetched = updated; @@ -453,6 +480,8 @@ export function createCaptureRoute(deps: CaptureDeps): Hono { let tags: string[] | undefined; let categorySelection: CategorySelection; let notes: string | null | undefined; + let isPinned: boolean | undefined; + let readLater: boolean | undefined; let source: CaptureMetadataInput; try { url = parsePublicUrl(body.url, "url"); @@ -461,6 +490,8 @@ export function createCaptureRoute(deps: CaptureDeps): Hono { categorySelection = parseCategorySelection(body); validateCategorySelection(categorySelection, categoryRepo); notes = parseOptionalText(body, "notes", MAX_NOTES_LENGTH, { emptyAsNull: true, nullable: true }); + isPinned = parseOptionalBoolean(body, "is_pinned"); + readLater = parseOptionalBoolean(body, "read_later"); source = parseSource(body); } catch (err) { if (err instanceof ValidationError) { @@ -475,6 +506,8 @@ export function createCaptureRoute(deps: CaptureDeps): Hono { title, tags, notes, + isPinned, + readLater, resolveCategory: () => resolveCategoryId(categorySelection, categoryRepo), source, }, diff --git a/daemon/src/routes/settings.ts b/daemon/src/routes/settings.ts index 647c9ee8..514140c7 100644 --- a/daemon/src/routes/settings.ts +++ b/daemon/src/routes/settings.ts @@ -1,4 +1,4 @@ -import { Hono, Context } from "hono"; +import { Hono, Context, type Next } from "hono"; import { settingsManager, redactSettings, validateSettingsPatch } from "../settings.js"; import { log } from "../logger.js"; import { resolveRuntimeSettings } from "../runtime-settings.js"; @@ -41,7 +41,11 @@ export function createSettingsRoute(): Hono { * GET /settings * Returns current settings. API keys are redacted (shown as "***" if set). */ - app.get("/settings", (c) => { + app.get("/settings", async (c, next: Next) => { + if (c.req.header("Accept")?.includes("text/html")) { + await next(); + return; + } const settings = settingsManager.read(); return ok(c, { ...redactSettings(settings), diff --git a/daemon/src/server.ts b/daemon/src/server.ts index b29e0ec9..befa9ecc 100644 --- a/daemon/src/server.ts +++ b/daemon/src/server.ts @@ -28,6 +28,7 @@ import { createMediaRoute } from "./routes/media.js"; import { createIntegrationTokensRoute } from "./routes/integration-tokens.js"; import { createCaptureRoute } from "./routes/capture.js"; import { createDemoRoute } from "./routes/demo.js"; +import { createBrowserIntegrationRoute } from "./routes/browser-integration.js"; import { validatePresentedIntegrationToken } from "./lib/integration-auth.js"; import { join } from "path"; import { existsSync } from "fs"; @@ -108,6 +109,48 @@ function isAllowedLocalOrigin(origin: string | undefined): boolean { return !!normalized && allowedBrowserOrigins().has(normalized); } +const BROWSER_EXTENSION_PROTOCOLS = new Set([ + "chrome-extension:", + "moz-extension:", + "safari-web-extension:", +]); + +function isBrowserExtensionOrigin(origin: string | undefined): boolean { + if (!origin) return false; + try { + const parsed = new URL(origin); + if (!BROWSER_EXTENSION_PROTOCOLS.has(parsed.protocol)) return false; + if (!parsed.hostname || parsed.username || parsed.password || parsed.search || parsed.hash) return false; + return parsed.pathname === "" || parsed.pathname === "/"; + } catch { + return false; + } +} + +function hasBrowserExtensionScheme(origin: string | undefined): boolean { + if (!origin) return false; + try { + return BROWSER_EXTENSION_PROTOCOLS.has(new URL(origin).protocol); + } catch { + return false; + } +} + +const BROWSER_EXTENSION_INTEGRATION_PATHS = new Set([ + "/capture", + "/integrations/browser/v1/capabilities", + "/integrations/browser/v1/taxonomy", +]); + +function isBrowserExtensionIntegrationPath(path: string): boolean { + return BROWSER_EXTENSION_INTEGRATION_PATHS.has(path); +} + +function isAllowedRequestOrigin(origin: string | undefined, path: string): boolean { + return isAllowedLocalOrigin(origin) || + (isBrowserExtensionIntegrationPath(path) && isBrowserExtensionOrigin(origin)); +} + function isValidCspSourceOrigin(origin: string): boolean { try { const parsed = new URL(origin); @@ -175,17 +218,35 @@ async function enforceLocalOrigin(c: Context, next: Next): Promise { + const origin = c.req.header("origin"); + if (!isBrowserExtensionOrigin(origin) || !isBrowserExtensionIntegrationPath(c.req.path)) { + await next(); + return; + } + + await next(); + c.header("Access-Control-Allow-Origin", origin); + c.header("Access-Control-Allow-Methods", "GET, POST, OPTIONS"); + c.header("Access-Control-Allow-Headers", "Content-Type, Authorization"); + c.header("Vary", "Origin", { append: true }); +} + async function enforceRequestBodyLimits(c: Context, next: Next): Promise { const limit = bodyLimitFor(c.req.path, c.req.method); const contentLength = declaredContentLength(c); @@ -227,6 +288,7 @@ export function createApp(deps: AppDeps): Hono { // Middleware app.use("*", applySecurityHeaders); + app.use("*", applyBrowserExtensionCors); app.use("*", enforceLocalOrigin); app.use("*", enforceRequestBodyLimits); app.use( @@ -237,7 +299,18 @@ export function createApp(deps: AppDeps): Hono { allowHeaders: ["Content-Type", "Authorization", "X-LittleImp-Frontend"], }) ); - app.use("*", validatePresentedIntegrationToken(deps.db, new Set(["/mcp", "/capture"]))); + app.use( + "*", + validatePresentedIntegrationToken( + deps.db, + new Set([ + "/mcp", + "/capture", + "/integrations/browser/v1/capabilities", + "/integrations/browser/v1/taxonomy", + ]) + ) + ); // JSON error handler — never leak internal details in production app.onError((err, c) => { @@ -258,6 +331,7 @@ export function createApp(deps: AppDeps): Hono { app.route("/", createDiagnosticsRoute(deps)); app.route("/", createBookmarksRoute({ db: deps.db, queue: deps.queue, dataDir: deps.dataDir ?? Config.DATA_DIR })); app.route("/", createCaptureRoute({ db: deps.db, queue: deps.queue })); + app.route("/", createBrowserIntegrationRoute({ db: deps.db, version: deps.version })); app.route("/", createMediaRoute({ db: deps.db, dataDir: deps.dataDir ?? Config.DATA_DIR })); app.route("/", createSearchRoute({ db: deps.db })); app.route("/", createImportRoute({ db: deps.db, queue: deps.queue })); diff --git a/daemon/src/test/cli-update.test.ts b/daemon/src/test/cli-update.test.ts index 8312789c..2c561999 100644 --- a/daemon/src/test/cli-update.test.ts +++ b/daemon/src/test/cli-update.test.ts @@ -165,20 +165,20 @@ describe("littleimp update CLI", () => { it("reports the newest compatible release from the configured source", async () => { const harness = makeUpdateHarness([ { - tag_name: "v1.2.0-beta.1", - name: "Grimoire 1.2.0 beta 1", + tag_name: "v1.3.0-beta.1", + name: "Grimoire 1.3.0 beta 1", draft: false, prerelease: true, published_at: "2026-05-18T12:00:00Z", - html_url: "https://github.com/goniszewski/grimoire/releases/tag/v1.2.0-beta.1", + html_url: "https://github.com/goniszewski/grimoire/releases/tag/v1.3.0-beta.1", }, { - tag_name: "v1.2.0", - name: "Grimoire 1.2.0", + tag_name: "v1.3.0", + name: "Grimoire 1.3.0", draft: false, prerelease: false, published_at: "2026-05-17T12:00:00Z", - html_url: "https://github.com/goniszewski/grimoire/releases/tag/v1.2.0", + html_url: "https://github.com/goniszewski/grimoire/releases/tag/v1.3.0", }, ]); @@ -195,20 +195,20 @@ describe("littleimp update CLI", () => { expect(harness.calls[0].url).toBe("https://updates.example.test/releases"); expect(harness.calls[0].init?.headers).toEqual({ accept: "application/vnd.github+json", - "user-agent": "littleimp-update-check/1.1.0", + "user-agent": "littleimp-update-check/1.2.0", }); expect(JSON.parse(harness.stdout[0])).toEqual({ - current_version: "1.1.0", + current_version: "1.2.0", update_available: true, source: "https://updates.example.test/releases", channel: "stable", latest: { - version: "1.2.0", - tag: "v1.2.0", - name: "Grimoire 1.2.0", + version: "1.3.0", + tag: "v1.3.0", + name: "Grimoire 1.3.0", prerelease: false, published_at: "2026-05-17T12:00:00Z", - url: "https://github.com/goniszewski/grimoire/releases/tag/v1.2.0", + url: "https://github.com/goniszewski/grimoire/releases/tag/v1.3.0", }, }); }); @@ -295,7 +295,7 @@ describe("littleimp update CLI", () => { expect(code).toBe(0); expect(harness.stdout.join("\n")).toContain("Grimoire is up to date"); - expect(harness.stdout.join("\n")).toContain("1.1.0"); + expect(harness.stdout.join("\n")).toContain("1.2.0"); }); it("uses LITTLEIMP_UPDATE_SOURCE when no source flag is provided", async () => { @@ -313,6 +313,19 @@ describe("littleimp update CLI", () => { }); }); + it("directs Homebrew-managed installations to brew for upgrades", async () => { + const harness = makeUpgradeHarness("1.2.0", undefined, { + LITTLEIMP_PACKAGE_MANAGER: "homebrew", + }); + + const code = await harness.run(["update", "install"]); + + expect(code).toBe(2); + expect(harness.stderr.join("\n")).toContain("brew upgrade grimoire"); + expect(harness.spawnCalls).toHaveLength(0); + expect(harness.fetchCalls).toHaveLength(0); + }); + it("rejects unexpected positional arguments with an update-specific error", async () => { const harness = makeUpdateHarness([]); @@ -345,7 +358,7 @@ describe("littleimp update CLI", () => { expect(harness.spawnCalls[0].args).toContain("--upgrade"); expect(harness.fetchCalls[0].url).toBe("http://127.0.0.1:3210/health"); expect(JSON.parse(harness.stdout[0])).toMatchObject({ - current_version: "1.1.0", + current_version: "1.2.0", upgraded_version: fixture.version, archive: fixture.archivePath, checksum_verified: true, @@ -544,7 +557,7 @@ describe("littleimp update CLI", () => { }); it("checks the release source before downloading the latest compatible upgrade when no version is provided", async () => { - const fixture = createUpgradeArchiveFixture({ version: "1.2.0", signature: true }); + const fixture = createUpgradeArchiveFixture({ version: "1.3.0", signature: true }); const stdout: string[] = []; const stderr: string[] = []; const fetchCalls: FetchCall[] = []; diff --git a/daemon/src/test/integration/browser-integration.test.ts b/daemon/src/test/integration/browser-integration.test.ts new file mode 100644 index 00000000..eb0bdcc6 --- /dev/null +++ b/daemon/src/test/integration/browser-integration.test.ts @@ -0,0 +1,145 @@ +import { afterEach, beforeEach, describe, expect, it } from "bun:test"; +import type { Database } from "bun:sqlite"; +import { createApp } from "../../server.js"; +import { JobQueue } from "../../queue.js"; +import { makeTestDb } from "../helpers/db.js"; + +describe("browser extension integration", () => { + let db: Database; + let app: ReturnType; + + beforeEach(() => { + db = makeTestDb(); + app = createApp({ + db, + queue: new JobQueue(db), + startTime: new Date(), + version: "1.2.3-test", + staticDir: false, + }); + }); + + afterEach(() => db.close()); + + async function token(): Promise { + const response = await app.request("/integration-tokens", { + method: "POST", + headers: { "Content-Type": "application/json", Origin: "http://127.0.0.1:3210" }, + body: JSON.stringify({ name: "Grimoire Companion" }), + }); + const body = (await response.json()) as { data: { token: string } }; + return body.data.token; + } + + it("advertises the versioned capture contract only to an authenticated client", async () => { + const unauthorized = await app.request("/integrations/browser/v1/capabilities"); + expect(unauthorized.status).toBe(401); + + const response = await app.request("/integrations/browser/v1/capabilities", { + headers: { + Authorization: `Bearer ${await token()}`, + Origin: "chrome-extension://abcdefghijklmnopabcdefghijklmnop", + }, + }); + + expect(response.status).toBe(200); + expect(response.headers.get("access-control-allow-origin")).toBe( + "chrome-extension://abcdefghijklmnopabcdefghijklmnop" + ); + expect(await response.json()).toMatchObject({ + data: { + protocol: "grimoire-browser-capture", + protocol_version: 1, + grimoire_version: "1.2.3-test", + endpoints: { + capture: "/capture", + taxonomy: "/integrations/browser/v1/taxonomy", + }, + capture_fields: { + is_pinned: true, + read_later: true, + }, + }, + }); + }); + + it("accepts authenticated capture from Chrome, Firefox, and Safari extension origins", async () => { + const bearer = await token(); + for (const origin of [ + "chrome-extension://abcdefghijklmnopabcdefghijklmnop", + "moz-extension://8f2d3a0b-31dc-47f4-b783-36f8c9746ee8", + "safari-web-extension://com.goniszewski.Grimoire-Companion", + ]) { + const response = await app.request("/capture", { + method: "POST", + headers: { + Authorization: `Bearer ${bearer}`, + "Content-Type": "application/json", + Origin: origin, + }, + body: JSON.stringify({ url: `https://example.com/${encodeURIComponent(origin)}` }), + }); + expect(response.status).toBe(201); + expect(response.headers.get("access-control-allow-origin")).toBe(origin); + } + }); + + it("does not extend extension-origin access to other browser writes", async () => { + const bearer = await token(); + for (const origin of [ + "chrome-extension://abcdefghijklmnopabcdefghijklmnop", + "safari-web-extension://com.goniszewski.Grimoire-Companion", + ]) { + const response = await app.request("/tags", { + method: "POST", + headers: { + Authorization: `Bearer ${bearer}`, + "Content-Type": "application/json", + Origin: origin, + }, + body: JSON.stringify({ name: "blocked" }), + }); + expect(response.status).toBe(403); + } + }); + + it("keeps extension-origin taxonomy reads authenticated and blocks unrelated reads", async () => { + const origin = "chrome-extension://abcdefghijklmnopabcdefghijklmnop"; + const blocked = await app.request("/bookmarks", { headers: { Origin: origin } }); + expect(blocked.status).toBe(403); + const unknownIntegrationRead = await app.request("/integrations/browser/v1/future", { + headers: { Origin: origin }, + }); + expect(unknownIntegrationRead.status).toBe(403); + + const unauthorized = await app.request("/integrations/browser/v1/taxonomy", { + headers: { Origin: origin }, + }); + expect(unauthorized.status).toBe(401); + + const response = await app.request("/integrations/browser/v1/taxonomy", { + headers: { Origin: origin, Authorization: `Bearer ${await token()}` }, + }); + expect(response.status).toBe(200); + expect(response.headers.get("access-control-allow-origin")).toBe(origin); + expect(await response.json()).toEqual({ data: { categories: [], tags: [] } }); + }); + + it("allows capture preflight without treating extension origins as first-party", async () => { + for (const origin of [ + "chrome-extension://abcdefghijklmnopabcdefghijklmnop", + "safari-web-extension://com.goniszewski.Grimoire-Companion", + ]) { + const response = await app.request("/capture", { + method: "OPTIONS", + headers: { + Origin: origin, + "Access-Control-Request-Method": "POST", + "Access-Control-Request-Headers": "authorization,content-type", + }, + }); + expect(response.status).toBe(204); + expect(response.headers.get("access-control-allow-origin")).toBe(origin); + } + }); +}); diff --git a/daemon/src/test/integration/capture.test.ts b/daemon/src/test/integration/capture.test.ts index fe476cd1..b7366d00 100644 --- a/daemon/src/test/integration/capture.test.ts +++ b/daemon/src/test/integration/capture.test.ts @@ -18,6 +18,8 @@ type CaptureResponse = { title: string | null; category_id: string | null; notes: string | null; + is_pinned: 0 | 1; + read_later: 0 | 1; tags: string[]; }; capture: { @@ -96,6 +98,8 @@ describe("capture endpoint", () => { tags: ["TypeScript", "rag", "typescript"], category_id: category.id, notes: "Read after the SQLite notes.", + is_pinned: true, + read_later: true, source: { client: "bookmarklet", source_url: "https://example.com/article-list", @@ -114,6 +118,8 @@ describe("capture endpoint", () => { title: "Captured resource", category_id: category.id, notes: "Read after the SQLite notes.", + is_pinned: 1, + read_later: 1, tags: ["rag", "typescript"], }); expect(json.data.capture).toMatchObject({ @@ -134,6 +140,23 @@ describe("capture endpoint", () => { }); }); + it("rejects non-boolean capture state fields", async () => { + const res = await app.request("/capture", { + method: "POST", + headers: { + "Content-Type": "application/json", + ...bearer(await createToken()), + }, + body: JSON.stringify({ + url: "https://example.com/invalid-capture-state", + is_pinned: 1, + }), + }); + + expect(res.status).toBe(422); + expect(await res.json()).toMatchObject({ detail: "`is_pinned` must be a boolean" }); + }); + it("resolves a root category name when category_id is not supplied", async () => { const token = await createToken(); diff --git a/daemon/src/test/integration/installer-static-frontend.test.ts b/daemon/src/test/integration/installer-static-frontend.test.ts index 60d4461c..2deb6d1d 100644 --- a/daemon/src/test/integration/installer-static-frontend.test.ts +++ b/daemon/src/test/integration/installer-static-frontend.test.ts @@ -15,6 +15,7 @@ type FixtureRepoOptions = { frontend: "source" | "prebuilt" | "missing"; disableRsync?: boolean; os?: "linux" | "macos"; + homeName?: string; prepareHome?: (homeDir: string) => Promise; }; @@ -68,6 +69,10 @@ case "\${1:-}" in printf '1.2.3\\n' ;; install) + if [[ " $* " == *" --production "* && " $* " != *" --ignore-scripts "* ]]; then + printf 'production install must disable lifecycle scripts: %s\\n' "$*" >&2 + exit 2 + fi ;; run) if [[ "\${2:-}" == "build" ]]; then @@ -114,6 +119,7 @@ exit 0 } async function runInstallerFixture(options: FixtureRepoOptions): Promise<{ + fakePath: string; homeDir: string; launchctlLogPath: string; stdout: string; @@ -124,7 +130,7 @@ async function runInstallerFixture(options: FixtureRepoOptions): Promise<{ try { const fixtureRepo = await createFixtureRepo(tempRoot, options); - const homeDir = join(tempRoot, "home"); + const homeDir = join(tempRoot, options.homeName ?? "home"); await mkdir(homeDir, { recursive: true }); await options.prepareHome?.(homeDir); const fakePath = await createFakePath(tempRoot, options.os ?? "linux"); @@ -166,7 +172,7 @@ async function runInstallerFixture(options: FixtureRepoOptions): Promise<{ expect(exitCode, `${stdout}\n${stderr}`).toBe(0); - return { homeDir, launchctlLogPath, stdout, stderr, tempRoot }; + return { fakePath, homeDir, launchctlLogPath, stdout, stderr, tempRoot }; } catch (error) { await rm(tempRoot, { recursive: true, force: true }); throw error; @@ -189,6 +195,59 @@ describe("install.sh static frontend install", () => { } }); + it("installs the primary and legacy CLI wrappers", async () => { + const result = await runInstallerFixture({ frontend: "prebuilt" }); + + try { + const cliDir = join(result.homeDir, ".local", "bin"); + expect(result.stdout).toContain("Grimoire installed and running!"); + for (const command of ["grimoire", "littleimp"]) { + const cliPath = join(cliDir, command); + expect(existsSync(cliPath)).toBe(true); + await expect(readFile(cliPath, "utf8")).resolves.toContain("daemon/src/cli.ts"); + } + } finally { + await rm(result.tempRoot, { recursive: true, force: true }); + } + }); + + it("removes both CLI wrappers when the install path contains spaces", async () => { + const result = await runInstallerFixture({ + frontend: "prebuilt", + os: "macos", + homeName: "home with spaces", + }); + + try { + const fixtureRepo = join(result.tempRoot, "repo"); + const proc = Bun.spawn({ + cmd: ["bash", join(fixtureRepo, "daemon", "install.sh"), "--uninstall"], + cwd: join(fixtureRepo, "daemon"), + env: { + ...process.env, + HOME: result.homeDir, + PATH: `${result.fakePath}:${process.env.PATH ?? ""}`, + LITTLEIMP_TEST_LAUNCHCTL_LOG: result.launchctlLogPath, + }, + stdout: "pipe", + stderr: "pipe", + }); + + const [exitCode, stdout, stderr] = await Promise.all([ + proc.exited, + new Response(proc.stdout).text(), + new Response(proc.stderr).text(), + ]); + + expect(exitCode, `${stdout}\n${stderr}`).toBe(0); + const cliDir = join(result.homeDir, ".local", "bin"); + expect(existsSync(join(cliDir, "grimoire"))).toBe(false); + expect(existsSync(join(cliDir, "littleimp"))).toBe(false); + } finally { + await rm(result.tempRoot, { recursive: true, force: true }); + } + }); + it("installs a prebuilt frontend bundle without rebuilding it", async () => { const result = await runInstallerFixture({ frontend: "prebuilt" }); diff --git a/daemon/src/test/integration/static-frontend.test.ts b/daemon/src/test/integration/static-frontend.test.ts index 8e51396d..2e8383e7 100644 --- a/daemon/src/test/integration/static-frontend.test.ts +++ b/daemon/src/test/integration/static-frontend.test.ts @@ -63,4 +63,19 @@ describe("Static frontend serving", () => { expect(res.status).toBe(200); await expect(res.text()).resolves.toContain("Little Imp"); }); + + it("serves the frontend for a browser navigation to the settings API path", async () => { + const res = await app.request("/settings", { + headers: { Accept: "text/html" }, + }); + + expect(res.status).toBe(200); + expect(res.headers.get("content-type")).toContain("text/html"); + await expect(res.text()).resolves.toContain("Little Imp"); + + const apiRes = await app.request("/settings"); + expect(apiRes.status).toBe(200); + expect(apiRes.headers.get("content-type")).toContain("application/json"); + expect((await apiRes.json())?.data).toBeDefined(); + }); }); diff --git a/daemon/src/test/integration/updates.test.ts b/daemon/src/test/integration/updates.test.ts index 80911281..52870ff2 100644 --- a/daemon/src/test/integration/updates.test.ts +++ b/daemon/src/test/integration/updates.test.ts @@ -40,20 +40,20 @@ describe("Updates API", () => { calls.push({ url: String(url), init }); return releasesResponse([ { - tag_name: "v1.2.0-beta.1", - name: "Grimoire 1.2.0 beta 1", + tag_name: "v1.3.0-beta.1", + name: "Grimoire 1.3.0 beta 1", draft: false, prerelease: true, published_at: "2026-05-19T10:00:00Z", - html_url: "https://github.com/goniszewski/grimoire/releases/tag/v1.2.0-beta.1", + html_url: "https://github.com/goniszewski/grimoire/releases/tag/v1.3.0-beta.1", }, { - tag_name: "v1.2.0", - name: "Grimoire 1.2.0", + tag_name: "v1.3.0", + name: "Grimoire 1.3.0", draft: false, prerelease: false, published_at: "2026-05-18T10:00:00Z", - html_url: "https://github.com/goniszewski/grimoire/releases/tag/v1.2.0", + html_url: "https://github.com/goniszewski/grimoire/releases/tag/v1.3.0", }, ]); }) as typeof fetch; @@ -65,7 +65,7 @@ describe("Updates API", () => { expect(calls[0].url).toBe(DEFAULT_UPDATE_SOURCE); expect(calls[0].init?.headers).toEqual({ accept: "application/vnd.github+json", - "user-agent": "littleimp-update-check/1.1.0", + "user-agent": "littleimp-update-check/1.2.0", }); const json = await res.json() as { data: { @@ -84,17 +84,17 @@ describe("Updates API", () => { }; }; expect(json.data).toEqual({ - current_version: "1.1.0", + current_version: "1.2.0", update_available: true, source: DEFAULT_UPDATE_SOURCE, channel: "stable", latest: { - version: "1.2.0", - tag: "v1.2.0", - name: "Grimoire 1.2.0", + version: "1.3.0", + tag: "v1.3.0", + name: "Grimoire 1.3.0", prerelease: false, published_at: "2026-05-18T10:00:00Z", - url: "https://github.com/goniszewski/grimoire/releases/tag/v1.2.0", + url: "https://github.com/goniszewski/grimoire/releases/tag/v1.3.0", }, }); }); diff --git a/docs/03-using-grimoire.md b/docs/03-using-grimoire.md index 647b7d65..01d04e41 100644 --- a/docs/03-using-grimoire.md +++ b/docs/03-using-grimoire.md @@ -43,7 +43,8 @@ Related bookmarks use embeddings when available. ## Capture from the browser -There is no Chrome/Firefox store extension in 1.x yet. Use the built-in bookmarklet: +The rewritten Chrome/Firefox extension is still being prepared for publication. +Until it is available in the existing store listings, use the built-in bookmarklet: 1. Open **Settings → Browser Integration** 2. Create an integration token (the full secret is shown once — copy it if you need it elsewhere) @@ -65,16 +66,28 @@ You can also import a Netscape/HTML bookmark export from your browser, and expor ## Backups -From Settings or the `littleimp` CLI you can create, list, verify, and restore local snapshots. Encrypted packages and optional scheduled/S3 targets are available in Settings when configured. Restores verify checksums and create a rollback copy first. +From Settings or the `grimoire` CLI you can create, list, verify, and restore local snapshots. The legacy `littleimp` command remains available as a compatibility alias. Encrypted packages and optional scheduled/S3 targets are available in Settings when configured. Restores verify checksums and create a rollback copy first. + +The already-published v1.1.0 archive predates the CLI rename and uses +`littleimp` when invoked directly; current source installs and future +repackaged releases use `grimoire`. ## Local integrations - **REST** — see [API.md](../API.md); health at `GET /health` - **MCP** — Streamable HTTP at `http://127.0.0.1:3210/mcp` with an integration bearer token - **Capture API** — token-protected `POST /capture` for same-machine clients +- **Browser Companion protocol** — authenticated capability negotiation at + `GET /integrations/browser/v1/capabilities`, with authenticated taxonomy at + `GET /integrations/browser/v1/taxonomy`, before extension capture ## Privacy defaults Data stays under your local data directory (Docker volume, or `~/.local/share/littleimp/` for native installs). The daemon binds to loopback by default. External AI is opt-in; without it, nothing leaves your machine for enrichment or embeddings. More answers: [FAQ](./faq.md). + +Search results default to **Relevance**. You can choose a different sort while +searching; clearing the query restores your saved library sort. AI and mixed +search rank indexed bookmarks by similarity, so their result count can include +the full indexed library even when keyword search would return no matches. diff --git a/docs/04-development.md b/docs/04-development.md index 09d652d9..9b1ebc96 100644 --- a/docs/04-development.md +++ b/docs/04-development.md @@ -50,6 +50,70 @@ npm run build npm run check ``` +Homebrew-specific validation requires Homebrew and performs a disposable +install through a local tap: + +```sh +npm run test:homebrew +``` + +The public tap uses this same repository: + +```sh +brew trust --formula goniszewski/grimoire/grimoire +brew tap goniszewski/grimoire https://github.com/goniszewski/grimoire.git +``` + +`npm run test:homebrew:published` checks that public path, including an online +formula audit. Run on a disposable host with no existing Grimoire Homebrew +installation or data. The smoke verifies CLI aliases, exact service version, +reinstall preservation of the database/configuration, restart, and explicit +uninstall preservation. It removes test-created data afterward. + +The script isolates runtime settings and Homebrew trust using a temporary +configuration directory. `HOMEBREW_TEST_PORT=13210` selects another loopback +port using [Homebrew service environment overrides](https://docs.brew.sh/Manpage#services-subcommand), so an existing native +daemon can keep running. This requires a Homebrew version supporting service +environment files; the default port remains 3210. + +For pre-publication testing, download and verify the signed release archives +first, then provide their directory in local mode. Homebrew verifies the +formula SHA-256 against the supplied archive; this does not prove public +asset availability: + +```sh +HOMEBREW_TEST_ARCHIVE_DIR=/absolute/path/to/verified-archives \ +HOMEBREW_TEST_EXPECTED_VERSION=1.2.0 \ +HOMEBREW_TEST_PORT=13210 npm run test:homebrew +``` + +For an actual upgrade, also set +`HOMEBREW_TEST_UPGRADE_FROM_FORMULA=/absolute/path/to/v1.1.0/grimoire.rb`. +The script installs that older formula, starts and stops the service, then +copies the working-tree formula into the test tap and runs `brew upgrade`. +A saved bookmark and edited `.env` are checked after restart and uninstall. +It requires the version to change and verifies preservation and service health +at the requested target version. Supply both archives when using local cache +mode. Without the older formula, the test exercises a same-version reinstall. + +For the v1.2 release: + +1. Keep `Formula/grimoire.rb` and the tracked `Formula/release.json` aligned + with the final signed artifacts. The prepared pins identify the verified + v1.2.0 candidate; if any archive changes, repeat verification and update + both files. Formula version checks are independent of the source branch's + package version and ignored local build output. +2. Publish the reviewed formula to the public repository's default branch + and publish the matching release assets. No separate tap repository is + needed. These are release actions, separate from local preparation. +3. Run `HOMEBREW_TEST_EXPECTED_VERSION=1.2.0 npm run test:homebrew:published` + on clean macOS and Linux Homebrew hosts. Record the public URL/download, + service health, and data-preservation results before declaring support. + +Local mode copies the current working-tree formula into its disposable tap, +so uncommitted formula edits are included. Archive URLs determine the release +installed; it does not build the current source checkout. + If tooling is missing in a constrained environment: ```sh diff --git a/docs/05-install-without-docker.md b/docs/05-install-without-docker.md index 28dd4ee5..c577f292 100644 --- a/docs/05-install-without-docker.md +++ b/docs/05-install-without-docker.md @@ -8,7 +8,11 @@ Native install on macOS or Linux. There is no native Windows installer; use Dock - A git clone of this repository (or an unpacked release that includes the frontend build sources) - macOS or Linux -The installer installs production dependencies, builds the frontend when possible, and registers a user service. You do not need to run `npm install` first. +The installer installs production dependencies, builds the frontend when possible, and registers a user service. It installs the `grimoire` CLI under `~/.local/bin` and keeps `littleimp` as a compatibility alias. You do not need to run `npm install` first. + +The published v1.1.0 archive predates this CLI rename and exposes `littleimp` +when used directly. The `grimoire` name is available from the current source +installer and future repackaged releases. ## Install @@ -59,4 +63,14 @@ cd daemon For day-to-day coding, prefer [Development](./04-development.md) (`npm run daemon:dev` + `npm run dev`) instead of reinstalling. -Homebrew formula files may exist in the repo, but live Homebrew install is not a supported user path until it is validated against published release assets. +The published v1.2.0 Homebrew path is validated on macOS, including service startup, reinstall, v1.1 to v1.2 upgrade, and data preservation after uninstall. Linux Homebrew remains unverified; use the native Linux installer above. The explicit repository URL uses Grimoire itself as the tap; no separate tap repository is required. Trust only this formula before tapping: + +```sh +brew trust --formula goniszewski/grimoire/grimoire +brew tap goniszewski/grimoire https://github.com/goniszewski/grimoire.git +brew install grimoire +brew services start grimoire +``` + +Use `brew upgrade grimoire` for Homebrew upgrades. Homebrew stores its data +under `$(brew --prefix)/var/little-imp`, separately from native-install data. diff --git a/docs/api-contract.json b/docs/api-contract.json index 632dbd18..52cd6e5d 100644 --- a/docs/api-contract.json +++ b/docs/api-contract.json @@ -947,6 +947,14 @@ "maxLength": 100000, "nullable": true }, + "is_pinned": { + "type": "boolean", + "description": "Whether the new bookmark should be pinned" + }, + "read_later": { + "type": "boolean", + "description": "Whether the new bookmark should be added to Read Later" + }, "source": { "ref": "CaptureSource" } @@ -4911,6 +4919,163 @@ ], "additionalProperties": false }, + "BrowserIntegrationCapabilities": { + "type": "object", + "properties": { + "protocol": { + "type": "string", + "description": "Stable browser integration protocol identifier", + "enum": [ + "grimoire-browser-capture" + ] + }, + "protocol_version": { + "type": "integer", + "description": "Browser integration protocol version", + "minimum": 1, + "maximum": 1 + }, + "grimoire_version": { + "type": "string", + "description": "Daemon package version" + }, + "endpoints": { + "type": "object", + "properties": { + "capture": { + "type": "string", + "description": "Authenticated bookmark capture path" + }, + "taxonomy": { + "type": "string", + "description": "Authenticated category and tag discovery path" + } + }, + "required": [ + "capture", + "taxonomy" + ], + "additionalProperties": false + }, + "capture_fields": { + "type": "object", + "properties": { + "is_pinned": { + "type": "boolean", + "description": "Capture accepts an initial pinned state" + }, + "read_later": { + "type": "boolean", + "description": "Capture accepts an initial Read Later state" + } + }, + "required": [ + "is_pinned", + "read_later" + ], + "additionalProperties": false, + "description": "Optional capture fields supported by this daemon" + }, + "limits": { + "type": "object", + "properties": { + "request_bytes": { + "type": "integer", + "description": "Maximum capture request bytes", + "minimum": 1 + }, + "title_characters": { + "type": "integer", + "description": "Maximum title characters", + "minimum": 1 + }, + "notes_characters": { + "type": "integer", + "description": "Maximum notes characters", + "minimum": 1 + }, + "selected_text_characters": { + "type": "integer", + "description": "Maximum selected text characters", + "minimum": 1 + }, + "tag_characters": { + "type": "integer", + "description": "Maximum characters per tag", + "minimum": 1 + } + }, + "required": [ + "request_bytes", + "title_characters", + "notes_characters", + "selected_text_characters", + "tag_characters" + ], + "additionalProperties": false + } + }, + "required": [ + "protocol", + "protocol_version", + "grimoire_version", + "endpoints", + "limits" + ], + "additionalProperties": false, + "description": "Authenticated capabilities advertised to packaged browser extensions" + }, + "BrowserIntegrationCapabilitiesResponse": { + "type": "object", + "properties": { + "data": { + "ref": "BrowserIntegrationCapabilities" + } + }, + "required": [ + "data" + ], + "additionalProperties": false, + "description": "Browser integration capability response" + }, + "BrowserIntegrationTaxonomy": { + "type": "object", + "properties": { + "categories": { + "type": "array", + "description": "Category tree", + "items": { + "ref": "CategoryNode" + } + }, + "tags": { + "type": "array", + "description": "Tags", + "items": { + "ref": "TagWithCount" + } + } + }, + "required": [ + "categories", + "tags" + ], + "additionalProperties": false, + "description": "Authenticated taxonomy subset used by the browser extension" + }, + "BrowserIntegrationTaxonomyResponse": { + "type": "object", + "properties": { + "data": { + "ref": "BrowserIntegrationTaxonomy" + } + }, + "required": [ + "data" + ], + "additionalProperties": false, + "description": "Browser integration taxonomy response" + }, "Diagnostics": { "type": "object", "properties": { @@ -9254,6 +9419,58 @@ } ] }, + { + "method": "GET", + "path": "/integrations/browser/v1/capabilities", + "tag": "Integrations", + "summary": "Negotiate the packaged browser extension capture protocol.", + "description": "Requires a managed integration bearer token. Packaged Chrome and Firefox clients use this endpoint to distinguish current Grimoire from the legacy account API before sending capture data. The response describes stable paths and enforced field limits; clients must not infer compatibility from the daemon marketing version alone.", + "responses": { + "200": { + "description": "Supported browser integration capabilities", + "contentType": "application/json", + "schema": { + "ref": "BrowserIntegrationCapabilitiesResponse" + } + }, + "401": { + "description": "Missing, invalid, rotated, or revoked integration token", + "contentType": "application/problem+json", + "schema": { + "ref": "ProblemDetails" + } + } + }, + "examples": [ + { + "title": "Negotiate browser capture support", + "request": "curl http://127.0.0.1:3210/integrations/browser/v1/capabilities \\\n -H \"Authorization: Bearer limp_it_example\"" + } + ] + }, + { + "method": "GET", + "path": "/integrations/browser/v1/taxonomy", + "tag": "Integrations", + "summary": "List the categories and tags available to the packaged browser extension.", + "description": "Requires a managed integration bearer token. This route keeps extension-origin reads inside the versioned browser integration surface instead of exposing unrelated local-library GET routes.", + "responses": { + "200": { + "description": "Browser capture categories and tags", + "contentType": "application/json", + "schema": { + "ref": "BrowserIntegrationTaxonomyResponse" + } + }, + "401": { + "description": "Missing, invalid, rotated, or revoked integration token", + "contentType": "application/problem+json", + "schema": { + "ref": "ProblemDetails" + } + } + } + }, { "method": "GET", "path": "/capture/bookmarklet", diff --git a/docs/diagnostics.md b/docs/diagnostics.md index 9afb717f..e22c61e1 100644 --- a/docs/diagnostics.md +++ b/docs/diagnostics.md @@ -3,6 +3,9 @@ Grimoire diagnostics are generated locally and are only shared when the user chooses to copy, export, or send them. They are not telemetry. +Current source installs and future repackaged releases use the `grimoire` CLI; +the already-published v1.1.0 archive uses `littleimp` when invoked directly. + ## Generate diagnostics From Settings: @@ -13,9 +16,9 @@ From Settings: From the packaged CLI: ```sh -littleimp diagnostics -littleimp diagnostics --json -littleimp diagnostics --daemon-url http://127.0.0.1:3210 --json +grimoire diagnostics +grimoire diagnostics --json +grimoire diagnostics --daemon-url http://127.0.0.1:3210 --json ``` From the daemon API: diff --git a/docs/faq.md b/docs/faq.md index 6012f508..52fa1d63 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -42,11 +42,11 @@ Migration is **non-destructive** for your v0.5 data and **additive** for Grimoir Run the [migration guide](./migration.md) for the complete workflow: ```sh -littleimp migrate inspect --data-dir /path/to/grimoire/data -littleimp migrate apply --data-dir /path/to/grimoire/data --owner YOUR_USERNAME --dry-run -littleimp migrate apply --data-dir /path/to/grimoire/data --owner YOUR_USERNAME --yes +grimoire migrate inspect --data-dir /path/to/grimoire/data +grimoire migrate apply --data-dir /path/to/grimoire/data --owner YOUR_USERNAME --dry-run +grimoire migrate apply --data-dir /path/to/grimoire/data --owner YOUR_USERNAME --yes # Or pack the data folder first: -littleimp migrate apply --archive /path/to/grimoire-data.tar.gz --owner YOUR_USERNAME --yes +grimoire migrate apply --archive /path/to/grimoire-data.tar.gz --owner YOUR_USERNAME --yes ``` @@ -60,8 +60,11 @@ era backups are not supported. See the [migration API reference](https://github. ## How do I upgrade? For source or unpacked release installs, run `daemon/install.sh --upgrade`. -Packaged installs can use `littleimp update install` once a reachable release -source is configured. Settings can also check for update availability. +Native packaged installs can use `grimoire update install` once a reachable +release source is configured; `littleimp` remains a compatibility alias. +Homebrew installations should use `brew upgrade grimoire`. Settings can also +check for update availability. The already-published v1.1.0 archive predates +the rename and uses `littleimp` as its direct archive command. ## Does it work on Windows? diff --git a/docs/migration.md b/docs/migration.md index 42ff61bb..99f561c7 100644 --- a/docs/migration.md +++ b/docs/migration.md @@ -5,7 +5,12 @@ owner's library from the legacy Grimoire v0.5 SQLite application into a local Grimoire 1.x library. > This guide describes the v0.5 migrator in the 1.1.0 release line. Older -> development builds may not include the `littleimp migrate` command. +> development builds may not include the `grimoire migrate` command. + +The already-published v1.1.0 archive predates the CLI rename and exposes the +same migrator as `littleimp migrate`. Current source installs and future +repackaged releases use `grimoire migrate`; `littleimp` remains a compatibility +alias. The migrator accepts a v0.5 SQLite data directory, a `db.sqlite` file, or an archive containing that data. It does not support PocketBase-era backups from @@ -24,7 +29,7 @@ the older 0.3.x line. curl http://127.0.0.1:3210/health ``` -4. Run the `littleimp` CLI from the same Grimoire installation as the daemon. +4. Run the `grimoire` CLI from the same Grimoire installation as the daemon. If you are working from a source checkout rather than a packaged install, run the CLI with `bun run cli --` from the `daemon/` directory. @@ -47,17 +52,17 @@ raw PocketBase backup, or an unrelated SQLite database is rejected. Start by listing the source owners and record counts: ```sh -littleimp migrate inspect --data-dir /path/to/grimoire/data +grimoire migrate inspect --data-dir /path/to/grimoire/data ``` For a database file or archive: ```sh -littleimp migrate inspect \ +grimoire migrate inspect \ --db /path/to/grimoire/data/db.sqlite \ --uploads-dir /path/to/grimoire/data/user-uploads -littleimp migrate inspect --archive /path/to/grimoire-data.tar.gz +grimoire migrate inspect --archive /path/to/grimoire-data.tar.gz ``` Inspect is read-only. It reports the number of users, bookmarks, categories, @@ -72,7 +77,7 @@ or numeric v0.5 user ID. Only the selected owner's library is imported. Use the same source and owner for a no-write preview: ```sh -littleimp migrate apply \ +grimoire migrate apply \ --data-dir /path/to/grimoire/data \ --owner YOUR_USERNAME \ --dry-run @@ -86,7 +91,7 @@ the planned counts and warnings before applying. It does not require `--yes`. When the preview is correct, apply it explicitly: ```sh -littleimp migrate apply \ +grimoire migrate apply \ --data-dir /path/to/grimoire/data \ --owner YOUR_USERNAME \ --yes @@ -96,7 +101,7 @@ littleimp migrate apply \ archive form is equivalent: ```sh -littleimp migrate apply \ +grimoire migrate apply \ --archive /path/to/grimoire-data.tar.gz \ --owner YOUR_USERNAME \ --yes @@ -116,7 +121,7 @@ Prefer a password file or environment variable over putting a password in shell history: ```sh -littleimp migrate apply \ +grimoire migrate apply \ --data-dir /path/to/grimoire/data \ --owner YOUR_USERNAME \ --password-file /path/to/v05-password.txt \ @@ -125,7 +130,7 @@ littleimp migrate apply \ ```sh LITTLEIMP_MIGRATE_PASSWORD='your-v05-password' \ - littleimp migrate apply \ + grimoire migrate apply \ --data-dir /path/to/grimoire/data \ --owner YOUR_USERNAME \ --yes diff --git a/docs/openapi.json b/docs/openapi.json index 711a9dc8..dab967c6 100644 --- a/docs/openapi.json +++ b/docs/openapi.json @@ -3882,6 +3882,72 @@ "x-grimoire-source-method": "POST" } }, + "/integrations/browser/v1/capabilities": { + "get": { + "tags": [ + "Integrations" + ], + "summary": "Negotiate the packaged browser extension capture protocol.", + "description": "Requires a managed integration bearer token. Packaged Chrome and Firefox clients use this endpoint to distinguish current Grimoire from the legacy account API before sending capture data. The response describes stable paths and enforced field limits; clients must not infer compatibility from the daemon marketing version alone.", + "operationId": "getIntegrationsBrowserV1Capabilities", + "responses": { + "200": { + "description": "Supported browser integration capabilities", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/BrowserIntegrationCapabilitiesResponse" + } + } + } + }, + "401": { + "description": "Missing, invalid, rotated, or revoked integration token", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + } + }, + "x-grimoire-source-method": "GET" + } + }, + "/integrations/browser/v1/taxonomy": { + "get": { + "tags": [ + "Integrations" + ], + "summary": "List the categories and tags available to the packaged browser extension.", + "description": "Requires a managed integration bearer token. This route keeps extension-origin reads inside the versioned browser integration surface instead of exposing unrelated local-library GET routes.", + "operationId": "getIntegrationsBrowserV1Taxonomy", + "responses": { + "200": { + "description": "Browser capture categories and tags", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/BrowserIntegrationTaxonomyResponse" + } + } + } + }, + "401": { + "description": "Missing, invalid, rotated, or revoked integration token", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + } + }, + "x-grimoire-source-method": "GET" + } + }, "/capture/bookmarklet": { "get": { "tags": [ @@ -5144,6 +5210,14 @@ "maxLength": 100000, "nullable": true }, + "is_pinned": { + "type": "boolean", + "description": "Whether the new bookmark should be pinned" + }, + "read_later": { + "type": "boolean", + "description": "Whether the new bookmark should be added to Read Later" + }, "source": { "$ref": "#/components/schemas/CaptureSource" } @@ -9094,6 +9168,163 @@ ], "additionalProperties": false }, + "BrowserIntegrationCapabilities": { + "type": "object", + "description": "Authenticated capabilities advertised to packaged browser extensions", + "properties": { + "protocol": { + "type": "string", + "description": "Stable browser integration protocol identifier", + "enum": [ + "grimoire-browser-capture" + ] + }, + "protocol_version": { + "type": "integer", + "description": "Browser integration protocol version", + "minimum": 1, + "maximum": 1 + }, + "grimoire_version": { + "type": "string", + "description": "Daemon package version" + }, + "endpoints": { + "type": "object", + "properties": { + "capture": { + "type": "string", + "description": "Authenticated bookmark capture path" + }, + "taxonomy": { + "type": "string", + "description": "Authenticated category and tag discovery path" + } + }, + "required": [ + "capture", + "taxonomy" + ], + "additionalProperties": false + }, + "capture_fields": { + "type": "object", + "description": "Optional capture fields supported by this daemon", + "properties": { + "is_pinned": { + "type": "boolean", + "description": "Capture accepts an initial pinned state" + }, + "read_later": { + "type": "boolean", + "description": "Capture accepts an initial Read Later state" + } + }, + "required": [ + "is_pinned", + "read_later" + ], + "additionalProperties": false + }, + "limits": { + "type": "object", + "properties": { + "request_bytes": { + "type": "integer", + "description": "Maximum capture request bytes", + "minimum": 1 + }, + "title_characters": { + "type": "integer", + "description": "Maximum title characters", + "minimum": 1 + }, + "notes_characters": { + "type": "integer", + "description": "Maximum notes characters", + "minimum": 1 + }, + "selected_text_characters": { + "type": "integer", + "description": "Maximum selected text characters", + "minimum": 1 + }, + "tag_characters": { + "type": "integer", + "description": "Maximum characters per tag", + "minimum": 1 + } + }, + "required": [ + "request_bytes", + "title_characters", + "notes_characters", + "selected_text_characters", + "tag_characters" + ], + "additionalProperties": false + } + }, + "required": [ + "protocol", + "protocol_version", + "grimoire_version", + "endpoints", + "limits" + ], + "additionalProperties": false + }, + "BrowserIntegrationCapabilitiesResponse": { + "type": "object", + "description": "Browser integration capability response", + "properties": { + "data": { + "$ref": "#/components/schemas/BrowserIntegrationCapabilities" + } + }, + "required": [ + "data" + ], + "additionalProperties": false + }, + "BrowserIntegrationTaxonomy": { + "type": "object", + "description": "Authenticated taxonomy subset used by the browser extension", + "properties": { + "categories": { + "type": "array", + "description": "Category tree", + "items": { + "$ref": "#/components/schemas/CategoryNode" + } + }, + "tags": { + "type": "array", + "description": "Tags", + "items": { + "$ref": "#/components/schemas/TagWithCount" + } + } + }, + "required": [ + "categories", + "tags" + ], + "additionalProperties": false + }, + "BrowserIntegrationTaxonomyResponse": { + "type": "object", + "description": "Browser integration taxonomy response", + "properties": { + "data": { + "$ref": "#/components/schemas/BrowserIntegrationTaxonomy" + } + }, + "required": [ + "data" + ], + "additionalProperties": false + }, "Diagnostics": { "type": "object", "description": "Redacted local diagnostics payload for user-shared support bundles", diff --git a/package-lock.json b/package-lock.json index 672bad24..5936775d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "grimoire", - "version": "1.1.0", + "version": "1.2.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "grimoire", - "version": "1.1.0", + "version": "1.2.0", "license": "MIT", "dependencies": { "@dnd-kit/core": "^6.3.1", @@ -55,7 +55,7 @@ "react-hook-form": "^7.61.1", "react-markdown": "^10.1.0", "react-resizable-panels": "^2.1.9", - "react-router-dom": "^6.30.1", + "react-router-dom": "^7.18.3", "recharts": "^2.15.4", "sonner": "^1.7.4", "tailwind-merge": "^2.6.0", @@ -85,8 +85,8 @@ "tailwindcss": "^3.4.17", "typescript": "^5.8.3", "typescript-eslint": "^8.38.0", - "vite": "^5.4.19", - "vitest": "^3.2.4" + "vite": "^6.4.3", + "vitest": "^4.1.11" } }, "node_modules/@adobe/css-tools": { @@ -198,9 +198,9 @@ } }, "node_modules/@esbuild/aix-ppc64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", - "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz", + "integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==", "cpu": [ "ppc64" ], @@ -211,13 +211,13 @@ "aix" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/android-arm": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", - "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.12.tgz", + "integrity": "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==", "cpu": [ "arm" ], @@ -228,13 +228,13 @@ "android" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/android-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", - "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz", + "integrity": "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==", "cpu": [ "arm64" ], @@ -245,13 +245,13 @@ "android" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/android-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", - "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.12.tgz", + "integrity": "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==", "cpu": [ "x64" ], @@ -262,13 +262,13 @@ "android" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/darwin-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", - "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz", + "integrity": "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==", "cpu": [ "arm64" ], @@ -279,13 +279,13 @@ "darwin" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/darwin-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", - "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz", + "integrity": "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==", "cpu": [ "x64" ], @@ -296,13 +296,13 @@ "darwin" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/freebsd-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", - "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz", + "integrity": "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==", "cpu": [ "arm64" ], @@ -313,13 +313,13 @@ "freebsd" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/freebsd-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", - "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz", + "integrity": "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==", "cpu": [ "x64" ], @@ -330,13 +330,13 @@ "freebsd" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-arm": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", - "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz", + "integrity": "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==", "cpu": [ "arm" ], @@ -347,13 +347,13 @@ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", - "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz", + "integrity": "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==", "cpu": [ "arm64" ], @@ -364,13 +364,13 @@ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-ia32": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", - "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz", + "integrity": "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==", "cpu": [ "ia32" ], @@ -381,13 +381,13 @@ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-loong64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", - "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz", + "integrity": "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==", "cpu": [ "loong64" ], @@ -398,13 +398,13 @@ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-mips64el": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", - "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz", + "integrity": "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==", "cpu": [ "mips64el" ], @@ -415,13 +415,13 @@ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-ppc64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", - "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz", + "integrity": "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==", "cpu": [ "ppc64" ], @@ -432,13 +432,13 @@ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-riscv64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", - "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz", + "integrity": "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==", "cpu": [ "riscv64" ], @@ -449,13 +449,13 @@ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-s390x": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", - "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz", + "integrity": "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==", "cpu": [ "s390x" ], @@ -466,13 +466,13 @@ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", - "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz", + "integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==", "cpu": [ "x64" ], @@ -483,13 +483,30 @@ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz", + "integrity": "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" } }, "node_modules/@esbuild/netbsd-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", - "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz", + "integrity": "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==", "cpu": [ "x64" ], @@ -500,13 +517,30 @@ "netbsd" ], "engines": { - "node": ">=12" + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz", + "integrity": "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" } }, "node_modules/@esbuild/openbsd-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", - "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz", + "integrity": "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==", "cpu": [ "x64" ], @@ -517,13 +551,30 @@ "openbsd" ], "engines": { - "node": ">=12" + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz", + "integrity": "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" } }, "node_modules/@esbuild/sunos-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", - "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz", + "integrity": "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==", "cpu": [ "x64" ], @@ -534,13 +585,13 @@ "sunos" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/win32-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", - "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz", + "integrity": "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==", "cpu": [ "arm64" ], @@ -551,13 +602,13 @@ "win32" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/win32-ia32": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", - "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz", + "integrity": "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==", "cpu": [ "ia32" ], @@ -568,13 +619,13 @@ "win32" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/win32-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", - "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz", + "integrity": "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==", "cpu": [ "x64" ], @@ -585,7 +636,7 @@ "win32" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@eslint-community/eslint-utils": { @@ -790,41 +841,41 @@ } }, "node_modules/@humanfs/core": { - "version": "0.19.1", - "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", - "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.2.tgz", + "integrity": "sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==", "dev": true, "license": "Apache-2.0", + "dependencies": { + "@humanfs/types": "^0.15.0" + }, "engines": { "node": ">=18.18.0" } }, "node_modules/@humanfs/node": { - "version": "0.16.6", - "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.6.tgz", - "integrity": "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==", + "version": "0.16.8", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.8.tgz", + "integrity": "sha512-gE1eQNZ3R++kTzFUpdGlpmy8kDZD/MLyHqDwqjkVQI0JMdI1D51sy1H958PNXYkM2rAac7e5/CnIKZrHtPh3BQ==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@humanfs/core": "^0.19.1", - "@humanwhocodes/retry": "^0.3.0" + "@humanfs/core": "^0.19.2", + "@humanfs/types": "^0.15.0", + "@humanwhocodes/retry": "^0.4.0" }, "engines": { "node": ">=18.18.0" } }, - "node_modules/@humanfs/node/node_modules/@humanwhocodes/retry": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.1.tgz", - "integrity": "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==", + "node_modules/@humanfs/types": { + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/@humanfs/types/-/types-0.15.0.tgz", + "integrity": "sha512-ZZ1w0aoQkwuUuC7Yf+7sdeaNfqQiiLcSRbfI08oAxqLtpXQr9AIVX7Ay7HLDuiLYAaFPu8oBYNq/QIi9URHJ3Q==", "dev": true, "license": "Apache-2.0", "engines": { - "node": ">=18.18" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" + "node": ">=18.18.0" } }, "node_modules/@humanwhocodes/module-importer": { @@ -920,6 +971,26 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@napi-rs/lzma-linux-x64-gnu": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/@napi-rs/lzma-linux-x64-gnu/-/lzma-linux-x64-gnu-1.5.1.tgz", + "integrity": "sha512-oTXEIha4SsuXdTA4Iyskj0kpdx2yVXdhd75c2v3xGrHFfVMsbhTPZU/nMPL4sWKo4pBHm3aucLaqGlF696dTyQ==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^22.20 || ^24.12 || >=25" + } + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -2335,15 +2406,6 @@ "integrity": "sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw==", "license": "MIT" }, - "node_modules/@remix-run/router": { - "version": "1.23.0", - "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.23.0.tgz", - "integrity": "sha512-O3rHJzAQKamUz1fvE0Qaw0xSFqsA/yafi2iqeE0pvdFtCO1viYx8QL6f3Ln/aCCTLxs68SLf0KPM9eSeM8yBnA==", - "license": "MIT", - "engines": { - "node": ">=14.0.0" - } - }, "node_modules/@rolldown/pluginutils": { "version": "1.0.0-beta.27", "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.27.tgz", @@ -2352,9 +2414,9 @@ "license": "MIT" }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.24.0.tgz", - "integrity": "sha512-Q6HJd7Y6xdB48x8ZNVDOqsbh2uByBhgK8PiQgPhwkIw/HC/YX5Ghq2mQY5sRMZWHb3VsFkWooUVOZHKr7DmDIA==", + "version": "4.63.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.63.1.tgz", + "integrity": "sha512-UZ8sUxPTiHWYX9QNdJedb1kDZSpS1t/VPWBWGSgqHNi9w3Cu6IXvu2mzbhiTiPvtrqgTQJ+zqiAq2iPIPilpaQ==", "cpu": [ "arm" ], @@ -2366,9 +2428,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.24.0.tgz", - "integrity": "sha512-ijLnS1qFId8xhKjT81uBHuuJp2lU4x2yxa4ctFPtG+MqEE6+C5f/+X/bStmxapgmwLwiL3ih122xv8kVARNAZA==", + "version": "4.63.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.63.1.tgz", + "integrity": "sha512-cQ4nFQABN5cDvDpbvJ7bMStCpnaVxynZrRMfUJYgxcIk9Sh54FIO1vtfkg0B69REjER77ioZ/ov+eAApx/KmLQ==", "cpu": [ "arm64" ], @@ -2380,9 +2442,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.24.0.tgz", - "integrity": "sha512-bIv+X9xeSs1XCk6DVvkO+S/z8/2AMt/2lMqdQbMrmVpgFvXlmde9mLcbQpztXm1tajC3raFDqegsH18HQPMYtA==", + "version": "4.63.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.63.1.tgz", + "integrity": "sha512-FQNqd1lRy/0QhDk3xeRIkSBiCpXCiDnZO3YLVdcDKN1UBiKToNftCzcXYNLshmPDUMlu2TdeS8tGcsU6f3YF1Q==", "cpu": [ "arm64" ], @@ -2394,9 +2456,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.24.0.tgz", - "integrity": "sha512-X6/nOwoFN7RT2svEQWUsW/5C/fYMBe4fnLK9DQk4SX4mgVBiTA9h64kjUYPvGQ0F/9xwJ5U5UfTbl6BEjaQdBQ==", + "version": "4.63.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.63.1.tgz", + "integrity": "sha512-pvD16V939D3CloK0+qikpGaxiPrDUXTe7Y5cWOMkMSy7m1cawa8EGy/kXYi/G/cKAC4HDAbSnzCIk1WmsoOKXg==", "cpu": [ "x64" ], @@ -2407,14 +2469,45 @@ "darwin" ] }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.63.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.63.1.tgz", + "integrity": "sha512-pcFGeL2345VwdTnJhA6zLbew+YgWB0qBG2+dMtXjCicf6+rm6kO6cOoh5VnTe0ZMrMRgRyuHmCJxZWrIdzYuOw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.63.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.63.1.tgz", + "integrity": "sha512-mRJlqSRulVzcKq/LKA6ICSIc3K/l4fzlVn/gePn2nXIHy8seRi5z/eeRE0d/XMBxcMldiXtQTSpRj0tkkC3g8Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.24.0.tgz", - "integrity": "sha512-0KXvIJQMOImLCVCz9uvvdPgfyWo93aHHp8ui3FrtOP57svqrF/roSSR5pjqL2hcMp0ljeGlU4q9o/rQaAQ3AYA==", + "version": "4.63.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.63.1.tgz", + "integrity": "sha512-YDUNvVM85TI3g/1OpnqKP1h4NeW/j64DfWMf+G3M809xNk1bJSnpFp4sh83NpmVE5DXnkh8ULor4LTVZKoYLHw==", "cpu": [ "arm" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -2422,13 +2515,16 @@ ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.24.0.tgz", - "integrity": "sha512-it2BW6kKFVh8xk/BnHfakEeoLPv8STIISekpoF+nBgWM4d55CZKc7T4Dx1pEbTnYm/xEKMgy1MNtYuoA8RFIWw==", + "version": "4.63.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.63.1.tgz", + "integrity": "sha512-7Mcn71p9ZuQFAj+h+dhQXy/yeLePRS2yKRnmW1DijA9thKO5qap0GNOIQK4yQ6iP3SU0Mrb/yWo8h8vgRba8lw==", "cpu": [ "arm" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -2436,13 +2532,16 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.24.0.tgz", - "integrity": "sha512-i0xTLXjqap2eRfulFVlSnM5dEbTVque/3Pi4g2y7cxrs7+a9De42z4XxKLYJ7+OhE3IgxvfQM7vQc43bwTgPwA==", + "version": "4.63.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.63.1.tgz", + "integrity": "sha512-4YiLQTX6U4CSl0L9cluep9A9W6UmTfqBDc2/CH6wlu54pl4E7Jn3cOD8oxzvBDEGk/JMKgJ47C8g+radF7mwvg==", "cpu": [ "arm64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -2450,27 +2549,84 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.24.0.tgz", - "integrity": "sha512-9E6MKUJhDuDh604Qco5yP/3qn3y7SLXYuiC0Rpr89aMScS2UAmK1wHP2b7KAa1nSjWJc/f/Lc0Wl1L47qjiyQw==", + "version": "4.63.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.63.1.tgz", + "integrity": "sha512-2ra8F7w8OquwZN9z2/fKFnli69wa8PLwaVzRMIPGb13ByMJwC28Fbp8YcVGoUhlYMTt7j5j9bNgpysrN2UM+vw==", "cpu": [ "arm64" ], "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.63.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.63.1.tgz", + "integrity": "sha512-Sy20ncyhjmBP0Ml+UvQbimjlk6VFgjW5uNP+qqwHB00mTE8Bl2C1TuHTlRwK2YoXeZbee5lP2XevBWVkAQAtSQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.63.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.63.1.tgz", + "integrity": "sha512-noITLp8oNjYliPnGWmLyelIHwULGqbHloQHGw1rtxbWhTuWooRpnZarZQJ1y9EUC4szuCusCc+HEpUtxpIwYvA==", + "cpu": [ + "loong64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.63.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.63.1.tgz", + "integrity": "sha512-hlxxXd+F1mWiAcaFR7Sv9ZQT6m6UfI8+Vy/kFJzztq2pDMU/0wZ9sish0iszNZvsQDo8Gc0i5yuFEOz5dDf6fA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ "linux" ] }, - "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.24.0.tgz", - "integrity": "sha512-2XFFPJ2XMEiF5Zi2EBf4h73oR1V/lycirxZxHZNc93SqDN/IWhYYSYj8I9381ikUFXZrz2v7r2tOVk2NBwxrWw==", + "node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.63.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.63.1.tgz", + "integrity": "sha512-EF7OpqQTQ/BvGqLzUi4rEHuagCV9MugAUXSHemwPW5vxZ75RR+jxO/2j95Ph2dalMpFHSVECjRoioHZgA9zOYA==", "cpu": [ "ppc64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -2478,13 +2634,33 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.24.0.tgz", - "integrity": "sha512-M3Dg4hlwuntUCdzU7KjYqbbd+BLq3JMAOhCKdBE3TcMGMZbKkDdJ5ivNdehOssMCIokNHFOsv7DO4rlEOfyKpg==", + "version": "4.63.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.63.1.tgz", + "integrity": "sha512-wQO3JesW9PRkwlabQ27y7sPfVOOTLRG73I4F2UYHG5PXun3J9U3y+b7ezVKSYbsvSKGQ1k1cq8Qlun4C9kLt3w==", "cpu": [ "riscv64" ], "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.63.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.63.1.tgz", + "integrity": "sha512-ouAGwhO6wHRXdnOVCOsB0tRFkA7nhNB2Nwax6oECXN0YiN8EYUTBAOudADOB1PI+yDL61TeNx/u7MVCzksNbkQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -2492,13 +2668,16 @@ ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.24.0.tgz", - "integrity": "sha512-mjBaoo4ocxJppTorZVKWFpy1bfFj9FeCMJqzlMQGjpNPY9JwQi7OuS1axzNIk0nMX6jSgy6ZURDZ2w0QW6D56g==", + "version": "4.63.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.63.1.tgz", + "integrity": "sha512-q2R38Sn+1J8RxhfJ+T54wSWmyKXWec+9jgDfqO2AtArEqHO5R2aeayp5H5OYLr5UYDVGsVaZPEFUooMhYCdz5A==", "cpu": [ "s390x" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -2506,13 +2685,16 @@ ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.24.0.tgz", - "integrity": "sha512-ZXFk7M72R0YYFN5q13niV0B7G8/5dcQ9JDp8keJSfr3GoZeXEoMHP/HlvqROA3OMbMdfr19IjCeNAnPUG93b6A==", + "version": "4.63.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.63.1.tgz", + "integrity": "sha512-gfI5T24WLLuFfSKw7Go/zDXjAAV0fny0swTaDv+WjK7vqcw4cRhFfdsyKL1n+ukI+ooBxn3bVQnyrn06WpI50w==", "cpu": [ "x64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -2520,23 +2702,54 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.24.0.tgz", - "integrity": "sha512-w1i+L7kAXZNdYl+vFvzSZy8Y1arS7vMgIy8wusXJzRrPyof5LAb02KGr1PD2EkRcl73kHulIID0M501lN+vobQ==", + "version": "4.63.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.63.1.tgz", + "integrity": "sha512-4h6XqthmB4Hspji84wvgk+ElodTsGj+dbZqHJHHtKxj4mYq0ANSEEPX9ys3moJueqsRjwpaJYH7874Itwnj2ow==", "cpu": [ "x64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ "linux" ] }, + "node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.63.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.63.1.tgz", + "integrity": "sha512-dlfCOa87o1VAYegLQ9EKilx2JCeRofiyPGhTCmqnuXZ6bMPiycO1rq1+sKoulAp7pGLIsTIw+1x5R+zgh5LhhA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.63.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.63.1.tgz", + "integrity": "sha512-cjkLbOlfcm3QGhMM1J5zaZjsw1GggbN6rw9UTSSRrPrR1KkcXnN7Uq9rPw34xImQ9VOY9GN+6u2Zj80B9ptkcw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.24.0.tgz", - "integrity": "sha512-VXBrnPWgBpVDCVY6XF3LEW0pOU51KbaHhccHw6AS6vBWIC60eqsH19DAeeObl+g8nKAz04QFdl/Cefta0xQtUQ==", + "version": "4.63.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.63.1.tgz", + "integrity": "sha512-Li1KdUnWGE4N3e1F/B4RTB1ms+nG4WBgjByO46pkeBVX/2UBsY53xf5vK9WygVmnH3RwncIST7lkSdLSY6P9lg==", "cpu": [ "arm64" ], @@ -2548,9 +2761,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.24.0.tgz", - "integrity": "sha512-xrNcGDU0OxVcPTH/8n/ShH4UevZxKIO6HJFK0e15XItZP2UcaiLFd5kiX7hJnqCbSztUF8Qot+JWBC/QXRPYWQ==", + "version": "4.63.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.63.1.tgz", + "integrity": "sha512-t4ZYOSoLTgwhuFMrmTMLx/+i1DQVK7HYqMc6kY46EApwi8X0nIVphzdNoThU3xt6n+N5urG1/gxBdCaKDLavfg==", "cpu": [ "ia32" ], @@ -2561,10 +2774,24 @@ "win32" ] }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.63.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.63.1.tgz", + "integrity": "sha512-RgroPfMmKlD1RzSDxvwgcPiy2HNQKoYV7OmwIXDsk73uKW5t6B/V8KIy27SMv/FNXFo/oSBtWc9J0X7t91ezZg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.24.0.tgz", - "integrity": "sha512-fbMkAF7fufku0N2dE5TBXcNlg0pt0cJue4xBRE2Qc5Vqikxr4VCgKj/ht6SMdFcOacVA9rqF70APJ8RN/4vMJw==", + "version": "4.63.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.63.1.tgz", + "integrity": "sha512-at8QVep6S3h5Y6gSbdGU06bRY5WJkf6WUduM9YtvYMbYhB1MOFfUgc6kehitQXzOtMSaT70q7f9ydPhpqu821w==", "cpu": [ "x64" ], @@ -2575,6 +2802,13 @@ "win32" ] }, + "node_modules/@standard-schema/spec": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", + "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==", + "dev": true, + "license": "MIT" + }, "node_modules/@swc/core": { "version": "1.13.2", "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.13.2.tgz", @@ -2947,9 +3181,9 @@ } }, "node_modules/@tootallnate/once": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", - "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.1.tgz", + "integrity": "sha512-HqmEUIGRJ5fSXchkVgR5F7qn48bDBzv0kWj/Kfu5e6uci4UlEeng4331LnBkWffb++Ei3FOVLxo8JJWMFBDMeQ==", "dev": true, "license": "MIT", "engines": { @@ -3055,9 +3289,9 @@ "license": "MIT" }, "node_modules/@types/estree": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", - "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==", + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", + "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", "license": "MIT" }, "node_modules/@types/estree-jsx": { @@ -3333,9 +3567,9 @@ } }, "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.4.tgz", + "integrity": "sha512-hGfVzPxthbf3+2yjg/RBs60cB0FhqBS/zvdV/4wn4/BmN0bNMMHPc4V/BbFieqf1TKAGGAHnY4eSjajCl0f2Xg==", "dev": true, "license": "MIT", "dependencies": { @@ -3343,13 +3577,13 @@ } }, "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", + "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", "dev": true, "license": "ISC", "dependencies": { - "brace-expansion": "^2.0.1" + "brace-expansion": "^2.0.2" }, "engines": { "node": ">=16 || 14 >=14.17" @@ -3421,39 +3655,40 @@ } }, "node_modules/@vitest/expect": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.2.4.tgz", - "integrity": "sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig==", + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.11.tgz", + "integrity": "sha512-VX2x5vNJXET47KAFzwERI+KRMtTTCSWTfSMKsW7JsUsXV4psq++e3DvZpuTDOpHcxytiDs6p2nhVb2tVDiiUYw==", "dev": true, "license": "MIT", "dependencies": { + "@standard-schema/spec": "^1.1.0", "@types/chai": "^5.2.2", - "@vitest/spy": "3.2.4", - "@vitest/utils": "3.2.4", - "chai": "^5.2.0", - "tinyrainbow": "^2.0.0" + "@vitest/spy": "4.1.11", + "@vitest/utils": "4.1.11", + "chai": "^6.2.2", + "tinyrainbow": "^3.1.0" }, "funding": { "url": "https://opencollective.com/vitest" } }, "node_modules/@vitest/mocker": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-3.2.4.tgz", - "integrity": "sha512-46ryTE9RZO/rfDd7pEqFl7etuyzekzEhUbTW3BvmeO/BcCMEgq59BKhek3dXDWgAj4oMK6OZi+vRr1wPW6qjEQ==", + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.11.tgz", + "integrity": "sha512-2XJVD55d1o5AZous5CCGKS74g/riOj9odEt2bQpCVZeblHyHdnMeFl4jl0XjU21stf4mbjUkew2eXQZt65g5CQ==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/spy": "3.2.4", + "@vitest/spy": "4.1.11", "estree-walker": "^3.0.3", - "magic-string": "^0.30.17" + "magic-string": "^0.30.21" }, "funding": { "url": "https://opencollective.com/vitest" }, "peerDependencies": { "msw": "^2.4.9", - "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" }, "peerDependenciesMeta": { "msw": { @@ -3465,42 +3700,42 @@ } }, "node_modules/@vitest/pretty-format": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.2.4.tgz", - "integrity": "sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA==", + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.11.tgz", + "integrity": "sha512-yiZzPbGTS9Sr/JpFl8zHrcIkAofNbFV6k21vIgQN/cY/oxZeXhJv5sc/MBJ5jFKWmWs+oJHw0UXLZjmf931+Vw==", "dev": true, "license": "MIT", "dependencies": { - "tinyrainbow": "^2.0.0" + "tinyrainbow": "^3.1.0" }, "funding": { "url": "https://opencollective.com/vitest" } }, "node_modules/@vitest/runner": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-3.2.4.tgz", - "integrity": "sha512-oukfKT9Mk41LreEW09vt45f8wx7DordoWUZMYdY/cyAk7w5TWkTRCNZYF7sX7n2wB7jyGAl74OxgwhPgKaqDMQ==", + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.11.tgz", + "integrity": "sha512-LztvUgdwMNJMIkj3hQnnxiC2Xy1zNxq928W/xhjCLaNCzqTZOudjwbQf6v9IntZGPw132i2Lq2rgTRZHD3JHNw==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/utils": "3.2.4", - "pathe": "^2.0.3", - "strip-literal": "^3.0.0" + "@vitest/utils": "4.1.11", + "pathe": "^2.0.3" }, "funding": { "url": "https://opencollective.com/vitest" } }, "node_modules/@vitest/snapshot": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-3.2.4.tgz", - "integrity": "sha512-dEYtS7qQP2CjU27QBC5oUOxLE/v5eLkGqPE0ZKEIDGMs4vKWe7IjgLOeauHsR0D5YuuycGRO5oSRXnwnmA78fQ==", + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.11.tgz", + "integrity": "sha512-pN7ikn1ON7h8ee4gIAp4AzyK+zBtJPzVbqOgu5LCEh4VaJVbPQcgYQYJIMGQPXVeJJq1fnfazis7a5pFNPahog==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "3.2.4", - "magic-string": "^0.30.17", + "@vitest/pretty-format": "4.1.11", + "@vitest/utils": "4.1.11", + "magic-string": "^0.30.21", "pathe": "^2.0.3" }, "funding": { @@ -3508,28 +3743,25 @@ } }, "node_modules/@vitest/spy": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.2.4.tgz", - "integrity": "sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw==", + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.11.tgz", + "integrity": "sha512-apNa/prQy2qCeywhnixOHPRCgGNhvg7T4Dapfl1GahLp/R+uhBm5cPyFoNVyqsNd2h1nJxL6BqqdIjiABL60YA==", "dev": true, "license": "MIT", - "dependencies": { - "tinyspy": "^4.0.3" - }, "funding": { "url": "https://opencollective.com/vitest" } }, "node_modules/@vitest/utils": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-3.2.4.tgz", - "integrity": "sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA==", + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.11.tgz", + "integrity": "sha512-zTCVGpyFsGWBhllOyKlTw/vnr6D9qxsfSDyfbyZmTyjHw5N/VuvzHpHoQjm2ZJzn4RJgx5w4r7V0er69CmLgPQ==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "3.2.4", - "loupe": "^3.1.4", - "tinyrainbow": "^2.0.0" + "@vitest/pretty-format": "4.1.11", + "convert-source-map": "^2.0.0", + "tinyrainbow": "^3.1.0" }, "funding": { "url": "https://opencollective.com/vitest" @@ -3604,9 +3836,9 @@ } }, "node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.15.0.tgz", + "integrity": "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==", "dev": true, "license": "MIT", "dependencies": { @@ -3772,6 +4004,19 @@ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "license": "MIT" }, + "node_modules/baseline-browser-mapping": { + "version": "2.11.21", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.11.21.tgz", + "integrity": "sha512-uh8vpY/1/YyFkunIDFH/12p7/7VdPKA1hejMVEbdkEaWnUz0Hesvx5EbiU6XxjyHZIOju+ZMbQJkRh+es3/spQ==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.cjs" + }, + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/binary-extensions": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", @@ -3785,9 +4030,9 @@ } }, "node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "version": "1.1.18", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.18.tgz", + "integrity": "sha512-Edep/X9fGqVNmzKBVsDYIOtD+z1tuezV70LBjdCst9Tqu76lsnvRiZ6oTic1n+/BIwX6QDGAO94PN4N2SADvtw==", "dev": true, "license": "MIT", "dependencies": { @@ -3808,9 +4053,9 @@ } }, "node_modules/browserslist": { - "version": "4.25.1", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.25.1.tgz", - "integrity": "sha512-KGj0KoOMXLpSNkkEI6Z6mShmQy0bc1I+T7K9N81k4WWMrfz+6fQ6es80B/YLAeRoKvjYE1YSHHOW1qe9xIVzHw==", + "version": "4.28.9", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.9.tgz", + "integrity": "sha512-EWazOblFYUvlGZcfGhPUPmYh3nikUxBVb+y9MJun5f3hBi812X+8MSQTujLBtgK3cf51fJWbWfOjyeO954d+Eg==", "dev": true, "funding": [ { @@ -3828,10 +4073,11 @@ ], "license": "MIT", "dependencies": { - "caniuse-lite": "^1.0.30001726", - "electron-to-chromium": "^1.5.173", - "node-releases": "^2.0.19", - "update-browserslist-db": "^1.1.3" + "baseline-browser-mapping": "^2.11.20", + "caniuse-lite": "^1.0.30001810", + "electron-to-chromium": "^1.5.420", + "node-releases": "^2.0.54", + "update-browserslist-db": "^1.3.2" }, "bin": { "browserslist": "cli.js" @@ -3840,16 +4086,6 @@ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, - "node_modules/cac": { - "version": "6.7.14", - "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", - "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, "node_modules/call-bind-apply-helpers": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", @@ -3884,9 +4120,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001727", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001727.tgz", - "integrity": "sha512-pB68nIHmbN6L/4C6MH1DokyR3bYqFwjaSs/sWDHGj4CTcFtQUQMuJftVwWkXq7mNWOybD3KhUv3oWHoGxgP14Q==", + "version": "1.0.30001810", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001810.tgz", + "integrity": "sha512-TITQPUkaz+aVk5GL6NhOdwk1aEaNTSDPsGFWrTuhKGtjTF70jL/Oht2W4c6rXUe5fu7Ie19VIahAXHIIiWWNeg==", "dev": true, "funding": [ { @@ -3915,18 +4151,11 @@ } }, "node_modules/chai": { - "version": "5.3.3", - "resolved": "https://registry.npmjs.org/chai/-/chai-5.3.3.tgz", - "integrity": "sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw==", + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/chai/-/chai-6.2.2.tgz", + "integrity": "sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==", "dev": true, "license": "MIT", - "dependencies": { - "assertion-error": "^2.0.1", - "check-error": "^2.1.1", - "deep-eql": "^5.0.1", - "loupe": "^3.1.0", - "pathval": "^2.0.0" - }, "engines": { "node": ">=18" } @@ -3988,16 +4217,6 @@ "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/check-error": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/check-error/-/check-error-2.1.3.tgz", - "integrity": "sha512-PAJdDJusoxnwm1VwW07VWwUN1sl7smmC3OKggvndJFadxxDRyFJBX/ggnu/KE4kQAB7a3Dp8f/YXC1FlUprWmA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 16" - } - }, "node_modules/chokidar": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", @@ -4127,6 +4346,26 @@ "dev": true, "license": "MIT" }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/cookie": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.1.1.tgz", + "integrity": "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, "node_modules/cross-spawn": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", @@ -4381,16 +4620,6 @@ "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/deep-eql": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-5.0.2.tgz", - "integrity": "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, "node_modules/deep-is": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", @@ -4502,9 +4731,9 @@ "license": "MIT" }, "node_modules/electron-to-chromium": { - "version": "1.5.192", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.192.tgz", - "integrity": "sha512-rP8Ez0w7UNw/9j5eSXCe10o1g/8B1P5SM90PCCMVkIRQn2R0LEHWz4Eh9RnxkniuDe1W0cTSOB3MLlkTGDcuCg==", + "version": "1.5.425", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.425.tgz", + "integrity": "sha512-QvPtl41EUOnuT1HBvMKgxXRIaHNcagBPs50u7VULzhZXaGfqTbZyE16LQsctZ/RQHlGu+FOWeDTR4mY6YbeF1g==", "dev": true, "license": "ISC" }, @@ -4576,9 +4805,9 @@ } }, "node_modules/es-module-lexer": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz", - "integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==", + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.3.2.tgz", + "integrity": "sha512-poHGpORABojJJucnV9KbOavETW8lBVnphkW77ER5/BQ5Fz7oXSoCNek7IH3vR5nRjdsEz926ibFYX8KtLQmdyw==", "dev": true, "license": "MIT" }, @@ -4612,9 +4841,9 @@ } }, "node_modules/esbuild": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", - "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz", + "integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==", "dev": true, "hasInstallScript": true, "license": "MIT", @@ -4622,32 +4851,35 @@ "esbuild": "bin/esbuild" }, "engines": { - "node": ">=12" + "node": ">=18" }, "optionalDependencies": { - "@esbuild/aix-ppc64": "0.21.5", - "@esbuild/android-arm": "0.21.5", - "@esbuild/android-arm64": "0.21.5", - "@esbuild/android-x64": "0.21.5", - "@esbuild/darwin-arm64": "0.21.5", - "@esbuild/darwin-x64": "0.21.5", - "@esbuild/freebsd-arm64": "0.21.5", - "@esbuild/freebsd-x64": "0.21.5", - "@esbuild/linux-arm": "0.21.5", - "@esbuild/linux-arm64": "0.21.5", - "@esbuild/linux-ia32": "0.21.5", - "@esbuild/linux-loong64": "0.21.5", - "@esbuild/linux-mips64el": "0.21.5", - "@esbuild/linux-ppc64": "0.21.5", - "@esbuild/linux-riscv64": "0.21.5", - "@esbuild/linux-s390x": "0.21.5", - "@esbuild/linux-x64": "0.21.5", - "@esbuild/netbsd-x64": "0.21.5", - "@esbuild/openbsd-x64": "0.21.5", - "@esbuild/sunos-x64": "0.21.5", - "@esbuild/win32-arm64": "0.21.5", - "@esbuild/win32-ia32": "0.21.5", - "@esbuild/win32-x64": "0.21.5" + "@esbuild/aix-ppc64": "0.25.12", + "@esbuild/android-arm": "0.25.12", + "@esbuild/android-arm64": "0.25.12", + "@esbuild/android-x64": "0.25.12", + "@esbuild/darwin-arm64": "0.25.12", + "@esbuild/darwin-x64": "0.25.12", + "@esbuild/freebsd-arm64": "0.25.12", + "@esbuild/freebsd-x64": "0.25.12", + "@esbuild/linux-arm": "0.25.12", + "@esbuild/linux-arm64": "0.25.12", + "@esbuild/linux-ia32": "0.25.12", + "@esbuild/linux-loong64": "0.25.12", + "@esbuild/linux-mips64el": "0.25.12", + "@esbuild/linux-ppc64": "0.25.12", + "@esbuild/linux-riscv64": "0.25.12", + "@esbuild/linux-s390x": "0.25.12", + "@esbuild/linux-x64": "0.25.12", + "@esbuild/netbsd-arm64": "0.25.12", + "@esbuild/netbsd-x64": "0.25.12", + "@esbuild/openbsd-arm64": "0.25.12", + "@esbuild/openbsd-x64": "0.25.12", + "@esbuild/openharmony-arm64": "0.25.12", + "@esbuild/sunos-x64": "0.25.12", + "@esbuild/win32-arm64": "0.25.12", + "@esbuild/win32-ia32": "0.25.12", + "@esbuild/win32-x64": "0.25.12" } }, "node_modules/escalade": { @@ -5053,9 +5285,9 @@ } }, "node_modules/flatted": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz", - "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==", + "version": "3.4.4", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.4.tgz", + "integrity": "sha512-5+ybhBZANEJxaH3X5evAFatUxLfEHSr7n6kYJ+1Qd0mUqr4eu9gIf6GDbWHf8RJijHrjjO8G+la14SlL2SeS1Q==", "dev": true, "license": "ISC" }, @@ -5076,17 +5308,17 @@ } }, "node_modules/form-data": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz", - "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==", + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.6.tgz", + "integrity": "sha512-vKatAh4SlVfgbv+YtmhiRjhEMJsYpsG1Y2rMQtR+SVSbytsSD1YGzDIcrAJmdFec88u/+VoGmxnl+80gL1tRCQ==", "dev": true, "license": "MIT", "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", "es-set-tostringtag": "^2.1.0", - "hasown": "^2.0.2", - "mime-types": "^2.1.12" + "hasown": "^2.0.4", + "mime-types": "^2.1.35" }, "engines": { "node": ">= 6" @@ -5178,9 +5410,10 @@ } }, "node_modules/glob": { - "version": "10.4.5", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", - "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", "license": "ISC", "dependencies": { "foreground-child": "^3.1.0", @@ -5210,21 +5443,21 @@ } }, "node_modules/glob/node_modules/brace-expansion": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.4.tgz", + "integrity": "sha512-hGfVzPxthbf3+2yjg/RBs60cB0FhqBS/zvdV/4wn4/BmN0bNMMHPc4V/BbFieqf1TKAGGAHnY4eSjajCl0f2Xg==", "license": "MIT", "dependencies": { "balanced-match": "^1.0.0" } }, "node_modules/glob/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", + "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", "license": "ISC", "dependencies": { - "brace-expansion": "^2.0.1" + "brace-expansion": "^2.0.2" }, "engines": { "node": ">=16 || 14 >=14.17" @@ -5306,9 +5539,9 @@ } }, "node_modules/hasown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz", + "integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==", "license": "MIT", "dependencies": { "function-bind": "^1.1.2" @@ -5676,10 +5909,20 @@ "license": "MIT" }, "node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.2.tgz", + "integrity": "sha512-SFNOvSJ+Dgf/9An904Yx+CgSlIPCkIpao4qo51lpee25TIRejdH3rhR4EZMGoNx3/TP3O+wzWuiTFl4sqbltzA==", "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/puzrin" + }, + { + "type": "github", + "url": "https://github.com/sponsors/nodeca" + } + ], "license": "MIT", "dependencies": { "argparse": "^2.0.1" @@ -5814,9 +6057,9 @@ } }, "node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz", + "integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==", "license": "MIT" }, "node_modules/lodash.castarray": { @@ -5860,13 +6103,6 @@ "loose-envify": "cli.js" } }, - "node_modules/loupe": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.2.1.tgz", - "integrity": "sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ==", - "dev": true, - "license": "MIT" - }, "node_modules/lru-cache": { "version": "10.4.3", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", @@ -6563,9 +6799,9 @@ } }, "node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", "dev": true, "license": "ISC", "dependencies": { @@ -6637,11 +6873,14 @@ } }, "node_modules/node-releases": { - "version": "2.0.19", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", - "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==", + "version": "2.0.55", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.55.tgz", + "integrity": "sha512-mIrE/Cw9y+9Au6dS5vDKDhQza9YvG6w+ZrS6X+ZzA7yFW/soAeaups4Qzn1bL6g5FVy8WtP79+0j82oPIbqRjQ==", "dev": true, - "license": "MIT" + "license": "MIT", + "engines": { + "node": ">=18" + } }, "node_modules/normalize-path": { "version": "3.0.0", @@ -6687,6 +6926,20 @@ "node": ">= 6" } }, + "node_modules/obug": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/obug/-/obug-2.2.1.tgz", + "integrity": "sha512-XrsrhT5sybtKI6wakr2SPOlGZWWYbUXZ7a0jT8/QOeAPau+1X/bSegNe5YR75oJmEZQbKningirmGOEJCIk61Q==", + "dev": true, + "funding": [ + "https://github.com/sponsors/sxzz", + "https://opencollective.com/debug" + ], + "license": "MIT", + "engines": { + "node": ">=12.20.0" + } + }, "node_modules/optionator": { "version": "0.9.4", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", @@ -6842,16 +7095,6 @@ "dev": true, "license": "MIT" }, - "node_modules/pathval": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/pathval/-/pathval-2.0.1.tgz", - "integrity": "sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 14.16" - } - }, "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", @@ -6859,9 +7102,9 @@ "license": "ISC" }, "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", "license": "MIT", "engines": { "node": ">=8.6" @@ -7060,9 +7303,9 @@ } }, "node_modules/postcss-selector-parser": { - "version": "6.1.2", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", - "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", + "version": "6.1.4", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.4.tgz", + "integrity": "sha512-bIoJLOmjCO1S9XdY/DcnR5hJxvrDir1PbGChrzXG3vw0/FOliy/fA3dmdhQ441kah4gKv+TwckGzex6wNS5cnQ==", "license": "MIT", "dependencies": { "cssesc": "^3.0.0", @@ -7360,35 +7603,41 @@ } }, "node_modules/react-router": { - "version": "6.30.1", - "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.30.1.tgz", - "integrity": "sha512-X1m21aEmxGXqENEPG3T6u0Th7g0aS4ZmoNynhbs+Cn+q+QGTLt+d5IQ2bHAXKzKcxGJjxACpVbnYQSCRcfxHlQ==", + "version": "7.18.3", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.18.3.tgz", + "integrity": "sha512-gyXgtdr5uACJ5b1Q4udzjVV+tb/rlHIMJKuJ0e89R4Kzgz47z/rgP0dIKxktqIEUhDHluGTPJJH/wRha7CyqsA==", "license": "MIT", "dependencies": { - "@remix-run/router": "1.23.0" + "cookie": "^1.0.1", + "set-cookie-parser": "^2.6.0" }, "engines": { - "node": ">=14.0.0" + "node": ">=20.0.0" }, "peerDependencies": { - "react": ">=16.8" + "react": ">=18", + "react-dom": ">=18" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + } } }, "node_modules/react-router-dom": { - "version": "6.30.1", - "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.30.1.tgz", - "integrity": "sha512-llKsgOkZdbPU1Eg3zK8lCn+sjD9wMRZZPuzmdWWX5SUs8OFkN5HnFVC0u5KMeMaC9aoancFI/KoLuKPqN+hxHw==", + "version": "7.18.3", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.18.3.tgz", + "integrity": "sha512-ytVbyBBM7vMfRCam25r0WMhSVSom909A8p+8m0/f1w853dz/xfFu6etAT2SEbVoSnI+ZoPRDqIsQXVT89gp7kg==", "license": "MIT", "dependencies": { - "@remix-run/router": "1.23.0", - "react-router": "6.30.1" + "react-router": "7.18.3" }, "engines": { - "node": ">=14.0.0" + "node": ">=20.0.0" }, "peerDependencies": { - "react": ">=16.8", - "react-dom": ">=16.8" + "react": ">=18", + "react-dom": ">=18" } }, "node_modules/react-smooth": { @@ -7589,13 +7838,13 @@ } }, "node_modules/rollup": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.24.0.tgz", - "integrity": "sha512-DOmrlGSXNk1DM0ljiQA+i+o0rSLhtii1je5wgk60j49d1jHT5YYttBv1iWOnYSTG+fZZESUOSNiAl89SIet+Cg==", + "version": "4.63.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.63.1.tgz", + "integrity": "sha512-3Df9jsstwhccuEfmAMi9l8XUh/GOkVObmFTU7CCVBysEbcOZLl84jCtaAZMcPiMz2EGKsATzQcU+Xr3n/wU6cg==", "dev": true, "license": "MIT", "dependencies": { - "@types/estree": "1.0.6" + "@types/estree": "1.0.9" }, "bin": { "rollup": "dist/bin/rollup" @@ -7605,22 +7854,32 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.24.0", - "@rollup/rollup-android-arm64": "4.24.0", - "@rollup/rollup-darwin-arm64": "4.24.0", - "@rollup/rollup-darwin-x64": "4.24.0", - "@rollup/rollup-linux-arm-gnueabihf": "4.24.0", - "@rollup/rollup-linux-arm-musleabihf": "4.24.0", - "@rollup/rollup-linux-arm64-gnu": "4.24.0", - "@rollup/rollup-linux-arm64-musl": "4.24.0", - "@rollup/rollup-linux-powerpc64le-gnu": "4.24.0", - "@rollup/rollup-linux-riscv64-gnu": "4.24.0", - "@rollup/rollup-linux-s390x-gnu": "4.24.0", - "@rollup/rollup-linux-x64-gnu": "4.24.0", - "@rollup/rollup-linux-x64-musl": "4.24.0", - "@rollup/rollup-win32-arm64-msvc": "4.24.0", - "@rollup/rollup-win32-ia32-msvc": "4.24.0", - "@rollup/rollup-win32-x64-msvc": "4.24.0", + "@napi-rs/lzma-linux-x64-gnu": "1.5.1", + "@rollup/rollup-android-arm-eabi": "4.63.1", + "@rollup/rollup-android-arm64": "4.63.1", + "@rollup/rollup-darwin-arm64": "4.63.1", + "@rollup/rollup-darwin-x64": "4.63.1", + "@rollup/rollup-freebsd-arm64": "4.63.1", + "@rollup/rollup-freebsd-x64": "4.63.1", + "@rollup/rollup-linux-arm-gnueabihf": "4.63.1", + "@rollup/rollup-linux-arm-musleabihf": "4.63.1", + "@rollup/rollup-linux-arm64-gnu": "4.63.1", + "@rollup/rollup-linux-arm64-musl": "4.63.1", + "@rollup/rollup-linux-loong64-gnu": "4.63.1", + "@rollup/rollup-linux-loong64-musl": "4.63.1", + "@rollup/rollup-linux-ppc64-gnu": "4.63.1", + "@rollup/rollup-linux-ppc64-musl": "4.63.1", + "@rollup/rollup-linux-riscv64-gnu": "4.63.1", + "@rollup/rollup-linux-riscv64-musl": "4.63.1", + "@rollup/rollup-linux-s390x-gnu": "4.63.1", + "@rollup/rollup-linux-x64-gnu": "4.63.1", + "@rollup/rollup-linux-x64-musl": "4.63.1", + "@rollup/rollup-openbsd-x64": "4.63.1", + "@rollup/rollup-openharmony-arm64": "4.63.1", + "@rollup/rollup-win32-arm64-msvc": "4.63.1", + "@rollup/rollup-win32-ia32-msvc": "4.63.1", + "@rollup/rollup-win32-x64-gnu": "4.63.1", + "@rollup/rollup-win32-x64-msvc": "4.63.1", "fsevents": "~2.3.2" } }, @@ -7689,6 +7948,12 @@ "node": ">=10" } }, + "node_modules/set-cookie-parser": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.2.tgz", + "integrity": "sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw==", + "license": "MIT" + }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -7777,9 +8042,9 @@ "license": "MIT" }, "node_modules/std-env": { - "version": "3.10.0", - "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.10.0.tgz", - "integrity": "sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-4.2.0.tgz", + "integrity": "sha512-oCUKSupKTHX53EyjDtuZQ64pjLJ6yYCtpmEw0goYxtjG9KpbRe8KAsl2tBUGU9DyMcJ0RwJ8GqJAFzMXcXW1Rw==", "dev": true, "license": "MIT" }, @@ -7919,26 +8184,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/strip-literal": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/strip-literal/-/strip-literal-3.1.0.tgz", - "integrity": "sha512-8r3mkIM/2+PpjHoOtiAW8Rg3jJLHaV7xPwG+YRGrv6FP0wwk/toTpATxWYOW0BKdWwl82VT2tFYi5DlROa0Mxg==", - "dev": true, - "license": "MIT", - "dependencies": { - "js-tokens": "^9.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/antfu" - } - }, - "node_modules/strip-literal/node_modules/js-tokens": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-9.0.1.tgz", - "integrity": "sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==", - "dev": true, - "license": "MIT" - }, "node_modules/style-to-js": { "version": "1.1.21", "resolved": "https://registry.npmjs.org/style-to-js/-/style-to-js-1.1.21.tgz", @@ -8102,11 +8347,14 @@ "license": "MIT" }, "node_modules/tinyexec": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.2.tgz", - "integrity": "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.3.1.tgz", + "integrity": "sha512-GCvB3aoys96IuDFBMcTB46JOR6mdMtAToqwiW8JlWhsoh1mhHi/xn9ss/Dg7N555GiJyEt2qzoG/NHCwM6h1EA==", "dev": true, - "license": "MIT" + "license": "MIT", + "engines": { + "node": ">=18" + } }, "node_modules/tinyglobby": { "version": "0.2.15", @@ -8144,9 +8392,9 @@ } }, "node_modules/tinyglobby/node_modules/picomatch": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", - "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.7.tgz", + "integrity": "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==", "dev": true, "license": "MIT", "engines": { @@ -8156,30 +8404,10 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/tinypool": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.1.1.tgz", - "integrity": "sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.0.0 || >=20.0.0" - } - }, "node_modules/tinyrainbow": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-2.0.0.tgz", - "integrity": "sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/tinyspy": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-4.0.4.tgz", - "integrity": "sha512-azl+t0z7pw/z958Gy9svOTuzqIk6xq+NSheJzn5MMWtWTFywIacg2wUlzKFGtt3cthx0r2SxMK0yzJOR0IES7Q==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-3.1.1.tgz", + "integrity": "sha512-yau8yJdTt989Mm0Bd/236QnzEiPf2xLLTqUZRUJOo/3CB078LSwzei343DgtJVmfJKJE3TMINY1u42SQsP6mXw==", "dev": true, "license": "MIT", "engines": { @@ -8428,9 +8656,9 @@ } }, "node_modules/update-browserslist-db": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", - "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.3.2.tgz", + "integrity": "sha512-UQ+MSxlhRm1bzjhU+DcuXfjFO1FzNtqhK5+9Yvlp90ItDLk5vT932A0rFu619nf7RVS+Y/VeaUW1jaRDqZ8VJw==", "dev": true, "funding": [ { @@ -8601,21 +8829,24 @@ } }, "node_modules/vite": { - "version": "5.4.19", - "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.19.tgz", - "integrity": "sha512-qO3aKv3HoQC8QKiNSTuUM1l9o/XX3+c+VTgLHbJWHZGeTPVAg2XwazI9UWzoxjIJCGCV2zU60uqMzjeLZuULqA==", + "version": "6.4.3", + "resolved": "https://registry.npmjs.org/vite/-/vite-6.4.3.tgz", + "integrity": "sha512-NTKlcQjlAK7MlQoyb6LgaqHc8sso/pVyUJYWMws3jg21uTJw/LddqIFPcPqP6PzpgbIcZyKI85sFE4HBrQDA8A==", "dev": true, "license": "MIT", "dependencies": { - "esbuild": "^0.21.3", - "postcss": "^8.4.43", - "rollup": "^4.20.0" + "esbuild": "^0.25.0", + "fdir": "^6.4.4", + "picomatch": "^4.0.2", + "postcss": "^8.5.3", + "rollup": "^4.34.9", + "tinyglobby": "^0.2.13" }, "bin": { "vite": "bin/vite.js" }, "engines": { - "node": "^18.0.0 || >=20.0.0" + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" }, "funding": { "url": "https://github.com/vitejs/vite?sponsor=1" @@ -8624,19 +8855,25 @@ "fsevents": "~2.3.3" }, "peerDependencies": { - "@types/node": "^18.0.0 || >=20.0.0", + "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", + "jiti": ">=1.21.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", - "terser": "^5.4.0" + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" }, "peerDependenciesMeta": { "@types/node": { "optional": true }, + "jiti": { + "optional": true + }, "less": { "optional": true }, @@ -8657,92 +8894,120 @@ }, "terser": { "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true } } }, - "node_modules/vite-node": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-3.2.4.tgz", - "integrity": "sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg==", + "node_modules/vite/node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", "dev": true, "license": "MIT", - "dependencies": { - "cac": "^6.7.14", - "debug": "^4.4.1", - "es-module-lexer": "^1.7.0", - "pathe": "^2.0.3", - "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" + "engines": { + "node": ">=12.0.0" }, - "bin": { - "vite-node": "vite-node.mjs" + "peerDependencies": { + "picomatch": "^3 || ^4" }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/vite/node_modules/picomatch": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.7.tgz", + "integrity": "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==", + "dev": true, + "license": "MIT", "engines": { - "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + "node": ">=12" }, "funding": { - "url": "https://opencollective.com/vitest" + "url": "https://github.com/sponsors/jonschlinkert" } }, "node_modules/vitest": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-3.2.4.tgz", - "integrity": "sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/chai": "^5.2.2", - "@vitest/expect": "3.2.4", - "@vitest/mocker": "3.2.4", - "@vitest/pretty-format": "^3.2.4", - "@vitest/runner": "3.2.4", - "@vitest/snapshot": "3.2.4", - "@vitest/spy": "3.2.4", - "@vitest/utils": "3.2.4", - "chai": "^5.2.0", - "debug": "^4.4.1", - "expect-type": "^1.2.1", - "magic-string": "^0.30.17", + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.11.tgz", + "integrity": "sha512-fhACrNXUidIbGSBr5FlbuBkO7VWC1ZyLl0DO4CU2DrQoAPxX84Ysxs+HeGQpii5lZWV1Q4gBZTTu49mF+A6Edw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/expect": "4.1.11", + "@vitest/mocker": "4.1.11", + "@vitest/pretty-format": "4.1.11", + "@vitest/runner": "4.1.11", + "@vitest/snapshot": "4.1.11", + "@vitest/spy": "4.1.11", + "@vitest/utils": "4.1.11", + "es-module-lexer": "^2.0.0", + "expect-type": "^1.3.0", + "magic-string": "^0.30.21", + "obug": "^2.1.1", "pathe": "^2.0.3", - "picomatch": "^4.0.2", - "std-env": "^3.9.0", + "picomatch": "^4.0.3", + "std-env": "^4.0.0-rc.1", "tinybench": "^2.9.0", - "tinyexec": "^0.3.2", - "tinyglobby": "^0.2.14", - "tinypool": "^1.1.1", - "tinyrainbow": "^2.0.0", - "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0", - "vite-node": "3.2.4", + "tinyexec": "^1.0.2", + "tinyglobby": "^0.2.15", + "tinyrainbow": "^3.1.0", + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0", "why-is-node-running": "^2.3.0" }, "bin": { "vitest": "vitest.mjs" }, "engines": { - "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + "node": "^20.0.0 || ^22.0.0 || >=24.0.0" }, "funding": { "url": "https://opencollective.com/vitest" }, "peerDependencies": { "@edge-runtime/vm": "*", - "@types/debug": "^4.1.12", - "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", - "@vitest/browser": "3.2.4", - "@vitest/ui": "3.2.4", + "@opentelemetry/api": "^1.9.0", + "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0", + "@vitest/browser-playwright": "4.1.11", + "@vitest/browser-preview": "4.1.11", + "@vitest/browser-webdriverio": "4.1.11", + "@vitest/coverage-istanbul": "4.1.11", + "@vitest/coverage-v8": "4.1.11", + "@vitest/ui": "4.1.11", "happy-dom": "*", - "jsdom": "*" + "jsdom": "*", + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" }, "peerDependenciesMeta": { "@edge-runtime/vm": { "optional": true }, - "@types/debug": { + "@opentelemetry/api": { "optional": true }, "@types/node": { "optional": true }, - "@vitest/browser": { + "@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": { @@ -8753,13 +9018,16 @@ }, "jsdom": { "optional": true + }, + "vite": { + "optional": false } } }, "node_modules/vitest/node_modules/picomatch": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", - "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.7.tgz", + "integrity": "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==", "dev": true, "license": "MIT", "engines": { @@ -8961,9 +9229,9 @@ } }, "node_modules/ws": { - "version": "8.19.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.19.0.tgz", - "integrity": "sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg==", + "version": "8.21.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.21.3.tgz", + "integrity": "sha512-201TZ/kPWxoPr/OKWjquZR1SWKXcvxdH+e1xrx89b3YbmzLMFCLfnaG1HFIgWzJOEWZ7MvpK++odZufgYR50Rw==", "dev": true, "license": "MIT", "engines": { @@ -9000,15 +9268,18 @@ "license": "MIT" }, "node_modules/yaml": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.6.0.tgz", - "integrity": "sha512-a6ae//JvKDEra2kdi1qzCyrJW/WZCgFi8ydDV+eXExl95t+5R+ijnqHJbz9tmMh8FUjx3iv2fCQ4dclAQlO2UQ==", + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.9.0.tgz", + "integrity": "sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA==", "license": "ISC", "bin": { "yaml": "bin.mjs" }, "engines": { - "node": ">= 14" + "node": ">= 14.6" + }, + "funding": { + "url": "https://github.com/sponsors/eemeli" } }, "node_modules/yocto-queue": { diff --git a/package.json b/package.json index 78b9616d..6cf11ff2 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "grimoire", "private": true, - "version": "1.1.0", + "version": "1.2.0", "license": "MIT", "author": "Robert Goniszewski", "repository": { @@ -43,7 +43,9 @@ "package:release": "bun run scripts/package-release.ts", "release:validate": "bun run scripts/release-artifacts-validator.ts", "installer:matrix:linux": "bash scripts/validate-linux-installer-matrix.sh", - "test:e2e:installed": "npm run package:release && bun run scripts/installed-app-smoke.ts", + "test:homebrew": "bash scripts/homebrew-smoke.sh", + "test:homebrew:published": "bash scripts/homebrew-smoke.sh published", + "test:e2e:installed": "bun run scripts/installed-app-smoke.ts", "test:e2e:installed:published": "bun run scripts/installed-app-smoke.ts --source published --require-signature", "docker:build": "docker build -t grimoire .", "docker:run": "docker run -d -p 127.0.0.1:3210:3210 -v grimoire-data:/data --name grimoire grimoire", @@ -99,7 +101,7 @@ "react-hook-form": "^7.61.1", "react-markdown": "^10.1.0", "react-resizable-panels": "^2.1.9", - "react-router-dom": "^6.30.1", + "react-router-dom": "^7.18.3", "recharts": "^2.15.4", "sonner": "^1.7.4", "tailwind-merge": "^2.6.0", @@ -129,7 +131,10 @@ "tailwindcss": "^3.4.17", "typescript": "^5.8.3", "typescript-eslint": "^8.38.0", - "vite": "^5.4.19", - "vitest": "^3.2.4" + "vite": "^6.4.3", + "vitest": "^4.1.11" + }, + "overrides": { + "lodash": "^4.18.0" } } diff --git a/scripts/api-docs-generator.test.ts b/scripts/api-docs-generator.test.ts index 4c22e457..84314836 100644 --- a/scripts/api-docs-generator.test.ts +++ b/scripts/api-docs-generator.test.ts @@ -269,7 +269,7 @@ describe("API documentation generator", () => { expect(markdown).toContain("Content-Type: application/json"); }); - it("documents common local-client error flows without extension or bookmarklet examples", () => { + it("documents common local-client error flows without extension or bookmarklet error examples", () => { const document = buildApiContractDocument(apiContract); const markdown = buildApiMarkdown(document); const problemExamples = document.routes.flatMap((route) => @@ -285,7 +285,6 @@ describe("API documentation generator", () => { ); expect(markdown).toContain('"type": "https://littleimp.app/problems/unprocessable-entity"'); expect(markdown).toContain('WWW-Authenticate: Bearer realm="littleimp-local-integrations"'); - expect(markdown).not.toMatch(/browser extension/i); // The bookmarklet endpoint is documented but must not appear in error-flow example titles expect(problemExamples.map((example) => example.title)).not.toEqual( expect.arrayContaining(["bookmarklet"]) @@ -300,7 +299,6 @@ describe("API documentation generator", () => { expect(markdown).toContain("http://localhost:5173"); expect(markdown).toContain("http://127.0.0.1:3210"); expect(markdown).toContain("Non-loopback origins are ignored"); - expect(markdown).not.toMatch(/browser extension/i); // The bookmarklet endpoint appears in the route table but not in the CORS setup prose expect(markdown).toContain("/capture/bookmarklet"); }); diff --git a/scripts/homebrew-formula.test.ts b/scripts/homebrew-formula.test.ts index a212cfc2..3f7219a1 100644 --- a/scripts/homebrew-formula.test.ts +++ b/scripts/homebrew-formula.test.ts @@ -1,9 +1,12 @@ -import { existsSync, readFileSync } from "node:fs"; +import { spawnSync } from "node:child_process"; +import { mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs"; +import { tmpdir } from "node:os"; import { join } from "node:path"; import { describe, expect, it } from "vitest"; type PackageJson = { version: string; + scripts?: Record; }; const platforms = ["macos", "linux"] as const; @@ -23,68 +26,24 @@ const projectRoot = process.cwd(); const formulaPath = join(projectRoot, "Formula", "grimoire.rb"); const readProjectFile = (path: string) => readFileSync(join(projectRoot, path), "utf8"); const sha256Pattern = /sha256 "([a-f0-9]{64})"/; -const releaseChecksumBaselines: Record = { - "0.1.0-beta": { - macos: "d27e19b85a55a0316e9e2700312e919223c1b4ce88262b74c11bd8e2f3ebaf59", - linux: "a1ffb52c12ed0a292ce58562ed322698b8ed43690e8260bec7dc59ea87ca8098", - }, - // Placeholder checksums — replace with actual SHA-256 values when release artifacts are built and published. - "1.0.0": { - macos: "000000000000000000000000000000000000000000000000000000000000000a", - linux: "000000000000000000000000000000000000000000000000000000000000000b", - }, - "1.0.1": { - macos: "a8c934821cc8db588ef9b3213f013c4cd99f1ae23ba8f18e400727191a9d49c1", - linux: "42cf4ea63bb31ea2380a0b3c8c4c65f7af943974ce1024dd9562a2484e343cff", - }, - "1.1.0": { - macos: "c68bc963602a79631c76df223b9cc4a3709a0d382c0b117288f27c72da96c88f", - linux: "50429c64e2befeca1daca6d44a95d74f755f7be4a16ed869d68a80007809d340", - }, -}; - -function packageVersion(): string { - return (JSON.parse(readProjectFile("package.json")) as PackageJson).version; -} - -function releaseManifest(): ReleaseManifest | null { - const manifestPath = join(projectRoot, "release", "release-manifest.json"); - if (!existsSync(manifestPath)) { - return null; - } - - return JSON.parse(readFileSync(manifestPath, "utf8")) as ReleaseManifest; -} - -function releaseManifestChecksums(version: string): ReleaseChecksumBaseline | null { - const manifest = releaseManifest(); - if (!manifest) { - return null; - } - - expect(manifest.version).toBe(version); - return Object.fromEntries( - platforms.map((platform) => { - const archive = `little-imp-${version}-${platform}.tar.gz`; - const artifact = manifest.artifacts.find( - (entry) => entry.platform === platform && entry.archive === archive - ); - expect(artifact, `Missing ${platform} artifact in release manifest`).toBeDefined(); - return [platform, artifact?.sha256]; - }) - ) as ReleaseChecksumBaseline; +// Formula releases can advance independently of this source branch. Keep the +// reviewed artifact manifest tracked, rather than consulting ignored build output. +function formulaRelease(): ReleaseManifest { + return JSON.parse(readProjectFile("Formula/release.json")) as ReleaseManifest; } function expectedReleaseChecksums(version: string): ReleaseChecksumBaseline { - const baseline = releaseChecksumBaselines[version]; - expect(baseline, `Missing tracked Homebrew checksum baseline for ${version}`).toBeDefined(); - - const manifestChecksums = releaseManifestChecksums(version); - if (manifestChecksums) { - expect(manifestChecksums).toEqual(baseline); - } - - return baseline; + const manifest = formulaRelease(); + expect(manifest.version).toBe(version); + expect(manifest.artifacts).toHaveLength(platforms.length); + return Object.fromEntries(platforms.map((platform) => { + const artifacts = manifest.artifacts.filter((entry) => entry.platform === platform); + expect(artifacts).toHaveLength(1); + const artifact = artifacts[0]; + expect(artifact.archive).toBe(`little-imp-${version}-${platform}.tar.gz`); + expect(artifact.sha256).toMatch(/^[a-f0-9]{64}$/); + return [platform, artifact.sha256]; + })) as ReleaseChecksumBaseline; } function formulaSnippetAfter(formula: string, expectedLine: string): string { @@ -94,8 +53,49 @@ function formulaSnippetAfter(formula: string, expectedLine: string): string { } describe("Homebrew formula packaging", () => { + it("blocks native upgrades before invoking an older runtime and forwards other commands unchanged", () => { + const root = mkdtempSync(join(tmpdir(), "grimoire-formula-")); + try { + const runtime = join(root, "old bun"); + // Stand in for an older archive that ignores LITTLEIMP_PACKAGE_MANAGER. + writeFileSync(runtime, '#!/bin/bash\nprintf "%s\\n" "$LITTLEIMP_PACKAGE_MANAGER" "$@"\n', { mode: 0o755 }); + const formula = readFileSync(formulaPath, "utf8"); + const wrapper = formula.match(/cli_wrapper = <<~EOS\n([\s\S]*?)^\s*EOS/m)?.[1]; + expect(wrapper).toBeDefined(); + const cliPath = join(root, "old release", "daemon", "src", "cli.ts"); + const contents = wrapper! + .replace(/^ {6}/gm, "") + .replaceAll("#{bun}", runtime) + .replaceAll("#{opt_libexec}", join(root, "old release")); + + for (const command of ["grimoire", "littleimp"]) { + const executable = join(root, command); + writeFileSync(executable, contents, { mode: 0o755 }); + for (const action of ["install", "upgrade"]) { + for (const options of [[], ["--archive", "missing.tar.gz", "--checksum", "missing.sha256", "--json"]]) { + const result = spawnSync(executable, ["update", action, ...options], { encoding: "utf8" }); + expect(result.error).toBeUndefined(); + expect(result.status).toBe(2); + expect(result.stderr).toContain("brew upgrade grimoire"); + expect(result.stdout).toBe(""); + } + } + + for (const args of [[], ["--help"], ["update", "check", "--json"], ["backup", "verify", "--file", "path with spaces", ""]]) { + const result = spawnSync(executable, args, { encoding: "utf8" }); + expect(result.error).toBeUndefined(); + expect(result.status).toBe(0); + expect(result.stderr).toBe(""); + expect(result.stdout).toBe(["homebrew", cliPath, ...args, ""].join("\n")); + } + } + } finally { + rmSync(root, { recursive: true, force: true }); + } + }); + it("installs the current release archives by pinned checksum instead of rebuilding from source", () => { - const version = packageVersion(); + const version = formulaRelease().version; const expectedChecksums = expectedReleaseChecksums(version); const formula = readFileSync(formulaPath, "utf8"); @@ -110,24 +110,48 @@ describe("Homebrew formula packaging", () => { expect(sha256Match?.[1]).toBe(expectedChecksums[platform]); } - expect(formula).toContain('depends_on "oven-sh/bun/bun"'); + expect(formula).toContain('depends_on "bun"'); + expect(formula).not.toContain('depends_on "oven-sh/bun/bun"'); expect(formula).toContain('libexec.install "daemon", "dist"'); - expect(formula).toContain('"install", "--production", "--cwd", libexec/"daemon"'); + expect(formula).toContain( + '"install", "--production", "--frozen-lockfile", "--ignore-scripts", "--cwd", libexec/"daemon"' + ); + expect(formula).toContain('formula_opt_bin("bun")'); + expect(formula).toContain("post_install_steps do"); + expect(formula).not.toContain("def post_install"); + expect(formula).toContain('unless_path_exists "little-imp/.env", base: :var'); + expect(formula).toContain('set_permissions "little-imp/.env", "0600", base: :var, recursive: false'); + expect(formula).toContain('LITTLEIMP_PACKAGE_MANAGER="homebrew"'); + expect(formula).toContain('(bin/"grimoire").write'); expect(formula).toContain('(bin/"littleimp").write'); + expect(formula).toContain('(bin/"grimoire").chmod 0555'); + expect(formula).toContain('(bin/"littleimp").chmod 0555'); expect(formula).toContain('(bin/"littleimpd").write'); + expect(formula).toContain('(bin/"littleimpd").chmod 0555'); expect(formula).toContain("service do"); expect(formula).toContain("keep_alive true"); expect(formula).toMatch(/HOST:\s+"127\.0\.0\.1"/); expect(formula).toMatch(/PORT:\s+"3210"/); + expect(formula).toContain("brew services start grimoire"); + expect(formula).toContain("brew upgrade grimoire"); expect(formula).not.toMatch(/git clone|npm run build|bun run build|system ".*daemon\/install\.sh/); }); - it("documents Homebrew as a pending path until live validation passes", () => { + it("documents the published macOS path and keeps unverified platforms explicit", () => { const readme = readProjectFile("README.md"); + const packageJson = JSON.parse(readProjectFile("package.json")) as PackageJson; - expect(readme).toContain("### Homebrew (pending live validation)"); - expect(readme).toContain("not a supported installation path yet"); + expect(readme).toContain("### Homebrew"); + expect(readme).toContain("Linux Homebrew remains unverified"); + expect(readme).toContain("brew trust --formula goniszewski/grimoire/grimoire"); expect(readme).not.toContain("brew install little-imp"); expect(readme).not.toContain("brew services start little-imp"); + + const installGuide = readProjectFile("docs/05-install-without-docker.md"); + expect(installGuide).toContain("brew trust --formula goniszewski/grimoire/grimoire"); + expect(packageJson.scripts?.["test:homebrew"]).toBe("bash scripts/homebrew-smoke.sh"); + expect(packageJson.scripts?.["test:homebrew:published"]).toBe( + "bash scripts/homebrew-smoke.sh published" + ); }); }); diff --git a/scripts/homebrew-smoke.sh b/scripts/homebrew-smoke.sh new file mode 100755 index 00000000..696e5146 --- /dev/null +++ b/scripts/homebrew-smoke.sh @@ -0,0 +1,299 @@ +#!/usr/bin/env bash +set -euo pipefail + +PROJECT_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +MODE="${1:-local}" +TAP_NAME="${HOMEBREW_TEST_TAP_NAME:-goniszewski/grimoire}" +FORMULA_NAME="grimoire" +TEST_PORT="${HOMEBREW_TEST_PORT:-3210}" +UPGRADE_FROM="${HOMEBREW_TEST_UPGRADE_FROM_FORMULA:-}" +ARCHIVE_DIR="${HOMEBREW_TEST_ARCHIVE_DIR:-}" + +if [[ ! "${TEST_PORT}" =~ ^[1-9][0-9]{0,4}$ ]] || (( TEST_PORT > 65535 )); then + printf 'Invalid HOMEBREW_TEST_PORT: %s\n' "${TEST_PORT}" >&2 + exit 2 +fi + +# Keep disposable uninstall from traversing or removing unrelated dependencies. +export HOMEBREW_NO_AUTOREMOVE=1 +export HOMEBREW_NO_AUTO_UPDATE="${HOMEBREW_NO_AUTO_UPDATE:-1}" +export HOMEBREW_NO_ENV_HINTS="${HOMEBREW_NO_ENV_HINTS:-1}" +export HOMEBREW_NO_INSTALL_CLEANUP="${HOMEBREW_NO_INSTALL_CLEANUP:-1}" + +case "${MODE}" in + local|published) + ;; + *) + printf 'Usage: %s [local|published]\n' "${BASH_SOURCE[0]}" >&2 + exit 2 + ;; +esac + +if [[ "${MODE}" == published && ( -n "${UPGRADE_FROM}" || -n "${ARCHIVE_DIR}" ) ]]; then + printf 'Archive caching and upgrade fixtures are local-mode only.\n' >&2 + exit 2 +fi +if [[ -n "${UPGRADE_FROM}" && ! -f "${UPGRADE_FROM}" ]]; then + printf 'Upgrade source formula does not exist: %s\n' "${UPGRADE_FROM}" >&2 + exit 2 +fi + +if ! command -v brew >/dev/null 2>&1; then + printf 'Homebrew smoke test requires brew on PATH.\n' >&2 + exit 1 +fi + +if ! command -v lsof >/dev/null 2>&1; then + printf 'Homebrew smoke test requires lsof for its port safety check.\n' >&2 + exit 1 +fi + +if lsof -nP -iTCP:"${TEST_PORT}" -sTCP:LISTEN >/dev/null 2>&1; then + printf 'Homebrew smoke test requires port %s to be unused.\n' "${TEST_PORT}" >&2 + exit 1 +fi + +# Homebrew 6 requires explicit trust for non-official taps. Keep that test-only +# trust entry out of the developer's normal Homebrew configuration. +homebrew_config_dir="$(mktemp -d)" +export XDG_CONFIG_HOME="${homebrew_config_dir}" +# Keep runtime settings and service overrides out of the user's normal profile. +mkdir -p "${homebrew_config_dir}/homebrew/services" +printf 'PORT=%s\nXDG_CONFIG_HOME=%s\n' "${TEST_PORT}" "${homebrew_config_dir}" > "${homebrew_config_dir}/homebrew/services/grimoire.env" +chmod 600 "${homebrew_config_dir}/homebrew/services/grimoire.env" + +tap_was_present=false +if brew tap | grep -Fxq "${TAP_NAME}"; then + tap_was_present=true +fi + +if brew list --formula "${FORMULA_NAME}" >/dev/null 2>&1; then + printf 'Refusing to replace an existing Homebrew installation: %s\n' "${FORMULA_NAME}" >&2 + rm -rf -- "${homebrew_config_dir}" + exit 1 +fi + +brew_prefix="$(brew --prefix)" +data_dir="${brew_prefix}/var/little-imp" +data_dir_was_present=false +if [[ -e "${data_dir}" || -L "${data_dir}" ]]; then + data_dir_was_present=true +fi + +tap_added=false +formula_installed=false +service_started=false + +cleanup() { + local exit_code=$? + trap - EXIT + + if [[ "${service_started}" == true ]]; then + if ! brew services stop "${FORMULA_NAME}" >/dev/null 2>&1; then + printf 'Cleanup could not stop Grimoire; preserving its data.\n' >&2 + rm -rf -- "${homebrew_config_dir}" + exit 1 + fi + fi + + if [[ "${formula_installed}" == true ]]; then + if ! brew uninstall --force --ignore-dependencies "${FORMULA_NAME}" >/dev/null 2>&1; then + printf 'Cleanup could not uninstall Grimoire; preserving its data and tap.\n' >&2 + exit 1 + fi + fi + + if [[ "${tap_added}" == true ]]; then + brew untap --force "${TAP_NAME}" >/dev/null 2>&1 || true + fi + + if [[ "${data_dir_was_present}" == false && -e "${data_dir}" ]]; then + rm -rf -- "${data_dir}" + fi + + rm -rf -- "${homebrew_config_dir}" + exit "${exit_code}" +} + +trap cleanup EXIT + +if [[ "${data_dir_was_present}" == true ]]; then + printf 'Refusing to use an existing Homebrew data directory: %s\n' "${data_dir}" >&2 + exit 1 +fi + +printf '==> Checking Homebrew formula style\n' +brew style "${PROJECT_ROOT}/Formula/grimoire.rb" + +if [[ "${MODE}" == "local" && "${tap_was_present}" == true ]]; then + printf 'Refusing to reuse existing tap %s for a local smoke test. Untap it first or set HOMEBREW_TEST_TAP_NAME.\n' "${TAP_NAME}" >&2 + exit 1 +fi + +if brew trust --help >/dev/null 2>&1; then + printf '==> Trusting only %s\n' "${TAP_NAME}/${FORMULA_NAME}" + brew trust --formula "${TAP_NAME}/${FORMULA_NAME}" +else + printf '==> Homebrew has no tap-trust command; continuing without explicit trust\n' +fi + +if [[ "${tap_was_present}" == false ]]; then + tap_added=true + if [[ "${MODE}" == "local" ]]; then + tap_source="${HOMEBREW_TAP_URL:-${PROJECT_ROOT}}" + printf '==> Tapping local checkout as %s\n' "${TAP_NAME}" + brew tap "${TAP_NAME}" "${tap_source}" + # git clone omits uncommitted edits; smoke the formula being reviewed. + cp "${PROJECT_ROOT}/Formula/grimoire.rb" "$(brew --repository "${TAP_NAME}")/Formula/grimoire.rb" + elif [[ -n "${HOMEBREW_TAP_URL:-}" ]]; then + printf '==> Tapping %s from %s\n' "${TAP_NAME}" "${HOMEBREW_TAP_URL}" + brew tap "${TAP_NAME}" "${HOMEBREW_TAP_URL}" + else + printf '==> Tapping public Grimoire repository as %s\n' "${TAP_NAME}" + brew tap "${TAP_NAME}" "https://github.com/goniszewski/grimoire.git" + fi +else + printf '==> Using existing tap %s\n' "${TAP_NAME}" +fi + + +tap_formula="$(brew --repository "${TAP_NAME}")/Formula/grimoire.rb" +if [[ -n "${UPGRADE_FROM}" ]]; then + cp "${UPGRADE_FROM}" "${tap_formula}" +fi + +# Pre-publication candidates may be supplied from an authenticated download. +# Homebrew still verifies the formula's SHA-256; public mode always downloads. +cache_archive() { + if [[ -n "${ARCHIVE_DIR}" ]]; then + local cache_path archive_name + cache_path="$(brew --cache --build-from-source "${TAP_NAME}/${FORMULA_NAME}")" + archive_name="${cache_path##*--}" + [[ -f "${ARCHIVE_DIR}/${archive_name}" ]] || exit 1 + mkdir -p "$(dirname "${cache_path}")" + cp "${ARCHIVE_DIR}/${archive_name}" "${cache_path}" + fi +} + +printf '==> Auditing %s\n' "${TAP_NAME}/${FORMULA_NAME}" +if [[ "${MODE}" == published ]]; then + brew audit --formula --strict --online "${TAP_NAME}/${FORMULA_NAME}" + brew fetch --force --build-from-source "${TAP_NAME}/${FORMULA_NAME}" +else + brew audit --formula --strict "${TAP_NAME}/${FORMULA_NAME}" +fi +cache_archive + +printf '==> Installing unqualified formula: brew install %s\n' "${FORMULA_NAME}" +formula_installed=true +brew install "${FORMULA_NAME}" + +formula_prefix="$(brew --prefix "${FORMULA_NAME}")" +[[ -x "${formula_prefix}/bin/grimoire" ]] || exit 1 +[[ -x "${formula_prefix}/bin/littleimp" ]] || exit 1 +[[ -x "${formula_prefix}/bin/littleimpd" ]] || exit 1 + +printf '==> Running Homebrew formula test\n' +brew test "${TAP_NAME}/${FORMULA_NAME}" + +printf '==> Starting Homebrew service\n' +service_started=true +brew services start "${FORMULA_NAME}" + +bun_bin="$(brew --prefix bun)/bin/bun" +expected_version="$(cat "${formula_prefix}/libexec/VERSION")" +if [[ -z "${UPGRADE_FROM}" && -n "${HOMEBREW_TEST_EXPECTED_VERSION:-}" && "${expected_version}" != "${HOMEBREW_TEST_EXPECTED_VERSION}" ]]; then + printf 'Installed version %s does not match requested version %s.\n' "${expected_version}" "${HOMEBREW_TEST_EXPECTED_VERSION}" >&2 + exit 1 +fi + +wait_for_health() { + local health + for _ in $(seq 1 30); do + if health="$(curl --fail --silent --max-time 2 "http://127.0.0.1:${TEST_PORT}/health")" && + printf '%s' "${health}" | "${bun_bin}" -e ' + const body = await Bun.stdin.json(); + process.exit(body.status === "ok" && body.version === process.argv[1] ? 0 : 1); + ' "${expected_version}"; then + return 0 + fi + sleep 1 + done + brew services list || true + cat "${data_dir}/logs/daemon.error.log" 2>/dev/null || true + printf 'Homebrew service did not become healthy at version %s.\n' "${expected_version}" >&2 + return 1 +} + +wait_for_health + +[[ -f "${data_dir}/.env" ]] || exit 1 +[[ -d "${data_dir}/logs" ]] || exit 1 + +printf '==> Stopping Homebrew service\n' +brew services stop "${FORMULA_NAME}" +service_started=false + +# Add durable user state while stopped, without queuing network extraction. +"${bun_bin}" -e ' + const { Database } = await import("bun:sqlite"); + const db = new Database(process.argv[1]); + db.run("INSERT INTO bookmarks (id, url, domain, title, status) VALUES (?, ?, ?, ?, ?)", + ["homebrew-smoke", "https://example.invalid/homebrew-smoke", "example.invalid", "Homebrew preservation fixture", "indexed"]); + db.exec("PRAGMA wal_checkpoint(TRUNCATE)"); + db.close(); +' "${data_dir}/littleimp.db" +printf '\n# Homebrew smoke: preserve custom configuration\n' >> "${data_dir}/.env" + +# Stop before taking the database snapshot so WAL writes cannot race it. +[[ -f "${data_dir}/littleimp.db" ]] || exit 1 +cp "${data_dir}/.env" "${homebrew_config_dir}/saved.env" +cp "${data_dir}/littleimp.db" "${homebrew_config_dir}/saved.db" +if [[ -n "${UPGRADE_FROM}" ]]; then + printf '==> Upgrading and checking data/config preservation\n' + cp "${PROJECT_ROOT}/Formula/grimoire.rb" "${tap_formula}" + cache_archive + brew upgrade "${FORMULA_NAME}" + old_version="${expected_version}" + expected_version="$(cat "${formula_prefix}/libexec/VERSION")" + if [[ "${expected_version}" == "${old_version}" || + ( -n "${HOMEBREW_TEST_EXPECTED_VERSION:-}" && "${expected_version}" != "${HOMEBREW_TEST_EXPECTED_VERSION}" ) ]]; then + printf 'Upgrade did not reach the requested version: %s -> %s.\n' "${old_version}" "${expected_version}" >&2 + exit 1 + fi +else + printf '==> Reinstalling and checking data/config preservation\n' + brew reinstall "${FORMULA_NAME}" +fi +cmp "${homebrew_config_dir}/saved.env" "${data_dir}/.env" +cmp "${homebrew_config_dir}/saved.db" "${data_dir}/littleimp.db" +brew test "${TAP_NAME}/${FORMULA_NAME}" +service_started=true +brew services start "${FORMULA_NAME}" +wait_for_health +brew services stop "${FORMULA_NAME}" +service_started=false +"${bun_bin}" -e ' + const { Database } = await import("bun:sqlite"); + const db = new Database(process.argv[1], { readwrite: true }); + const bookmark = db.query("SELECT title FROM bookmarks WHERE id = ?").get("homebrew-smoke"); + if (bookmark?.title !== "Homebrew preservation fixture") throw new Error("Saved bookmark was lost"); + db.exec("PRAGMA wal_checkpoint(TRUNCATE)"); + db.close(); +' "${data_dir}/littleimp.db" +cmp "${homebrew_config_dir}/saved.env" "${data_dir}/.env" +cp "${data_dir}/littleimp.db" "${homebrew_config_dir}/saved.db" + +# This disposable install cannot have pre-existing dependents. Avoid loading +# unrelated installed formulae from the user's other (untrusted here) taps. +printf '==> Uninstalling and checking data/config preservation\n' +brew uninstall --force --ignore-dependencies "${FORMULA_NAME}" +if brew list --formula "${FORMULA_NAME}" >/dev/null 2>&1; then + printf 'Homebrew uninstall left an installed Grimoire keg.\n' >&2 + exit 1 +fi +formula_installed=false +cmp "${homebrew_config_dir}/saved.env" "${data_dir}/.env" +cmp "${homebrew_config_dir}/saved.db" "${data_dir}/littleimp.db" + +printf 'Homebrew %s smoke test passed for %s.\n' "${MODE}" "${TAP_NAME}/${FORMULA_NAME}" diff --git a/scripts/homebrew-smoke.test.ts b/scripts/homebrew-smoke.test.ts new file mode 100644 index 00000000..c226e101 --- /dev/null +++ b/scripts/homebrew-smoke.test.ts @@ -0,0 +1,107 @@ +import { spawnSync } from "node:child_process"; +import { mkdtempSync, mkdirSync, writeFileSync, rmSync, readFileSync } from "node:fs"; +import { tmpdir } from "node:os"; +import { join } from "node:path"; +import { describe, expect, it } from "vitest"; + +// Exercise orchestration without installing packages or touching host services. +function smoke(fault = "", mode = "local", upgrade = false) { + const root = mkdtempSync(join(tmpdir(), "grimoire-brew-smoke-")); + try { + mkdirSync(join(root, "tap", "Formula"), { recursive: true }); + mkdirSync(join(root, "bin")); + mkdirSync(join(root, "libexec")); + writeFileSync(join(root, "libexec", "VERSION"), "1.2.0\n"); + const olderFormula = join(root, "older.rb"); + writeFileSync(olderFormula, "# older formula fixture\n"); + for (const name of ["grimoire", "littleimp", "littleimpd", "bun"]) { + if (fault === "missing_cli" && name === "grimoire") continue; + writeFileSync(join(root, "bin", name), name === "bun" + ? '#!/bin/bash\n[[ "$2" == *Bun.stdin* ]] || exit 0\nbody="$(cat)"\n[[ "$body" == *"$3"* ]]\n' + : "#!/bin/bash\nexit 0\n", { mode: 0o755 }); + } + const envFile = join(root, "doubles.sh"); + writeFileSync(envFile, ` +lsof() { [[ "$FAULT" == occupied ]]; } +sleep() { :; } +curl() { + if [[ "$FAULT" == version ]]; then printf '{"status":"ok","version":"1.1.0"}'; + else printf '{"status":"ok","version":"%s"}' "$(cat "$ROOT/libexec/VERSION")"; fi +} +brew() { + printf '%s\\n' "$*" >> "$ROOT/calls" + case "$1" in + tap) return 0 ;; + list) + if [[ "$FAULT" == uninstall_noop && -f "$ROOT/installed" ]]; then return 0; fi + return 1 ;; + --prefix) printf '%s\\n' "$ROOT" ;; + --repository) printf '%s/tap\\n' "$ROOT" ;; + install) + touch "$ROOT/installed" + if [[ "$UPGRADE" == true ]]; then printf '1.1.0' > "$ROOT/libexec/VERSION"; fi + mkdir -p "$ROOT/var/little-imp/logs" + printf 'config' > "$ROOT/var/little-imp/.env" + printf 'database' > "$ROOT/var/little-imp/littleimp.db" ;; + reinstall) + if [[ "$FAULT" == reinstall ]]; then printf 'clobbered' > "$ROOT/var/little-imp/.env"; fi ;; + upgrade) + if [[ "$FAULT" != upgrade_noop ]]; then printf '1.2.0' > "$ROOT/libexec/VERSION"; fi ;; + services) + if [[ "$FAULT" == stop && "$2" == stop ]]; then return 1; fi ;; + uninstall) + if [[ "$FAULT" == uninstall ]]; then rm -f "$ROOT/var/little-imp/littleimp.db"; fi + if [[ "$FAULT" == uninstall_failure ]]; then return 1; fi ;; + esac + return 0 +} +`); + const result = spawnSync("/bin/bash", ["scripts/homebrew-smoke.sh", mode], { + encoding: "utf8", timeout: 15000, + env: { ...process.env, BASH_ENV: envFile, ROOT: root, FAULT: fault, HOMEBREW_TEST_EXPECTED_VERSION: "1.2.0", UPGRADE: String(upgrade), HOMEBREW_TEST_UPGRADE_FROM_FORMULA: upgrade ? olderFormula : "", HOMEBREW_TEST_ARCHIVE_DIR: "", HOMEBREW_TEST_PORT: fault === "invalid_port" ? "0" : "13210" }, + }); + expect(result.error).toBeUndefined(); + return { ...result, calls: readFileSync(join(root, "calls"), { encoding: "utf8", flag: "a+" }) }; + } finally { + rmSync(root, { recursive: true, force: true }); + } +} + +describe("Homebrew smoke lifecycle", () => { + it("uses the existing public repository as the tap and audits online", () => { + const result = smoke("", "published"); + expect(result.status).toBe(0); + expect(result.calls).toContain("tap goniszewski/grimoire https://github.com/goniszewski/grimoire.git"); + expect(result.calls.indexOf("trust --formula goniszewski/grimoire/grimoire")).toBeLessThan( + result.calls.indexOf("tap goniszewski/grimoire https://github.com/goniszewski/grimoire.git") + ); + expect(result.calls).toContain("audit --formula --strict --online"); + expect(result.calls).toContain("fetch --force --build-from-source goniszewski/grimoire/grimoire"); + }); + it("invokes brew upgrade and requires the target version", () => { + const result = smoke("", "local", true); + expect(result.status).toBe(0); + expect(result.calls).toContain("upgrade grimoire"); + expect(result.calls).not.toContain("reinstall grimoire"); + }); + it("rejects a no-op upgrade", () => { + const result = smoke("upgrade_noop", "local", true); + expect(result.status, result.stdout + result.stderr + result.calls).not.toBe(0); + expect(result.stdout).not.toContain("smoke test passed"); + }); + it("checks reinstall and uninstall before announcing success", () => { + const result = smoke(); + expect(result.status).toBe(0); + expect(result.stdout).toContain("smoke test passed"); + expect(result.calls).toContain("reinstall grimoire"); + expect(result.calls).toContain("uninstall --force --ignore-dependencies grimoire"); + }); + for (const fault of ["occupied", "version", "reinstall", "uninstall", "uninstall_failure", "stop", "missing_cli", "invalid_port", "uninstall_noop"]) { + it(`fails without reporting success on ${fault}`, () => { + const result = smoke(fault); + expect(result.status).not.toBe(0); + expect(result.stdout).not.toContain("smoke test passed"); + if (fault === "occupied") expect(result.calls).toBe(""); + }); + } +}); diff --git a/scripts/installed-app-smoke.test.ts b/scripts/installed-app-smoke.test.ts index c39479fd..45c73c89 100644 --- a/scripts/installed-app-smoke.test.ts +++ b/scripts/installed-app-smoke.test.ts @@ -38,7 +38,7 @@ function fetchFromReleaseDir(releaseDir: string): typeof fetch { describe("installed-app smoke suite", () => { it("exposes a repeatable installed-artifact smoke command", () => { expect(packageJson.scripts?.["test:e2e:installed"]).toBe( - "npm run package:release && bun run scripts/installed-app-smoke.ts" + "bun run scripts/installed-app-smoke.ts" ); expect(packageJson.scripts?.["test:e2e:installed:published"]).toBe( "bun run scripts/installed-app-smoke.ts --source published --require-signature" @@ -197,9 +197,13 @@ describe("installed-app smoke suite", () => { "GET /settings", "POST /backup", "POST /restore", - "littleimp update check", + "with its native installer", + 'outputDir: join(dirs.rootDir, "release")', + "grimoire CLI and littleimp compatibility alias", + "grimoire update check", "data survives upgrade", "uninstall without purge", + "Native uninstall", "daemon.log", ]) { expect(smokeRunner).toContain(expected); diff --git a/scripts/installed-app-smoke.ts b/scripts/installed-app-smoke.ts index b811b348..7ddf7333 100644 --- a/scripts/installed-app-smoke.ts +++ b/scripts/installed-app-smoke.ts @@ -2,9 +2,7 @@ import { createHash } from "node:crypto"; import { once } from "node:events"; import { - chmodSync, closeSync, - cpSync, existsSync, lstatSync, mkdirSync, @@ -18,9 +16,10 @@ import { } from "node:fs"; import { createServer, type Server } from "node:http"; import { tmpdir } from "node:os"; -import { basename, dirname, isAbsolute, join, resolve } from "node:path"; +import { basename, isAbsolute, join, resolve } from "node:path"; import { fileURLToPath } from "node:url"; import { spawn, spawnSync, type ChildProcess } from "node:child_process"; +import { packageRelease } from "./release-packager"; export type ReleasePlatform = "macos" | "linux"; type SmokeArtifactSource = "local" | "published"; @@ -55,7 +54,6 @@ interface SmokeDirs { homeDir: string; dataDir: string; daemonDir: string; - frontendDir: string; binDir: string; logDir: string; daemonLogPath: string; @@ -209,7 +207,7 @@ function usage(): string { return [ "Usage: bun run scripts/installed-app-smoke.ts [options]", "", - "Validates a packaged Little Imp release archive as an installed app in an isolated temp home.", + "Validates a packaged Grimoire release archive as an installed app in an isolated temp home.", "", "Options:", " --source local|published", @@ -246,7 +244,6 @@ function createSmokeDirs(): SmokeDirs { const homeDir = join(rootDir, "home"); const dataDir = join(homeDir, ".local", "share", "littleimp"); const daemonDir = join(dataDir, "daemon"); - const frontendDir = join(dataDir, "dist"); const binDir = join(homeDir, ".local", "bin"); const logDir = join(dataDir, "logs"); const unpackDir = join(rootDir, "unpacked"); @@ -262,7 +259,6 @@ function createSmokeDirs(): SmokeDirs { homeDir, dataDir, daemonDir, - frontendDir, binDir, logDir, daemonLogPath: join(logDir, "daemon.log"), @@ -283,7 +279,6 @@ function smokeEnv(dirs: SmokeDirs, port: number): NodeJS.ProcessEnv { return { ...process.env, HOME: dirs.homeDir, - PATH: `${dirs.binDir}:${process.env.PATH ?? ""}`, HOST: "127.0.0.1", PORT: String(port), DATA_DIR: dirs.dataDir, @@ -529,26 +524,35 @@ function defaultSignatureRunner( return spawnSync(command, args, options); } -function installRuntimeFromRelease(releaseRoot: string, dirs: SmokeDirs, port: number): void { - logStep("Installing runtime from release archive into isolated temp home"); - rmSync(dirs.daemonDir, { recursive: true, force: true }); - rmSync(dirs.frontendDir, { recursive: true, force: true }); - mkdirSync(dirname(dirs.daemonDir), { recursive: true }); - mkdirSync(dirname(dirs.frontendDir), { recursive: true }); - cpSync(join(releaseRoot, "daemon"), dirs.daemonDir, { recursive: true }); - cpSync(join(releaseRoot, "dist"), dirs.frontendDir, { recursive: true }); - - const cliPath = join(dirs.binDir, "littleimp"); +function createInstallerBashEnv(dirs: SmokeDirs, platform: ReleasePlatform): string { + const bashEnvPath = join(dirs.rootDir, "installer.bashenv"); + const serviceShim = + platform === "macos" + ? "launchctl() { return 0; }" + : [ + "systemctl() {", + ' if [[ "${1:-}" == "--user" && "${2:-}" == "is-active" ]]; then return 1; fi', + " return 0", + "}", + ].join("\n"); + writeFileSync( - cliPath, + bashEnvPath, [ - "#!/usr/bin/env bash", - "set -euo pipefail", - `exec bun ${JSON.stringify(join(dirs.daemonDir, "src", "cli.ts"))} "$@"`, + "curl() { printf '{\"status\":\"ok\"}\\n'; }", + serviceShim, "", ].join("\n") ); - chmodSync(cliPath, 0o755); + return bashEnvPath; +} + +function installRuntimeFromRelease(releaseRoot: string, dirs: SmokeDirs, port: number): void { + logStep("Installing runtime from release archive with its native installer"); + assert(existsSync(join(releaseRoot, "bin")), "Release archive is missing its CLI entrypoint directory"); + for (const command of ["grimoire", "littleimp"]) { + assert(existsSync(join(releaseRoot, "bin", command)), `Release archive is missing its ${command} CLI entrypoint`); + } writeFileSync( join(dirs.dataDir, ".env"), @@ -562,14 +566,18 @@ function installRuntimeFromRelease(releaseRoot: string, dirs: SmokeDirs, port: n ].join("\n") ); - logStep("Installing daemon production dependencies"); - const install = spawnSync("bun", ["install", "--production"], { - cwd: dirs.daemonDir, - env: smokeEnv(dirs, port), + const installEnv = smokeEnv(dirs, port); + const platform = detectReleasePlatform(); + const installer = spawnSync("/bin/bash", [join(releaseRoot, "daemon", "install.sh")], { + cwd: join(releaseRoot, "daemon"), + env: { + ...installEnv, + BASH_ENV: createInstallerBashEnv(dirs, platform), + }, stdio: "inherit", }); - if (install.status !== 0) { - throw new Error(`bun install --production failed with exit code ${install.status ?? "unknown"}`); + if (installer.status !== 0) { + throw new Error(`Release archive installer failed with exit code ${installer.status ?? "unknown"}`); } } @@ -811,10 +819,29 @@ export async function runCommandCapture( }); } +async function runCliHelpChecks(dirs: SmokeDirs, port: number, version: string): Promise { + logStep("grimoire CLI and littleimp compatibility alias"); + for (const command of ["grimoire", "littleimp"]) { + const result = await runCommandCapture(join(dirs.binDir, command), ["--help"], { + cwd: dirs.daemonDir, + env: smokeEnv(dirs, port), + }); + + if (result.timedOut) { + throw new Error(`${command} --help timed out after ${COMMAND_TIMEOUT_MS}ms`); + } + if (result.status !== 0) { + throw new Error(`${command} --help failed:\n${result.stderr}`); + } + assert(result.stdout.includes(version), `${command} --help did not report ${version}`); + assert(result.stdout.includes("Usage:\n grimoire "), `${command} --help did not use the grimoire command name`); + } +} + async function runCliUpdateCheck(dirs: SmokeDirs, port: number, updateSourceUrl: string): Promise { - logStep("littleimp update check"); + logStep("grimoire update check"); const result = await runCommandCapture( - "littleimp", + join(dirs.binDir, "grimoire"), ["update", "check", "--json", "--source", updateSourceUrl], { cwd: dirs.daemonDir, @@ -826,10 +853,10 @@ async function runCliUpdateCheck(dirs: SmokeDirs, port: number, updateSourceUrl: ); if (result.timedOut) { - throw new Error(`littleimp update check timed out after ${COMMAND_TIMEOUT_MS}ms`); + throw new Error(`grimoire update check timed out after ${COMMAND_TIMEOUT_MS}ms`); } if (result.status !== 0) { - throw new Error(`littleimp update check failed:\n${result.stderr}`); + throw new Error(`grimoire update check failed:\n${result.stderr}`); } const payload = JSON.parse(result.stdout) as { current_version?: string; latest?: { version?: string } | null }; @@ -867,10 +894,23 @@ async function runInstalledAppSmoke(options: { requireSignature: options.requireSignature, }) ).archivePath - : resolve(options.archivePath ?? join(options.projectRoot, "release", releaseArchiveName(version, platform))); + : options.archivePath + ? resolve(options.archivePath) + : (() => { + const packaged = packageRelease({ + projectRoot: options.projectRoot, + outputDir: join(dirs.rootDir, "release"), + platforms: [platform], + version, + }); + const artifact = packaged.manifest.artifacts.find((entry) => entry.platform === platform); + assert(artifact, `Packager did not produce a ${platform} release archive`); + return join(packaged.outputDir, artifact.archive); + })(); const releaseRoot = extractReleaseArchive(archivePath, dirs, platform); installRuntimeFromRelease(releaseRoot, dirs, port); + await runCliHelpChecks(dirs, port, version); daemon = startDaemon(dirs, port); await waitForHealth(baseUrl); @@ -899,9 +939,22 @@ async function runInstalledAppSmoke(options: { logStep("uninstall without purge"); await stopDaemon(daemon); daemon = null; - rmSync(dirs.daemonDir, { recursive: true, force: true }); - rmSync(dirs.frontendDir, { recursive: true, force: true }); - rmSync(join(dirs.binDir, "littleimp"), { force: true }); + const uninstallEnv = smokeEnv(dirs, port); + const uninstall = spawnSync("/bin/bash", [join(dirs.daemonDir, "install.sh"), "--uninstall"], { + cwd: dirs.daemonDir, + env: { + ...uninstallEnv, + BASH_ENV: createInstallerBashEnv(dirs, platform), + }, + stdio: "inherit", + }); + if (uninstall.status !== 0) { + throw new Error(`Native uninstall failed with exit code ${uninstall.status ?? "unknown"}`); + } + assert(!existsSync(dirs.daemonDir), "Uninstall without purge left daemon files behind"); + for (const command of ["grimoire", "littleimp"]) { + assert(!existsSync(join(dirs.binDir, command)), `Uninstall without purge left ${command} behind`); + } assert(existsSync(join(dirs.dataDir, "littleimp.db")), "Uninstall without purge removed user data"); success = true; diff --git a/scripts/release-packager.test.ts b/scripts/release-packager.test.ts index 5c874994..855068ba 100644 --- a/scripts/release-packager.test.ts +++ b/scripts/release-packager.test.ts @@ -60,10 +60,13 @@ describe("release packager", () => { expect(readFileSync(join(result.payloadRoot, "daemon/src/db/migrations/0001_initial.sql"), "utf8")).toContain( "SELECT 1" ); + expect(readFileSync(join(result.payloadRoot, "bin/grimoire"), "utf8")).toContain("../daemon/src/cli.ts"); expect(readFileSync(join(result.payloadRoot, "bin/littleimp"), "utf8")).toContain("../daemon/src/cli.ts"); expect(readFileSync(join(result.payloadRoot, "RELEASE.json"), "utf8")).toContain("\"platform\": \"linux\""); + expect(readFileSync(join(result.payloadRoot, "RELEASE.json"), "utf8")).toContain("\"cli\": \"bin/grimoire\""); expect(readFileSync(join(result.payloadRoot, "SIGNING.md"), "utf8")).toContain("detach-sign"); + expect(statSync(join(result.payloadRoot, "bin/grimoire")).mode & 0o111).toBeGreaterThan(0); expect(statSync(join(result.payloadRoot, "bin/littleimp")).mode & 0o111).toBeGreaterThan(0); expect(statSync(join(result.payloadRoot, "daemon/install.sh")).mode & 0o111).toBeGreaterThan(0); diff --git a/scripts/release-packager.ts b/scripts/release-packager.ts index b4941b22..3d56acb6 100644 --- a/scripts/release-packager.ts +++ b/scripts/release-packager.ts @@ -265,19 +265,20 @@ function copyDaemonRuntime(projectRoot: string, payloadRoot: string): void { function writeCliEntrypoint(payloadRoot: string): void { const binDir = join(payloadRoot, "bin"); - const cliPath = join(binDir, "littleimp"); mkdirSync(binDir, { recursive: true }); - writeFileSync( - cliPath, - [ - "#!/usr/bin/env bash", - "set -euo pipefail", - 'SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"', - 'exec bun "${SCRIPT_DIR}/../daemon/src/cli.ts" "$@"', - "", - ].join("\n") - ); - chmodSync(cliPath, 0o755); + const cliContents = [ + "#!/usr/bin/env bash", + "set -euo pipefail", + 'SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"', + 'exec bun "${SCRIPT_DIR}/../daemon/src/cli.ts" "$@"', + "", + ].join("\n"); + + for (const name of ["grimoire", "littleimp"]) { + const cliPath = join(binDir, name); + writeFileSync(cliPath, cliContents); + chmodSync(cliPath, 0o755); + } } function writeVersionMetadata(options: { @@ -302,7 +303,7 @@ function writeVersionMetadata(options: { installer: "daemon/install.sh", daemon: "daemon", frontend: "dist", - cli: "bin/littleimp", + cli: "bin/grimoire", checksums: CHECKSUMS_FILE, }, }, diff --git a/src/components/DaemonOfflineBanner.tsx b/src/components/DaemonOfflineBanner.tsx index ed3a6b1f..a460b887 100644 --- a/src/components/DaemonOfflineBanner.tsx +++ b/src/components/DaemonOfflineBanner.tsx @@ -11,8 +11,7 @@ export function DaemonOfflineBanner({ online, loading }: DaemonOfflineBannerProp
- Daemon offline — changes won't be saved.{" "} - little-imp daemon start to reconnect. + Daemon offline — changes won't be saved. Start the Grimoire daemon with your platform service manager to reconnect.
); diff --git a/src/hooks/use-bookmarks.test.ts b/src/hooks/use-bookmarks.test.ts index 1ac382c2..9ac71ef0 100644 --- a/src/hooks/use-bookmarks.test.ts +++ b/src/hooks/use-bookmarks.test.ts @@ -539,12 +539,31 @@ describe("useBookmarks — sorting", () => { ); }); + it.each(["keyword", "semantic", "hybrid"] as const)("defaults %s search to relevance without changing library sorting", async (mode) => { + const { result } = renderHook(() => useBookmarks(), { wrapper: makeWrapper() }); + act(() => result.current.setSortBy("oldest")); + act(() => result.current.setSearchMode(mode)); + act(() => result.current.setSearchQuery("react")); + await waitFor(() => expect(mockedSearchBookmarks).toHaveBeenCalled(), { timeout: 2000 }); + const params = mockedSearchBookmarks.mock.calls.at(-1)![0]; + expect(params).toMatchObject({ q: "react", mode }); + expect(params).not.toHaveProperty("sort"); + expect(params).not.toHaveProperty("direction"); + expect(result.current.sortBy).toBe("relevance"); + act(() => result.current.setSortBy("title-az")); + await waitFor(() => expect(mockedSearchBookmarks).toHaveBeenLastCalledWith(expect.objectContaining({ sort: "title" }))); + act(() => result.current.setSearchQuery("")); + expect(result.current.sortBy).toBe("oldest"); + act(() => result.current.setSearchQuery("typescript")); + expect(result.current.sortBy).toBe("relevance"); + }); + it("passes sort params to search requests", async () => { const { result } = renderHook(() => useBookmarks(), { wrapper: makeWrapper() }); await waitFor(() => expect(result.current.isLoading).toBe(false), { timeout: 3000 }); - act(() => result.current.setSortBy("last-opened-newest")); act(() => result.current.setSearchQuery("react")); + act(() => result.current.setSortBy("last-opened-newest")); await waitFor(() => expect(mockedSearchBookmarks).toHaveBeenCalledWith(expect.objectContaining({ diff --git a/src/hooks/use-bookmarks.ts b/src/hooks/use-bookmarks.ts index c40b8c69..c661fb33 100644 --- a/src/hooks/use-bookmarks.ts +++ b/src/hooks/use-bookmarks.ts @@ -28,6 +28,7 @@ import { export type SearchMode = "keyword" | "semantic" | "hybrid"; export type SortOption = + | "relevance" | "newest" | "oldest" | "updated-newest" @@ -365,8 +366,10 @@ function toUICategories(categories: ApiCategory[], depth = 0, counts?: Map { +function toLibrarySortParams(sortBy: SortOption): LibrarySortParams { switch (sortBy) { + case "relevance": + return {}; case "oldest": return { sort: "created_at", direction: "asc" }; case "updated-newest": @@ -420,6 +423,7 @@ export function useBookmarks() { const [selectedTagOverride, setSelectedTagOverride] = useState(undefined); const [searchQuery, setSearchQueryState] = useState(""); const [debouncedQuery, setDebouncedQuery] = useState(""); + const [searchSortBy, setSearchSortBy] = useState("relevance"); const [pageOffset, setPageOffset] = useState(0); const { searchMode, @@ -433,9 +437,10 @@ export function useBookmarks() { openActivityFilter, lastOpenedRange, dateRange, - sortBy, + sortBy: librarySortBy, pageSize, } = libraryPreferences; + const sortBy = searchQuery.trim() ? searchSortBy : librarySortBy; const selectedTag = selectedTagOverride !== undefined ? selectedTagOverride : persistedSelectedTag; const dateFrom = dateRange.from?.toISOString().slice(0, 10); @@ -460,6 +465,7 @@ export function useBookmarks() { const setSearchQuery = useCallback((query: string) => { setSearchQueryState(query); + if (!query.trim()) setSearchSortBy("relevance"); resetPage(); }, [resetPage]); @@ -517,9 +523,13 @@ export function useBookmarks() { }, [resetPage, updateLibraryPreferences]); const setSortBy = useCallback((sort: SortOption) => { - updateLibraryPreferences((current) => ({ ...current, sortBy: sort })); + if (searchQuery.trim()) { + setSearchSortBy(sort); + } else if (sort !== "relevance") { + updateLibraryPreferences((current) => ({ ...current, sortBy: sort })); + } resetPage(); - }, [resetPage, updateLibraryPreferences]); + }, [searchQuery, resetPage, updateLibraryPreferences]); const setPageSize = useCallback((nextPageSize: number) => { const safePageSize = LIBRARY_PAGE_SIZE_OPTIONS.includes( @@ -617,8 +627,8 @@ export function useBookmarks() { date_to: dateTo, read_later: readLaterOnly ? true : undefined, ...parityFilterParams, - ...toLibrarySortParams(sortBy), - }), [pageSize, pageOffset, selectedTag, selectedDomain, selectedCategoryId, selectedCategory, dateFrom, dateTo, readLaterOnly, parityFilterParams, sortBy]); + ...toLibrarySortParams(librarySortBy), + }), [pageSize, pageOffset, selectedTag, selectedDomain, selectedCategoryId, selectedCategory, dateFrom, dateTo, readLaterOnly, parityFilterParams, librarySortBy]); const bookmarksQuery = useQuery({ queryKey: bookmarkKeys.list(listParams), @@ -642,8 +652,8 @@ export function useBookmarks() { ...parityFilterParams, limit: pageSize, offset: pageOffset, - ...toLibrarySortParams(sortBy), - }), [debouncedQuery, searchMode, selectedTag, selectedDomain, selectedCategoryId, selectedCategory, dateFrom, dateTo, readLaterOnly, parityFilterParams, pageSize, pageOffset, sortBy]); + ...toLibrarySortParams(searchSortBy), + }), [debouncedQuery, searchMode, selectedTag, selectedDomain, selectedCategoryId, selectedCategory, dateFrom, dateTo, readLaterOnly, parityFilterParams, pageSize, pageOffset, searchSortBy]); const searchQuery_ = useQuery({ queryKey: bookmarkKeys.search(debouncedQuery, searchParams), @@ -973,6 +983,7 @@ export function useBookmarks() { }, [updateBookmarkMutation]); const resetLibraryPreferences = useCallback(() => { + setSearchSortBy("relevance"); clearLibraryViewPreferences(); setLibraryPreferences(DEFAULT_LIBRARY_VIEW_PREFERENCES); resetPage(); diff --git a/src/pages/Index.tsx b/src/pages/Index.tsx index b07ec715..e543cac3 100644 --- a/src/pages/Index.tsx +++ b/src/pages/Index.tsx @@ -581,6 +581,7 @@ const Index = () => { {showButtonLabels && } + {store.searchQuery.trim() && Relevance} Newest first Oldest first Updated newest diff --git a/src/pages/Settings.test.tsx b/src/pages/Settings.test.tsx index 30ea90b3..90afe563 100644 --- a/src/pages/Settings.test.tsx +++ b/src/pages/Settings.test.tsx @@ -17,6 +17,7 @@ vi.mock("@/lib/api", () => ({ getReprocessStatus: vi.fn(), getDiagnostics: vi.fn(), getSettings: vi.fn(), + testAiConnection: vi.fn(), reprocessBookmarks: vi.fn(), updateSettings: vi.fn(), checkHealthAfterRestore: vi.fn(), @@ -51,12 +52,14 @@ import * as api from "@/lib/api"; import * as backupHooks from "@/hooks/use-backup"; const mockedGetSettings = api.getSettings as unknown as ReturnType; +const mockedTestAiConnection = api.testAiConnection as unknown as ReturnType; const mockedFetchAiModels = (api as unknown as { fetchAiModels: ReturnType }).fetchAiModels; const mockedCheckForUpdates = (api as unknown as { checkForUpdates: ReturnType }).checkForUpdates; const mockedCheckHealthAfterRestore = (api as unknown as { checkHealthAfterRestore: ReturnType }).checkHealthAfterRestore; const mockedGetDiagnostics = (api as unknown as { getDiagnostics: ReturnType }).getDiagnostics; const mockedReprocessBookmarks = (api as unknown as { reprocessBookmarks: ReturnType }).reprocessBookmarks; const mockedGetReprocessStatus = (api as unknown as { getReprocessStatus: ReturnType }).getReprocessStatus; +const mockedListIntegrationTokens = (api as unknown as { listIntegrationTokens: ReturnType }).listIntegrationTokens; const mockedUseBackupList = backupHooks.useBackupList as unknown as ReturnType; const mockedUseCreateBackup = backupHooks.useCreateBackup as unknown as ReturnType; const mockedUseRestoreBackup = backupHooks.useRestoreBackup as unknown as ReturnType; @@ -168,6 +171,7 @@ beforeEach(() => { vi.clearAllMocks(); window.localStorage.clear(); mockedGetSettings.mockResolvedValue(settingsResponse()); + mockedTestAiConnection.mockResolvedValue({ ok: true }); mockedFetchAiModels.mockResolvedValue([]); mockedGetDiagnostics.mockResolvedValue({ data: { @@ -230,6 +234,7 @@ beforeEach(() => { failed: 0, }, }); + mockedListIntegrationTokens.mockResolvedValue({ data: [] }); mockedUseBackupList.mockReturnValue({ data: [ { @@ -263,6 +268,20 @@ beforeEach(() => { mockedUseRestoreEncryptedBackupPackage.mockReturnValue({ mutate: vi.fn(), isPending: false }); }); +describe("Browser Integration guidance", () => { + it("explains that integration tokens authenticate every supported local client", async () => { + render(, { wrapper: makeWrapper() }); + + expect(await screen.findByText("Browser Integration")).toBeInTheDocument(); + expect(screen.getByText(/official browser extension, bookmarklets, MCP clients/)).toBeInTheDocument(); + expect(screen.getByText(/Create one for the official browser extension, a bookmarklet, an MCP client/)).toBeInTheDocument(); + + fireEvent.click(screen.getByRole("button", { name: "Setup guide" })); + expect(screen.getByText("Using integration tokens")).toBeInTheDocument(); + expect(screen.getByText(/paste the token into its connection screen/)).toBeInTheDocument(); + }); +}); + describe("Settings update checks", () => { it("renders when the daemon returns an older settings shape", async () => { const legacySettings = settingsResponse(); @@ -611,6 +630,58 @@ describe("Settings diagnostics", () => { }); describe("Settings AI providers", () => { + it("enables retry with a newly selected model after a failed connection test", async () => { + const updateSettings = api.updateSettings as unknown as ReturnType; + updateSettings.mockResolvedValue(settingsResponse()); + mockedGetSettings.mockResolvedValue(settingsResponse({ + provider: "openrouter" as const, + openrouter: { + api_key: "***", + base_url: "https://openrouter.ai/api/v1", + model: "openai/gpt-latest", + }, + })); + mockedFetchAiModels.mockResolvedValue([ + { id: "openai/gpt-latest", name: "OpenAI: GPT (latest)" }, + { id: "openai/gpt-5.2", name: "OpenAI: GPT-5.2" }, + ]); + mockedTestAiConnection + .mockResolvedValueOnce({ ok: false, error: "Connection failed" }) + .mockResolvedValueOnce({ ok: true }); + + render(, { wrapper: makeWrapper() }); + + const modelCombobox = await screen.findByRole("combobox", { name: "Model" }); + const initialTestButton = screen + .getAllByRole("button", { name: "Test connection" }) + .find((button) => !button.hasAttribute("disabled")); + expect(initialTestButton).toBeDefined(); + fireEvent.click(initialTestButton!); + expect(await screen.findByText("Connection failed")).toBeInTheDocument(); + + fireEvent.click(modelCombobox); + fireEvent.click(screen.getByRole("option", { name: /OpenAI: GPT-5\.2/ })); + + const testButton = screen.getByTitle("Save changes and test the current configuration"); + expect(testButton).toBeEnabled(); + expect(screen.queryByText("Connection failed")).not.toBeInTheDocument(); + fireEvent.click(testButton); + + await waitFor(() => { + expect(updateSettings).toHaveBeenCalledWith( + expect.objectContaining({ + ai: expect.objectContaining({ + openrouter: expect.objectContaining({ model: "openai/gpt-5.2" }), + }), + }) + ); + expect(mockedTestAiConnection).toHaveBeenCalledTimes(2); + }); + expect(updateSettings.mock.invocationCallOrder[0]).toBeLessThan( + mockedTestAiConnection.mock.invocationCallOrder[1] + ); + }); + it("renders provider-specific Anthropic fields from settings", async () => { mockedGetSettings.mockResolvedValue(settingsResponse({ provider: "anthropic" as const, diff --git a/src/pages/Settings.tsx b/src/pages/Settings.tsx index 123611fd..d5fa5237 100644 --- a/src/pages/Settings.tsx +++ b/src/pages/Settings.tsx @@ -708,35 +708,35 @@ const LocalSettings = () => { [openRouterModelsQuery.data] ); - const saveMutation = useMutation({ - mutationFn: () => { - // Build the patch. For API keys that are still showing the redacted - // sentinel (user didn't touch them), omit the field entirely so the - // daemon's deepMerge leaves the real stored key unchanged. - const openaiPatch: { api_key?: string; model: string } = { model: ai.openai.model }; - if (!isKeyRedacted(ai.openai.api_key)) { - openaiPatch.api_key = ai.openai.api_key; - } + const buildAiSettingsPatch = () => { + // Build the patch. For API keys that are still showing the redacted + // sentinel (user didn't touch them), omit the field entirely so the + // daemon's deepMerge leaves the real stored key unchanged. + const openaiPatch: { api_key?: string; model: string } = { model: ai.openai.model }; + if (!isKeyRedacted(ai.openai.api_key)) { + openaiPatch.api_key = ai.openai.api_key; + } - const patch = { - ai: { - provider: ai.provider, - openai: openaiPatch, - ollama: { base_url: ai.ollama.base_url, model: ai.ollama.model }, - anthropic: secretProviderPatch(ai.anthropic), - openrouter: secretProviderPatch(ai.openrouter), - openai_compatible: secretProviderPatch(ai.openai_compatible), - deepseek: secretProviderPatch(ai.deepseek), - embeddings: { - provider: embeddings.provider, - model: embeddings.model, - openai_compatible: secretProviderPatch(embeddings.openai_compatible), - }, + return { + ai: { + provider: ai.provider, + openai: openaiPatch, + ollama: { base_url: ai.ollama.base_url, model: ai.ollama.model }, + anthropic: secretProviderPatch(ai.anthropic), + openrouter: secretProviderPatch(ai.openrouter), + openai_compatible: secretProviderPatch(ai.openai_compatible), + deepseek: secretProviderPatch(ai.deepseek), + embeddings: { + provider: embeddings.provider, + model: embeddings.model, + openai_compatible: secretProviderPatch(embeddings.openai_compatible), }, - }; + }, + }; + }; - return updateSettings(patch); - }, + const saveMutation = useMutation({ + mutationFn: () => updateSettings(buildAiSettingsPatch()), onSuccess: () => { setDirty(false); setTestResult(null); @@ -751,12 +751,15 @@ const LocalSettings = () => { }, }); - // Test connection uses the currently persisted settings on the daemon. - // The button is disabled while dirty to prevent misleading results. + // Test connection uses persisted daemon settings, so save any edited form + // values first to ensure the selected provider and model are the ones tested. const handleTestConnection = async () => { setTesting(true); setTestResult(null); try { + if (dirty) { + await saveMutation.mutateAsync(); + } const json = await testAiConnection(); setTestResult(json); } catch (err) { @@ -775,11 +778,13 @@ const LocalSettings = () => { function updateAiOpenai(patch: Partial) { setAi((prev) => ({ ...prev, openai: { ...prev.openai, ...patch } })); + setTestResult(null); setDirty(true); } function updateAiOllama(patch: Partial) { setAi((prev) => ({ ...prev, ollama: { ...prev.ollama, ...patch } })); + setTestResult(null); setDirty(true); } @@ -788,6 +793,7 @@ const LocalSettings = () => { ...prev, [provider]: { ...prev[provider], ...patch }, })); + setTestResult(null); setDirty(true); } @@ -810,7 +816,7 @@ const LocalSettings = () => { } const canSave = dirty && !isLoading && !isError && !saveMutation.isPending; - const canTest = ai.provider !== "none" && !dirty && !testing; + const canTest = ai.provider !== "none" && !testing && !saveMutation.isPending; function handleVerifyBackup(name: string) { setVerifyingBackupName(name); @@ -1148,7 +1154,7 @@ const LocalSettings = () => { size="sm" onClick={handleTestConnection} disabled={!canTest} - title={dirty ? "Save your changes first to test the current configuration" : undefined} + title={dirty ? "Save changes and test the current configuration" : undefined} > {testing && } Test connection @@ -2284,7 +2290,8 @@ function BrowserIntegration() {

Browser Integration

- Create an integration token and install the bookmarklet to save pages from any browser. + Integration tokens authenticate the official browser extension, bookmarklets, MCP clients, + and other supported local integrations. Create a separate token for each client so you can revoke access independently.

- Then{" "} + Paste the token into the official browser extension or another supported integration. For a bookmarklet, {" "}