From 0f6b3fa379740bfe7aaaf61c4963f77aa552c79a Mon Sep 17 00:00:00 2001 From: MarioCadenas Date: Tue, 6 Oct 2026 11:01:15 +0200 Subject: [PATCH] test(playground): add e2e suite for execution identity Adds an e2e (TesterArmy) suite that checks which principal AppKit runs each call as: the app service principal by default, the signed-in user on the OBO path. A minimal test app is started in production mode and requests carry the Databricks Apps proxy headers. All assertions are deterministic. Covers checklist rows B1, B2, B3, B4, B5, B7 plus the fail-closed and no-deprecation guardrails. Without an SP secret the suite runs in local mode and skips the SP-vs-user comparisons with an explicit reason. Requires the execution-identity stack (#592, #594, #595, #597, #601). Also bumps @playwright/test to 1.63.0 (peer range of @e2edev/web). Co-authored-by: Isaac Signed-off-by: MarioCadenas --- apps/dev-playground/.gitignore | 1 + apps/dev-playground/e2e.config.ts | 28 + apps/dev-playground/e2e/identity/README.md | 74 ++ .../dev-playground/e2e/identity/agents.e2e.ts | 83 ++ .../e2e/identity/analytics.e2e.ts | 37 + .../identity/app/config/queries/whoami.sql | 1 + .../app/config/queries/whoami_obo.obo.sql | 1 + .../dev-playground/e2e/identity/app/server.ts | 58 ++ .../app/server/agents/identity/agent.ts | 65 ++ .../e2e/identity/as-user.e2e.ts | 66 ++ .../e2e/identity/deprecation.e2e.ts | 25 + apps/dev-playground/e2e/identity/env.ts | 85 ++ .../e2e/identity/lakebase.e2e.ts | 31 + apps/dev-playground/package.json | 8 +- pnpm-lock.yaml | 795 ++++++++++++++++-- 15 files changed, 1295 insertions(+), 63 deletions(-) create mode 100644 apps/dev-playground/e2e.config.ts create mode 100644 apps/dev-playground/e2e/identity/README.md create mode 100644 apps/dev-playground/e2e/identity/agents.e2e.ts create mode 100644 apps/dev-playground/e2e/identity/analytics.e2e.ts create mode 100644 apps/dev-playground/e2e/identity/app/config/queries/whoami.sql create mode 100644 apps/dev-playground/e2e/identity/app/config/queries/whoami_obo.obo.sql create mode 100644 apps/dev-playground/e2e/identity/app/server.ts create mode 100644 apps/dev-playground/e2e/identity/app/server/agents/identity/agent.ts create mode 100644 apps/dev-playground/e2e/identity/as-user.e2e.ts create mode 100644 apps/dev-playground/e2e/identity/deprecation.e2e.ts create mode 100644 apps/dev-playground/e2e/identity/env.ts create mode 100644 apps/dev-playground/e2e/identity/lakebase.e2e.ts diff --git a/apps/dev-playground/.gitignore b/apps/dev-playground/.gitignore index d79530cf5..91142ff84 100644 --- a/apps/dev-playground/.gitignore +++ b/apps/dev-playground/.gitignore @@ -1,6 +1,7 @@ # Playwright test-results/ playwright-report/ +.e2e/ # Auto-generated types (regenerated on `pnpm dev` by appKitTypesPlugin) shared/appkit-types/serving.d.ts \ No newline at end of file diff --git a/apps/dev-playground/e2e.config.ts b/apps/dev-playground/e2e.config.ts new file mode 100644 index 000000000..1aacfb1f5 --- /dev/null +++ b/apps/dev-playground/e2e.config.ts @@ -0,0 +1,28 @@ +import { web } from "@e2edev/web"; +import type { E2EConfig } from "e2e"; + +import { appEnv, userHeaders } from "./e2e/identity/env"; + +export default { + tests: "e2e/**/*.e2e.ts", + targets: [ + { + engine: web({ + url: "http://127.0.0.1:0", + readyUrl: "http://127.0.0.1:{port}/health", + // Every browser request carries the signed-in user, as the Apps proxy would. + headers: userHeaders, + command: { + executable: "node", + args: ["--import", "tsx", "server.ts"], + cwd: "e2e/identity/app", + env: appEnv, + startupTimeout: 120_000, + log: ".e2e/logs/identity-app.log", + }, + }), + }, + ], + // Identity checks share one SP and one user; keep the order and the log readable. + workers: 1, +} satisfies E2EConfig; diff --git a/apps/dev-playground/e2e/identity/README.md b/apps/dev-playground/e2e/identity/README.md new file mode 100644 index 000000000..9980a042b --- /dev/null +++ b/apps/dev-playground/e2e/identity/README.md @@ -0,0 +1,74 @@ +# Execution identity e2e suite + +End-to-end checks that AppKit runs each call as the right principal: the app +service principal (SP) by default, the signed-in user on the on-behalf-of (OBO) +path. Built on [`e2e`](https://www.npmjs.com/package/e2e) with the +`@e2edev/web` engine. + +Requires the execution-identity stack (#592, #594, #595, #597, #601). On `main` +without it, the `appkit.asUser(req)` routes in `app/server.ts` do not exist. + +## How identity is injected + +The runner starts `app/server.ts` as a production server (`NODE_ENV=production`; +development mode would turn a missing user token into a silent SP run). Requests +carry the headers the Databricks Apps proxy forwards for a signed-in user: +`x-forwarded-access-token`, `x-forwarded-user`, `x-forwarded-email`. API checks +send them with `fetch`; browser steps get them from `web({ headers })` in +`e2e.config.ts`. The server authenticates as a real SP through +`DATABRICKS_CLIENT_ID` / `DATABRICKS_CLIENT_SECRET`, so the SP and the user are +different principals and every check can fail. + +All assertions are deterministic. B5 sends a chat turn to the app's own agent; +its serving-endpoint model picks the tools, and the test reads the tool outputs +from the SSE stream, never the model's text. No e2e model provider is needed. + +## Run + +```sh +cd apps/dev-playground +export DATABRICKS_HOST=https:// +export E2E_SP_CLIENT_ID= E2E_SP_CLIENT_SECRET= +export E2E_USER_EMAIL= +export E2E_USER_TOKEN=$(databricks auth token --profile | jq -r .access_token) +export DATABRICKS_WAREHOUSE_ID= DATABRICKS_SERVING_ENDPOINT_NAME= +export LAKEBASE_ENDPOINT= PGHOST= PGDATABASE= +pnpm test:e2e # all tests +pnpm test:e2e analytics # one file +``` + +**Local mode (no SP secret).** Leave `E2E_SP_CLIENT_ID` / `E2E_SP_CLIENT_SECRET` +unset and the server runs on `E2E_USER_TOKEN`. SP and user are then the same +principal, so the 7 tests that compare them skip with +`needs a distinct SP`; the principal-kind, fail-closed, app-only, and +deprecation checks still run. + +The SP needs `CAN USE` on the warehouse, `CAN QUERY` on the endpoint, and a +Lakebase role. The app log is `.e2e/logs/identity-app.log`. + +## Checklist mapping + +| Checklist row | File | Test | +| --- | --- | --- | +| B1 `.sql` runs as SP | `analytics.e2e.ts` | B1: a .sql query runs as the app service principal | +| B2 `.obo.sql` runs as user | `analytics.e2e.ts` | B2: a .obo.sql query runs as the signed-in user | +| B3 `asUser(req).run(...)` | `as-user.e2e.ts` | B3: asUser(req).run(...) runs as the user | +| B3 one-call form | `as-user.e2e.ts` | B3: the one-call asUser(req).plugin.method() form runs as the user | +| B4 plain call runs as SP | `as-user.e2e.ts` | B4: a plain plugin call runs as the SP even with user headers | +| B5 toolkit tool as user | `agents.e2e.ts` | B5: a plugin-toolkit tool runs as the user | +| B5 hand-rolled tool and model as SP | `agents.e2e.ts` | B5: a hand-rolled tool and the model call run as the SP | +| B7 Lakebase always SP | `lakebase.e2e.ts` | B7: a Lakebase query connects as the SP even with user headers | +| B7 Lakebase is app-only | `lakebase.e2e.ts` | B7: asUser(req).lakebase is refused, never run as the user | +| Guardrail: fail-closed (asUser) | `as-user.e2e.ts` | fail-closed: asUser with no user token rejects instead of running as the SP | +| Guardrail: fail-closed (agent tool) | `agents.e2e.ts` | fail-closed: a plugin tool call with no user token never runs as the SP | +| Guardrail: no `Plugin.asUser` deprecation from core plugins | `deprecation.e2e.ts` | core plugins on the OBO path log no Plugin.asUser deprecation warning | + +B6 (unbound-warehouse OBO) and Part A (provisioning) need a deployed app or the +CLI and are not covered here. + +## How this differs from `server/testing-kit.integration.test.ts` + +That suite runs in-process against a mocked workspace client: it proves AppKit +routes a call to the user or SP context. This suite sends real HTTP through the +proxy-header path to a real workspace, so it proves which principal the +warehouse, Lakebase, and the agent's tools actually see. diff --git a/apps/dev-playground/e2e/identity/agents.e2e.ts b/apps/dev-playground/e2e/identity/agents.e2e.ts new file mode 100644 index 000000000..f397c5dbc --- /dev/null +++ b/apps/dev-playground/e2e/identity/agents.e2e.ts @@ -0,0 +1,83 @@ +import { expect, test } from "e2e"; + +import { + DISTINCT_SP, + NEEDS_DISTINCT_SP, + postSse, + SERVER_IDENTITY, + type SseEvent, + SP_CLIENT_ID, + USER_EMAIL, + userHeaders, +} from "./env"; + +// The app's own model decides to call the tools; the assertions only read tool outputs. +const PROMPT = "Who am I? Prove identity with both tools."; + +/** Tool name -> parsed output, joined from function_call and function_call_output items. */ +function toolOutputs(events: SseEvent[]): Record { + const items = events + .filter((e) => e.data?.type === "response.output_item.done") + .map((e) => e.data.item); + const names = new Map( + items + .filter((i) => i?.type === "function_call") + .map((i) => [i.call_id, i.name]), + ); + const outputs: Record = {}; + for (const item of items.filter((i) => i?.type === "function_call_output")) { + try { + outputs[names.get(item.call_id)] = JSON.parse(item.output); + } catch { + outputs[names.get(item.call_id)] = item.output; + } + } + return outputs; +} + +async function chat( + baseUrl: string | undefined, + headers?: Record, +) { + const res = await postSse( + new URL("/api/agents/chat", baseUrl), + { message: PROMPT }, + headers, + ); + return { ...res, outputs: toolOutputs(res.events) }; +} + +test.describe("agents execution identity", () => { + test("B5: a plugin-toolkit tool runs as the user", async ({ app }) => { + test.skip(!DISTINCT_SP, NEEDS_DISTINCT_SP); + const { status, outputs } = await chat(app.baseUrl, userHeaders); + expect(status).toBe(200); + expect(JSON.stringify(outputs["analytics.query"])).toContain(USER_EMAIL); + expect(JSON.stringify(outputs["analytics.query"])).not.toContain( + SP_CLIENT_ID, + ); + }); + + test("B5: a hand-rolled tool and the model call run as the SP", async ({ + app, + }) => { + const { status, outputs, text } = await chat(app.baseUrl, userHeaders); + expect(status).toBe(200); + expect(outputs.whoami_sp).toMatchObject({ principal: "app" }); + // The model call ran (it chose the tools) with no model-serving user scope: SP. + expect(text).not.toMatch(/^event: error$/m); + }); + + test("fail-closed: a plugin tool call with no user token never runs as the SP", async ({ + app, + }) => { + // A signed-in user whose token did not arrive: only the token is missing. + const { "x-forwarded-access-token": _, ...noToken } = userHeaders; + const { status, outputs } = await chat(app.baseUrl, noToken); + expect(status).toBe(200); + // The tool was called and answered with the token error, not with the server's rows. + const query = JSON.stringify(outputs["analytics.query"]); + expect(query).toMatch(/token/i); + expect(query).not.toContain(SERVER_IDENTITY); + }); +}); diff --git a/apps/dev-playground/e2e/identity/analytics.e2e.ts b/apps/dev-playground/e2e/identity/analytics.e2e.ts new file mode 100644 index 000000000..a55c0798c --- /dev/null +++ b/apps/dev-playground/e2e/identity/analytics.e2e.ts @@ -0,0 +1,37 @@ +import { expect, test } from "e2e"; + +import { + DISTINCT_SP, + identityFromQuery, + NEEDS_DISTINCT_SP, + postSse, + SP_CLIENT_ID, + USER_EMAIL, + userHeaders, +} from "./env"; + +test.describe("analytics query files", () => { + test("B1: a .sql query runs as the app service principal", async ({ + app, + }) => { + test.skip(!DISTINCT_SP, NEEDS_DISTINCT_SP); + const { status, events } = await postSse( + new URL("/api/analytics/query/whoami", app.baseUrl), + { parameters: {} }, + userHeaders, + ); + expect(status).toBe(200); + expect(identityFromQuery(events)).toBe(SP_CLIENT_ID); + }); + + test("B2: a .obo.sql query runs as the signed-in user", async ({ app }) => { + test.skip(!DISTINCT_SP, NEEDS_DISTINCT_SP); + const { status, events } = await postSse( + new URL("/api/analytics/query/whoami_obo", app.baseUrl), + { parameters: {} }, + userHeaders, + ); + expect(status).toBe(200); + expect(identityFromQuery(events)).toBe(USER_EMAIL); + }); +}); diff --git a/apps/dev-playground/e2e/identity/app/config/queries/whoami.sql b/apps/dev-playground/e2e/identity/app/config/queries/whoami.sql new file mode 100644 index 000000000..c88ff5d3b --- /dev/null +++ b/apps/dev-playground/e2e/identity/app/config/queries/whoami.sql @@ -0,0 +1 @@ +SELECT current_user() AS identity diff --git a/apps/dev-playground/e2e/identity/app/config/queries/whoami_obo.obo.sql b/apps/dev-playground/e2e/identity/app/config/queries/whoami_obo.obo.sql new file mode 100644 index 000000000..c88ff5d3b --- /dev/null +++ b/apps/dev-playground/e2e/identity/app/config/queries/whoami_obo.obo.sql @@ -0,0 +1 @@ +SELECT current_user() AS identity diff --git a/apps/dev-playground/e2e/identity/app/server.ts b/apps/dev-playground/e2e/identity/app/server.ts new file mode 100644 index 000000000..7683aef8d --- /dev/null +++ b/apps/dev-playground/e2e/identity/app/server.ts @@ -0,0 +1,58 @@ +import { analytics, createApp, lakebase, server } from "@databricks/appkit"; +import { agents } from "@databricks/appkit/beta"; + +const WHOAMI_SQL = "SELECT current_user() AS identity"; + +// The e2e runner appends to one log across runs; tests read from the last marker. +console.log("e2e-identity-app: boot"); + +/** Identity probes for the execution-identity e2e suite. Every route answers `{ identity }`. */ +createApp({ + plugins: [agents(), analytics(), lakebase(), server()], + async onPluginsReady(appkit) { + appkit.server.extend((app) => { + type Req = Parameters[0]; + const probe = (path: string, fn: (req: Req) => Promise) => + app.get(path, async (req, res) => { + try { + res.json({ identity: await fn(req) }); + } catch (error) { + const err = error as Error & { code?: string }; + res.status(500).json({ code: err.code, error: err.message }); + } + }); + const identity = (rows: unknown) => + (rows as { identity: string }[])[0]?.identity; + + probe("/e2e/default", async () => + identity(await appkit.analytics.query(WHOAMI_SQL)), + ); + probe("/e2e/as-user-block", (req) => + appkit + .asUser(req) + .run(async (kit) => identity(await kit.analytics.query(WHOAMI_SQL))), + ); + probe("/e2e/as-user-oneshot", async (req) => + identity(await appkit.asUser(req).analytics.query(WHOAMI_SQL)), + ); + probe( + "/e2e/lakebase", + async () => + (await appkit.lakebase.query("SELECT current_user AS identity")) + .rows[0]?.identity, + ); + probe( + "/e2e/lakebase-as-user", + async (req) => + ( + await appkit + .asUser(req) + .lakebase.query("SELECT current_user AS identity") + ).rows[0]?.identity, + ); + }); + }, +}).catch((error) => { + console.error(error); + process.exit(1); +}); diff --git a/apps/dev-playground/e2e/identity/app/server/agents/identity/agent.ts b/apps/dev-playground/e2e/identity/app/server/agents/identity/agent.ts new file mode 100644 index 000000000..eaf569d0e --- /dev/null +++ b/apps/dev-playground/e2e/identity/app/server/agents/identity/agent.ts @@ -0,0 +1,65 @@ +import { getCurrentPrincipalKey } from "@databricks/appkit"; +import { createAgent, tool } from "@databricks/appkit/beta"; +import { z } from "zod"; + +/** + * B5 runtime-identity probe agent (default chat agent, id = folder "identity"). + * + * Two tools make the execution identity observable in one chat turn: + * + * - `analytics.query` is a PLUGIN-TOOLKIT tool. The agents plugin dispatches + * it through `executeTool`, which opens the request's user scope, so the + * SQL runs on behalf of the signed-in USER (OBO). `current_user()` returns + * the user's email. + * + * - `whoami_sp` is a HAND-ROLLED tool({ execute }). `execute` receives only + * its arguments and runs in the ambient app context, never a user scope. + * `getCurrentPrincipalKey()` therefore returns "app" (the service + * principal), the direct complement of the OBO tool's "user:". We also + * surface the SP client id from the platform-injected env so the SP has a + * concrete identifier alongside the principal kind. + * + * Why the accessor and not a SQL round-trip: inside an agent `tools(plugins)` + * builder, `plugins.` is a toolkit provider (it only exposes + * `toolkit()`), not the service-principal exports, so a hand-rolled execute + * cannot call `plugins.analytics.query`. `getCurrentPrincipalKey()` is the + * simplest correct way for a hand-rolled execute to prove its principal. + * + * The agent model (serving endpoint) call also runs as the service principal: + * the endpoint is bound to the app SP and the app declares no `model-serving` + * user scope, so a working chat proves the model call does not use the user + * token. + */ +const WHOAMI_SQL = "SELECT current_user() AS identity"; + +export default createAgent({ + default: true, + instructions: [ + "You are a runtime identity probe.", + "When the user asks who they are, who is running, or to prove identity,", + "you MUST call BOTH tools, each exactly once, then report both results.", + `1. call \`analytics.query\` with the query "${WHOAMI_SQL}" to get the`, + "on-behalf-of USER identity (read the `identity` column from the result).", + "2. call `whoami_sp` to get the app SERVICE PRINCIPAL identity.", + "Reply with exactly these two lines:", + "USER (OBO): ", + "SERVICE PRINCIPAL: ", + ].join(" "), + tools: (plugins) => ({ + // Plugin-toolkit tool: dispatched via executeTool, runs OBO (user). + ...plugins.analytics.toolkit({ only: ["query"] }), + // Hand-rolled tool: runs in the ambient app context (service principal). + whoami_sp: tool({ + description: + "Return the running principal for a hand-rolled tool: the principal kind and the app service principal client id.", + schema: z.object({}), + annotations: { effect: "read" }, + execute: async () => ({ + // "app" when running as the service principal; "user:" would mean + // a user scope leaked into a hand-rolled tool (it must not). + principal: getCurrentPrincipalKey(), + servicePrincipalClientId: process.env.DATABRICKS_CLIENT_ID ?? null, + }), + }), + }), +}); diff --git a/apps/dev-playground/e2e/identity/as-user.e2e.ts b/apps/dev-playground/e2e/identity/as-user.e2e.ts new file mode 100644 index 000000000..de0ad800e --- /dev/null +++ b/apps/dev-playground/e2e/identity/as-user.e2e.ts @@ -0,0 +1,66 @@ +import { expect, test } from "e2e"; + +import { + DISTINCT_SP, + NEEDS_DISTINCT_SP, + SP_CLIENT_ID, + USER_EMAIL, + userHeaders, +} from "./env"; + +async function probe( + baseUrl: string | undefined, + path: string, + headers: Record = {}, +) { + const res = await fetch(new URL(path, baseUrl), { headers }); + return { + status: res.status, + body: (await res.json()) as Record, + }; +} + +test.describe("appkit.asUser(req)", () => { + test("B3: asUser(req).run(...) runs as the user", async ({ app }) => { + test.skip(!DISTINCT_SP, NEEDS_DISTINCT_SP); + expect(await probe(app.baseUrl, "/e2e/as-user-block", userHeaders)).toEqual( + { + status: 200, + body: { identity: USER_EMAIL }, + }, + ); + }); + + test("B3: the one-call asUser(req).plugin.method() form runs as the user", async ({ + app, + }) => { + test.skip(!DISTINCT_SP, NEEDS_DISTINCT_SP); + expect( + await probe(app.baseUrl, "/e2e/as-user-oneshot", userHeaders), + ).toEqual({ + status: 200, + body: { identity: USER_EMAIL }, + }); + }); + + test("B4: a plain plugin call runs as the SP even with user headers", async ({ + app, + }) => { + test.skip(!DISTINCT_SP, NEEDS_DISTINCT_SP); + expect(await probe(app.baseUrl, "/e2e/default", userHeaders)).toEqual({ + status: 200, + body: { identity: SP_CLIENT_ID }, + }); + }); + + test("fail-closed: asUser with no user token rejects instead of running as the SP", async ({ + app, + }) => { + for (const path of ["/e2e/as-user-block", "/e2e/as-user-oneshot"]) { + const { status, body } = await probe(app.baseUrl, path); + expect(status).toBe(500); + expect(body.code).toBe("AUTHENTICATION_ERROR"); + expect(body.identity).toBeUndefined(); + } + }); +}); diff --git a/apps/dev-playground/e2e/identity/deprecation.e2e.ts b/apps/dev-playground/e2e/identity/deprecation.e2e.ts new file mode 100644 index 000000000..71c2f974d --- /dev/null +++ b/apps/dev-playground/e2e/identity/deprecation.e2e.ts @@ -0,0 +1,25 @@ +import { readFile } from "node:fs/promises"; + +import { expect, test } from "e2e"; + +import { postSse, userHeaders } from "./env"; + +const LOG = new URL("../../.e2e/logs/identity-app.log", import.meta.url); + +test("core plugins on the OBO path log no Plugin.asUser deprecation warning", async ({ + app, +}) => { + await postSse( + new URL("/api/analytics/query/whoami_obo", app.baseUrl), + { parameters: {} }, + userHeaders, + ); + await fetch(new URL("/e2e/as-user-oneshot", app.baseUrl), { + headers: userHeaders, + }); + + const log = await readFile(LOG, "utf8"); + const thisRun = log.slice(log.lastIndexOf("e2e-identity-app: boot")); + expect(thisRun).toContain("e2e-identity-app: boot"); + expect(thisRun).not.toContain("Plugin.asUser is deprecated"); +}); diff --git a/apps/dev-playground/e2e/identity/env.ts b/apps/dev-playground/e2e/identity/env.ts new file mode 100644 index 000000000..5ca975d62 --- /dev/null +++ b/apps/dev-playground/e2e/identity/env.ts @@ -0,0 +1,85 @@ +function need(name: string): string { + const value = process.env[name]; + if (!value) + throw new Error(`${name} is required (see e2e/identity/README.md)`); + return value; +} + +/** The signed-in user the forwarded token belongs to. */ +export const USER_EMAIL = need("E2E_USER_EMAIL"); +const USER_TOKEN = need("E2E_USER_TOKEN"); + +/** + * With an SP secret the server runs as that SP. Without one (local mode) it runs + * on the user's own token, so SP and user are the same principal and the tests + * that compare them skip with this reason instead of passing for nothing. + */ +export const DISTINCT_SP = Boolean(process.env.E2E_SP_CLIENT_SECRET); +export const NEEDS_DISTINCT_SP = + "needs a distinct SP (set E2E_SP_CLIENT_ID and E2E_SP_CLIENT_SECRET)"; + +/** The app service principal the server runs as; `current_user()` returns its client id. */ +export const SP_CLIENT_ID = DISTINCT_SP ? need("E2E_SP_CLIENT_ID") : ""; +/** What `current_user()` returns for a call that ran on the server's own credentials. */ +export const SERVER_IDENTITY = DISTINCT_SP ? SP_CLIENT_ID : USER_EMAIL; + +/** The headers the Databricks Apps proxy forwards for a signed-in user. */ +export const userHeaders = { + "x-forwarded-access-token": USER_TOKEN, + "x-forwarded-user": USER_EMAIL, + "x-forwarded-email": USER_EMAIL, +}; + +export const appEnv = { + // Not "development": dev mode turns a missing user token into a silent SP run. + NODE_ENV: "production", + DATABRICKS_APP_PORT: "{port}", + DATABRICKS_HOST: need("DATABRICKS_HOST"), + ...(DISTINCT_SP + ? { + DATABRICKS_CLIENT_ID: SP_CLIENT_ID, + DATABRICKS_CLIENT_SECRET: need("E2E_SP_CLIENT_SECRET"), + } + : { DATABRICKS_TOKEN: USER_TOKEN }), + DATABRICKS_WAREHOUSE_ID: need("DATABRICKS_WAREHOUSE_ID"), + DATABRICKS_SERVING_ENDPOINT_NAME: need("DATABRICKS_SERVING_ENDPOINT_NAME"), + LAKEBASE_ENDPOINT: need("LAKEBASE_ENDPOINT"), + PGHOST: need("PGHOST"), + PGDATABASE: need("PGDATABASE"), + PGPORT: process.env.PGPORT ?? "5432", + PGSSLMODE: process.env.PGSSLMODE ?? "require", +}; + +export type SseEvent = { event: string; data: any }; + +/** POST a JSON body and collect every SSE event the route streams. */ +export async function postSse( + url: URL, + body: unknown, + headers: Record = {}, +): Promise<{ status: number; events: SseEvent[]; text: string }> { + const res = await fetch(url, { + method: "POST", + headers: { "content-type": "application/json", ...headers }, + body: JSON.stringify(body), + }); + const text = await res.text(); + const events: SseEvent[] = []; + for (const block of text.split("\n\n")) { + const event = /^event: (.*)$/m.exec(block)?.[1] ?? "message"; + const data = /^data: (.*)$/m.exec(block)?.[1]; + if (data === undefined) continue; + try { + events.push({ event, data: JSON.parse(data) }); + } catch { + events.push({ event, data }); + } + } + return { status: res.status, events, text }; +} + +/** `current_user()` from an analytics query stream, or undefined when it errored. */ +export function identityFromQuery(events: SseEvent[]): string | undefined { + return events.find((e) => e.data?.type === "result")?.data.data?.[0] + ?.identity; +} diff --git a/apps/dev-playground/e2e/identity/lakebase.e2e.ts b/apps/dev-playground/e2e/identity/lakebase.e2e.ts new file mode 100644 index 000000000..0b3d9e700 --- /dev/null +++ b/apps/dev-playground/e2e/identity/lakebase.e2e.ts @@ -0,0 +1,31 @@ +import { expect, test } from "e2e"; + +import { + DISTINCT_SP, + NEEDS_DISTINCT_SP, + SP_CLIENT_ID, + userHeaders, +} from "./env"; + +test.describe("lakebase is app-only", () => { + test("B7: a Lakebase query connects as the SP even with user headers", async ({ + app, + }) => { + test.skip(!DISTINCT_SP, NEEDS_DISTINCT_SP); + const res = await fetch(new URL("/e2e/lakebase", app.baseUrl), { + headers: userHeaders, + }); + expect(res.status).toBe(200); + expect(await res.json()).toEqual({ identity: SP_CLIENT_ID }); + }); + + test("B7: asUser(req).lakebase is refused, never run as the user", async ({ + app, + }) => { + const res = await fetch(new URL("/e2e/lakebase-as-user", app.baseUrl), { + headers: userHeaders, + }); + expect(res.status).toBe(500); + expect(await res.json()).toMatchObject({ code: "APP_ONLY_RESOURCE" }); + }); +}); diff --git a/apps/dev-playground/package.json b/apps/dev-playground/package.json index d07afce6d..f39bdee37 100644 --- a/apps/dev-playground/package.json +++ b/apps/dev-playground/package.json @@ -17,7 +17,8 @@ "clean:full": "rm -rf build node_modules && cd client && rm -rf dist node_modules", "test:integration": "playwright test", "test:integration:ui": "playwright test --ui", - "test:integration:headed": "playwright test --headed" + "test:integration:headed": "playwright test --headed", + "test:e2e": "e2e run" }, "keywords": [], "author": "", @@ -34,9 +35,12 @@ "typeorm": "0.3.28" }, "devDependencies": { - "@playwright/test": "1.61.0", + "@e2edev/web": "0.11.0-canary-20260925150007", + "@playwright/test": "1.63.0", "@types/node": "20.19.21", "dotenv": "16.6.1", + "e2e": "0.15.0-canary-20260925150007", + "playwright": "1.63.0", "tsdown": "0.20.3", "tsx": "4.20.6", "vite": "npm:rolldown-vite@7.1.14" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 4ced623e4..4af343b52 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -132,7 +132,7 @@ importers: version: 19.2.3(@types/react@19.2.7) '@vitejs/plugin-react': specifier: 5.1.1 - version: 5.1.1(vite@7.2.4(@types/node@24.10.1)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(tsx@4.20.6)(yaml@2.8.2)) + version: 5.1.1(vite@7.2.4(@types/node@24.10.1)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(tsx@4.23.14)(yaml@2.8.2)) eslint: specifier: 9.39.1 version: 9.39.1(jiti@2.6.1) @@ -153,7 +153,7 @@ importers: version: 8.49.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3) vite: specifier: 7.2.4 - version: 7.2.4(@types/node@24.10.1)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(tsx@4.20.6)(yaml@2.8.2) + version: 7.2.4(@types/node@24.10.1)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(tsx@4.23.14)(yaml@2.8.2) apps/dev-playground: dependencies: @@ -173,15 +173,24 @@ importers: specifier: 0.3.28 version: 0.3.28(pg@8.18.0) devDependencies: + '@e2edev/web': + specifier: 0.11.0-canary-20260925150007 + version: 0.11.0-canary-20260925150007(e2e@0.15.0-canary-20260925150007)(playwright@1.63.0) '@playwright/test': - specifier: 1.61.0 - version: 1.61.0 + specifier: 1.63.0 + version: 1.63.0 '@types/node': specifier: 20.19.21 version: 20.19.21 dotenv: specifier: 16.6.1 version: 16.6.1 + e2e: + specifier: 0.15.0-canary-20260925150007 + version: 0.15.0-canary-20260925150007 + playwright: + specifier: 1.63.0 + version: 1.63.0 tsdown: specifier: 0.20.3 version: 0.20.3(@arethetypeswrong/core@0.18.4)(oxc-resolver@11.19.1)(publint@0.3.15)(typescript@5.9.3) @@ -368,7 +377,7 @@ importers: version: link:../shared vite: specifier: npm:rolldown-vite@7.1.14 - version: rolldown-vite@7.1.14(@types/node@25.2.3)(esbuild@0.25.10)(jiti@2.6.1)(terser@5.44.1)(tsx@4.20.6)(yaml@2.8.2) + version: rolldown-vite@7.1.14(@types/node@25.2.3)(esbuild@0.25.10)(jiti@2.6.1)(terser@5.44.1)(tsx@4.23.14)(yaml@2.8.2) ws: specifier: 8.21.0 version: 8.21.0 @@ -399,13 +408,13 @@ importers: version: 8.18.1 '@vitejs/plugin-react': specifier: 5.1.1 - version: 5.1.1(rolldown-vite@7.1.14(@types/node@25.2.3)(esbuild@0.25.10)(jiti@2.6.1)(terser@5.44.1)(tsx@4.20.6)(yaml@2.8.2)) + version: 5.1.1(rolldown-vite@7.1.14(@types/node@25.2.3)(esbuild@0.25.10)(jiti@2.6.1)(terser@5.44.1)(tsx@4.23.14)(yaml@2.8.2)) autoevals: specifier: 0.3.0 version: 0.3.0(ws@8.21.0)(zod@4.3.6) vitest: specifier: 3.2.4 - version: 3.2.4(@types/debug@4.1.12)(@types/node@25.2.3)(jiti@2.6.1)(jsdom@27.0.0(postcss@8.5.6))(lightningcss@1.30.2)(terser@5.44.1)(tsx@4.20.6)(yaml@2.8.2) + version: 3.2.4(@types/debug@4.1.12)(@types/node@25.2.3)(jiti@2.6.1)(jsdom@27.0.0(postcss@8.5.6))(lightningcss@1.30.2)(terser@5.44.1)(tsx@4.23.14)(yaml@2.8.2) packages/appkit-ui: dependencies: @@ -656,6 +665,10 @@ packages: resolution: {integrity: sha512-6o7Y2SeO9vFKB8lArHXehNuusnpddKPk7xqL7T2/b+OvXMRIXUO1rR4wcv1hAFUAT9avGZshty3Wlua/XA7TvA==} engines: {node: '>=18'} + '@ai-sdk/provider@4.0.17': + resolution: {integrity: sha512-VYMBxIQdcHqbIf1j+YZlI9Ati6LZ4wJe0GGd4z4a5H/KxTggjeOiyaVYTnfF7LHZK5jMQ+rofmzz4QPqf++NUw==} + engines: {node: '>=22'} + '@ai-sdk/react@2.0.115': resolution: {integrity: sha512-Etu7gWSEi2dmXss1PoR5CAZGwGShXsF9+Pon1eRO6EmatjYaBMhq1CfHPyYhGzWrint8jJIK2VaAhiMef29qZw==} engines: {node: '>=18'} @@ -1572,9 +1585,17 @@ packages: '@clack/core@1.0.1': resolution: {integrity: sha512-WKeyK3NOBwDOzagPR5H08rFk9D/WuN705yEbuZvKqlkmoLM2woKtXb10OO2k1NoSU4SFG947i2/SCYh+2u5e4g==} + '@clack/core@1.5.1': + resolution: {integrity: sha512-iHTrHA8MtVuLl2TfZySmcKv1qO2PoyC9Z7pfSDozEuV5vtY3/wcOPKJXlqJ5Oq2Cx5DDGQGAMVx6HZfRRoVEbQ==} + engines: {node: '>= 20.12.0'} + '@clack/prompts@1.0.1': resolution: {integrity: sha512-/42G73JkuYdyWZ6m8d/CJtBrGl1Hegyc7Fy78m5Ob+jF85TOUmLR5XLce/U3LxYAw0kJ8CT5aI99RIvPHcGp/Q==} + '@clack/prompts@1.8.1': + resolution: {integrity: sha512-dlT1m5e/0yUL0kRNcQn7yGLVThkgbB0Ga/1AmfDDC/8ik6AIiSf2QLQO2zPYvefsHP0aFgxO93cVLCCfDp7kzQ==} + engines: {node: '>= 20.12.0'} + '@colors/colors@1.5.0': resolution: {integrity: sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==} engines: {node: '>=0.1.90'} @@ -2189,6 +2210,14 @@ packages: resolution: {integrity: sha512-lBSBiRruFurFKXr5Hbsl2thmGweAPmddhF3jb99U4EMDA5L+e5Y1rAkOS07Nvrup7HUMBDrCV45meaxZnt28nQ==} engines: {node: '>=20.0'} + '@e2edev/web@0.11.0-canary-20260925150007': + resolution: {integrity: sha512-PLLCM8ZXw2vpP7fqps2EfA1gRBOvhfEDrRmOCQIzn1D8yq89JC/BivuLoWLvW40uGuvVT2UIsMUGceWwvvErvQ==} + engines: {node: '>=22.12.0'} + deprecated: Moved to @e2e-dev/web + peerDependencies: + e2e: 0.15.0-canary-20260925150007 + playwright: '>=1.63.0 <2' + '@emnapi/core@1.8.1': resolution: {integrity: sha512-AvT9QFpxK0Zd8J0jopedNm+w/2fIzvtPKPjqyw9jwvBaReTTqPBk9Hixaz7KbjimP+QNz605/XnjFcDAL2pqBg==} @@ -2204,156 +2233,312 @@ packages: cpu: [ppc64] os: [aix] + '@esbuild/aix-ppc64@0.28.2': + resolution: {integrity: sha512-XExcO+dvLKvVtNTibSTBej1NCAbaGhWn9Ww1ZPx80qsahhPFe/8jgWP0IchNe0F3HwkU7n8ejhH8bjonqht8mQ==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + '@esbuild/android-arm64@0.25.10': resolution: {integrity: sha512-LSQa7eDahypv/VO6WKohZGPSJDq5OVOo3UoFR1E4t4Gj1W7zEQMUhI+lo81H+DtB+kP+tDgBp+M4oNCwp6kffg==} engines: {node: '>=18'} cpu: [arm64] os: [android] + '@esbuild/android-arm64@0.28.2': + resolution: {integrity: sha512-5YfKeeI8qWfBZIX+u2xZC3Zlb3Os/gLS2sbEKM+I4ZOcsWmHS2WLysCcQZDAFRslDUU5Oiq44gf6PYN1vGwG5A==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + '@esbuild/android-arm@0.25.10': resolution: {integrity: sha512-dQAxF1dW1C3zpeCDc5KqIYuZ1tgAdRXNoZP7vkBIRtKZPYe2xVr/d3SkirklCHudW1B45tGiUlz2pUWDfbDD4w==} engines: {node: '>=18'} cpu: [arm] os: [android] + '@esbuild/android-arm@0.28.2': + resolution: {integrity: sha512-kXXoiPVVGQcnIYGOeaovwOURpniDBpSq4A03qkQ+BMQqtGG6HYap3xne9C1O1yo4TR3qxlCX5IqqmX6fFo2Lqg==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + '@esbuild/android-x64@0.25.10': resolution: {integrity: sha512-MiC9CWdPrfhibcXwr39p9ha1x0lZJ9KaVfvzA0Wxwz9ETX4v5CHfF09bx935nHlhi+MxhA63dKRRQLiVgSUtEg==} engines: {node: '>=18'} cpu: [x64] os: [android] + '@esbuild/android-x64@0.28.2': + resolution: {integrity: sha512-O387ite7SzUyCcy3JQX4P4bLtEA7bLLkx+esve5JHnyYfNTxcVpXZo9jhdB0lTKN44gztELTdU7nS8Nr16Fs1Q==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + '@esbuild/darwin-arm64@0.25.10': resolution: {integrity: sha512-JC74bdXcQEpW9KkV326WpZZjLguSZ3DfS8wrrvPMHgQOIEIG/sPXEN/V8IssoJhbefLRcRqw6RQH2NnpdprtMA==} engines: {node: '>=18'} cpu: [arm64] os: [darwin] + '@esbuild/darwin-arm64@0.28.2': + resolution: {integrity: sha512-n4KqkOQrraxHJcgjM1RvwbigfQKIKJVpM7xp+KsxiyUSrRdIXnt73VhrPAx0fV44hgfmIVKjxMN9J1t5jySVkw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + '@esbuild/darwin-x64@0.25.10': resolution: {integrity: sha512-tguWg1olF6DGqzws97pKZ8G2L7Ig1vjDmGTwcTuYHbuU6TTjJe5FXbgs5C1BBzHbJ2bo1m3WkQDbWO2PvamRcg==} engines: {node: '>=18'} cpu: [x64] os: [darwin] + '@esbuild/darwin-x64@0.28.2': + resolution: {integrity: sha512-uq6suIWYP37qzGddBKPw5QEQPi6HiLGsO7UmkpfyaYNQ3D+rN6w6WfwH+nuqcGXWvawGwxOEroO4YGnFh95azw==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + '@esbuild/freebsd-arm64@0.25.10': resolution: {integrity: sha512-3ZioSQSg1HT2N05YxeJWYR+Libe3bREVSdWhEEgExWaDtyFbbXWb49QgPvFH8u03vUPX10JhJPcz7s9t9+boWg==} engines: {node: '>=18'} cpu: [arm64] os: [freebsd] + '@esbuild/freebsd-arm64@0.28.2': + resolution: {integrity: sha512-n+I0BTSRIoy+d6RPKnEVwql5UwBJolytvY4mAOIEJorKlqgPII8ix6slVVrfZ5Tnj7glIZvloylbB/EJPMWEXw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + '@esbuild/freebsd-x64@0.25.10': resolution: {integrity: sha512-LLgJfHJk014Aa4anGDbh8bmI5Lk+QidDmGzuC2D+vP7mv/GeSN+H39zOf7pN5N8p059FcOfs2bVlrRr4SK9WxA==} engines: {node: '>=18'} cpu: [x64] os: [freebsd] + '@esbuild/freebsd-x64@0.28.2': + resolution: {integrity: sha512-78XJTJkvPs0kz2w61301PJjXl4g7q3JqiYMZ/M/yVI73EHBrCRTgkhu9oqG7vPqq+a/yadEW8aD+agKlk5xrmg==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + '@esbuild/linux-arm64@0.25.10': resolution: {integrity: sha512-5luJWN6YKBsawd5f9i4+c+geYiVEw20FVW5x0v1kEMWNq8UctFjDiMATBxLvmmHA4bf7F6hTRaJgtghFr9iziQ==} engines: {node: '>=18'} cpu: [arm64] os: [linux] + '@esbuild/linux-arm64@0.28.2': + resolution: {integrity: sha512-pW4AC0P3it8c7do9MVM4p51FzHzdM/TZrerurgRcHJ2WTa1VQ1CIq18xncfpBJw4ojkiZZrKW2yIBWBP92j6Ug==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + '@esbuild/linux-arm@0.25.10': resolution: {integrity: sha512-oR31GtBTFYCqEBALI9r6WxoU/ZofZl962pouZRTEYECvNF/dtXKku8YXcJkhgK/beU+zedXfIzHijSRapJY3vg==} engines: {node: '>=18'} cpu: [arm] os: [linux] + '@esbuild/linux-arm@0.28.2': + resolution: {integrity: sha512-XlDnu2q5yoqems+xay6wSAcg9DDD7K9RLKZEBOMZm3ckNpJBvOX20tSfby8KfrrhINDyv9V2YVZKY/SpoGJI8w==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + '@esbuild/linux-ia32@0.25.10': resolution: {integrity: sha512-NrSCx2Kim3EnnWgS4Txn0QGt0Xipoumb6z6sUtl5bOEZIVKhzfyp/Lyw4C1DIYvzeW/5mWYPBFJU3a/8Yr75DQ==} engines: {node: '>=18'} cpu: [ia32] os: [linux] + '@esbuild/linux-ia32@0.28.2': + resolution: {integrity: sha512-CYbnj78HsIeA+DhgUKgFCfvNsTHFhMMrinUrMZpDXJXKN8T3XViTZ/+wtHeVxEWY8ewSzTFN+nRmSwO2tZaLUQ==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + '@esbuild/linux-loong64@0.25.10': resolution: {integrity: sha512-xoSphrd4AZda8+rUDDfD9J6FUMjrkTz8itpTITM4/xgerAZZcFW7Dv+sun7333IfKxGG8gAq+3NbfEMJfiY+Eg==} engines: {node: '>=18'} cpu: [loong64] os: [linux] + '@esbuild/linux-loong64@0.28.2': + resolution: {integrity: sha512-buwkd8nsph4R+ajRvw0qM5Hja/TXQow3ptzWO2EbG/cqcIkHloRrdlBtQlshyYGTNFvfkfJ5tpPLVkY4DtsPfQ==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + '@esbuild/linux-mips64el@0.25.10': resolution: {integrity: sha512-ab6eiuCwoMmYDyTnyptoKkVS3k8fy/1Uvq7Dj5czXI6DF2GqD2ToInBI0SHOp5/X1BdZ26RKc5+qjQNGRBelRA==} engines: {node: '>=18'} cpu: [mips64el] os: [linux] + '@esbuild/linux-mips64el@0.28.2': + resolution: {integrity: sha512-ZVykbDyk7519VwiNb9Lcj9m8XM6v5V9uKPvrEMkkEedVewf+0itkhahp4HDpgERXhwLRpWFypsGbG/J8s0QjJA==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + '@esbuild/linux-ppc64@0.25.10': resolution: {integrity: sha512-NLinzzOgZQsGpsTkEbdJTCanwA5/wozN9dSgEl12haXJBzMTpssebuXR42bthOF3z7zXFWH1AmvWunUCkBE4EA==} engines: {node: '>=18'} cpu: [ppc64] os: [linux] + '@esbuild/linux-ppc64@0.28.2': + resolution: {integrity: sha512-CAXl+Dtd9UUuJd8pKKdwh6MLm3MUMiqMPmhZ3tTSXPqfyQ3vDl6R5hZdZ/kYojK4ofXtdfSv1tFq8XzWx3heNQ==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + '@esbuild/linux-riscv64@0.25.10': resolution: {integrity: sha512-FE557XdZDrtX8NMIeA8LBJX3dC2M8VGXwfrQWU7LB5SLOajfJIxmSdyL/gU1m64Zs9CBKvm4UAuBp5aJ8OgnrA==} engines: {node: '>=18'} cpu: [riscv64] os: [linux] + '@esbuild/linux-riscv64@0.28.2': + resolution: {integrity: sha512-GeXCej4IQtU1B+QlDV8W/RRvbzI3O/Stss+/bCXv4lZls5WGRtu2a+3JkA3i4qIUlMXpcHebWpF8AkJhATowuA==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + '@esbuild/linux-s390x@0.25.10': resolution: {integrity: sha512-3BBSbgzuB9ajLoVZk0mGu+EHlBwkusRmeNYdqmznmMc9zGASFjSsxgkNsqmXugpPk00gJ0JNKh/97nxmjctdew==} engines: {node: '>=18'} cpu: [s390x] os: [linux] + '@esbuild/linux-s390x@0.28.2': + resolution: {integrity: sha512-3H1weTYZPxt/WOhByszQZybS9w5lKzUn1FDMsgEChbHWQwHYQQRfBxgCcZvPhjHfKyJjIievvMmEUawJrdY9Dg==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + '@esbuild/linux-x64@0.25.10': resolution: {integrity: sha512-QSX81KhFoZGwenVyPoberggdW1nrQZSvfVDAIUXr3WqLRZGZqWk/P4T8p2SP+de2Sr5HPcvjhcJzEiulKgnxtA==} engines: {node: '>=18'} cpu: [x64] os: [linux] + '@esbuild/linux-x64@0.28.2': + resolution: {integrity: sha512-4xTZr1FUmSoQW4XIWmit3tzQrUTZM+N3P0XV8xROKYF50XfI7xeO90+1bZvNwxIufQ9hDQVRJH5YhgPVF8A/HQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + '@esbuild/netbsd-arm64@0.25.10': resolution: {integrity: sha512-AKQM3gfYfSW8XRk8DdMCzaLUFB15dTrZfnX8WXQoOUpUBQ+NaAFCP1kPS/ykbbGYz7rxn0WS48/81l9hFl3u4A==} engines: {node: '>=18'} cpu: [arm64] os: [netbsd] + '@esbuild/netbsd-arm64@0.28.2': + resolution: {integrity: sha512-sSATRjPeDBg3pdgHoQfoYBob11Kk1FGa9lui5RIHZCoCkJa9QKlvl3/vKz2usCmYYjs7ymJR/2Nnsqe+Hjt5nw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + '@esbuild/netbsd-x64@0.25.10': resolution: {integrity: sha512-7RTytDPGU6fek/hWuN9qQpeGPBZFfB4zZgcz2VK2Z5VpdUxEI8JKYsg3JfO0n/Z1E/6l05n0unDCNc4HnhQGig==} engines: {node: '>=18'} cpu: [x64] os: [netbsd] + '@esbuild/netbsd-x64@0.28.2': + resolution: {integrity: sha512-lqnzCV+mM0gIADaKihiCg6ifgfU2L3h5E33rNQBN1Y4MaVGnzryzmvvf7UHxprpQdE8hpqLolJ9Rl+SkIRDpyw==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + '@esbuild/openbsd-arm64@0.25.10': resolution: {integrity: sha512-5Se0VM9Wtq797YFn+dLimf2Zx6McttsH2olUBsDml+lm0GOCRVebRWUvDtkY4BWYv/3NgzS8b/UM3jQNh5hYyw==} engines: {node: '>=18'} cpu: [arm64] os: [openbsd] + '@esbuild/openbsd-arm64@0.28.2': + resolution: {integrity: sha512-AL2qJILH7lNjrDmCQDvdxMfAUIv8KMNZOvrwAQ8i8//ntL9FflhOyMJ8OZSMBb8/AWXe3/5v5S20y3zCoZWKoQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + '@esbuild/openbsd-x64@0.25.10': resolution: {integrity: sha512-XkA4frq1TLj4bEMB+2HnI0+4RnjbuGZfet2gs/LNs5Hc7D89ZQBHQ0gL2ND6Lzu1+QVkjp3x1gIcPKzRNP8bXw==} engines: {node: '>=18'} cpu: [x64] os: [openbsd] + '@esbuild/openbsd-x64@0.28.2': + resolution: {integrity: sha512-QtiuPytchRyC4rwUKhexJdQKvDuZ6hWloi3igqPQNUJCS1/v9EiO3UTOXR6A3FoMo4fnAKbWJdqaIwhOzh8qEw==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + '@esbuild/openharmony-arm64@0.25.10': resolution: {integrity: sha512-AVTSBhTX8Y/Fz6OmIVBip9tJzZEUcY8WLh7I59+upa5/GPhh2/aM6bvOMQySspnCCHvFi79kMtdJS1w0DXAeag==} engines: {node: '>=18'} cpu: [arm64] os: [openharmony] + '@esbuild/openharmony-arm64@0.28.2': + resolution: {integrity: sha512-WkhYDmpTjLvGlScA1rwjRUmhl4k8oXR3cIbtqWmELgU/dFeHHlEllxDvdWcNJV9rbzCexB5vz8gtNewWLgCT7Q==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openharmony] + '@esbuild/sunos-x64@0.25.10': resolution: {integrity: sha512-fswk3XT0Uf2pGJmOpDB7yknqhVkJQkAQOcW/ccVOtfx05LkbWOaRAtn5SaqXypeKQra1QaEa841PgrSL9ubSPQ==} engines: {node: '>=18'} cpu: [x64] os: [sunos] + '@esbuild/sunos-x64@0.28.2': + resolution: {integrity: sha512-GPMSkTOtMnv2U2F8gxe4Io6qmVs+YKyp832Etqqxr0hFngmXQ3rzwytelm3GIn7T4VviRUlf3sOgBOiTdvaf7g==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + '@esbuild/win32-arm64@0.25.10': resolution: {integrity: sha512-ah+9b59KDTSfpaCg6VdJoOQvKjI33nTaQr4UluQwW7aEwZQsbMCfTmfEO4VyewOxx4RaDT/xCy9ra2GPWmO7Kw==} engines: {node: '>=18'} cpu: [arm64] os: [win32] + '@esbuild/win32-arm64@0.28.2': + resolution: {integrity: sha512-PIhhEkE9uPBleRBrQEJpUn7MBnibZzbGzYWPmY3x+YoVg/95zbjB4CxPPOQ8l5tYYM4mMaCthF8/1DIfBQQyWQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + '@esbuild/win32-ia32@0.25.10': resolution: {integrity: sha512-QHPDbKkrGO8/cz9LKVnJU22HOi4pxZnZhhA2HYHez5Pz4JeffhDjf85E57Oyco163GnzNCVkZK0b/n4Y0UHcSw==} engines: {node: '>=18'} cpu: [ia32] os: [win32] + '@esbuild/win32-ia32@0.28.2': + resolution: {integrity: sha512-YmJbfTlvU7Sdn9BB+4PRES4oB6pxgS37MAONj+hBr/cpXS1aBPKXxNnDbu+QCWPj0o9dgyxeq79g6c5P8KeuYA==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + '@esbuild/win32-x64@0.25.10': resolution: {integrity: sha512-9KpxSVFCu0iK1owoez6aC/s/EdUQLDN3adTxGCqxMVhrPDj6bt5dbrHDXUuq+Bs2vATFBBrQS5vdQ/Ed2P+nbw==} engines: {node: '>=18'} cpu: [x64] os: [win32] + '@esbuild/win32-x64@0.28.2': + resolution: {integrity: sha512-5ebpxr3nWMzrL/rnUI755Jkuee0bHL/Gq0WTF9lvcpv73wAp5eu8MfBUgWK9bhWvZjj7yX8etf/8tI8Ney695g==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + '@eslint-community/eslint-utils@4.9.0': resolution: {integrity: sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -2429,6 +2614,12 @@ packages: '@hapi/topo@5.1.0': resolution: {integrity: sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg==} + '@hono/node-server@2.1.1': + resolution: {integrity: sha512-ELuehkj5VCBdgEw9zs+ivkKwyzzUCSQuE96YmiPvn1ECBoZCczbFXJLeEGMTYjphP6gydh4pHMqEYPVMYUVgQg==} + engines: {node: '>=20'} + peerDependencies: + hono: ^4 + '@humanfs/core@0.19.1': resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==} engines: {node: '>=18.18.0'} @@ -2713,6 +2904,16 @@ packages: engines: {node: '>=18'} hasBin: true + '@modelcontextprotocol/sdk@1.30.0': + resolution: {integrity: sha512-xKd8OIzlqNzcqcNumGAa6g+PW2kjD5vrpcKOnfldAUPP3j7lnqMPwlTXQm8gF+UwH72z0lqaRbjr9hqGz0eITA==} + engines: {node: '>=18'} + peerDependencies: + '@cfworker/json-schema': ^4.1.1 + zod: ^3.25 || ^4.0 + peerDependenciesMeta: + '@cfworker/json-schema': + optional: true + '@napi-rs/wasm-runtime@1.1.1': resolution: {integrity: sha512-p64ah1M1ld8xjWv3qbvFwHiFVWrq1yFvV4f7w+mzaqiR4IlSgkqhcRdHwsGgomwzBH51sRY4NEowLxnaBjcW/A==} @@ -3803,9 +4004,9 @@ packages: resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} engines: {node: '>=14'} - '@playwright/test@1.61.0': - resolution: {integrity: sha512-cKA5B6lpFEMyMGjxF54QihfYpB4FkEGH+qZhtArDEG+wezQAJY8Pq6C7T1SjWz+FFzt3TbyoXBQYk/0292TdJA==} - engines: {node: '>=18'} + '@playwright/test@1.63.0': + resolution: {integrity: sha512-oxMK4vllB9RK5NQ2l1pq1IfOf2AvnEuj/vYGDj0H2nMtmtZpKtCwt/l00GEO6xjGfpBNAvjovvYdCm50dRQkpQ==} + engines: {node: '>=20'} hasBin: true '@pnpm/config.env-replace@1.1.0': @@ -5628,6 +5829,9 @@ packages: '@vitest/expect@3.2.4': resolution: {integrity: sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig==} + '@vitest/expect@5.0.1': + resolution: {integrity: sha512-U4YclNr7ds8kqZdtLcoVCYTaLRmaKeyjRjQHQ2We5iY0ZjegavIeBWJOr2F4pgUFuN0tO0bz/cVa4ERwKhf1Hg==} + '@vitest/mocker@3.2.4': resolution: {integrity: sha512-46ryTE9RZO/rfDd7pEqFl7etuyzekzEhUbTW3BvmeO/BcCMEgq59BKhek3dXDWgAj4oMK6OZi+vRr1wPW6qjEQ==} peerDependencies: @@ -5642,6 +5846,9 @@ packages: '@vitest/pretty-format@3.2.4': resolution: {integrity: sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA==} + '@vitest/pretty-format@5.0.1': + resolution: {integrity: sha512-6guWwj5d9bguuefTOvJoq387tfpkzSv554YdUEGzjJH2PnnmvzTLQ1UQSuAk5wBFVhf2CUmy/S/palOcb6dmpA==} + '@vitest/runner@3.2.4': resolution: {integrity: sha512-oukfKT9Mk41LreEW09vt45f8wx7DordoWUZMYdY/cyAk7w5TWkTRCNZYF7sX7n2wB7jyGAl74OxgwhPgKaqDMQ==} @@ -5651,9 +5858,15 @@ packages: '@vitest/spy@3.2.4': resolution: {integrity: sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw==} + '@vitest/spy@5.0.1': + resolution: {integrity: sha512-rbto/mF/SGERxEgYOek7Xm6B9b+y+mVoo+f4b2LymYO8zM1b7uB5nHuhVMTP2hxdzgxvGiZYGxGIaMvL5y180Q==} + '@vitest/utils@3.2.4': resolution: {integrity: sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA==} + '@vitest/utils@5.0.1': + resolution: {integrity: sha512-E9+yEA+jsfaoxZcUHFzEqUrQcoNh2EwrPT5efIqkUPUwD5Ua2Li9BRWaYeRwvzvdLgTSVrre8oKNeyrfg7KkdQ==} + '@webassemblyjs/ast@1.14.1': resolution: {integrity: sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==} @@ -5720,6 +5933,10 @@ packages: resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==} engines: {node: '>= 0.6'} + accepts@2.0.0: + resolution: {integrity: sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==} + engines: {node: '>= 0.6'} + acorn-import-attributes@1.9.5: resolution: {integrity: sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==} peerDependencies: @@ -6227,6 +6444,10 @@ packages: resolution: {integrity: sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw==} engines: {node: '>=18'} + chai@6.2.2: + resolution: {integrity: sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==} + engines: {node: '>=18'} + chalk-template@0.4.0: resolution: {integrity: sha512-/ghrgmhfY8RaSdeo43hNXxpoHAtxdbskUHjPpfqUWGttFgycUhYPGx3YZBCnUCvOa7Doivn1IZec3DEGFoMgLg==} engines: {node: '>=12'} @@ -6440,6 +6661,10 @@ packages: resolution: {integrity: sha512-/rFeCpNJQbhSZjGVwO9RFV3xPqbnERS8MmIQzCtD/zl6gpJuV/bMLuN92oG3F7d8oDEHHRrujSXNUr8fpjntKw==} engines: {node: '>=18'} + commander@15.0.0: + resolution: {integrity: sha512-z67u4ZhzCL/Tydu1lJARtEZYWbWaN7oYLHbsuzocr6y4N6WZAagG3RQ4FW61V1/0+jImpj293XfrcYnd1qxtPg==} + engines: {node: '>=22.12.0'} + commander@2.20.3: resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} @@ -6524,6 +6749,10 @@ packages: resolution: {integrity: sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==} engines: {node: '>= 0.6'} + content-disposition@1.1.0: + resolution: {integrity: sha512-5jRCH9Z/+DRP7rkvY83B+yGIGX96OYdJmzngqnw2SBSxqCFPd0w2km3s5iawpGX8krnwSGmF0FW5Nhr0Hfai3g==} + engines: {node: '>=18'} + content-type@1.0.5: resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==} engines: {node: '>= 0.6'} @@ -6579,6 +6808,10 @@ packages: cookie-signature@1.0.7: resolution: {integrity: sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA==} + cookie-signature@1.2.2: + resolution: {integrity: sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==} + engines: {node: '>=6.6.0'} + cookie@0.7.2: resolution: {integrity: sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==} engines: {node: '>= 0.6'} @@ -6601,6 +6834,10 @@ packages: core-util-is@1.0.3: resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} + cors@2.8.6: + resolution: {integrity: sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw==} + engines: {node: '>= 0.10'} + cose-base@1.0.3: resolution: {integrity: sha512-s9whTXInMSgAp/NVXVNuVxVKzGH2qck3aQlVHxDCdAEPgtMKwc4Wq6/QKhgdEdgbLSi9rBTAcPoRa6JpiG4ksg==} @@ -7282,6 +7519,25 @@ packages: duplexer@0.1.2: resolution: {integrity: sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==} + e2e@0.15.0-canary-20260925150007: + resolution: {integrity: sha512-vMlKmliBKdHDfSJMXWIHT6+Z49FEuDmDdDrFxzamp03ue1+QJkksox+fM/7YT1uMvK1oN/cCaKDgonidp8tMWA==} + engines: {node: '>=22.12.0'} + hasBin: true + peerDependencies: + '@ai-sdk/openai': ^4.0.0 + '@ai-sdk/openai-compatible': ^3.0.0 + '@ai-sdk/xai': ^5.0.0 + ai: ^7.0.0 + peerDependenciesMeta: + '@ai-sdk/openai': + optional: true + '@ai-sdk/openai-compatible': + optional: true + '@ai-sdk/xai': + optional: true + ai: + optional: true + eastasianwidth@0.2.0: resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} @@ -7420,6 +7676,11 @@ packages: engines: {node: '>=18'} hasBin: true + esbuild@0.28.2: + resolution: {integrity: sha512-HKVLS8dvII+xoKW9kmqxbRKrnWEXfJJr/FZhhJmiqIB0e053QNYFqOBouTMO/k5sID4MvCiUCvv8b9M4h32wIA==} + engines: {node: '>=18'} + hasBin: true + escalade@3.2.0: resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} engines: {node: '>=6'} @@ -7572,6 +7833,10 @@ packages: resolution: {integrity: sha512-Vo1ab+QXPzZ4tCa8SwIHJFaSzy4R6SHf7BY79rFBDf0idraZWAkYrDjDj8uWaSm3S2TK+hJ7/t1CEmZ7jXw+pg==} engines: {node: '>=18.0.0'} + eventsource@3.0.7: + resolution: {integrity: sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA==} + engines: {node: '>=18.0.0'} + execa@5.1.1: resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==} engines: {node: '>=10'} @@ -7591,10 +7856,20 @@ packages: exponential-backoff@3.1.3: resolution: {integrity: sha512-ZgEeZXj30q+I0EN+CbSSpIyPaJ5HVQD18Z1m+u1FXbAeT94mr1zw50q4q6jiiC447Nl/YTcIYSAftiGqetwXCA==} + express-rate-limit@8.7.0: + resolution: {integrity: sha512-hOwV7WOxXfjRpAM1DSJWZDXx3GhplwD8IfwuwvogD8i1Qnkgosw/H45s4ZnFAUHDAhPjlY9hLBvJhKmGMyY26g==} + engines: {node: '>= 16'} + peerDependencies: + express: '>= 4.11' + express@4.22.2: resolution: {integrity: sha512-IuL+Elrou2ZvCFHs18/CIzy2Nzvo25nZ1/D2eIZlz7c+QUayAcYoiM2BthCjs+EBHVpjYjcuLDAiCWgeIX3X1Q==} engines: {node: '>= 0.10.0'} + express@5.2.1: + resolution: {integrity: sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==} + engines: {node: '>= 18'} + exsolve@1.0.8: resolution: {integrity: sha512-LmDxfWXwcTArk8fUEnOfSZpHOJ6zOMUJKOtFLFqJLoKJetuQG874Uc7/Kki7zFLzYybmZhp1M7+98pfMqeX8yA==} @@ -7627,9 +7902,18 @@ packages: fast-safe-stringify@2.1.1: resolution: {integrity: sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==} + fast-string-truncated-width@3.0.3: + resolution: {integrity: sha512-0jjjIEL6+0jag3l2XWWizO64/aZVtpiGE3t0Zgqxv0DPuxiMjvB3M24fCyhZUO4KomJQPj3LTSUnDP3GpdwC0g==} + + fast-string-width@3.0.2: + resolution: {integrity: sha512-gX8LrtNEI5hq8DVUfRQMbr5lpaS4nMIWV+7XEbXk2b8kiQIizgnlr12B4dA3ZEx3308ze0O4Q1R+cHts8kyUJg==} + fast-uri@3.1.0: resolution: {integrity: sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==} + fast-wrap-ansi@0.2.2: + resolution: {integrity: sha512-7F2Fl+TjRSenLqlU3UjSH0iyqopqoZIu7eZVpEirP2g1GtWa2G/ecEmBdgz31+Mxr+ELclgg6sokpSFIQiZ02Q==} + fastq@1.19.1: resolution: {integrity: sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==} @@ -7692,6 +7976,10 @@ packages: resolution: {integrity: sha512-aA4RyPcd3badbdABGDuTXCMTtOneUCAYH/gxoYRTZlIJdF0YPWuGqiAsIrhNnnqdXGswYk6dGujem4w80UJFhg==} engines: {node: '>= 0.8'} + finalhandler@2.1.1: + resolution: {integrity: sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==} + engines: {node: '>= 18.0.0'} + find-cache-dir@4.0.0: resolution: {integrity: sha512-9ZonPT4ZAK4a+1pUPVPZJapbi7O5qbbJPdYw/NOQWZZbVLdDTYM3A4R9z/DpAM08IDaFGsvPgiGZ82WEwUDWjg==} engines: {node: '>=14.16'} @@ -7790,6 +8078,10 @@ packages: resolution: {integrity: sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==} engines: {node: '>= 0.6'} + fresh@2.0.0: + resolution: {integrity: sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==} + engines: {node: '>= 0.8'} + fs-constants@1.0.0: resolution: {integrity: sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==} @@ -7801,11 +8093,6 @@ packages: resolution: {integrity: sha512-XUBA9XClHbnJWSfBzjkm6RvPsyg3sryZt06BEQoXcF7EK/xpGaQYJgQKDJSUH5SGZ76Y7pFx1QBnXz09rU5Fbw==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - fsevents@2.3.2: - resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==} - engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} - os: [darwin] - fsevents@2.3.3: resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} @@ -8145,6 +8432,10 @@ packages: hoist-non-react-statics@3.3.2: resolution: {integrity: sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==} + hono@4.13.10: + resolution: {integrity: sha512-dQuLsa5oO+47QVMVMaaD9cIv8ctmVtK1iRvwWngkfloFJMeFeuoUFDswIqZGxmGX3hrRzREgEArjkK9OgsQEhA==} + engines: {node: '>=16.9.0'} + hookable@6.0.1: resolution: {integrity: sha512-uKGyY8BuzN/a5gvzvA+3FVWo0+wUjgtfSdnmjtrOVwQCZPHpHDH2WRO3VZSOeluYrHoDCiXFffZXs8Dj1ULWtw==} @@ -8407,6 +8698,10 @@ packages: resolution: {integrity: sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q==} engines: {node: '>= 12'} + ip-address@10.7.2: + resolution: {integrity: sha512-7H/2gFSIitxc0hG3nOI1glS8QLo/EHBFFLk8vEUjXY/xu0AdL8jZ9U1IzO2PUm0d2D/ofQcAifb0g6OBkt8U7w==} + engines: {node: '>= 12'} + ipaddr.js@1.9.1: resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==} engines: {node: '>= 0.10'} @@ -8543,6 +8838,9 @@ packages: is-potential-custom-element-name@1.0.1: resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==} + is-promise@4.0.0: + resolution: {integrity: sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==} + is-regexp@1.0.0: resolution: {integrity: sha512-7zjFAPO4/gwyQAAgRRmqeEeyIICSdmCqa3tsVHMdBzaXXRiqopZL4Cyghg/XulGWrtABTpbnYYzzIRffLkP4oA==} engines: {node: '>=0.10.0'} @@ -8726,6 +9024,9 @@ packages: json-schema-traverse@1.0.0: resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} + json-schema-typed@8.0.2: + resolution: {integrity: sha512-fQhoXdcvc3V28x7C7BMs4P5+kNlgUURe2jmUT1T//oBRMDrqy1QPelJimwZGo7Hg9VPV3EQV5Bnq4hbFy2vetA==} + json-schema@0.4.0: resolution: {integrity: sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==} @@ -9185,6 +9486,10 @@ packages: merge-descriptors@1.0.3: resolution: {integrity: sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==} + merge-descriptors@2.0.0: + resolution: {integrity: sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==} + engines: {node: '>=18'} + merge-stream@2.0.0: resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} @@ -9956,6 +10261,9 @@ packages: path-to-regexp@3.3.0: resolution: {integrity: sha512-qyCH421YQPS2WFDxDjftfc1ZR5WKQzVzqsp4n9M2kQhVOo/ByahFoUNJfl58kOcEGfQ//7weFTDhm+ss8Ecxgw==} + path-to-regexp@8.4.2: + resolution: {integrity: sha512-qRcuIdP69NPm4qbACK+aDogI5CBDMi1jKe0ry5rSQJz8JVLsC7jV8XpiJjGRLLol3N+R5ihGYcrPLTno6pAdBA==} + path-type@4.0.0: resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} engines: {node: '>=8'} @@ -10020,6 +10328,10 @@ packages: engines: {node: '>=0.10'} hasBin: true + pkce-challenge@5.0.1: + resolution: {integrity: sha512-wQ0b/W4Fr01qtpHlqSqspcj3EhBvimsdh0KlHhH8HRZnMsEa0ea2fTULOXOS9ccQr3om+GcGRk4e+isrZWV8qQ==} + engines: {node: '>=16.20.0'} + pkg-dir@7.0.0: resolution: {integrity: sha512-Ie9z/WINcxxLp27BKOCHGde4ITq9UklYKDzVo1nhk5sqGEXU3FpkwP5GM2voTGJkGd9B3Otl+Q4uwSOeSUtOBA==} engines: {node: '>=14.16'} @@ -10030,16 +10342,20 @@ packages: pkg-types@2.3.0: resolution: {integrity: sha512-SIqCzDRg0s9npO5XQ3tNZioRY1uK06lA41ynBC1YmFTmnY6FjUjVt6s4LoADmwoig1qqD0oK8h1p/8mlMx8Oig==} - playwright-core@1.61.0: - resolution: {integrity: sha512-caX7TrY3Ml6egyDX0WUcTHDxodl/b51y5wJOdCEA36QviK/s2g081hvmGs8eaE3DWb6NYZQ6BjO/QkNRPenoPA==} - engines: {node: '>=18'} + playwright-core@1.63.0: + resolution: {integrity: sha512-rYCsBF/M5HjUch52bbtVONEFjv6Xu8sm8h72dNlR5bzIE1fvC/bxgspzkjSfU+MweEMmPM8KJebG6nnyxo5mCg==} + engines: {node: '>=20'} hasBin: true - playwright@1.61.0: - resolution: {integrity: sha512-Z+7BeeqQPRRzklHsVFP4KTGIyMxKUmfeRA4WisM6G3/XW6nwGeX6fX9qYaDa+CiUqpOkb2f6X3nar05R3kSuJQ==} - engines: {node: '>=18'} + playwright@1.63.0: + resolution: {integrity: sha512-+7ziBLidS4NaNCdt57SUDT+wYmmd5fmiQejUic/kb+YsYSCPyOOE9sebzMjNmQrsnNpDJqd4WHvV/8lfKfUDUg==} + engines: {node: '>=20'} hasBin: true + pngjs@7.0.0: + resolution: {integrity: sha512-LKWqWJRhstyYo9pGvgor/ivk2w94eSjE3RGVuzLGlr3NmD8bf7RcYGze1mNdEHRP6TRP6rMuDHk5t44hnTRyow==} + engines: {node: '>=14.19.0'} + points-on-curve@0.2.0: resolution: {integrity: sha512-0mYKnYYe9ZcqMCWhUjItv/oHjvgEsfKvnUTg8sAtnHr3GVy7rGkXCb6d5cSyqrWqL4k81b9CPg3urd+T7aop3A==} @@ -10988,6 +11304,10 @@ packages: roughjs@4.6.6: resolution: {integrity: sha512-ZUz/69+SYpFN/g/lUlo2FXcIjRkSu3nDarreVdGGndHEBJ6cXPdKguS8JGxwj5HA5xIbVKSmLgr5b3AWxtRfvQ==} + router@2.2.0: + resolution: {integrity: sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==} + engines: {node: '>= 18'} + rrweb-cssom@0.8.0: resolution: {integrity: sha512-guoltQEx+9aMf2gDZ0s62EcV8lsXR+0w8915TC3ITdn2YueuNjdAYh/levpU9nFaoChh9RUS5ZdQMrKfVEN9tw==} @@ -11087,6 +11407,10 @@ packages: resolution: {integrity: sha512-p4rRk4f23ynFEfcD9LA0xRYngj+IyGiEYyqqOak8kaN0TvNmuxC2dcVeBn62GpCeR2CpWqyHCNScTP91QbAVFg==} engines: {node: '>= 0.8.0'} + send@1.2.1: + resolution: {integrity: sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==} + engines: {node: '>= 18'} + sequelize-pool@7.1.0: resolution: {integrity: sha512-G9c0qlIWQSK29pR/5U2JF5dDQeqqHRragoyahj/Nx4KOOQ3CPPfzxnfqFPCSB7x5UgjOgnZ61nSxz+fjDpRlJg==} engines: {node: '>= 10.0.0'} @@ -11138,6 +11462,10 @@ packages: resolution: {integrity: sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==} engines: {node: '>= 0.8.0'} + serve-static@2.2.1: + resolution: {integrity: sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==} + engines: {node: '>= 18'} + set-function-length@1.2.2: resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==} engines: {node: '>= 0.4'} @@ -11609,6 +11937,10 @@ packages: resolution: {integrity: sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==} engines: {node: '>=14.0.0'} + tinyrainbow@3.1.1: + resolution: {integrity: sha512-yau8yJdTt989Mm0Bd/236QnzEiPf2xLLTqUZRUJOo/3CB078LSwzei343DgtJVmfJKJE3TMINY1u42SQsP6mXw==} + engines: {node: '>=14.0.0'} + tinyspy@4.0.4: resolution: {integrity: sha512-azl+t0z7pw/z958Gy9svOTuzqIk6xq+NSheJzn5MMWtWTFywIacg2wUlzKFGtt3cthx0r2SxMK0yzJOR0IES7Q==} engines: {node: '>=14.0.0'} @@ -11736,6 +12068,11 @@ packages: engines: {node: '>=18.0.0'} hasBin: true + tsx@4.23.14: + resolution: {integrity: sha512-yFwMnbAsUFz/T3kR8P2ENc/DDUaYd9tKg4oVYo0lhkX0LlK4UuqYAO6mpQ2y6lmcyip1uPJ34C2kPACopDwG4w==} + engines: {node: '>=18.0.0'} + hasBin: true + tunnel-agent@0.6.0: resolution: {integrity: sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==} @@ -12567,6 +12904,11 @@ packages: peerDependencies: zod: ^3.25 || ^4 + zod-to-json-schema@3.25.2: + resolution: {integrity: sha512-O/PgfnpT1xKSDeQYSCfRI5Gy3hPf91mKVDuYLUHZJMiDFptvP41MSnWofm8dnCm0256ZNfZIM7DSzuSMAFnjHA==} + peerDependencies: + zod: ^3.25.28 || ^4 + zod-validation-error@4.0.2: resolution: {integrity: sha512-Q6/nZLe6jxuU80qb/4uJ4t5v2VEZ44lzQjPDhYJNztRQ4wyWc6VF3D3Kb/fAuPetZQnhS3hnajCf9CsWesghLQ==} engines: {node: '>=18.0.0'} @@ -12579,6 +12921,9 @@ packages: zod@4.3.6: resolution: {integrity: sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==} + zod@4.6.1: + resolution: {integrity: sha512-341aRWQsve0rvronKNTqZpjmzdbUDlFuzHaI/XLg/Ej82qffDJRRfBTCuv7+9q/rMjB6LSLyEBnW4InJeMtt/Q==} + zrender@6.0.0: resolution: {integrity: sha512-41dFXEEXuJpNecuUQq6JlbybmnHaqqpGlbH1yxnA5V9MMP4SbohSVZsJIwz+zdjQXSSlR1Vc34EgH1zxyTDvhg==} @@ -12608,6 +12953,10 @@ snapshots: dependencies: json-schema: 0.4.0 + '@ai-sdk/provider@4.0.17': + dependencies: + json-schema: 0.4.0 + '@ai-sdk/react@2.0.115(react@19.2.0)(zod@4.3.6)': dependencies: '@ai-sdk/provider-utils': 3.0.19(zod@4.3.6) @@ -13734,12 +14083,24 @@ snapshots: picocolors: 1.1.1 sisteransi: 1.0.5 + '@clack/core@1.5.1': + dependencies: + fast-wrap-ansi: 0.2.2 + sisteransi: 1.0.5 + '@clack/prompts@1.0.1': dependencies: '@clack/core': 1.0.1 picocolors: 1.1.1 sisteransi: 1.0.5 + '@clack/prompts@1.8.1': + dependencies: + '@clack/core': 1.5.1 + fast-string-width: 3.0.2 + fast-wrap-ansi: 0.2.2 + sisteransi: 1.0.5 + '@colors/colors@1.5.0': optional: true @@ -15057,6 +15418,11 @@ snapshots: - uglify-js - webpack-cli + '@e2edev/web@0.11.0-canary-20260925150007(e2e@0.15.0-canary-20260925150007)(playwright@1.63.0)': + dependencies: + e2e: 0.15.0-canary-20260925150007 + playwright: 1.63.0 + '@emnapi/core@1.8.1': dependencies: '@emnapi/wasi-threads': 1.1.0 @@ -15076,81 +15442,159 @@ snapshots: '@esbuild/aix-ppc64@0.25.10': optional: true + '@esbuild/aix-ppc64@0.28.2': + optional: true + '@esbuild/android-arm64@0.25.10': optional: true + '@esbuild/android-arm64@0.28.2': + optional: true + '@esbuild/android-arm@0.25.10': optional: true + '@esbuild/android-arm@0.28.2': + optional: true + '@esbuild/android-x64@0.25.10': optional: true + '@esbuild/android-x64@0.28.2': + optional: true + '@esbuild/darwin-arm64@0.25.10': optional: true + '@esbuild/darwin-arm64@0.28.2': + optional: true + '@esbuild/darwin-x64@0.25.10': optional: true + '@esbuild/darwin-x64@0.28.2': + optional: true + '@esbuild/freebsd-arm64@0.25.10': optional: true + '@esbuild/freebsd-arm64@0.28.2': + optional: true + '@esbuild/freebsd-x64@0.25.10': optional: true + '@esbuild/freebsd-x64@0.28.2': + optional: true + '@esbuild/linux-arm64@0.25.10': optional: true + '@esbuild/linux-arm64@0.28.2': + optional: true + '@esbuild/linux-arm@0.25.10': optional: true + '@esbuild/linux-arm@0.28.2': + optional: true + '@esbuild/linux-ia32@0.25.10': optional: true + '@esbuild/linux-ia32@0.28.2': + optional: true + '@esbuild/linux-loong64@0.25.10': optional: true + '@esbuild/linux-loong64@0.28.2': + optional: true + '@esbuild/linux-mips64el@0.25.10': optional: true + '@esbuild/linux-mips64el@0.28.2': + optional: true + '@esbuild/linux-ppc64@0.25.10': optional: true + '@esbuild/linux-ppc64@0.28.2': + optional: true + '@esbuild/linux-riscv64@0.25.10': optional: true + '@esbuild/linux-riscv64@0.28.2': + optional: true + '@esbuild/linux-s390x@0.25.10': optional: true + '@esbuild/linux-s390x@0.28.2': + optional: true + '@esbuild/linux-x64@0.25.10': optional: true + '@esbuild/linux-x64@0.28.2': + optional: true + '@esbuild/netbsd-arm64@0.25.10': optional: true + '@esbuild/netbsd-arm64@0.28.2': + optional: true + '@esbuild/netbsd-x64@0.25.10': optional: true + '@esbuild/netbsd-x64@0.28.2': + optional: true + '@esbuild/openbsd-arm64@0.25.10': optional: true + '@esbuild/openbsd-arm64@0.28.2': + optional: true + '@esbuild/openbsd-x64@0.25.10': optional: true + '@esbuild/openbsd-x64@0.28.2': + optional: true + '@esbuild/openharmony-arm64@0.25.10': optional: true + '@esbuild/openharmony-arm64@0.28.2': + optional: true + '@esbuild/sunos-x64@0.25.10': optional: true + '@esbuild/sunos-x64@0.28.2': + optional: true + '@esbuild/win32-arm64@0.25.10': optional: true + '@esbuild/win32-arm64@0.28.2': + optional: true + '@esbuild/win32-ia32@0.25.10': optional: true + '@esbuild/win32-ia32@0.28.2': + optional: true + '@esbuild/win32-x64@0.25.10': optional: true + '@esbuild/win32-x64@0.28.2': + optional: true + '@eslint-community/eslint-utils@4.9.0(eslint@9.39.1(jiti@2.6.1))': dependencies: eslint: 9.39.1(jiti@2.6.1) @@ -15242,6 +15686,10 @@ snapshots: dependencies: '@hapi/hoek': 9.3.0 + '@hono/node-server@2.1.1(hono@4.13.10)': + dependencies: + hono: 4.13.10 + '@humanfs/core@0.19.1': {} '@humanfs/node@0.16.7': @@ -15594,6 +16042,28 @@ snapshots: - supports-color - utf-8-validate + '@modelcontextprotocol/sdk@1.30.0(zod@4.6.1)': + dependencies: + '@hono/node-server': 2.1.1(hono@4.13.10) + ajv: 8.18.0 + ajv-formats: 3.0.1(ajv@8.18.0) + content-type: 1.0.5 + cors: 2.8.6 + cross-spawn: 7.0.6 + eventsource: 3.0.7 + eventsource-parser: 3.0.6 + express: 5.2.1 + express-rate-limit: 8.7.0(express@5.2.1) + hono: 4.13.10 + jose: 6.2.10 + json-schema-typed: 8.0.2 + pkce-challenge: 5.0.1 + raw-body: 3.0.2 + zod: 4.6.1 + zod-to-json-schema: 3.25.2(zod@4.6.1) + transitivePeerDependencies: + - supports-color + '@napi-rs/wasm-runtime@1.1.1': dependencies: '@emnapi/core': 1.8.1 @@ -16829,9 +17299,9 @@ snapshots: '@pkgjs/parseargs@0.11.0': optional: true - '@playwright/test@1.61.0': + '@playwright/test@1.63.0': dependencies: - playwright: 1.61.0 + playwright: 1.63.0 '@pnpm/config.env-replace@1.1.0': {} @@ -18601,7 +19071,7 @@ snapshots: transitivePeerDependencies: - supports-color - '@vitejs/plugin-react@5.1.1(rolldown-vite@7.1.14(@types/node@25.2.3)(esbuild@0.25.10)(jiti@2.6.1)(terser@5.44.1)(tsx@4.20.6)(yaml@2.8.2))': + '@vitejs/plugin-react@5.1.1(rolldown-vite@7.1.14(@types/node@25.2.3)(esbuild@0.25.10)(jiti@2.6.1)(terser@5.44.1)(tsx@4.23.14)(yaml@2.8.2))': dependencies: '@babel/core': 7.28.5 '@babel/plugin-transform-react-jsx-self': 7.27.1(@babel/core@7.28.5) @@ -18609,11 +19079,11 @@ snapshots: '@rolldown/pluginutils': 1.0.0-beta.47 '@types/babel__core': 7.20.5 react-refresh: 0.18.0 - vite: rolldown-vite@7.1.14(@types/node@25.2.3)(esbuild@0.25.10)(jiti@2.6.1)(terser@5.44.1)(tsx@4.20.6)(yaml@2.8.2) + vite: rolldown-vite@7.1.14(@types/node@25.2.3)(esbuild@0.25.10)(jiti@2.6.1)(terser@5.44.1)(tsx@4.23.14)(yaml@2.8.2) transitivePeerDependencies: - supports-color - '@vitejs/plugin-react@5.1.1(vite@7.2.4(@types/node@24.10.1)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(tsx@4.20.6)(yaml@2.8.2))': + '@vitejs/plugin-react@5.1.1(vite@7.2.4(@types/node@24.10.1)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(tsx@4.23.14)(yaml@2.8.2))': dependencies: '@babel/core': 7.28.5 '@babel/plugin-transform-react-jsx-self': 7.27.1(@babel/core@7.28.5) @@ -18621,7 +19091,7 @@ snapshots: '@rolldown/pluginutils': 1.0.0-beta.47 '@types/babel__core': 7.20.5 react-refresh: 0.18.0 - vite: 7.2.4(@types/node@24.10.1)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(tsx@4.20.6)(yaml@2.8.2) + vite: 7.2.4(@types/node@24.10.1)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(tsx@4.23.14)(yaml@2.8.2) transitivePeerDependencies: - supports-color @@ -18649,6 +19119,15 @@ snapshots: chai: 5.3.3 tinyrainbow: 2.0.0 + '@vitest/expect@5.0.1': + dependencies: + '@standard-schema/spec': 1.1.0 + '@types/chai': 5.2.2 + '@vitest/spy': 5.0.1 + '@vitest/utils': 5.0.1 + chai: 6.2.2 + tinyrainbow: 3.1.1 + '@vitest/mocker@3.2.4(vite@7.2.4(@types/node@24.7.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(tsx@4.20.6)(yaml@2.8.2))': dependencies: '@vitest/spy': 3.2.4 @@ -18657,18 +19136,22 @@ snapshots: optionalDependencies: vite: 7.2.4(@types/node@24.7.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(tsx@4.20.6)(yaml@2.8.2) - '@vitest/mocker@3.2.4(vite@7.2.4(@types/node@25.2.3)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(tsx@4.20.6)(yaml@2.8.2))': + '@vitest/mocker@3.2.4(vite@7.2.4(@types/node@25.2.3)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(tsx@4.23.14)(yaml@2.8.2))': dependencies: '@vitest/spy': 3.2.4 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: - vite: 7.2.4(@types/node@25.2.3)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(tsx@4.20.6)(yaml@2.8.2) + vite: 7.2.4(@types/node@25.2.3)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(tsx@4.23.14)(yaml@2.8.2) '@vitest/pretty-format@3.2.4': dependencies: tinyrainbow: 2.0.0 + '@vitest/pretty-format@5.0.1': + dependencies: + tinyrainbow: 3.1.1 + '@vitest/runner@3.2.4': dependencies: '@vitest/utils': 3.2.4 @@ -18685,12 +19168,20 @@ snapshots: dependencies: tinyspy: 4.0.4 + '@vitest/spy@5.0.1': {} + '@vitest/utils@3.2.4': dependencies: '@vitest/pretty-format': 3.2.4 loupe: 3.2.1 tinyrainbow: 2.0.0 + '@vitest/utils@5.0.1': + dependencies: + '@vitest/pretty-format': 5.0.1 + convert-source-map: 2.0.0 + tinyrainbow: 3.1.1 + '@webassemblyjs/ast@1.14.1': dependencies: '@webassemblyjs/helper-numbers': 1.13.2 @@ -18786,6 +19277,11 @@ snapshots: mime-types: 2.1.35 negotiator: 0.6.3 + accepts@2.0.0: + dependencies: + mime-types: 3.0.2 + negotiator: 1.0.0 + acorn-import-attributes@1.9.5(acorn@8.15.0): dependencies: acorn: 8.15.0 @@ -19178,7 +19674,6 @@ snapshots: type-is: 2.0.1 transitivePeerDependencies: - supports-color - optional: true bonjour-service@1.3.0: dependencies: @@ -19371,6 +19866,8 @@ snapshots: loupe: 3.2.1 pathval: 2.0.1 + chai@6.2.2: {} + chalk-template@0.4.0: dependencies: chalk: 4.1.2 @@ -19590,6 +20087,8 @@ snapshots: commander@13.1.0: {} + commander@15.0.0: {} + commander@2.20.3: {} commander@5.1.0: {} @@ -19688,6 +20187,8 @@ snapshots: dependencies: safe-buffer: 5.2.1 + content-disposition@1.1.0: {} + content-type@1.0.5: {} conventional-changelog-angular@7.0.0: @@ -19749,6 +20250,8 @@ snapshots: cookie-signature@1.0.7: {} + cookie-signature@1.2.2: {} + cookie@0.7.2: {} copy-webpack-plugin@11.0.0(webpack@5.103.0(esbuild@0.25.10)): @@ -19771,6 +20274,11 @@ snapshots: core-util-is@1.0.3: {} + cors@2.8.6: + dependencies: + object-assign: 4.1.1 + vary: 1.1.2 + cose-base@1.0.3: dependencies: layout-base: 1.0.2 @@ -20391,6 +20899,21 @@ snapshots: duplexer@0.1.2: {} + e2e@0.15.0-canary-20260925150007: + dependencies: + '@ai-sdk/provider': 4.0.17 + '@clack/prompts': 1.8.1 + '@modelcontextprotocol/sdk': 1.30.0(zod@4.6.1) + '@vitest/expect': 5.0.1 + commander: 15.0.0 + picocolors: 1.1.1 + pngjs: 7.0.0 + tsx: 4.23.14 + zod: 4.6.1 + transitivePeerDependencies: + - '@cfworker/json-schema' + - supports-color + eastasianwidth@0.2.0: {} ecdsa-sig-formatter@1.0.11: @@ -20539,6 +21062,35 @@ snapshots: '@esbuild/win32-ia32': 0.25.10 '@esbuild/win32-x64': 0.25.10 + esbuild@0.28.2: + optionalDependencies: + '@esbuild/aix-ppc64': 0.28.2 + '@esbuild/android-arm': 0.28.2 + '@esbuild/android-arm64': 0.28.2 + '@esbuild/android-x64': 0.28.2 + '@esbuild/darwin-arm64': 0.28.2 + '@esbuild/darwin-x64': 0.28.2 + '@esbuild/freebsd-arm64': 0.28.2 + '@esbuild/freebsd-x64': 0.28.2 + '@esbuild/linux-arm': 0.28.2 + '@esbuild/linux-arm64': 0.28.2 + '@esbuild/linux-ia32': 0.28.2 + '@esbuild/linux-loong64': 0.28.2 + '@esbuild/linux-mips64el': 0.28.2 + '@esbuild/linux-ppc64': 0.28.2 + '@esbuild/linux-riscv64': 0.28.2 + '@esbuild/linux-s390x': 0.28.2 + '@esbuild/linux-x64': 0.28.2 + '@esbuild/netbsd-arm64': 0.28.2 + '@esbuild/netbsd-x64': 0.28.2 + '@esbuild/openbsd-arm64': 0.28.2 + '@esbuild/openbsd-x64': 0.28.2 + '@esbuild/openharmony-arm64': 0.28.2 + '@esbuild/sunos-x64': 0.28.2 + '@esbuild/win32-arm64': 0.28.2 + '@esbuild/win32-ia32': 0.28.2 + '@esbuild/win32-x64': 0.28.2 + escalade@3.2.0: {} escape-goat@4.0.0: {} @@ -20714,6 +21266,10 @@ snapshots: eventsource-parser@3.0.6: {} + eventsource@3.0.7: + dependencies: + eventsource-parser: 3.0.6 + execa@5.1.1: dependencies: cross-spawn: 7.0.6 @@ -20746,6 +21302,14 @@ snapshots: exponential-backoff@3.1.3: optional: true + express-rate-limit@8.7.0(express@5.2.1): + dependencies: + debug: 4.4.3 + express: 5.2.1 + ip-address: 10.7.2 + transitivePeerDependencies: + - supports-color + express@4.22.2: dependencies: accepts: 1.3.8 @@ -20782,6 +21346,39 @@ snapshots: transitivePeerDependencies: - supports-color + express@5.2.1: + dependencies: + accepts: 2.0.0 + body-parser: 2.2.2 + content-disposition: 1.1.0 + content-type: 1.0.5 + cookie: 0.7.2 + cookie-signature: 1.2.2 + debug: 4.4.3 + depd: 2.0.0 + encodeurl: 2.0.0 + escape-html: 1.0.3 + etag: 1.8.1 + finalhandler: 2.1.1 + fresh: 2.0.0 + http-errors: 2.0.1 + merge-descriptors: 2.0.0 + mime-types: 3.0.2 + on-finished: 2.4.1 + once: 1.4.0 + parseurl: 1.3.3 + proxy-addr: 2.0.7 + qs: 6.15.2 + range-parser: 1.2.1 + router: 2.2.0 + send: 1.2.1 + serve-static: 2.2.1 + statuses: 2.0.2 + type-is: 2.0.1 + vary: 1.1.2 + transitivePeerDependencies: + - supports-color + exsolve@1.0.8: {} extend-shallow@2.0.1: @@ -20811,8 +21408,18 @@ snapshots: fast-safe-stringify@2.1.1: {} + fast-string-truncated-width@3.0.3: {} + + fast-string-width@3.0.2: + dependencies: + fast-string-truncated-width: 3.0.3 + fast-uri@3.1.0: {} + fast-wrap-ansi@0.2.2: + dependencies: + fast-string-width: 3.0.2 + fastq@1.19.1: dependencies: reusify: 1.1.0 @@ -20889,6 +21496,17 @@ snapshots: transitivePeerDependencies: - supports-color + finalhandler@2.1.1: + dependencies: + debug: 4.4.3 + encodeurl: 2.0.0 + escape-html: 1.0.3 + on-finished: 2.4.1 + parseurl: 1.3.3 + statuses: 2.0.2 + transitivePeerDependencies: + - supports-color + find-cache-dir@4.0.0: dependencies: common-path-prefix: 3.0.0 @@ -20967,6 +21585,8 @@ snapshots: fresh@0.5.2: {} + fresh@2.0.0: {} + fs-constants@1.0.0: optional: true @@ -20981,9 +21601,6 @@ snapshots: minipass: 7.1.2 optional: true - fsevents@2.3.2: - optional: true - fsevents@2.3.3: optional: true @@ -21547,6 +22164,8 @@ snapshots: dependencies: react-is: 16.13.1 + hono@4.13.10: {} + hookable@6.0.1: {} hosted-git-info@8.1.0: @@ -21810,6 +22429,8 @@ snapshots: ip-address@10.1.0: {} + ip-address@10.7.2: {} + ipaddr.js@1.9.1: {} ipaddr.js@2.3.0: {} @@ -21900,6 +22521,8 @@ snapshots: is-potential-custom-element-name@1.0.1: {} + is-promise@4.0.0: {} + is-regexp@1.0.0: {} is-ssh@1.4.1: @@ -22030,8 +22653,7 @@ snapshots: '@sideway/formula': 3.0.1 '@sideway/pinpoint': 2.0.0 - jose@6.2.10: - optional: true + jose@6.2.10: {} js-levenshtein@1.1.6: {} @@ -22097,6 +22719,8 @@ snapshots: json-schema-traverse@1.0.0: {} + json-schema-typed@8.0.2: {} + json-schema@0.4.0: {} json-stable-stringify-without-jsonify@1.0.1: {} @@ -22649,8 +23273,7 @@ snapshots: media-typer@0.3.0: {} - media-typer@1.1.0: - optional: true + media-typer@1.1.0: {} memfs@4.51.1: dependencies: @@ -22667,6 +23290,8 @@ snapshots: merge-descriptors@1.0.3: {} + merge-descriptors@2.0.0: {} + merge-stream@2.0.0: {} merge2@1.4.1: {} @@ -23170,8 +23795,7 @@ snapshots: negotiator@0.6.4: {} - negotiator@1.0.0: - optional: true + negotiator@1.0.0: {} neo-async@2.6.2: {} @@ -23354,7 +23978,6 @@ snapshots: once@1.4.0: dependencies: wrappy: 1.0.2 - optional: true onetime@5.1.2: dependencies: @@ -23660,6 +24283,8 @@ snapshots: path-to-regexp@3.3.0: {} + path-to-regexp@8.4.2: {} + path-type@4.0.0: {} pathe@2.0.3: {} @@ -23711,6 +24336,8 @@ snapshots: pidtree@0.6.0: {} + pkce-challenge@5.0.1: {} + pkg-dir@7.0.0: dependencies: find-up: 6.3.0 @@ -23727,13 +24354,13 @@ snapshots: exsolve: 1.0.8 pathe: 2.0.3 - playwright-core@1.61.0: {} + playwright-core@1.63.0: {} - playwright@1.61.0: + playwright@1.63.0: dependencies: - playwright-core: 1.61.0 - optionalDependencies: - fsevents: 2.3.2 + playwright-core: 1.63.0 + + pngjs@7.0.0: {} points-on-curve@0.2.0: {} @@ -24351,7 +24978,6 @@ snapshots: http-errors: 2.0.1 iconv-lite: 0.7.2 unpipe: 1.0.0 - optional: true rc9@2.1.2: dependencies: @@ -24809,7 +25435,7 @@ snapshots: tsx: 4.20.6 yaml: 2.8.2 - rolldown-vite@7.1.14(@types/node@25.2.3)(esbuild@0.25.10)(jiti@2.6.1)(terser@5.44.1)(tsx@4.20.6)(yaml@2.8.2): + rolldown-vite@7.1.14(@types/node@25.2.3)(esbuild@0.25.10)(jiti@2.6.1)(terser@5.44.1)(tsx@4.23.14)(yaml@2.8.2): dependencies: '@oxc-project/runtime': 0.92.0 fdir: 6.5.0(picomatch@4.0.3) @@ -24824,7 +25450,7 @@ snapshots: fsevents: 2.3.3 jiti: 2.6.1 terser: 5.44.1 - tsx: 4.20.6 + tsx: 4.23.14 yaml: 2.8.2 rolldown@1.0.0-beta.41: @@ -24921,6 +25547,16 @@ snapshots: points-on-curve: 0.2.0 points-on-path: 0.2.1 + router@2.2.0: + dependencies: + debug: 4.4.3 + depd: 2.0.0 + is-promise: 4.0.0 + parseurl: 1.3.3 + path-to-regexp: 8.4.2 + transitivePeerDependencies: + - supports-color + rrweb-cssom@0.8.0: {} rtlcss@4.3.0: @@ -25037,6 +25673,22 @@ snapshots: transitivePeerDependencies: - supports-color + send@1.2.1: + dependencies: + debug: 4.4.3 + encodeurl: 2.0.0 + escape-html: 1.0.3 + etag: 1.8.1 + fresh: 2.0.0 + http-errors: 2.0.1 + mime-types: 3.0.2 + ms: 2.1.3 + on-finished: 2.4.1 + range-parser: 1.2.1 + statuses: 2.0.2 + transitivePeerDependencies: + - supports-color + sequelize-pool@7.1.0: {} sequelize@6.37.7(pg@8.18.0): @@ -25097,6 +25749,15 @@ snapshots: transitivePeerDependencies: - supports-color + serve-static@2.2.1: + dependencies: + encodeurl: 2.0.0 + escape-html: 1.0.3 + parseurl: 1.3.3 + send: 1.2.1 + transitivePeerDependencies: + - supports-color + set-function-length@1.2.2: dependencies: define-data-property: 1.1.4 @@ -25605,6 +26266,8 @@ snapshots: tinyrainbow@2.0.0: {} + tinyrainbow@3.1.1: {} + tinyspy@4.0.4: {} tldts-core@7.0.17: {} @@ -25711,6 +26374,12 @@ snapshots: optionalDependencies: fsevents: 2.3.3 + tsx@4.23.14: + dependencies: + esbuild: 0.28.2 + optionalDependencies: + fsevents: 2.3.3 + tunnel-agent@0.6.0: dependencies: safe-buffer: 5.2.1 @@ -25767,7 +26436,6 @@ snapshots: content-type: 1.0.5 media-typer: 1.1.0 mime-types: 3.0.2 - optional: true typed-array-buffer@1.0.3: dependencies: @@ -26121,13 +26789,13 @@ snapshots: - tsx - yaml - vite-node@3.2.4(@types/node@25.2.3)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(tsx@4.20.6)(yaml@2.8.2): + vite-node@3.2.4(@types/node@25.2.3)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(tsx@4.23.14)(yaml@2.8.2): dependencies: cac: 6.7.14 debug: 4.4.3 es-module-lexer: 1.7.0 pathe: 2.0.3 - vite: 7.2.4(@types/node@25.2.3)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(tsx@4.20.6)(yaml@2.8.2) + vite: 7.2.4(@types/node@25.2.3)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(tsx@4.23.14)(yaml@2.8.2) transitivePeerDependencies: - '@types/node' - jiti @@ -26153,7 +26821,7 @@ snapshots: - supports-color - typescript - vite@7.2.4(@types/node@24.10.1)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(tsx@4.20.6)(yaml@2.8.2): + vite@7.2.4(@types/node@24.10.1)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(tsx@4.23.14)(yaml@2.8.2): dependencies: esbuild: 0.25.10 fdir: 6.5.0(picomatch@4.0.3) @@ -26167,7 +26835,7 @@ snapshots: jiti: 2.6.1 lightningcss: 1.30.2 terser: 5.44.1 - tsx: 4.20.6 + tsx: 4.23.14 yaml: 2.8.2 vite@7.2.4(@types/node@24.7.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(tsx@4.20.6)(yaml@2.8.2): @@ -26187,7 +26855,7 @@ snapshots: tsx: 4.20.6 yaml: 2.8.2 - vite@7.2.4(@types/node@25.2.3)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(tsx@4.20.6)(yaml@2.8.2): + vite@7.2.4(@types/node@25.2.3)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(tsx@4.23.14)(yaml@2.8.2): dependencies: esbuild: 0.25.10 fdir: 6.5.0(picomatch@4.0.3) @@ -26201,7 +26869,7 @@ snapshots: jiti: 2.6.1 lightningcss: 1.30.2 terser: 5.44.1 - tsx: 4.20.6 + tsx: 4.23.14 yaml: 2.8.2 vitest@3.2.4(@types/debug@4.1.12)(@types/node@24.7.2)(jiti@2.6.1)(jsdom@27.0.0(postcss@8.5.6))(lightningcss@1.30.2)(terser@5.44.1)(tsx@4.20.6)(yaml@2.8.2): @@ -26247,11 +26915,11 @@ snapshots: - tsx - yaml - vitest@3.2.4(@types/debug@4.1.12)(@types/node@25.2.3)(jiti@2.6.1)(jsdom@27.0.0(postcss@8.5.6))(lightningcss@1.30.2)(terser@5.44.1)(tsx@4.20.6)(yaml@2.8.2): + vitest@3.2.4(@types/debug@4.1.12)(@types/node@25.2.3)(jiti@2.6.1)(jsdom@27.0.0(postcss@8.5.6))(lightningcss@1.30.2)(terser@5.44.1)(tsx@4.23.14)(yaml@2.8.2): dependencies: '@types/chai': 5.2.2 '@vitest/expect': 3.2.4 - '@vitest/mocker': 3.2.4(vite@7.2.4(@types/node@25.2.3)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(tsx@4.20.6)(yaml@2.8.2)) + '@vitest/mocker': 3.2.4(vite@7.2.4(@types/node@25.2.3)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(tsx@4.23.14)(yaml@2.8.2)) '@vitest/pretty-format': 3.2.4 '@vitest/runner': 3.2.4 '@vitest/snapshot': 3.2.4 @@ -26269,8 +26937,8 @@ snapshots: tinyglobby: 0.2.15 tinypool: 1.1.1 tinyrainbow: 2.0.0 - vite: 7.2.4(@types/node@25.2.3)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(tsx@4.20.6)(yaml@2.8.2) - vite-node: 3.2.4(@types/node@25.2.3)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(tsx@4.20.6)(yaml@2.8.2) + vite: 7.2.4(@types/node@25.2.3)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(tsx@4.23.14)(yaml@2.8.2) + vite-node: 3.2.4(@types/node@25.2.3)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(tsx@4.23.14)(yaml@2.8.2) why-is-node-running: 2.3.0 optionalDependencies: '@types/debug': 4.1.12 @@ -26556,8 +27224,7 @@ snapshots: string-width: 7.2.0 strip-ansi: 7.1.2 - wrappy@1.0.2: - optional: true + wrappy@1.0.2: {} write-file-atomic@3.0.3: dependencies: @@ -26636,6 +27303,10 @@ snapshots: dependencies: zod: 4.3.6 + zod-to-json-schema@3.25.2(zod@4.6.1): + dependencies: + zod: 4.6.1 + zod-validation-error@4.0.2(zod@4.1.13): dependencies: zod: 4.1.13 @@ -26644,6 +27315,8 @@ snapshots: zod@4.3.6: {} + zod@4.6.1: {} + zrender@6.0.0: dependencies: tslib: 2.3.0