diff --git a/connectors/shopify/.env.example b/connectors/shopify/.env.example new file mode 100644 index 0000000..60f81b2 --- /dev/null +++ b/connectors/shopify/.env.example @@ -0,0 +1,11 @@ +SHOPIFY_SHOP=drinkvaritea.myshopify.com +SHOPIFY_ADMIN_TOKEN=shpat_xxx +SHOPIFY_API_VERSION=2025-07 +SHOPIFY_WEBHOOK_SECRET=xxx +SHOPLOOP_MCP_TOKEN=xxx +TEMPORAL_ADDRESS=localhost:7233 +TEMPORAL_NAMESPACE=shoploop +OPEN_IMAGINE_ROUTER_URL=https://router.open-imagine.local +BRAND_RULES_PATH=/etc/shoploop/brand_rules.json +USD_OUT_DIR=/var/shoploop/twins +SHOPLOOP_MCP_PORT=8787 diff --git a/connectors/shopify/.gitignore b/connectors/shopify/.gitignore new file mode 100644 index 0000000..8bca619 --- /dev/null +++ b/connectors/shopify/.gitignore @@ -0,0 +1,5 @@ +node_modules +dist +*.tsbuildinfo +.env +.env.local diff --git a/connectors/shopify/DESIGN.md b/connectors/shopify/DESIGN.md new file mode 100644 index 0000000..34638ad --- /dev/null +++ b/connectors/shopify/DESIGN.md @@ -0,0 +1,63 @@ +# DESIGN — connectors/shopify + +Companion to the root [`design.md`](../../design.md) and [`AGENTS.md`](../../AGENTS.md). This +doc covers only what is specific to the Shopify → USD connector. + +## Position in the architecture loop + +``` +Shopify Catalog → [THIS] Product Ingest (→ product_twin.usda) +→ [THIS] Scene Composer hook (brand_rules.json + USD stage) +→ open-imagine Render Pipeline (Flux Pro default) ← contract boundary +→ Creative Pack → Distribution → Performance Ingest → brand_rules.json (loop closes) +``` + +The moat is the **USD scene graph + brand-constraint enforcement**, not image generation. +This connector owns the left half: turning a commerce catalog into a constrained scene graph +**before any pixels exist**. Render is `open-imagine`'s job. + +## Why MCP-shaped, not a REST wrapper + +A REST wrapper is commodity (decision gate #2 → cut). The first-class surface is the value: +products are addressable as USD primitives that an agent (Claude/Cursor, Open Employee, +Agentframe) can **read, inspect, and manipulate** — `inspect_product_twin` returns a prim tree ++ variant sets + a textual summary; `manipulate_variant_set` edits the stage. Same shape as +`daslabhq/openusd-mcp`, commerce-typed. + +## Single internal core + +`client.ts` (Admin GraphQL, 2025-07) + `usd/*` (emit / inspect / manipulate) are the only +place catalog→USD logic lives. The CLI, the MCP tools, and the Temporal activities are thin +adapters over that core — a product update via webhook and a manual `ingest` produce byte-for-byte +identical `.usda` (modulo the `commerce:generated_at` line). Tests assert this against the golden. + +## Brand-constraint layer (Primitive 3) + +`composeScene` emits a composed stage that sublayers `[brand_rules, product_twin, scene_preset]` +and clamps preset params (depth-of-field, copy density) into the brand's allowed band. Defaults +are the Kinfolk profile: soft window/overcast light, matte/raw materials, sparse copy. + +### Kinfolk test + +> **If a composed scene wouldn't fit a Kinfolk spread, `brand_rules` failed.** + +This is the gate. `rulesApplied` always includes `kinfolk_gate`; the brand-rules sublayer pins +palette, allowed materials, allowed lighting rigs, the DoF band, and the copy-density cap. A +scene that violates these never reaches the renderer. + +## Idempotency + +Workflow id = `shopify::::` with +`WorkflowIdReusePolicy=REJECT_DUPLICATE`. Duplicate webhook deliveries (Shopify retries) collapse +to one execution per product revision. + +## Contracts (referenced, not vendored) + +- `open-imagine/contracts/product_twin.v1.json` — the `.usda` we emit validates against this. +- `open-imagine/contracts/render_job.v1.json` — the body `RecomposeCreativePackWorkflow` POSTs + to `OPEN_IMAGINE_ROUTER_URL`. + +## Cost discipline (decision gate #5) + +CPU-only. ~$13/mo at Varitea dogfood scale (1× t4g.small co-locating MCP + worker, Temporal Cloud +dev tier, S3 for `.usda`). No GPU here — that cost lives in `open-imagine`. Never Vercel. diff --git a/connectors/shopify/README.md b/connectors/shopify/README.md new file mode 100644 index 0000000..beca68c --- /dev/null +++ b/connectors/shopify/README.md @@ -0,0 +1,102 @@ +# @shoploop/connector-shopify + +First-class **Shopify → `product_twin.usda` → Scene Composer** primitives. Not a thin REST +wrapper. Modeled on [`daslabhq/openusd-mcp`](https://github.com/daslabhq/openusd-mcp) (read / +inspect / manipulate over MCP) and Griptape Nodes' agent-callable USD pattern. + +Shopify products become OpenUSD primitives the [`open-imagine`](../../../open-imagine) render +path consumes. This connector is **CPU-only** — it emits `.usda` text; it does **not** render. + +> Contracts referenced (not vendored): `open-imagine/contracts/product_twin.v1.json`, +> `open-imagine/contracts/render_job.v1.json`. + +## The four primitives + +| # | Primitive | Entry | +|---|-----------|-------| +| 1 | **Catalog ingest** → `.usda` + `_manifest.json` | `ingestCatalog()` / `shoploop-shopify ingest` | +| 2 | **MCP** read/inspect/manipulate (7 tools, HTTP/SSE) | `shoploop-shopify mcp` → `:8787/sse` | +| 3 | **Scene composition** (brand-rule enforcement at USD layer) | `composeScene()` | +| 4 | **Webhook → Temporal** (4 workflows, idempotent) | `POST /webhooks/shopify` + `shoploop-shopify worker` | + +All four call the **same internal core** (`client.ts`, `usd/*`). Importable from +`@shoploop/connector-shopify`, callable from MCP, triggerable from webhooks. No duplication. + +## Quickstart — Varitea dogfood path + +```bash +cd connectors/shopify +pnpm install # or: npm install +cp .env.example .env # fill in SHOPIFY_ADMIN_TOKEN at minimum +``` + +```bash +# .env essentials +SHOPIFY_SHOP=drinkvaritea.myshopify.com +SHOPIFY_ADMIN_TOKEN=shpat_... +SHOPIFY_API_VERSION=2025-07 +BRAND_RULES_PATH=/etc/shoploop/brand_rules.json +USD_OUT_DIR=/var/shoploop/twins +``` + +### 1. Ingest the catalog + +```bash +pnpm shoploop-shopify ingest --shop drinkvaritea.myshopify.com --out ./twins +# incremental: +pnpm shoploop-shopify ingest --shop drinkvaritea.myshopify.com --since 2026-06-01T00:00:00Z --out ./twins +``` + +**Expected output:** one `.usda` per product plus `twins/_manifest.json` +(`shopify_id ↔ usda_path`). For `varitea-loose-leaf-original` you get a `def Xform` root prim, +a `variantSet "size"` with `_50g / _100g / _250g`, a placeholder `def Mesh` (no Model3d media on +that SKU), a `PreviewMat` bound from the product image, and commerce/brand attrs +(`commerce:price`, `commerce:sku`, `commerce:inventory`, `commerce:vendor`, `brand:tags`, +`brand:rules_ref`). See `test/fixtures/varitea_product_twin.usda` for the golden shape. + +### 2. Boot the MCP server (read/inspect/manipulate) + +```bash +pnpm shoploop-shopify mcp # Express + SSE on :8787 (/sse, /messages, /webhooks/shopify) +``` + +Bearer auth via `SHOPLOOP_MCP_TOKEN` (unset ⇒ open, dev only). CORS allowlist for Open Employee ++ Agentframe origins. Tools: `list_products`, `get_product`, `get_variants`, `get_metafields`, +`update_metafield`, `inspect_product_twin`, `manipulate_variant_set`. `inspect_product_twin` +also returns a textual prim summary for LLM consumption. + +### 3. Run the Temporal worker + +```bash +pnpm shoploop-shopify worker # task queue: shoploop-shopify +``` + +Webhook topics → workflows (workflow id `shopify::::`): + +| Topic | Workflow(s) | +|-------|-------------| +| `products/create` | `ReingestProductWorkflow` | +| `products/update` | `ReingestProductWorkflow` + `RecomposeCreativePackWorkflow` | +| `products/delete` | `PurgeProductTwinWorkflow` | +| `inventory_levels/update` | `UpdateCommerceAttrWorkflow` (patches `commerce:inventory`, no re-render) | + +## Develop + +```bash +pnpm typecheck # tsc --noEmit (src + test) +pnpm test # vitest (primitives, mcp, webhook) +pnpm build # emit dist/ (CLI bin → dist/cli.js) +``` + +## Cost (dogfood scale) + +Varitea ~30 SKUs, ~100 webhook events/day: **~$13/mo** — 1× t4g.small (MCP + worker co-located) +≈ $12, Temporal Cloud dev tier $0, S3 for `.usda` (~50 MB) <$1. At 100 brands ≈ $60/mo +(Fargate + Temporal starter). + +## Boundaries + +- No render/image code → `open-imagine`. +- No Polaris/Remix embed app → `shoploop-shopify`. +- No ad-buying / agency replacement → `shoploop-growth`. +- AWS-only for the GPU-adjacent path. This connector is CPU-only. diff --git a/connectors/shopify/bin/cli.js b/connectors/shopify/bin/cli.js new file mode 100644 index 0000000..8fb1272 --- /dev/null +++ b/connectors/shopify/bin/cli.js @@ -0,0 +1,2 @@ +#!/usr/bin/env node +import "../dist/cli.js"; diff --git a/connectors/shopify/package-lock.json b/connectors/shopify/package-lock.json new file mode 100644 index 0000000..7bbb8ff --- /dev/null +++ b/connectors/shopify/package-lock.json @@ -0,0 +1,5769 @@ +{ + "name": "@shoploop/connector-shopify", + "version": "0.1.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "@shoploop/connector-shopify", + "version": "0.1.0", + "license": "UNLICENSED", + "dependencies": { + "@modelcontextprotocol/sdk": "^1.18.0", + "@shopify/admin-api-client": "^1.1.0", + "@temporalio/activity": "^1.11.0", + "@temporalio/client": "^1.11.0", + "@temporalio/worker": "^1.11.0", + "@temporalio/workflow": "^1.11.0", + "express": "^4.21.0", + "zod": "^3.23.0" + }, + "bin": { + "shoploop-shopify": "bin/cli.js" + }, + "devDependencies": { + "@types/express": "^4.17.21", + "@types/node": "^20.14.0", + "tsx": "^4.19.0", + "typescript": "^5.6.0", + "vitest": "^2.1.0" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.1.tgz", + "integrity": "sha512-Svl7tq8k/08+p6CXPpRjQ1fKX+1odH/BQbb48fV6fj3CWHhsoIOoY87w1oHXm0qEpkIK3ZfVgp0hed3XBXzXMQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.28.1.tgz", + "integrity": "sha512-0k2F129Xdio1TdJfzJ8sy1Q47vUD2NnwdhiAf7drUN1EBTfPf4hsFCtmMgu/6m8JSzsBrlmVjudMBQqOfG8usQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.28.1.tgz", + "integrity": "sha512-34EGEbCIAgosYz6goLcopX6Mo7NyGv9tfwEM2/7Ce2VcVRk568iSvniGWcUXIy7wEDR1wzolcxcriFVrWYcwBg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.28.1.tgz", + "integrity": "sha512-dbwY7ltSMDWsRatcRpCnES4F+im88OCUgGZjy52shC7GqHRE/cYlxNbB4Z4UpJswpcc4Qxd2oE/ufM0p61IKng==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.28.1.tgz", + "integrity": "sha512-TZbWkQY7kvTAXbXUT7uVACR5cMHsDiSz9z7ZKAX/RTq/WJEk3QyRr0wZpNhBDX+/0CtdqUIJlOiodQcta6tY3Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.28.1.tgz", + "integrity": "sha512-zfdzgK9ACBNZLI/CyHTOx81SyNbM6YXn7rxSgX97VjyiPl9W1i4Ka4fgKECEoFCKGpvBj5qArWIGgQjOwkgskQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.1.tgz", + "integrity": "sha512-wG2EA8ENdEI0qhkSZMjfqrdY+ziCYCPMmtZjjIwOmXFjmyzEHn+UUxk5of+SYsjtfs3VpnlC7QLzSI5hY/rOAw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.28.1.tgz", + "integrity": "sha512-i7dZ9vQgnvSCzi/rYCXNgtF/U+eKZNJBzu3eTQbRgHnM7tNSizLOkRFAl3qzVc/Op/u5YkHHa4pf/3DOYHthLQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.28.1.tgz", + "integrity": "sha512-qVXBOHQS+d5Y722GwJzJUtOLlX7km3CraOaGormF1pDtPd2C/l1SHRPgjLunLGe51Sh5YYWKMFDyV4SxgMQYTQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.28.1.tgz", + "integrity": "sha512-yHs+0uc8+nvEAfAfxrWQKK5peSNzBc4PegcMO0EJ2hT71uA7vB8Ihg2e77R2P7SG5uYjPbHlLLmve4LLLRCf0g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.28.1.tgz", + "integrity": "sha512-d1z4ZuP0ajrfz/FhGT4vv278rX8KnPPJx8i5+AtK7TYbx9Le9F1hyzurZpkEyjkGa9dUGhQow4C1NmeGvqxN2w==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.28.1.tgz", + "integrity": "sha512-M5sRjUVZrkm1OAPR3dlOYzNmN+loZKGVi1VUQGrwuqLcbR6qeAz+famMhjASeH3YVKvZz+zT1jlh/keC3Rj/lg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.28.1.tgz", + "integrity": "sha512-mRObBZeHh2OxcBFPWE/FjylkRgZdYuiTR3vaTozquCGOH14iP9oN4x4Ge81CoIDYQrXmIxpFumJBu5MtZpnQJQ==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.28.1.tgz", + "integrity": "sha512-slScBsMAb3GFDcdrCgLwZtPYRoH2H/youv10QiZyRjmsP48fznoveWytSgCI/R0ZcUgpc0ZhIUEx6LHts8yrfQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.28.1.tgz", + "integrity": "sha512-kw0owk1o0GFETUJyW0jc0G4Yzs0BHZn0JDZ8JRT088vjJYX777BAs1fDGxAC+q831qOs2DTC96mNsG2opdfyyQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.28.1.tgz", + "integrity": "sha512-/lAIjX8aYFRByhh6L5rYtPEDRqa9de/4V/juOXcta5frjvzXO4/sqEtyytse0g3zZFuWu5cDN0MkLz2qRDD2Ag==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.28.1.tgz", + "integrity": "sha512-u/anNYF2mmVOEDwLtnQ1wOr3EZ9sTNGLWrsYGYwHWzGA3Si84IOkHXlbWTD1NB+9/1lcnweYKO54uhxZydNzfA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.1.tgz", + "integrity": "sha512-oks0DYbLwWMmaakTsCb+zL4E+aHRVLom9IJZOAthMQEPiQmydXHkziYEsGYRx0uNV/IjEKGAV941JzH02pflqw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.28.1.tgz", + "integrity": "sha512-aeL6lAnN89Hz43Mlh1G8ARasbuoYvSITDEx0tHh5b7jJnHcssqgjy9Yx430GDpmCa6OyrKoS0aNRjKundRizGg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.1.tgz", + "integrity": "sha512-MEFJe5C3R8pwXdZ5Y21oo6m7ePiS0d9pWucn99O/wvyJZChoIQKrQDxKrGeW8F5+T0okTHesAmDeiHDTIq0V/Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.28.1.tgz", + "integrity": "sha512-i/ZLIOafE0Z8cI/XANJAixoJL/uRAoS2xOA3rb0xN+KK0K177cMAsQYkzHtBrtMXAKuAc7HGgcWiZ/sRC1Nxgw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.1.tgz", + "integrity": "sha512-ge+Z7EXFNt2BO1oAMsVpiQ8EwndV9i1xXerAeTIK7AtPs3bKFXQM7nlRxDSIUIMeueR1CNXxqztLzdNeReKBJg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.28.1.tgz", + "integrity": "sha512-BEjgtECkL3vY+SaSQ6nzVfiALUeFxpawyp8Jmf5PtYhf1Ug40N1h/hxlhts+f1FvSvarEigdxS3BlSMI2PJLcQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.28.1.tgz", + "integrity": "sha512-lCv9eK/H6ZJWbE7bh2nw54CZ9M2nupBxJcTsdk/QQnWkdSjKGuxmmH8/GWrlT1eMmZfn4dGcCjRte397WqfQXA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.28.1.tgz", + "integrity": "sha512-zvb/mB2bSCoJOpoCBgYKKpX6YM6mJBlBUVUtVj41DlZJVEB6/0CKlRYxP5wWl1C1ILiCoAU5wZZ4q1P3qeS6Eg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.28.1.tgz", + "integrity": "sha512-bm4Mowrv+GXMlpWX++EcXw/iLyd1o3+bJkC2DkWXYVvgZCqD/bSj9ctZeAMC3cIxgjRVR2Dufaiu4YPxr5gW1A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@grpc/grpc-js": { + "version": "1.14.4", + "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.14.4.tgz", + "integrity": "sha512-k9Dj3DV/itK9D06Y8f190Qgop7/Ui+D0njFV3LHMPwPT75DpXLQohE9Wmz0QElrJnzsjB7KPWiKJbOl7IPDArQ==", + "license": "Apache-2.0", + "dependencies": { + "@grpc/proto-loader": "^0.8.0", + "@js-sdsl/ordered-map": "^4.4.2" + }, + "engines": { + "node": ">=12.10.0" + } + }, + "node_modules/@grpc/proto-loader": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.8.1.tgz", + "integrity": "sha512-wtF6h+DY6M3YaDBPAmvuuA6jV8Sif9MjtOI5euKFWRgCDl5PeDpPsHR9u2l6St5ceY8AZgoNDww5+HvEsXFsGg==", + "license": "Apache-2.0", + "dependencies": { + "lodash.camelcase": "^4.3.0", + "long": "^5.0.0", + "protobufjs": "^7.5.5", + "yargs": "^17.7.2" + }, + "bin": { + "proto-loader-gen-types": "build/bin/proto-loader-gen-types.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@hono/node-server": { + "version": "1.19.14", + "resolved": "https://registry.npmjs.org/@hono/node-server/-/node-server-1.19.14.tgz", + "integrity": "sha512-GwtvgtXxnWsucXvbQXkRgqksiH2Qed37H9xHZocE5sA3N8O8O8/8FA3uclQXxXVzc9XBZuEOMK7+r02FmSpHtw==", + "license": "MIT", + "engines": { + "node": ">=18.14.1" + }, + "peerDependencies": { + "hono": "^4" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/source-map": { + "version": "0.3.11", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.11.tgz", + "integrity": "sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA==", + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@js-sdsl/ordered-map": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/@js-sdsl/ordered-map/-/ordered-map-4.4.2.tgz", + "integrity": "sha512-iUKgm52T8HOE/makSxjqoWhe95ZJA1/G1sYsGev2JDKUSS14KAgg1LHb+Ba+IPow0xflbnSkOsZcO08C7w1gYw==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/js-sdsl" + } + }, + "node_modules/@jsonjoy.com/base64": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/base64/-/base64-1.1.2.tgz", + "integrity": "sha512-q6XAnWQDIMA3+FTiOYajoYqySkO+JSat0ytXGSuRdq9uXE7o92gzuQwQM14xaCRlBLGq3v5miDGC4vkVTn54xA==", + "license": "Apache-2.0", + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/@jsonjoy.com/buffers": { + "version": "17.67.0", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/buffers/-/buffers-17.67.0.tgz", + "integrity": "sha512-tfExRpYxBvi32vPs9ZHaTjSP4fHAfzSmcahOfNxtvGHcyJel+aibkPlGeBB+7AoC6hL7lXIE++8okecBxx7lcw==", + "license": "Apache-2.0", + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/@jsonjoy.com/codegen": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/codegen/-/codegen-1.0.0.tgz", + "integrity": "sha512-E8Oy+08cmCf0EK/NMxpaJZmOxPqM+6iSe2S4nlSBrPZOORoDJILxtbSUEDKQyTamm/BVAhIGllOBNU79/dwf0g==", + "license": "Apache-2.0", + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/@jsonjoy.com/fs-core": { + "version": "4.57.8", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-core/-/fs-core-4.57.8.tgz", + "integrity": "sha512-YzVbwggV9452VCeHgo0bjsTaUt1O7JE0XpEsPar93nn/+RAwXk0mb1Y+f5EDJ3TRtRCFe+Ck5RuojdfB4jeHVw==", + "license": "Apache-2.0", + "dependencies": { + "@jsonjoy.com/fs-node-builtins": "4.57.8", + "@jsonjoy.com/fs-node-utils": "4.57.8", + "thingies": "^2.5.0" + }, + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/@jsonjoy.com/fs-fsa": { + "version": "4.57.8", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-fsa/-/fs-fsa-4.57.8.tgz", + "integrity": "sha512-vmClyvCQMxgqz7uamDiGtRfp4MjzOznk3pcQjCxlIwJcw7TWeyr+bF30hI0x8NxdtNOGMg1pHM74VDIXOeyjuw==", + "license": "Apache-2.0", + "dependencies": { + "@jsonjoy.com/fs-core": "4.57.8", + "@jsonjoy.com/fs-node-builtins": "4.57.8", + "@jsonjoy.com/fs-node-utils": "4.57.8", + "thingies": "^2.5.0" + }, + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/@jsonjoy.com/fs-node": { + "version": "4.57.8", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-node/-/fs-node-4.57.8.tgz", + "integrity": "sha512-IPEOlDYSnTDYpjQlQg2F8h+eqxKQN3sdbroI0WrteRiQZ462HzVpBo9ZZX485njz4nAacoe3fd4iDiIhk+k5Hg==", + "license": "Apache-2.0", + "dependencies": { + "@jsonjoy.com/fs-core": "4.57.8", + "@jsonjoy.com/fs-node-builtins": "4.57.8", + "@jsonjoy.com/fs-node-utils": "4.57.8", + "@jsonjoy.com/fs-print": "4.57.8", + "@jsonjoy.com/fs-snapshot": "4.57.8", + "glob-to-regex.js": "^1.0.0", + "thingies": "^2.5.0" + }, + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/@jsonjoy.com/fs-node-builtins": { + "version": "4.57.8", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-node-builtins/-/fs-node-builtins-4.57.8.tgz", + "integrity": "sha512-mxXSXw8zZwRVakcjLqR2I/psy4gURFSASZS10kKJ2kJw05GC2nXGroGrWVHxwgkxXgQLsFQnB74QaLzsxzdL/w==", + "license": "Apache-2.0", + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/@jsonjoy.com/fs-node-to-fsa": { + "version": "4.57.8", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-node-to-fsa/-/fs-node-to-fsa-4.57.8.tgz", + "integrity": "sha512-AWZcT/4+H+iDl4XCukbXrarvwEgOrf/prFI5/7eg4ix9FxqVsZysIDJd1Kjd+AjlCeHKHJOaRqjLd5HiGSCJEw==", + "license": "Apache-2.0", + "dependencies": { + "@jsonjoy.com/fs-fsa": "4.57.8", + "@jsonjoy.com/fs-node-builtins": "4.57.8", + "@jsonjoy.com/fs-node-utils": "4.57.8" + }, + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/@jsonjoy.com/fs-node-utils": { + "version": "4.57.8", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-node-utils/-/fs-node-utils-4.57.8.tgz", + "integrity": "sha512-E/bJ7sQAb4pu9nbeJhbULU3WnqWrswte4N9Js/oHt7aHB746S8/XBqKlcbrqIgnD3095XluovNEZuu5ONT230g==", + "license": "Apache-2.0", + "dependencies": { + "@jsonjoy.com/fs-node-builtins": "4.57.8" + }, + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/@jsonjoy.com/fs-print": { + "version": "4.57.8", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-print/-/fs-print-4.57.8.tgz", + "integrity": "sha512-DfzhOBpmvNu5P/KSe4NNQaOnvNliTdcf0qrh/4EReErF/XUQXYkd0vZl/OiJCm/qjEEo8DWRstliw2/JNS84dA==", + "license": "Apache-2.0", + "dependencies": { + "@jsonjoy.com/fs-node-utils": "4.57.8", + "tree-dump": "^1.1.0" + }, + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/@jsonjoy.com/fs-snapshot": { + "version": "4.57.8", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-snapshot/-/fs-snapshot-4.57.8.tgz", + "integrity": "sha512-L+eqKaWOHLDaiMv1dh/EWQ4hA+o6xAhWSumTo3Teg7OM18jU/KE13/e8Mfal+eAZ/pSl4wIhKHcDiwapJzC8Wg==", + "license": "Apache-2.0", + "dependencies": { + "@jsonjoy.com/buffers": "^17.65.0", + "@jsonjoy.com/fs-node-utils": "4.57.8", + "@jsonjoy.com/json-pack": "^17.65.0", + "@jsonjoy.com/util": "^17.65.0" + }, + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/@jsonjoy.com/fs-snapshot/node_modules/@jsonjoy.com/base64": { + "version": "17.67.0", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/base64/-/base64-17.67.0.tgz", + "integrity": "sha512-5SEsJGsm15aP8TQGkDfJvz9axgPwAEm98S5DxOuYe8e1EbfajcDmgeXXzccEjh+mLnjqEKrkBdjHWS5vFNwDdw==", + "license": "Apache-2.0", + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/@jsonjoy.com/fs-snapshot/node_modules/@jsonjoy.com/codegen": { + "version": "17.67.0", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/codegen/-/codegen-17.67.0.tgz", + "integrity": "sha512-idnkUplROpdBOV0HMcwhsCUS5TRUi9poagdGs70A6S4ux9+/aPuKbh8+UYRTLYQHtXvAdNfQWXDqZEx5k4Dj2Q==", + "license": "Apache-2.0", + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/@jsonjoy.com/fs-snapshot/node_modules/@jsonjoy.com/json-pack": { + "version": "17.67.0", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/json-pack/-/json-pack-17.67.0.tgz", + "integrity": "sha512-t0ejURcGaZsn1ClbJ/3kFqSOjlryd92eQY465IYrezsXmPcfHPE/av4twRSxf6WE+TkZgLY+71vCZbiIiFKA/w==", + "license": "Apache-2.0", + "dependencies": { + "@jsonjoy.com/base64": "17.67.0", + "@jsonjoy.com/buffers": "17.67.0", + "@jsonjoy.com/codegen": "17.67.0", + "@jsonjoy.com/json-pointer": "17.67.0", + "@jsonjoy.com/util": "17.67.0", + "hyperdyperid": "^1.2.0", + "thingies": "^2.5.0", + "tree-dump": "^1.1.0" + }, + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/@jsonjoy.com/fs-snapshot/node_modules/@jsonjoy.com/json-pointer": { + "version": "17.67.0", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/json-pointer/-/json-pointer-17.67.0.tgz", + "integrity": "sha512-+iqOFInH+QZGmSuaybBUNdh7yvNrXvqR+h3wjXm0N/3JK1EyyFAeGJvqnmQL61d1ARLlk/wJdFKSL+LHJ1eaUA==", + "license": "Apache-2.0", + "dependencies": { + "@jsonjoy.com/util": "17.67.0" + }, + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/@jsonjoy.com/fs-snapshot/node_modules/@jsonjoy.com/util": { + "version": "17.67.0", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/util/-/util-17.67.0.tgz", + "integrity": "sha512-6+8xBaz1rLSohlGh68D1pdw3AwDi9xydm8QNlAFkvnavCJYSze+pxoW2VKP8p308jtlMRLs5NTHfPlZLd4w7ew==", + "license": "Apache-2.0", + "dependencies": { + "@jsonjoy.com/buffers": "17.67.0", + "@jsonjoy.com/codegen": "17.67.0" + }, + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/@jsonjoy.com/json-pack": { + "version": "1.21.0", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/json-pack/-/json-pack-1.21.0.tgz", + "integrity": "sha512-+AKG+R2cfZMShzrF2uQw34v3zbeDYUqnQ+jg7ORic3BGtfw9p/+N6RJbq/kkV8JmYZaINknaEQ2m0/f693ZPpg==", + "license": "Apache-2.0", + "dependencies": { + "@jsonjoy.com/base64": "^1.1.2", + "@jsonjoy.com/buffers": "^1.2.0", + "@jsonjoy.com/codegen": "^1.0.0", + "@jsonjoy.com/json-pointer": "^1.0.2", + "@jsonjoy.com/util": "^1.9.0", + "hyperdyperid": "^1.2.0", + "thingies": "^2.5.0", + "tree-dump": "^1.1.0" + }, + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/@jsonjoy.com/json-pack/node_modules/@jsonjoy.com/buffers": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/buffers/-/buffers-1.2.1.tgz", + "integrity": "sha512-12cdlDwX4RUM3QxmUbVJWqZ/mrK6dFQH4Zxq6+r1YXKXYBNgZXndx2qbCJwh3+WWkCSn67IjnlG3XYTvmvYtgA==", + "license": "Apache-2.0", + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/@jsonjoy.com/json-pointer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/json-pointer/-/json-pointer-1.0.2.tgz", + "integrity": "sha512-Fsn6wM2zlDzY1U+v4Nc8bo3bVqgfNTGcn6dMgs6FjrEnt4ZCe60o6ByKRjOGlI2gow0aE/Q41QOigdTqkyK5fg==", + "license": "Apache-2.0", + "dependencies": { + "@jsonjoy.com/codegen": "^1.0.0", + "@jsonjoy.com/util": "^1.9.0" + }, + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/@jsonjoy.com/util": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/util/-/util-1.9.0.tgz", + "integrity": "sha512-pLuQo+VPRnN8hfPqUTLTHk126wuYdXVxE6aDmjSeV4NCAgyxWbiOIeNJVtID3h1Vzpoi9m4jXezf73I6LgabgQ==", + "license": "Apache-2.0", + "dependencies": { + "@jsonjoy.com/buffers": "^1.0.0", + "@jsonjoy.com/codegen": "^1.0.0" + }, + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/@jsonjoy.com/util/node_modules/@jsonjoy.com/buffers": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/buffers/-/buffers-1.2.1.tgz", + "integrity": "sha512-12cdlDwX4RUM3QxmUbVJWqZ/mrK6dFQH4Zxq6+r1YXKXYBNgZXndx2qbCJwh3+WWkCSn67IjnlG3XYTvmvYtgA==", + "license": "Apache-2.0", + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/@modelcontextprotocol/sdk": { + "version": "1.29.0", + "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.29.0.tgz", + "integrity": "sha512-zo37mZA9hJWpULgkRpowewez1y6ML5GsXJPY8FI0tBBCd77HEvza4jDqRKOXgHNn867PVGCyTdzqpz0izu5ZjQ==", + "license": "MIT", + "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" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@cfworker/json-schema": "^4.1.1", + "zod": "^3.25 || ^4.0" + }, + "peerDependenciesMeta": { + "@cfworker/json-schema": { + "optional": true + }, + "zod": { + "optional": false + } + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/accepts": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", + "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", + "license": "MIT", + "dependencies": { + "mime-types": "^3.0.0", + "negotiator": "^1.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/body-parser": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.3.0.tgz", + "integrity": "sha512-2cGmJupaNgg+QUwVLAucDuWuoMZ6EX9iHDRswZ5lsNYEmwPaRknMPCLZz07yTzVq/83p4o/wzbDZbBrTvGGTIw==", + "license": "MIT", + "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" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/body-parser/node_modules/content-type": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-2.0.0.tgz", + "integrity": "sha512-j/O/d7GcZCyNl7/hwZAb606rzqkyvaDctLmckbxLzHvFBzTJHuGEdodATcP3yIRoDrLHkIATJuvzbFlp/ki2cQ==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/content-disposition": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.1.0.tgz", + "integrity": "sha512-5jRCH9Z/+DRP7rkvY83B+yGIGX96OYdJmzngqnw2SBSxqCFPd0w2km3s5iawpGX8krnwSGmF0FW5Nhr0Hfai3g==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/cookie-signature": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", + "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", + "license": "MIT", + "engines": { + "node": ">=6.6.0" + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/express": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz", + "integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==", + "license": "MIT", + "dependencies": { + "accepts": "^2.0.0", + "body-parser": "^2.2.1", + "content-disposition": "^1.0.0", + "content-type": "^1.0.5", + "cookie": "^0.7.1", + "cookie-signature": "^1.2.1", + "debug": "^4.4.0", + "depd": "^2.0.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "finalhandler": "^2.1.0", + "fresh": "^2.0.0", + "http-errors": "^2.0.0", + "merge-descriptors": "^2.0.0", + "mime-types": "^3.0.0", + "on-finished": "^2.4.1", + "once": "^1.4.0", + "parseurl": "^1.3.3", + "proxy-addr": "^2.0.7", + "qs": "^6.14.0", + "range-parser": "^1.2.1", + "router": "^2.2.0", + "send": "^1.1.0", + "serve-static": "^2.2.0", + "statuses": "^2.0.1", + "type-is": "^2.0.1", + "vary": "^1.1.2" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/finalhandler": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.1.tgz", + "integrity": "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==", + "license": "MIT", + "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" + }, + "engines": { + "node": ">= 18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/fresh": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", + "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/iconv-lite": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.2.tgz", + "integrity": "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/media-typer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", + "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/merge-descriptors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", + "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/mime-db": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/mime-types": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz", + "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", + "license": "MIT", + "dependencies": { + "mime-db": "^1.54.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/negotiator": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", + "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/send": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/send/-/send-1.2.1.tgz", + "integrity": "sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.3", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "fresh": "^2.0.0", + "http-errors": "^2.0.1", + "mime-types": "^3.0.2", + "ms": "^2.1.3", + "on-finished": "^2.4.1", + "range-parser": "^1.2.1", + "statuses": "^2.0.2" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/serve-static": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.1.tgz", + "integrity": "sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==", + "license": "MIT", + "dependencies": { + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "parseurl": "^1.3.3", + "send": "^1.2.0" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/type-is": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.1.0.tgz", + "integrity": "sha512-faYHw0anBbc/kWF3zFTEnxSFOAGUX9GFbOBthvDdLsIlEoWOFOtS0zgCiQYwIskL9iGXZL3kAXD8OoZ4GmMATA==", + "license": "MIT", + "dependencies": { + "content-type": "^2.0.0", + "media-typer": "^1.1.0", + "mime-types": "^3.0.0" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/type-is/node_modules/content-type": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-2.0.0.tgz", + "integrity": "sha512-j/O/d7GcZCyNl7/hwZAb606rzqkyvaDctLmckbxLzHvFBzTJHuGEdodATcP3yIRoDrLHkIATJuvzbFlp/ki2cQ==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/@protobufjs/aspromise": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", + "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/base64": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", + "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/codegen": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.5.tgz", + "integrity": "sha512-zgXFLzW3Ap33e6d0Wlj4MGIm6Ce8O89n/apUaGNB/jx+hw+ruWEp7EwGUshdLKVRCxZW12fp9r40E1mQrf/34g==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/eventemitter": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.1.tgz", + "integrity": "sha512-vW1GmwMZNnL+gMRaovlh9yZX74kc+TTU3FObkkurpMaRtBfLP3ldjS9KQWlwZgraRE0+dheEEoAxdzcJQ8eXZg==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/fetch": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.1.tgz", + "integrity": "sha512-GpptLrs57adMSuHi3VNj0mAF8dwh36LMaYF6XyJ6JMWlVsc+t42tm1HSEDmOs3A8fC9yyeisgLhsTVQokOZ0zw==", + "license": "BSD-3-Clause", + "dependencies": { + "@protobufjs/aspromise": "^1.1.1" + } + }, + "node_modules/@protobufjs/float": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", + "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/inquire": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.2.tgz", + "integrity": "sha512-pa0vFRuws4wkvaXKK1uXZMAwAX4/t8ANaJo45iw/oQHNQ9q5xUzwgFmVJGXiga2BeN+zpX7Vf9vmsiIa2J+MUw==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/path": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", + "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/pool": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", + "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/utf8": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.1.tgz", + "integrity": "sha512-oOAWABowe8EAbMyWKM0tYDKi8Yaox52D+HWZhAIJqQXbqe0xI/GV7FhLWqlEKreMkfDjshR5FKgi3mnle0h6Eg==", + "license": "BSD-3-Clause" + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.62.2.tgz", + "integrity": "sha512-6o7ZLZK+BeenkZCFNDXqpbjw9bD6nuWonvS/lwQJp7NoVVxm6p3qE7qQ5jGuBjiFsgvqjD8mZAU5oWxTmbOeOg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.62.2.tgz", + "integrity": "sha512-BaH7BllCACHoH1LguOU56UItGfUWjujlO65kS9LAodViaN4bwIKd7oeW/ZHJ/4ljr/7MIiENnNy3HJ0zXv8Zkw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.62.2.tgz", + "integrity": "sha512-v39RCCvj4He82I9sFmk+M1VZ0PLM9sfsLVikjfx2hYBNALhrrOR2D3JjQA6AhlaSOgcR+RzrKY7e1+bT6SUO/A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.62.2.tgz", + "integrity": "sha512-yl0y2vq3S3lHeuXhEdss6TWfKW8vkujImO12tn4ZkG/4oghr09LvdYm2RElVjokTQiUvDUGXLGsYeLqUMCKpGA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.62.2.tgz", + "integrity": "sha512-tT4pvt4qXD+vEoezupCWi+a1F0vvDiksiHc+PxRlYTOH1I6/X4id9jPxTP+Fg+545euaFT1jJVs4CEdHZAU1vw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.62.2.tgz", + "integrity": "sha512-6nU5F2wCW+qvCBhTn1pdIU3bzsIoF7EUwsCDRxilWGprQR6yd508YnH9+OKFCwpfS8pjZqDUmnCAr7exax0XCg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.62.2.tgz", + "integrity": "sha512-n1GJHPOvpIfhi3TmrCeh6S6URt9BFCt0KQE3qvexyGCTAKpR4Lg+eWvNZEqu7epxwus/8ElT3hacYEucm49SZg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.62.2.tgz", + "integrity": "sha512-JqgflS8wEB+UXV/vS1RpRbifGBeN4D5lz8D8oOFbFZw4vedvdOgCFAjfBmIMdW3yL10XpQQ0Ambepw6MXrhOnA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.62.2.tgz", + "integrity": "sha512-wnFJkogWvN4jm/hQRF2UBaeUmk20j5+DmHvoyWii2b8HJDyvz1MF2OU/6ynXt2KR63rbZLWkFpoytpdc/yBuSA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.62.2.tgz", + "integrity": "sha512-HVu2bp0zhvJ8xHEV9+UUs7S90VadmBSY3LcIMvozbPo4AuMGDWlz3ymHLHZPX4hR67TKTt8Qp5PJ5RBg/i+RMQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.62.2.tgz", + "integrity": "sha512-mQqqAV8QaoSgr9I2fKDLY2BAVvmKjWoGiu/cSYQonsLvtqwEn1E4QYfnCOcp5zoEqNhsDYin1s6jx/VJmrxlZg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.62.2.tgz", + "integrity": "sha512-IxKLoxCQ2IWi6bT2akyDUBGsOImDKB+sPp4EsTmwFQ/fMwpCKm8uLSSgP/Kx/QYUgKis6SEZ5/Nlhup0DIA0PQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.62.2.tgz", + "integrity": "sha512-Mk5ha2RQSgyFfmYYLkBpPnUk8D8FriBxesO1u9O75X0mHgXL1UQcH5Itl2lurWL2tj0RxV9b9tJgipac0hRY9A==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.62.2.tgz", + "integrity": "sha512-CjvEnqJL/0/TQ3TXX3OPIJ/kmBellrWd4heXUmHeJlTnmwjKpSJzoehLaL6Xk0ZnMHBu9dZuFADNOrtjF4v+2w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.62.2.tgz", + "integrity": "sha512-1SiZbzwdkaDURsew/tSOrooKiYy7EQGT6m8ufavAi9NEyQb/6VuIxFXAL1fqa4iZe3g4NbNk4P7J32z2tw5Mgg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.62.2.tgz", + "integrity": "sha512-nQts12zJ3NQRoE6uYljOH89v7szzLDvG2JD/vsX+vGXU8w/At1GowTZ5/7qeFQ8m7L55rpR8Okugnuo5bgjy2Q==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.62.2.tgz", + "integrity": "sha512-E9/ll019jhPIJgpzfZoIkBGhcz+kKNgVWYRY0zr9srBdPPFVpvOKW8VaJKUbeK+eZXyQF9ltME+Kk6affeaPgg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.62.2.tgz", + "integrity": "sha512-5BqxR/pshjey51iliyzTD5Xi3EN0aLmQ2lZ3lvefVV9c82BvrLo2/6OT55iifpWBufs6kdwWbuOKS841DrmK9A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.62.2.tgz", + "integrity": "sha512-uNN83XxQrRAh/w0/pmAfibcwyb6YWt4gP+dpnQKPVJshAloQ785ii8CT8ZCIxkGg9opVsvAlGhFitSm6D1Jjpg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.62.2.tgz", + "integrity": "sha512-srjEIxSH3LRnJN6THczDHWQplqEMFiAJrTab0msUryh9kwNpkICf3Ea6q6MN/2cZwRFUNx5w+h6Hpi4QuHS6Zg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.62.2.tgz", + "integrity": "sha512-8hOJnxgbyObnCm5AlRA3A931xX19xq80RjVTKgJOvEKWqJruP/Uf12IbAOaDjjEXYRewwHLfmF0YRIdK3OwKWA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.62.2.tgz", + "integrity": "sha512-mmF4AY1i0hG/bLWUctUq59gtmgaSIRa3cu/A3JFRp/sCNEme2bgDEiDS22P9FbnJB8NJNF4jPJiSP5RHQpUTDg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.62.2.tgz", + "integrity": "sha512-DZgkknc6jhHrk46V25vbAM0zZkyP0nSDkJB8/dRkLTxv470dOmWDqGoEJl/9A0dFfS7yE3REOwNDxpHwSLSt0Q==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.62.2.tgz", + "integrity": "sha512-T6xr6ucWSFto+VGajA8YH26LdpHRuP4YLHEKAtCWvJDOlnmWcDZVCI2Jmjr+IFHDlt2zRaTAKE4tfjTaWLgJBg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.62.2.tgz", + "integrity": "sha512-BfzEnDJOt9T8M989/lA37EcJgat01wLRnoi5dQf3QzOH7jzpqTAzdDbVfRljVr5r+jzKqpbHeyOfAaXxAd0PAA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@shopify/admin-api-client": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@shopify/admin-api-client/-/admin-api-client-1.1.2.tgz", + "integrity": "sha512-sV5gS8x4kj0KLULlOJkoBzm+tu6tn+1PCK9a6inySv5qtzmf/JxDWtLgaW9vRXeXHo/Sl/p3mBWhY3//fKA8iw==", + "license": "MIT", + "dependencies": { + "@shopify/graphql-client": "^1.4.2" + } + }, + "node_modules/@shopify/graphql-client": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/@shopify/graphql-client/-/graphql-client-1.4.2.tgz", + "integrity": "sha512-C/fEyx6BZmTGeJv0HSsduhfn5ZbkXk0nePkyo0+xXxorP1I+uvx9pobdFxTfiPnOrh+P6QiKzBgIviksnSo4YA==", + "license": "MIT" + }, + "node_modules/@swc/core": { + "version": "1.15.43", + "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.15.43.tgz", + "integrity": "sha512-1CuKjFkPxIgGdeHVuNbkxmBxkcbdc08u0aiI43pFq6yY1tTVKmXT9hFEooyyKs/sJ3xf1GPHyEwTtk9Xl8dvQw==", + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "@swc/counter": "^0.1.3", + "@swc/types": "^0.1.27" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/swc" + }, + "optionalDependencies": { + "@swc/core-darwin-arm64": "1.15.43", + "@swc/core-darwin-x64": "1.15.43", + "@swc/core-linux-arm-gnueabihf": "1.15.43", + "@swc/core-linux-arm64-gnu": "1.15.43", + "@swc/core-linux-arm64-musl": "1.15.43", + "@swc/core-linux-ppc64-gnu": "1.15.43", + "@swc/core-linux-s390x-gnu": "1.15.43", + "@swc/core-linux-x64-gnu": "1.15.43", + "@swc/core-linux-x64-musl": "1.15.43", + "@swc/core-win32-arm64-msvc": "1.15.43", + "@swc/core-win32-ia32-msvc": "1.15.43", + "@swc/core-win32-x64-msvc": "1.15.43" + }, + "peerDependencies": { + "@swc/helpers": ">=0.5.17" + }, + "peerDependenciesMeta": { + "@swc/helpers": { + "optional": true + } + } + }, + "node_modules/@swc/core-darwin-arm64": { + "version": "1.15.43", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.15.43.tgz", + "integrity": "sha512-v1aVuvXdo/BHxJzco9V2xpHrvwWmhfS8t6gziY5wJxd+Z2h8AeJRnAwPD8itCDaGXVBwJ/CaKfxEzTkG0Va0OA==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-darwin-x64": { + "version": "1.15.43", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.15.43.tgz", + "integrity": "sha512-lp3d4Lamc8dt5huYdGLSR+9hLxmfr1jb0l+4XXG2zPqZwYWRN9R0U2qYoTrggiU2RWW0oV9VbWM3kBnqIc2kdQ==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-arm-gnueabihf": { + "version": "1.15.43", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.15.43.tgz", + "integrity": "sha512-JWTQQELtsG5GgphDrr/XqqmM2pDN3cZqbMS0Mrg+iTiXL3F74sn/S2IyYE/5u4h2KLkTf9qQ7dXyxsbx7YzkeA==", + "cpu": [ + "arm" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-arm64-gnu": { + "version": "1.15.43", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.15.43.tgz", + "integrity": "sha512-B4otJRdPWIsmiSBf0uG7Z/+vMWmkufjz5MmYxubwKuZazDW14Zd3symga1N62QR4RT+kEFeHEgsXfZGyn/w0hw==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-arm64-musl": { + "version": "1.15.43", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.15.43.tgz", + "integrity": "sha512-6zB6OnpViBxYy4tgY3v2i6AZY9fwkcHZ032UOwtwUuW1d19sdT07qF0kZe6/3UR1tUaK6jjg2rmVcUIBCEYVjQ==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-ppc64-gnu": { + "version": "1.15.43", + "resolved": "https://registry.npmjs.org/@swc/core-linux-ppc64-gnu/-/core-linux-ppc64-gnu-1.15.43.tgz", + "integrity": "sha512-coxE1ZWdB3uSDVNoEtYNrRi/1epvckZx9cTJ8ICUxTMTxGk+yvQ/Twacp3ruZSaMPGCriUjP86C37VhaT6nyRg==", + "cpu": [ + "ppc64" + ], + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-s390x-gnu": { + "version": "1.15.43", + "resolved": "https://registry.npmjs.org/@swc/core-linux-s390x-gnu/-/core-linux-s390x-gnu-1.15.43.tgz", + "integrity": "sha512-lXfLhs+LpBsD5inuYx+YDH5WsPPBQ95KPUiy8P5wq9ob9xKDZFqwNfU2QW6bGO8NqRO/H9JQomTSt5Yyh+FGfA==", + "cpu": [ + "s390x" + ], + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-x64-gnu": { + "version": "1.15.43", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.15.43.tgz", + "integrity": "sha512-07XnKwTmKy8TGOZG3D9fRnLWGynxPjwQnZLVmBFbo6F+7vHYzBIOuwXEhemrChBWb6yDNZsVCcMWCPX6FDD2xg==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-x64-musl": { + "version": "1.15.43", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.15.43.tgz", + "integrity": "sha512-TJc+bsSIaBh+hZvZ5GRtW/K1bw66TJ9vsUwvVIsZdiWxU5ObLwZvfcnZ3UpgVfMnFibRes9uriJrQNBHEEogRQ==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-win32-arm64-msvc": { + "version": "1.15.43", + "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.15.43.tgz", + "integrity": "sha512-jfd7s2/bUQYkOHLs+LWQNKZdmDa8+sufKLllhpWAhVQ2GDCwsHe3vR/j+OSiItZNtkzFuaawa3+SAKz9y5gYfw==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-win32-ia32-msvc": { + "version": "1.15.43", + "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.15.43.tgz", + "integrity": "sha512-rLAE8JvucqEW1ZGohxPQrQWPBQeJG4+ypKbWfdlU/qmKScvCkxf9/Jxnzki1dkUQCQ7P5Enp13RlvqOlvx/32g==", + "cpu": [ + "ia32" + ], + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-win32-x64-msvc": { + "version": "1.15.43", + "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.15.43.tgz", + "integrity": "sha512-h8MLDHZcfIukwQWj03rIJZx1I0E81AYj2X7J/nGErG4nz+QAv6G1Z+peotvinL3lqpbo32tLYSMFo32/ySzxKg==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/counter": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz", + "integrity": "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==", + "license": "Apache-2.0" + }, + "node_modules/@swc/types": { + "version": "0.1.27", + "resolved": "https://registry.npmjs.org/@swc/types/-/types-0.1.27.tgz", + "integrity": "sha512-K6h3iUlqeM946U4sXFYeahefR1YBbXJvko+hv8WS8/0BNJ4OHiHRywMnQUJCqkR7Y9+hqQ1TvEpiKqUhz7NEFg==", + "license": "Apache-2.0", + "dependencies": { + "@swc/counter": "^0.1.3" + } + }, + "node_modules/@temporalio/activity": { + "version": "1.18.1", + "resolved": "https://registry.npmjs.org/@temporalio/activity/-/activity-1.18.1.tgz", + "integrity": "sha512-Y24veV/dOSavnXK0YRifM/eoM+i7A814w2CMnlw8H9GYYIKMo71JMMq6CEJkhueVuMJxGb4Ajg+uvWUT/Ocebw==", + "license": "MIT", + "dependencies": { + "@temporalio/client": "1.18.1", + "@temporalio/common": "1.18.1" + }, + "engines": { + "node": ">= 20.0.0" + } + }, + "node_modules/@temporalio/client": { + "version": "1.18.1", + "resolved": "https://registry.npmjs.org/@temporalio/client/-/client-1.18.1.tgz", + "integrity": "sha512-ToYL+qTCO8AE7IBsk1kmCZYh2Nu6e2W8QB+H5cKp2l8VUYWljNKXfb1aguGVN1gnCsBhbcu3s7dJmltLvlOcSw==", + "license": "MIT", + "dependencies": { + "@grpc/grpc-js": "^1.12.4", + "@temporalio/common": "1.18.1", + "@temporalio/proto": "1.18.1", + "abort-controller": "^3.0.0", + "long": "^5.2.3", + "nexus-rpc": "^0.0.2", + "uuid": "^11.1.0" + }, + "engines": { + "node": ">= 20.0.0" + } + }, + "node_modules/@temporalio/common": { + "version": "1.18.1", + "resolved": "https://registry.npmjs.org/@temporalio/common/-/common-1.18.1.tgz", + "integrity": "sha512-2hYWRKmY8WLt/8Nkrx2tziGAoNcbHDWbQR8AGvJ2xcap5E3hTIQPiNDARUm0tyCEK9umnCQztndbDxzLcxUZeA==", + "license": "MIT", + "dependencies": { + "@temporalio/proto": "1.18.1", + "long": "^5.2.3", + "ms": "3.0.0-canary.1", + "nexus-rpc": "^0.0.2", + "proto3-json-serializer": "^2.0.0" + }, + "engines": { + "node": ">= 20.0.0" + } + }, + "node_modules/@temporalio/core-bridge": { + "version": "1.18.1", + "resolved": "https://registry.npmjs.org/@temporalio/core-bridge/-/core-bridge-1.18.1.tgz", + "integrity": "sha512-iMTF6piD03YyjFFDjbmY+yGYptEtNtM4QEC8/8EUR36Ze6DkyqW1gll0VUyUCLpkhHhy4+XLBtqQ0bfpen+Csw==", + "license": "MIT", + "dependencies": { + "@grpc/grpc-js": "^1.12.4", + "@temporalio/common": "1.18.1" + }, + "engines": { + "node": ">= 20.0.0" + } + }, + "node_modules/@temporalio/nexus": { + "version": "1.18.1", + "resolved": "https://registry.npmjs.org/@temporalio/nexus/-/nexus-1.18.1.tgz", + "integrity": "sha512-LNrB+41kTEV32fCRHboLOT3D8RRVzaI953iqiHEaz0tkVVQmYUzRVQpSID/UjWExWw1mbaIvpFmKkYvY0g2j2A==", + "license": "MIT", + "dependencies": { + "@temporalio/client": "1.18.1", + "@temporalio/common": "1.18.1", + "@temporalio/proto": "1.18.1", + "long": "^5.2.3", + "nexus-rpc": "^0.0.2" + }, + "engines": { + "node": ">= 20.0.0" + } + }, + "node_modules/@temporalio/proto": { + "version": "1.18.1", + "resolved": "https://registry.npmjs.org/@temporalio/proto/-/proto-1.18.1.tgz", + "integrity": "sha512-pLa1JkmVwGdoFPCmb9DJ4P0z2mk3Zyu1VW6pXvJiYjt6W52Vm7YYi0caqfcSs/Wb/ZamdMbGqMcezftzVdzkYg==", + "license": "MIT", + "dependencies": { + "long": "^5.2.3", + "protobufjs": "7.5.8" + }, + "engines": { + "node": ">= 20.0.0" + } + }, + "node_modules/@temporalio/proto/node_modules/protobufjs": { + "version": "7.5.8", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.5.8.tgz", + "integrity": "sha512-dvpCIeLPbXZS/Ete7yLaO7RenOdken2NHKykBXbsaGxZT0UTltcarBciw+A78SRQs9iMAAVpsYA+l8b1hTePIA==", + "hasInstallScript": true, + "license": "BSD-3-Clause", + "dependencies": { + "@protobufjs/aspromise": "^1.1.2", + "@protobufjs/base64": "^1.1.2", + "@protobufjs/codegen": "^2.0.5", + "@protobufjs/eventemitter": "^1.1.0", + "@protobufjs/fetch": "^1.1.0", + "@protobufjs/float": "^1.0.2", + "@protobufjs/inquire": "^1.1.1", + "@protobufjs/path": "^1.1.2", + "@protobufjs/pool": "^1.1.0", + "@protobufjs/utf8": "^1.1.1", + "@types/node": ">=13.7.0", + "long": "^5.0.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/@temporalio/worker": { + "version": "1.18.1", + "resolved": "https://registry.npmjs.org/@temporalio/worker/-/worker-1.18.1.tgz", + "integrity": "sha512-ydeTHhlZF4G9ye5YnyBNgZT8/7lpGdEaF4LwJLUsW33AtLiqUp5zkzwefktpn/vyHWmNWjDdacxZ3l8WgdN+0w==", + "license": "MIT", + "dependencies": { + "@grpc/grpc-js": "^1.12.4", + "@swc/core": "^1.3.102", + "@temporalio/activity": "1.18.1", + "@temporalio/client": "1.18.1", + "@temporalio/common": "1.18.1", + "@temporalio/core-bridge": "1.18.1", + "@temporalio/nexus": "1.18.1", + "@temporalio/proto": "1.18.1", + "@temporalio/workflow": "1.18.1", + "heap-js": "^2.6.0", + "memfs": "^4.6.0", + "nexus-rpc": "^0.0.2", + "protobufjs": "^7.5.8", + "rxjs": "^7.8.1", + "source-map": "^0.7.4", + "source-map-loader": "^5.0.0", + "supports-color": "^8.1.1", + "swc-loader": "^0.2.3", + "unionfs": "^4.5.1", + "webpack": "^5.106.2" + }, + "engines": { + "node": ">= 20.0.0" + } + }, + "node_modules/@temporalio/workflow": { + "version": "1.18.1", + "resolved": "https://registry.npmjs.org/@temporalio/workflow/-/workflow-1.18.1.tgz", + "integrity": "sha512-UabVy4/Jbg9IiQFKfHQn4DKhnAqczo/TkzP/j8lebH+oK6Gqq/sLaFwIWEdVB0hSMO99q/A85NJvUbkr4n0DCA==", + "license": "MIT", + "dependencies": { + "@temporalio/common": "1.18.1", + "@temporalio/proto": "1.18.1", + "nexus-rpc": "^0.0.2" + }, + "engines": { + "node": ">= 20.0.0" + } + }, + "node_modules/@types/body-parser": { + "version": "1.19.6", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.6.tgz", + "integrity": "sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "node_modules/@types/connect": { + "version": "3.4.38", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", + "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/estree": { + "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/express": { + "version": "4.17.25", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.25.tgz", + "integrity": "sha512-dVd04UKsfpINUnK0yBoYHDF3xu7xVH4BuDotC/xGuycx4CgbP48X/KF/586bcObxT0HENHXEU8Nqtu6NR+eKhw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^4.17.33", + "@types/qs": "*", + "@types/serve-static": "^1" + } + }, + "node_modules/@types/express-serve-static-core": { + "version": "4.19.8", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.8.tgz", + "integrity": "sha512-02S5fmqeoKzVZCHPZid4b8JH2eM5HzQLZWN2FohQEy/0eXTq8VXZfSN6Pcr3F6N9R/vNrj7cpgbhjie6m/1tCA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*", + "@types/send": "*" + } + }, + "node_modules/@types/http-errors": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.5.tgz", + "integrity": "sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "license": "MIT" + }, + "node_modules/@types/mime": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", + "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "20.19.43", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.43.tgz", + "integrity": "sha512-6oYBAi5ikg4Pl+kGsoYtawUMBT2zZMCvPNF7pVLnHZfd1zf38DRiWn/gT01RYCdUqkv7Fhr+C9ot4/tb+2sVvA==", + "license": "MIT", + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "node_modules/@types/qs": { + "version": "6.15.1", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.15.1.tgz", + "integrity": "sha512-GZHUBZR9hckSUhrxmp1nG6NwdpM9fCunJwyThLW1X3AyHgd9IlHb6VANpQQqDr2o/qQp6McZ3y/IA2rVzKzSbw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/range-parser": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", + "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/send": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@types/send/-/send-1.2.1.tgz", + "integrity": "sha512-arsCikDvlU99zl1g69TcAB3mzZPpxgw0UQnaHeC1Nwb015xp8bknZv5rIfri9xTOcMuaVgvabfIRA7PSZVuZIQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/serve-static": { + "version": "1.15.10", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.10.tgz", + "integrity": "sha512-tRs1dB+g8Itk72rlSI2ZrW6vZg0YrLI81iQSTkMmOqnqCaNr/8Ek4VwWcN5vZgCYWbg/JJSGBlUaYGAOP73qBw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/http-errors": "*", + "@types/node": "*", + "@types/send": "<1" + } + }, + "node_modules/@types/serve-static/node_modules/@types/send": { + "version": "0.17.6", + "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.6.tgz", + "integrity": "sha512-Uqt8rPBE8SY0RK8JB1EzVOIZ32uqy8HwdxCnoCOsYrvnswqmFZ/k+9Ikidlk/ImhsdvBsloHbAlewb2IEBV/Og==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/mime": "^1", + "@types/node": "*" + } + }, + "node_modules/@vitest/expect": { + "version": "2.1.9", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-2.1.9.tgz", + "integrity": "sha512-UJCIkTBenHeKT1TTlKMJWy1laZewsRIzYighyYiJKZreqtdxSos/S1t+ktRMQWu2CKqaarrkeszJx1cgC5tGZw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/spy": "2.1.9", + "@vitest/utils": "2.1.9", + "chai": "^5.1.2", + "tinyrainbow": "^1.2.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/mocker": { + "version": "2.1.9", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-2.1.9.tgz", + "integrity": "sha512-tVL6uJgoUdi6icpxmdrn5YNo3g3Dxv+IHJBr0GXHaEdTcw3F+cPKnsXFhli6nO+f/6SDKPHEK1UN+k+TQv0Ehg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/spy": "2.1.9", + "estree-walker": "^3.0.3", + "magic-string": "^0.30.12" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "msw": "^2.4.9", + "vite": "^5.0.0" + }, + "peerDependenciesMeta": { + "msw": { + "optional": true + }, + "vite": { + "optional": true + } + } + }, + "node_modules/@vitest/pretty-format": { + "version": "2.1.9", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-2.1.9.tgz", + "integrity": "sha512-KhRIdGV2U9HOUzxfiHmY8IFHTdqtOhIzCpd8WRdJiE7D/HUcZVD0EgQCVjm+Q9gkUXWgBvMmTtZgIG48wq7sOQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyrainbow": "^1.2.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/runner": { + "version": "2.1.9", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-2.1.9.tgz", + "integrity": "sha512-ZXSSqTFIrzduD63btIfEyOmNcBmQvgOVsPNPe0jYtESiXkhd8u2erDLnMxmGrDCwHCCHE7hxwRDCT3pt0esT4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/utils": "2.1.9", + "pathe": "^1.1.2" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/snapshot": { + "version": "2.1.9", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-2.1.9.tgz", + "integrity": "sha512-oBO82rEjsxLNJincVhLhaxxZdEtV0EFHMK5Kmx5sJ6H9L183dHECjiefOAdnqpIgT5eZwT04PoggUnW88vOBNQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "2.1.9", + "magic-string": "^0.30.12", + "pathe": "^1.1.2" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/spy": { + "version": "2.1.9", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-2.1.9.tgz", + "integrity": "sha512-E1B35FwzXXTs9FHNK6bDszs7mtydNi5MIfUWpceJ8Xbfb1gBMscAnwLbEu+B44ed6W3XjL9/ehLPHR1fkf1KLQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyspy": "^3.0.2" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/utils": { + "version": "2.1.9", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-2.1.9.tgz", + "integrity": "sha512-v0psaMSkNJ3A2NMrUEHFRzJtDPFn+/VWZ5WxImB21T9fjucJRmS7xCS3ppEnARb9y11OAzaD+P2Ps+b+BGX5iQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "2.1.9", + "loupe": "^3.1.2", + "tinyrainbow": "^1.2.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@webassemblyjs/ast": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.14.1.tgz", + "integrity": "sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==", + "license": "MIT", + "dependencies": { + "@webassemblyjs/helper-numbers": "1.13.2", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2" + } + }, + "node_modules/@webassemblyjs/floating-point-hex-parser": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.13.2.tgz", + "integrity": "sha512-6oXyTOzbKxGH4steLbLNOu71Oj+C8Lg34n6CqRvqfS2O71BxY6ByfMDRhBytzknj9yGUPVJ1qIKhRlAwO1AovA==", + "license": "MIT" + }, + "node_modules/@webassemblyjs/helper-api-error": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.13.2.tgz", + "integrity": "sha512-U56GMYxy4ZQCbDZd6JuvvNV/WFildOjsaWD3Tzzvmw/mas3cXzRJPMjP83JqEsgSbyrmaGjBfDtV7KDXV9UzFQ==", + "license": "MIT" + }, + "node_modules/@webassemblyjs/helper-buffer": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.14.1.tgz", + "integrity": "sha512-jyH7wtcHiKssDtFPRB+iQdxlDf96m0E39yb0k5uJVhFGleZFoNw1c4aeIcVUPPbXUVJ94wwnMOAqUHyzoEPVMA==", + "license": "MIT" + }, + "node_modules/@webassemblyjs/helper-numbers": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.13.2.tgz", + "integrity": "sha512-FE8aCmS5Q6eQYcV3gI35O4J789wlQA+7JrqTTpJqn5emA4U2hvwJmvFRC0HODS+3Ye6WioDklgd6scJ3+PLnEA==", + "license": "MIT", + "dependencies": { + "@webassemblyjs/floating-point-hex-parser": "1.13.2", + "@webassemblyjs/helper-api-error": "1.13.2", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webassemblyjs/helper-wasm-bytecode": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.13.2.tgz", + "integrity": "sha512-3QbLKy93F0EAIXLh0ogEVR6rOubA9AoZ+WRYhNbFyuB70j3dRdwH9g+qXhLAO0kiYGlg3TxDV+I4rQTr/YNXkA==", + "license": "MIT" + }, + "node_modules/@webassemblyjs/helper-wasm-section": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.14.1.tgz", + "integrity": "sha512-ds5mXEqTJ6oxRoqjhWDU83OgzAYjwsCV8Lo/N+oRsNDmx/ZDpqalmrtgOMkHwxsG0iI//3BwWAErYRHtgn0dZw==", + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/wasm-gen": "1.14.1" + } + }, + "node_modules/@webassemblyjs/ieee754": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.13.2.tgz", + "integrity": "sha512-4LtOzh58S/5lX4ITKxnAK2USuNEvpdVV9AlgGQb8rJDHaLeHciwG4zlGr0j/SNWlr7x3vO1lDEsuePvtcDNCkw==", + "license": "MIT", + "dependencies": { + "@xtuc/ieee754": "^1.2.0" + } + }, + "node_modules/@webassemblyjs/leb128": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.13.2.tgz", + "integrity": "sha512-Lde1oNoIdzVzdkNEAWZ1dZ5orIbff80YPdHx20mrHwHrVNNTjNr8E3xz9BdpcGqRQbAEa+fkrCb+fRFTl/6sQw==", + "license": "Apache-2.0", + "dependencies": { + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webassemblyjs/utf8": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.13.2.tgz", + "integrity": "sha512-3NQWGjKTASY1xV5m7Hr0iPeXD9+RDobLll3T9d2AO+g3my8xy5peVyjSag4I50mR1bBSN/Ct12lo+R9tJk0NZQ==", + "license": "MIT" + }, + "node_modules/@webassemblyjs/wasm-edit": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.14.1.tgz", + "integrity": "sha512-RNJUIQH/J8iA/1NzlE4N7KtyZNHi3w7at7hDjvRNm5rcUXa00z1vRz3glZoULfJ5mpvYhLybmVcwcjGrC1pRrQ==", + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/helper-wasm-section": "1.14.1", + "@webassemblyjs/wasm-gen": "1.14.1", + "@webassemblyjs/wasm-opt": "1.14.1", + "@webassemblyjs/wasm-parser": "1.14.1", + "@webassemblyjs/wast-printer": "1.14.1" + } + }, + "node_modules/@webassemblyjs/wasm-gen": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.14.1.tgz", + "integrity": "sha512-AmomSIjP8ZbfGQhumkNvgC33AY7qtMCXnN6bL2u2Js4gVCg8fp735aEiMSBbDR7UQIj90n4wKAFUSEd0QN2Ukg==", + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/ieee754": "1.13.2", + "@webassemblyjs/leb128": "1.13.2", + "@webassemblyjs/utf8": "1.13.2" + } + }, + "node_modules/@webassemblyjs/wasm-opt": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.14.1.tgz", + "integrity": "sha512-PTcKLUNvBqnY2U6E5bdOQcSM+oVP/PmrDY9NzowJjislEjwP/C4an2303MCVS2Mg9d3AJpIGdUFIQQWbPds0Sw==", + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/wasm-gen": "1.14.1", + "@webassemblyjs/wasm-parser": "1.14.1" + } + }, + "node_modules/@webassemblyjs/wasm-parser": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.14.1.tgz", + "integrity": "sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ==", + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-api-error": "1.13.2", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/ieee754": "1.13.2", + "@webassemblyjs/leb128": "1.13.2", + "@webassemblyjs/utf8": "1.13.2" + } + }, + "node_modules/@webassemblyjs/wast-printer": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.14.1.tgz", + "integrity": "sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw==", + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@xtuc/ieee754": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", + "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", + "license": "BSD-3-Clause" + }, + "node_modules/@xtuc/long": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", + "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", + "license": "Apache-2.0" + }, + "node_modules/abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "license": "MIT", + "dependencies": { + "event-target-shim": "^5.0.0" + }, + "engines": { + "node": ">=6.5" + } + }, + "node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "license": "MIT", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/acorn": { + "version": "8.17.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.17.0.tgz", + "integrity": "sha512-xRQbDb9BnwDafYNn6Vwl839DYVjqXYb1XVGtWAZ1kcDc6iwAL4hg3B1dZlRiuENFeO2H53gFG3in621AdERVAg==", + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-import-phases": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/acorn-import-phases/-/acorn-import-phases-1.0.4.tgz", + "integrity": "sha512-wKmbr/DDiIXzEOiWrTTUcDm24kQ2vGfZQvM2fwg2vXqR5uW6aapr7ObPtj1th32b9u90/Pf4AItvdTh42fBmVQ==", + "license": "MIT", + "engines": { + "node": ">=10.13.0" + }, + "peerDependencies": { + "acorn": "^8.14.0" + } + }, + "node_modules/ajv": { + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz", + "integrity": "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==", + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-formats": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", + "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", + "license": "MIT", + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3" + }, + "peerDependencies": { + "ajv": "^8.8.2" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", + "license": "MIT" + }, + "node_modules/assertion-error": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", + "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + } + }, + "node_modules/baseline-browser-mapping": { + "version": "2.10.38", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.38.tgz", + "integrity": "sha512-31/02mVB4yuQU6adKk5SlY6m+mxDwUq5KZkyYgnLrrKl7TEm1+3PyDtDBz2kOv/wxZz41GHsvV1A/u6RmiyBvw==", + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.cjs" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/body-parser": { + "version": "1.20.5", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.5.tgz", + "integrity": "sha512-3grm+/2tUOvu2cjJkvsIxrv/wVpfXQW4PsQHYm7yk4vfpu7Ekl6nEsYBoJUL6qDwZUx8wUhQ8tR2qz+ad9c9OA==", + "license": "MIT", + "dependencies": { + "bytes": "~3.1.2", + "content-type": "~1.0.5", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "~1.2.0", + "http-errors": "~2.0.1", + "iconv-lite": "~0.4.24", + "on-finished": "~2.4.1", + "qs": "~6.15.1", + "raw-body": "~2.5.3", + "type-is": "~1.6.18", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/body-parser/node_modules/raw-body": { + "version": "2.5.3", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.3.tgz", + "integrity": "sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA==", + "license": "MIT", + "dependencies": { + "bytes": "~3.1.2", + "http-errors": "~2.0.1", + "iconv-lite": "~0.4.24", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/browserslist": { + "version": "4.28.4", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.4.tgz", + "integrity": "sha512-MTc8i/x9jBQd1iMw2CFGS+rwMa07eYjLR0CCTLDACl9xhxy+nIs3KeML/biicXtk9JrZ6dnnTatmc7ErPXIxqw==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "baseline-browser-mapping": "^2.10.38", + "caniuse-lite": "^1.0.30001799", + "electron-to-chromium": "^1.5.376", + "node-releases": "^2.0.48", + "update-browserslist-db": "^1.2.3" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "license": "MIT" + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "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", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001799", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001799.tgz", + "integrity": "sha512-hG1bReV+OUU+MOqK4t/ZWI0tZOyz3rqS9XuhOUz1cIcbwBKjOyJEJuw9ER5JuNyqxNk8u/JUVbGibBOL1yrjFw==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "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==", + "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" + } + }, + "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/chrome-trace-event": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.4.tgz", + "integrity": "sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==", + "license": "MIT", + "engines": { + "node": ">=6.0" + } + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" + }, + "node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "license": "MIT" + }, + "node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "license": "MIT", + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.7.tgz", + "integrity": "sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA==", + "license": "MIT" + }, + "node_modules/cors": { + "version": "2.8.6", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.6.tgz", + "integrity": "sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw==", + "license": "MIT", + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + }, + "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", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/debug/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, + "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/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "license": "MIT", + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "license": "MIT" + }, + "node_modules/electron-to-chromium": { + "version": "1.5.378", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.378.tgz", + "integrity": "sha512-VinvOAuuPmdD1guEgGv5f2Qp7/vlfqOrUOMYNnOD4wj3pit8kRsQHzfIf6teyUGWo15Tg5+bOJaRunvyltpVWQ==", + "license": "ISC" + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT" + }, + "node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/enhanced-resolve": { + "version": "5.24.1", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.24.1.tgz", + "integrity": "sha512-7DdUaTjmNwMcH2gLr1qycesKII3BK4RLy/mdAb7x10Lq7bR4aNKHt1BR1ZALSv0rPM/hF5wYF0PhGop/rJm8vw==", + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.3.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "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==", + "dev": true, + "license": "MIT" + }, + "node_modules/es-object-atoms": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.2.tgz", + "integrity": "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/esbuild": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.1.tgz", + "integrity": "sha512-HrJrvZv5ayxBzPfwphOoNzkzOIIlifzk0KJrGK2c8R4+LKpMtpYLQeUdjnwjWv/LZlkH2laZk+4w78pi99D4Vw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.28.1", + "@esbuild/android-arm": "0.28.1", + "@esbuild/android-arm64": "0.28.1", + "@esbuild/android-x64": "0.28.1", + "@esbuild/darwin-arm64": "0.28.1", + "@esbuild/darwin-x64": "0.28.1", + "@esbuild/freebsd-arm64": "0.28.1", + "@esbuild/freebsd-x64": "0.28.1", + "@esbuild/linux-arm": "0.28.1", + "@esbuild/linux-arm64": "0.28.1", + "@esbuild/linux-ia32": "0.28.1", + "@esbuild/linux-loong64": "0.28.1", + "@esbuild/linux-mips64el": "0.28.1", + "@esbuild/linux-ppc64": "0.28.1", + "@esbuild/linux-riscv64": "0.28.1", + "@esbuild/linux-s390x": "0.28.1", + "@esbuild/linux-x64": "0.28.1", + "@esbuild/netbsd-arm64": "0.28.1", + "@esbuild/netbsd-x64": "0.28.1", + "@esbuild/openbsd-arm64": "0.28.1", + "@esbuild/openbsd-x64": "0.28.1", + "@esbuild/openharmony-arm64": "0.28.1", + "@esbuild/sunos-x64": "0.28.1", + "@esbuild/win32-arm64": "0.28.1", + "@esbuild/win32-ia32": "0.28.1", + "@esbuild/win32-x64": "0.28.1" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "license": "MIT" + }, + "node_modules/eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esrecurse/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0" + } + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/event-target-shim": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "license": "MIT", + "engines": { + "node": ">=0.8.x" + } + }, + "node_modules/eventsource": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-3.0.7.tgz", + "integrity": "sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA==", + "license": "MIT", + "dependencies": { + "eventsource-parser": "^3.0.1" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/eventsource-parser": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/eventsource-parser/-/eventsource-parser-3.1.0.tgz", + "integrity": "sha512-kJezFj9YFAMLeORyi7aCLxLbD5/qWMQnoMVlVPyHIll7lgRJCc3JVln9Vgl9nwQi0YkMnhdGTMNn7CkRRAptMg==", + "license": "MIT", + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/expect-type": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.3.0.tgz", + "integrity": "sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/express": { + "version": "4.22.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.22.2.tgz", + "integrity": "sha512-IuL+Elrou2ZvCFHs18/CIzy2Nzvo25nZ1/D2eIZlz7c+QUayAcYoiM2BthCjs+EBHVpjYjcuLDAiCWgeIX3X1Q==", + "license": "MIT", + "dependencies": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "~1.20.5", + "content-disposition": "~0.5.4", + "content-type": "~1.0.4", + "cookie": "~0.7.1", + "cookie-signature": "~1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "~1.3.1", + "fresh": "~0.5.2", + "http-errors": "~2.0.0", + "merge-descriptors": "1.0.3", + "methods": "~1.1.2", + "on-finished": "~2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "~0.1.12", + "proxy-addr": "~2.0.7", + "qs": "~6.15.1", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "~0.19.0", + "serve-static": "~1.16.2", + "setprototypeof": "1.2.0", + "statuses": "~2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/express-rate-limit": { + "version": "8.5.2", + "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-8.5.2.tgz", + "integrity": "sha512-5Kb34ipNX694DH48vN9irak1Qx30nb0PLYHXfJgw4YEjiC3ZEmZJhwOp+VfiCYwFzvFTdB9QkArYS5kXa2cx2A==", + "license": "MIT", + "dependencies": { + "ip-address": "^10.2.0" + }, + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://github.com/sponsors/express-rate-limit" + }, + "peerDependencies": { + "express": ">= 4.11" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "license": "MIT" + }, + "node_modules/fast-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.2.tgz", + "integrity": "sha512-rVjf7ArG3LTk+FS6Yw81V1DLuZl1bRbNrev6Tmd/9RaroeeRRJhAt7jg/6YFxbvAQXUCavSoZhPPj6oOx+5KjQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/finalhandler": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.2.tgz", + "integrity": "sha512-aA4RyPcd3badbdABGDuTXCMTtOneUCAYH/gxoYRTZlIJdF0YPWuGqiAsIrhNnnqdXGswYk6dGujem4w80UJFhg==", + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "on-finished": "~2.4.1", + "parseurl": "~1.3.3", + "statuses": "~2.0.2", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fs-monkey": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.1.0.tgz", + "integrity": "sha512-QMUezzXWII9EV5aTFXW1UBVUO77wYPpjqIF8/AviUCThNeSYZykpoTixUeaNNBwmCev0AMDWMAni+f8Hxb1IFw==", + "license": "Unlicense" + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/glob-to-regex.js": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/glob-to-regex.js/-/glob-to-regex.js-1.2.0.tgz", + "integrity": "sha512-QMwlOQKU/IzqMUOAZWubUOT8Qft+Y0KQWnX9nK3ch0CJg0tTp4TvGZsTfudYKv2NzoQSyPcnA6TYeIQ3jGichQ==", + "license": "Apache-2.0", + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/glob-to-regexp": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", + "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", + "license": "BSD-2-Clause" + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "license": "ISC" + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "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" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/heap-js": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/heap-js/-/heap-js-2.7.1.tgz", + "integrity": "sha512-EQfezRg0NCZGNlhlDR3Evrw1FVL2G3LhU7EgPoxufQKruNBSYA8MiRPHeWbU+36o+Fhel0wMwM+sLEiBAlNLJA==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/hono": { + "version": "4.12.27", + "resolved": "https://registry.npmjs.org/hono/-/hono-4.12.27.tgz", + "integrity": "sha512-1yrb/+w6HWQJrUCLkJ2IF5jNIPvvFkblV5RNOYl6bV+OA6p9GLcMpHFFGTosSvHvcAUibuUukRqhlYI4z32C7Q==", + "license": "MIT", + "engines": { + "node": ">=16.9.0" + } + }, + "node_modules/http-errors": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", + "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", + "license": "MIT", + "dependencies": { + "depd": "~2.0.0", + "inherits": "~2.0.4", + "setprototypeof": "~1.2.0", + "statuses": "~2.0.2", + "toidentifier": "~1.0.1" + }, + "engines": { + "node": ">= 0.8" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/hyperdyperid": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/hyperdyperid/-/hyperdyperid-1.2.0.tgz", + "integrity": "sha512-Y93lCzHYgGWdrJ66yIktxiaGULYc6oGiABxhcO5AufBeOyoIdZF7bIfLaOrbM0iGIOXQQgxxRrFEnb+Y6w1n4A==", + "license": "MIT", + "engines": { + "node": ">=10.18" + } + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, + "node_modules/ip-address": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.2.0.tgz", + "integrity": "sha512-/+S6j4E9AHvW9SWMSEY9Xfy66O5PWvVEJ08O0y5JGyEKQpojb0K0GKpz/v5HJ/G0vi3D2sjGK78119oXZeE0qA==", + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-promise": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", + "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", + "license": "MIT" + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "license": "ISC" + }, + "node_modules/jest-worker": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", + "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", + "license": "MIT", + "dependencies": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": ">= 10.13.0" + } + }, + "node_modules/jose": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/jose/-/jose-6.2.3.tgz", + "integrity": "sha512-YYVDInQKFJfR/xa3ojUTl8c2KoTwiL1R5Wg9YCydwH0x0B9grbzlg5HC7mMjCtUJjbQ/YnGEZIhI5tCgfTb4Hw==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, + "node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "license": "MIT" + }, + "node_modules/json-schema-typed": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/json-schema-typed/-/json-schema-typed-8.0.2.tgz", + "integrity": "sha512-fQhoXdcvc3V28x7C7BMs4P5+kNlgUURe2jmUT1T//oBRMDrqy1QPelJimwZGo7Hg9VPV3EQV5Bnq4hbFy2vetA==", + "license": "BSD-2-Clause" + }, + "node_modules/loader-runner": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.2.tgz", + "integrity": "sha512-DFEqQ3ihfS9blba08cLfYf1NRAIEm+dDjic073DRDc3/JspI/8wYmtDsHwd3+4hwvdxSK7PGaElfTmm0awWJ4w==", + "license": "MIT", + "engines": { + "node": ">=6.11.5" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/lodash.camelcase": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", + "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==", + "license": "MIT" + }, + "node_modules/long": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/long/-/long-5.3.2.tgz", + "integrity": "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==", + "license": "Apache-2.0" + }, + "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/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/memfs": { + "version": "4.57.8", + "resolved": "https://registry.npmjs.org/memfs/-/memfs-4.57.8.tgz", + "integrity": "sha512-bApYhn8BLpFAnAQmFfEl/NPN+8qx5Ar3V4Qt3ek23mVwBEElzV7c6XoPkb/PCG8ZFpowCEpHcPwMFTwHS7tSMA==", + "license": "Apache-2.0", + "dependencies": { + "@jsonjoy.com/fs-core": "4.57.8", + "@jsonjoy.com/fs-fsa": "4.57.8", + "@jsonjoy.com/fs-node": "4.57.8", + "@jsonjoy.com/fs-node-builtins": "4.57.8", + "@jsonjoy.com/fs-node-to-fsa": "4.57.8", + "@jsonjoy.com/fs-node-utils": "4.57.8", + "@jsonjoy.com/fs-print": "4.57.8", + "@jsonjoy.com/fs-snapshot": "4.57.8", + "@jsonjoy.com/json-pack": "^1.11.0", + "@jsonjoy.com/util": "^1.9.0", + "glob-to-regex.js": "^1.0.1", + "thingies": "^2.5.0", + "tree-dump": "^1.0.3", + "tslib": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/merge-descriptors": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", + "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "license": "MIT" + }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "license": "MIT", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/ms": { + "version": "3.0.0-canary.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-3.0.0-canary.1.tgz", + "integrity": "sha512-kh8ARjh8rMN7Du2igDRO9QJnqCb2xYTJxyQYK7vJJS4TvLLmsbyhiKpSW+t+y26gyOyMd0riphX0GeWKU3ky5g==", + "license": "MIT", + "engines": { + "node": ">=12.13" + } + }, + "node_modules/nanoid": { + "version": "3.3.15", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.15.tgz", + "integrity": "sha512-y7Wygv/7mEOvxTuEQDB8StXdMRBWf1kR/tlhAzBRUFkB2jfcLOAxO/SHmOO2zgz1pVgK29/kyupn059/bCHdjA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "license": "MIT" + }, + "node_modules/nexus-rpc": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/nexus-rpc/-/nexus-rpc-0.0.2.tgz", + "integrity": "sha512-IWjIExdVYlmwXuzHdY/Q3lXCv1gbqoAXPazQhy2w4Xgtgha3H0OOujEESVPQcFUFMWm+pAk2gKnb57g8S41JZg==", + "license": "MIT", + "engines": { + "node": ">= 20.0.0" + } + }, + "node_modules/node-releases": { + "version": "2.0.49", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.49.tgz", + "integrity": "sha512-f06bl1D+8ZDkn2oOQQKAh5/otFWqVnM1Q5oerA8Pex7UfT66Tx4IPHIqVVFKqFT3FUtaDstdgkM7yT7JWhqxfw==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-to-regexp": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.13.tgz", + "integrity": "sha512-A/AGNMFN3c8bOlvV9RreMdrv7jsmF9XIfDeCd87+I8RNg6s78BhJxMu69NEMHBSJFxKidViTEdruRwEk/WIKqA==", + "license": "MIT" + }, + "node_modules/pathe": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.2.tgz", + "integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==", + "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", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "license": "ISC" + }, + "node_modules/pkce-challenge": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/pkce-challenge/-/pkce-challenge-5.0.1.tgz", + "integrity": "sha512-wQ0b/W4Fr01qtpHlqSqspcj3EhBvimsdh0KlHhH8HRZnMsEa0ea2fTULOXOS9ccQr3om+GcGRk4e+isrZWV8qQ==", + "license": "MIT", + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/postcss": { + "version": "8.5.15", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.15.tgz", + "integrity": "sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.12", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/proto3-json-serializer": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/proto3-json-serializer/-/proto3-json-serializer-2.0.2.tgz", + "integrity": "sha512-SAzp/O4Yh02jGdRc+uIrGoe87dkN/XtwxfZ4ZyafJHymd79ozp5VG5nyZ7ygqPM5+cpLDjjGnYFUkngonyDPOQ==", + "license": "Apache-2.0", + "dependencies": { + "protobufjs": "^7.2.5" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/protobufjs": { + "version": "7.6.4", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.6.4.tgz", + "integrity": "sha512-RJJPTTpvFfHcWLkIa2JFWK4XvtSzS0yEWDmunqHXli1h3JlkbcQZXDZdcWxv+JK3Xsl5/UFDPZ0iGm7DAengYw==", + "hasInstallScript": true, + "license": "BSD-3-Clause", + "dependencies": { + "@protobufjs/aspromise": "^1.1.2", + "@protobufjs/base64": "^1.1.2", + "@protobufjs/codegen": "^2.0.5", + "@protobufjs/eventemitter": "^1.1.1", + "@protobufjs/fetch": "^1.1.1", + "@protobufjs/float": "^1.0.2", + "@protobufjs/path": "^1.1.2", + "@protobufjs/pool": "^1.1.0", + "@protobufjs/utf8": "^1.1.1", + "@types/node": ">=13.7.0", + "long": "^5.3.2" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "license": "MIT", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/qs": { + "version": "6.15.3", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.3.tgz", + "integrity": "sha512-O9gl3zCl5h5blw1KGUzQKhA5oUXSl8rwUIM5o0S3nCXMliSvy5Dzx7/DJcI+SwgICv+IneSZwhBh1oSyEHA71A==", + "license": "BSD-3-Clause", + "dependencies": { + "es-define-property": "^1.0.1", + "side-channel": "^1.1.1" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.2.tgz", + "integrity": "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==", + "license": "MIT", + "dependencies": { + "bytes": "~3.1.2", + "http-errors": "~2.0.1", + "iconv-lite": "~0.7.0", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/raw-body/node_modules/iconv-lite": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.2.tgz", + "integrity": "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/rollup": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.62.2.tgz", + "integrity": "sha512-RFnrW4lhXA3s3eqHDZvN654g8OTjzRfqpIRJYczCGB6HzphckVAi/Qh4tbPUbRuDi7s1Llv8g/NspLkttY3gTA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.9" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.62.2", + "@rollup/rollup-android-arm64": "4.62.2", + "@rollup/rollup-darwin-arm64": "4.62.2", + "@rollup/rollup-darwin-x64": "4.62.2", + "@rollup/rollup-freebsd-arm64": "4.62.2", + "@rollup/rollup-freebsd-x64": "4.62.2", + "@rollup/rollup-linux-arm-gnueabihf": "4.62.2", + "@rollup/rollup-linux-arm-musleabihf": "4.62.2", + "@rollup/rollup-linux-arm64-gnu": "4.62.2", + "@rollup/rollup-linux-arm64-musl": "4.62.2", + "@rollup/rollup-linux-loong64-gnu": "4.62.2", + "@rollup/rollup-linux-loong64-musl": "4.62.2", + "@rollup/rollup-linux-ppc64-gnu": "4.62.2", + "@rollup/rollup-linux-ppc64-musl": "4.62.2", + "@rollup/rollup-linux-riscv64-gnu": "4.62.2", + "@rollup/rollup-linux-riscv64-musl": "4.62.2", + "@rollup/rollup-linux-s390x-gnu": "4.62.2", + "@rollup/rollup-linux-x64-gnu": "4.62.2", + "@rollup/rollup-linux-x64-musl": "4.62.2", + "@rollup/rollup-openbsd-x64": "4.62.2", + "@rollup/rollup-openharmony-arm64": "4.62.2", + "@rollup/rollup-win32-arm64-msvc": "4.62.2", + "@rollup/rollup-win32-ia32-msvc": "4.62.2", + "@rollup/rollup-win32-x64-gnu": "4.62.2", + "@rollup/rollup-win32-x64-msvc": "4.62.2", + "fsevents": "~2.3.2" + } + }, + "node_modules/router": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", + "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "depd": "^2.0.0", + "is-promise": "^4.0.0", + "parseurl": "^1.3.3", + "path-to-regexp": "^8.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/router/node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/router/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/router/node_modules/path-to-regexp": { + "version": "8.4.2", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.4.2.tgz", + "integrity": "sha512-qRcuIdP69NPm4qbACK+aDogI5CBDMi1jKe0ry5rSQJz8JVLsC7jV8XpiJjGRLLol3N+R5ihGYcrPLTno6pAdBA==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/rxjs": { + "version": "7.8.2", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz", + "integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" + }, + "node_modules/schema-utils": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.3.tgz", + "integrity": "sha512-eflK8wEtyOE6+hsaRVPxvUKYCpRgzLqDTb8krvAsRIwOGlHoSgYLgBXoubGgLd2fT41/OUYdb48v4k4WWHQurA==", + "license": "MIT", + "dependencies": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.9.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.1.0" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/schema-utils/node_modules/ajv-formats": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", + "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", + "license": "MIT", + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/send": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/send/-/send-0.19.2.tgz", + "integrity": "sha512-VMbMxbDeehAxpOtWJXlcUS5E8iXh6QmN+BkRX1GARS3wRaXEEgzCcB10gTQazO42tpNIya8xIyNx8fll1OFPrg==", + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "~0.5.2", + "http-errors": "~2.0.1", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "~2.4.1", + "range-parser": "~1.2.1", + "statuses": "~2.0.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/serve-static": { + "version": "1.16.3", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.3.tgz", + "integrity": "sha512-x0RTqQel6g5SY7Lg6ZreMmsOzncHFU7nhnRWkKgWuMTu5NN0DR5oruckMqRvacAN9d5w6ARnRBXl9xhDCgfMeA==", + "license": "MIT", + "dependencies": { + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "~0.19.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "license": "ISC" + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/side-channel": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.1.tgz", + "integrity": "sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ==", + "license": "MIT", + "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" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.1.tgz", + "integrity": "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/siginfo": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", + "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", + "dev": true, + "license": "ISC" + }, + "node_modules/source-map": { + "version": "0.7.6", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.6.tgz", + "integrity": "sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==", + "license": "BSD-3-Clause", + "engines": { + "node": ">= 12" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-loader": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/source-map-loader/-/source-map-loader-5.0.0.tgz", + "integrity": "sha512-k2Dur7CbSLcAH73sBcIkV5xjPV4SzqO1NJ7+XaQl8if3VODDUj3FNchNGpqgJSKbvUfJuhVdv8K2Eu8/TNl2eA==", + "license": "MIT", + "dependencies": { + "iconv-lite": "^0.6.3", + "source-map-js": "^1.0.2" + }, + "engines": { + "node": ">= 18.12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.72.1" + } + }, + "node_modules/source-map-loader/node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/source-map-support/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/stackback": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", + "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", + "dev": true, + "license": "MIT" + }, + "node_modules/statuses": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/std-env": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.10.0.tgz", + "integrity": "sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==", + "dev": true, + "license": "MIT" + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/swc-loader": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/swc-loader/-/swc-loader-0.2.7.tgz", + "integrity": "sha512-nwYWw3Fh9ame3Rtm7StS9SBLpHRRnYcK7bnpF3UKZmesAK0gw2/ADvlURFAINmPvKtDLzp+GBiP9yLoEjg6S9w==", + "license": "MIT", + "dependencies": { + "@swc/counter": "^0.1.3" + }, + "peerDependencies": { + "@swc/core": "^1.2.147", + "webpack": ">=2" + } + }, + "node_modules/tapable": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.3.tgz", + "integrity": "sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==", + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/terser": { + "version": "5.48.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.48.0.tgz", + "integrity": "sha512-J/9An6vs9Us6wKRriSFXBWdRZapREHqFzdNUKk0pmu804EMR6dr6winwo7e5JDxN4xahxQsuysyYFwlwj4XN/Q==", + "license": "BSD-2-Clause", + "dependencies": { + "@jridgewell/source-map": "^0.3.3", + "acorn": "^8.15.0", + "commander": "^2.20.0", + "source-map-support": "~0.5.20" + }, + "bin": { + "terser": "bin/terser" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/terser-webpack-plugin": { + "version": "5.6.1", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.6.1.tgz", + "integrity": "sha512-201R5j+sJpK8nFWwKVyNfZot8FaJbLZDq5evriVzbV1wDtSXDjRUDRfJzHpAaxFDMEhsZL1QkeqM61wgsS3KaQ==", + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.25", + "jest-worker": "^27.4.5", + "schema-utils": "^4.3.0", + "terser": "^5.31.1" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.1.0" + }, + "peerDependenciesMeta": { + "@minify-html/node": { + "optional": true + }, + "@swc/core": { + "optional": true + }, + "@swc/css": { + "optional": true + }, + "@swc/html": { + "optional": true + }, + "clean-css": { + "optional": true + }, + "cssnano": { + "optional": true + }, + "csso": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "html-minifier-terser": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "postcss": { + "optional": true + }, + "uglify-js": { + "optional": true + } + } + }, + "node_modules/thingies": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/thingies/-/thingies-2.6.0.tgz", + "integrity": "sha512-rMHRjmlFLM1R96UYPvpmnc3LYtdFrT33JIB7L9hetGue1qAPfn1N2LJeEjxUSidu1Iku+haLZXDuEXUHNGO/lg==", + "license": "MIT", + "engines": { + "node": ">=10.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "^2" + } + }, + "node_modules/tinybench": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", + "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", + "dev": true, + "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==", + "dev": true, + "license": "MIT" + }, + "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": "1.2.0", + "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-1.2.0.tgz", + "integrity": "sha512-weEDEq7Z5eTHPDh4xjX789+fHfF+P8boiFB+0vbWzpbnbsEr/GRaohi/uMKxg8RZMXnl1ItAi/IUHWMsjDV7kQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/tinyspy": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-3.0.2.tgz", + "integrity": "sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "license": "MIT", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/tree-dump": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/tree-dump/-/tree-dump-1.1.0.tgz", + "integrity": "sha512-rMuvhU4MCDbcbnleZTFezWsaZXRFemSqAM+7jPnzUl1fo9w3YEKOxAeui0fz3OI4EU4hf23iyA7uQRVko+UaBA==", + "license": "Apache-2.0", + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/tsx": { + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.22.4.tgz", + "integrity": "sha512-X8EX+XV4QR5xCsrgxaED954zTDfY8KqlDtskKEL0cHhyS/P8b4IFOvGDQpsC9Q1XnLq915wEfwwY/zzskCtmhg==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "~0.28.0" + }, + "bin": { + "tsx": "dist/cli.mjs" + }, + "engines": { + "node": ">=18.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + } + }, + "node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "license": "MIT", + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "license": "MIT" + }, + "node_modules/unionfs": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/unionfs/-/unionfs-4.6.0.tgz", + "integrity": "sha512-fJAy3gTHjFi5S3TP5EGdjs/OUMFFvI/ady3T8qVuZfkv8Qi8prV/Q8BuFEgODJslhZTT2z2qdD2lGdee9qjEnA==", + "dependencies": { + "fs-monkey": "^1.0.0" + } + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", + "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "license": "MIT", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/uuid": { + "version": "11.1.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.1.tgz", + "integrity": "sha512-vIYxrBCC/N/K+Js3qSN88go7kIfNPssr/hHCesKCQNAjmgvYS2oqr69kIufEG+O4+PfezOH4EbIeHCfFov8ZgQ==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/esm/bin/uuid" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/vite": { + "version": "5.4.21", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.21.tgz", + "integrity": "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.21.3", + "postcss": "^8.4.43", + "rollup": "^4.20.0" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "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" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + } + } + }, + "node_modules/vite-node": { + "version": "2.1.9", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-2.1.9.tgz", + "integrity": "sha512-AM9aQ/IPrW/6ENLQg3AGY4K1N2TGZdR5e4gu/MmmR2xR3Ll1+dib+nook92g4TV3PXVyeyxdWwtaCAiUL0hMxA==", + "dev": true, + "license": "MIT", + "dependencies": { + "cac": "^6.7.14", + "debug": "^4.3.7", + "es-module-lexer": "^1.5.4", + "pathe": "^1.1.2", + "vite": "^5.0.0" + }, + "bin": { + "vite-node": "vite-node.mjs" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/vite-node/node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/vite-node/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/vite/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==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/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==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/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==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/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==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/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==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/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==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/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==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/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==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/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==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/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==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/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==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/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==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/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==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/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==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/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==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/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==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/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==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/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==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/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==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/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==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/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==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/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==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/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==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/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==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "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" + } + }, + "node_modules/vitest": { + "version": "2.1.9", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-2.1.9.tgz", + "integrity": "sha512-MSmPM9REYqDGBI8439mA4mWhV5sKmDlBKWIYbA3lRb2PTHACE0mgKwA8yQ2xq9vxDTuk4iPrECBAEW2aoFXY0Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/expect": "2.1.9", + "@vitest/mocker": "2.1.9", + "@vitest/pretty-format": "^2.1.9", + "@vitest/runner": "2.1.9", + "@vitest/snapshot": "2.1.9", + "@vitest/spy": "2.1.9", + "@vitest/utils": "2.1.9", + "chai": "^5.1.2", + "debug": "^4.3.7", + "expect-type": "^1.1.0", + "magic-string": "^0.30.12", + "pathe": "^1.1.2", + "std-env": "^3.8.0", + "tinybench": "^2.9.0", + "tinyexec": "^0.3.1", + "tinypool": "^1.0.1", + "tinyrainbow": "^1.2.0", + "vite": "^5.0.0", + "vite-node": "2.1.9", + "why-is-node-running": "^2.3.0" + }, + "bin": { + "vitest": "vitest.mjs" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@edge-runtime/vm": "*", + "@types/node": "^18.0.0 || >=20.0.0", + "@vitest/browser": "2.1.9", + "@vitest/ui": "2.1.9", + "happy-dom": "*", + "jsdom": "*" + }, + "peerDependenciesMeta": { + "@edge-runtime/vm": { + "optional": true + }, + "@types/node": { + "optional": true + }, + "@vitest/browser": { + "optional": true + }, + "@vitest/ui": { + "optional": true + }, + "happy-dom": { + "optional": true + }, + "jsdom": { + "optional": true + } + } + }, + "node_modules/vitest/node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/vitest/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/watchpack": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.5.2.tgz", + "integrity": "sha512-6i/00NBjP4yGPs+caKSyRfpTF/8Torsu0MOW3mMzIbhgISFder8i7xbqgHlLMwJrdiN8ndBV3UA1/AfzPSr+jg==", + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.1.2" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/webpack": { + "version": "5.107.2", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.107.2.tgz", + "integrity": "sha512-v7RhXaJbpMlV0D7hC7lb2EbnxkoeUqf9qhKr6lozx3Q48pmFrqqNRmZFUEGmi7pSwm6fCQ2H1IjvCkHqdpVdjQ==", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.8", + "@types/json-schema": "^7.0.15", + "@webassemblyjs/ast": "^1.14.1", + "@webassemblyjs/wasm-edit": "^1.14.1", + "@webassemblyjs/wasm-parser": "^1.14.1", + "acorn": "^8.16.0", + "acorn-import-phases": "^1.0.3", + "browserslist": "^4.28.1", + "chrome-trace-event": "^1.0.2", + "enhanced-resolve": "^5.22.0", + "es-module-lexer": "^2.1.0", + "eslint-scope": "5.1.1", + "events": "^3.2.0", + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.2.11", + "loader-runner": "^4.3.2", + "mime-db": "^1.54.0", + "neo-async": "^2.6.2", + "schema-utils": "^4.3.3", + "tapable": "^2.3.0", + "terser-webpack-plugin": "^5.5.0", + "watchpack": "^2.5.1", + "webpack-sources": "^3.5.0" + }, + "bin": { + "webpack": "bin/webpack.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependenciesMeta": { + "webpack-cli": { + "optional": true + } + } + }, + "node_modules/webpack-sources": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.5.0.tgz", + "integrity": "sha512-HPuy+uuoTCaaoEoI1LQ3JN9+vrPBvEesnnX1jADHy728cHSMlq4wUc4afYqahq2B1mhQVZxCXOkNTnXltr+2vQ==", + "license": "MIT", + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/webpack/node_modules/es-module-lexer": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.1.0.tgz", + "integrity": "sha512-n27zTYMjYu1aj4MjCWzSP7G9r75utsaoc8m61weK+W8JMBGGQybd43GstCXZ3WNmSFtGT9wi59qQTW6mhTR5LQ==", + "license": "MIT" + }, + "node_modules/webpack/node_modules/mime-db": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/why-is-node-running": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", + "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", + "dev": true, + "license": "MIT", + "dependencies": { + "siginfo": "^2.0.0", + "stackback": "0.0.2" + }, + "bin": { + "why-is-node-running": "cli.js" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "license": "ISC" + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs": { + "version": "17.7.3", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.3.tgz", + "integrity": "sha512-GZtjxm/J/4TSxuL3FNYjCmLktBTnIw/rVmKSIyKeYAZpmJB2ig9VauCC5xsa82GNKVKDAqpOn3KVzNt0zmrU0g==", + "license": "MIT", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/zod": { + "version": "3.25.76", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/zod-to-json-schema": { + "version": "3.25.2", + "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.25.2.tgz", + "integrity": "sha512-O/PgfnpT1xKSDeQYSCfRI5Gy3hPf91mKVDuYLUHZJMiDFptvP41MSnWofm8dnCm0256ZNfZIM7DSzuSMAFnjHA==", + "license": "ISC", + "peerDependencies": { + "zod": "^3.25.28 || ^4" + } + } + } +} diff --git a/connectors/shopify/package.json b/connectors/shopify/package.json new file mode 100644 index 0000000..f65bd59 --- /dev/null +++ b/connectors/shopify/package.json @@ -0,0 +1,52 @@ +{ + "name": "@shoploop/connector-shopify", + "version": "0.1.0", + "description": "First-class Shopify → product_twin.usda primitives + MCP (HTTP/SSE) + Temporal. Grip-from-below.", + "license": "UNLICENSED", + "private": true, + "type": "module", + "main": "./dist/index.js", + "types": "./dist/index.d.ts", + "exports": { + ".": { + "types": "./dist/index.d.ts", + "import": "./dist/index.js" + } + }, + "bin": { + "shoploop-shopify": "./bin/cli.js" + }, + "files": [ + "dist", + "bin" + ], + "scripts": { + "build": "tsc -p tsconfig.build.json", + "typecheck": "tsc -p tsconfig.json --noEmit", + "test": "vitest run", + "test:watch": "vitest", + "cli": "tsx src/cli.ts", + "mcp": "tsx src/cli.ts mcp", + "worker": "tsx src/cli.ts worker" + }, + "dependencies": { + "@modelcontextprotocol/sdk": "^1.18.0", + "@shopify/admin-api-client": "^1.1.0", + "@temporalio/activity": "^1.11.0", + "@temporalio/client": "^1.11.0", + "@temporalio/worker": "^1.11.0", + "@temporalio/workflow": "^1.11.0", + "express": "^4.21.0", + "zod": "^3.23.0" + }, + "devDependencies": { + "@types/express": "^4.17.21", + "@types/node": "^20.14.0", + "tsx": "^4.19.0", + "typescript": "^5.6.0", + "vitest": "^2.1.0" + }, + "engines": { + "node": ">=20" + } +} diff --git a/connectors/shopify/src/cli.ts b/connectors/shopify/src/cli.ts new file mode 100644 index 0000000..a42e8e2 --- /dev/null +++ b/connectors/shopify/src/cli.ts @@ -0,0 +1,60 @@ +import { loadConfig } from "./config.js"; +import { ingestCatalog } from "./primitives/ingest_catalog.js"; +import { startMcpServer } from "./mcp/transport.js"; +import { runWorker } from "./temporal/worker.js"; + +function flag(argv: string[], name: string): string | undefined { + const i = argv.indexOf(`--${name}`); + return i >= 0 && i + 1 < argv.length ? argv[i + 1] : undefined; +} + +const USAGE = `shoploop-shopify + +Commands: + ingest --shop [--since ] --out Catalog → product_twin.usda + mcp Boot MCP HTTP/SSE server on :8787 + worker Boot Temporal worker (queue: shoploop-shopify) +`; + +async function main(): Promise { + const [, , command, ...rest] = process.argv; + + switch (command) { + case "ingest": { + const shop = flag(rest, "shop") ?? process.env.SHOPIFY_SHOP; + const out = flag(rest, "out") ?? process.env.USD_OUT_DIR; + const since = flag(rest, "since"); + if (!shop || !out) { + console.error("ingest requires --shop and --out"); + process.exit(2); + } + const cfg = loadConfig({ shop }); + const report = await ingestCatalog({ + shop, + since, + outDir: out, + brandRulesPath: cfg.brandRulesPath, + }); + console.log(JSON.stringify(report, null, 2)); + console.log(`\n${report.count} twins written → ${out}`); + console.log(`manifest: ${report.manifestPath}`); + break; + } + case "mcp": { + startMcpServer(); + break; + } + case "worker": { + await runWorker(); + break; + } + default: + console.log(USAGE); + process.exit(command ? 2 : 0); + } +} + +main().catch((err) => { + console.error(err instanceof Error ? err.stack : String(err)); + process.exit(1); +}); diff --git a/connectors/shopify/src/client.ts b/connectors/shopify/src/client.ts new file mode 100644 index 0000000..ac00222 --- /dev/null +++ b/connectors/shopify/src/client.ts @@ -0,0 +1,268 @@ +import { createAdminApiClient } from "@shopify/admin-api-client"; +import type { + ShopifyImage, + ShopifyMedia3d, + ShopifyMetafield, + ShopifyOption, + ShopifyProduct, + ShopifyVariant, +} from "./types.js"; + +export interface ProductFilter { + tag?: string; + productType?: string; + vendor?: string; + status?: string; // ACTIVE | ARCHIVED | DRAFT + since?: string; // ISO; updated_at filter +} + +export interface ProductSummary { + id: string; + handle: string; + title: string; + status: string; + productType: string; + vendor: string; + tags: string[]; +} + +// The internal core surface. CLI, MCP tools, and Temporal activities all depend on +// THIS interface — a single source of truth, trivially mockable in tests. +export interface ShopifyClient { + listProducts(filter?: ProductFilter): Promise; + getProduct(id: string): Promise; + getVariants(id: string): Promise; + getMetafields(id: string): Promise; + updateMetafield(input: { + ownerId: string; + namespace: string; + key: string; + type: string; + value: string; + }): Promise; + // Full paginated catalog walk for ingest. + iterateProducts(filter?: ProductFilter): AsyncGenerator; +} + +const PRODUCT_FIELDS = ` + id + handle + title + description + productType + vendor + status + tags + updatedAt + options(first: 25) { name optionValues { name } } + variants(first: 100) { + nodes { + id + title + sku + price + inventoryQuantity + selectedOptions { name value } + } + } + images(first: 25) { nodes { id url altText } } + media(first: 25) { + nodes { + ... on Model3d { id sources { url format } } + } + } + metafields(first: 50) { nodes { namespace key type value } } +`; + +interface RawProduct { + id: string; + handle: string; + title: string; + description: string; + productType: string; + vendor: string; + status: string; + tags: string[]; + updatedAt: string; + options: { name: string; optionValues: { name: string }[] }[]; + variants: { nodes: RawVariant[] }; + images: { nodes: { id: string; url: string; altText: string | null }[] }; + media: { nodes: { id?: string; sources?: { url: string; format: string }[] }[] }; + metafields: { nodes: ShopifyMetafield[] }; +} + +interface RawVariant { + id: string; + title: string; + sku: string | null; + price: string; + inventoryQuantity: number | null; + selectedOptions: { name: string; value: string }[]; +} + +export function mapProduct(raw: RawProduct): ShopifyProduct { + const options: ShopifyOption[] = (raw.options ?? []).map((o) => ({ + name: o.name, + values: (o.optionValues ?? []).map((v) => v.name), + })); + const variants: ShopifyVariant[] = (raw.variants?.nodes ?? []).map((v) => ({ + id: v.id, + title: v.title, + sku: v.sku, + price: v.price, + inventoryQuantity: v.inventoryQuantity, + selectedOptions: v.selectedOptions ?? [], + })); + const images: ShopifyImage[] = (raw.images?.nodes ?? []).map((i) => ({ + id: i.id, + url: i.url, + altText: i.altText, + })); + const media3d: ShopifyMedia3d[] = (raw.media?.nodes ?? []) + .filter((m) => Array.isArray(m.sources) && m.sources.length > 0) + .map((m) => { + const sources = m.sources!; + const glb = sources.find((s) => s.format?.toLowerCase() === "glb"); + const chosen = glb ?? sources[0]; + return { id: m.id ?? "", sourceUrl: chosen.url, format: chosen.format }; + }); + return { + id: raw.id, + handle: raw.handle, + title: raw.title, + description: raw.description ?? "", + productType: raw.productType ?? "", + vendor: raw.vendor ?? "", + status: raw.status ?? "ACTIVE", + tags: raw.tags ?? [], + updatedAt: raw.updatedAt, + options, + variants, + images, + media3d, + metafields: raw.metafields?.nodes ?? [], + }; +} + +function buildQueryString(filter?: ProductFilter): string | undefined { + if (!filter) return undefined; + const parts: string[] = []; + if (filter.tag) parts.push(`tag:'${filter.tag}'`); + if (filter.productType) parts.push(`product_type:'${filter.productType}'`); + if (filter.vendor) parts.push(`vendor:'${filter.vendor}'`); + if (filter.status) parts.push(`status:${filter.status}`); + if (filter.since) parts.push(`updated_at:>='${filter.since}'`); + return parts.length ? parts.join(" AND ") : undefined; +} + +export interface AdminClientOptions { + shop: string; + adminToken: string; + apiVersion: string; +} + +export function createShopifyClient(opts: AdminClientOptions): ShopifyClient { + const api = createAdminApiClient({ + storeDomain: opts.shop, + accessToken: opts.adminToken, + apiVersion: opts.apiVersion, + }); + + async function request(query: string, variables?: Record): Promise { + const res = await api.request(query, variables ? { variables } : undefined); + if (res.errors) { + throw new Error(`shopify graphql error: ${JSON.stringify(res.errors)}`); + } + return res.data as T; + } + + return { + async listProducts(filter) { + const q = buildQueryString(filter); + const data = await request<{ products: { nodes: ProductSummary[] } }>( + `query ListProducts($q: String) { + products(first: 100, query: $q) { + nodes { id handle title status productType vendor tags } + } + }`, + { q: q ?? null }, + ); + return data.products.nodes; + }, + + async getProduct(id) { + const data = await request<{ product: RawProduct | null }>( + `query GetProduct($id: ID!) { product(id: $id) { ${PRODUCT_FIELDS} } }`, + { id }, + ); + return data.product ? mapProduct(data.product) : null; + }, + + async getVariants(id) { + const p = await this.getProduct(id); + return p?.variants ?? []; + }, + + async getMetafields(id) { + const data = await request<{ product: { metafields: { nodes: ShopifyMetafield[] } } | null }>( + `query GetMetafields($id: ID!) { + product(id: $id) { metafields(first: 100) { nodes { namespace key type value } } } + }`, + { id }, + ); + return data.product?.metafields.nodes ?? []; + }, + + async updateMetafield(input) { + const data = await request<{ + metafieldsSet: { + metafields: ShopifyMetafield[]; + userErrors: { field: string[]; message: string }[]; + }; + }>( + `mutation SetMetafield($metafields: [MetafieldsSetInput!]!) { + metafieldsSet(metafields: $metafields) { + metafields { namespace key type value } + userErrors { field message } + } + }`, + { + metafields: [ + { + ownerId: input.ownerId, + namespace: input.namespace, + key: input.key, + type: input.type, + value: input.value, + }, + ], + }, + ); + const errs = data.metafieldsSet.userErrors; + if (errs.length) throw new Error(`metafieldsSet failed: ${JSON.stringify(errs)}`); + const mf = data.metafieldsSet.metafields[0]; + if (!mf) throw new Error("metafieldsSet returned no metafield"); + return mf; + }, + + async *iterateProducts(filter) { + const q = buildQueryString(filter); + let cursor: string | null = null; + for (;;) { + const data: { products: { nodes: RawProduct[]; pageInfo: { hasNextPage: boolean; endCursor: string | null } } } = + await request( + `query AllProducts($q: String, $after: String) { + products(first: 50, query: $q, after: $after) { + nodes { ${PRODUCT_FIELDS} } + pageInfo { hasNextPage endCursor } + } + }`, + { q: q ?? null, after: cursor }, + ); + for (const node of data.products.nodes) yield mapProduct(node); + if (!data.products.pageInfo.hasNextPage) break; + cursor = data.products.pageInfo.endCursor; + } + }, + }; +} diff --git a/connectors/shopify/src/config.ts b/connectors/shopify/src/config.ts new file mode 100644 index 0000000..8e65aa6 --- /dev/null +++ b/connectors/shopify/src/config.ts @@ -0,0 +1,51 @@ +// Centralized env + secrets. Every entrypoint (CLI, MCP, worker, webhook) reads here. +// CONTRACT: brand_rules consumed downstream per open-imagine/contracts/product_twin.v1.json + +export interface ShopifyConnectorConfig { + shop: string; + adminToken: string; + apiVersion: string; + webhookSecret: string; + mcpToken: string; + temporalAddress: string; + temporalNamespace: string; + openImagineRouterUrl: string; + brandRulesPath: string; + usdOutDir: string; + mcpPort: number; +} + +function env(key: string, fallback?: string): string { + const v = process.env[key]; + if (v === undefined || v === "") { + if (fallback !== undefined) return fallback; + throw new Error(`missing required env: ${key}`); + } + return v; +} + +function optional(key: string, fallback: string): string { + const v = process.env[key]; + return v === undefined || v === "" ? fallback : v; +} + +// Lazy so importing the package never throws when env is absent (tests, type tooling). +export function loadConfig(overrides: Partial = {}): ShopifyConnectorConfig { + return { + shop: overrides.shop ?? env("SHOPIFY_SHOP"), + adminToken: overrides.adminToken ?? env("SHOPIFY_ADMIN_TOKEN"), + apiVersion: overrides.apiVersion ?? optional("SHOPIFY_API_VERSION", "2025-07"), + webhookSecret: overrides.webhookSecret ?? optional("SHOPIFY_WEBHOOK_SECRET", ""), + mcpToken: overrides.mcpToken ?? optional("SHOPLOOP_MCP_TOKEN", ""), + temporalAddress: overrides.temporalAddress ?? optional("TEMPORAL_ADDRESS", "localhost:7233"), + temporalNamespace: overrides.temporalNamespace ?? optional("TEMPORAL_NAMESPACE", "shoploop"), + openImagineRouterUrl: + overrides.openImagineRouterUrl ?? + optional("OPEN_IMAGINE_ROUTER_URL", "https://router.open-imagine.local"), + brandRulesPath: overrides.brandRulesPath ?? optional("BRAND_RULES_PATH", "/etc/shoploop/brand_rules.json"), + usdOutDir: overrides.usdOutDir ?? optional("USD_OUT_DIR", "/var/shoploop/twins"), + mcpPort: overrides.mcpPort ?? Number(optional("SHOPLOOP_MCP_PORT", "8787")), + }; +} + +export const TASK_QUEUE = "shoploop-shopify"; diff --git a/connectors/shopify/src/index.ts b/connectors/shopify/src/index.ts new file mode 100644 index 0000000..6e6950e --- /dev/null +++ b/connectors/shopify/src/index.ts @@ -0,0 +1,84 @@ +// @shoploop/connector-shopify — first-class Shopify → product_twin.usda primitives. +// CONTRACT: open-imagine/contracts/product_twin.v1.json +// CONTRACT: open-imagine/contracts/render_job.v1.json + +// Config +export { loadConfig, TASK_QUEUE, type ShopifyConnectorConfig } from "./config.js"; + +// Types +export type { + ShopifyProduct, + ShopifyVariant, + ShopifyOption, + ShopifyImage, + ShopifyMedia3d, + ShopifyMetafield, + IngestReport, + TwinManifest, +} from "./types.js"; +export { numericId } from "./types.js"; + +// Client +export { + createShopifyClient, + mapProduct, + type ShopifyClient, + type ProductFilter, + type ProductSummary, +} from "./client.js"; + +// USD layer +export { + emitProductTwin, + stripGeneratedAt, + GENERATED_AT_KEY, + type EmitOptions, +} from "./usd/emit_product_twin.js"; +export { inspectTwin, type TwinInspection, type PrimNode } from "./usd/inspect.js"; +export { manipulateVariantSet, type VariantOp, type ManipulateResult } from "./usd/manipulate.js"; +export { COMMERCE_ATTRS, BRAND_ATTRS, usdToken } from "./usd/schema.js"; + +// Primitive 1: ingest +export { + ingestCatalog, + writeProductTwin, + type IngestCatalogOptions, +} from "./primitives/ingest_catalog.js"; + +// Primitive 2: MCP +export { + createMcpServer, + buildApp, + startMcpServer, + ALL_TOOLS, + type ToolContext, + type ToolDef, +} from "./primitives/mcp_tools.js"; + +// Primitive 3: compose scene +export { + composeScene, + DEFAULT_BRAND_RULES, + type ComposeSceneOptions, + type ComposeSceneResult, + type ScenePreset, + type BrandRules, +} from "./primitives/compose_scene.js"; + +// Primitive 4: webhooks + Temporal +export { + verifyShopifyHmac, + registerWebhookRoute, + extractDispatchInput, +} from "./primitives/webhooks.js"; +export { + dispatchWebhook, + workflowId, + getTemporalClient, + clientStarter, + type ShopifyTopic, + type DispatchInput, + type WorkflowStarter, +} from "./temporal/dispatch.js"; +export * as workflows from "./temporal/workflows.js"; +export { runWorker } from "./temporal/worker.js"; diff --git a/connectors/shopify/src/mcp/server.ts b/connectors/shopify/src/mcp/server.ts new file mode 100644 index 0000000..59c8293 --- /dev/null +++ b/connectors/shopify/src/mcp/server.ts @@ -0,0 +1,34 @@ +import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; +import { ALL_TOOLS, type ToolContext } from "./tools/index.js"; + +export const MCP_SERVER_NAME = "shoploop-connector-shopify"; +export const MCP_SERVER_VERSION = "0.1.0"; + +// Builds an McpServer with all 7 commerce tools registered against a shared core context. +// Used by both the HTTP/SSE transport and in-process tests. +export function createMcpServer(ctx: ToolContext): McpServer { + const server = new McpServer( + { name: MCP_SERVER_NAME, version: MCP_SERVER_VERSION }, + { capabilities: { tools: {} } }, + ); + + for (const tool of ALL_TOOLS) { + server.tool(tool.name, tool.description, tool.shape, async (args: Record) => { + try { + const result = await tool.handler(ctx, args ?? {}); + const content: { type: "text"; text: string }[] = [ + { type: "text", text: JSON.stringify(result.data) }, + ]; + if (result.text) content.push({ type: "text", text: result.text }); + return { content }; + } catch (err) { + return { + isError: true, + content: [{ type: "text", text: `error: ${(err as Error).message}` }], + }; + } + }); + } + + return server; +} diff --git a/connectors/shopify/src/mcp/tools/get_metafields.ts b/connectors/shopify/src/mcp/tools/get_metafields.ts new file mode 100644 index 0000000..0d8f70e --- /dev/null +++ b/connectors/shopify/src/mcp/tools/get_metafields.ts @@ -0,0 +1,18 @@ +import { z } from "zod"; +import type { ToolDef } from "./tool.js"; + +export const getMetafields: ToolDef = { + name: "get_metafields", + description: + "Get all metafields for a product, including the shoploop.* namespace (usd_path, brand_score, last_render).", + shape: { + productId: z.string().describe("gid://shopify/Product/"), + namespace: z.string().optional().describe("filter to a single namespace, e.g. shoploop"), + }, + async handler(ctx, args) { + const all = await ctx.client.getMetafields(args.productId as string); + const ns = args.namespace as string | undefined; + const metafields = ns ? all.filter((m) => m.namespace === ns) : all; + return { data: { productId: args.productId, count: metafields.length, metafields } }; + }, +}; diff --git a/connectors/shopify/src/mcp/tools/get_product.ts b/connectors/shopify/src/mcp/tools/get_product.ts new file mode 100644 index 0000000..6a91d64 --- /dev/null +++ b/connectors/shopify/src/mcp/tools/get_product.ts @@ -0,0 +1,16 @@ +import { z } from "zod"; +import type { ToolDef } from "./tool.js"; + +export const getProduct: ToolDef = { + name: "get_product", + description: + "Get a full Shopify product including variants, images, metafields, and 3D media by gid.", + shape: { + productId: z.string().describe("gid://shopify/Product/"), + }, + async handler(ctx, args) { + const product = await ctx.client.getProduct(args.productId as string); + if (!product) return { data: { found: false, productId: args.productId } }; + return { data: { found: true, product } }; + }, +}; diff --git a/connectors/shopify/src/mcp/tools/get_variants.ts b/connectors/shopify/src/mcp/tools/get_variants.ts new file mode 100644 index 0000000..c6408bc --- /dev/null +++ b/connectors/shopify/src/mcp/tools/get_variants.ts @@ -0,0 +1,20 @@ +import { z } from "zod"; +import type { ToolDef } from "./tool.js"; + +export const getVariants: ToolDef = { + name: "get_variants", + description: "Get the variant matrix (options × inventory × price) for a product.", + shape: { + productId: z.string().describe("gid://shopify/Product/"), + }, + async handler(ctx, args) { + const variants = await ctx.client.getVariants(args.productId as string); + return { + data: { + productId: args.productId, + count: variants.length, + variants, + }, + }; + }, +}; diff --git a/connectors/shopify/src/mcp/tools/index.ts b/connectors/shopify/src/mcp/tools/index.ts new file mode 100644 index 0000000..8e7380d --- /dev/null +++ b/connectors/shopify/src/mcp/tools/index.ts @@ -0,0 +1,20 @@ +import type { ToolDef } from "./tool.js"; +import { listProducts } from "./list_products.js"; +import { getProduct } from "./get_product.js"; +import { getVariants } from "./get_variants.js"; +import { getMetafields } from "./get_metafields.js"; +import { updateMetafield } from "./update_metafield.js"; +import { inspectProductTwin } from "./inspect_product_twin.js"; +import { manipulateVariantSetTool } from "./manipulate_variant_set.js"; + +export const ALL_TOOLS: ToolDef[] = [ + listProducts, + getProduct, + getVariants, + getMetafields, + updateMetafield, + inspectProductTwin, + manipulateVariantSetTool, +]; + +export type { ToolContext, ToolDef, ToolResult } from "./tool.js"; diff --git a/connectors/shopify/src/mcp/tools/inspect_product_twin.ts b/connectors/shopify/src/mcp/tools/inspect_product_twin.ts new file mode 100644 index 0000000..ccc8a6e --- /dev/null +++ b/connectors/shopify/src/mcp/tools/inspect_product_twin.ts @@ -0,0 +1,39 @@ +import { readFile } from "node:fs/promises"; +import { isAbsolute, join } from "node:path"; +import { z } from "zod"; +import { inspectTwin } from "../../usd/inspect.js"; +import { usdToken } from "../../usd/schema.js"; +import type { ToolContext, ToolDef } from "./tool.js"; + +export function resolveTwinPath(ctx: ToolContext, args: Record): string { + const path = args.path as string | undefined; + if (path) return isAbsolute(path) ? path : join(ctx.usdOutDir, path); + const handle = args.handle as string | undefined; + if (!handle) throw new Error("inspect_product_twin requires `handle` or `path`"); + return join(ctx.usdOutDir, `${usdToken(handle)}.usda`); +} + +export const inspectProductTwin: ToolDef = { + name: "inspect_product_twin", + description: + "Open an emitted product_twin .usda and return its prim tree, variant sets, and commerce/brand attrs. Read-only.", + shape: { + handle: z.string().optional().describe("product handle; resolves .usda in USD_OUT_DIR"), + path: z.string().optional().describe("explicit .usda path (absolute or relative to USD_OUT_DIR)"), + }, + async handler(ctx, args) { + const twinPath = resolveTwinPath(ctx, args); + const usda = await readFile(twinPath, "utf8"); + const inspection = inspectTwin(usda); + return { + data: { + path: twinPath, + defaultPrim: inspection.defaultPrim, + prims: inspection.prims, + variantSets: inspection.variantSets, + attrs: inspection.attrs, + }, + text: inspection.summary, + }; + }, +}; diff --git a/connectors/shopify/src/mcp/tools/list_products.ts b/connectors/shopify/src/mcp/tools/list_products.ts new file mode 100644 index 0000000..fbff201 --- /dev/null +++ b/connectors/shopify/src/mcp/tools/list_products.ts @@ -0,0 +1,23 @@ +import { z } from "zod"; +import type { ToolDef } from "./tool.js"; + +export const listProducts: ToolDef = { + name: "list_products", + description: + "List Shopify products filtered by tag, productType, vendor, or status. Returns ids + handles.", + shape: { + tag: z.string().optional(), + productType: z.string().optional(), + vendor: z.string().optional(), + status: z.enum(["ACTIVE", "ARCHIVED", "DRAFT"]).optional(), + }, + async handler(ctx, args) { + const products = await ctx.client.listProducts({ + tag: args.tag as string | undefined, + productType: args.productType as string | undefined, + vendor: args.vendor as string | undefined, + status: args.status as string | undefined, + }); + return { data: { count: products.length, products } }; + }, +}; diff --git a/connectors/shopify/src/mcp/tools/manipulate_variant_set.ts b/connectors/shopify/src/mcp/tools/manipulate_variant_set.ts new file mode 100644 index 0000000..eec4a95 --- /dev/null +++ b/connectors/shopify/src/mcp/tools/manipulate_variant_set.ts @@ -0,0 +1,49 @@ +import { readFile, writeFile } from "node:fs/promises"; +import { z } from "zod"; +import { inspectTwin } from "../../usd/inspect.js"; +import { manipulateVariantSet } from "../../usd/manipulate.js"; +import type { ToolDef } from "./tool.js"; +import { resolveTwinPath } from "./inspect_product_twin.js"; + +export const manipulateVariantSetTool: ToolDef = { + name: "manipulate_variant_set", + description: + "Add, remove, or rename a variant on a product_twin USD stage (agent-driven scene edit). Writes the .usda back.", + shape: { + handle: z.string().optional(), + path: z.string().optional(), + action: z.enum(["add", "remove", "rename"]), + variantSet: z.string().describe("e.g. size"), + variant: z.string().describe("variant token to act on"), + to: z.string().optional().describe("new name (rename only)"), + optionValue: z.string().optional().describe("display value (add only)"), + dryRun: z.boolean().optional().describe("compute change without writing"), + }, + async handler(ctx, args) { + const twinPath = resolveTwinPath(ctx, args); + const usda = await readFile(twinPath, "utf8"); + const action = args.action as "add" | "remove" | "rename"; + const result = manipulateVariantSet(usda, { + action, + variantSet: args.variantSet as string, + variant: args.variant as string, + to: args.to as string, + optionValue: args.optionValue as string, + } as Parameters[1]); + + if (result.changed && !args.dryRun) { + await writeFile(twinPath, result.usda, "utf8"); + } + const inspection = inspectTwin(result.usda); + return { + data: { + path: twinPath, + changed: result.changed, + note: result.note, + written: result.changed && !args.dryRun, + variantSets: inspection.variantSets, + }, + text: inspection.summary, + }; + }, +}; diff --git a/connectors/shopify/src/mcp/tools/tool.ts b/connectors/shopify/src/mcp/tools/tool.ts new file mode 100644 index 0000000..2305df7 --- /dev/null +++ b/connectors/shopify/src/mcp/tools/tool.ts @@ -0,0 +1,20 @@ +import type { ZodRawShape } from "zod"; +import type { ShopifyClient } from "../../client.js"; + +export interface ToolContext { + client: ShopifyClient; + usdOutDir: string; + brandRulesPath: string; +} + +export interface ToolResult { + data: unknown; // JSON-serializable payload + text?: string; // optional LLM-facing textual summary (daslabhq/openusd-mcp pattern) +} + +export interface ToolDef { + name: string; + description: string; + shape: ZodRawShape; + handler: (ctx: ToolContext, args: Record) => Promise; +} diff --git a/connectors/shopify/src/mcp/tools/update_metafield.ts b/connectors/shopify/src/mcp/tools/update_metafield.ts new file mode 100644 index 0000000..61d5a1c --- /dev/null +++ b/connectors/shopify/src/mcp/tools/update_metafield.ts @@ -0,0 +1,25 @@ +import { z } from "zod"; +import type { ToolDef } from "./tool.js"; + +export const updateMetafield: ToolDef = { + name: "update_metafield", + description: + "Write a metafield. Used for shoploop.usd_path, shoploop.brand_score, shoploop.last_render.", + shape: { + productId: z.string().describe("gid://shopify/Product/"), + namespace: z.string().default("shoploop"), + key: z.string(), + type: z.string().default("single_line_text_field"), + value: z.string(), + }, + async handler(ctx, args) { + const metafield = await ctx.client.updateMetafield({ + ownerId: args.productId as string, + namespace: (args.namespace as string) ?? "shoploop", + key: args.key as string, + type: (args.type as string) ?? "single_line_text_field", + value: args.value as string, + }); + return { data: { ok: true, metafield } }; + }, +}; diff --git a/connectors/shopify/src/mcp/transport.ts b/connectors/shopify/src/mcp/transport.ts new file mode 100644 index 0000000..0ac85ab --- /dev/null +++ b/connectors/shopify/src/mcp/transport.ts @@ -0,0 +1,92 @@ +import express, { type Express, type NextFunction, type Request, type Response } from "express"; +import { SSEServerTransport } from "@modelcontextprotocol/sdk/server/sse.js"; +import { createShopifyClient } from "../client.js"; +import { loadConfig, type ShopifyConnectorConfig } from "../config.js"; +import { registerWebhookRoute } from "../primitives/webhooks.js"; +import { createMcpServer } from "./server.js"; +import type { ToolContext } from "./tools/index.js"; + +const CORS_ALLOWLIST = [ + "https://open-employee.local", + "https://agentframe.local", + "http://localhost:3000", +]; + +function bearerAuth(token: string) { + return (req: Request, res: Response, next: NextFunction) => { + if (!token) return next(); // unset token => open (dev only); set SHOPLOOP_MCP_TOKEN in prod + const header = req.header("authorization") ?? ""; + const provided = header.startsWith("Bearer ") ? header.slice(7) : ""; + if (provided !== token) { + res.status(401).json({ error: "unauthorized" }); + return; + } + next(); + }; +} + +function cors(req: Request, res: Response, next: NextFunction) { + const origin = req.header("origin"); + if (origin && CORS_ALLOWLIST.includes(origin)) { + res.header("Access-Control-Allow-Origin", origin); + res.header("Access-Control-Allow-Headers", "authorization, content-type"); + res.header("Access-Control-Allow-Methods", "GET, POST, OPTIONS"); + } + if (req.method === "OPTIONS") { + res.sendStatus(204); + return; + } + next(); +} + +export function buildApp(cfg: ShopifyConnectorConfig): Express { + const app = express(); + app.use(cors); + + const ctx: ToolContext = { + client: createShopifyClient({ + shop: cfg.shop, + adminToken: cfg.adminToken, + apiVersion: cfg.apiVersion, + }), + usdOutDir: cfg.usdOutDir, + brandRulesPath: cfg.brandRulesPath, + }; + + const transports = new Map(); + + app.get("/health", (_req, res) => res.json({ ok: true, server: "shoploop-connector-shopify" })); + + // SSE stream: client connects here, receives a sessionId, then POSTs to /messages. + app.get("/sse", bearerAuth(cfg.mcpToken), async (_req, res) => { + const transport = new SSEServerTransport("/messages", res); + transports.set(transport.sessionId, transport); + res.on("close", () => transports.delete(transport.sessionId)); + const server = createMcpServer(ctx); + await server.connect(transport); + }); + + app.post("/messages", bearerAuth(cfg.mcpToken), express.json(), async (req, res) => { + const sessionId = String(req.query.sessionId ?? ""); + const transport = transports.get(sessionId); + if (!transport) { + res.status(404).json({ error: "unknown sessionId" }); + return; + } + await transport.handlePostMessage(req, res, req.body); + }); + + // Primitive 4 webhook endpoint shares the same Express app. + registerWebhookRoute(app, cfg); + + return app; +} + +export function startMcpServer(overrides?: Partial): { port: number } { + const cfg = loadConfig(overrides); + const app = buildApp(cfg); + app.listen(cfg.mcpPort, () => { + console.log(`[shoploop-shopify] MCP HTTP/SSE on :${cfg.mcpPort} (/sse, /messages, /webhooks/shopify)`); + }); + return { port: cfg.mcpPort }; +} diff --git a/connectors/shopify/src/primitives/compose_scene.ts b/connectors/shopify/src/primitives/compose_scene.ts new file mode 100644 index 0000000..d2e7144 --- /dev/null +++ b/connectors/shopify/src/primitives/compose_scene.ts @@ -0,0 +1,193 @@ +import { mkdir, readFile, writeFile } from "node:fs/promises"; +import { basename, dirname, join } from "node:path"; +import { inspectTwin } from "../usd/inspect.js"; +import { usdString, usdStringArray } from "../usd/schema.js"; +import type { TwinManifest } from "../types.js"; + +export type ScenePreset = "hero" | "lifestyle" | "pdp" | "ugc"; + +export interface BrandRules { + palette?: string[]; + allowed_materials?: string[]; + allowed_lighting_rigs?: string[]; + depth_of_field_band?: [number, number]; + copy_density_max?: number; + rules_version?: string; +} + +// Kinfolk-test defaults: soft, editorial, sparse. If a scene wouldn't fit a Kinfolk +// spread, these constraints failed. See DESIGN.md. +export const DEFAULT_BRAND_RULES: Required = { + palette: ["#F5F3EE", "#0A0A0A", "#C8F135"], + allowed_materials: ["matte_ceramic", "raw_linen", "brushed_aluminum", "kraft_paper"], + allowed_lighting_rigs: ["softbox_window", "overcast_dome"], + depth_of_field_band: [0.045, 0.09], + copy_density_max: 0.18, + rules_version: "kinfolk-default-1", +}; + +export interface ComposeSceneOptions { + productId: string; + brandRulesPath: string; + outStage: string; // e.g. scenes/__hero.usda + preset?: ScenePreset; + usdOutDir?: string; // where _manifest.json + twins live; for productId → twin resolution + twinPath?: string; // explicit override (skips manifest lookup) +} + +export interface ComposeSceneResult { + stagePath: string; + rulesApplied: string[]; + brandRulesLayer: string; + presetLayer: string; + twinLayer: string; +} + +async function loadBrandRules(path: string): Promise> { + try { + const raw = JSON.parse(await readFile(path, "utf8")) as BrandRules; + return { ...DEFAULT_BRAND_RULES, ...raw }; + } catch { + return DEFAULT_BRAND_RULES; + } +} + +async function resolveTwinPath(opts: ComposeSceneOptions): Promise { + if (opts.twinPath) return opts.twinPath; + const dir = opts.usdOutDir; + if (!dir) throw new Error("composeScene needs twinPath or usdOutDir to locate product_twin"); + const manifest = JSON.parse(await readFile(join(dir, "_manifest.json"), "utf8")) as TwinManifest; + const entry = manifest.entries.find((e) => e.shopifyId === opts.productId); + if (!entry) throw new Error(`no product_twin for ${opts.productId} in ${dir}/_manifest.json`); + return entry.usdaPath; +} + +function presetParams(preset: ScenePreset): { camera: string; copyDensity: number; dof: number } { + switch (preset) { + case "hero": + return { camera: "three_quarter_low", copyDensity: 0.08, dof: 0.06 }; + case "lifestyle": + return { camera: "eye_level_context", copyDensity: 0.12, dof: 0.075 }; + case "pdp": + return { camera: "orthographic_front", copyDensity: 0.05, dof: 0.05 }; + case "ugc": + return { camera: "handheld_45", copyDensity: 0.16, dof: 0.085 }; + } +} + +function clampDof(dof: number, band: [number, number]): { value: number; clamped: boolean } { + const [lo, hi] = band; + if (dof < lo) return { value: lo, clamped: true }; + if (dof > hi) return { value: hi, clamped: true }; + return { value: dof, clamped: false }; +} + +function emitBrandRulesLayer(rules: Required): string { + const out: string[] = []; + out.push("#usda 1.0"); + out.push("("); + out.push(' defaultPrim = "BrandRules"'); + out.push(")"); + out.push(""); + out.push('def Scope "BrandRules"'); + out.push("{"); + out.push(` custom string[] brand:palette = ${usdStringArray(rules.palette)}`); + out.push(` custom string[] brand:allowed_materials = ${usdStringArray(rules.allowed_materials)}`); + out.push(` custom string[] brand:allowed_lighting_rigs = ${usdStringArray(rules.allowed_lighting_rigs)}`); + out.push( + ` custom float2 brand:depth_of_field_band = (${rules.depth_of_field_band[0]}, ${rules.depth_of_field_band[1]})`, + ); + out.push(` custom float brand:copy_density_max = ${rules.copy_density_max}`); + out.push(` custom string brand:rules_version = ${usdString(rules.rules_version)}`); + out.push("}"); + out.push(""); + return out.join("\n"); +} + +function emitPresetLayer( + preset: ScenePreset, + params: ReturnType, + dof: number, +): string { + const out: string[] = []; + out.push("#usda 1.0"); + out.push("("); + out.push(' defaultPrim = "ScenePreset"'); + out.push(")"); + out.push(""); + out.push('def Scope "ScenePreset"'); + out.push("{"); + out.push(` custom token scene:preset = ${usdString(preset)}`); + out.push(` custom token scene:camera = ${usdString(params.camera)}`); + out.push(` custom float scene:depth_of_field = ${dof}`); + out.push(` custom float scene:copy_density = ${params.copyDensity}`); + out.push("}"); + out.push(""); + return out.join("\n"); +} + +// Primitive 3: brand-constraint enforcement at the USD layer. Emits a composed stage +// that sublayers [brand_rules, product_twin, scene_preset]. Does NOT render. +export async function composeScene(opts: ComposeSceneOptions): Promise { + const preset = opts.preset ?? "hero"; + const rules = await loadBrandRules(opts.brandRulesPath); + const twinPath = await resolveTwinPath(opts); + const twinUsda = await readFile(twinPath, "utf8"); + const twin = inspectTwin(twinUsda); + + const params = presetParams(preset); + const dof = clampDof(params.dof, rules.depth_of_field_band); + const rulesApplied: string[] = [ + `palette:${rules.palette.length}`, + `materials:allow(${rules.allowed_materials.length})`, + `lighting:allow(${rules.allowed_lighting_rigs.length})`, + `dof:${rules.depth_of_field_band[0]}-${rules.depth_of_field_band[1]}${dof.clamped ? ":clamped" : ""}`, + `copy_density<=${rules.copy_density_max}`, + "kinfolk_gate", + ]; + + const outDir = dirname(opts.outStage); + await mkdir(outDir, { recursive: true }); + const stem = basename(opts.outStage).replace(/\.usda$/, ""); + const brandLayerPath = join(outDir, `${stem}__brand_rules.usda`); + const presetLayerPath = join(outDir, `${stem}__preset.usda`); + + await writeFile(brandLayerPath, emitBrandRulesLayer(rules), "utf8"); + await writeFile(presetLayerPath, emitPresetLayer(preset, params, dof.value), "utf8"); + + const root = twin.defaultPrim ?? "Product"; + const composed: string[] = []; + composed.push("#usda 1.0"); + composed.push("("); + composed.push(` defaultPrim = "${root}"`); + composed.push(" subLayers = ["); + composed.push(` @${basename(brandLayerPath)}@,`); + composed.push(` @${twinPath}@,`); + composed.push(` @${basename(presetLayerPath)}@`); + composed.push(" ]"); + composed.push(" customLayerData = {"); + composed.push(` string "scene:preset" = ${usdString(preset)}`); + composed.push(` string "brand:rules_ref" = ${usdString(opts.brandRulesPath)}`); + composed.push(` string "brand:rules_version" = ${usdString(rules.rules_version)}`); + composed.push(" }"); + composed.push(")"); + composed.push(""); + // Brand-constrained overrides applied to the product twin's root prim. + composed.push(`over "${root}"`); + composed.push("{"); + composed.push(` custom token scene:preset = ${usdString(preset)}`); + composed.push(` custom float scene:depth_of_field = ${dof.value}`); + composed.push(` custom string[] brand:rules_applied = ${usdStringArray(rulesApplied)}`); + composed.push("}"); + composed.push(""); + + await writeFile(opts.outStage, composed.join("\n"), "utf8"); + + return { + stagePath: opts.outStage, + rulesApplied, + brandRulesLayer: brandLayerPath, + presetLayer: presetLayerPath, + twinLayer: twinPath, + }; +} diff --git a/connectors/shopify/src/primitives/ingest_catalog.ts b/connectors/shopify/src/primitives/ingest_catalog.ts new file mode 100644 index 0000000..49c61cd --- /dev/null +++ b/connectors/shopify/src/primitives/ingest_catalog.ts @@ -0,0 +1,71 @@ +import { mkdir, writeFile } from "node:fs/promises"; +import { join } from "node:path"; +import { createShopifyClient, type ShopifyClient } from "../client.js"; +import { loadConfig } from "../config.js"; +import { emitProductTwin } from "../usd/emit_product_twin.js"; +import { usdToken } from "../usd/schema.js"; +import type { IngestReport, ShopifyProduct, TwinManifest } from "../types.js"; + +export interface IngestCatalogOptions { + shop: string; + since?: string; + outDir: string; + brandRulesPath: string; + // injectable for tests; defaults to a live Admin GraphQL client from env + client?: ShopifyClient; +} + +// Primitive 1: catalog → product_twin.usda. Pure-ish: pulls products, emits one .usda each. +export async function ingestCatalog(opts: IngestCatalogOptions): Promise { + const client = + opts.client ?? + createShopifyClient({ + shop: opts.shop, + adminToken: loadConfig({ shop: opts.shop }).adminToken, + apiVersion: loadConfig({ shop: opts.shop }).apiVersion, + }); + + await mkdir(opts.outDir, { recursive: true }); + + const written: IngestReport["written"] = []; + const skipped: IngestReport["skipped"] = []; + const manifest: TwinManifest = { + shop: opts.shop, + generatedAt: new Date().toISOString(), + entries: [], + }; + + for await (const product of client.iterateProducts({ since: opts.since })) { + try { + const { usdaPath } = await writeProductTwin(product, opts); + written.push({ productId: product.id, handle: product.handle, usdaPath }); + manifest.entries.push({ + shopifyId: product.id, + handle: product.handle, + usdaPath, + updatedAt: product.updatedAt, + }); + } catch (err) { + skipped.push({ productId: product.id, reason: (err as Error).message }); + } + } + + const manifestPath = join(opts.outDir, "_manifest.json"); + await writeFile(manifestPath, JSON.stringify(manifest, null, 2) + "\n", "utf8"); + + return { shop: opts.shop, count: written.length, written, manifestPath, skipped }; +} + +// Single-product emit reused by Temporal ReingestProductWorkflow (no duplication). +export async function writeProductTwin( + product: ShopifyProduct, + opts: { shop: string; outDir: string; brandRulesPath: string }, +): Promise<{ usdaPath: string; usda: string }> { + const usda = emitProductTwin(product, { + shop: opts.shop, + brandRulesPath: opts.brandRulesPath, + }); + const usdaPath = join(opts.outDir, `${usdToken(product.handle)}.usda`); + await writeFile(usdaPath, usda, "utf8"); + return { usdaPath, usda }; +} diff --git a/connectors/shopify/src/primitives/mcp_tools.ts b/connectors/shopify/src/primitives/mcp_tools.ts new file mode 100644 index 0000000..ce6491c --- /dev/null +++ b/connectors/shopify/src/primitives/mcp_tools.ts @@ -0,0 +1,6 @@ +// Primitive 2 barrel: MCP read/inspect/manipulate surface. Same internal core as the +// CLI and Temporal paths — the MCP server simply exposes it over HTTP/SSE. +export { createMcpServer, MCP_SERVER_NAME, MCP_SERVER_VERSION } from "../mcp/server.js"; +export { buildApp, startMcpServer } from "../mcp/transport.js"; +export { ALL_TOOLS } from "../mcp/tools/index.js"; +export type { ToolContext, ToolDef, ToolResult } from "../mcp/tools/index.js"; diff --git a/connectors/shopify/src/primitives/webhooks.ts b/connectors/shopify/src/primitives/webhooks.ts new file mode 100644 index 0000000..2edbc6c --- /dev/null +++ b/connectors/shopify/src/primitives/webhooks.ts @@ -0,0 +1,104 @@ +import { createHmac, timingSafeEqual } from "node:crypto"; +import type { Express, Request, Response } from "express"; +import express from "express"; +import type { ShopifyConnectorConfig } from "../config.js"; +import { + clientStarter, + dispatchWebhook, + getTemporalClient, + type DispatchInput, + type ShopifyTopic, + type WorkflowStarter, +} from "../temporal/dispatch.js"; +import { numericId } from "../types.js"; + +const SUPPORTED: ShopifyTopic[] = [ + "products/create", + "products/update", + "products/delete", + "inventory_levels/update", +]; + +// HMAC verify per Shopify: base64(HMAC-SHA256(rawBody, secret)) === X-Shopify-Hmac-Sha256. +export function verifyShopifyHmac(rawBody: Buffer | string, hmacHeader: string, secret: string): boolean { + if (!hmacHeader || !secret) return false; + const digest = createHmac("sha256", secret) + .update(typeof rawBody === "string" ? Buffer.from(rawBody, "utf8") : rawBody) + .digest(); + let provided: Buffer; + try { + provided = Buffer.from(hmacHeader, "base64"); + } catch { + return false; + } + return digest.length === provided.length && timingSafeEqual(digest, provided); +} + +interface ShopifyWebhookPayload { + id?: number | string; + admin_graphql_api_id?: string; + updated_at?: string; + available?: number; + inventory_item_id?: number | string; +} + +export function extractDispatchInput( + shop: string, + topic: ShopifyTopic, + payload: ShopifyWebhookPayload, +): DispatchInput { + const productId = + payload.admin_graphql_api_id ?? + (payload.id !== undefined ? `gid://shopify/Product/${numericId(String(payload.id))}` : "unknown"); + const updatedAt = payload.updated_at ?? new Date().toISOString(); + const input: DispatchInput = { shop, topic, productId, updatedAt }; + if (topic === "inventory_levels/update") input.inventory = Number(payload.available ?? 0); + return input; +} + +export interface WebhookRouteDeps { + // injectable for tests; defaults to the live Temporal client + starter?: WorkflowStarter; +} + +// Mounts POST /webhooks/shopify on an existing Express app. +export function registerWebhookRoute( + app: Express, + cfg: ShopifyConnectorConfig, + deps: WebhookRouteDeps = {}, +): void { + app.post( + "/webhooks/shopify", + express.raw({ type: "*/*", limit: "2mb" }), + async (req: Request, res: Response) => { + const raw = req.body as Buffer; + const hmac = req.header("x-shopify-hmac-sha256") ?? ""; + if (!verifyShopifyHmac(raw, hmac, cfg.webhookSecret)) { + res.status(401).json({ error: "invalid hmac" }); + return; + } + const topic = (req.header("x-shopify-topic") ?? "") as ShopifyTopic; + if (!SUPPORTED.includes(topic)) { + res.status(202).json({ ignored: topic }); + return; + } + let payload: ShopifyWebhookPayload; + try { + payload = JSON.parse(raw.toString("utf8")) as ShopifyWebhookPayload; + } catch { + res.status(400).json({ error: "invalid json" }); + return; + } + const shop = req.header("x-shopify-shop-domain") ?? cfg.shop; + const input = extractDispatchInput(shop, topic, payload); + + try { + const starter = deps.starter ?? clientStarter(await getTemporalClient(cfg)); + const started = await dispatchWebhook(starter, input); + res.status(200).json({ ok: true, started }); + } catch (err) { + res.status(500).json({ error: (err as Error).message }); + } + }, + ); +} diff --git a/connectors/shopify/src/temporal/activities.ts b/connectors/shopify/src/temporal/activities.ts new file mode 100644 index 0000000..1765704 --- /dev/null +++ b/connectors/shopify/src/temporal/activities.ts @@ -0,0 +1,143 @@ +import { readFile, rm, writeFile } from "node:fs/promises"; +import { join } from "node:path"; +import { createShopifyClient } from "../client.js"; +import { loadConfig } from "../config.js"; +import { composeScene, type ScenePreset } from "../primitives/compose_scene.js"; +import { writeProductTwin } from "../primitives/ingest_catalog.js"; +import { usdToken } from "../usd/schema.js"; +import { numericId } from "../types.js"; + +const PRESETS: ScenePreset[] = ["hero", "lifestyle", "pdp", "ugc"]; + +function core() { + const cfg = loadConfig(); + const client = createShopifyClient({ + shop: cfg.shop, + adminToken: cfg.adminToken, + apiVersion: cfg.apiVersion, + }); + return { cfg, client }; +} + +export async function ingestSingleProduct(input: { productId: string }): Promise<{ + usdaPath: string; + handle: string; +}> { + const { cfg, client } = core(); + const product = await client.getProduct(input.productId); + if (!product) throw new Error(`product ${input.productId} not found`); + const { usdaPath } = await writeProductTwin(product, { + shop: cfg.shop, + outDir: cfg.usdOutDir, + brandRulesPath: cfg.brandRulesPath, + }); + return { usdaPath, handle: product.handle }; +} + +export async function setUsdPathMetafield(input: { + productId: string; + usdaPath: string; +}): Promise { + const { client } = core(); + await client.updateMetafield({ + ownerId: input.productId, + namespace: "shoploop", + key: "usd_path", + type: "single_line_text_field", + value: input.usdaPath, + }); +} + +export async function clearUsdMetafields(input: { productId: string }): Promise { + const { client } = core(); + await client.updateMetafield({ + ownerId: input.productId, + namespace: "shoploop", + key: "usd_path", + type: "single_line_text_field", + value: "", + }); +} + +export async function composeAllPresets(input: { + productId: string; + handle: string; +}): Promise<{ preset: ScenePreset; stagePath: string }[]> { + const { cfg } = core(); + const out: { preset: ScenePreset; stagePath: string }[] = []; + for (const preset of PRESETS) { + const outStage = join(cfg.usdOutDir, "scenes", `${usdToken(input.handle)}__${preset}.usda`); + const res = await composeScene({ + productId: input.productId, + brandRulesPath: cfg.brandRulesPath, + outStage, + preset, + usdOutDir: cfg.usdOutDir, + }); + out.push({ preset, stagePath: res.stagePath }); + } + return out; +} + +// Enqueue a render job in open-imagine. CONTRACT: open-imagine/contracts/render_job.v1.json +export async function enqueueRenderJob(input: { + productId: string; + handle: string; + stagePath: string; + preset: string; +}): Promise<{ accepted: boolean; status: number }> { + const { cfg } = core(); + const body = { + schema: "render_job.v1", + source: "shoploop/connector-shopify", + product_id: input.productId, + handle: input.handle, + stage_path: input.stagePath, + preset: input.preset, + tier: "flux_pro", + }; + const res = await fetch(`${cfg.openImagineRouterUrl}/render_jobs`, { + method: "POST", + headers: { "content-type": "application/json" }, + body: JSON.stringify(body), + }); + return { accepted: res.ok, status: res.status }; +} + +export async function purgeTwin(input: { handle: string }): Promise<{ removed: boolean }> { + const { cfg } = core(); + const path = join(cfg.usdOutDir, `${usdToken(input.handle)}.usda`); + try { + await rm(path, { force: true }); + return { removed: true }; + } catch { + return { removed: false }; + } +} + +// UpdateCommerceAttrWorkflow activity: patch commerce:inventory on the USD stage only (cheap). +export async function patchCommerceInventory(input: { + handle: string; + inventory: number; +}): Promise<{ patched: boolean }> { + const { cfg } = core(); + const path = join(cfg.usdOutDir, `${usdToken(input.handle)}.usda`); + let usda: string; + try { + usda = await readFile(path, "utf8"); + } catch { + return { patched: false }; + } + const next = usda.replace( + /(custom int commerce:inventory = )(-?\d+)/, + `$1${Math.trunc(input.inventory)}`, + ); + await writeFile(path, next, "utf8"); + return { patched: next !== usda }; +} + +export async function resolveHandle(input: { productId: string }): Promise { + const { client } = core(); + const product = await client.getProduct(input.productId); + return product ? product.handle : numericId(input.productId); +} diff --git a/connectors/shopify/src/temporal/dispatch.ts b/connectors/shopify/src/temporal/dispatch.ts new file mode 100644 index 0000000..5f5d07b --- /dev/null +++ b/connectors/shopify/src/temporal/dispatch.ts @@ -0,0 +1,99 @@ +import { Connection, Client, type WorkflowHandle } from "@temporalio/client"; +import { loadConfig, TASK_QUEUE, type ShopifyConnectorConfig } from "../config.js"; +import { + PurgeProductTwinWorkflow, + RecomposeCreativePackWorkflow, + ReingestProductWorkflow, + UpdateCommerceAttrWorkflow, +} from "./workflows.js"; + +export type ShopifyTopic = + | "products/create" + | "products/update" + | "products/delete" + | "inventory_levels/update"; + +// Idempotency: workflow id = shopify::::. +// Temporal de-dupes duplicate webhook deliveries via WorkflowIdReusePolicy. +export function workflowId( + shop: string, + topic: string, + productId: string, + updatedAt: string, +): string { + return `shopify:${shop}:${topic}:${productId}:${updatedAt}`; +} + +// Minimal surface so the webhook handler can be unit-tested with a mock. +export interface WorkflowStarter { + start( + workflow: string, + opts: { args: unknown[]; taskQueue: string; workflowId: string }, + ): Promise<{ workflowId: string }>; +} + +export interface DispatchInput { + shop: string; + topic: ShopifyTopic; + productId: string; + updatedAt: string; + inventory?: number; +} + +// Maps a verified webhook to one or more Temporal workflows. Returns started ids. +export async function dispatchWebhook( + starter: WorkflowStarter, + input: DispatchInput, +): Promise { + const { shop, topic, productId, updatedAt } = input; + const base = { shop, productId, updatedAt }; + const started: string[] = []; + + const fire = async (name: string, suffix: string, args: unknown[]) => { + const id = workflowId(shop, `${topic}${suffix}`, productId, updatedAt); + await starter.start(name, { args, taskQueue: TASK_QUEUE, workflowId: id }); + started.push(id); + }; + + switch (topic) { + case "products/create": + await fire(ReingestProductWorkflow.name, "", [base]); + break; + case "products/update": + await fire(ReingestProductWorkflow.name, ":reingest", [base]); + await fire(RecomposeCreativePackWorkflow.name, ":recompose", [base]); + break; + case "products/delete": + await fire(PurgeProductTwinWorkflow.name, "", [base]); + break; + case "inventory_levels/update": + await fire(UpdateCommerceAttrWorkflow.name, "", [{ ...base, inventory: input.inventory ?? 0 }]); + break; + } + return started; +} + +let cached: Client | null = null; + +export async function getTemporalClient(cfg?: ShopifyConnectorConfig): Promise { + if (cached) return cached; + const c = cfg ?? loadConfig(); + const connection = await Connection.connect({ address: c.temporalAddress }); + cached = new Client({ connection, namespace: c.temporalNamespace }); + return cached; +} + +// Adapts the real Temporal Client to the WorkflowStarter interface. +export function clientStarter(client: Client): WorkflowStarter { + return { + async start(workflow, opts) { + const handle: WorkflowHandle = await client.workflow.start(workflow, { + args: opts.args, + taskQueue: opts.taskQueue, + workflowId: opts.workflowId, + workflowIdReusePolicy: "REJECT_DUPLICATE", + }); + return { workflowId: handle.workflowId }; + }, + }; +} diff --git a/connectors/shopify/src/temporal/worker.ts b/connectors/shopify/src/temporal/worker.ts new file mode 100644 index 0000000..376b758 --- /dev/null +++ b/connectors/shopify/src/temporal/worker.ts @@ -0,0 +1,21 @@ +import { fileURLToPath } from "node:url"; +import { dirname, join } from "node:path"; +import { NativeConnection, Worker } from "@temporalio/worker"; +import { loadConfig, TASK_QUEUE } from "../config.js"; +import * as activities from "./activities.js"; + +// Separate process: `shoploop-shopify worker`. Task queue: shoploop-shopify. +export async function runWorker(): Promise { + const cfg = loadConfig(); + const connection = await NativeConnection.connect({ address: cfg.temporalAddress }); + const here = dirname(fileURLToPath(import.meta.url)); + const worker = await Worker.create({ + connection, + namespace: cfg.temporalNamespace, + taskQueue: TASK_QUEUE, + workflowsPath: join(here, "workflows.js"), + activities, + }); + console.log(`[shoploop-shopify] worker on '${cfg.temporalNamespace}' queue '${TASK_QUEUE}'`); + await worker.run(); +} diff --git a/connectors/shopify/src/temporal/workflows.ts b/connectors/shopify/src/temporal/workflows.ts new file mode 100644 index 0000000..9a4fdc5 --- /dev/null +++ b/connectors/shopify/src/temporal/workflows.ts @@ -0,0 +1,61 @@ +import { proxyActivities } from "@temporalio/workflow"; +import type * as activities from "./activities.js"; + +const acts = proxyActivities({ + startToCloseTimeout: "2 minutes", + retry: { maximumAttempts: 5 }, +}); + +export interface ProductWorkflowArgs { + shop: string; + productId: string; + updatedAt: string; +} + +export interface InventoryWorkflowArgs extends ProductWorkflowArgs { + inventory: number; +} + +// products/create, products/update → re-emit product_twin.usda, write usd_path metafield. +export async function ReingestProductWorkflow(args: ProductWorkflowArgs): Promise<{ usdaPath: string }> { + const { usdaPath } = await acts.ingestSingleProduct({ productId: args.productId }); + await acts.setUsdPathMetafield({ productId: args.productId, usdaPath }); + return { usdaPath }; +} + +// products/update → recompose every preset and enqueue render jobs in open-imagine. +export async function RecomposeCreativePackWorkflow( + args: ProductWorkflowArgs, +): Promise<{ enqueued: number }> { + const handle = await acts.resolveHandle({ productId: args.productId }); + const stages = await acts.composeAllPresets({ productId: args.productId, handle }); + let enqueued = 0; + for (const s of stages) { + const r = await acts.enqueueRenderJob({ + productId: args.productId, + handle, + stagePath: s.stagePath, + preset: s.preset, + }); + if (r.accepted) enqueued++; + } + return { enqueued }; +} + +// products/delete → remove .usda, clear metafield. +export async function PurgeProductTwinWorkflow( + args: ProductWorkflowArgs, +): Promise<{ removed: boolean }> { + const handle = await acts.resolveHandle({ productId: args.productId }); + const { removed } = await acts.purgeTwin({ handle }); + await acts.clearUsdMetafields({ productId: args.productId }); + return { removed }; +} + +// inventory_levels/update → patch commerce:inventory attr only. No re-render (cheap). +export async function UpdateCommerceAttrWorkflow( + args: InventoryWorkflowArgs, +): Promise<{ patched: boolean }> { + const handle = await acts.resolveHandle({ productId: args.productId }); + return acts.patchCommerceInventory({ handle, inventory: args.inventory }); +} diff --git a/connectors/shopify/src/types.ts b/connectors/shopify/src/types.ts new file mode 100644 index 0000000..aac6f61 --- /dev/null +++ b/connectors/shopify/src/types.ts @@ -0,0 +1,78 @@ +// Normalized Shopify product shape. The Admin GraphQL client maps into this; +// emit/MCP/temporal all consume this single shape (no duplication of field handling). + +export interface ShopifyMoney { + amount: string; + currencyCode: string; +} + +export interface ShopifyVariant { + id: string; + title: string; + sku: string | null; + price: string; + inventoryQuantity: number | null; + selectedOptions: { name: string; value: string }[]; +} + +export interface ShopifyOption { + name: string; + values: string[]; +} + +export interface ShopifyImage { + id: string; + url: string; + altText: string | null; +} + +export interface ShopifyMedia3d { + id: string; + // resolved GLB/USDZ source url from a Model3d media node + sourceUrl: string; + format: string; // glb | usdz +} + +export interface ShopifyMetafield { + namespace: string; + key: string; + type: string; + value: string; +} + +export interface ShopifyProduct { + id: string; // gid://shopify/Product/123 + handle: string; + title: string; + description: string; + productType: string; + vendor: string; + status: string; + tags: string[]; + updatedAt: string; // ISO + options: ShopifyOption[]; + variants: ShopifyVariant[]; + images: ShopifyImage[]; + media3d: ShopifyMedia3d[]; + metafields: ShopifyMetafield[]; +} + +export interface IngestReport { + shop: string; + count: number; + written: { productId: string; handle: string; usdaPath: string }[]; + manifestPath: string; + skipped: { productId: string; reason: string }[]; +} + +export interface TwinManifest { + shop: string; + generatedAt: string; + entries: { shopifyId: string; handle: string; usdaPath: string; updatedAt: string }[]; +} + +// Numeric id helper: gid://shopify/Product/123 -> "123" +export function numericId(gid: string): string { + const m = gid.match(/(\d+)(?:\?.*)?$/); + return m ? m[1] : gid; +} diff --git a/connectors/shopify/src/usd/emit_product_twin.ts b/connectors/shopify/src/usd/emit_product_twin.ts new file mode 100644 index 0000000..2623222 --- /dev/null +++ b/connectors/shopify/src/usd/emit_product_twin.ts @@ -0,0 +1,202 @@ +import { + BRAND_ATTRS, + COMMERCE_ATTRS, + usdInt, + usdString, + usdStringArray, + usdToken, +} from "./schema.js"; +import type { ShopifyProduct, ShopifyVariant } from "../types.js"; + +export interface EmitOptions { + shop: string; + brandRulesPath: string; + // injectable for deterministic golden tests + generatedAt?: string; +} + +// Line that carries the only non-deterministic value; tests strip it before diffing. +export const GENERATED_AT_KEY = "commerce:generated_at"; + +function rootPrimName(p: ShopifyProduct): string { + return `Product_${usdToken(p.handle)}`; +} + +function variantForOptionValue( + p: ShopifyProduct, + optionName: string, + value: string, +): ShopifyVariant | undefined { + return p.variants.find((v) => + v.selectedOptions.some((o) => o.name === optionName && o.value === value), + ); +} + +// The Shopify variant that represents the default selection (first value of every option). +function defaultVariant(p: ShopifyProduct): ShopifyVariant | undefined { + if (p.variants.length === 0) return undefined; + if (p.options.length === 0) return p.variants[0]; + const wanted = new Map(p.options.map((o) => [o.name, o.values[0]])); + return ( + p.variants.find((v) => + v.selectedOptions.every((o) => wanted.get(o.name) === o.value), + ) ?? p.variants[0] + ); +} + +function emitMesh(p: ShopifyProduct, indent: string): string[] { + const lines: string[] = []; + const model = p.media3d[0]; + if (model) { + // Real GLB/USDZ from Shopify Model3d media. + lines.push(`${indent}def Xform "Geometry" (`); + lines.push(`${indent} prepend references = @${model.sourceUrl}@`); + lines.push(`${indent})`); + lines.push(`${indent}{`); + lines.push(`${indent} custom string commerce:mesh_source = ${usdString(model.sourceUrl)}`); + lines.push(`${indent} custom token commerce:mesh_format = ${usdString(model.format)}`); + lines.push(`${indent}}`); + } else { + // Placeholder unit cube — open-imagine swaps in a real asset at render time. + lines.push(`${indent}def Mesh "Placeholder" (`); + lines.push(`${indent} prepend apiSchemas = ["MaterialBindingAPI"]`); + lines.push(`${indent})`); + lines.push(`${indent}{`); + lines.push(`${indent} float3[] extent = [(-0.5, -0.5, -0.5), (0.5, 0.5, 0.5)]`); + lines.push(`${indent} int[] faceVertexCounts = [4, 4, 4, 4, 4, 4]`); + lines.push( + `${indent} int[] faceVertexIndices = [0, 1, 3, 2, 2, 3, 5, 4, 4, 5, 7, 6, 6, 7, 1, 0, 1, 7, 5, 3, 6, 0, 2, 4]`, + ); + lines.push( + `${indent} point3f[] points = [(-0.5, -0.5, 0.5), (0.5, -0.5, 0.5), (-0.5, 0.5, 0.5), (0.5, 0.5, 0.5), (-0.5, 0.5, -0.5), (0.5, 0.5, -0.5), (-0.5, -0.5, -0.5), (0.5, -0.5, -0.5)]`, + ); + lines.push(`${indent} uniform token subdivisionScheme = "none"`); + lines.push(`${indent} rel material:binding = `); + lines.push(`${indent} custom bool commerce:placeholder = true`); + lines.push(`${indent}}`); + } + return lines; +} + +function emitLooks(p: ShopifyProduct, indent: string): string[] { + // Material binding hint from the primary product image. Full PBR bake is open-imagine's job. + const img = p.images[0]; + const lines: string[] = []; + lines.push(`${indent}def Scope "Looks"`); + lines.push(`${indent}{`); + lines.push(`${indent} def Material "PreviewMat"`); + lines.push(`${indent} {`); + lines.push( + `${indent} custom asset commerce:preview_image = ${img ? `@${img.url}@` : "@@"}`, + ); + if (img?.altText) { + lines.push(`${indent} custom string commerce:preview_alt = ${usdString(img.altText)}`); + } + lines.push(`${indent} token outputs:surface`); + lines.push(`${indent} }`); + lines.push(`${indent}}`); + return lines; +} + +function emitVariantSets(p: ShopifyProduct, indent: string): string[] { + const lines: string[] = []; + for (const opt of p.options) { + const setName = usdToken(opt.name.toLowerCase()); + lines.push(`${indent}variantSet "${setName}" = {`); + for (const value of opt.values) { + const variant = variantForOptionValue(p, opt.name, value); + const vName = usdToken(value); + lines.push(`${indent} "${vName}" {`); + lines.push(`${indent} custom string commerce:option_value = ${usdString(value)}`); + if (variant) { + lines.push(`${indent} custom string ${COMMERCE_ATTRS.sku} = ${usdString(variant.sku ?? "")}`); + lines.push(`${indent} custom string ${COMMERCE_ATTRS.price} = ${usdString(variant.price)}`); + lines.push(`${indent} custom int ${COMMERCE_ATTRS.inventory} = ${usdInt(variant.inventoryQuantity)}`); + lines.push(`${indent} custom string commerce:variant_id = ${usdString(variant.id)}`); + } + lines.push(`${indent} }`); + lines.push(""); + } + // drop trailing blank line inside the block + if (lines[lines.length - 1] === "") lines.pop(); + lines.push(`${indent}}`); + } + return lines; +} + +export function emitProductTwin(p: ShopifyProduct, opts: EmitOptions): string { + const root = rootPrimName(p); + const dv = defaultVariant(p); + const generatedAt = opts.generatedAt ?? new Date().toISOString(); + + const setNames = p.options.map((o) => usdToken(o.name.toLowerCase())); + const out: string[] = []; + + out.push("#usda 1.0"); + out.push("("); + out.push(` defaultPrim = "${root}"`); + out.push(' metersPerUnit = 1'); + out.push(' upAxis = "Y"'); + out.push(" customLayerData = {"); + out.push(` string "commerce:shop" = ${usdString(opts.shop)}`); + out.push(` string "${COMMERCE_ATTRS.shopifyId}" = ${usdString(p.id)}`); + out.push(' string "commerce:generator" = "@shoploop/connector-shopify"'); + out.push(` string "${GENERATED_AT_KEY}" = ${usdString(generatedAt)}`); + out.push(" }"); + out.push(")"); + out.push(""); + + // Root prim metadata: declare variant sets + default selection. + out.push(`def Xform "${root}" (`); + out.push(' kind = "component"'); + if (setNames.length === 1) { + out.push(` variantSets = "${setNames[0]}"`); + } else if (setNames.length > 1) { + out.push(` variantSets = [${setNames.map((n) => `"${n}"`).join(", ")}]`); + } + if (p.options.length > 0) { + out.push(" variants = {"); + for (const opt of p.options) { + out.push(` string ${usdToken(opt.name.toLowerCase())} = "${usdToken(opt.values[0] ?? "")}"`); + } + out.push(" }"); + } + out.push(")"); + out.push("{"); + + // Product-level commerce + brand attrs (default variant drives price/sku/inventory). + out.push(` custom string ${COMMERCE_ATTRS.title} = ${usdString(p.title)}`); + out.push(` custom string ${COMMERCE_ATTRS.handle} = ${usdString(p.handle)}`); + out.push(` custom string ${COMMERCE_ATTRS.vendor} = ${usdString(p.vendor)}`); + out.push(` custom string ${COMMERCE_ATTRS.productType} = ${usdString(p.productType)}`); + out.push(` custom string ${COMMERCE_ATTRS.status} = ${usdString(p.status)}`); + out.push(` custom string ${COMMERCE_ATTRS.description} = ${usdString(p.description)}`); + out.push(` custom string ${COMMERCE_ATTRS.price} = ${usdString(dv?.price ?? "0.00")}`); + out.push(` custom string ${COMMERCE_ATTRS.sku} = ${usdString(dv?.sku ?? "")}`); + out.push(` custom int ${COMMERCE_ATTRS.inventory} = ${usdInt(dv?.inventoryQuantity ?? 0)}`); + out.push(` custom string[] ${BRAND_ATTRS.tags} = ${usdStringArray(p.tags)}`); + out.push(` custom string ${BRAND_ATTRS.rulesRef} = ${usdString(opts.brandRulesPath)}`); + out.push(""); + + for (const line of emitLooks(p, " ")) out.push(line); + out.push(""); + for (const line of emitMesh(p, " ")) out.push(line); + + const vsets = emitVariantSets(p, " "); + if (vsets.length > 0) { + out.push(""); + for (const line of vsets) out.push(line); + } + + out.push("}"); + out.push(""); + return out.join("\n"); +} + +// Strip the only non-deterministic line so goldens compare stably. +export function stripGeneratedAt(usda: string): string { + return usda + .split("\n") + .filter((l) => !l.includes(GENERATED_AT_KEY)) + .join("\n"); +} diff --git a/connectors/shopify/src/usd/inspect.ts b/connectors/shopify/src/usd/inspect.ts new file mode 100644 index 0000000..0790621 --- /dev/null +++ b/connectors/shopify/src/usd/inspect.ts @@ -0,0 +1,115 @@ +// Lightweight reader for the USDA we emit. Not a general USD parser — it understands +// our own product_twin grammar well enough for MCP read/inspect (daslabhq/openusd-mcp shape). + +export interface PrimNode { + path: string; + name: string; + type: string; + depth: number; +} + +export interface TwinInspection { + defaultPrim: string | null; + prims: PrimNode[]; + variantSets: Record; + attrs: Record; + summary: string; +} + +const DEF_RE = /^\s*def\s+(\w+)?\s*"([^"]+)"/; +const DEF_NOTYPE_RE = /^\s*def\s+"([^"]+)"/; +const VARSET_RE = /^\s*variantSet\s+"?([A-Za-z0-9_]+)"?\s*=\s*\{/; +const VARIANT_RE = /^\s*"([^"]+)"\s*\{/; +const ATTR_RE = /^\s*(?:custom\s+)?(?:uniform\s+)?[\w\[\]]+\s+([\w:]+)\s*=\s*(.+?)\s*$/; +const DEFAULT_PRIM_RE = /defaultPrim\s*=\s*"([^"]+)"/; + +export function inspectTwin(usda: string): TwinInspection { + const lines = usda.split("\n"); + const prims: PrimNode[] = []; + const variantSets: Record = {}; + const attrs: Record = {}; + const pathStack: string[] = []; + let defaultPrim: string | null = null; + let depth = 0; + let currentVarSet: string | null = null; + let varSetDepth = -1; + let rootDepthForAttrs = -1; + + for (const line of lines) { + if (defaultPrim === null) { + const dp = DEFAULT_PRIM_RE.exec(line); + if (dp) defaultPrim = dp[1]; + } + + const vs = VARSET_RE.exec(line); + if (vs) { + currentVarSet = vs[1]; + variantSets[currentVarSet] ??= []; + varSetDepth = depth; + } else if (currentVarSet && depth === varSetDepth + 1) { + const vm = VARIANT_RE.exec(line); + if (vm) variantSets[currentVarSet].push(vm[1]); + } + + const defMatch = DEF_RE.exec(line); + const defNoType = DEF_NOTYPE_RE.exec(line); + if (defMatch && defMatch[2]) { + const name = defMatch[2]; + const type = defMatch[1] ?? ""; + pathStack[depth] = name; + const path = "/" + pathStack.slice(0, depth + 1).join("/"); + prims.push({ path, name, type, depth }); + if (depth === 0) rootDepthForAttrs = 1; + } else if (defNoType && defNoType[1] && !defMatch) { + const name = defNoType[1]; + pathStack[depth] = name; + const path = "/" + pathStack.slice(0, depth + 1).join("/"); + prims.push({ path, name, type: "" , depth }); + } + + // Root-prim attrs only (depth 1, not inside a variantSet/material). + if (depth === rootDepthForAttrs && currentVarSet === null) { + const am = ATTR_RE.exec(line); + if (am && am[1].includes(":")) attrs[am[1]] = am[2]; + } + + for (const ch of line) { + if (ch === "{") depth++; + else if (ch === "}") { + depth--; + if (currentVarSet && depth <= varSetDepth) { + currentVarSet = null; + varSetDepth = -1; + } + } + } + } + + const summary = renderSummary(defaultPrim, prims, variantSets, attrs); + return { defaultPrim, prims, variantSets, attrs, summary }; +} + +function renderSummary( + defaultPrim: string | null, + prims: PrimNode[], + variantSets: Record, + attrs: Record, +): string { + const out: string[] = []; + out.push(`Stage defaultPrim: ${defaultPrim ?? "(none)"}`); + out.push("Prim tree:"); + for (const p of prims) { + out.push(`${" ".repeat(p.depth + 1)}${p.type ? `[${p.type}] ` : ""}${p.name} (${p.path})`); + } + const vsKeys = Object.keys(variantSets); + if (vsKeys.length) { + out.push("Variant sets:"); + for (const k of vsKeys) out.push(` ${k}: ${variantSets[k].join(", ")}`); + } + const aKeys = Object.keys(attrs); + if (aKeys.length) { + out.push("Commerce/brand attrs:"); + for (const k of aKeys) out.push(` ${k} = ${attrs[k]}`); + } + return out.join("\n"); +} diff --git a/connectors/shopify/src/usd/manipulate.ts b/connectors/shopify/src/usd/manipulate.ts new file mode 100644 index 0000000..43353fe --- /dev/null +++ b/connectors/shopify/src/usd/manipulate.ts @@ -0,0 +1,106 @@ +import { usdString, usdToken } from "./schema.js"; + +// Agent-driven edits to variant sets on an emitted USDA stage (text-level, deterministic). +export type VariantOp = + | { action: "add"; variantSet: string; variant: string; optionValue?: string } + | { action: "remove"; variantSet: string; variant: string } + | { action: "rename"; variantSet: string; variant: string; to: string }; + +export interface ManipulateResult { + usda: string; + changed: boolean; + note: string; +} + +function findVariantSetBlock(lines: string[], setName: string): { start: number; end: number; indent: string } | null { + const open = new RegExp(`^(\\s*)variantSet\\s+"?${setName}"?\\s*=\\s*\\{`); + let start = -1; + let indent = ""; + for (let i = 0; i < lines.length; i++) { + const m = open.exec(lines[i]); + if (m) { + start = i; + indent = m[1]; + break; + } + } + if (start === -1) return null; + let depth = 0; + for (let i = start; i < lines.length; i++) { + for (const ch of lines[i]) { + if (ch === "{") depth++; + else if (ch === "}") depth--; + } + if (depth === 0) return { start, end: i, indent }; + } + return null; +} + +export function manipulateVariantSet(usda: string, op: VariantOp): ManipulateResult { + const lines = usda.split("\n"); + const block = findVariantSetBlock(lines, op.variantSet); + if (!block) { + return { usda, changed: false, note: `variantSet "${op.variantSet}" not found` }; + } + const inner = `${block.indent} `; + const variantToken = (v: string) => usdToken(v); + + if (op.action === "rename") { + const fromTok = variantToken(op.variant); + const toTok = variantToken(op.to); + const re = new RegExp(`^(\\s*)"${fromTok}"\\s*\\{`); + for (let i = block.start; i <= block.end; i++) { + if (re.test(lines[i])) { + lines[i] = lines[i].replace(`"${fromTok}"`, `"${toTok}"`); + return { usda: lines.join("\n"), changed: true, note: `renamed ${fromTok} -> ${toTok}` }; + } + } + return { usda, changed: false, note: `variant "${fromTok}" not in "${op.variantSet}"` }; + } + + if (op.action === "remove") { + const tok = variantToken(op.variant); + const re = new RegExp(`^\\s*"${tok}"\\s*\\{`); + let s = -1; + for (let i = block.start + 1; i <= block.end; i++) { + if (re.test(lines[i])) { + s = i; + break; + } + } + if (s === -1) return { usda, changed: false, note: `variant "${tok}" not in "${op.variantSet}"` }; + let depth = 0; + let e = s; + for (let i = s; i <= block.end; i++) { + for (const ch of lines[i]) { + if (ch === "{") depth++; + else if (ch === "}") depth--; + } + if (depth === 0) { + e = i; + break; + } + } + // also drop a trailing blank separator line if present + if (lines[e + 1] === "") e++; + lines.splice(s, e - s + 1); + return { usda: lines.join("\n"), changed: true, note: `removed ${tok}` }; + } + + // add + const tok = variantToken(op.variant); + const exists = new RegExp(`^\\s*"${tok}"\\s*\\{`); + for (let i = block.start + 1; i < block.end; i++) { + if (exists.test(lines[i])) { + return { usda, changed: false, note: `variant "${tok}" already exists` }; + } + } + const insert = [ + `${inner}"${tok}" {`, + `${inner} custom string commerce:option_value = ${usdString(op.optionValue ?? op.variant)}`, + `${inner}}`, + "", + ]; + lines.splice(block.end, 0, ...insert); + return { usda: lines.join("\n"), changed: true, note: `added ${tok}` }; +} diff --git a/connectors/shopify/src/usd/schema.ts b/connectors/shopify/src/usd/schema.ts new file mode 100644 index 0000000..e6e0f68 --- /dev/null +++ b/connectors/shopify/src/usd/schema.ts @@ -0,0 +1,49 @@ +// Commerce schema overlaid on OpenUSD. These attribute names are the contract the +// Scene Composer + open-imagine render path read back. +// CONTRACT: open-imagine/contracts/product_twin.v1.json +// CONTRACT: open-imagine/contracts/render_job.v1.json + +export const COMMERCE_ATTRS = { + price: "commerce:price", + sku: "commerce:sku", + inventory: "commerce:inventory", + vendor: "commerce:vendor", + title: "commerce:title", + handle: "commerce:handle", + productType: "commerce:product_type", + status: "commerce:status", + description: "commerce:description", + shopifyId: "commerce:shopify_id", +} as const; + +export const BRAND_ATTRS = { + tags: "brand:tags", + rulesRef: "brand:rules_ref", + score: "brand:score", +} as const; + +// USDA token must be a valid identifier: [A-Za-z_][A-Za-z0-9_]* +export function usdToken(raw: string): string { + let t = raw.replace(/[^A-Za-z0-9_]/g, "_"); + if (t.length === 0) t = "_"; + if (/^[0-9]/.test(t)) t = `_${t}`; + return t; +} + +// Escape a JS string for a double-quoted USDA string literal. +export function usdString(raw: string): string { + const s = raw + .replace(/\\/g, "\\\\") + .replace(/"/g, '\\"') + .replace(/\r?\n/g, "\\n") + .replace(/\t/g, "\\t"); + return `"${s}"`; +} + +export function usdStringArray(items: string[]): string { + return `[${items.map(usdString).join(", ")}]`; +} + +export function usdInt(n: number | null | undefined): string { + return String(Math.trunc(Number.isFinite(n as number) ? (n as number) : 0)); +} diff --git a/connectors/shopify/test/fixtures/varitea_product.json b/connectors/shopify/test/fixtures/varitea_product.json new file mode 100644 index 0000000..bcadbb5 --- /dev/null +++ b/connectors/shopify/test/fixtures/varitea_product.json @@ -0,0 +1,62 @@ +{ + "id": "gid://shopify/Product/7700112233445", + "handle": "varitea-loose-leaf-original", + "title": "Varitea Loose-Leaf — Original", + "description": "Small-batch loose-leaf tea blend. Stone-fruit and chamomile notes, cold-brew friendly. Steep 3–5 min at 90°C.", + "productType": "Tea", + "vendor": "Drink Varitea", + "status": "ACTIVE", + "tags": ["loose-leaf", "tea", "wellness", "cold-brew"], + "updatedAt": "2026-06-20T12:00:00Z", + "options": [ + { "name": "Size", "values": ["50g", "100g", "250g"] } + ], + "variants": [ + { + "id": "gid://shopify/ProductVariant/41001", + "title": "50g", + "sku": "VAR-LL-50", + "price": "14.00", + "inventoryQuantity": 120, + "selectedOptions": [{ "name": "Size", "value": "50g" }] + }, + { + "id": "gid://shopify/ProductVariant/41002", + "title": "100g", + "sku": "VAR-LL-100", + "price": "24.00", + "inventoryQuantity": 80, + "selectedOptions": [{ "name": "Size", "value": "100g" }] + }, + { + "id": "gid://shopify/ProductVariant/41003", + "title": "250g", + "sku": "VAR-LL-250", + "price": "52.00", + "inventoryQuantity": 35, + "selectedOptions": [{ "name": "Size", "value": "250g" }] + } + ], + "images": [ + { + "id": "gid://shopify/ProductImage/9001", + "url": "https://cdn.shopify.com/s/files/1/varitea/loose-leaf-original.jpg", + "altText": "Varitea loose-leaf original in a kraft pouch on linen" + } + ], + "media3d": [], + "metafields": [ + { + "namespace": "shoploop", + "key": "brand_score", + "type": "number_decimal", + "value": "0.0" + }, + { + "namespace": "custom", + "key": "steep_time", + "type": "single_line_text_field", + "value": "3-5 min" + } + ] +} diff --git a/connectors/shopify/test/fixtures/varitea_product_twin.usda b/connectors/shopify/test/fixtures/varitea_product_twin.usda new file mode 100644 index 0000000..d5c95b8 --- /dev/null +++ b/connectors/shopify/test/fixtures/varitea_product_twin.usda @@ -0,0 +1,82 @@ +#usda 1.0 +( + defaultPrim = "Product_varitea_loose_leaf_original" + metersPerUnit = 1 + upAxis = "Y" + customLayerData = { + string "commerce:shop" = "drinkvaritea.myshopify.com" + string "commerce:shopify_id" = "gid://shopify/Product/7700112233445" + string "commerce:generator" = "@shoploop/connector-shopify" + string "commerce:generated_at" = "2026-06-20T12:00:00.000Z" + } +) + +def Xform "Product_varitea_loose_leaf_original" ( + kind = "component" + variantSets = "size" + variants = { + string size = "_50g" + } +) +{ + custom string commerce:title = "Varitea Loose-Leaf — Original" + custom string commerce:handle = "varitea-loose-leaf-original" + custom string commerce:vendor = "Drink Varitea" + custom string commerce:product_type = "Tea" + custom string commerce:status = "ACTIVE" + custom string commerce:description = "Small-batch loose-leaf tea blend. Stone-fruit and chamomile notes, cold-brew friendly. Steep 3–5 min at 90°C." + custom string commerce:price = "14.00" + custom string commerce:sku = "VAR-LL-50" + custom int commerce:inventory = 120 + custom string[] brand:tags = ["loose-leaf", "tea", "wellness", "cold-brew"] + custom string brand:rules_ref = "/etc/shoploop/brand_rules.json" + + def Scope "Looks" + { + def Material "PreviewMat" + { + custom asset commerce:preview_image = @https://cdn.shopify.com/s/files/1/varitea/loose-leaf-original.jpg@ + custom string commerce:preview_alt = "Varitea loose-leaf original in a kraft pouch on linen" + token outputs:surface + } + } + + def Mesh "Placeholder" ( + prepend apiSchemas = ["MaterialBindingAPI"] + ) + { + float3[] extent = [(-0.5, -0.5, -0.5), (0.5, 0.5, 0.5)] + int[] faceVertexCounts = [4, 4, 4, 4, 4, 4] + int[] faceVertexIndices = [0, 1, 3, 2, 2, 3, 5, 4, 4, 5, 7, 6, 6, 7, 1, 0, 1, 7, 5, 3, 6, 0, 2, 4] + point3f[] points = [(-0.5, -0.5, 0.5), (0.5, -0.5, 0.5), (-0.5, 0.5, 0.5), (0.5, 0.5, 0.5), (-0.5, 0.5, -0.5), (0.5, 0.5, -0.5), (-0.5, -0.5, -0.5), (0.5, -0.5, -0.5)] + uniform token subdivisionScheme = "none" + rel material:binding = + custom bool commerce:placeholder = true + } + + variantSet "size" = { + "_50g" { + custom string commerce:option_value = "50g" + custom string commerce:sku = "VAR-LL-50" + custom string commerce:price = "14.00" + custom int commerce:inventory = 120 + custom string commerce:variant_id = "gid://shopify/ProductVariant/41001" + } + + "_100g" { + custom string commerce:option_value = "100g" + custom string commerce:sku = "VAR-LL-100" + custom string commerce:price = "24.00" + custom int commerce:inventory = 80 + custom string commerce:variant_id = "gid://shopify/ProductVariant/41002" + } + + "_250g" { + custom string commerce:option_value = "250g" + custom string commerce:sku = "VAR-LL-250" + custom string commerce:price = "52.00" + custom int commerce:inventory = 35 + custom string commerce:variant_id = "gid://shopify/ProductVariant/41003" + } + } +} diff --git a/connectors/shopify/test/helpers.ts b/connectors/shopify/test/helpers.ts new file mode 100644 index 0000000..75b5175 --- /dev/null +++ b/connectors/shopify/test/helpers.ts @@ -0,0 +1,76 @@ +import { readFileSync } from "node:fs"; +import { fileURLToPath } from "node:url"; +import { dirname, join } from "node:path"; +import type { + ProductFilter, + ProductSummary, + ShopifyClient, +} from "../src/client.js"; +import type { ShopifyMetafield, ShopifyProduct, ShopifyVariant } from "../src/types.js"; + +export const HERE = dirname(fileURLToPath(import.meta.url)); + +export function loadVaritea(): ShopifyProduct { + return JSON.parse(readFileSync(join(HERE, "fixtures", "varitea_product.json"), "utf8")); +} + +export function loadGolden(): string { + return readFileSync(join(HERE, "fixtures", "varitea_product_twin.usda"), "utf8"); +} + +// In-memory ShopifyClient backed by fixtures — no network. Mirrors the real interface. +export class FixtureClient implements ShopifyClient { + private products: Map; + public metafieldWrites: { ownerId: string; namespace: string; key: string; value: string }[] = []; + + constructor(products: ShopifyProduct[]) { + this.products = new Map(products.map((p) => [p.id, p])); + } + + async listProducts(filter?: ProductFilter): Promise { + return [...this.products.values()] + .filter((p) => !filter?.vendor || p.vendor === filter.vendor) + .filter((p) => !filter?.productType || p.productType === filter.productType) + .filter((p) => !filter?.tag || p.tags.includes(filter.tag)) + .filter((p) => !filter?.status || p.status === filter.status) + .map((p) => ({ + id: p.id, + handle: p.handle, + title: p.title, + status: p.status, + productType: p.productType, + vendor: p.vendor, + tags: p.tags, + })); + } + + async getProduct(id: string): Promise { + return this.products.get(id) ?? null; + } + + async getVariants(id: string): Promise { + return this.products.get(id)?.variants ?? []; + } + + async getMetafields(id: string): Promise { + return this.products.get(id)?.metafields ?? []; + } + + async updateMetafield(input: { + ownerId: string; + namespace: string; + key: string; + type: string; + value: string; + }): Promise { + this.metafieldWrites.push(input); + return { namespace: input.namespace, key: input.key, type: input.type, value: input.value }; + } + + async *iterateProducts(filter?: ProductFilter): AsyncGenerator { + for (const p of this.products.values()) { + if (filter?.since && p.updatedAt < filter.since) continue; + yield p; + } + } +} diff --git a/connectors/shopify/test/mcp.test.ts b/connectors/shopify/test/mcp.test.ts new file mode 100644 index 0000000..bd3cc7c --- /dev/null +++ b/connectors/shopify/test/mcp.test.ts @@ -0,0 +1,134 @@ +import { mkdtemp } from "node:fs/promises"; +import { tmpdir } from "node:os"; +import { join } from "node:path"; +import { Client } from "@modelcontextprotocol/sdk/client/index.js"; +import { InMemoryTransport } from "@modelcontextprotocol/sdk/inMemory.js"; +import { afterAll, beforeAll, describe, expect, it } from "vitest"; +import { createMcpServer } from "../src/mcp/server.js"; +import { ingestCatalog } from "../src/primitives/ingest_catalog.js"; +import type { ToolContext } from "../src/mcp/tools/index.js"; +import { FixtureClient, loadVaritea } from "./helpers.js"; + +const PRODUCT_ID = "gid://shopify/Product/7700112233445"; + +let client: Client; +let fixtureClient: FixtureClient; + +interface ContentItem { + type: string; + text: string; +} + +async function call(name: string, args: Record): Promise<{ data: any; text?: string }> { + const res = (await client.callTool({ name, arguments: args })) as { + content: ContentItem[]; + isError?: boolean; + }; + expect(res.isError ?? false).toBe(false); + const texts = res.content.filter((c) => c.type === "text"); + return { data: JSON.parse(texts[0].text), text: texts[1]?.text }; +} + +beforeAll(async () => { + const outDir = await mkdtemp(join(tmpdir(), "mcp-twins-")); + fixtureClient = new FixtureClient([loadVaritea()]); + await ingestCatalog({ + shop: "drinkvaritea.myshopify.com", + outDir, + brandRulesPath: "/etc/shoploop/brand_rules.json", + client: fixtureClient, + }); + + const ctx: ToolContext = { + client: fixtureClient, + usdOutDir: outDir, + brandRulesPath: "/etc/shoploop/brand_rules.json", + }; + const server = createMcpServer(ctx); + const [clientT, serverT] = InMemoryTransport.createLinkedPair(); + client = new Client({ name: "test", version: "0.0.0" }); + await Promise.all([server.connect(serverT), client.connect(clientT)]); +}); + +afterAll(async () => { + await client?.close(); +}); + +describe("MCP server", () => { + it("exposes all 7 commerce tools", async () => { + const tools = await client.listTools(); + const names = tools.tools.map((t) => t.name).sort(); + expect(names).toEqual( + [ + "get_metafields", + "get_product", + "get_variants", + "inspect_product_twin", + "list_products", + "manipulate_variant_set", + "update_metafield", + ].sort(), + ); + }); + + it("list_products returns ids + handles", async () => { + const { data } = await call("list_products", {}); + expect(data.count).toBe(1); + expect(data.products[0].handle).toBe("varitea-loose-leaf-original"); + }); + + it("get_product returns the full product", async () => { + const { data } = await call("get_product", { productId: PRODUCT_ID }); + expect(data.found).toBe(true); + expect(data.product.variants).toHaveLength(3); + }); + + it("get_variants returns the variant matrix", async () => { + const { data } = await call("get_variants", { productId: PRODUCT_ID }); + expect(data.count).toBe(3); + expect(data.variants[0].sku).toBe("VAR-LL-50"); + }); + + it("get_metafields filters by namespace", async () => { + const { data } = await call("get_metafields", { productId: PRODUCT_ID, namespace: "shoploop" }); + expect(data.count).toBe(1); + expect(data.metafields[0].key).toBe("brand_score"); + }); + + it("update_metafield writes through the client", async () => { + const { data } = await call("update_metafield", { + productId: PRODUCT_ID, + key: "usd_path", + value: "/var/shoploop/twins/varitea_loose_leaf_original.usda", + }); + expect(data.ok).toBe(true); + expect(fixtureClient.metafieldWrites.at(-1)?.key).toBe("usd_path"); + }); + + it("inspect_product_twin returns prim tree, variant sets, and a text summary", async () => { + const { data, text } = await call("inspect_product_twin", { + handle: "varitea-loose-leaf-original", + }); + expect(data.defaultPrim).toBe("Product_varitea_loose_leaf_original"); + expect(data.variantSets.size).toEqual(["_50g", "_100g", "_250g"]); + expect(text).toContain("Stage defaultPrim:"); + }); + + it("manipulate_variant_set edits the stage on disk", async () => { + const before = await call("inspect_product_twin", { handle: "varitea-loose-leaf-original" }); + expect(before.data.variantSets.size).not.toContain("_1kg"); + + const { data } = await call("manipulate_variant_set", { + handle: "varitea-loose-leaf-original", + action: "add", + variantSet: "size", + variant: "1kg", + optionValue: "1kg", + }); + expect(data.changed).toBe(true); + expect(data.written).toBe(true); + + const after = await call("inspect_product_twin", { handle: "varitea-loose-leaf-original" }); + expect(after.data.variantSets.size).toContain("_1kg"); + }); +}); diff --git a/connectors/shopify/test/primitives.test.ts b/connectors/shopify/test/primitives.test.ts new file mode 100644 index 0000000..d6ea2c7 --- /dev/null +++ b/connectors/shopify/test/primitives.test.ts @@ -0,0 +1,141 @@ +import { mkdtemp, readFile } from "node:fs/promises"; +import { tmpdir } from "node:os"; +import { join } from "node:path"; +import { describe, expect, it } from "vitest"; +import { emitProductTwin, stripGeneratedAt } from "../src/usd/emit_product_twin.js"; +import { inspectTwin } from "../src/usd/inspect.js"; +import { manipulateVariantSet } from "../src/usd/manipulate.js"; +import { ingestCatalog } from "../src/primitives/ingest_catalog.js"; +import { composeScene } from "../src/primitives/compose_scene.js"; +import { FixtureClient, loadGolden, loadVaritea } from "./helpers.js"; + +describe("emit_product_twin", () => { + it("matches the golden USDA modulo the generated_at timestamp", () => { + const product = loadVaritea(); + const usda = emitProductTwin(product, { + shop: "drinkvaritea.myshopify.com", + brandRulesPath: "/etc/shoploop/brand_rules.json", + generatedAt: "1999-01-01T00:00:00.000Z", + }); + expect(stripGeneratedAt(usda)).toBe(stripGeneratedAt(loadGolden())); + }); + + it("maps Shopify options to USD variant sets", () => { + const usda = emitProductTwin(loadVaritea(), { + shop: "s", + brandRulesPath: "r", + }); + const ins = inspectTwin(usda); + expect(Object.keys(ins.variantSets)).toEqual(["size"]); + expect(ins.variantSets.size).toEqual(["_50g", "_100g", "_250g"]); + expect(ins.attrs["commerce:sku"]).toContain("VAR-LL-50"); + expect(ins.attrs["brand:rules_ref"]).toContain("r"); + }); + + it("emits a placeholder mesh when no Model3d media exists", () => { + const usda = emitProductTwin(loadVaritea(), { shop: "s", brandRulesPath: "r" }); + expect(usda).toContain('def Mesh "Placeholder"'); + expect(usda).toContain("commerce:placeholder = true"); + }); + + it("references a real mesh asset when Model3d media is present", () => { + const p = loadVaritea(); + p.media3d = [{ id: "m1", sourceUrl: "https://cdn/varitea.glb", format: "glb" }]; + const usda = emitProductTwin(p, { shop: "s", brandRulesPath: "r" }); + expect(usda).toContain("prepend references = @https://cdn/varitea.glb@"); + expect(usda).not.toContain('def Mesh "Placeholder"'); + }); +}); + +describe("manipulate_variant_set", () => { + it("adds, renames, and removes variants deterministically", () => { + const base = emitProductTwin(loadVaritea(), { shop: "s", brandRulesPath: "r" }); + const added = manipulateVariantSet(base, { + action: "add", + variantSet: "size", + variant: "500g", + optionValue: "500g", + }); + expect(added.changed).toBe(true); + expect(inspectTwin(added.usda).variantSets.size).toContain("_500g"); + + const renamed = manipulateVariantSet(added.usda, { + action: "rename", + variantSet: "size", + variant: "500g", + to: "bulk", + }); + expect(inspectTwin(renamed.usda).variantSets.size).toContain("bulk"); + + const removed = manipulateVariantSet(renamed.usda, { + action: "remove", + variantSet: "size", + variant: "bulk", + }); + expect(inspectTwin(removed.usda).variantSets.size).not.toContain("bulk"); + }); + + it("is a no-op for an unknown variant set", () => { + const base = emitProductTwin(loadVaritea(), { shop: "s", brandRulesPath: "r" }); + const r = manipulateVariantSet(base, { + action: "remove", + variantSet: "flavor", + variant: "mint", + }); + expect(r.changed).toBe(false); + }); +}); + +describe("ingestCatalog", () => { + it("writes one .usda per product plus a manifest", async () => { + const out = await mkdtemp(join(tmpdir(), "twins-")); + const client = new FixtureClient([loadVaritea()]); + const report = await ingestCatalog({ + shop: "drinkvaritea.myshopify.com", + outDir: out, + brandRulesPath: "/etc/shoploop/brand_rules.json", + client, + }); + expect(report.count).toBe(1); + expect(report.written[0].usdaPath).toContain("varitea_loose_leaf_original.usda"); + + const written = await readFile(report.written[0].usdaPath, "utf8"); + expect(stripGeneratedAt(written)).toBe(stripGeneratedAt(loadGolden())); + + const manifest = JSON.parse(await readFile(report.manifestPath, "utf8")); + expect(manifest.entries[0].shopifyId).toBe("gid://shopify/Product/7700112233445"); + }); +}); + +describe("composeScene", () => { + it("emits a composed stage with sublayers and applies Kinfolk brand rules", async () => { + const out = await mkdtemp(join(tmpdir(), "twins-")); + const client = new FixtureClient([loadVaritea()]); + const report = await ingestCatalog({ + shop: "drinkvaritea.myshopify.com", + outDir: out, + brandRulesPath: "/nonexistent/brand_rules.json", + client, + }); + + const stagePath = join(out, "scenes", "varitea__hero.usda"); + const res = await composeScene({ + productId: report.written[0].productId, + brandRulesPath: "/nonexistent/brand_rules.json", + outStage: stagePath, + preset: "hero", + usdOutDir: out, + }); + + expect(res.rulesApplied).toContain("kinfolk_gate"); + const composed = await readFile(res.stagePath, "utf8"); + expect(composed).toContain("subLayers = ["); + expect(composed).toContain("__brand_rules.usda"); + expect(composed).toContain("__preset.usda"); + expect(composed).toContain('over "Product_varitea_loose_leaf_original"'); + expect(composed).toContain("scene:preset"); + + const brandLayer = await readFile(res.brandRulesLayer, "utf8"); + expect(brandLayer).toContain("brand:allowed_lighting_rigs"); + }); +}); diff --git a/connectors/shopify/test/webhook.test.ts b/connectors/shopify/test/webhook.test.ts new file mode 100644 index 0000000..c3db61c --- /dev/null +++ b/connectors/shopify/test/webhook.test.ts @@ -0,0 +1,119 @@ +import { createHmac } from "node:crypto"; +import express from "express"; +import { afterEach, describe, expect, it } from "vitest"; +import type { AddressInfo } from "node:net"; +import type { Server } from "node:http"; +import { loadConfig } from "../src/config.js"; +import { registerWebhookRoute, verifyShopifyHmac } from "../src/primitives/webhooks.js"; +import { workflowId, type WorkflowStarter } from "../src/temporal/dispatch.js"; + +const SECRET = "shh-webhook-secret"; + +function sign(body: string): string { + return createHmac("sha256", SECRET).update(body, "utf8").digest("base64"); +} + +describe("verifyShopifyHmac", () => { + it("accepts a correctly signed body and rejects tampering", () => { + const body = JSON.stringify({ id: 1 }); + const sig = sign(body); + expect(verifyShopifyHmac(body, sig, SECRET)).toBe(true); + expect(verifyShopifyHmac(body + "x", sig, SECRET)).toBe(false); + expect(verifyShopifyHmac(body, sig, "wrong-secret")).toBe(false); + expect(verifyShopifyHmac(body, "", SECRET)).toBe(false); + }); +}); + +describe("workflowId", () => { + it("is idempotent and topic-scoped", () => { + expect(workflowId("s.myshopify.com", "products/update", "gid://shopify/Product/1", "2026-06-20")).toBe( + "shopify:s.myshopify.com:products/update:gid://shopify/Product/1:2026-06-20", + ); + }); +}); + +describe("POST /webhooks/shopify", () => { + let server: Server | undefined; + + afterEach(async () => { + if (server) await new Promise((r) => server!.close(() => r())); + server = undefined; + }); + + async function boot(starter: WorkflowStarter): Promise { + const cfg = loadConfig({ + shop: "drinkvaritea.myshopify.com", + adminToken: "shpat_test", + webhookSecret: SECRET, + }); + const app = express(); + registerWebhookRoute(app, cfg, { starter }); + return new Promise((resolve) => { + server = app.listen(0, () => { + const { port } = server!.address() as AddressInfo; + resolve(`http://127.0.0.1:${port}`); + }); + }); + } + + it("verifies HMAC and starts the mapped workflows for products/update", async () => { + const started: { workflow: string; workflowId: string }[] = []; + const starter: WorkflowStarter = { + async start(workflow, opts) { + started.push({ workflow, workflowId: opts.workflowId }); + return { workflowId: opts.workflowId }; + }, + }; + const base = await boot(starter); + const payload = JSON.stringify({ + admin_graphql_api_id: "gid://shopify/Product/7700112233445", + updated_at: "2026-06-20T12:00:00Z", + }); + + const res = await fetch(`${base}/webhooks/shopify`, { + method: "POST", + headers: { + "content-type": "application/json", + "x-shopify-hmac-sha256": sign(payload), + "x-shopify-topic": "products/update", + "x-shopify-shop-domain": "drinkvaritea.myshopify.com", + }, + body: payload, + }); + expect(res.status).toBe(200); + const json = (await res.json()) as { ok: boolean; started: string[] }; + expect(json.ok).toBe(true); + // products/update fans out to Reingest + Recompose + expect(started.map((s) => s.workflow)).toEqual([ + "ReingestProductWorkflow", + "RecomposeCreativePackWorkflow", + ]); + expect(started[0].workflowId).toContain( + "shopify:drinkvaritea.myshopify.com:products/update:reingest:", + ); + }); + + it("rejects an invalid signature with 401 and starts nothing", async () => { + const started: string[] = []; + const starter: WorkflowStarter = { + async start(_w, opts) { + started.push(opts.workflowId); + return { workflowId: opts.workflowId }; + }, + }; + const base = await boot(starter); + const payload = JSON.stringify({ admin_graphql_api_id: "gid://shopify/Product/1" }); + + const res = await fetch(`${base}/webhooks/shopify`, { + method: "POST", + headers: { + "content-type": "application/json", + "x-shopify-hmac-sha256": "deadbeef", + "x-shopify-topic": "products/create", + }, + body: payload, + }); + expect(res.status).toBe(401); + expect(started).toHaveLength(0); + }); +}); diff --git a/connectors/shopify/tsconfig.build.json b/connectors/shopify/tsconfig.build.json new file mode 100644 index 0000000..376ad7f --- /dev/null +++ b/connectors/shopify/tsconfig.build.json @@ -0,0 +1,9 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "rootDir": "src", + "noEmit": false + }, + "include": ["src/**/*.ts"], + "exclude": ["dist", "node_modules", "test"] +} diff --git a/connectors/shopify/tsconfig.json b/connectors/shopify/tsconfig.json new file mode 100644 index 0000000..b55ba1e --- /dev/null +++ b/connectors/shopify/tsconfig.json @@ -0,0 +1,22 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "NodeNext", + "moduleResolution": "NodeNext", + "lib": ["ES2022"], + "outDir": "./dist", + "rootDir": ".", + "declaration": true, + "sourceMap": true, + "strict": true, + "noImplicitOverride": true, + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "resolveJsonModule": true, + "verbatimModuleSyntax": false, + "types": ["node"] + }, + "include": ["src/**/*.ts", "test/**/*.ts"], + "exclude": ["dist", "node_modules"] +} diff --git a/connectors/shopify/vitest.config.ts b/connectors/shopify/vitest.config.ts new file mode 100644 index 0000000..0388827 --- /dev/null +++ b/connectors/shopify/vitest.config.ts @@ -0,0 +1,9 @@ +import { defineConfig } from "vitest/config"; + +export default defineConfig({ + test: { + environment: "node", + include: ["test/**/*.test.ts"], + globals: false, + }, +});