diff --git a/README.ja.md b/README.ja.md index cc5f251..6d97d6f 100644 --- a/README.ja.md +++ b/README.ja.md @@ -11,6 +11,16 @@ Cloudflare Workers 上で動く、GitHub の issue / pull request / release / do - いま何が起きたかを push で受け取ること - 次に必要な状態を hybrid retrieval (dense + sparse) で引き戻すこと +## 破壊的変更: MCP プロトコル版 2026-07-28 + +このリリースから、Worker は **MCP プロトコル版 2026-07-28 のみ**を提供します。旧版向けの互換レーンは持ちません。 + +- **このリリースより古いブリッジは動作しなくなります。** 旧ブリッジは `initialize` でセッションを開こうとしますが、Worker はもう応答しません。落ち方は静かです。ブリッジはクラッシュせず、プロトコルエラーをツール出力のテキストとして返します。 +- **新しいブリッジを取り込むには Claude Desktop を再起動してください。** ブリッジは `npx` で起動され、`@latest` はプロセス開始時に解決されます。すでに起動している Claude Desktop は、どれだけ新しい版が公開されていても起動時のコピーを保持し続けます。完全に終了してから開き直してください。 +- **ブリッジの版を固定していると復旧しません。** MCP クライアント設定でこのリリースより古い版を pin している場合、再起動しても解決しません。まず pin を外す(または進める)必要があります。 + +Worker とブリッジは同一リリースで出るため、このリリース以降のブリッジであれば設定変更は不要です。 + ## Memory Model このプロジェクトは GitHub を AI 作業の visible state store として扱います。 diff --git a/README.md b/README.md index fcabf4d..ec88590 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,16 @@ It is the search-oriented counterpart to [github-webhook-mcp](https://github.com - push-based awareness of what just happened - hybrid retrieval (dense + sparse) of the state that matters for the next step +## Breaking change: MCP protocol revision 2026-07-28 + +From this release the Worker serves **MCP protocol revision 2026-07-28 only**. It keeps no compatibility lane for the previous revision. + +- **Bridge versions older than this release stop working.** They open a session with `initialize`, which the Worker no longer answers. The failure is quiet: the bridge does not crash, it returns the protocol error as tool output text. +- **Restart Claude Desktop to pick up the new bridge.** The bridge is launched with `npx`, and `@latest` is resolved at process start — an already-running Claude Desktop keeps the copy it started with, however new the published version is. Quit it fully and reopen. +- **Pinning the bridge version leaves you stuck.** If your MCP client config pins a version older than this release, restarting does not help; remove the pin (or move it forward) first. + +The Worker and the bridge ship together, so a bridge from this release or later needs no configuration change. + ## Memory Model The project treats GitHub as a visible state store for AI work. diff --git a/docs/0-requirements.ja.md b/docs/0-requirements.ja.md index e9fdf86..b37958e 100644 --- a/docs/0-requirements.ja.md +++ b/docs/0-requirements.ja.md @@ -94,6 +94,41 @@ params の静的ミラーから応答する(起動時を auth/network なし enum から `wiki_doc` が抜けたまま気付けず、Worker は受け付けるのにクライアント側で 値が弾かれていた(gh#181)。 +#### プロトコル版 2026-07-28、単レーン + +Worker が提供する MCP プロトコル版は **2026-07-28 のみ**。2025 系の互換レーンは +持たない。この版が要求するリクエスト毎の `_meta` envelope を欠くリクエスト +(2025 系の `initialize` ハンドシェイクを含む)は、endpoint が提供する唯一の版を +名指しした unsupported-protocol-version エラーで返る。SDK は旧版向けの +ステートレスなフォールバックを用意しているが、意図して有効化していない(gh#224)。 + +理由はフォールバックが技術的に誤りだからではなく、保守されないフォールバックが +死にコード化するからである。畳む条件は観測可能(旧レーンへの到達がゼロになる)だが、 +観測しに行く者がいない。破断はコードではなく別チャネル、すなわち minor リリースと +README 注意書きで吸収する。 + +ステートレス化の帰結: + +- セッションが無い。各リクエストはそのリクエスト専用に組み立てたサーバー + インスタンスが処理する。セッション ID から解決するものは無く、`mcp-session-id` + も発行しない。 +- ユーザー同一性は、処理中のリクエストが持つ OAuth props から取る。インスタンスの + フィールドではなく、ハンドラのリクエスト毎 auth context 経由で読む。 +- かつて MCP を提供していた Durable Object(`RagMcpAgent` / `RagMcpAgentV2`)は + 経路から外れた。過去の migration が名指しするクラスはスクリプト内に存在せねば + ならないという Cloudflare の制約のため、退役スタブとして export だけ残す。撤去には + `deleted_classes` migration が要る。実データを保持する `IssueStore` は無変更。 + +この版は本 repository の 2 成果物のあいだの私的な契約である。Worker へ到達する経路は +npx ブリッジ(`mcp-server/`)だけ——`server.json` は stdio トランスポートしか宣言 +していない——ため、第三者クライアントが版を交渉することはなく、ブリッジ側は探索せず +版を pin する。ブリッジが Claude Desktop に向ける面は SDK v1 / 2025 系のまま。 +そちらの移行は別 issue(gh#228)。 + +Worker は全員共有の 1 デプロイ、ブリッジは利用者ごとであるため、切替は非対称になる。 +Worker は一斉に飛び、ブリッジはホストプロセスの再起動でようやく追随する。版を固定して +いる利用者は再起動しても復旧しない。この層の切り捨ては明示的に受け入れる。 + ### 2. Webhook Receiver webhook receiver は GitHub event を near real time で取り込む。 diff --git a/docs/0-requirements.md b/docs/0-requirements.md index 16261ab..a601852 100644 --- a/docs/0-requirements.md +++ b/docs/0-requirements.md @@ -95,6 +95,45 @@ The check compares two axes: param names and, for enum params, their values — a name-only comparison let the proxy `type` enum omit `wiki_doc` while the Worker accepted it, so clients rejected the value before any request was sent (gh#181). +#### Protocol revision: 2026-07-28, single lane + +The Worker serves MCP protocol revision **2026-07-28 only**. There is no +compatibility lane for the 2025 era: a request without the revision's +per-request `_meta` envelope — including a 2025 `initialize` handshake — is +answered with the unsupported-protocol-version error naming the one revision +the endpoint serves. The SDK offers a stateless fallback for the older era; +it is deliberately not enabled (gh#224). + +The reason is not that a fallback would be technically wrong, but that an +unmaintained one becomes dead code. Its removal condition is observable (no +traffic reaches the old lane) yet nobody goes and observes it. The breakage is +absorbed on a different channel instead: a minor release plus a README notice. + +Consequences of statelessness: + +- No session. Each request is served by a server instance built for it alone, + so nothing is resolved from a session ID and `mcp-session-id` is not issued. +- User identity comes from the OAuth props of the request being served, read + through the handler's per-request auth context rather than from an instance + field. +- The Durable Objects that once served MCP (`RagMcpAgent`, `RagMcpAgentV2`) are + out of the serving path. Their classes remain exported as retired stubs + because Cloudflare requires every class named in a past migration to exist in + the script; removing them needs a `deleted_classes` migration. `IssueStore`, + which holds real data, is untouched. + +The revision is a private contract between this repository's two artifacts. The +npx bridge (`mcp-server/`) is the only way to reach the Worker — `server.json` +declares stdio transport alone — so no third-party client negotiates with it, +and the bridge pins the revision instead of probing for it. The bridge's own +face toward Claude Desktop stays on SDK v1 / the 2025 era; that side is a +separate migration (gh#228). + +Because the Worker is one shared deployment and each bridge is per user, the +flip is asymmetric: the Worker moves at once, while a bridge follows only when +its host process restarts. A user pinned to an older bridge version does not +recover by restarting. That cohort is explicitly cut off. + ### 2. Webhook Receiver The webhook receiver ingests GitHub events in near real time. diff --git a/mcp-server/README.md b/mcp-server/README.md index 600a71f..45c42d4 100644 --- a/mcp-server/README.md +++ b/mcp-server/README.md @@ -4,10 +4,20 @@ Stdio MCP proxy that bridges local MCP clients (Claude Desktop, Claude Code, etc This package is the **client-side proxy only**. The actual indexing pipeline (Vectorize + D1 FTS5 + Workers AI BGE-M3 + cross-encoder rerank) runs on the Worker. See the [main repository](https://github.com/Liplus-Project/github-rag-mcp) for architecture and self-hosting instructions. +## Breaking change: MCP protocol revision 2026-07-28 + +From this release the Worker serves **MCP protocol revision 2026-07-28 only**, with no compatibility lane for the previous revision. + +- **Proxy versions older than this release stop working.** They open a session with `initialize`, which the Worker no longer answers. The failure is quiet: the proxy does not crash, it returns the protocol error as tool output text. +- **Restart Claude Desktop to pick up the new proxy.** `npx` resolves `@latest` at process start, so an already-running Claude Desktop keeps the copy it launched with however new the published version is. Quit it fully and reopen. +- **Pinning the proxy version leaves you stuck.** If your MCP client config pins a version older than this release, restarting does not help; remove the pin (or move it forward) first. + +Nothing else changes for you: the proxy still speaks the same MCP dialect to Claude Desktop, and the same `search` tool with the same parameters. + ## What this proxy does -- Speaks stdio MCP locally to your client. -- Forwards `tools/call` to the Worker's Streamable HTTP MCP endpoint (`/mcp`). +- Speaks stdio MCP locally to your client (SDK v1, unchanged by the revision above). +- Forwards `tools/call` to the Worker's `/mcp` endpoint over MCP protocol revision 2026-07-28. That side is stateless: no `initialize` handshake and no session id — each call carries the revision's per-request envelope. - Handles OAuth 2.1 with PKCE against the Worker (browser-based localhost callback). - Caches access and refresh tokens under `~/.github-rag-mcp/` (mode `0600`). @@ -123,6 +133,7 @@ The browser callback never leaves your machine; the authorization code is delive URI set does not cover the callback port selected for this authorization. - **`OAuth callback timed out after 5 minutes`.** Re-invoke any tool to restart the flow. - **`Failed to reach worker`.** Check that `RAG_WORKER_URL` is correct and reachable from your machine. +- **`Unsupported protocol version`, or a tool that answers with a protocol error instead of results.** The proxy predates the 2026-07-28 flip. Quit Claude Desktop fully and reopen so `npx` fetches the current version; if your config pins a version, move the pin forward first. - **Stale credentials.** Remove `~/.github-rag-mcp/oauth-tokens.json` (and optionally `oauth-client.json`) and retry. ## Links diff --git a/mcp-server/package-lock.json b/mcp-server/package-lock.json index 8886daa..4ca9732 100644 --- a/mcp-server/package-lock.json +++ b/mcp-server/package-lock.json @@ -7,8 +7,9 @@ "": { "name": "github-rag-mcp", "version": "0.1.0", - "license": "MIT", + "license": "Apache-2.0", "dependencies": { + "@modelcontextprotocol/client": "^2.0.0", "@modelcontextprotocol/sdk": "^1.0.0" }, "bin": { @@ -275,6 +276,54 @@ "node": ">=18" } }, + "node_modules/@modelcontextprotocol/client": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@modelcontextprotocol/client/-/client-2.0.0.tgz", + "integrity": "sha512-8f1OghQ2rjzIOfqgUCP+8GiUWqRs89njoWLNqAe8kWmDePv3s1fZXseej+QXemssEuuOvLLmLO/kqM3IQHtISw==", + "license": "MIT", + "dependencies": { + "@modelcontextprotocol/core": "2.0.0", + "cross-spawn": "^7.0.5", + "eventsource": "^3.0.2", + "eventsource-parser": "^3.0.0", + "jose": "^6.1.3", + "pkce-challenge": "^5.0.0", + "zod": "^4.2.0" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@modelcontextprotocol/client/node_modules/zod": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.4.3.tgz", + "integrity": "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/@modelcontextprotocol/core": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@modelcontextprotocol/core/-/core-2.0.0.tgz", + "integrity": "sha512-pJCEwGG7Lfr/+PQp9ZTwKXNeO5wzbfKL7H3MYpCorM4oFBoQrdjnBgEoqG+RjhsvS1FKrDbKux+M1HhlnGWqcA==", + "license": "MIT", + "dependencies": { + "zod": "^4.2.0" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@modelcontextprotocol/core/node_modules/zod": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.4.3.tgz", + "integrity": "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, "node_modules/@modelcontextprotocol/sdk": { "version": "1.29.0", "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.29.0.tgz", diff --git a/mcp-server/package.json b/mcp-server/package.json index e190a65..cdce7bd 100644 --- a/mcp-server/package.json +++ b/mcp-server/package.json @@ -18,6 +18,7 @@ "pack:mcpb": "mcpb pack" }, "dependencies": { + "@modelcontextprotocol/client": "^2.0.0", "@modelcontextprotocol/sdk": "^1.0.0" }, "devDependencies": { diff --git a/mcp-server/server/index.js b/mcp-server/server/index.js index a49da86..2e0e772 100644 --- a/mcp-server/server/index.js +++ b/mcp-server/server/index.js @@ -3,12 +3,25 @@ * GitHub RAG MCP — Cloudflare Worker bridge * * Thin stdio MCP server that proxies tool calls to a remote - * Cloudflare Worker + Durable Object backend via Streamable HTTP. - * Authenticates via OAuth 2.1 with PKCE (localhost callback). + * Cloudflare Worker backend. Authenticates via OAuth 2.1 with PKCE + * (localhost callback). * * Tools are proxied to the Worker's MCP endpoint: * search — unified hybrid search / time-ordered activity scan / * inline doc content fetch via Vectorize + Workers AI + * + * The bridge has two independent protocol faces (issue #224): + * + * Claude Desktop -> bridge : SDK v1 stdio server, 2025-era. Unchanged. + * bridge -> Worker : SDK v2 client pinned to protocol revision + * 2026-07-28. Stateless — no `initialize` + * handshake and no `mcp-session-id`; every + * request carries the per-request `_meta` + * envelope the revision requires. + * + * The Worker's revision is a private contract between the two artifacts of + * this repository, so the Desktop face is not bound by it. Moving the Desktop + * face to SDK v2 is issue #228. */ import { Server } from "@modelcontextprotocol/sdk/server/index.js"; import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; @@ -24,6 +37,7 @@ import { join } from "node:path"; import { exec } from "node:child_process"; import { createRequire } from "node:module"; import { supportsRedirectUris } from "./oauth-client-registration.js"; +import { createRemoteClient } from "./remote-client.js"; import { TOOLS } from "./tools.js"; const require = createRequire(import.meta.url); @@ -328,87 +342,35 @@ async function getAccessToken() { return _cachedTokens.access_token; } -/** Build common headers with OAuth Bearer auth */ -async function authHeaders(extra) { - const h = { ...extra }; - const token = await getAccessToken(); - if (token) h["Authorization"] = `Bearer ${token}`; - return h; -} - -// ── Remote MCP Session (lazy, reused) ──────────────────────────────────────── - -let _sessionId = null; - -async function getSessionId() { - if (_sessionId) return _sessionId; - - const res = await fetch(`${WORKER_URL}/mcp`, { - method: "POST", - headers: await authHeaders({ - "Content-Type": "application/json", - Accept: "application/json, text/event-stream", - }), - body: JSON.stringify({ - jsonrpc: "2.0", - method: "initialize", - params: { - protocolVersion: "2024-11-05", - capabilities: {}, - clientInfo: { name: "github-rag-mcp-bridge", version: PACKAGE_VERSION }, - }, - id: "init", - }), - }); - - _sessionId = res.headers.get("mcp-session-id") || ""; - return _sessionId; -} +// ── Remote MCP Client (lazy, reused) ───────────────────────────────────────── +// Construction and caching live in ./remote-client.js so they can be tested +// without importing this module (which connects the stdio transport on import). + +const remote = createRemoteClient({ + workerUrl: WORKER_URL, + clientVersion: PACKAGE_VERSION, + // The OAuth flow above stays the source of tokens; this only hands the + // current one over, and clears the cache when the Worker says it is stale so + // the next `token()` re-mints. + authProvider: { + token: () => getAccessToken(), + onUnauthorized: async () => { + _cachedTokens = null; + await getAccessToken(); + }, + }, +}); async function callRemoteTool(name, args) { - const sessionId = await getSessionId(); - - const res = await fetch(`${WORKER_URL}/mcp`, { - method: "POST", - headers: await authHeaders({ - "Content-Type": "application/json", - Accept: "application/json, text/event-stream", - "mcp-session-id": sessionId, - }), - body: JSON.stringify({ - jsonrpc: "2.0", - method: "tools/call", - params: { name, arguments: args }, - id: crypto.randomUUID(), - }), - }); - - // 401 = token expired or revoked, re-authenticate and retry - if (res.status === 401) { - _cachedTokens = null; - _sessionId = null; - return callRemoteTool(name, args); - } - - const text = await res.text(); - - // Streamable HTTP may return SSE format - const dataLine = text.split("\n").find((l) => l.startsWith("data: ")); - const json = dataLine ? JSON.parse(dataLine.slice(6)) : JSON.parse(text); - - if (json.error) { - // Session expired — retry once with a fresh session - if (json.error.code === -32600 || json.error.code === -32001) { - _sessionId = null; - return callRemoteTool(name, args); - } - return { content: [{ type: "text", text: JSON.stringify(json.error) }] }; - } + // Resolve credentials first so an interactive-auth requirement surfaces as + // OAuthPendingError from here, where the caller already handles it, rather + // than from inside the transport wrapped as a network failure. + await getAccessToken(); - return json.result; + return await remote.callTool(name, args); } -// ── MCP Server Setup ───────────────────────────────────────────────────────── +// ── MCP Server Setup (Claude Desktop face — SDK v1, unchanged) ─────────────── const server = new Server( { name: "github-rag-mcp", version: PACKAGE_VERSION }, diff --git a/mcp-server/server/remote-client.d.ts b/mcp-server/server/remote-client.d.ts new file mode 100644 index 0000000..7ffee58 --- /dev/null +++ b/mcp-server/server/remote-client.d.ts @@ -0,0 +1,37 @@ +/** + * Types for `remote-client.js`. + * + * Hand-written because the bridge is plain JavaScript while the repository's + * `tsc --noEmit` gate covers `src/`, and `src/mcp-stateless-contract.test.ts` + * imports this module to assert the Worker <-> bridge protocol contract. The + * surface is three functions; keep this file in step when it changes. + */ + +import type { Client } from "@modelcontextprotocol/client"; + +export declare const WORKER_PROTOCOL_VERSION: "2026-07-28"; + +export interface RemoteClientOptions { + /** Worker origin; `/mcp` is appended. */ + workerUrl: string; + /** Reported as this client's version. */ + clientVersion: string; + /** Bearer credentials, per the SDK's `AuthProvider` shape. */ + authProvider?: { + token(): Promise; + onUnauthorized?(): Promise; + }; + /** Fetch override; tests route it at an in-process handler. */ + fetch?: typeof fetch; +} + +export interface RemoteClient { + /** Connect on first use, then reuse. */ + getClient(): Promise; + /** Call a Worker tool, dropping the cached client on failure. */ + callTool(name: string, args: unknown): Promise; + /** Forget the cached client. The next call reconnects. */ + reset(): Promise; +} + +export declare function createRemoteClient(options: RemoteClientOptions): RemoteClient; diff --git a/mcp-server/server/remote-client.js b/mcp-server/server/remote-client.js new file mode 100644 index 0000000..b0f034a --- /dev/null +++ b/mcp-server/server/remote-client.js @@ -0,0 +1,89 @@ +/** + * The bridge's client face toward the Cloudflare Worker. + * + * Split out of index.js for the same reason tools.js was: index.js connects the + * stdio transport at import time, so nothing in it can be exercised by a test. + * This module has no side effects on import, which lets the Worker <-> bridge + * protocol contract be asserted directly (see + * `src/mcp-stateless-contract.test.ts` in the repository root project). + * + * Protocol revision 2026-07-28, pinned (issue #224). The Worker serves that one + * revision and rejects every other, so negotiation would only add a round trip + * and a fallback branch that can never succeed. Pinning also means there is no + * `initialize` handshake and no `mcp-session-id`: each request carries the + * per-request `_meta` envelope the revision requires, and the SDK client + * attaches it. + * + * There is no session to hold. What is cached here is the client object and its + * transport, not server state — a dropped connection costs a reconnect, never a + * lost session. + */ +import { Client, StreamableHTTPClientTransport } from "@modelcontextprotocol/client"; + +/** The single protocol revision the Worker serves. */ +export const WORKER_PROTOCOL_VERSION = "2026-07-28"; + +/** + * Build a lazily-connecting MCP client for the Worker. + * + * @param {object} options + * @param {string} options.workerUrl Worker origin; `/mcp` is appended. + * @param {string} options.clientVersion Reported as this client's version. + * @param {object} [options.authProvider] Bearer credentials, per the SDK's + * `AuthProvider` shape (`token()` plus optional `onUnauthorized()`). + * @param {typeof fetch} [options.fetch] Fetch override; tests route it at an + * in-process handler instead of the network. + */ +export function createRemoteClient({ workerUrl, clientVersion, authProvider, fetch }) { + let client = null; + let connecting = null; + + async function getClient() { + if (client) return client; + + // Concurrent tool calls must share one connect attempt, not race two. + connecting ??= (async () => { + const next = new Client( + { name: "github-rag-mcp-bridge", version: clientVersion }, + { versionNegotiation: { mode: { pin: WORKER_PROTOCOL_VERSION } } }, + ); + const transport = new StreamableHTTPClientTransport(new URL(`${workerUrl}/mcp`), { + ...(authProvider ? { authProvider } : {}), + ...(fetch ? { fetch } : {}), + }); + await next.connect(transport); + client = next; + return next; + })(); + + try { + return await connecting; + } catch (err) { + // Let the next call retry from scratch rather than inherit the failure. + connecting = null; + throw err; + } + } + + /** Forget the cached client. The next call reconnects. */ + async function reset() { + const stale = client; + client = null; + connecting = null; + if (stale) await stale.close().catch(() => {}); + } + + async function callTool(name, args) { + const active = await getClient(); + try { + return await active.callTool({ name, arguments: args }); + } catch (err) { + // A dead transport would otherwise be cached forever. Dropping it costs + // one reconnect on the next call; keeping it costs every later call. + await reset(); + throw err; + } + } + + return { getClient, callTool, reset }; +} diff --git a/package-lock.json b/package-lock.json index 9b8faa1..79c83f7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,8 +10,8 @@ "license": "Apache-2.0", "dependencies": { "@cloudflare/workers-oauth-provider": "^0.3.1", - "@modelcontextprotocol/sdk": "^1.0.0", - "agents": "^0.19.0", + "@modelcontextprotocol/server": "2.0.0", + "agents": "^0.20.1", "zod": "^4.0.0" }, "devDependencies": { @@ -310,18 +310,18 @@ } }, "node_modules/@babel/runtime": { - "version": "7.29.2", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.2.tgz", - "integrity": "sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.7.tgz", + "integrity": "sha512-Nq8OhGWiZIZGV6hLHoyAKLLcJihP/xFeBMGJoUrxTX2psI8dCifzLhZISFb+VWS3wFMRDmCGw5R+dOySCqPLhw==", "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/runtime-corejs3": { - "version": "7.29.2", - "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.29.2.tgz", - "integrity": "sha512-Lc94FOD5+0aXhdb0Tdg3RUtqT6yWbI/BbFWvlaSJ3gAb9Ks+99nHRDKADVqC37er4eCB0fHyWT+y+K3QOvJKbw==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.29.7.tgz", + "integrity": "sha512-ppj9ouYku+RX0ljtgZd+KMO5mkM2bCqg8H2PYAFWnLsHEIKIdRojqbJ2i3eVHrisuxy7nOFCmngTDdWtUCdXUQ==", "license": "MIT", "dependencies": { "core-js-pure": "^3.48.0" @@ -381,36 +381,6 @@ "integrity": "sha512-gAmrUZSGtKc3AiBL71iNWxDsyUC5uMaKKGdvzYsBoTW/xi42JQHl7eKV2OYzCUqvc+D2RCcf7EXY2iCyFIk6og==", "license": "MIT" }, - "node_modules/@cloudflare/codemode": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/@cloudflare/codemode/-/codemode-0.5.1.tgz", - "integrity": "sha512-PcX5+qAvupi8p1bMLKhqvPHziZpDubbrxDIvVH+iuuNUaFyOxxWNS9HplfFqIULqUzDPdFf1w7IiSCKHp7GDgg==", - "license": "MIT", - "dependencies": { - "@types/json-schema": "^7.0.15", - "acorn": "^8.17.0" - }, - "peerDependencies": { - "@modelcontextprotocol/sdk": "^1.25.0", - "@tanstack/ai": ">=0.8.0 <1.0.0", - "ai": "^6.0.0 || ^7.0.0", - "zod": "^4.0.0" - }, - "peerDependenciesMeta": { - "@modelcontextprotocol/sdk": { - "optional": true - }, - "@tanstack/ai": { - "optional": true - }, - "ai": { - "optional": true - }, - "zod": { - "optional": true - } - } - }, "node_modules/@cloudflare/kv-asset-handler": { "version": "0.5.0", "resolved": "https://registry.npmjs.org/@cloudflare/kv-asset-handler/-/kv-asset-handler-0.5.0.tgz", @@ -438,16 +408,16 @@ } }, "node_modules/@cloudflare/vitest-pool-workers": { - "version": "0.16.18", - "resolved": "https://registry.npmjs.org/@cloudflare/vitest-pool-workers/-/vitest-pool-workers-0.16.18.tgz", - "integrity": "sha512-TEktXyevK9lkTWouElbIcDPK3YEfV+Szqgnlq5sNk+KYZR3LiDdYDaGNmUYgiT2LiiFeGU2yzCrcgmN8mJhqWQ==", + "version": "0.16.20", + "resolved": "https://registry.npmjs.org/@cloudflare/vitest-pool-workers/-/vitest-pool-workers-0.16.20.tgz", + "integrity": "sha512-buw0YgsAMT7s60wcmyxbtciEJjMJzKcWzayDMPhWaqMqfQzW+0WPLV67Lobn4C80nkNQhYocEJPnrEhLWnOf+A==", "dev": true, "license": "MIT", "dependencies": { "cjs-module-lexer": "1.2.3", "esbuild": "0.28.1", - "miniflare": "4.20260617.1", - "wrangler": "4.103.0", + "miniflare": "4.20260625.0", + "wrangler": "4.105.0", "zod": "3.25.76" }, "peerDependencies": { @@ -456,6 +426,49 @@ "vitest": "^4.1.0" } }, + "node_modules/@cloudflare/vitest-pool-workers/node_modules/path-to-regexp": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.3.0.tgz", + "integrity": "sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@cloudflare/vitest-pool-workers/node_modules/wrangler": { + "version": "4.105.0", + "resolved": "https://registry.npmjs.org/wrangler/-/wrangler-4.105.0.tgz", + "integrity": "sha512-7dXFH6OLj1Fv0y6ZeRPUxFTkp+duWD7/xxVi/1c0vfOeEYwIFKWB7cdqnY05DvY1Ta3BnqAwRkXfLs8PDj538g==", + "dev": true, + "license": "MIT OR Apache-2.0", + "dependencies": { + "@cloudflare/kv-asset-handler": "0.5.0", + "@cloudflare/unenv-preset": "2.16.1", + "blake3-wasm": "2.1.5", + "esbuild": "0.28.1", + "miniflare": "4.20260625.0", + "path-to-regexp": "6.3.0", + "unenv": "2.0.0-rc.24", + "workerd": "1.20260625.1" + }, + "bin": { + "cf-wrangler": "bin/cf-wrangler.js", + "wrangler": "bin/wrangler.js", + "wrangler2": "bin/wrangler.js" + }, + "engines": { + "node": ">=22.0.0" + }, + "optionalDependencies": { + "fsevents": "2.3.3" + }, + "peerDependencies": { + "@cloudflare/workers-types": "^4.20260625.1" + }, + "peerDependenciesMeta": { + "@cloudflare/workers-types": { + "optional": true + } + } + }, "node_modules/@cloudflare/vitest-pool-workers/node_modules/zod": { "version": "3.25.76", "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", @@ -467,9 +480,9 @@ } }, "node_modules/@cloudflare/workerd-darwin-64": { - "version": "1.20260617.1", - "resolved": "https://registry.npmjs.org/@cloudflare/workerd-darwin-64/-/workerd-darwin-64-1.20260617.1.tgz", - "integrity": "sha512-jWwmgEVVWbsHNrLSNXzwjJaH90VzRxq1cWkQFUidxyeUPnMxemeNE8I9qFAfrpzGgE11e9sKDcE3ettJW08swQ==", + "version": "1.20260625.1", + "resolved": "https://registry.npmjs.org/@cloudflare/workerd-darwin-64/-/workerd-darwin-64-1.20260625.1.tgz", + "integrity": "sha512-naCfBv0WnnTQIQPTniqMoUlklOIFjrAcSn1X+IAOhY8aFLF/xGYtFjs1eEE8sFib3ZuChGGpU23FFORVczqr0A==", "cpu": [ "x64" ], @@ -484,9 +497,9 @@ } }, "node_modules/@cloudflare/workerd-darwin-arm64": { - "version": "1.20260617.1", - "resolved": "https://registry.npmjs.org/@cloudflare/workerd-darwin-arm64/-/workerd-darwin-arm64-1.20260617.1.tgz", - "integrity": "sha512-LHH7b565g9znfCUOkwbec6FG2rmRbsgCy6aJiU9KN662mNheWl5sw/iKleiFSiljPKQQP3HkjnC/NSkdgi/aSA==", + "version": "1.20260625.1", + "resolved": "https://registry.npmjs.org/@cloudflare/workerd-darwin-arm64/-/workerd-darwin-arm64-1.20260625.1.tgz", + "integrity": "sha512-jmH6zjp6Wrux46+qtFwDwrj+vd7s5bdwEqeGvdnwE0a4IEeAhKs0L42HQOyID+g5lkrHq9m55+AbhtmRAm63Pw==", "cpu": [ "arm64" ], @@ -501,9 +514,9 @@ } }, "node_modules/@cloudflare/workerd-linux-64": { - "version": "1.20260617.1", - "resolved": "https://registry.npmjs.org/@cloudflare/workerd-linux-64/-/workerd-linux-64-1.20260617.1.tgz", - "integrity": "sha512-FMnaAKXe4Cfd8TQurCVd9fs2XQVBFRCsP+Id/SRdUv89MlwYu9zXfoyx6BxM+brPTIUK38SHbo8iaxiwzLi9JQ==", + "version": "1.20260625.1", + "resolved": "https://registry.npmjs.org/@cloudflare/workerd-linux-64/-/workerd-linux-64-1.20260625.1.tgz", + "integrity": "sha512-MiQkpA/dX8d83Zp64pzHUKfd6ca4cvwxnNobSP6CnXvfESvnNI9pfa+nfwnParla36sPmnYntNkjR7NjRuDeKQ==", "cpu": [ "x64" ], @@ -518,9 +531,9 @@ } }, "node_modules/@cloudflare/workerd-linux-arm64": { - "version": "1.20260617.1", - "resolved": "https://registry.npmjs.org/@cloudflare/workerd-linux-arm64/-/workerd-linux-arm64-1.20260617.1.tgz", - "integrity": "sha512-MRoifFYcqbxxIIQy7PqO5tFY/qPFSnjXzakWl0sO93l+HLyG35jRAgOi6jfqa4kBxc7gKKtH861DcewjxUfkjA==", + "version": "1.20260625.1", + "resolved": "https://registry.npmjs.org/@cloudflare/workerd-linux-arm64/-/workerd-linux-arm64-1.20260625.1.tgz", + "integrity": "sha512-LxxW7Qv60Xvv37+w6gUSDpYZziyqMy+cZWd9IvSA5ehVgKAxmzEaYPMiSZlxk32nbIWL9u/tfjXYCOKJ4Lo+XQ==", "cpu": [ "arm64" ], @@ -535,9 +548,9 @@ } }, "node_modules/@cloudflare/workerd-windows-64": { - "version": "1.20260617.1", - "resolved": "https://registry.npmjs.org/@cloudflare/workerd-windows-64/-/workerd-windows-64-1.20260617.1.tgz", - "integrity": "sha512-rgBV9wQrv0OSKgCTTbhFUFY3sLGNANZ88aqaLvtmEn2gmbFVb1J4PDGochVUdB7NSEp4D/ghHva6/8SZmbONpw==", + "version": "1.20260625.1", + "resolved": "https://registry.npmjs.org/@cloudflare/workerd-windows-64/-/workerd-windows-64-1.20260625.1.tgz", + "integrity": "sha512-LH6iIX1HHaTwVKV5VokDxxUErXJzQoNZFRwVm7Vx/3fB/ApcTcRCUaMqcxI4as94jEUqg+pmX5czOndiveohow==", "cpu": [ "x64" ], @@ -587,31 +600,11 @@ "@jridgewell/sourcemap-codec": "^1.4.10" } }, - "node_modules/@emnapi/core": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.10.0.tgz", - "integrity": "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==", - "license": "MIT", - "optional": true, - "dependencies": { - "@emnapi/wasi-threads": "1.2.1", - "tslib": "^2.4.0" - } - }, "node_modules/@emnapi/runtime": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.10.0.tgz", - "integrity": "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==", - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, - "node_modules/@emnapi/wasi-threads": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz", - "integrity": "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==", + "version": "1.11.3", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.3.tgz", + "integrity": "sha512-Xz4Tpyki7XyrpbUK1jR1AhdAdaXyhhY4lZ3neLodmhpuWfy2PAQN5B46sAiU4liOXGLkHypn/qU+jvfWSCYYLA==", + "dev": true, "license": "MIT", "optional": true, "dependencies": { @@ -1035,12 +1028,13 @@ } }, "node_modules/@hono/node-server": { - "version": "1.19.12", - "resolved": "https://registry.npmjs.org/@hono/node-server/-/node-server-1.19.12.tgz", - "integrity": "sha512-txsUW4SQ1iilgE0l9/e9VQWmELXifEFvmdA1j6WFh/aFPj99hIntrSsq/if0UWyGVkmrRPKA1wCeP+UCr1B9Uw==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@hono/node-server/-/node-server-2.1.0.tgz", + "integrity": "sha512-XovyyCCnBzW+zKu+z/zq8hwNs4KOR5rEMAOxo2f40Q5xoOI37IMm6MIg2COOUtUApo0i6850MTBKH2u4QLGIqg==", "license": "MIT", + "peer": true, "engines": { - "node": ">=18.14.1" + "node": ">=20" }, "peerDependencies": { "hono": "^4" @@ -1102,6 +1096,43 @@ "@img/sharp-libvips-darwin-x64": "1.2.4" } }, + "node_modules/@img/sharp-freebsd-wasm32": { + "version": "0.35.2", + "resolved": "https://registry.npmjs.org/@img/sharp-freebsd-wasm32/-/sharp-freebsd-wasm32-0.35.2.tgz", + "integrity": "sha512-YoAxdnd8hPUkvLHd3bWY+YA8nw3xM/RyRopYucNsWHVSan8NLVM3X2volsfoRDcXdUJPg6tXahSd7HXPK7lRnw==", + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "dependencies": { + "@img/sharp-wasm32": "0.35.2" + }, + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-freebsd-wasm32/node_modules/@img/sharp-wasm32": { + "version": "0.35.2", + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.35.2.tgz", + "integrity": "sha512-Mrv4JQNYVQ94xH+jzZ9r+gowleN8mv2FTgKT+PI6bx5C0G8TdNYndu161pg2i7uoBwxy2ImPMHrJOM2LZef7Bw==", + "dev": true, + "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", + "optional": true, + "dependencies": { + "@emnapi/runtime": "^1.11.1" + }, + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, "node_modules/@img/sharp-libvips-darwin-arm64": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.4.tgz", @@ -1524,6 +1555,43 @@ "url": "https://opencollective.com/libvips" } }, + "node_modules/@img/sharp-webcontainers-wasm32": { + "version": "0.35.2", + "resolved": "https://registry.npmjs.org/@img/sharp-webcontainers-wasm32/-/sharp-webcontainers-wasm32-0.35.2.tgz", + "integrity": "sha512-QNV27pxs9wpApEiCfvHM1RDoP1w1+2KrUWWDPEhEwg+latvOrfuhWrHWZKwdSFwU6jh3myjw/yOCRsUIuOft3g==", + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@img/sharp-wasm32": "0.35.2" + }, + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-webcontainers-wasm32/node_modules/@img/sharp-wasm32": { + "version": "0.35.2", + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.35.2.tgz", + "integrity": "sha512-Mrv4JQNYVQ94xH+jzZ9r+gowleN8mv2FTgKT+PI6bx5C0G8TdNYndu161pg2i7uoBwxy2ImPMHrJOM2LZef7Bw==", + "dev": true, + "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", + "optional": true, + "dependencies": { + "@emnapi/runtime": "^1.11.1" + }, + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, "node_modules/@img/sharp-win32-arm64": { "version": "0.34.5", "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.5.tgz", @@ -1619,13 +1687,45 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@modelcontextprotocol/client": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@modelcontextprotocol/client/-/client-2.0.0.tgz", + "integrity": "sha512-8f1OghQ2rjzIOfqgUCP+8GiUWqRs89njoWLNqAe8kWmDePv3s1fZXseej+QXemssEuuOvLLmLO/kqM3IQHtISw==", + "license": "MIT", + "peer": true, + "dependencies": { + "@modelcontextprotocol/core": "2.0.0", + "cross-spawn": "^7.0.5", + "eventsource": "^3.0.2", + "eventsource-parser": "^3.0.0", + "jose": "^6.1.3", + "pkce-challenge": "^5.0.0", + "zod": "^4.2.0" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@modelcontextprotocol/core": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@modelcontextprotocol/core/-/core-2.0.0.tgz", + "integrity": "sha512-pJCEwGG7Lfr/+PQp9ZTwKXNeO5wzbfKL7H3MYpCorM4oFBoQrdjnBgEoqG+RjhsvS1FKrDbKux+M1HhlnGWqcA==", + "license": "MIT", + "dependencies": { + "zod": "^4.2.0" + }, + "engines": { + "node": ">=20" + } + }, "node_modules/@modelcontextprotocol/sdk": { - "version": "1.29.0", - "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.29.0.tgz", - "integrity": "sha512-zo37mZA9hJWpULgkRpowewez1y6ML5GsXJPY8FI0tBBCd77HEvza4jDqRKOXgHNn867PVGCyTdzqpz0izu5ZjQ==", + "version": "1.30.0", + "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.30.0.tgz", + "integrity": "sha512-xKd8OIzlqNzcqcNumGAa6g+PW2kjD5vrpcKOnfldAUPP3j7lnqMPwlTXQm8gF+UwH72z0lqaRbjr9hqGz0eITA==", "license": "MIT", + "peer": true, "dependencies": { - "@hono/node-server": "^1.19.9", + "@hono/node-server": "^1.19.9 || ^2.0.5", "ajv": "^8.17.1", "ajv-formats": "^3.0.1", "content-type": "^1.0.5", @@ -1659,28 +1759,23 @@ } } }, - "node_modules/@napi-rs/wasm-runtime": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.5.tgz", - "integrity": "sha512-AWPoBRJ9tsnVhor4sjO7rkni+7p+2IAEFj6cx06UgP10jkQHqay/36uRV/bFkgrh18D9vb4cr8Q0Pthskgzy+Q==", + "node_modules/@modelcontextprotocol/server": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@modelcontextprotocol/server/-/server-2.0.0.tgz", + "integrity": "sha512-YhHWdHfpFMQfd0prsEnxKeS3Qz3ytIGmsS0sth4KDjnacIT7hxk6hXHkJ9KysxlkvTM+WZAtQbbcUhdoP4Hvtw==", "license": "MIT", - "optional": true, "dependencies": { - "@tybys/wasm-util": "^0.10.2" + "@modelcontextprotocol/core": "2.0.0", + "zod": "^4.2.0" }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/Brooooooklyn" - }, - "peerDependencies": { - "@emnapi/core": "^1.7.1", - "@emnapi/runtime": "^1.7.1" + "engines": { + "node": ">=20" } }, "node_modules/@oxc-project/types": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.133.0.tgz", - "integrity": "sha512-KzkdCd6Uxqnf6l3HOw1xfatAlUURA0g14cvBYFyJ5SaNOQbOUvBr9PKArcPcrNIeRsBdgcUzOGrhKveVpvOIGA==", + "version": "0.143.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.143.0.tgz", + "integrity": "sha512-u6JZdLBTLotrNC9Vd6vPssINdzcCzleKAH6EJKImQb7GtYvX5keN2dxkoK44stCc4tffE6QQRtZTXVSzsLUlWA==", "license": "MIT", "funding": { "url": "https://github.com/sponsors/Boshen" @@ -1716,9 +1811,9 @@ "license": "MIT" }, "node_modules/@rolldown/binding-android-arm64": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.3.tgz", - "integrity": "sha512-454rs7jHngixp/NMxd5srYD57OnzSlZ/eFTETjORQHLwJG1lRtmNOJcBerZlfu4GjKqeq8aCCIQrMdHyhI51Hw==", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.2.3.tgz", + "integrity": "sha512-zrJtHDcaZJ1Fp7xf4hNl+7seH9Cn/N5TwLYkhgXREtBwAd/jaqW3uqeHxpDugJLVICWg4eW44kOQEGJ1r6jCGw==", "cpu": [ "arm64" ], @@ -1732,9 +1827,9 @@ } }, "node_modules/@rolldown/binding-darwin-arm64": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.3.tgz", - "integrity": "sha512-PcAhP+ynjURNyy8SKGl5DQP94aGuB/7JrXJb/t7P+hanXvQVMWzUvRRhBAcg/lNRadBhoUPqSoP4xw5tR/KBEA==", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.2.3.tgz", + "integrity": "sha512-ieIiibVCp0tX7TLu2cafoNPv8wJyYi01ekXpbf8q2j7F4rGAhhXb/eQh7ge9DRBY78GwmRQtvjZDux7EDbA8kA==", "cpu": [ "arm64" ], @@ -1748,9 +1843,9 @@ } }, "node_modules/@rolldown/binding-darwin-x64": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.3.tgz", - "integrity": "sha512-9YpfeUvSE2RS7wysJ81uOZkXJz7f7Q55H2Gvp3VEw/EsahqDtrphrZ0EwDLK5vvKOzaCrBsjF8JmnMLcUt78Gg==", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.2.3.tgz", + "integrity": "sha512-Zh9tCon19eDXJoihx0rqKhMUlMYqzwj3aPsSuHmI4RWZh62dWUL+DJN4C5YQya5TcQBJU/Fe8+rY0jhXTQITqA==", "cpu": [ "x64" ], @@ -1764,9 +1859,9 @@ } }, "node_modules/@rolldown/binding-freebsd-x64": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.3.tgz", - "integrity": "sha512-yB1IlAsSNHncV6SCTL27/MVGR5htvQsoGxIv5KMGXALp+Ll1wYsn+x98M9MW7qa+NdSbvrrY7ANI4wLJ0n1e6g==", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.2.3.tgz", + "integrity": "sha512-nGbJWewA1wrXXZiQhjAT5rhibGfns5ZNkDVqxsO6zJ3f3YvpoDNNmGMSbbhLuXKjNScaBJVOAboztAWVespQMg==", "cpu": [ "x64" ], @@ -1780,9 +1875,9 @@ } }, "node_modules/@rolldown/binding-linux-arm-gnueabihf": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.3.tgz", - "integrity": "sha512-Yi30IVAAfLUCy2MseFjbB1jAMDl1VMCAas5StnYp8da9+CKvMd2H2cbEjWcw5NPaPqzvYkVIaF1nNUG+b7u/sw==", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.2.3.tgz", + "integrity": "sha512-QNniJr5Kml0kDEB98jiDOJjXNroxIIi0IXIbdYzY26Xt1pVbeP62+KnoIZLwirOymX/0jDk/2gI/bNUv7A7OIw==", "cpu": [ "arm" ], @@ -1796,9 +1891,9 @@ } }, "node_modules/@rolldown/binding-linux-arm64-gnu": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.3.tgz", - "integrity": "sha512-jsO7R8To+AdlYgUmN5sHSCZbfhtMBkO0WUx8iORQnPcMMdgr7qM2DQmMwgabs3GhNztdmoKkMKQFHD6DTMCIQw==", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.2.3.tgz", + "integrity": "sha512-TkqEAcmmvH3I/q4114NB4RVt6241Dao48pF45uLcFGrwAaIn0iITgTAKP/dLjbN0R4buJjGb91+UHSoFmpgIWw==", "cpu": [ "arm64" ], @@ -1815,9 +1910,9 @@ } }, "node_modules/@rolldown/binding-linux-arm64-musl": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.3.tgz", - "integrity": "sha512-VWkUHwWriDciit80wleYwKILoR/KMvxh/IdwS/paX+ZgpuRpCrKLUdadJbc0NpBEiyhpYawsJ73j9aCvOH+f7Q==", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.2.3.tgz", + "integrity": "sha512-NHqjnxpsndf4MPymxteFAWHHfkTL8HjWh1KB7z23ofZ6QO2euONuxDXjat69dKZRALnGypg8k8SsK8vZJoXv1Q==", "cpu": [ "arm64" ], @@ -1834,9 +1929,9 @@ } }, "node_modules/@rolldown/binding-linux-ppc64-gnu": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.3.tgz", - "integrity": "sha512-5f1laC0SlIR0yDbFCd8acUhvJIag6N3zC5P7oUPN6wX0aOma+uKJ0wBDH5aq7I1PVI2ttTlhJwzwRIBnLiSGEg==", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.2.3.tgz", + "integrity": "sha512-6tbrbwfz5GB9DQ4Jwo6hy9v+vR31xZlvzZ6n5Xut6Hhx5PvrA9q/HsK8KMaYQp063iqZGXwNvZtYNLD7EM/x0w==", "cpu": [ "ppc64" ], @@ -1853,9 +1948,9 @@ } }, "node_modules/@rolldown/binding-linux-s390x-gnu": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.3.tgz", - "integrity": "sha512-Iq4ko0r4XsgbrF/LunNgHtAGLRRVE2kXonAXQ/MV0mC6jQpMOhW1SvtZja2EhC/kd05++bP78dsqBeIQyYJ6Yg==", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.2.3.tgz", + "integrity": "sha512-oyuXxXmoZHjXC917IAPFAAv4wWAa0cM9afk8nx1+9/jNNOX1uPf8yDA6p7G0RypOfw/X0PQt5IfoquY1um+zSg==", "cpu": [ "s390x" ], @@ -1872,9 +1967,9 @@ } }, "node_modules/@rolldown/binding-linux-x64-gnu": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.3.tgz", - "integrity": "sha512-B8m6tD5+/N5FeNQFbKlLA/2yVq9ycQP1SeedyEYYKWBNR3ZQbkvIUcNnDNM03lO1l5F2roiiFJGgvoLLyZXtSg==", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.2.3.tgz", + "integrity": "sha512-TytMwF2KVGqP2tgd0I1OY0PAv78dZRAYcF5ssDzjM34SUXCED3uXvSd5+lHoC0bTD6eEdFz7LdQNCO1y0oVk9w==", "cpu": [ "x64" ], @@ -1891,9 +1986,9 @@ } }, "node_modules/@rolldown/binding-linux-x64-musl": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.3.tgz", - "integrity": "sha512-pSdpdUJHkuCxun9LE7jvgUB9qsRgaiyNNCX7m/AvHTcq67AiT/Yhoxvw5zPfhrM8k/BfP8ce/hMOpthKDpEUow==", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.2.3.tgz", + "integrity": "sha512-/E9m3qstrJFVPoULV25mVQblSNExY2+kBsYe4sy0Tn0yOOgJ8wZbZt3KnRbF/XeU2Gl1STKUQnDNTqhIE5MD4A==", "cpu": [ "x64" ], @@ -1910,9 +2005,9 @@ } }, "node_modules/@rolldown/binding-openharmony-arm64": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.3.tgz", - "integrity": "sha512-OXXS3RKJgX2uLwM+gYyuH5omcH8fL1LJs96pZGgtetVCahON57+d4SJHzTgZiOjxgGkSnpXpOsWuPDGAKAigEg==", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.2.3.tgz", + "integrity": "sha512-Kr0OcsoQI816i6HOl3vFHpd1K0eZyh76zgfj4c1nTyaTsd5r2Mj1lwM4R90y/qaCfmTn9eHy0SKwi98eitRxug==", "cpu": [ "arm64" ], @@ -1925,28 +2020,10 @@ "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/@rolldown/binding-wasm32-wasi": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.3.tgz", - "integrity": "sha512-JTtb8BWFynicNSoPrehsCzBtOKjZ6jhMiPFEmOiuXg1Fl8dn2KHQob+GuPSGR0dryQa1PQJbzjF3dqO/whhjLg==", - "cpu": [ - "wasm32" - ], - "license": "MIT", - "optional": true, - "dependencies": { - "@emnapi/core": "1.10.0", - "@emnapi/runtime": "1.10.0", - "@napi-rs/wasm-runtime": "^1.1.4" - }, - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, "node_modules/@rolldown/binding-win32-arm64-msvc": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.3.tgz", - "integrity": "sha512-gEdFFEN70A/jxb2svrWsN3aDL7OUtmvlOy+6fa2jxG8K0wQ1ZbdeLGnidov6Yu5/733dI5ySfzFlQ/cb0bSz1g==", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.2.3.tgz", + "integrity": "sha512-hOtMwTqnME+/gJcH/PCZ0wn0zPUjiWOgkHpxbSJpfGKMezHltx1S7/k1SitzVa7Ww2cqrDDaFbZEhcJZO8o+Jw==", "cpu": [ "arm64" ], @@ -1960,9 +2037,9 @@ } }, "node_modules/@rolldown/binding-win32-x64-msvc": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.3.tgz", - "integrity": "sha512-eXB7CHuaQdqmJcc3koCNtNPmT/bj2gc999kUFgBxG8Ac0NdgXc4rkCHhqrgrhN3zddvvvrgzj1e90SuSfmyIXA==", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.2.3.tgz", + "integrity": "sha512-ekcqMMkI2PlhYnfzQnB/cEdYUVVJViWvoUyLrbzgDoi3Snfc1mVBwdnc306ufA5ejy8JSPjT2RlW1nQSjW7efg==", "cpu": [ "x64" ], @@ -2025,9 +2102,9 @@ } }, "node_modules/@speed-highlight/core": { - "version": "1.2.15", - "resolved": "https://registry.npmjs.org/@speed-highlight/core/-/core-1.2.15.tgz", - "integrity": "sha512-BMq1K3DsElxDWawkX6eLg9+CKJrTVGCBAWVuHXVUV2u0s2711qiChLSId6ikYPfxhdYocLNt3wWwSvDiTvFabw==", + "version": "1.2.23", + "resolved": "https://registry.npmjs.org/@speed-highlight/core/-/core-1.2.23.tgz", + "integrity": "sha512-iRoq6i6JDJP6Mt2A5JaPvzw0pgYHH6k92ij+yXiTrB7T2y9N789aWE3EHWj/5ztlJBokcCBja3iYLVdu5wgnkg==", "dev": true, "license": "CC0-1.0" }, @@ -2035,19 +2112,9 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==", - "devOptional": true, + "dev": true, "license": "MIT" }, - "node_modules/@tybys/wasm-util": { - "version": "0.10.2", - "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.2.tgz", - "integrity": "sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg==", - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, "node_modules/@types/chai": { "version": "5.2.3", "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.3.tgz", @@ -2086,23 +2153,17 @@ "integrity": "sha512-9VN+6yxLOPLOav+7PwjZbxiID2bVaeq0ED4qSQmdQTdjnXJSaCVKTR58t15oqH1H5t8Ng2ZX1SabJVoN9Q34bw==", "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/@vitest/expect": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.9.tgz", - "integrity": "sha512-vl/rYsUKcBr3SnQn166+XR5ZQcgMx3DQhFWdfli/cWpLnLUmbxZvyrJZotLFUryib+LtArYMSTJ5RbQ57ZqrlA==", + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.10.tgz", + "integrity": "sha512-YsCn+qAk1GWjQOWFEsEcL2gNQ0zmVmQu3T03qP6UyjhtmdtwtbuI+DASn/7iQB3HGTXkdBwGddzxPlmiql5vlA==", "dev": true, "license": "MIT", "dependencies": { "@standard-schema/spec": "^1.1.0", "@types/chai": "^5.2.2", - "@vitest/spy": "4.1.9", - "@vitest/utils": "4.1.9", + "@vitest/spy": "4.1.10", + "@vitest/utils": "4.1.10", "chai": "^6.2.2", "tinyrainbow": "^3.1.0" }, @@ -2111,13 +2172,13 @@ } }, "node_modules/@vitest/mocker": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.9.tgz", - "integrity": "sha512-EVkXzBjrPGM+cK8/ANWgBrkUCfJfb38/EfTSO8h7pWvKkyPkpWxvR7BkD2MyItMF62C97zAEoqdpUixwR/e+Rw==", + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.10.tgz", + "integrity": "sha512-v0xaezt+DKEmKfaxg133ldzADrwLGd7Ze1MfQQTYfvs8OqZIwbxyxaYURivwV7sWy5fqn3rH5uOrSp07bp44Ow==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/spy": "4.1.9", + "@vitest/spy": "4.1.10", "estree-walker": "^3.0.3", "magic-string": "^0.30.21" }, @@ -2138,9 +2199,9 @@ } }, "node_modules/@vitest/pretty-format": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.9.tgz", - "integrity": "sha512-s0iufns3iIFitdgm+YR7g1whCAaGtXz459VS9/PqyKDEEFgYIhsHOQmXgIgDuYCt7DeQmiZT0Qe2OA2p4ZPu5A==", + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.10.tgz", + "integrity": "sha512-W1HsjSH4MXQ9YfmmhLAoIYf1HRfekQCGngeIgcei6MP5QQGWUe0gkopdZQaVCFO+JDJMrAJGwa5pRpNpvy4P8Q==", "dev": true, "license": "MIT", "dependencies": { @@ -2151,13 +2212,13 @@ } }, "node_modules/@vitest/runner": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.9.tgz", - "integrity": "sha512-KXLMDtc7oe70+3mJfGrPUWPesswH+3sTxAMAMl8DG7I8IUQT4XW718dY5ID3vPUcmlu27CcKfY4P3h3I29SLJg==", + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.10.tgz", + "integrity": "sha512-IKI6kpIH+LmpROplyLwBBaCfMgOZOMsygVa6BARD6ahA04VRuJSa6OaVG7kRvSEMD870Vd91rSSw0eegtWyLGg==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/utils": "4.1.9", + "@vitest/utils": "4.1.10", "pathe": "^2.0.3" }, "funding": { @@ -2165,14 +2226,14 @@ } }, "node_modules/@vitest/snapshot": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.9.tgz", - "integrity": "sha512-Jc7RKGNBo8Z28WYIm0Niej4xdSPByRf6mU58VpHQkd6Zh05rlnA+twjbK5HyeIGHxrzsc3mJgS43uM0CZKzaIA==", + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.10.tgz", + "integrity": "sha512-xRkfOT1qpTAi/Ti4Y1LtfRc3kEuqxGw59eN2jN9pRWMtS/XDevekhcFSqvQqjUNGksfjMJu3Y+oJ+4Ypn2OaJw==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "4.1.9", - "@vitest/utils": "4.1.9", + "@vitest/pretty-format": "4.1.10", + "@vitest/utils": "4.1.10", "magic-string": "^0.30.21", "pathe": "^2.0.3" }, @@ -2181,9 +2242,9 @@ } }, "node_modules/@vitest/spy": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.9.tgz", - "integrity": "sha512-fHpsS6mIi+PiEW+vcRVOMkX1oSaPKne3VOclSFICPcGOmfKgXPU5iAah+wcNcj2xPrCCmfq99IDGf+EojhhvhA==", + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.10.tgz", + "integrity": "sha512-PLf/Ugvoq5wO/b4rwYCR1h2PSIdXz7wnkQFMiUpLdtM7l6pqVFcQIBEHyT1+l+cj7mNwAfZHzqXqDyjvOuwbDw==", "dev": true, "license": "MIT", "funding": { @@ -2191,13 +2252,13 @@ } }, "node_modules/@vitest/utils": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.9.tgz", - "integrity": "sha512-A51o8ymO5PpqlWNnBP9ZHPXDIpuMtTLlGSjN7la4US+LJzoUMyhwjA5QXlm39JexgwHKW4Xjs8Z2d3dLCXOeuA==", + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.10.tgz", + "integrity": "sha512-fy9am/HWxbaGt/Sawrp90vt6Y6jQwf1RX77cz3uwoJwJVMli/e1IEwRPnMNJ7vKfPTwo0diXifkpPvwH9v7nGA==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "4.1.9", + "@vitest/pretty-format": "4.1.10", "convert-source-map": "^2.0.0", "tinyrainbow": "^3.1.0" }, @@ -2210,6 +2271,7 @@ "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", "license": "MIT", + "peer": true, "dependencies": { "mime-types": "^3.0.0", "negotiator": "^1.0.0" @@ -2218,28 +2280,14 @@ "node": ">= 0.6" } }, - "node_modules/acorn": { - "version": "8.18.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.18.0.tgz", - "integrity": "sha512-lGq+9yr1/GuAWaVYIHRjvvySG5/4VfKIvC8EWxStPdcDh/Ka7FG3twP6v4d5BkravUilhIAsG4Qj83t02LWUPQ==", - "license": "MIT", - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, "node_modules/agents": { - "version": "0.19.0", - "resolved": "https://registry.npmjs.org/agents/-/agents-0.19.0.tgz", - "integrity": "sha512-0/4p1eDRRI9PfOzZgpAH3OUQASkKGStqie9HN7KXji91dHnH+81UerYcPsMqFXqwB8qRi8onrvkoV8FL3RJ5tQ==", + "version": "0.20.1", + "resolved": "https://registry.npmjs.org/agents/-/agents-0.20.1.tgz", + "integrity": "sha512-HQRYMeZpD3k8djYBH7atRPojZMee3NvmXkzsmMWXfdHZ94vMljmWqSsD1XZd70LovHyQrw6/R81AZZIsRiFM6Q==", "license": "MIT", "dependencies": { "@babel/plugin-proposal-decorators": "^8.0.2", "@cfworker/json-schema": "^4.1.1", - "@cloudflare/codemode": "^0.5.0", - "@modelcontextprotocol/sdk": "1.29.0", "@rolldown/plugin-babel": "^0.2.3", "cron-schedule": "^6.0.0", "esbuild": "^0.28.1", @@ -2255,6 +2303,10 @@ }, "peerDependencies": { "@ai-sdk/react": "^3.0.0 || ^4.0.0", + "@cloudflare/codemode": ">=0.5.0", + "@modelcontextprotocol/client": "2.0.0", + "@modelcontextprotocol/sdk": "1.30.0", + "@modelcontextprotocol/server": "2.0.0", "@tanstack/ai": ">=0.10.2 <1.0.0", "@x402/core": "^2.0.0", "@x402/evm": "^2.0.0", @@ -2269,6 +2321,9 @@ "@ai-sdk/react": { "optional": true }, + "@cloudflare/codemode": { + "optional": true + }, "@tanstack/ai": { "optional": true }, @@ -2305,10 +2360,11 @@ } }, "node_modules/ajv": { - "version": "8.18.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz", - "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", + "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", + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", @@ -2325,6 +2381,7 @@ "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", "license": "MIT", + "peer": true, "dependencies": { "ajv": "^8.0.0" }, @@ -2392,20 +2449,21 @@ "license": "MIT" }, "node_modules/body-parser": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.2.tgz", - "integrity": "sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA==", + "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", + "peer": true, "dependencies": { "bytes": "^3.1.2", - "content-type": "^1.0.5", + "content-type": "^2.0.0", "debug": "^4.4.3", - "http-errors": "^2.0.0", - "iconv-lite": "^0.7.0", + "http-errors": "^2.0.1", + "iconv-lite": "^0.7.2", "on-finished": "^2.4.1", - "qs": "^6.14.1", - "raw-body": "^3.0.1", - "type-is": "^2.0.1" + "qs": "^6.15.2", + "raw-body": "^3.0.2", + "type-is": "^2.1.0" }, "engines": { "node": ">=18" @@ -2415,6 +2473,20 @@ "url": "https://opencollective.com/express" } }, + "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", + "peer": true, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, "node_modules/browserslist": { "version": "4.28.7", "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.7.tgz", @@ -2454,6 +2526,7 @@ "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", "license": "MIT", + "peer": true, "engines": { "node": ">= 0.8" } @@ -2463,6 +2536,7 @@ "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", + "peer": true, "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2" @@ -2476,6 +2550,7 @@ "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", "license": "MIT", + "peer": true, "dependencies": { "call-bind-apply-helpers": "^1.0.2", "get-intrinsic": "^1.3.0" @@ -2539,11 +2614,29 @@ "node": ">=20" } }, + "node_modules/cliui/node_modules/string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/content-disposition": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.1.tgz", - "integrity": "sha512-oIXISMynqSqm241k6kcQ5UwttDILMK4BiurCfGEREw6+X9jkkpEe5T9FZaApyLGGOnFuyMWZpdolTXMtvEJ08Q==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.1.0.tgz", + "integrity": "sha512-5jRCH9Z/+DRP7rkvY83B+yGIGX96OYdJmzngqnw2SBSxqCFPd0w2km3s5iawpGX8krnwSGmF0FW5Nhr0Hfai3g==", "license": "MIT", + "peer": true, "engines": { "node": ">=18" }, @@ -2557,6 +2650,7 @@ "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", "license": "MIT", + "peer": true, "engines": { "node": ">= 0.6" } @@ -2572,6 +2666,7 @@ "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", "license": "MIT", + "peer": true, "engines": { "node": ">= 0.6" } @@ -2581,16 +2676,20 @@ "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", "license": "MIT", + "peer": true, "engines": { "node": ">=6.6.0" } }, "node_modules/core-js-pure": { - "version": "3.49.0", - "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.49.0.tgz", - "integrity": "sha512-XM4RFka59xATyJv/cS3O3Kml72hQXUeGRuuTmMYFxwzc9/7C8OYTaIR/Ji+Yt8DXzsFLNhat15cE/JP15HrCgw==", + "version": "3.50.0", + "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.50.0.tgz", + "integrity": "sha512-6GP3Pxz4IKyWjAfa747vIu/jilB5z29JWROLqH/b+pXVcpgh6tM06ZIBwSuglgVqzDYURhOK6oEzTrG0bCHitA==", "hasInstallScript": true, "license": "MIT", + "engines": { + "node": "*" + }, "funding": { "type": "opencollective", "url": "https://opencollective.com/core-js" @@ -2601,6 +2700,7 @@ "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.6.tgz", "integrity": "sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw==", "license": "MIT", + "peer": true, "dependencies": { "object-assign": "^4", "vary": "^1" @@ -2627,6 +2727,7 @@ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "license": "MIT", + "peer": true, "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", @@ -2641,6 +2742,7 @@ "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", "license": "MIT", + "peer": true, "dependencies": { "ms": "^2.1.3" }, @@ -2658,6 +2760,7 @@ "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", "license": "MIT", + "peer": true, "engines": { "node": ">= 0.8" } @@ -2677,6 +2780,7 @@ "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", "license": "MIT", + "peer": true, "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", @@ -2690,7 +2794,8 @@ "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" + "license": "MIT", + "peer": true }, "node_modules/electron-to-chromium": { "version": "1.5.402", @@ -2720,6 +2825,7 @@ "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", "license": "MIT", + "peer": true, "engines": { "node": ">= 0.8" } @@ -2739,6 +2845,7 @@ "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", "license": "MIT", + "peer": true, "engines": { "node": ">= 0.4" } @@ -2748,22 +2855,24 @@ "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", "license": "MIT", + "peer": true, "engines": { "node": ">= 0.4" } }, "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==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.3.1.tgz", + "integrity": "sha512-shc1dbU90Yl/xq1QrC7QRtfcwURZuVRfPhZbDoldJ1cn1gzDvBaBWlv0eFolj5+0znnPJz5TXLxsN77X/12KTA==", "dev": true, "license": "MIT" }, "node_modules/es-object-atoms": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", - "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "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", + "peer": true, "dependencies": { "es-errors": "^1.3.0" }, @@ -2825,7 +2934,8 @@ "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" + "license": "MIT", + "peer": true }, "node_modules/estree-walker": { "version": "3.0.3", @@ -2842,6 +2952,7 @@ "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", "license": "MIT", + "peer": true, "engines": { "node": ">= 0.6" } @@ -2857,6 +2968,7 @@ "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-3.0.7.tgz", "integrity": "sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA==", "license": "MIT", + "peer": true, "dependencies": { "eventsource-parser": "^3.0.1" }, @@ -2865,18 +2977,19 @@ } }, "node_modules/eventsource-parser": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/eventsource-parser/-/eventsource-parser-3.0.6.tgz", - "integrity": "sha512-Vo1ab+QXPzZ4tCa8SwIHJFaSzy4R6SHf7BY79rFBDf0idraZWAkYrDjDj8uWaSm3S2TK+hJ7/t1CEmZ7jXw+pg==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/eventsource-parser/-/eventsource-parser-3.1.0.tgz", + "integrity": "sha512-kJezFj9YFAMLeORyi7aCLxLbD5/qWMQnoMVlVPyHIll7lgRJCc3JVln9Vgl9nwQi0YkMnhdGTMNn7CkRRAptMg==", "license": "MIT", + "peer": true, "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==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.4.0.tgz", + "integrity": "sha512-KfYbmpRm0VbLjEvVa9yGwCi9GI34xvi7A/HXYWQO65CSD2u3MczUJSuwXKFIxlGsgBQizV9q5J9NHj4VG0n+pA==", "dev": true, "license": "Apache-2.0", "engines": { @@ -2888,6 +3001,7 @@ "resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz", "integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==", "license": "MIT", + "peer": true, "dependencies": { "accepts": "^2.0.0", "body-parser": "^2.2.1", @@ -2927,12 +3041,14 @@ } }, "node_modules/express-rate-limit": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-8.3.2.tgz", - "integrity": "sha512-77VmFeJkO0/rvimEDuUC5H30oqUC4EyOhyGccfqoLebB0oiEYfM7nwPrsDsBL1gsTpwfzX8SFy2MT3TDyRq+bg==", + "version": "8.6.2", + "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-8.6.2.tgz", + "integrity": "sha512-YH4ru+eOJxQABscKFfRCy9R7x9QFGdezclVMwwgFFndzS2Xnm0uo6B0ABZsLhcpeptGv2qvuJVWlQr9gQZoC3A==", "license": "MIT", + "peer": true, "dependencies": { - "ip-address": "10.1.0" + "debug": "^4.4.3", + "ip-address": "^10.2.0" }, "engines": { "node": ">= 16" @@ -2948,12 +3064,13 @@ "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" + "license": "MIT", + "peer": true }, "node_modules/fast-uri": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz", - "integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==", + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.5.tgz", + "integrity": "sha512-gHwA1O9LDIcKunMKhObS/HimwtehO1nPUECKAu5TpKgaO19fcWEl4bliWe1jWxVFvIXztJjjQ4L8XQ1EU9f7Jw==", "funding": [ { "type": "github", @@ -2964,7 +3081,8 @@ "url": "https://opencollective.com/fastify" } ], - "license": "BSD-3-Clause" + "license": "BSD-3-Clause", + "peer": true }, "node_modules/fdir": { "version": "6.5.0", @@ -2989,6 +3107,7 @@ "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.1.tgz", "integrity": "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==", "license": "MIT", + "peer": true, "dependencies": { "debug": "^4.4.0", "encodeurl": "^2.0.0", @@ -3010,6 +3129,7 @@ "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", "license": "MIT", + "peer": true, "engines": { "node": ">= 0.6" } @@ -3019,6 +3139,7 @@ "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", "license": "MIT", + "peer": true, "engines": { "node": ">= 0.8" } @@ -3042,6 +3163,7 @@ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", "license": "MIT", + "peer": true, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -3066,9 +3188,9 @@ } }, "node_modules/get-east-asian-width": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.5.0.tgz", - "integrity": "sha512-CQ+bEO+Tva/qlmw24dCejulK5pMzVnUOFOijVogd3KQs07HnRIgp8TGipvCCRT06xeYEbpbgwaCxglFyiuIcmA==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.6.0.tgz", + "integrity": "sha512-QRbvDIbx6YklUe6RxeTeleMR0yv3cYH6PsPZHcnVn7xv7zO1BHN8r0XETu8n6Ye3Q+ahtSarc3WgtNWmehIBfA==", "license": "MIT", "engines": { "node": ">=18" @@ -3082,6 +3204,7 @@ "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", "license": "MIT", + "peer": true, "dependencies": { "call-bind-apply-helpers": "^1.0.2", "es-define-property": "^1.0.1", @@ -3106,6 +3229,7 @@ "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", "license": "MIT", + "peer": true, "dependencies": { "dunder-proto": "^1.0.1", "es-object-atoms": "^1.0.0" @@ -3119,6 +3243,7 @@ "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", "license": "MIT", + "peer": true, "engines": { "node": ">= 0.4" }, @@ -3131,6 +3256,7 @@ "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", "license": "MIT", + "peer": true, "engines": { "node": ">= 0.4" }, @@ -3139,10 +3265,11 @@ } }, "node_modules/hasown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz", + "integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==", "license": "MIT", + "peer": true, "dependencies": { "function-bind": "^1.1.2" }, @@ -3151,10 +3278,11 @@ } }, "node_modules/hono": { - "version": "4.12.10", - "resolved": "https://registry.npmjs.org/hono/-/hono-4.12.10.tgz", - "integrity": "sha512-mx/p18PLy5og9ufies2GOSUqep98Td9q4i/EF6X7yJgAiIopxqdfIO3jbqsi3jRgTgw88jMDEzVKi+V2EF+27w==", + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/hono/-/hono-4.13.0.tgz", + "integrity": "sha512-jhunvfHWxd7J5EFfSgH4xsYJzSe/lfqbUCxiyyeaQasUsXeEHXtzVid+7EOGByc5JnFa23SSFL3Y2RV/z1T+eQ==", "license": "MIT", + "peer": true, "engines": { "node": ">=16.9.0" } @@ -3164,6 +3292,7 @@ "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", "license": "MIT", + "peer": true, "dependencies": { "depd": "~2.0.0", "inherits": "~2.0.4", @@ -3180,10 +3309,11 @@ } }, "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==", + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.3.tgz", + "integrity": "sha512-IKXpvIzjnC9XTAUbVBcMfGS0EPaIXtW6v+zr+RRp+hqULEpo0owZax6wyRwPOJbWbzjYspQwusTsfVr0ifh4uQ==", "license": "MIT", + "peer": true, "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" }, @@ -3210,13 +3340,15 @@ "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "license": "ISC" + "license": "ISC", + "peer": true }, "node_modules/ip-address": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.1.0.tgz", - "integrity": "sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q==", + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.4.0.tgz", + "integrity": "sha512-oSK96Grm3aP6OrS263xVxbNDGVL7rzBtYdpGqlDG8iQdoenDoTs/nkki+DflYbAEE8Xl6o5YxhxlrKvI3nqKXQ==", "license": "MIT", + "peer": true, "engines": { "node": ">= 12" } @@ -3226,6 +3358,7 @@ "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", "license": "MIT", + "peer": true, "engines": { "node": ">= 0.10" } @@ -3234,27 +3367,30 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "license": "ISC" + "license": "ISC", + "peer": true }, "node_modules/jose": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/jose/-/jose-6.2.2.tgz", - "integrity": "sha512-d7kPDd34KO/YnzaDOlikGpOurfF0ByC2sEV4cANCtdqLlTfBlw2p14O/5d/zv40gJPbIQxfES3nSx1/oYNyuZQ==", + "version": "6.2.8", + "resolved": "https://registry.npmjs.org/jose/-/jose-6.2.8.tgz", + "integrity": "sha512-Bsdjwm3Qsd/P0jR+BHDe3LytDfY7WBq2HmCCLIwuVRHMuEC9ae7/R474GIUdF1NgCyZjzVo/A9DOiOBtXq8ZoQ==", "license": "MIT", + "peer": true, "funding": { "url": "https://github.com/sponsors/panva" } }, "node_modules/js-base64": { - "version": "3.7.8", - "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-3.7.8.tgz", - "integrity": "sha512-hNngCeKxIUQiEUN3GPJOkz4wF/YvdUdbNL9hsBcMQTkKzboD7T/q3OYOuuPZLUE6dBxSGpwhk5mwuDud7JVAow==", + "version": "3.9.2", + "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-3.9.2.tgz", + "integrity": "sha512-6zayE8QlUdiweYI6cETD/XBSqFcoCUlufn/29PJR99r82x1yDnIprRca0YvAYpAW+ez0GuQkVBC6xG5QkD7OjA==", "license": "BSD-3-Clause" }, "node_modules/js-tokens": { @@ -3279,13 +3415,15 @@ "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" + "license": "MIT", + "peer": true }, "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" + "license": "BSD-2-Clause", + "peer": true }, "node_modules/json5": { "version": "2.2.3", @@ -3311,9 +3449,9 @@ } }, "node_modules/lightningcss": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz", - "integrity": "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==", + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.33.0.tgz", + "integrity": "sha512-WkUDrojuJs0xkgGf2udWxa3yGBRxPtxUkB79i6aCZLRgc7PM8fZe9TosfPDcvEpQZbuFASnHYmRLBLUbmLOIIA==", "devOptional": true, "license": "MPL-2.0", "dependencies": { @@ -3327,23 +3465,23 @@ "url": "https://opencollective.com/parcel" }, "optionalDependencies": { - "lightningcss-android-arm64": "1.32.0", - "lightningcss-darwin-arm64": "1.32.0", - "lightningcss-darwin-x64": "1.32.0", - "lightningcss-freebsd-x64": "1.32.0", - "lightningcss-linux-arm-gnueabihf": "1.32.0", - "lightningcss-linux-arm64-gnu": "1.32.0", - "lightningcss-linux-arm64-musl": "1.32.0", - "lightningcss-linux-x64-gnu": "1.32.0", - "lightningcss-linux-x64-musl": "1.32.0", - "lightningcss-win32-arm64-msvc": "1.32.0", - "lightningcss-win32-x64-msvc": "1.32.0" + "lightningcss-android-arm64": "1.33.0", + "lightningcss-darwin-arm64": "1.33.0", + "lightningcss-darwin-x64": "1.33.0", + "lightningcss-freebsd-x64": "1.33.0", + "lightningcss-linux-arm-gnueabihf": "1.33.0", + "lightningcss-linux-arm64-gnu": "1.33.0", + "lightningcss-linux-arm64-musl": "1.33.0", + "lightningcss-linux-x64-gnu": "1.33.0", + "lightningcss-linux-x64-musl": "1.33.0", + "lightningcss-win32-arm64-msvc": "1.33.0", + "lightningcss-win32-x64-msvc": "1.33.0" } }, "node_modules/lightningcss-android-arm64": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.32.0.tgz", - "integrity": "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==", + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.33.0.tgz", + "integrity": "sha512-gEpRTalKdosp4Bb8qWtc2iOgE5SeIHlpS1up9bFq2wAyYhl1UdTObYiHe98zEM9SQvSoqQZ1IQD0JNpg3Ml5pg==", "cpu": [ "arm64" ], @@ -3361,9 +3499,9 @@ } }, "node_modules/lightningcss-darwin-arm64": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.32.0.tgz", - "integrity": "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==", + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.33.0.tgz", + "integrity": "sha512-Sciaz8eenNTKn9b3t7+xr0ipTp9YxKQY4npwQ3mrRuL0BAVHBLyZxofhaKBAVtzmtRZ/zTyo0/to4B1uWG/Djg==", "cpu": [ "arm64" ], @@ -3381,9 +3519,9 @@ } }, "node_modules/lightningcss-darwin-x64": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.32.0.tgz", - "integrity": "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==", + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.33.0.tgz", + "integrity": "sha512-Z5UPAxzrjlWNNyGy6i65cJzzvgJ5D3T6wMvs+gWpY9d7qRhANrxqAp6LhxIgZhWEw18RfJTGcRxjuLIBr+m8XQ==", "cpu": [ "x64" ], @@ -3401,9 +3539,9 @@ } }, "node_modules/lightningcss-freebsd-x64": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.32.0.tgz", - "integrity": "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==", + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.33.0.tgz", + "integrity": "sha512-QQM/Ti/hQajJwCY+RiWuCZ9sdtI/XQk7nDK5vC8kkdwixezOlDgvDx7+RT+QjK6FcFT4MpsuoBnHIo/O3StRRg==", "cpu": [ "x64" ], @@ -3421,9 +3559,9 @@ } }, "node_modules/lightningcss-linux-arm-gnueabihf": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.32.0.tgz", - "integrity": "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==", + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.33.0.tgz", + "integrity": "sha512-N7FVBe6iS24MlM6R/4RBTxGhQheZGs7tiQ9U32UtF75NzP5Q7xWPRqLBCKxlRQRk3rY1jCIPLzx7WzOhuUIRLQ==", "cpu": [ "arm" ], @@ -3441,9 +3579,9 @@ } }, "node_modules/lightningcss-linux-arm64-gnu": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.32.0.tgz", - "integrity": "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==", + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.33.0.tgz", + "integrity": "sha512-j2v/itmy4HlNxlc6voKXYgBqNi0Ng2LShg4z7GufpEgs05P+2suBVyi9I6YHq5uoVFx9ETin3eCEhLVyXGQnKg==", "cpu": [ "arm64" ], @@ -3464,9 +3602,9 @@ } }, "node_modules/lightningcss-linux-arm64-musl": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.32.0.tgz", - "integrity": "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==", + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.33.0.tgz", + "integrity": "sha512-yiO5ROMuYQgXbC60yjZU5CYSFZGKXL0HFATXt9mHJn1+zW55oCtMI9NfcVhYLMFDL7gV7oBPon/EmMMGg2OvtQ==", "cpu": [ "arm64" ], @@ -3487,9 +3625,9 @@ } }, "node_modules/lightningcss-linux-x64-gnu": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.32.0.tgz", - "integrity": "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==", + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.33.0.tgz", + "integrity": "sha512-ar+Ju7LmcN0Jo4FpL4hpFybwNG9/3A/Br5KW2n2jyODg3MEZXaDYADdemoNS+BDNfMgKvylJLj4S5tyRActuAg==", "cpu": [ "x64" ], @@ -3510,9 +3648,9 @@ } }, "node_modules/lightningcss-linux-x64-musl": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.32.0.tgz", - "integrity": "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==", + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.33.0.tgz", + "integrity": "sha512-RYiYbkokw0trfKqqzfF55lginwEPrD3OJDfTuJzFs1MK6iFnDenaz1fqLLtX4ITG3OktJQXOeTaw1awrBAlZPw==", "cpu": [ "x64" ], @@ -3533,9 +3671,9 @@ } }, "node_modules/lightningcss-win32-arm64-msvc": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.32.0.tgz", - "integrity": "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==", + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.33.0.tgz", + "integrity": "sha512-1K+MPfLSFVpphzpdbfkhlWk6wBrTObBzS2T6db10PNOZgR9GoVsAWzwNyuhUYYbTp23j+4RrncfujZ4uAzXvwA==", "cpu": [ "arm64" ], @@ -3553,9 +3691,9 @@ } }, "node_modules/lightningcss-win32-x64-msvc": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.32.0.tgz", - "integrity": "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==", + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.33.0.tgz", + "integrity": "sha512-OlEICDx/Xl0FqSp4bry8zFnCvGpig3Gl4gCquvYwHuqJKEC1+n9NgDniFvqHGmMv1ZkqDJrDqKKSykTDX+ehuA==", "cpu": [ "x64" ], @@ -3597,17 +3735,23 @@ "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", "license": "MIT", + "peer": true, "engines": { "node": ">= 0.4" } }, "node_modules/media-typer": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", - "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.1.tgz", + "integrity": "sha512-yz3xRaG20c6/BOzvYoDaGtPmGscs7YivItZEEqe6GbwNfHuxu9YNmvnEkMzKldAGY4/80pRcQRZSEnhquk9XuQ==", "license": "MIT", + "peer": true, "engines": { "node": ">= 0.8" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/merge-descriptors": { @@ -3615,6 +3759,7 @@ "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==", "license": "MIT", + "peer": true, "engines": { "node": ">=18" }, @@ -3627,6 +3772,7 @@ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", "license": "MIT", + "peer": true, "engines": { "node": ">= 0.6" } @@ -3636,6 +3782,7 @@ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz", "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", "license": "MIT", + "peer": true, "dependencies": { "mime-db": "^1.54.0" }, @@ -3685,16 +3832,16 @@ } }, "node_modules/miniflare": { - "version": "4.20260617.1", - "resolved": "https://registry.npmjs.org/miniflare/-/miniflare-4.20260617.1.tgz", - "integrity": "sha512-Go3/gzStm99QHptsSgU+q1S+xDfLoRgwjJNY80kaTVi0ENhTyqKq+sc4xZiWBSbM7uUcJwmzm8+QFKtcYLJ9nw==", + "version": "4.20260625.0", + "resolved": "https://registry.npmjs.org/miniflare/-/miniflare-4.20260625.0.tgz", + "integrity": "sha512-3kKXwRUObJsnBYPBgR0NiNZYKF/yv8GFyha1cx2EeAEraxNODgRVcyeRo+F1ok1tg5Mg7iUpOWSkknQTHuFhwA==", "dev": true, "license": "MIT", "dependencies": { "@cspotcode/source-map-support": "0.8.1", "sharp": "0.34.5", "undici": "7.28.0", - "workerd": "1.20260617.1", + "workerd": "1.20260625.1", "ws": "8.21.0", "youch": "4.1.0-beta.10" }, @@ -3709,7 +3856,8 @@ "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/nanoid": { "version": "5.1.16", @@ -3734,6 +3882,7 @@ "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", "license": "MIT", + "peer": true, "engines": { "node": ">= 0.6" } @@ -3753,6 +3902,7 @@ "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", "license": "MIT", + "peer": true, "engines": { "node": ">=0.10.0" } @@ -3762,6 +3912,7 @@ "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", "license": "MIT", + "peer": true, "engines": { "node": ">= 0.4" }, @@ -3770,9 +3921,9 @@ } }, "node_modules/obug": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/obug/-/obug-2.1.3.tgz", - "integrity": "sha512-9miFgM2OFba7hB+pRgvtV84pYTBaoTHohvmIgiRt6dRIzbwEOIaNaP+dIlGs2fNFoB0SeISs0Jz5WFVRid6Xyg==", + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/obug/-/obug-2.1.4.tgz", + "integrity": "sha512-4a+OsYv9UktOJKE+l1A4OufDgdRF9PifWj+tJnHURo/P+WOxpG4GzUFL9qCalmWauao6ogiG+QvnCovwPoyAWA==", "funding": [ "https://github.com/sponsors/sxzz", "https://opencollective.com/debug" @@ -3787,6 +3938,7 @@ "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", "license": "MIT", + "peer": true, "dependencies": { "ee-first": "1.1.1" }, @@ -3799,6 +3951,7 @@ "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", "license": "ISC", + "peer": true, "dependencies": { "wrappy": "1" } @@ -3808,6 +3961,7 @@ "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", "license": "MIT", + "peer": true, "engines": { "node": ">= 0.8" } @@ -3834,6 +3988,7 @@ "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", "license": "MIT", + "peer": true, "engines": { "node": ">=8" } @@ -3843,6 +3998,7 @@ "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.4.2.tgz", "integrity": "sha512-qRcuIdP69NPm4qbACK+aDogI5CBDMi1jKe0ry5rSQJz8JVLsC7jV8XpiJjGRLLol3N+R5ihGYcrPLTno6pAdBA==", "license": "MIT", + "peer": true, "funding": { "type": "opencollective", "url": "https://opencollective.com/express" @@ -3862,9 +4018,9 @@ "license": "ISC" }, "node_modules/picomatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", - "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.5.tgz", + "integrity": "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==", "license": "MIT", "engines": { "node": ">=12" @@ -3878,14 +4034,15 @@ "resolved": "https://registry.npmjs.org/pkce-challenge/-/pkce-challenge-5.0.1.tgz", "integrity": "sha512-wQ0b/W4Fr01qtpHlqSqspcj3EhBvimsdh0KlHhH8HRZnMsEa0ea2fTULOXOS9ccQr3om+GcGRk4e+isrZWV8qQ==", "license": "MIT", + "peer": true, "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==", + "version": "8.5.26", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.26.tgz", + "integrity": "sha512-u82N74LFzG8ca+dD8puPnplTXoGH4fTPpVGuIbt36G3qvNlkvfD0lEAZSxaly3KX8TS/L1A1gsCEmvKmBcVbkQ==", "devOptional": true, "funding": [ { @@ -3903,7 +4060,7 @@ ], "license": "MIT", "dependencies": { - "nanoid": "^3.3.12", + "nanoid": "^3.3.17", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" }, @@ -3912,9 +4069,9 @@ } }, "node_modules/postcss/node_modules/nanoid": { - "version": "3.3.14", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.14.tgz", - "integrity": "sha512-U9kYi5bpVMEI31yC8iw4bJJp0avcHXA0W8/wNfLfnvJYzihQo2ZRPYPvpAAd570HAcCBjCTN7vnr+v4StKl1IQ==", + "version": "3.3.17", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.17.tgz", + "integrity": "sha512-xQLf0A3HOMlgHq0n247/LRuAOYmB7dXJ/DvAxGvsSBij45XtBSmQycu+F8ODbHwns/XyFZagyL1+J0Offw1E0g==", "devOptional": true, "funding": [ { @@ -3935,6 +4092,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", + "peer": true, "dependencies": { "forwarded": "0.2.0", "ipaddr.js": "1.9.1" @@ -3944,12 +4102,14 @@ } }, "node_modules/qs": { - "version": "6.15.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.0.tgz", - "integrity": "sha512-mAZTtNCeetKMH+pSjrb76NAM8V9a05I9aBZOHztWy/UqcJdQYNsf59vrRKWnojAT9Y+GbIvoTBC++CPHqpDBhQ==", + "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", + "peer": true, "dependencies": { - "side-channel": "^1.1.0" + "es-define-property": "^1.0.1", + "side-channel": "^1.1.1" }, "engines": { "node": ">=0.6" @@ -3959,12 +4119,17 @@ } }, "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==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.3.0.tgz", + "integrity": "sha512-hek2mFQpPuI4E1BBKrSto+BU3e3x4xuarsbiwr3+lf7p44juvFMV0XFWQAP3xUyqXA4RrXLIoaSUGbSt056ZMw==", "license": "MIT", + "peer": true, "engines": { "node": ">= 0.6" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/raw-body": { @@ -3972,6 +4137,7 @@ "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.2.tgz", "integrity": "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==", "license": "MIT", + "peer": true, "dependencies": { "bytes": "~3.1.2", "http-errors": "~2.0.1", @@ -3997,17 +4163,18 @@ "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", + "peer": true, "engines": { "node": ">=0.10.0" } }, "node_modules/rolldown": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.3.tgz", - "integrity": "sha512-i00lAJ2ks1BYr7rjNjKC7BcqAS7nVfiT3QX1SI5aY+AFHblCmaUf9OE9dbdzDvW6dJxbi2ZCZiy9v3CcwOiX3g==", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.2.3.tgz", + "integrity": "sha512-rn9wpmxplLf7NLNyCk9FyWh3FM43DbY8jOzCdEPzH7uflhTftRbCEpqi6Ly2osgoU8OwObtmavMbWLaWy4LX7A==", "license": "MIT", "dependencies": { - "@oxc-project/types": "=0.133.0", + "@oxc-project/types": "=0.143.0", "@rolldown/pluginutils": "^1.0.0" }, "bin": { @@ -4017,21 +4184,20 @@ "node": "^20.19.0 || >=22.12.0" }, "optionalDependencies": { - "@rolldown/binding-android-arm64": "1.0.3", - "@rolldown/binding-darwin-arm64": "1.0.3", - "@rolldown/binding-darwin-x64": "1.0.3", - "@rolldown/binding-freebsd-x64": "1.0.3", - "@rolldown/binding-linux-arm-gnueabihf": "1.0.3", - "@rolldown/binding-linux-arm64-gnu": "1.0.3", - "@rolldown/binding-linux-arm64-musl": "1.0.3", - "@rolldown/binding-linux-ppc64-gnu": "1.0.3", - "@rolldown/binding-linux-s390x-gnu": "1.0.3", - "@rolldown/binding-linux-x64-gnu": "1.0.3", - "@rolldown/binding-linux-x64-musl": "1.0.3", - "@rolldown/binding-openharmony-arm64": "1.0.3", - "@rolldown/binding-wasm32-wasi": "1.0.3", - "@rolldown/binding-win32-arm64-msvc": "1.0.3", - "@rolldown/binding-win32-x64-msvc": "1.0.3" + "@rolldown/binding-android-arm64": "1.2.3", + "@rolldown/binding-darwin-arm64": "1.2.3", + "@rolldown/binding-darwin-x64": "1.2.3", + "@rolldown/binding-freebsd-x64": "1.2.3", + "@rolldown/binding-linux-arm-gnueabihf": "1.2.3", + "@rolldown/binding-linux-arm64-gnu": "1.2.3", + "@rolldown/binding-linux-arm64-musl": "1.2.3", + "@rolldown/binding-linux-ppc64-gnu": "1.2.3", + "@rolldown/binding-linux-s390x-gnu": "1.2.3", + "@rolldown/binding-linux-x64-gnu": "1.2.3", + "@rolldown/binding-linux-x64-musl": "1.2.3", + "@rolldown/binding-openharmony-arm64": "1.2.3", + "@rolldown/binding-win32-arm64-msvc": "1.2.3", + "@rolldown/binding-win32-x64-msvc": "1.2.3" } }, "node_modules/router": { @@ -4039,6 +4205,7 @@ "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==", "license": "MIT", + "peer": true, "dependencies": { "debug": "^4.4.0", "depd": "^2.0.0", @@ -4054,7 +4221,8 @@ "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/semver": { "version": "7.8.5", @@ -4073,6 +4241,7 @@ "resolved": "https://registry.npmjs.org/send/-/send-1.2.1.tgz", "integrity": "sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==", "license": "MIT", + "peer": true, "dependencies": { "debug": "^4.4.3", "encodeurl": "^2.0.0", @@ -4099,6 +4268,7 @@ "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.1.tgz", "integrity": "sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==", "license": "MIT", + "peer": true, "dependencies": { "encodeurl": "^2.0.0", "escape-html": "^1.0.3", @@ -4117,7 +4287,8 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", - "license": "ISC" + "license": "ISC", + "peer": true }, "node_modules/sharp": { "version": "0.34.5", @@ -4169,6 +4340,7 @@ "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", "license": "MIT", + "peer": true, "dependencies": { "shebang-regex": "^3.0.0" }, @@ -4181,19 +4353,21 @@ "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", "license": "MIT", + "peer": true, "engines": { "node": ">=8" } }, "node_modules/side-channel": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", - "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.1.tgz", + "integrity": "sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ==", "license": "MIT", + "peer": true, "dependencies": { "es-errors": "^1.3.0", - "object-inspect": "^1.13.3", - "side-channel-list": "^1.0.0", + "object-inspect": "^1.13.4", + "side-channel-list": "^1.0.1", "side-channel-map": "^1.0.1", "side-channel-weakmap": "^1.0.2" }, @@ -4205,13 +4379,14 @@ } }, "node_modules/side-channel-list": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", - "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "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", + "peer": true, "dependencies": { "es-errors": "^1.3.0", - "object-inspect": "^1.13.3" + "object-inspect": "^1.13.4" }, "engines": { "node": ">= 0.4" @@ -4225,6 +4400,7 @@ "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", "license": "MIT", + "peer": true, "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", @@ -4243,6 +4419,7 @@ "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", "license": "MIT", + "peer": true, "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", @@ -4286,29 +4463,29 @@ "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", "license": "MIT", + "peer": true, "engines": { "node": ">= 0.8" } }, "node_modules/std-env": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/std-env/-/std-env-4.1.0.tgz", - "integrity": "sha512-Rq7ybcX2RuC55r9oaPVEW7/xu3tj8u4GeBYHBWCychFtzMIr86A7e3PPEBPT37sHStKX3+TiX/Fr/ACmJLVlLQ==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-4.2.0.tgz", + "integrity": "sha512-oCUKSupKTHX53EyjDtuZQ64pjLJ6yYCtpmEw0goYxtjG9KpbRe8KAsl2tBUGU9DyMcJ0RwJ8GqJAFzMXcXW1Rw==", "dev": true, "license": "MIT" }, "node_modules/string-width": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", - "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", + "version": "8.2.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-8.2.2.tgz", + "integrity": "sha512-GaPUh5gfdrYzqeVNZvUfT23vYYxXzKYidUcnMtJg/3rxRV63EFZy3k6xfKlmfeJD0176lnUV/Usr3XcwSvFzpg==", "license": "MIT", "dependencies": { - "emoji-regex": "^10.3.0", - "get-east-asian-width": "^1.0.0", - "strip-ansi": "^7.1.0" + "get-east-asian-width": "^1.5.0", + "strip-ansi": "^7.1.2" }, "engines": { - "node": ">=18" + "node": ">=20" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -4350,9 +4527,9 @@ "license": "MIT" }, "node_modules/tinyexec": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.2.4.tgz", - "integrity": "sha512-SHf/r48b7vOrjve9PxJo3MN5v5yuyjHvdUcrQffT3WXMUfnGmHDVbC4k3sHJaJTgZCwpUplIaAo5ANtMyp3YHg==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.3.0.tgz", + "integrity": "sha512-QKAl9m8gWWGHV8jZcPeym6j+XULi6tOf1mT83WYJ4Lk2ytW/uwAWkrP0uFsdoYMdueVJ0qs26wZ+23xeB4ibNQ==", "dev": true, "license": "MIT", "engines": { @@ -4377,9 +4554,9 @@ } }, "node_modules/tinyrainbow": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-3.1.0.tgz", - "integrity": "sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-3.1.1.tgz", + "integrity": "sha512-yau8yJdTt989Mm0Bd/236QnzEiPf2xLLTqUZRUJOo/3CB078LSwzei343DgtJVmfJKJE3TMINY1u42SQsP6mXw==", "dev": true, "license": "MIT", "engines": { @@ -4391,6 +4568,7 @@ "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", "license": "MIT", + "peer": true, "engines": { "node": ">=0.6" } @@ -4399,21 +4577,41 @@ "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, "license": "0BSD", "optional": true }, "node_modules/type-is": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz", - "integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.1.0.tgz", + "integrity": "sha512-faYHw0anBbc/kWF3zFTEnxSFOAGUX9GFbOBthvDdLsIlEoWOFOtS0zgCiQYwIskL9iGXZL3kAXD8OoZ4GmMATA==", "license": "MIT", + "peer": true, "dependencies": { - "content-type": "^1.0.5", + "content-type": "^2.0.0", "media-typer": "^1.1.0", "mime-types": "^3.0.0" }, "engines": { - "node": ">= 0.6" + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "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", + "peer": true, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/typescript": { @@ -4455,6 +4653,7 @@ "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", "license": "MIT", + "peer": true, "engines": { "node": ">= 0.8" } @@ -4495,21 +4694,22 @@ "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", "license": "MIT", + "peer": true, "engines": { "node": ">= 0.8" } }, "node_modules/vite": { - "version": "8.0.16", - "resolved": "https://registry.npmjs.org/vite/-/vite-8.0.16.tgz", - "integrity": "sha512-h9bXPmJichP5fLmVQo3PyaGSDE2n3aPuomeAlVRm0JLmt4rY6zmPKd59HYI4LNW8oTK7tlTsuC7l/m7awx9Jcw==", + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/vite/-/vite-8.2.0.tgz", + "integrity": "sha512-pn+CFpM0lwDeKwmOq1ZaBK/9sjorZcgqxki6MbY/jPEVd9vichIlmlD4HmQ5wdP5EgqQCFRaACBxMC7uEGc6lQ==", "devOptional": true, "license": "MIT", "dependencies": { - "lightningcss": "^1.32.0", - "picomatch": "^4.0.4", - "postcss": "^8.5.15", - "rolldown": "1.0.3", + "lightningcss": "^1.33.0", + "picomatch": "^4.0.5", + "postcss": "^8.5.23", + "rolldown": "~1.2.0", "tinyglobby": "^0.2.17" }, "bin": { @@ -4526,7 +4726,7 @@ }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", - "@vitejs/devtools": "^0.1.18", + "@vitejs/devtools": "^0.4.0", "esbuild": "^0.27.0 || ^0.28.0", "jiti": ">=1.21.0", "less": "^4.0.0", @@ -4578,19 +4778,19 @@ } }, "node_modules/vitest": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.9.tgz", - "integrity": "sha512-nE3/LEyc0z87uHYLZebqCUOaJr2hdtuPp7BQ4BosVFnfltxgAvMG08NyrSGlPpOUWvR27c5flSmYFTNr78L9GQ==", + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.10.tgz", + "integrity": "sha512-R9jUTe5S4Qb0HCd4TNqpC7oGcrMssMRGXLW80ubjWsW9VH5GF8y1Y0SFLY9AbqSk6nt0PnOx4H4WNJYZ13GUPw==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/expect": "4.1.9", - "@vitest/mocker": "4.1.9", - "@vitest/pretty-format": "4.1.9", - "@vitest/runner": "4.1.9", - "@vitest/snapshot": "4.1.9", - "@vitest/spy": "4.1.9", - "@vitest/utils": "4.1.9", + "@vitest/expect": "4.1.10", + "@vitest/mocker": "4.1.10", + "@vitest/pretty-format": "4.1.10", + "@vitest/runner": "4.1.10", + "@vitest/snapshot": "4.1.10", + "@vitest/spy": "4.1.10", + "@vitest/utils": "4.1.10", "es-module-lexer": "^2.0.0", "expect-type": "^1.3.0", "magic-string": "^0.30.21", @@ -4618,12 +4818,12 @@ "@edge-runtime/vm": "*", "@opentelemetry/api": "^1.9.0", "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0", - "@vitest/browser-playwright": "4.1.9", - "@vitest/browser-preview": "4.1.9", - "@vitest/browser-webdriverio": "4.1.9", - "@vitest/coverage-istanbul": "4.1.9", - "@vitest/coverage-v8": "4.1.9", - "@vitest/ui": "4.1.9", + "@vitest/browser-playwright": "4.1.10", + "@vitest/browser-preview": "4.1.10", + "@vitest/browser-webdriverio": "4.1.10", + "@vitest/coverage-istanbul": "4.1.10", + "@vitest/coverage-v8": "4.1.10", + "@vitest/ui": "4.1.10", "happy-dom": "*", "jsdom": "*", "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" @@ -4672,6 +4872,7 @@ "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", "license": "ISC", + "peer": true, "dependencies": { "isexe": "^2.0.0" }, @@ -4700,9 +4901,9 @@ } }, "node_modules/workerd": { - "version": "1.20260617.1", - "resolved": "https://registry.npmjs.org/workerd/-/workerd-1.20260617.1.tgz", - "integrity": "sha512-Re5pl6pdowt3ZmWUzGlOuB7jbRIIPetgKalmo4cYmucQnVhpo7/3e4MfpekbhLi2EhZZz5EY9NWRu8zFzuEZew==", + "version": "1.20260625.1", + "resolved": "https://registry.npmjs.org/workerd/-/workerd-1.20260625.1.tgz", + "integrity": "sha512-GApQvFX52SDM6L4u0+RRnUDB1wJOnEwoXjinkmOPtIyofWBxrlZckdegJSYc1leg++lLZ3+DQ4zMVmBqYVtzfA==", "dev": true, "hasInstallScript": true, "license": "Apache-2.0", @@ -4713,17 +4914,17 @@ "node": ">=16" }, "optionalDependencies": { - "@cloudflare/workerd-darwin-64": "1.20260617.1", - "@cloudflare/workerd-darwin-arm64": "1.20260617.1", - "@cloudflare/workerd-linux-64": "1.20260617.1", - "@cloudflare/workerd-linux-arm64": "1.20260617.1", - "@cloudflare/workerd-windows-64": "1.20260617.1" + "@cloudflare/workerd-darwin-64": "1.20260625.1", + "@cloudflare/workerd-darwin-arm64": "1.20260625.1", + "@cloudflare/workerd-linux-64": "1.20260625.1", + "@cloudflare/workerd-linux-arm64": "1.20260625.1", + "@cloudflare/workerd-windows-64": "1.20260625.1" } }, "node_modules/wrangler": { - "version": "4.103.0", - "resolved": "https://registry.npmjs.org/wrangler/-/wrangler-4.103.0.tgz", - "integrity": "sha512-3Lv1P5t2xcSEkSTKtG+Lz+3JFryuU7YPLkaCUj7gNe+CJsjZJLtUwqsh1x595QBxkIbCE0GAvDx2DCJUU4+oqw==", + "version": "4.119.0", + "resolved": "https://registry.npmjs.org/wrangler/-/wrangler-4.119.0.tgz", + "integrity": "sha512-ookClf+zly4DTc8pBMNrwGQzZKH8IpIYTXkjDw3XS7ZvBQ5mLYH6eOvfD5BEpk3U63zTbv91WRlo1UeRSKXa0g==", "dev": true, "license": "MIT OR Apache-2.0", "dependencies": { @@ -4731,10 +4932,10 @@ "@cloudflare/unenv-preset": "2.16.1", "blake3-wasm": "2.1.5", "esbuild": "0.28.1", - "miniflare": "4.20260617.1", + "miniflare": "5.20260801.0-alpha", "path-to-regexp": "6.3.0", "unenv": "2.0.0-rc.24", - "workerd": "1.20260617.1" + "workerd": "1.20260801.1" }, "bin": { "cf-wrangler": "bin/cf-wrangler.js", @@ -4748,7 +4949,7 @@ "fsevents": "2.3.3" }, "peerDependencies": { - "@cloudflare/workers-types": "^4.20260617.1" + "@cloudflare/workers-types": "^5.20260801.1" }, "peerDependenciesMeta": { "@cloudflare/workers-types": { @@ -4756,65 +4957,760 @@ } } }, - "node_modules/wrangler/node_modules/path-to-regexp": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.3.0.tgz", - "integrity": "sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==", + "node_modules/wrangler/node_modules/@cloudflare/workerd-darwin-64": { + "version": "1.20260801.1", + "resolved": "https://registry.npmjs.org/@cloudflare/workerd-darwin-64/-/workerd-darwin-64-1.20260801.1.tgz", + "integrity": "sha512-wuJWbXpKvncJi1P0GKS+iYpN5tHdb7JPJJ/+6ZQe8zzovHHVMkLJPNBsgWpqeUhpM3g9qTwEKd2rglNKejuh5A==", + "cpu": [ + "x64" + ], "dev": true, - "license": "MIT" - }, - "node_modules/wrap-ansi": { - "version": "9.0.2", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.2.tgz", - "integrity": "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^6.2.1", - "string-width": "^7.0.0", - "strip-ansi": "^7.1.0" - }, + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + "node": ">=16" } }, - "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/wrangler/node_modules/@cloudflare/workerd-darwin-arm64": { + "version": "1.20260801.1", + "resolved": "https://registry.npmjs.org/@cloudflare/workerd-darwin-arm64/-/workerd-darwin-arm64-1.20260801.1.tgz", + "integrity": "sha512-kwoZiTpnhNrF3+APx84Q/oAqvJ3sU9yefGagwm/ASaH/2W19x0vghkW/r4qCoHCK0WW7EPugZ+aXjgPMRtlq1Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=16" + } }, - "node_modules/ws": { - "version": "8.21.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.21.0.tgz", - "integrity": "sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g==", + "node_modules/wrangler/node_modules/@cloudflare/workerd-linux-64": { + "version": "1.20260801.1", + "resolved": "https://registry.npmjs.org/@cloudflare/workerd-linux-64/-/workerd-linux-64-1.20260801.1.tgz", + "integrity": "sha512-r0vAxCZH+Jih9Unm1yoyiByPNWNgawcKciOHDm5Q37ZVGOkKLsT9AtLe3yLSaul76WrKqtf+JP2n0WW32VBLJg==", + "cpu": [ + "x64" + ], "dev": true, - "license": "MIT", + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": ">=5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } + "node": ">=16" } }, - "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", + "node_modules/wrangler/node_modules/@cloudflare/workerd-linux-arm64": { + "version": "1.20260801.1", + "resolved": "https://registry.npmjs.org/@cloudflare/workerd-linux-arm64/-/workerd-linux-arm64-1.20260801.1.tgz", + "integrity": "sha512-zWgpdZtSozvIgzQNmQiDSF8yEOQJUkRAWNsDXXzAAoy+fCn8YUoSibj3mpFSbZRvbUldeBEaW6SCdC2VEMkhNQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=10" + "node": ">=16" + } + }, + "node_modules/wrangler/node_modules/@cloudflare/workerd-windows-64": { + "version": "1.20260801.1", + "resolved": "https://registry.npmjs.org/@cloudflare/workerd-windows-64/-/workerd-windows-64-1.20260801.1.tgz", + "integrity": "sha512-2oQz+Ksu4ji6e/+ZoYX+tWQEcxAii2p7l+iR8kx48W1llMalaufAsmVxTlk+3/vrM7D3/2c0iK448e0UQTcIMg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=16" + } + }, + "node_modules/wrangler/node_modules/@img/sharp-darwin-arm64": { + "version": "0.35.2", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.35.2.tgz", + "integrity": "sha512-eEieHsMksAW4IiO5NzauESRl2D2qz3J/kwUxUrSfV06A93eEaRfMpHXyUb1mAqrR7i8U9A0GRqE9pjn6u1Jjpg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-arm64": "1.3.1" + } + }, + "node_modules/wrangler/node_modules/@img/sharp-darwin-x64": { + "version": "0.35.2", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.35.2.tgz", + "integrity": "sha512-BaktuGPCeHJMARpodR8jK4uKiZrPAy9WrfQW0sdI37clracq8Bp01AYS3SZgi5FS/y5twa9t4+LIuuxQjqRrWw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-x64": "1.3.1" + } + }, + "node_modules/wrangler/node_modules/@img/sharp-libvips-darwin-arm64": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.3.1.tgz", + "integrity": "sha512-4V/M3roRMTYjiwZY9IOVQOE8OyeCxFAkYmyZDrZl51uOKjibm3oeEJ4WAmLxutAfzFbC9jqUiPs2gbnGflH+7g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/wrangler/node_modules/@img/sharp-libvips-darwin-x64": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.3.1.tgz", + "integrity": "sha512-c0/DxItpJv2+dGhgycJBBgotdqruGYDvA79drdh0MD1dFpy7JzJ/PlXwi1H4rFf0eTy8tgbI91aHDnZIceY3jQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/wrangler/node_modules/@img/sharp-libvips-linux-arm": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.3.1.tgz", + "integrity": "sha512-aGGy9aWzXgHBG7HNyQPWorZthlp7+x6fDRoPAQbGO3ThcttuTyKIx3NuSHb6zb4gBNq6/yNn9f1cy9nFKS/Vmg==", + "cpu": [ + "arm" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/wrangler/node_modules/@img/sharp-libvips-linux-arm64": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.3.1.tgz", + "integrity": "sha512-JznefmcK9j1JKPz8AkQDh89kjojubyfOasWBPKfzMIhPwsgDy9evpE/naJTXXXmghS1iFwR8u/kTwh/I2/+GCw==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/wrangler/node_modules/@img/sharp-libvips-linux-ppc64": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.3.1.tgz", + "integrity": "sha512-1EkwGNCZk6iWNCMWqrvdJ+r1j0PT1zIz60CNPhYnJlK/zyeWqlsPZIe+ocBVqPF8k/Ssee/NCk+tE9Ryrko6ng==", + "cpu": [ + "ppc64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/wrangler/node_modules/@img/sharp-libvips-linux-riscv64": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.3.1.tgz", + "integrity": "sha512-Ilays+w2bXdnxzxtQdmXR62u8o8GYa3eL4+Gr+1KiE4xperMZUslRaVPJwwPkzlHEjGfXAfRVAa/7CYCtSqsBw==", + "cpu": [ + "riscv64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/wrangler/node_modules/@img/sharp-libvips-linux-s390x": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.3.1.tgz", + "integrity": "sha512-VfBwVHQTbRoj4XlpA/KLZ7ltgMpz+4WSejFzQ+GnoImjo1PtEJ59QB2qR1xQEeRPYIkNrPIm2L4cICMvz4C2ew==", + "cpu": [ + "s390x" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/wrangler/node_modules/@img/sharp-libvips-linux-x64": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.3.1.tgz", + "integrity": "sha512-+c8ukgwU62DS54nCAjw7keOfHUkmr0B5QHEdcOqRnodF/MNXJbVI8Eopoj4B/0H8Asr65I+A4Amrn7a85/md6A==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/wrangler/node_modules/@img/sharp-libvips-linuxmusl-arm64": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.3.1.tgz", + "integrity": "sha512-qlKb/pwbkAi1WMsJrYHk7CuDrd12s27U2QnRhFYUoJNrRCmkosMTttuRFat/DDB3IlDm5qE1TJgZ4JDnHX8Ldw==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/wrangler/node_modules/@img/sharp-libvips-linuxmusl-x64": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.3.1.tgz", + "integrity": "sha512-yO21HwoUVLN8Qa+/SBjQLMYwBWAVJjeGPNe+hc0OUeMeifEtJqu5a1c4HayE1nNpDih9y3/KkoltfkDodmKAlg==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/wrangler/node_modules/@img/sharp-linux-arm": { + "version": "0.35.2", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.35.2.tgz", + "integrity": "sha512-SE4kzF2mepn6z+6E7L6lsV8FzuLL6IPQdyX8ZiwROAG/G8td+hP/m7FsFPwidtrF19gvajuC9l6TxAVcsA4S7A==", + "cpu": [ + "arm" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm": "1.3.1" + } + }, + "node_modules/wrangler/node_modules/@img/sharp-linux-arm64": { + "version": "0.35.2", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.35.2.tgz", + "integrity": "sha512-af12Pnd0ZGu2HfP8NayB0kk6eC/lrfbQE6HlR4jD+34wdJ1Vw9TF6TMn6ZvffT+WgqVsl0hRbmNvz2u/23VmwA==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm64": "1.3.1" + } + }, + "node_modules/wrangler/node_modules/@img/sharp-linux-ppc64": { + "version": "0.35.2", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.35.2.tgz", + "integrity": "sha512-hYSBm7zcNtDCozCxQHYZJiu63b/bXsgRZuOxCIBZsStMM9Vap47iFHdbX4kCvQsblPB/k+clhELpdQJHQLSHvg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-ppc64": "1.3.1" + } + }, + "node_modules/wrangler/node_modules/@img/sharp-linux-riscv64": { + "version": "0.35.2", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.35.2.tgz", + "integrity": "sha512-qQt0Kc13+Hoan/Awq/qMSQw3L+RI1NCRPgD5cUJ/1WSSmIoysLOc72jlRM3E0OHN9Yr313jgeQ2T+zW+F03QFA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-riscv64": "1.3.1" + } + }, + "node_modules/wrangler/node_modules/@img/sharp-linux-s390x": { + "version": "0.35.2", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.35.2.tgz", + "integrity": "sha512-E4fLLfRPzDLlEeDaTzI98OFLcv++WL5ChLLMwPoVd0CIoZQqupBSNbOisPL5am9XsbQ9T84+iiMpUvbFtkunbA==", + "cpu": [ + "s390x" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-s390x": "1.3.1" + } + }, + "node_modules/wrangler/node_modules/@img/sharp-linux-x64": { + "version": "0.35.2", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.35.2.tgz", + "integrity": "sha512-gi0zFJJRLswfCZmHtJdikXPOc5u7qamSOS3NHedLqLd4W8Q0NqjdBr6TTRIgsfFjqfTsHFgdfvJ9LwqSgcHiAA==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-x64": "1.3.1" + } + }, + "node_modules/wrangler/node_modules/@img/sharp-linuxmusl-arm64": { + "version": "0.35.2", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.35.2.tgz", + "integrity": "sha512-siWbOW1u6HFnFLrp0waKyW7VEf7jYvcDWdrXEFa8AkdAQgEvuu5Fz8/Y70w9EeqAdwDtfU012BhEHHaDqvQNzg==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-arm64": "1.3.1" + } + }, + "node_modules/wrangler/node_modules/@img/sharp-linuxmusl-x64": { + "version": "0.35.2", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.35.2.tgz", + "integrity": "sha512-YBqMMcjDi4QGYiSn4vNOYBhmlC4z5AXqkOUUqI2e0AFA4urNv4ESgOgwNl3K+4etQhha0twXlzeF20bbULm9Yg==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-x64": "1.3.1" + } + }, + "node_modules/wrangler/node_modules/@img/sharp-win32-arm64": { + "version": "0.35.2", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.35.2.tgz", + "integrity": "sha512-BiVRYc/t6/Vl3e1hBx0hugG4oN9Pydf4fgMSpxTQJmwGUg/YoXTWHiFeRymHfCZzifxu4F4rpk/I67D0LQ20wQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/wrangler/node_modules/@img/sharp-win32-ia32": { + "version": "0.35.2", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.35.2.tgz", + "integrity": "sha512-YYEhx9PImCC7T0tI8JDMi4DB9LwLCXCU5OWNYEXAxh5Q1ShKkyC6byxzoBJ3gEFDnH2lQckWuDe70G7mB2XJog==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/wrangler/node_modules/@img/sharp-win32-x64": { + "version": "0.35.2", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.35.2.tgz", + "integrity": "sha512-imoOyBcoM/iiUr4J6VPpCNjPnjvP/Gks95898yB8YqoGGYmHYbOyCuNv9FMhFgtaiHFGbHW8bxKqRV6VjtXThQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/wrangler/node_modules/miniflare": { + "version": "5.20260801.0-alpha", + "resolved": "https://registry.npmjs.org/miniflare/-/miniflare-5.20260801.0-alpha.tgz", + "integrity": "sha512-AfMrnQbJg81ESsGkGhOkHBtwTqCG+mosR+3GE+qDrhF1I/ieIvgg3ICxNyBvgHBZ3iapy6cysBQHNPykkzrfgw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@cspotcode/source-map-support": "0.8.1", + "sharp": "0.35.2", + "undici": "7.28.0", + "workerd": "1.20260801.1", + "ws": "8.21.0", + "youch": "4.1.0-beta.10" + }, + "engines": { + "node": ">=22.0.0" + } + }, + "node_modules/wrangler/node_modules/path-to-regexp": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.3.0.tgz", + "integrity": "sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/wrangler/node_modules/sharp": { + "version": "0.35.2", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.35.2.tgz", + "integrity": "sha512-FVtFjtBCMiJS6yb5CX7Sop45WFMpeGw6oRKuJnXYgf/f1ms/D7LE/ZUSNxnW7rZ/dbslQWYkoqFHGPaDBtaK4w==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@img/colour": "^1.1.0", + "detect-libc": "^2.1.2", + "semver": "^7.8.4" + }, + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-darwin-arm64": "0.35.2", + "@img/sharp-darwin-x64": "0.35.2", + "@img/sharp-freebsd-wasm32": "0.35.2", + "@img/sharp-libvips-darwin-arm64": "1.3.1", + "@img/sharp-libvips-darwin-x64": "1.3.1", + "@img/sharp-libvips-linux-arm": "1.3.1", + "@img/sharp-libvips-linux-arm64": "1.3.1", + "@img/sharp-libvips-linux-ppc64": "1.3.1", + "@img/sharp-libvips-linux-riscv64": "1.3.1", + "@img/sharp-libvips-linux-s390x": "1.3.1", + "@img/sharp-libvips-linux-x64": "1.3.1", + "@img/sharp-libvips-linuxmusl-arm64": "1.3.1", + "@img/sharp-libvips-linuxmusl-x64": "1.3.1", + "@img/sharp-linux-arm": "0.35.2", + "@img/sharp-linux-arm64": "0.35.2", + "@img/sharp-linux-ppc64": "0.35.2", + "@img/sharp-linux-riscv64": "0.35.2", + "@img/sharp-linux-s390x": "0.35.2", + "@img/sharp-linux-x64": "0.35.2", + "@img/sharp-linuxmusl-arm64": "0.35.2", + "@img/sharp-linuxmusl-x64": "0.35.2", + "@img/sharp-webcontainers-wasm32": "0.35.2", + "@img/sharp-win32-arm64": "0.35.2", + "@img/sharp-win32-ia32": "0.35.2", + "@img/sharp-win32-x64": "0.35.2" + } + }, + "node_modules/wrangler/node_modules/workerd": { + "version": "1.20260801.1", + "resolved": "https://registry.npmjs.org/workerd/-/workerd-1.20260801.1.tgz", + "integrity": "sha512-/g9JGTyqnHtoIscpBHqKD8swE2V4StBs2i69PmLiOhH45OP95jCFICl4F1hKlgN57rqfni5LiCitttoX5OOkVA==", + "dev": true, + "hasInstallScript": true, + "license": "Apache-2.0", + "bin": { + "workerd": "bin/workerd" + }, + "engines": { + "node": ">=16" + }, + "optionalDependencies": { + "@cloudflare/workerd-darwin-64": "1.20260801.1", + "@cloudflare/workerd-darwin-arm64": "1.20260801.1", + "@cloudflare/workerd-linux-64": "1.20260801.1", + "@cloudflare/workerd-linux-arm64": "1.20260801.1", + "@cloudflare/workerd-windows-64": "1.20260801.1" + } + }, + "node_modules/wrap-ansi": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.2.tgz", + "integrity": "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.2.1", + "string-width": "^7.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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", + "peer": true + }, + "node_modules/ws": { + "version": "8.21.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.21.0.tgz", + "integrity": "sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "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/yaml": { @@ -4833,15 +5729,15 @@ } }, "node_modules/yargs": { - "version": "18.0.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-18.0.0.tgz", - "integrity": "sha512-4UEqdc2RYGHZc7Doyqkrqiln3p9X2DZVxaGbwhn2pi7MrRagKaOcIKe8L3OxYcbhXLgLFUS3zAYuQjKBQgmuNg==", + "version": "18.1.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-18.1.0.tgz", + "integrity": "sha512-2rAgRKu54VsHkqI0/tYkmluGXHD4KW7yZoycuqDQ15QOTnc2VVfy0nN/1eMhnQLO00A+dwtK20xuCnc1YGeUyg==", "license": "MIT", "dependencies": { "cliui": "^9.0.1", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", - "string-width": "^7.2.0", + "string-width": "^8.2.1", "y18n": "^5.0.5", "yargs-parser": "^22.0.0" }, @@ -4898,9 +5794,9 @@ } }, "node_modules/zod": { - "version": "4.3.6", - "resolved": "https://registry.npmjs.org/zod/-/zod-4.3.6.tgz", - "integrity": "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==", + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.4.3.tgz", + "integrity": "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==", "license": "MIT", "funding": { "url": "https://github.com/sponsors/colinhacks" @@ -4911,6 +5807,7 @@ "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.25.2.tgz", "integrity": "sha512-O/PgfnpT1xKSDeQYSCfRI5Gy3hPf91mKVDuYLUHZJMiDFptvP41MSnWofm8dnCm0256ZNfZIM7DSzuSMAFnjHA==", "license": "ISC", + "peer": true, "peerDependencies": { "zod": "^3.25.28 || ^4" } diff --git a/package.json b/package.json index 2ea6625..c6817ee 100644 --- a/package.json +++ b/package.json @@ -11,8 +11,8 @@ }, "dependencies": { "@cloudflare/workers-oauth-provider": "^0.3.1", - "@modelcontextprotocol/sdk": "^1.0.0", - "agents": "^0.19.0", + "@modelcontextprotocol/server": "2.0.0", + "agents": "^0.20.1", "zod": "^4.0.0" }, "devDependencies": { @@ -23,7 +23,10 @@ "wrangler": "^4.0.0" }, "comments": { - "overrides": "Pin @cloudflare/workers-types across the tree to the version the worker code is written against; the vitest-pool-workers toolchain otherwise pulls a newer one whose stricter AI binding types break src/rerank.ts (types-only; runtime unaffected)." + "overrides": "Pin @cloudflare/workers-types across the tree to the version the worker code is written against; the vitest-pool-workers toolchain otherwise pulls a newer one whose stricter AI binding types break src/rerank.ts (types-only; runtime unaffected).", + "@modelcontextprotocol/server": "Exact, not a range: agents@0.20.x declares this package as a non-optional peer at exactly 2.0.0, so a caret here would let npm resolve a newer copy, nest 2.0.0 under agents/, and reproduce the two-copies type mismatch #227 was opened about. Exact makes the coupling loud — an agents bump that moves the peer fails at install time instead of at tsc.", + "@modelcontextprotocol/sdk": "Deliberately absent. The worker no longer imports SDK v1 anywhere after the 2026-07-28 flip (#224); agents still needs it as a required peer, so npm installs it, but this package declares no range of its own to drift against agents' exact pin. The bridge keeps its own SDK v1 dependency for the Claude Desktop face (mcp-server/package.json).", + "@modelcontextprotocol/client": "Deliberately absent for the same reason: only the bridge speaks client-side, and it declares its own copy. agents' required peer covers the install here." }, "overrides": { "@cloudflare/workers-types": "$@cloudflare/workers-types" diff --git a/scripts/check-schema-drift.mjs b/scripts/check-schema-drift.mjs index 9988c7f..8573fd5 100644 --- a/scripts/check-schema-drift.mjs +++ b/scripts/check-schema-drift.mjs @@ -2,7 +2,7 @@ // Guards against drift between the Worker `search` tool param schema and the // client proxy's static mirror of it. // -// source of truth : src/mcp.ts (zod object passed to this.server.tool("search", ...)) +// source of truth : src/mcp.ts (inputSchema passed to server.registerTool("search", ...)) // mirror : mcp-server/server/tools.js (TOOLS[0].inputSchema.properties) // // Why this exists (gh#157 / gh#159): the proxy answers tools/list from a @@ -62,7 +62,8 @@ function paramBlocks(schema, headerRe) { return blocks; } -// Worker: zod object is the 3rd arg of this.server.tool("search", "", { ... }, handler). +// Worker: the zod object is `inputSchema` inside the config arg of +// server.registerTool("search", { description, inputSchema }, handler). // Top-level param keys are 8-space-indented `: z`. Bounded from the tool // name to the `async ({` handler so other zod objects in the file are excluded. // Enum values come from the `.enum([...])` call inside each param's block. diff --git a/src/index.ts b/src/index.ts index 18ba5fc..cc69e32 100644 --- a/src/index.ts +++ b/src/index.ts @@ -7,7 +7,7 @@ * /oauth/token -- Token issuance and refresh * * Routes (OAuth-protected API, validated by OAuthProvider): - * POST /mcp -- MCP server (Streamable HTTP MCP protocol) + * POST /mcp -- MCP server (protocol revision 2026-07-28, stateless) * * Routes (defaultHandler, no OAuth token required): * POST /webhooks/github -- GitHub webhook receiver (IP allowlist + signature verification) @@ -22,8 +22,9 @@ * POST /admin/backfill-issue-index?repo=owner/repo[&dry_run=true][&limit=N][&cursor=N] -- Index the issue/PR numbers missing from search_docs (requires GITHUB_TOKEN header) * * Durable Objects: - * RagMcpAgentV2 -- MCP server (tools: search, get_issue_context, list_recent_activity) * IssueStore -- Issue/PR/wiki state store (SQLite-backed) + * RagMcpAgent / RagMcpAgentV2 -- retired MCP-serving classes, exported only + * to satisfy the past-migration constraint (see retired-do.ts) * * Cron Trigger: * Hourly (fallback) -- poll GitHub API for issue/PR updates, generate embeddings, upsert vectors @@ -40,7 +41,8 @@ import { } from "./oauth.js"; import { handleScheduled, pollWiki } from "./poller.js"; import { handleWebhook } from "./webhook.js"; -import { RagMcpAgentV2 } from "./mcp.js"; +import { createMcpHandler, type StatelessMcpHandler } from "agents/mcp/server"; +import { createRagMcpServer } from "./mcp.js"; import { indexWikiEdges } from "./graph.js"; import { backfillNatSegments } from "./fts.js"; import { @@ -62,15 +64,41 @@ import { // Durable Object: issue/PR state store (SQLite-backed) export { IssueStore } from "./store.js"; -// Durable Object: MCP server — legacy stub (retained for migration compatibility only). -export { RagMcpAgent } from "./mcp.js"; +// Durable Objects: retired MCP-serving stubs, retained for migration +// compatibility only (see the module comment in retired-do.ts). +export { RagMcpAgent, RagMcpAgentV2 } from "./retired-do.js"; -// Durable Object: MCP server (tools: search, get_issue_context, list_recent_activity) -export { RagMcpAgentV2 } from "./mcp.js"; - -// McpAgent.serve() returns a fetch handler for MCP protocol. -// It reads ctx.props (set by OAuthProvider) and passes them to the DO. -const mcpHandler = RagMcpAgentV2.serve("/mcp"); +/** + * MCP handler for the 2026-07-28 stateless core (issue #224). + * + * `legacy: "reject"` is the single-lane decision made literal: 2025-era + * traffic (`initialize` + `mcp-session-id`) is answered with the + * unsupported-protocol-version error naming the one revision this endpoint + * serves, rather than being routed to a compatibility lane. There is no + * fallback path to go stale. + * + * The handler is memoized per `env` rather than built at module scope, because + * the server factory needs the bindings and `env` only exists inside `fetch`. + * A Worker isolate sees one `env`, so this resolves to a single handler in + * practice; the WeakMap states that rather than assuming it. + * + * Per-request user identity does NOT come through here. The handler reads + * `ctx.props` (rewritten below) and republishes it per request, which + * `mcp.ts` reads back via `getMcpAuthContext()`. + */ +const mcpHandlers = new WeakMap(); + +function getMcpHandler(env: Env): StatelessMcpHandler { + let handler = mcpHandlers.get(env); + if (!handler) { + handler = createMcpHandler(() => createRagMcpServer(env), { + route: "/mcp", + legacy: "reject", + }); + mcpHandlers.set(env, handler); + } + return handler; +} /** * Inner handler -- processes requests after OAuthProvider routing. @@ -574,15 +602,17 @@ const innerHandler: ExportedHandler = { return new Response("Unauthorized", { status: 401 }); } - // Rewrite ctx.props to McpProps shape expected by RagMcpAgentV2. - // Pass the GitHub access token so the agent can make API calls. + // Rewrite ctx.props to the McpProps shape the tool handlers expect. + // Pass the GitHub access token so they can make API calls. writeMcpProps(ctx, { githubUserId: props.githubUserId, githubLogin: props.githubLogin, accessToken: props.githubAccessToken, }); - return mcpHandler.fetch(request, env, ctx); + // Callable form (not `.fetch`): only this one reads `ctx.props` and + // republishes it as the per-request auth context. + return getMcpHandler(env)(request, env, ctx); } // -- OAuth authorize (redirect to GitHub) -- diff --git a/src/mcp-stateless-contract.test.ts b/src/mcp-stateless-contract.test.ts new file mode 100644 index 0000000..1c5a956 --- /dev/null +++ b/src/mcp-stateless-contract.test.ts @@ -0,0 +1,180 @@ +/** + * Worker <-> bridge protocol contract (issue #224). + * + * The Worker's protocol revision is a private contract between the two + * artifacts of this repository: the Worker at `src/` and the npx bridge at + * `mcp-server/`. `server.json` declares stdio transport only, so no third-party + * client reaches the Worker directly — which is exactly why nothing outside + * this repository verifies the contract, and why it is asserted here. + * + * The two sides are wired to each other in-process: the bridge's real client + * module talks to the real `createMcpHandler` wiring over a fetch that lands on + * the handler instead of the network. What is NOT covered is anything needing + * Worker bindings — `tools/call` reaches Vectorize / Workers AI / D1, so the + * call path is verified against the deployed Worker after merge, not here. + */ + +import { describe, it, expect } from "vitest"; +import { createMcpHandler } from "agents/mcp/server"; +import { createRemoteClient } from "../mcp-server/server/remote-client.js"; +import { createRagMcpServer } from "./mcp.js"; +import type { Env } from "./types.js"; + +const ENDPOINT = "https://github-rag-mcp.liplus.workers.dev"; + +const PROPS = { + githubUserId: 4242, + githubLogin: "smileygames", + accessToken: "gho_test_token", +}; + +/** + * The Worker's `/mcp` wiring, as `index.ts` builds it. Bindings are absent — + * `createRagMcpServer` only captures `env`, and nothing below `tools/call` + * dereferences it. + */ +function workerHandler() { + const env = {} as Env; + return createMcpHandler(() => createRagMcpServer(env), { + route: "/mcp", + legacy: "reject", + }); +} + +/** + * A fetch that lands on the handler. The Host header is set explicitly because + * the handler applies DNS-rebinding protection against the endpoint hostname, + * and a `Request` built in-process carries no Host of its own. + */ +function fetchInto(handler: ReturnType): typeof fetch { + return (async (input: RequestInfo | URL, init?: RequestInit) => { + const base = input instanceof Request ? input : new Request(input, init); + const headers = new Headers(base.headers); + headers.set("host", new URL(base.url).host); + return handler(new Request(base, { headers }), {} as Env, { + props: PROPS, + } as unknown as ExecutionContext); + }) as typeof fetch; +} + +describe("worker <-> bridge stateless contract", () => { + it("serves the bridge's pinned client without a session handshake", async () => { + const seen: Array> = []; + const handler = workerHandler(); + const into = fetchInto(handler); + + const recording: typeof fetch = async (input, init) => { + const req = input instanceof Request ? input : new Request(input, init); + const body = await req.clone().text(); + seen.push({ + method: req.method, + sessionHeader: req.headers.get("mcp-session-id"), + body: body ? JSON.parse(body) : null, + }); + return into(req); + }; + + const remote = createRemoteClient({ + workerUrl: ENDPOINT, + clientVersion: "0.0.0-test", + fetch: recording, + }); + + const client = await remote.getClient(); + const tools = await client.listTools(); + expect(tools.tools.map((t: { name: string }) => t.name)).toEqual(["search"]); + + // The whole exchange, connect included, is POST-only and session-free. + expect(seen.length).toBeGreaterThan(0); + for (const call of seen) { + expect(call.method).toBe("POST"); + expect(call.sessionHeader).toBeNull(); + } + + // No `initialize`: connecting to a pinned modern endpoint probes with + // `server/discover` instead of opening a session. + const methods = seen.map((c) => (c.body as { method?: string })?.method); + expect(methods).not.toContain("initialize"); + expect(methods[0]).toBe("server/discover"); + + // Every request carries the per-request envelope the revision requires. + for (const call of seen) { + const params = (call.body as { params?: { _meta?: Record } })?.params; + expect(params?._meta?.["io.modelcontextprotocol/protocolVersion"]).toBe("2026-07-28"); + expect(params?._meta?.["io.modelcontextprotocol/clientCapabilities"]).toBeDefined(); + } + + await remote.reset(); + }); + + it("publishes the full search schema over the modern wire", async () => { + const remote = createRemoteClient({ + workerUrl: ENDPOINT, + clientVersion: "0.0.0-test", + fetch: fetchInto(workerHandler()), + }); + + const client = await remote.getClient(); + const [search] = (await client.listTools()).tools; + + // The drift check compares source text; this asserts the schema actually + // survives registration and serialization to the client. + const props = (search.inputSchema as { properties?: Record }).properties ?? {}; + expect(Object.keys(props).sort()).toEqual( + [ + "assignee", + "fusion", + "graph_expand", + "graph_hops", + "include_content", + "labels", + "milestone", + "query", + "repo", + "rerank", + "since", + "sort", + "state", + "top_k", + "type", + "until", + ].sort(), + ); + + await remote.reset(); + }); + + it("rejects the pre-flip bridge instead of serving it a compatibility lane", async () => { + const handler = workerHandler(); + const into = fetchInto(handler); + + // Byte-shape of what the pre-#224 bridge sent as its first request. + const res = await into(`${ENDPOINT}/mcp`, { + method: "POST", + headers: { + "Content-Type": "application/json", + Accept: "application/json, text/event-stream", + }, + body: JSON.stringify({ + jsonrpc: "2.0", + method: "initialize", + params: { + protocolVersion: "2024-11-05", + capabilities: {}, + clientInfo: { name: "github-rag-mcp-bridge", version: "0.9.0" }, + }, + id: "init", + }), + }); + + expect(res.status).toBe(400); + expect(res.headers.get("mcp-session-id")).toBeNull(); + + const body = (await res.json()) as { + error: { code: number; data?: { supported?: string[] } }; + }; + expect(body.error.code).toBe(-32022); + // The endpoint names the one revision it serves — a single lane, stated. + expect(body.error.data?.supported).toEqual(["2026-07-28"]); + }); +}); diff --git a/src/mcp.ts b/src/mcp.ts index dee0e1e..cdeffe1 100644 --- a/src/mcp.ts +++ b/src/mcp.ts @@ -1,20 +1,25 @@ /** - * RagMcpAgentV2 — MCP server Durable Object exposing a single consolidated - * semantic search tool. + * MCP server factory exposing a single consolidated semantic search tool. * * Tools: * search — hybrid search + time-ordered activity scan + inline doc * content fetch. Single entry point for GitHub issue / PR / * release / doc / commit-diff retrieval. * - * Extends McpAgent from "agents/mcp" (same pattern as github-webhook-mcp). - * Per-user: each authenticated user gets their own DO instance via - * idFromName("user-{githubUserId}"). + * Protocol revision 2026-07-28 (stateless core, issue #224). The server is + * built fresh per HTTP request by `createMcpHandler` in `index.ts` — there is + * no session, no `initialize`, and no Durable Object in the serving path. What + * `McpAgent` used to provide in two roles is now split: instance resolution + * from a session ID is gone outright, and user identity comes from the OAuth + * props of the request being served, read through `getMcpAuthContext()`. + * + * The Durable Objects that hold real data (`IssueStore`) are untouched; only + * the MCP-serving DO left the path. Its retired class stubs live in + * `retired-do.ts` so this module imports nothing from `cloudflare:workers`. */ -import { DurableObject } from "cloudflare:workers"; -import { McpAgent } from "agents/mcp"; -import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; +import { getMcpAuthContext } from "agents/mcp/server"; +import { McpServer } from "@modelcontextprotocol/server"; import { z } from "zod"; import type { Env, @@ -22,7 +27,7 @@ import type { ReleaseRecord, VectorMetadata, } from "./types.js"; -import type { GitHubUserProps } from "./oauth.js"; +import type { McpProps } from "./oauth.js"; import { queryFts, detectUnmatchedFilters, @@ -36,16 +41,33 @@ import { queryNeighbors, getDocsByVectorIds } from "./graph.js"; import { runScan, type ScanRow } from "./scan.js"; import { entityKey, groupByEntity } from "./aggregate.js"; -/** User context passed via props from OAuth layer */ -interface McpProps extends Record { - githubUserId: number; - githubLogin: string; - accessToken: string; -} - const GITHUB_API = "https://api.github.com"; const USER_AGENT = "github-rag-mcp/0.1.0"; +/** Get a stub to the global IssueStore DO */ +function getStore(env: Env): DurableObjectStub { + const id = env.ISSUE_STORE.idFromName("global"); + return env.ISSUE_STORE.get(id); +} + +/** + * The authenticated user's GitHub access token for the request being served. + * + * The props are the ones `index.ts` wrote onto `ctx` before handing the + * request to the MCP handler; the handler republishes them per request through + * an AsyncLocalStorage store, which is what `getMcpAuthContext()` reads. There + * is no instance field to hold them any more — the server object itself lives + * only for the duration of one request. + */ +function getGitHubToken(): string { + const props = getMcpAuthContext()?.props as McpProps | undefined; + const token = props?.accessToken; + if (!token) { + throw new Error("No GitHub access token available"); + } + return token; +} + /** Build GitHub API request headers using the authenticated user's token */ function githubHeaders(token: string): Record { return { @@ -171,59 +193,25 @@ function buildResultUrl(r: ResolvedRow): string { } /** - * Legacy class retained solely to satisfy Cloudflare's "class must exist in - * script for classes declared in past migrations (v1 new_sqlite_classes)" - * constraint. MCP_OBJECT binding points to RagMcpAgentV2 now, so this class - * never receives live traffic. It exists only so `wrangler deploy` does not - * fail with "script does not export class 'RagMcpAgent'". A follow-up PR can - * delete this class together with a deleted_classes migration. + * Build the MCP server for one request. + * + * Called by `createMcpHandler` once per HTTP request. `env` is captured from + * the Worker fetch closure; the per-user identity is NOT captured here — it is + * read at call time from `getMcpAuthContext()`, so one captured `env` serves + * every user. */ -export class RagMcpAgent extends DurableObject { - async fetch(): Promise { - return new Response("RagMcpAgent has been retired; use RagMcpAgentV2", { status: 410 }); - } -} - -export class RagMcpAgentV2 extends McpAgent { - // `agents` used to bundle its own older @modelcontextprotocol/sdk copy, so the - // McpServer type here and the one McpAgent expects came from two different - // installs and needed a @ts-expect-error. agents@0.19.0 made the SDK a plain - // dependency pinned to exactly 1.29.0, and the committed lockfile holds our own - // `^1.0.0` at that same 1.29.0, so both sides share one copy today and this - // assignment typechecks (issue #225). - // - // That agreement rests on the lockfile alone, not on the ranges. SDK 1.30.0 is - // already published, so `^1.0.0` re-resolves to it as soon as the lock is - // regenerated or `npm update` runs; npm then nests 1.29.0 under agents/ again - // and this line fails with TS2416 (the two McpServer copies have separate - // declarations of the private `_serverInfo`). Confirmed by resolving from - // package.json alone. Fix by realigning the versions, not by re-adding a - // suppression. - server = new McpServer({ +export function createRagMcpServer(env: Env): McpServer { + const server = new McpServer({ name: "github-rag-mcp", version: "0.1.0", }); - /** Get a stub to the global IssueStore DO */ - private getStore(): DurableObjectStub { - const id = this.env.ISSUE_STORE.idFromName("global"); - return this.env.ISSUE_STORE.get(id); - } - - /** Get the authenticated user's GitHub access token from props */ - private getGitHubToken(): string { - const token = this.props?.accessToken; - if (!token) { - throw new Error("No GitHub access token available"); - } - return token; - } - - async init() { - // ── search ────────────────────────────────────────── - this.server.tool( - "search", - "Unified search across GitHub issues, PRs, releases, repository documentation, GitHub Wiki pages, " + + // ── search ────────────────────────────────────────── + server.registerTool( + "search", + { + description: + "Unified search across GitHub issues, PRs, releases, repository documentation, GitHub Wiki pages, " + "commit diffs, issue/PR top-level comments, PR reviews, and PR inline review comments. " + "Three modes via the query / sort axes:\n" + " 1. Hybrid semantic search (default): dense BGE-M3 over Vectorize + sparse BM25 over D1 FTS5, " + @@ -246,7 +234,7 @@ export class RagMcpAgentV2 extends McpAgent { "Results are aggregated per underlying entity: a file's doc row and its commit diffs are one result, " + "an issue or PR and its comments / reviews are one result. top_k therefore counts distinct entities, " + "and a result that absorbed others carries same_entity { count, others[] } with links to them.", - { + inputSchema: z.object({ query: z .string() .optional() @@ -385,865 +373,867 @@ export class RagMcpAgentV2 extends McpAgent { .describe( "Graph traversal depth for graph_expand (1 or 2). Default 1. Ignored when graph_expand is false.", ), - }, - async ({ - query, - repo, - state, - labels, - milestone, - assignee, - type, - top_k, - fusion, - rerank, - sort, - since, - until, - include_content, - graph_expand, - graph_hops, - }) => { - const requestedTopK = top_k ?? 10; - const fusionMode = fusion ?? "rrf"; - const rerankEnabled = rerank ?? true; - const trimmedQuery = (query ?? "").trim(); - const graphExpand = graph_expand ?? false; - const graphHops = graph_hops ?? 1; - const isScanMode = trimmedQuery.length === 0; - const effectiveSort = - sort ?? (isScanMode ? "updated_desc" : "relevance"); - const includeContent = include_content ?? false; - - // ── Scan mode (empty query): pull time-ordered metadata ───── - // Aggregated in `scan.ts`; both window bounds are pushed down to the - // store so the per-endpoint row cap applies inside [since, until). - if (isScanMode) { - const scan = await runScan(this.getStore(), { - repo, - state, - labels, - milestone, - assignee, - type, - topK: requestedTopK, - sort: effectiveSort, - since, - until, - }); + }), + }, + async ({ + query, + repo, + state, + labels, + milestone, + assignee, + type, + top_k, + fusion, + rerank, + sort, + since, + until, + include_content, + graph_expand, + graph_hops, + }) => { + const requestedTopK = top_k ?? 10; + const fusionMode = fusion ?? "rrf"; + const rerankEnabled = rerank ?? true; + const trimmedQuery = (query ?? "").trim(); + const graphExpand = graph_expand ?? false; + const graphHops = graph_hops ?? 1; + const isScanMode = trimmedQuery.length === 0; + const effectiveSort = + sort ?? (isScanMode ? "updated_desc" : "relevance"); + const includeContent = include_content ?? false; - // Optional doc content inlining (scan mode). - type ScanResultRow = ScanRow & { content?: string }; - const items: ScanResultRow[] = scan.rows; - if (includeContent) { - await this.inlineDocContent(items, repo); - } + // ── Scan mode (empty query): pull time-ordered metadata ───── + // Aggregated in `scan.ts`; both window bounds are pushed down to the + // store so the per-endpoint row cap applies inside [since, until). + if (isScanMode) { + const scan = await runScan(getStore(env), { + repo, + state, + labels, + milestone, + assignee, + type, + topK: requestedTopK, + sort: effectiveSort, + since, + until, + }); - return { - content: [ - { - type: "text" as const, - text: JSON.stringify( - { - count: items.length, - mode: "scan", - sort: effectiveSort, - since: scan.since, - until: until ?? null, - truncated: scan.truncated, - results: items, - }, - null, - 2, - ), - }, - ], - }; + // Optional doc content inlining (scan mode). + type ScanResultRow = ScanRow & { content?: string }; + const items: ScanResultRow[] = scan.rows; + if (includeContent) { + await inlineDocContent(items, repo); } - // ── Search mode (non-empty query): existing hybrid pipeline ─ - // Overfetch on both sides (issue #91 default: top_k × 5, capped at 50). - // RERANK_MAX_CANDIDATES is the AI-side upper bound; we mirror it here - // so dense and sparse fetch enough rows to feed the reranker. - // - // The overfetch used to be conditional (label / assignee post-filter, - // time window, or reranker enabled). Entity aggregation (#189) removes - // that condition: every search-mode query now collapses several rows of - // one referent into a single result, so the candidate pool must exceed - // top_k on every path — including `rerank: false` — or the caller gets - // fewer than the top_k it asked for. - const internalTopK = Math.min(requestedTopK * 5, RERANK_MAX_CANDIDATES); + return { + content: [ + { + type: "text" as const, + text: JSON.stringify( + { + count: items.length, + mode: "scan", + sort: effectiveSort, + since: scan.since, + until: until ?? null, + truncated: scan.truncated, + results: items, + }, + null, + 2, + ), + }, + ], + }; + } - // ── Dense path: Vectorize embedding query ──────────────── - const densePromise: Promise<{ - hits: Array<{ - vectorId: string; - score: number; - meta: VectorMetadata | undefined; - }>; - error?: string; - }> = - fusionMode === "sparse_only" - ? Promise.resolve({ hits: [] }) - : (async () => { - const aiResult = await this.env.AI.run("@cf/baai/bge-m3", { - text: [trimmedQuery], - }); - const vectors = (aiResult as { data: Array }).data; - if (!vectors || vectors.length === 0) { - return { hits: [], error: "embedding_failed" }; - } - const embedding = vectors[0]; + // ── Search mode (non-empty query): existing hybrid pipeline ─ + // Overfetch on both sides (issue #91 default: top_k × 5, capped at 50). + // RERANK_MAX_CANDIDATES is the AI-side upper bound; we mirror it here + // so dense and sparse fetch enough rows to feed the reranker. + // + // The overfetch used to be conditional (label / assignee post-filter, + // time window, or reranker enabled). Entity aggregation (#189) removes + // that condition: every search-mode query now collapses several rows of + // one referent into a single result, so the candidate pool must exceed + // top_k on every path — including `rerank: false` — or the caller gets + // fewer than the top_k it asked for. + const internalTopK = Math.min(requestedTopK * 5, RERANK_MAX_CANDIDATES); - const filter: VectorizeVectorMetadataFilter = {}; - if (repo) filter["repo"] = { $eq: repo }; - if (state && state !== "all") filter["state"] = { $eq: state }; - if (type && type !== "all") filter["type"] = { $eq: type }; - if (milestone) filter["milestone"] = { $eq: milestone }; + // ── Dense path: Vectorize embedding query ──────────────── + const densePromise: Promise<{ + hits: Array<{ + vectorId: string; + score: number; + meta: VectorMetadata | undefined; + }>; + error?: string; + }> = + fusionMode === "sparse_only" + ? Promise.resolve({ hits: [] }) + : (async () => { + const aiResult = await env.AI.run("@cf/baai/bge-m3", { + text: [trimmedQuery], + }); + const vectors = (aiResult as { data: Array }).data; + if (!vectors || vectors.length === 0) { + return { hits: [], error: "embedding_failed" }; + } + const embedding = vectors[0]; - const vectorizeFilter: VectorizeVectorMetadataFilter | undefined = - Object.keys(filter).length > 0 ? filter : undefined; + const filter: VectorizeVectorMetadataFilter = {}; + if (repo) filter["repo"] = { $eq: repo }; + if (state && state !== "all") filter["state"] = { $eq: state }; + if (type && type !== "all") filter["type"] = { $eq: type }; + if (milestone) filter["milestone"] = { $eq: milestone }; - const results = await this.env.VECTORIZE.query(embedding, { - topK: internalTopK, - filter: vectorizeFilter, - returnMetadata: "all", - }); + const vectorizeFilter: VectorizeVectorMetadataFilter | undefined = + Object.keys(filter).length > 0 ? filter : undefined; - return { - hits: results.matches.map((m) => ({ - vectorId: m.id, - score: m.score, - meta: m.metadata as unknown as VectorMetadata | undefined, - })), - }; - })(); + const results = await env.VECTORIZE.query(embedding, { + topK: internalTopK, + filter: vectorizeFilter, + returnMetadata: "all", + }); - // ── Sparse path: D1 FTS5 BM25 query ────────────────────── - const sparsePromise: Promise = - fusionMode === "dense_only" - ? Promise.resolve([]) - : (async () => { - const ftsFilter: FtsFilter = {}; - if (repo) ftsFilter.repo = repo; - if (state && state !== "all") { - ftsFilter.state = state as FtsFilter["state"]; - } - if (type && type !== "all") { - ftsFilter.type = type as FtsFilter["type"]; - } - if (milestone) ftsFilter.milestone = milestone; - try { - return await queryFts(this.env.DB_FTS, trimmedQuery, internalTopK, ftsFilter); - } catch (err) { - console.error( - "search: D1 FTS5 query failed:", - err instanceof Error ? err.message : String(err), - ); - return []; - } - })(); + return { + hits: results.matches.map((m) => ({ + vectorId: m.id, + score: m.score, + meta: m.metadata as unknown as VectorMetadata | undefined, + })), + }; + })(); - const [denseResult, sparseHits] = await Promise.all([ - densePromise, - sparsePromise, - ]); + // ── Sparse path: D1 FTS5 BM25 query ────────────────────── + const sparsePromise: Promise = + fusionMode === "dense_only" + ? Promise.resolve([]) + : (async () => { + const ftsFilter: FtsFilter = {}; + if (repo) ftsFilter.repo = repo; + if (state && state !== "all") { + ftsFilter.state = state as FtsFilter["state"]; + } + if (type && type !== "all") { + ftsFilter.type = type as FtsFilter["type"]; + } + if (milestone) ftsFilter.milestone = milestone; + try { + return await queryFts(env.DB_FTS, trimmedQuery, internalTopK, ftsFilter); + } catch (err) { + console.error( + "search: D1 FTS5 query failed:", + err instanceof Error ? err.message : String(err), + ); + return []; + } + })(); - if (denseResult.error === "embedding_failed" && fusionMode !== "sparse_only") { - return { - content: [ - { type: "text" as const, text: "Failed to generate embedding for query" }, - ], - isError: true, - }; - } + const [denseResult, sparseHits] = await Promise.all([ + densePromise, + sparsePromise, + ]); - // ── Filter-match observability (issue #219) ────────────── - // `repo` filters on both sides by exact match on the full slug — - // Vectorize metadata `$eq` on the dense side, `d.repo = ?` on the sparse - // side — so a bare repository name empties BOTH candidate sets at once. - // The combined count is therefore the right probe condition; the check - // itself (and why it never asserts on a failed probe) lives in - // `detectUnmatchedFilters`. - // - // Deliberately observability only: the short slug is NOT resolved to a - // full one. Doing that needs an ambiguity design for a prefix matching - // several repositories, which is a heavier change (issue #219 non-scope). - const filtersUnmatched = await detectUnmatchedFilters( - this.env.DB_FTS, - { repo }, - denseResult.hits.length + sparseHits.length, - ); + if (denseResult.error === "embedding_failed" && fusionMode !== "sparse_only") { + return { + content: [ + { type: "text" as const, text: "Failed to generate embedding for query" }, + ], + isError: true, + }; + } - // ── Fusion: build rank maps and combine via RRF ────────── - // Both hit arrays are already ordered best-first by their respective ranker. - // For dense_only / sparse_only, RRF degenerates to a single-ranker sort, - // which preserves the original ordering without additional logic. - const denseRanks = toRankMap( - denseResult.hits.map((h) => ({ vectorId: h.vectorId })), - ); - const sparseRanks = toRankMap( - sparseHits.map((h) => ({ vectorId: h.vectorId })), - ); + // ── Filter-match observability (issue #219) ────────────── + // `repo` filters on both sides by exact match on the full slug — + // Vectorize metadata `$eq` on the dense side, `d.repo = ?` on the sparse + // side — so a bare repository name empties BOTH candidate sets at once. + // The combined count is therefore the right probe condition; the check + // itself (and why it never asserts on a failed probe) lives in + // `detectUnmatchedFilters`. + // + // Deliberately observability only: the short slug is NOT resolved to a + // full one. Doing that needs an ambiguity design for a prefix matching + // several repositories, which is a heavier change (issue #219 non-scope). + const filtersUnmatched = await detectUnmatchedFilters( + env.DB_FTS, + { repo }, + denseResult.hits.length + sparseHits.length, + ); + + // ── Fusion: build rank maps and combine via RRF ────────── + // Both hit arrays are already ordered best-first by their respective ranker. + // For dense_only / sparse_only, RRF degenerates to a single-ranker sort, + // which preserves the original ordering without additional logic. + const denseRanks = toRankMap( + denseResult.hits.map((h) => ({ vectorId: h.vectorId })), + ); + const sparseRanks = toRankMap( + sparseHits.map((h) => ({ vectorId: h.vectorId })), + ); - const rankers = new Map>(); - if (fusionMode !== "sparse_only") rankers.set("dense", denseRanks); - if (fusionMode !== "dense_only") rankers.set("sparse", sparseRanks); + const rankers = new Map>(); + if (fusionMode !== "sparse_only") rankers.set("dense", denseRanks); + if (fusionMode !== "dense_only") rankers.set("sparse", sparseRanks); - const fused = reciprocalRankFusion({ rankers }); + const fused = reciprocalRankFusion({ rankers }); - // Build a vector_id → payload lookup combining dense metadata and sparse rows. - // Dense metadata wins when both sides see a vector; sparse hits fill in the gaps - // (e.g., when BM25 surfaces a row that dense missed entirely). - const payload = new Map(); - for (const h of denseResult.hits) { + // Build a vector_id → payload lookup combining dense metadata and sparse rows. + // Dense metadata wins when both sides see a vector; sparse hits fill in the gaps + // (e.g., when BM25 surfaces a row that dense missed entirely). + const payload = new Map(); + for (const h of denseResult.hits) { + payload.set(h.vectorId, { + meta: h.meta, + ftsRow: undefined, + denseScore: h.score, + sparseScore: undefined, + }); + } + for (const h of sparseHits) { + const existing = payload.get(h.vectorId); + if (existing) { + existing.ftsRow = h; + existing.sparseScore = h.score; + } else { payload.set(h.vectorId, { - meta: h.meta, - ftsRow: undefined, - denseScore: h.score, - sparseScore: undefined, + meta: undefined, + ftsRow: h, + denseScore: undefined, + sparseScore: h.score, }); } - for (const h of sparseHits) { - const existing = payload.get(h.vectorId); - if (existing) { - existing.ftsRow = h; - existing.sparseScore = h.score; - } else { - payload.set(h.vectorId, { - meta: undefined, - ftsRow: h, - denseScore: undefined, - sparseScore: h.score, - }); - } - } + } - // ── Post-filter: labels (AND), assignee, and time window ─ - // Applied after fusion on the combined view so both dense-only and sparse-only - // hits are filtered consistently. Prefers dense metadata when available - // (has expanded label_0..3 / assignee_0..1 slots), falls back to sparse row - // (comma-separated labels / assignees) otherwise. - const resolveLabels = (vectorId: string): Set => { - const p = payload.get(vectorId); - if (!p) return new Set(); - const out = new Set(); - if (p.meta) { - for (const l of [p.meta.label_0, p.meta.label_1, p.meta.label_2, p.meta.label_3]) { - if (l) out.add(l); - } - if (p.meta.labels) { - for (const l of p.meta.labels.split(",")) { - const t = l.trim(); - if (t) out.add(t); - } - } + // ── Post-filter: labels (AND), assignee, and time window ─ + // Applied after fusion on the combined view so both dense-only and sparse-only + // hits are filtered consistently. Prefers dense metadata when available + // (has expanded label_0..3 / assignee_0..1 slots), falls back to sparse row + // (comma-separated labels / assignees) otherwise. + const resolveLabels = (vectorId: string): Set => { + const p = payload.get(vectorId); + if (!p) return new Set(); + const out = new Set(); + if (p.meta) { + for (const l of [p.meta.label_0, p.meta.label_1, p.meta.label_2, p.meta.label_3]) { + if (l) out.add(l); } - if (p.ftsRow?.labels) { - for (const l of p.ftsRow.labels.split(",")) { + if (p.meta.labels) { + for (const l of p.meta.labels.split(",")) { const t = l.trim(); if (t) out.add(t); } } - return out; - }; - - const resolveAssignees = (vectorId: string): Set => { - const p = payload.get(vectorId); - if (!p) return new Set(); - const out = new Set(); - if (p.meta) { - if (p.meta.assignee_0) out.add(p.meta.assignee_0); - if (p.meta.assignee_1) out.add(p.meta.assignee_1); - if (p.meta.assignees) { - for (const a of p.meta.assignees.split(",")) { - const t = a.trim(); - if (t) out.add(t); - } - } + } + if (p.ftsRow?.labels) { + for (const l of p.ftsRow.labels.split(",")) { + const t = l.trim(); + if (t) out.add(t); } - if (p.ftsRow?.assignees) { - for (const a of p.ftsRow.assignees.split(",")) { + } + return out; + }; + + const resolveAssignees = (vectorId: string): Set => { + const p = payload.get(vectorId); + if (!p) return new Set(); + const out = new Set(); + if (p.meta) { + if (p.meta.assignee_0) out.add(p.meta.assignee_0); + if (p.meta.assignee_1) out.add(p.meta.assignee_1); + if (p.meta.assignees) { + for (const a of p.meta.assignees.split(",")) { const t = a.trim(); if (t) out.add(t); } } - return out; - }; - - const resolveUpdatedAt = (vectorId: string): string => { - const p = payload.get(vectorId); - return p?.meta?.updated_at ?? p?.ftsRow?.updatedAt ?? ""; - }; - - let filtered = fused; - if (labels && labels.length > 0) { - filtered = filtered.filter((f) => { - const all = resolveLabels(f.vectorId); - return labels.every((l) => all.has(l)); - }); - } - if (assignee) { - filtered = filtered.filter((f) => { - const all = resolveAssignees(f.vectorId); - return all.has(assignee); - }); } - if (since) { - filtered = filtered.filter((f) => resolveUpdatedAt(f.vectorId) >= since); - } - if (until) { - filtered = filtered.filter((f) => resolveUpdatedAt(f.vectorId) < until); + if (p.ftsRow?.assignees) { + for (const a of p.ftsRow.assignees.split(",")) { + const t = a.trim(); + if (t) out.add(t); + } } + return out; + }; - // ── Reranker (3rd tier): cross-encoder re-scoring ──────── - // Only invoked when: - // - rerank is enabled (default true), - // - sort is "relevance" (time-sorted callers do not need ranker score), - // - more than one candidate survived post-filter (single-element - // reranking would not change order). - // Content supply: sparse FtsRow carries content inline. Dense-only - // candidates (no sparse hit) have no FtsRow, so we backfill their - // content from D1 `search_docs` in ONE batched - // `vector_id IN (...)` query. Without this backfill every candidate - // reaches the reranker with an empty string, `rerankCandidates` drops - // them all, and the cross-encoder never runs (issue #172). - // The reranker re-orders `filtered` in place. On error or unexpected - // shape it returns null, in which case we keep the post-filter order. - const rerankScores = new Map(); - let rerankApplied = false; - if ( - rerankEnabled && - effectiveSort === "relevance" && - filtered.length > 1 - ) { - // Backfill content for candidates with no usable sparse content. - // Bounded by `filtered.length` (already capped by the overfetch - // budget) and issued as a single batched D1 query — never a - // per-candidate fan-out. The emptiness test is `trim()`-based to match - // the filter `rerankCandidates` applies, so a whitespace-only FTS row - // is treated as missing here rather than silently dropped there. - const missingContentIds = filtered - .filter((f) => (payload.get(f.vectorId)?.ftsRow?.content ?? "").trim() === "") - .map((f) => f.vectorId); - let backfilled: Map = new Map(); - if (missingContentIds.length > 0) { - try { - const rows = await getDocsByVectorIds(this.env.DB_FTS, missingContentIds); - backfilled = new Map( - [...rows].map(([vid, row]) => [vid, String(row.content ?? "")]), - ); - } catch (err) { - // Backfill is best-effort: a D1 failure must not take the search - // down. We fall through with whatever content we already have. - console.error( - "search: rerank content backfill failed:", - err instanceof Error ? err.message : String(err), - ); - } + const resolveUpdatedAt = (vectorId: string): string => { + const p = payload.get(vectorId); + return p?.meta?.updated_at ?? p?.ftsRow?.updatedAt ?? ""; + }; + + let filtered = fused; + if (labels && labels.length > 0) { + filtered = filtered.filter((f) => { + const all = resolveLabels(f.vectorId); + return labels.every((l) => all.has(l)); + }); + } + if (assignee) { + filtered = filtered.filter((f) => { + const all = resolveAssignees(f.vectorId); + return all.has(assignee); + }); + } + if (since) { + filtered = filtered.filter((f) => resolveUpdatedAt(f.vectorId) >= since); + } + if (until) { + filtered = filtered.filter((f) => resolveUpdatedAt(f.vectorId) < until); + } + + // ── Reranker (3rd tier): cross-encoder re-scoring ──────── + // Only invoked when: + // - rerank is enabled (default true), + // - sort is "relevance" (time-sorted callers do not need ranker score), + // - more than one candidate survived post-filter (single-element + // reranking would not change order). + // Content supply: sparse FtsRow carries content inline. Dense-only + // candidates (no sparse hit) have no FtsRow, so we backfill their + // content from D1 `search_docs` in ONE batched + // `vector_id IN (...)` query. Without this backfill every candidate + // reaches the reranker with an empty string, `rerankCandidates` drops + // them all, and the cross-encoder never runs (issue #172). + // The reranker re-orders `filtered` in place. On error or unexpected + // shape it returns null, in which case we keep the post-filter order. + const rerankScores = new Map(); + let rerankApplied = false; + if ( + rerankEnabled && + effectiveSort === "relevance" && + filtered.length > 1 + ) { + // Backfill content for candidates with no usable sparse content. + // Bounded by `filtered.length` (already capped by the overfetch + // budget) and issued as a single batched D1 query — never a + // per-candidate fan-out. The emptiness test is `trim()`-based to match + // the filter `rerankCandidates` applies, so a whitespace-only FTS row + // is treated as missing here rather than silently dropped there. + const missingContentIds = filtered + .filter((f) => (payload.get(f.vectorId)?.ftsRow?.content ?? "").trim() === "") + .map((f) => f.vectorId); + let backfilled: Map = new Map(); + if (missingContentIds.length > 0) { + try { + const rows = await getDocsByVectorIds(env.DB_FTS, missingContentIds); + backfilled = new Map( + [...rows].map(([vid, row]) => [vid, String(row.content ?? "")]), + ); + } catch (err) { + // Backfill is best-effort: a D1 failure must not take the search + // down. We fall through with whatever content we already have. + console.error( + "search: rerank content backfill failed:", + err instanceof Error ? err.message : String(err), + ); } + } - const rerankInput = filtered.map((f) => { - const sparseContent = payload.get(f.vectorId)?.ftsRow?.content ?? ""; - // Prefer sparse content, fall back to the D1 backfill for - // dense-only hits (and for whitespace-only sparse rows). - const content = - sparseContent.trim() !== "" ? sparseContent : backfilled.get(f.vectorId) ?? ""; - return { id: f.vectorId, content }; - }); + const rerankInput = filtered.map((f) => { + const sparseContent = payload.get(f.vectorId)?.ftsRow?.content ?? ""; + // Prefer sparse content, fall back to the D1 backfill for + // dense-only hits (and for whitespace-only sparse rows). + const content = + sparseContent.trim() !== "" ? sparseContent : backfilled.get(f.vectorId) ?? ""; + return { id: f.vectorId, content }; + }); - const reranked = await rerankCandidates( - this.env, - trimmedQuery, - rerankInput, - // Ask the reranker to return all rows so we can attach scores even - // to candidates that drop below requestedTopK; we trim ourselves. - rerankInput.length, - ); + const reranked = await rerankCandidates( + env, + trimmedQuery, + rerankInput, + // Ask the reranker to return all rows so we can attach scores even + // to candidates that drop below requestedTopK; we trim ourselves. + rerankInput.length, + ); - // `reranked` is null on reranker error / malformed response, and an - // empty array when every candidate had empty content (nothing to - // score). Both mean "fusion order stands"; only a non-empty result - // set counts as applied, so `rerank_applied: true` always implies at - // least one non-null `rerank_score` in the response (issue #172). - if (rerankWasApplied(reranked)) { - for (const r of reranked) { - rerankScores.set(r.id, r.score); - } - // Re-order `filtered` by reranker score descending. Candidates - // missing from the reranker response (defensive: model may drop - // rows) are appended in their original relative order. - const rerankedIds = new Set(reranked.map((r) => r.id)); - const byId = new Map(filtered.map((f) => [f.vectorId, f])); - const reorderedHits: typeof filtered = []; - for (const r of reranked) { - const hit = byId.get(r.id); - if (hit) reorderedHits.push(hit); - } - for (const f of filtered) { - if (!rerankedIds.has(f.vectorId)) reorderedHits.push(f); - } - filtered = reorderedHits; - rerankApplied = true; + // `reranked` is null on reranker error / malformed response, and an + // empty array when every candidate had empty content (nothing to + // score). Both mean "fusion order stands"; only a non-empty result + // set counts as applied, so `rerank_applied: true` always implies at + // least one non-null `rerank_score` in the response (issue #172). + if (rerankWasApplied(reranked)) { + for (const r of reranked) { + rerankScores.set(r.id, r.score); + } + // Re-order `filtered` by reranker score descending. Candidates + // missing from the reranker response (defensive: model may drop + // rows) are appended in their original relative order. + const rerankedIds = new Set(reranked.map((r) => r.id)); + const byId = new Map(filtered.map((f) => [f.vectorId, f])); + const reorderedHits: typeof filtered = []; + for (const r of reranked) { + const hit = byId.get(r.id); + if (hit) reorderedHits.push(hit); } + for (const f of filtered) { + if (!rerankedIds.has(f.vectorId)) reorderedHits.push(f); + } + filtered = reorderedHits; + rerankApplied = true; } + } - // ── Time sort (override ranker order) ───────────────────── - // When the caller asked for time-ordered output even on a semantic - // query, re-sort `filtered` by the requested timestamp column. - // Rows missing the column fall to the tail (empty string sorts low - // under localeCompare-desc semantics). - if (effectiveSort === "updated_desc" || effectiveSort === "created_desc") { - const resolveTimeKey = (vectorId: string): string => { - const p = payload.get(vectorId); - if (effectiveSort === "updated_desc") { - return p?.meta?.updated_at ?? p?.ftsRow?.updatedAt ?? ""; - } - // created_desc: VectorMetadata has no created_at; FTS hit has no - // createdAt either, so both collapse to updated_at as the best - // available proxy. Documented in the schema description. + // ── Time sort (override ranker order) ───────────────────── + // When the caller asked for time-ordered output even on a semantic + // query, re-sort `filtered` by the requested timestamp column. + // Rows missing the column fall to the tail (empty string sorts low + // under localeCompare-desc semantics). + if (effectiveSort === "updated_desc" || effectiveSort === "created_desc") { + const resolveTimeKey = (vectorId: string): string => { + const p = payload.get(vectorId); + if (effectiveSort === "updated_desc") { return p?.meta?.updated_at ?? p?.ftsRow?.updatedAt ?? ""; - }; - filtered = [...filtered].sort((a, b) => { - const av = resolveTimeKey(a.vectorId); - const bv = resolveTimeKey(b.vectorId); - return bv.localeCompare(av); - }); - } + } + // created_desc: VectorMetadata has no created_at; FTS hit has no + // createdAt either, so both collapse to updated_at as the best + // available proxy. Documented in the schema description. + return p?.meta?.updated_at ?? p?.ftsRow?.updatedAt ?? ""; + }; + filtered = [...filtered].sort((a, b) => { + const av = resolveTimeKey(a.vectorId); + const bv = resolveTimeKey(b.vectorId); + return bv.localeCompare(av); + }); + } - // ── Entity aggregation (issue #189) ────────────────────── - // Several rows can point at one referent: a file is a `doc` row plus a - // `diff` row per commit, an issue or PR is its own row plus its - // comments / reviews. They crowd out independent results in the same - // top_k pool. Collapse them here — after every reordering stage, before - // the trim — so `top_k` counts referents and the representative is - // whichever row the final order ranked highest (see aggregate.ts for - // why the newest version is deliberately NOT pinned). - const groups = groupByEntity(filtered, (f) => { - const r = resolveRow(payload.get(f.vectorId)); - return entityKey({ - vectorId: f.vectorId, - repo: r.repo, - type: r.type, - number: r.number, - docPath: r.docPath, - filePath: r.filePath, - }); + // ── Entity aggregation (issue #189) ────────────────────── + // Several rows can point at one referent: a file is a `doc` row plus a + // `diff` row per commit, an issue or PR is its own row plus its + // comments / reviews. They crowd out independent results in the same + // top_k pool. Collapse them here — after every reordering stage, before + // the trim — so `top_k` counts referents and the representative is + // whichever row the final order ranked highest (see aggregate.ts for + // why the newest version is deliberately NOT pinned). + const groups = groupByEntity(filtered, (f) => { + const r = resolveRow(payload.get(f.vectorId)); + return entityKey({ + vectorId: f.vectorId, + repo: r.repo, + type: r.type, + number: r.number, + docPath: r.docPath, + filePath: r.filePath, }); + }); - // vector_id of a representative → the rows folded into it, in rank order. - const collapsedInto = new Map(); - for (const g of groups.slice(0, requestedTopK)) { - if (g.others.length > 0) { - collapsedInto.set(g.representative.vectorId, g.others); - } + // vector_id of a representative → the rows folded into it, in rank order. + const collapsedInto = new Map(); + for (const g of groups.slice(0, requestedTopK)) { + if (g.others.length > 0) { + collapsedInto.set(g.representative.vectorId, g.others); } + } - // Trim to requested top-K after fusion + post-filter (+ rerank / time - // sort / entity aggregation). - filtered = groups.slice(0, requestedTopK).map((g) => g.representative); + // Trim to requested top-K after fusion + post-filter (+ rerank / time + // sort / entity aggregation). + filtered = groups.slice(0, requestedTopK).map((g) => g.representative); - // ── Format results ─────────────────────────────────────── - type ResultItem = { - number: number; - title: string; - state: string; - type: string; - labels: string[]; - milestone: string; - assignees: string[]; - score: number; - dense_score: number | null; - sparse_score: number | null; - dense_rank: number | null; - sparse_rank: number | null; - rerank_score: number | null; - url: string; - updated_at: string; - repo: string; - tag_name?: string; - doc_path?: string; - wiki_path?: string; - wiki_extension?: string; - commit_sha?: string; - file_path?: string; - file_status?: string; - commit_date?: string; - commit_author?: string; - author?: string; - comment_id?: number; - review_id?: number; - line?: number; - content?: string; - graph_hop?: number; - graph_from?: string; - /** - * Present only when this item is the representative of an entity that - * had other rows in the candidate pool (issue #189). `count` includes - * the representative, so it is always ≥ 2 when the field is present. - * Additive: a client that ignores it sees the pre-#189 shape. - */ - same_entity?: { - count: number; - others: Array<{ - type: string; - url: string; - updated_at: string; - score: number; - commit_sha?: string; - }>; - }; + // ── Format results ─────────────────────────────────────── + type ResultItem = { + number: number; + title: string; + state: string; + type: string; + labels: string[]; + milestone: string; + assignees: string[]; + score: number; + dense_score: number | null; + sparse_score: number | null; + dense_rank: number | null; + sparse_rank: number | null; + rerank_score: number | null; + url: string; + updated_at: string; + repo: string; + tag_name?: string; + doc_path?: string; + wiki_path?: string; + wiki_extension?: string; + commit_sha?: string; + file_path?: string; + file_status?: string; + commit_date?: string; + commit_author?: string; + author?: string; + comment_id?: number; + review_id?: number; + line?: number; + content?: string; + graph_hop?: number; + graph_from?: string; + /** + * Present only when this item is the representative of an entity that + * had other rows in the candidate pool (issue #189). `count` includes + * the representative, so it is always ≥ 2 when the field is present. + * Additive: a client that ignores it sees the pre-#189 shape. + */ + same_entity?: { + count: number; + others: Array<{ + type: string; + url: string; + updated_at: string; + score: number; + commit_sha?: string; + }>; }; + }; - const items: ResultItem[] = filtered.map((f) => { - const p = payload.get(f.vectorId); - const r = resolveRow(p); + const items: ResultItem[] = filtered.map((f) => { + const p = payload.get(f.vectorId); + const r = resolveRow(p); - // Rows folded into this representative. Kept as references (never - // dropped) so the caller can still reach every version / comment. - const folded = collapsedInto.get(f.vectorId) ?? []; - const sameEntity = - folded.length > 0 - ? { - count: folded.length + 1, - others: folded.map((o) => { - const or = resolveRow(payload.get(o.vectorId)); - return { - type: or.type, - url: buildResultUrl(or), - updated_at: or.updatedAt, - score: o.fusedScore, - ...(or.type === "diff" ? { commit_sha: or.commitSha } : {}), - }; - }), - } - : undefined; - - return { - number: r.number, - title: "", // Enriched below - state: r.state, - type: r.type, - labels: r.labelsCsv ? r.labelsCsv.split(",").filter(Boolean) : [], - milestone: r.milestone, - assignees: r.assigneesCsv ? r.assigneesCsv.split(",").filter(Boolean) : [], - score: f.fusedScore, - dense_score: p?.denseScore ?? null, - sparse_score: p?.sparseScore ?? null, - dense_rank: f.contributions["dense"] ?? null, - sparse_rank: f.contributions["sparse"] ?? null, - // null when reranker was disabled, skipped (≤1 candidate), failed - // gracefully, or when this row had no content to score; populated - // otherwise. `rerank_applied: true` guarantees at least one row in - // the response carries a non-null score. - rerank_score: rerankScores.get(f.vectorId) ?? null, - url: buildResultUrl(r), - updated_at: r.updatedAt, - repo: r.repo, - ...(r.type === "release" ? { tag_name: r.tagName } : {}), - ...(r.type === "doc" ? { doc_path: r.docPath } : {}), - ...(r.type === "wiki_doc" - ? { - wiki_path: r.wikiPath, - ...(r.wikiExtension ? { wiki_extension: r.wikiExtension } : {}), - } - : {}), - ...(r.type === "diff" - ? { - commit_sha: r.commitSha, - file_path: r.filePath, - file_status: r.fileStatus, - commit_date: r.commitDate, - commit_author: r.commitAuthor, - } - : {}), - ...(r.type === "issue_comment" - ? { - author: r.author, - comment_id: r.commentId, - } - : {}), - ...(r.type === "pr_review" - ? { - author: r.author, - review_id: r.reviewId, - } - : {}), - ...(r.type === "pr_review_comment" - ? { - author: r.author, - comment_id: r.commentId, - file_path: r.filePath, - line: r.line, - commit_sha: r.commitSha, - } - : {}), - ...(sameEntity ? { same_entity: sameEntity } : {}), - }; - }); + // Rows folded into this representative. Kept as references (never + // dropped) so the caller can still reach every version / comment. + const folded = collapsedInto.get(f.vectorId) ?? []; + const sameEntity = + folded.length > 0 + ? { + count: folded.length + 1, + others: folded.map((o) => { + const or = resolveRow(payload.get(o.vectorId)); + return { + type: or.type, + url: buildResultUrl(or), + updated_at: or.updatedAt, + score: o.fusedScore, + ...(or.type === "diff" ? { commit_sha: or.commitSha } : {}), + }; + }), + } + : undefined; - // Enrich with titles from IssueStore / release store / doc store - const store = this.getStore(); - for (const item of items) { - if (item.type === "release" && item.repo && item.tag_name) { - try { - const res = await store.fetch( - new Request( - `http://store/release?repo=${encodeURIComponent(item.repo)}&tag_name=${encodeURIComponent(item.tag_name)}`, - ), - ); - if (res.ok) { - const record = (await res.json()) as ReleaseRecord; - item.title = record.name || record.tagName; + return { + number: r.number, + title: "", // Enriched below + state: r.state, + type: r.type, + labels: r.labelsCsv ? r.labelsCsv.split(",").filter(Boolean) : [], + milestone: r.milestone, + assignees: r.assigneesCsv ? r.assigneesCsv.split(",").filter(Boolean) : [], + score: f.fusedScore, + dense_score: p?.denseScore ?? null, + sparse_score: p?.sparseScore ?? null, + dense_rank: f.contributions["dense"] ?? null, + sparse_rank: f.contributions["sparse"] ?? null, + // null when reranker was disabled, skipped (≤1 candidate), failed + // gracefully, or when this row had no content to score; populated + // otherwise. `rerank_applied: true` guarantees at least one row in + // the response carries a non-null score. + rerank_score: rerankScores.get(f.vectorId) ?? null, + url: buildResultUrl(r), + updated_at: r.updatedAt, + repo: r.repo, + ...(r.type === "release" ? { tag_name: r.tagName } : {}), + ...(r.type === "doc" ? { doc_path: r.docPath } : {}), + ...(r.type === "wiki_doc" + ? { + wiki_path: r.wikiPath, + ...(r.wikiExtension ? { wiki_extension: r.wikiExtension } : {}), } - } catch { - // Best-effort enrichment - } - } else if (item.type === "doc" && item.repo && item.doc_path) { - // Use the file path as the title for docs - item.title = item.doc_path; - } else if (item.type === "wiki_doc" && item.repo && item.wiki_path) { - // Wiki page slug serves as the title; the row's url already points - // at the rendered wiki page so the slug is sufficient context. - item.title = item.wiki_path; - } else if (item.type === "diff") { - // Title = "{short-sha} {file_path}" so the result list remains - // scannable without making an additional API call. - const fp = item.file_path ?? ""; - const sha = item.commit_sha ?? ""; - const shortSha = sha ? sha.slice(0, 7) : ""; - item.title = [shortSha, fp].filter(Boolean).join(" "); - } else if (item.type === "issue_comment") { - // Title = "{author} on #{number}" — enough context to skim results. - const author = item.author ?? ""; - item.title = author ? `${author} on #${item.number}` : `comment on #${item.number}`; - } else if (item.type === "pr_review") { - // Title = "{author} {state} on #{number}" — review state gives - // the classification at a glance (APPROVED / CHANGES_REQUESTED / COMMENTED). - const author = item.author ?? ""; - const state = item.state || ""; - item.title = author ? `${author} ${state} on #${item.number}` : `review on #${item.number}`; - } else if (item.type === "pr_review_comment") { - // Title = "{author} @ {file_path}:{line}" — inline comment location. - const author = item.author ?? ""; - const fp = item.file_path ?? ""; - const line = item.line ?? 0; - item.title = author ? `${author} @ ${fp}:${line}` : `inline on #${item.number}`; - } else if (item.repo && item.number) { - try { - const res = await store.fetch( - new Request( - `http://store/issue?repo=${encodeURIComponent(item.repo)}&number=${item.number}`, - ), - ); - if (res.ok) { - const record = (await res.json()) as IssueRecord; - item.title = record.title; + : {}), + ...(r.type === "diff" + ? { + commit_sha: r.commitSha, + file_path: r.filePath, + file_status: r.fileStatus, + commit_date: r.commitDate, + commit_author: r.commitAuthor, + } + : {}), + ...(r.type === "issue_comment" + ? { + author: r.author, + comment_id: r.commentId, } - } catch { - // Best-effort enrichment; continue without title + : {}), + ...(r.type === "pr_review" + ? { + author: r.author, + review_id: r.reviewId, + } + : {}), + ...(r.type === "pr_review_comment" + ? { + author: r.author, + comment_id: r.commentId, + file_path: r.filePath, + line: r.line, + commit_sha: r.commitSha, + } + : {}), + ...(sameEntity ? { same_entity: sameEntity } : {}), + }; + }); + + // Enrich with titles from IssueStore / release store / doc store + const store = getStore(env); + for (const item of items) { + if (item.type === "release" && item.repo && item.tag_name) { + try { + const res = await store.fetch( + new Request( + `http://store/release?repo=${encodeURIComponent(item.repo)}&tag_name=${encodeURIComponent(item.tag_name)}`, + ), + ); + if (res.ok) { + const record = (await res.json()) as ReleaseRecord; + item.title = record.name || record.tagName; } + } catch { + // Best-effort enrichment + } + } else if (item.type === "doc" && item.repo && item.doc_path) { + // Use the file path as the title for docs + item.title = item.doc_path; + } else if (item.type === "wiki_doc" && item.repo && item.wiki_path) { + // Wiki page slug serves as the title; the row's url already points + // at the rendered wiki page so the slug is sufficient context. + item.title = item.wiki_path; + } else if (item.type === "diff") { + // Title = "{short-sha} {file_path}" so the result list remains + // scannable without making an additional API call. + const fp = item.file_path ?? ""; + const sha = item.commit_sha ?? ""; + const shortSha = sha ? sha.slice(0, 7) : ""; + item.title = [shortSha, fp].filter(Boolean).join(" "); + } else if (item.type === "issue_comment") { + // Title = "{author} on #{number}" — enough context to skim results. + const author = item.author ?? ""; + item.title = author ? `${author} on #${item.number}` : `comment on #${item.number}`; + } else if (item.type === "pr_review") { + // Title = "{author} {state} on #{number}" — review state gives + // the classification at a glance (APPROVED / CHANGES_REQUESTED / COMMENTED). + const author = item.author ?? ""; + const state = item.state || ""; + item.title = author ? `${author} ${state} on #${item.number}` : `review on #${item.number}`; + } else if (item.type === "pr_review_comment") { + // Title = "{author} @ {file_path}:{line}" — inline comment location. + const author = item.author ?? ""; + const fp = item.file_path ?? ""; + const line = item.line ?? 0; + item.title = author ? `${author} @ ${fp}:${line}` : `inline on #${item.number}`; + } else if (item.repo && item.number) { + try { + const res = await store.fetch( + new Request( + `http://store/issue?repo=${encodeURIComponent(item.repo)}&number=${item.number}`, + ), + ); + if (res.ok) { + const record = (await res.json()) as IssueRecord; + item.title = record.title; + } + } catch { + // Best-effort enrichment; continue without title } } + } - // Optional doc content inlining (search mode). - if (includeContent) { - await this.inlineDocContent(items); - } + // Optional doc content inlining (search mode). + if (includeContent) { + await inlineDocContent(items); + } - // ── Optional graph expansion (opt-in; default off leaves everything - // above byte-identical). Seeds from the final result set, traverses the - // Decision-Structure mention graph, and appends related wiki pages as - // extra results marked with graph_hop / graph_from. Best-effort: any - // failure returns the organic results unchanged. - let graphNeighborsAdded = 0; - if (graphExpand && filtered.length > 0) { - try { - const seedIds = filtered.map((f) => f.vectorId); - const seedSet = new Set(seedIds); - const neighbors = await queryNeighbors(this.env.DB_FTS, seedIds, { - hops: graphHops, - repo, - limit: Math.min(requestedTopK * 2, 30), - }); - const fresh = neighbors.filter((n) => !seedSet.has(n.vectorId)); - if (fresh.length > 0) { - const enrich = await getDocsByVectorIds( - this.env.DB_FTS, - fresh.map((n) => n.vectorId), - ); - const slugOf = (vid: string): string => { - const p = payload.get(vid); - return p?.meta?.wiki_path ?? p?.ftsRow?.docPath ?? vid; + // ── Optional graph expansion (opt-in; default off leaves everything + // above byte-identical). Seeds from the final result set, traverses the + // Decision-Structure mention graph, and appends related wiki pages as + // extra results marked with graph_hop / graph_from. Best-effort: any + // failure returns the organic results unchanged. + let graphNeighborsAdded = 0; + if (graphExpand && filtered.length > 0) { + try { + const seedIds = filtered.map((f) => f.vectorId); + const seedSet = new Set(seedIds); + const neighbors = await queryNeighbors(env.DB_FTS, seedIds, { + hops: graphHops, + repo, + limit: Math.min(requestedTopK * 2, 30), + }); + const fresh = neighbors.filter((n) => !seedSet.has(n.vectorId)); + if (fresh.length > 0) { + const enrich = await getDocsByVectorIds( + env.DB_FTS, + fresh.map((n) => n.vectorId), + ); + const slugOf = (vid: string): string => { + const p = payload.get(vid); + return p?.meta?.wiki_path ?? p?.ftsRow?.docPath ?? vid; + }; + for (const n of fresh) { + const row = enrich.get(n.vectorId); + if (!row) continue; // dangling edge (target not indexed) — skip + const nRepo = String(row.repo ?? ""); + const nType = String(row.type ?? ""); + const nPath = String(row.doc_path ?? ""); + const url = + nType === "wiki_doc" && nRepo && nPath + ? `https://github.com/${nRepo}/wiki/${nPath}` + : nType === "doc" && nRepo && nPath + ? `https://github.com/${nRepo}/blob/HEAD/${nPath}` + : ""; + const item: ResultItem = { + number: Number(row.number ?? 0), + title: nPath || n.vectorId, + state: String(row.state ?? ""), + type: nType, + labels: [], + milestone: String(row.milestone ?? ""), + assignees: [], + score: 0, + dense_score: null, + sparse_score: null, + dense_rank: null, + sparse_rank: null, + rerank_score: null, + url, + updated_at: String(row.updated_at ?? ""), + repo: nRepo, + graph_hop: n.hop, + graph_from: slugOf(n.fromVectorId), }; - for (const n of fresh) { - const row = enrich.get(n.vectorId); - if (!row) continue; // dangling edge (target not indexed) — skip - const nRepo = String(row.repo ?? ""); - const nType = String(row.type ?? ""); - const nPath = String(row.doc_path ?? ""); - const url = - nType === "wiki_doc" && nRepo && nPath - ? `https://github.com/${nRepo}/wiki/${nPath}` - : nType === "doc" && nRepo && nPath - ? `https://github.com/${nRepo}/blob/HEAD/${nPath}` - : ""; - const item: ResultItem = { - number: Number(row.number ?? 0), - title: nPath || n.vectorId, - state: String(row.state ?? ""), - type: nType, - labels: [], - milestone: String(row.milestone ?? ""), - assignees: [], - score: 0, - dense_score: null, - sparse_score: null, - dense_rank: null, - sparse_rank: null, - rerank_score: null, - url, - updated_at: String(row.updated_at ?? ""), - repo: nRepo, - graph_hop: n.hop, - graph_from: slugOf(n.fromVectorId), - }; - if (nType === "wiki_doc") item.wiki_path = nPath; - if (nType === "doc") item.doc_path = nPath; - if (includeContent && typeof row.content === "string") { - item.content = row.content; - } - items.push(item); - graphNeighborsAdded++; + if (nType === "wiki_doc") item.wiki_path = nPath; + if (nType === "doc") item.doc_path = nPath; + if (includeContent && typeof row.content === "string") { + item.content = row.content; } + items.push(item); + graphNeighborsAdded++; } - } catch (graphErr) { - console.error( - "graph_expand failed (returning organic results):", - graphErr instanceof Error ? graphErr.message : String(graphErr), - ); } + } catch (graphErr) { + console.error( + "graph_expand failed (returning organic results):", + graphErr instanceof Error ? graphErr.message : String(graphErr), + ); } + } - return { - content: [ - { - type: "text" as const, - text: JSON.stringify( - { - count: items.length, - mode: "search", - fusion: fusionMode, - sort: effectiveSort, - dense_candidates: denseResult.hits.length, - sparse_candidates: sparseHits.length, - // Filters that matched no row in the index at all (issue #219). - // Always present; `[]` means every applied filter matched - // something, so `count: 0` is a genuine zero-hit result. A - // listed filter means the population it selects is empty — - // the value is wrong (for `repo`, typically a bare repository - // name where the full `owner/repo` slug is required), not the - // query. Only checked when the candidate set is empty. - filters_unmatched: filtersUnmatched, - // rerank metadata: - // - rerank_requested: caller-facing flag (default true) - // - rerank_applied: whether the cross-encoder actually - // ran and re-scored. False when disabled, when there - // was ≤1 candidate to rerank, when sort != "relevance", - // when the AI call errored / returned an unexpected - // shape, or when no candidate had content to score - // (graceful fallback to fusion order). True implies at - // least one item carries a non-null `rerank_score`. - rerank_requested: rerankEnabled, - rerank_applied: rerankApplied, - since: since ?? null, - until: until ?? null, - graph_expanded: graphExpand, - graph_neighbors: graphNeighborsAdded, - results: items, - }, - null, - 2, - ), - }, - ], - }; - }, - ); - } - - /** - * Inline raw file content on up to INCLUDE_CONTENT_MAX_DOCS doc / wiki_doc rows. - * Mutates the rows in place (adds a `content` field). Non-doc rows and rows - * beyond the cap are left untouched. - * - * Doc rows are fetched via the GitHub Contents REST API (authenticated). - * Wiki_doc rows are fetched from `raw.githubusercontent.com/wiki/...` — - * GitHub does not expose wiki content through REST, so the public raw URL - * is the only path. Both branches share the same INCLUDE_CONTENT_MAX_DOCS - * cap since they target the same surface (rendered documentation) from the - * caller's perspective. - * - * Scope note: top-N doc fetch is a fan-out bound. Callers needing more - * bodies should page by repeating the search. - */ - private async inlineDocContent< - T extends { - type: string; - repo?: string; - doc_path?: string; - wiki_path?: string; - wiki_extension?: string; - content?: string; + return { + content: [ + { + type: "text" as const, + text: JSON.stringify( + { + count: items.length, + mode: "search", + fusion: fusionMode, + sort: effectiveSort, + dense_candidates: denseResult.hits.length, + sparse_candidates: sparseHits.length, + // Filters that matched no row in the index at all (issue #219). + // Always present; `[]` means every applied filter matched + // something, so `count: 0` is a genuine zero-hit result. A + // listed filter means the population it selects is empty — + // the value is wrong (for `repo`, typically a bare repository + // name where the full `owner/repo` slug is required), not the + // query. Only checked when the candidate set is empty. + filters_unmatched: filtersUnmatched, + // rerank metadata: + // - rerank_requested: caller-facing flag (default true) + // - rerank_applied: whether the cross-encoder actually + // ran and re-scored. False when disabled, when there + // was ≤1 candidate to rerank, when sort != "relevance", + // when the AI call errored / returned an unexpected + // shape, or when no candidate had content to score + // (graceful fallback to fusion order). True implies at + // least one item carries a non-null `rerank_score`. + rerank_requested: rerankEnabled, + rerank_applied: rerankApplied, + since: since ?? null, + until: until ?? null, + graph_expanded: graphExpand, + graph_neighbors: graphNeighborsAdded, + results: items, + }, + null, + 2, + ), + }, + ], + }; }, - >(rows: T[], fallbackRepo?: string): Promise { - const docRows = rows.filter((r) => r.type === "doc"); - const wikiRows = rows.filter((r) => r.type === "wiki_doc"); - if (docRows.length === 0 && wikiRows.length === 0) return; + ); - const docToFetch = docRows.slice(0, INCLUDE_CONTENT_MAX_DOCS); - const wikiToFetch = wikiRows.slice(0, INCLUDE_CONTENT_MAX_DOCS); + return server; +} - const token = this.getGitHubToken(); - const headers = githubHeaders(token); +/** + * Inline raw file content on up to INCLUDE_CONTENT_MAX_DOCS doc / wiki_doc rows. + * Mutates the rows in place (adds a `content` field). Non-doc rows and rows + * beyond the cap are left untouched. + * + * Doc rows are fetched via the GitHub Contents REST API (authenticated). + * Wiki_doc rows are fetched from `raw.githubusercontent.com/wiki/...` — + * GitHub does not expose wiki content through REST, so the public raw URL + * is the only path. Both branches share the same INCLUDE_CONTENT_MAX_DOCS + * cap since they target the same surface (rendered documentation) from the + * caller's perspective. + * + * Scope note: top-N doc fetch is a fan-out bound. Callers needing more + * bodies should page by repeating the search. + */ +async function inlineDocContent< + T extends { + type: string; + repo?: string; + doc_path?: string; + wiki_path?: string; + wiki_extension?: string; + content?: string; + }, +>(rows: T[], fallbackRepo?: string): Promise { + const docRows = rows.filter((r) => r.type === "doc"); + const wikiRows = rows.filter((r) => r.type === "wiki_doc"); + if (docRows.length === 0 && wikiRows.length === 0) return; - await Promise.all([ - ...docToFetch.map(async (row) => { - const docPath = row.doc_path; - const itemRepo = row.repo ?? fallbackRepo ?? ""; - if (!docPath || !itemRepo) return; - const url = new URL(`${GITHUB_API}/repos/${itemRepo}/contents/${docPath}`); - try { - const res = await fetch(url.toString(), { headers }); - if (!res.ok) return; - const data = (await res.json()) as { - content?: string; - encoding?: string; - }; - if (!data.content) return; - const binary = atob(data.content.replace(/\n/g, "")); - const bytes = Uint8Array.from(binary, (c) => c.charCodeAt(0)); - row.content = new TextDecoder().decode(bytes); - } catch { - // Best-effort inline; a failed fetch leaves `content` unset. - } - }), - ...wikiToFetch.map(async (row) => { - const pageName = row.wiki_path; - const itemRepo = row.repo ?? fallbackRepo ?? ""; - const ext = row.wiki_extension || "md"; - if (!pageName || !itemRepo) return; - const url = `https://raw.githubusercontent.com/wiki/${itemRepo}/${encodeURIComponent(pageName)}.${ext}`; - try { - const res = await fetch(url, { - headers: { "User-Agent": "github-rag-mcp/0.1.0" }, - }); - if (!res.ok) return; - row.content = await res.text(); - } catch { - // Best-effort inline; a failed fetch leaves `content` unset. - } - }), - ]); - } + const docToFetch = docRows.slice(0, INCLUDE_CONTENT_MAX_DOCS); + const wikiToFetch = wikiRows.slice(0, INCLUDE_CONTENT_MAX_DOCS); + + const token = getGitHubToken(); + const headers = githubHeaders(token); + + await Promise.all([ + ...docToFetch.map(async (row) => { + const docPath = row.doc_path; + const itemRepo = row.repo ?? fallbackRepo ?? ""; + if (!docPath || !itemRepo) return; + const url = new URL(`${GITHUB_API}/repos/${itemRepo}/contents/${docPath}`); + try { + const res = await fetch(url.toString(), { headers }); + if (!res.ok) return; + const data = (await res.json()) as { + content?: string; + encoding?: string; + }; + if (!data.content) return; + const binary = atob(data.content.replace(/\n/g, "")); + const bytes = Uint8Array.from(binary, (c) => c.charCodeAt(0)); + row.content = new TextDecoder().decode(bytes); + } catch { + // Best-effort inline; a failed fetch leaves `content` unset. + } + }), + ...wikiToFetch.map(async (row) => { + const pageName = row.wiki_path; + const itemRepo = row.repo ?? fallbackRepo ?? ""; + const ext = row.wiki_extension || "md"; + if (!pageName || !itemRepo) return; + const url = `https://raw.githubusercontent.com/wiki/${itemRepo}/${encodeURIComponent(pageName)}.${ext}`; + try { + const res = await fetch(url, { + headers: { "User-Agent": "github-rag-mcp/0.1.0" }, + }); + if (!res.ok) return; + row.content = await res.text(); + } catch { + // Best-effort inline; a failed fetch leaves `content` unset. + } + }), + ]); } diff --git a/src/oauth.ts b/src/oauth.ts index 215bf7c..ddb2ff9 100644 --- a/src/oauth.ts +++ b/src/oauth.ts @@ -45,9 +45,10 @@ export interface OAuthEnv { } /** - * Props consumed by RagMcpAgentV2 on the /mcp route, after the inner handler - * rewrites ctx.props from GitHubUserProps. Distinct from GitHubUserProps so - * the MCP agent does not see GitHub refresh tokens it has no use for. + * Props consumed by the MCP tool handlers on the /mcp route, after the inner + * handler rewrites ctx.props from GitHubUserProps. Distinct from + * GitHubUserProps so the MCP surface does not see GitHub refresh tokens it has + * no use for. */ export interface McpProps { githubUserId: number; @@ -66,7 +67,7 @@ export function readGitHubProps(ctx: ExecutionContext): GitHubUserProps | undefi } /** - * Replace ctx.props with the McpProps shape RagMcpAgentV2 expects. + * Replace ctx.props with the McpProps shape the MCP handler republishes. * Mirrors readGitHubProps on the write side. */ export function writeMcpProps(ctx: ExecutionContext, props: McpProps): void { diff --git a/src/retired-do.ts b/src/retired-do.ts new file mode 100644 index 0000000..39adb7f --- /dev/null +++ b/src/retired-do.ts @@ -0,0 +1,39 @@ +/** + * Retired MCP-serving Durable Object classes. + * + * Both are retained solely to satisfy Cloudflare's "class must exist in script + * for classes declared in past migrations" constraint (`RagMcpAgent` from the + * v1 migration, `RagMcpAgentV2` from v2). Neither receives live traffic since + * the stateless flip (issue #224): `/mcp` is served per request by + * `createMcpHandler`, so no session ID resolves to a DO instance any more. + * They exist only so `wrangler deploy` does not fail with "script does not + * export class 'RagMcpAgent'". Deleting them needs a `deleted_classes` + * migration, which is a separate, destructive change. + * + * They live apart from `mcp.ts` so that file imports nothing from + * `cloudflare:workers` and stays loadable outside workerd — which is what lets + * the stateless serving contract be tested in the plain node pool + * (`mcp-stateless-contract.test.ts`). + * + * The Durable Objects that hold real data (`IssueStore`) are untouched by the + * flip; only the MCP-serving classes left the path. + */ + +import { DurableObject } from "cloudflare:workers"; +import type { Env } from "./types.js"; + +export class RagMcpAgent extends DurableObject { + async fetch(): Promise { + return new Response("RagMcpAgent has been retired; /mcp is served statelessly", { + status: 410, + }); + } +} + +export class RagMcpAgentV2 extends DurableObject { + async fetch(): Promise { + return new Response("RagMcpAgentV2 has been retired; /mcp is served statelessly", { + status: 410, + }); + } +}