@agent-native/core 0.17.0 → 0.17.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/dist/client/composer/TiptapComposer.d.ts.map +1 -1
  2. package/dist/client/composer/TiptapComposer.js +2 -3
  3. package/dist/client/composer/TiptapComposer.js.map +1 -1
  4. package/dist/client/settings/DemoModeSection.d.ts +7 -6
  5. package/dist/client/settings/DemoModeSection.d.ts.map +1 -1
  6. package/dist/client/settings/DemoModeSection.js +24 -16
  7. package/dist/client/settings/DemoModeSection.js.map +1 -1
  8. package/dist/client/use-chat-threads.d.ts.map +1 -1
  9. package/dist/client/use-chat-threads.js +59 -19
  10. package/dist/client/use-chat-threads.js.map +1 -1
  11. package/dist/client/use-db-sync.d.ts.map +1 -1
  12. package/dist/client/use-db-sync.js +5 -0
  13. package/dist/client/use-db-sync.js.map +1 -1
  14. package/dist/db/client.d.ts +24 -0
  15. package/dist/db/client.d.ts.map +1 -1
  16. package/dist/db/client.js +52 -13
  17. package/dist/db/client.js.map +1 -1
  18. package/dist/db/create-get-db.d.ts.map +1 -1
  19. package/dist/db/create-get-db.js +6 -9
  20. package/dist/db/create-get-db.js.map +1 -1
  21. package/dist/db/index.d.ts.map +1 -1
  22. package/dist/db/index.js +2 -1
  23. package/dist/db/index.js.map +1 -1
  24. package/dist/demo/fetch-interceptor.d.ts +8 -0
  25. package/dist/demo/fetch-interceptor.d.ts.map +1 -0
  26. package/dist/demo/fetch-interceptor.js +174 -0
  27. package/dist/demo/fetch-interceptor.js.map +1 -0
  28. package/dist/demo/redact.d.ts +6 -0
  29. package/dist/demo/redact.d.ts.map +1 -1
  30. package/dist/demo/redact.js +0 -0
  31. package/dist/demo/redact.js.map +1 -1
  32. package/dist/server/action-routes.d.ts.map +1 -1
  33. package/dist/server/action-routes.js +3 -13
  34. package/dist/server/action-routes.js.map +1 -1
  35. package/dist/server/auth.d.ts.map +1 -1
  36. package/dist/server/auth.js +13 -2
  37. package/dist/server/auth.js.map +1 -1
  38. package/dist/server/core-routes-plugin.d.ts.map +1 -1
  39. package/dist/server/core-routes-plugin.js +28 -0
  40. package/dist/server/core-routes-plugin.js.map +1 -1
  41. package/dist/styles/agent-native.css +15 -0
  42. package/dist/vite/client.d.ts.map +1 -1
  43. package/dist/vite/client.js +6 -0
  44. package/dist/vite/client.js.map +1 -1
  45. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/db/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,4BAA4B,CAAC;AACrD,OAAO,QAAQ,MAAM,gBAAgB,CAAC;AAOtC;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,QAAQ,CAAC,MAAgB;IAC7C,IAAI,MAAM,CAAC,MAAM,KAAK,UAAU,EAAE,CAAC;QACjC,MAAM,EAAE,OAAO,EAAE,SAAS,EAAE,GAAG,MAAM,MAAM,CAAC,yBAAyB,CAAC,CAAC;QACvE,MAAM,EAAE,OAAO,EAAE,EAAE,EAAE,GAAG,MAAM,MAAM,CAAC,UAAU,CAAC,CAAC;QACjD,OAAO,SAAS,CAAC,EAAE,CAAC,MAAM,CAAC,gBAAgB,CAAC,CAAC,CAAC;IAChD,CAAC;IACD,IAAI,MAAM,CAAC,MAAM,KAAK,QAAQ,EAAE,CAAC;QAC/B,MAAM,MAAM,GAAG,IAAI,QAAQ,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;QAC7C,MAAM,CAAC,MAAM,CAAC,oBAAoB,CAAC,CAAC;QACpC,OAAO,OAAO,CAAC,MAAM,CAAC,CAAC;IACzB,CAAC;IACD,MAAM,IAAI,KAAK,CAAC,uBAAwB,MAAc,CAAC,MAAM,EAAE,CAAC,CAAC;AACnE,CAAC;AAID,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,EACL,SAAS,EACT,YAAY,EACZ,UAAU,EACV,UAAU,EACV,OAAO,EACP,WAAW,GAIZ,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,aAAa,CAAC","sourcesContent":["import { drizzle } from \"drizzle-orm/better-sqlite3\";\nimport Database from \"better-sqlite3\";\n\nexport type DbConfig =\n | { driver: \"sqlite\"; filename: string }\n | { driver: \"d1\"; binding: any }\n | { driver: \"postgres\"; connectionString: string };\n\n/**\n * Create a Drizzle ORM database instance.\n * Supports SQLite via better-sqlite3 and Postgres via postgres-js.\n * Postgres driver is loaded dynamically to avoid bundling in edge runtimes.\n */\nexport async function createDb(config: DbConfig) {\n if (config.driver === \"postgres\") {\n const { drizzle: drizzlePg } = await import(\"drizzle-orm/postgres-js\");\n const { default: pg } = await import(\"postgres\");\n return drizzlePg(pg(config.connectionString));\n }\n if (config.driver === \"sqlite\") {\n const sqlite = new Database(config.filename);\n sqlite.pragma(\"journal_mode = WAL\");\n return drizzle(sqlite);\n }\n throw new Error(`Unsupported driver: ${(config as any).driver}`);\n}\n\nexport type DrizzleDb = Awaited<ReturnType<typeof createDb>>;\n\nexport { createGetDb } from \"./create-get-db.js\";\nexport { runMigrations } from \"./migrations.js\";\nexport {\n getDbExec,\n createDbExec,\n getDialect,\n isPostgres,\n intType,\n closeDbExec,\n type DbExec,\n type DbExecConfig,\n type Dialect,\n} from \"./client.js\";\nexport { table, text, integer, now } from \"./schema.js\";\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/db/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,4BAA4B,CAAC;AACrD,OAAO,QAAQ,MAAM,gBAAgB,CAAC;AAOtC;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,QAAQ,CAAC,MAAgB;IAC7C,IAAI,MAAM,CAAC,MAAM,KAAK,UAAU,EAAE,CAAC;QACjC,MAAM,EAAE,OAAO,EAAE,SAAS,EAAE,GAAG,MAAM,MAAM,CAAC,yBAAyB,CAAC,CAAC;QACvE,MAAM,EAAE,OAAO,EAAE,EAAE,EAAE,GAAG,MAAM,MAAM,CAAC,UAAU,CAAC,CAAC;QACjD,MAAM,EAAE,aAAa,EAAE,GAAG,MAAM,MAAM,CAAC,aAAa,CAAC,CAAC;QACtD,OAAO,SAAS,CACd,EAAE,CAAC,MAAM,CAAC,gBAAgB,EAAE,aAAa,CAAC,MAAM,CAAC,gBAAgB,CAAC,CAAC,CACpE,CAAC;IACJ,CAAC;IACD,IAAI,MAAM,CAAC,MAAM,KAAK,QAAQ,EAAE,CAAC;QAC/B,MAAM,MAAM,GAAG,IAAI,QAAQ,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;QAC7C,MAAM,CAAC,MAAM,CAAC,oBAAoB,CAAC,CAAC;QACpC,OAAO,OAAO,CAAC,MAAM,CAAC,CAAC;IACzB,CAAC;IACD,MAAM,IAAI,KAAK,CAAC,uBAAwB,MAAc,CAAC,MAAM,EAAE,CAAC,CAAC;AACnE,CAAC;AAID,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,EACL,SAAS,EACT,YAAY,EACZ,UAAU,EACV,UAAU,EACV,OAAO,EACP,WAAW,GAIZ,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,aAAa,CAAC","sourcesContent":["import { drizzle } from \"drizzle-orm/better-sqlite3\";\nimport Database from \"better-sqlite3\";\n\nexport type DbConfig =\n | { driver: \"sqlite\"; filename: string }\n | { driver: \"d1\"; binding: any }\n | { driver: \"postgres\"; connectionString: string };\n\n/**\n * Create a Drizzle ORM database instance.\n * Supports SQLite via better-sqlite3 and Postgres via postgres-js.\n * Postgres driver is loaded dynamically to avoid bundling in edge runtimes.\n */\nexport async function createDb(config: DbConfig) {\n if (config.driver === \"postgres\") {\n const { drizzle: drizzlePg } = await import(\"drizzle-orm/postgres-js\");\n const { default: pg } = await import(\"postgres\");\n const { pgPoolOptions } = await import(\"./client.js\");\n return drizzlePg(\n pg(config.connectionString, pgPoolOptions(config.connectionString)),\n );\n }\n if (config.driver === \"sqlite\") {\n const sqlite = new Database(config.filename);\n sqlite.pragma(\"journal_mode = WAL\");\n return drizzle(sqlite);\n }\n throw new Error(`Unsupported driver: ${(config as any).driver}`);\n}\n\nexport type DrizzleDb = Awaited<ReturnType<typeof createDb>>;\n\nexport { createGetDb } from \"./create-get-db.js\";\nexport { runMigrations } from \"./migrations.js\";\nexport {\n getDbExec,\n createDbExec,\n getDialect,\n isPostgres,\n intType,\n closeDbExec,\n type DbExec,\n type DbExecConfig,\n type Dialect,\n} from \"./client.js\";\nexport { table, text, integer, now } from \"./schema.js\";\n"]}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Install the demo-mode fetch interceptor and start polling demo status.
3
+ * Idempotent and browser-only — safe to call from any hook that runs in
4
+ * every template root (we call it from `useDbSync`). A no-op until demo
5
+ * mode is actually on.
6
+ */
7
+ export declare function ensureDemoModeFetchInterceptor(): void;
8
+ //# sourceMappingURL=fetch-interceptor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fetch-interceptor.d.ts","sourceRoot":"","sources":["../../src/demo/fetch-interceptor.ts"],"names":[],"mappings":"AA2GA;;;;;GAKG;AACH,wBAAgB,8BAA8B,IAAI,IAAI,CAuErD"}
@@ -0,0 +1,174 @@
1
+ /**
2
+ * Client-side demo-mode redaction.
3
+ *
4
+ * Why client-side and not (only) at the server action boundary: templates
5
+ * serve a lot of their UI data through their OWN custom Nitro `/api/*`
6
+ * handlers (e.g. mail's `/api/emails`, `/api/threads/:id/messages`,
7
+ * `/api/contacts`, `/api/apollo/person`), which never pass through the
8
+ * framework action runtime. On this stack (Nitro v3 / h3 v2) there is no
9
+ * single server hook that can safely rewrite every JSON body (the `response`
10
+ * hook hands you an immutable Web `Response` and returns `void`). Patching the
11
+ * browser's `fetch` is the one place that is BOTH universal (every template's
12
+ * reads — actions and custom routes alike — go through it) AND low-risk: it
13
+ * can only ever post-process JSON the app already parses for display, so it
14
+ * physically cannot break auth, SSE streams, SSR HTML, or binary downloads.
15
+ *
16
+ * The agent is handled separately and in-process (its action tool results are
17
+ * redacted in `production-agent.ts`), so it doesn't depend on this at all.
18
+ *
19
+ * Scope intentionally narrow:
20
+ * - Only same-document `GET` requests are redacted. Mutation responses
21
+ * (POST/PUT/PATCH/DELETE) pass through untouched so a draft you just
22
+ * typed isn't echoed back as fake data mid-demo.
23
+ * - Only `application/json` 2xx bodies. Streams (`text/event-stream`),
24
+ * HTML, and binary are skipped by content-type.
25
+ * - Framework infra endpoints (poll, events, the demo-status endpoint
26
+ * itself) are skipped — no PII and avoids self-recursion.
27
+ * - Any error during interception falls back to the original response.
28
+ */
29
+ import { redactDemoData } from "./redact.js";
30
+ import { agentNativePath } from "../client/api-path.js";
31
+ const STATUS_PATH = agentNativePath("/_agent-native/demo/status");
32
+ const SKIP_SUBSTRINGS = [
33
+ "/_agent-native/demo/status",
34
+ "/_agent-native/poll",
35
+ "/_agent-native/events",
36
+ // Never touch agent transport. The agent already gets in-process
37
+ // redaction of its tool results; faking its own transcript adds no demo
38
+ // value and must stay clear of the tool_use/tool_result protocol. Covers
39
+ // "/_agent-native/agent" (stream) and "/_agent-native/agent-chat"
40
+ // (thread history) and any sub-paths.
41
+ "/_agent-native/agent",
42
+ // Run-manager state read by the reconnect/recovery loop. Faking numeric
43
+ // run state here would make recovery think it's not progressing and
44
+ // exhaust its retries ("agent connection kept failing").
45
+ "/_agent-native/runs",
46
+ ];
47
+ let installed = false;
48
+ let pollTimer = null;
49
+ let demoEnabled = false;
50
+ let originalFetch = null;
51
+ // Set once the first demo-status check completes. We DO NOT block requests on
52
+ // it — if status isn't known yet a response is simply passed through
53
+ // un-redacted (a brief first-paint window) rather than delaying transport.
54
+ // Injecting latency into early GETs previously risked the agent's streaming
55
+ // reconnect logic; transport safety wins over redacting the first paint.
56
+ let firstStatusDone = false;
57
+ /** Reject after `ms` so a misclassified streaming body can never hang. */
58
+ function withTimeout(p, ms) {
59
+ return Promise.race([
60
+ p,
61
+ new Promise((_, reject) => setTimeout(() => reject(new Error("redact-timeout")), ms)),
62
+ ]);
63
+ }
64
+ function urlOf(input) {
65
+ try {
66
+ if (typeof input === "string")
67
+ return input;
68
+ if (input instanceof URL)
69
+ return input.href;
70
+ return input.url ?? "";
71
+ }
72
+ catch {
73
+ return "";
74
+ }
75
+ }
76
+ function methodOf(input, init) {
77
+ const m = init?.method ??
78
+ (typeof input !== "string" && !(input instanceof URL)
79
+ ? input.method
80
+ : undefined) ??
81
+ "GET";
82
+ return m.toUpperCase();
83
+ }
84
+ async function refreshDemoFlag() {
85
+ const f = originalFetch ?? fetch;
86
+ try {
87
+ const res = await f(STATUS_PATH, { credentials: "include" });
88
+ if (!res.ok)
89
+ return;
90
+ const json = (await res.json());
91
+ demoEnabled = json?.enabled === true || json?.forced === true;
92
+ }
93
+ catch {
94
+ // Status endpoint unreachable — leave the last known value.
95
+ }
96
+ finally {
97
+ firstStatusDone = true;
98
+ }
99
+ }
100
+ /**
101
+ * Install the demo-mode fetch interceptor and start polling demo status.
102
+ * Idempotent and browser-only — safe to call from any hook that runs in
103
+ * every template root (we call it from `useDbSync`). A no-op until demo
104
+ * mode is actually on.
105
+ */
106
+ export function ensureDemoModeFetchInterceptor() {
107
+ if (typeof window === "undefined")
108
+ return;
109
+ if (installed)
110
+ return;
111
+ installed = true;
112
+ originalFetch = window.fetch.bind(window);
113
+ const base = originalFetch;
114
+ window.fetch = async function patchedFetch(input, init) {
115
+ const res = await base(input, init);
116
+ // Fast path: anything that isn't a demo-enabled, plain GET returns the
117
+ // ORIGINAL response with zero body work and zero extra awaits — when
118
+ // demo mode is off this wrapper is byte-for-byte native fetch, so it
119
+ // cannot influence agent/run/stream transport.
120
+ if (!demoEnabled || !firstStatusDone)
121
+ return res;
122
+ if (methodOf(input, init) !== "GET")
123
+ return res;
124
+ if (!res.ok)
125
+ return res;
126
+ try {
127
+ const url = urlOf(input);
128
+ if (SKIP_SUBSTRINGS.some((s) => url.includes(s)))
129
+ return res;
130
+ // Only buffered, finite JSON. SSE / streaming / chunked-forever bodies
131
+ // never reach `redactDemoData`: streaming content-types are excluded,
132
+ // and the JSON read is hard-timeout-bounded so a misclassified stream
133
+ // degrades to "return the original response" instead of hanging the
134
+ // request (which is what tripped the reconnect/recovery loop).
135
+ const contentType = res.headers.get("content-type") ?? "";
136
+ if (!contentType.includes("application/json"))
137
+ return res;
138
+ if (contentType.includes("event-stream") ||
139
+ contentType.includes("ndjson") ||
140
+ contentType.includes("stream")) {
141
+ return res;
142
+ }
143
+ if (res.bodyUsed)
144
+ return res;
145
+ const data = await withTimeout(res.clone().json(), 3_000);
146
+ const redacted = redactDemoData(data);
147
+ const headers = new Headers(res.headers);
148
+ // Body is re-serialized — these would be wrong now.
149
+ headers.delete("content-length");
150
+ headers.delete("content-encoding");
151
+ return new Response(JSON.stringify(redacted), {
152
+ status: res.status,
153
+ statusText: res.statusText,
154
+ headers,
155
+ });
156
+ }
157
+ catch {
158
+ // Never let redaction break a request — fall back to the real
159
+ // response (its body stream is untouched; we only ever read a clone).
160
+ return res;
161
+ }
162
+ };
163
+ void refreshDemoFlag();
164
+ pollTimer = setInterval(() => void refreshDemoFlag(), 4_000);
165
+ if (typeof pollTimer === "object" && "unref" in pollTimer) {
166
+ try {
167
+ pollTimer.unref();
168
+ }
169
+ catch {
170
+ // unref unavailable in the browser — fine, interval is cheap.
171
+ }
172
+ }
173
+ }
174
+ //# sourceMappingURL=fetch-interceptor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fetch-interceptor.js","sourceRoot":"","sources":["../../src/demo/fetch-interceptor.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Client-side demo-mode redaction.\n *\n * Why client-side and not (only) at the server action boundary: templates\n * serve a lot of their UI data through their OWN custom Nitro `/api/*`\n * handlers (e.g. mail's `/api/emails`, `/api/threads/:id/messages`,\n * `/api/contacts`, `/api/apollo/person`), which never pass through the\n * framework action runtime. On this stack (Nitro v3 / h3 v2) there is no\n * single server hook that can safely rewrite every JSON body (the `response`\n * hook hands you an immutable Web `Response` and returns `void`). Patching the\n * browser's `fetch` is the one place that is BOTH universal (every template's\n * reads — actions and custom routes alike — go through it) AND low-risk: it\n * can only ever post-process JSON the app already parses for display, so it\n * physically cannot break auth, SSE streams, SSR HTML, or binary downloads.\n *\n * The agent is handled separately and in-process (its action tool results are\n * redacted in `production-agent.ts`), so it doesn't depend on this at all.\n *\n * Scope intentionally narrow:\n * - Only same-document `GET` requests are redacted. Mutation responses\n * (POST/PUT/PATCH/DELETE) pass through untouched so a draft you just\n * typed isn't echoed back as fake data mid-demo.\n * - Only `application/json` 2xx bodies. Streams (`text/event-stream`),\n * HTML, and binary are skipped by content-type.\n * - Framework infra endpoints (poll, events, the demo-status endpoint\n * itself) are skipped — no PII and avoids self-recursion.\n * - Any error during interception falls back to the original response.\n */\nimport { redactDemoData } from \"./redact.js\";\nimport { agentNativePath } from \"../client/api-path.js\";\n\nconst STATUS_PATH = agentNativePath(\"/_agent-native/demo/status\");\nconst SKIP_SUBSTRINGS = [\n \"/_agent-native/demo/status\",\n \"/_agent-native/poll\",\n \"/_agent-native/events\",\n // Never touch agent transport. The agent already gets in-process\n // redaction of its tool results; faking its own transcript adds no demo\n // value and must stay clear of the tool_use/tool_result protocol. Covers\n // \"/_agent-native/agent\" (stream) and \"/_agent-native/agent-chat\"\n // (thread history) and any sub-paths.\n \"/_agent-native/agent\",\n // Run-manager state read by the reconnect/recovery loop. Faking numeric\n // run state here would make recovery think it's not progressing and\n // exhaust its retries (\"agent connection kept failing\").\n \"/_agent-native/runs\",\n];\n\nlet installed = false;\nlet pollTimer: ReturnType<typeof setInterval> | null = null;\nlet demoEnabled = false;\nlet originalFetch: typeof fetch | null = null;\n\n// Set once the first demo-status check completes. We DO NOT block requests on\n// it — if status isn't known yet a response is simply passed through\n// un-redacted (a brief first-paint window) rather than delaying transport.\n// Injecting latency into early GETs previously risked the agent's streaming\n// reconnect logic; transport safety wins over redacting the first paint.\nlet firstStatusDone = false;\n\n/** Reject after `ms` so a misclassified streaming body can never hang. */\nfunction withTimeout<T>(p: Promise<T>, ms: number): Promise<T> {\n return Promise.race([\n p,\n new Promise<T>((_, reject) =>\n setTimeout(() => reject(new Error(\"redact-timeout\")), ms),\n ),\n ]);\n}\n\nfunction urlOf(input: RequestInfo | URL): string {\n try {\n if (typeof input === \"string\") return input;\n if (input instanceof URL) return input.href;\n return (input as Request).url ?? \"\";\n } catch {\n return \"\";\n }\n}\n\nfunction methodOf(input: RequestInfo | URL, init?: RequestInit): string {\n const m =\n init?.method ??\n (typeof input !== \"string\" && !(input instanceof URL)\n ? (input as Request).method\n : undefined) ??\n \"GET\";\n return m.toUpperCase();\n}\n\nasync function refreshDemoFlag(): Promise<void> {\n const f = originalFetch ?? fetch;\n try {\n const res = await f(STATUS_PATH, { credentials: \"include\" });\n if (!res.ok) return;\n const json = (await res.json()) as {\n enabled?: boolean;\n forced?: boolean;\n } | null;\n demoEnabled = json?.enabled === true || json?.forced === true;\n } catch {\n // Status endpoint unreachable — leave the last known value.\n } finally {\n firstStatusDone = true;\n }\n}\n\n/**\n * Install the demo-mode fetch interceptor and start polling demo status.\n * Idempotent and browser-only — safe to call from any hook that runs in\n * every template root (we call it from `useDbSync`). A no-op until demo\n * mode is actually on.\n */\nexport function ensureDemoModeFetchInterceptor(): void {\n if (typeof window === \"undefined\") return;\n if (installed) return;\n installed = true;\n\n originalFetch = window.fetch.bind(window);\n const base = originalFetch;\n\n window.fetch = async function patchedFetch(\n input: RequestInfo | URL,\n init?: RequestInit,\n ): Promise<Response> {\n const res = await base(input, init);\n\n // Fast path: anything that isn't a demo-enabled, plain GET returns the\n // ORIGINAL response with zero body work and zero extra awaits — when\n // demo mode is off this wrapper is byte-for-byte native fetch, so it\n // cannot influence agent/run/stream transport.\n if (!demoEnabled || !firstStatusDone) return res;\n if (methodOf(input, init) !== \"GET\") return res;\n if (!res.ok) return res;\n\n try {\n const url = urlOf(input);\n if (SKIP_SUBSTRINGS.some((s) => url.includes(s))) return res;\n\n // Only buffered, finite JSON. SSE / streaming / chunked-forever bodies\n // never reach `redactDemoData`: streaming content-types are excluded,\n // and the JSON read is hard-timeout-bounded so a misclassified stream\n // degrades to \"return the original response\" instead of hanging the\n // request (which is what tripped the reconnect/recovery loop).\n const contentType = res.headers.get(\"content-type\") ?? \"\";\n if (!contentType.includes(\"application/json\")) return res;\n if (\n contentType.includes(\"event-stream\") ||\n contentType.includes(\"ndjson\") ||\n contentType.includes(\"stream\")\n ) {\n return res;\n }\n if (res.bodyUsed) return res;\n\n const data = await withTimeout(res.clone().json(), 3_000);\n const redacted = redactDemoData(data);\n\n const headers = new Headers(res.headers);\n // Body is re-serialized — these would be wrong now.\n headers.delete(\"content-length\");\n headers.delete(\"content-encoding\");\n\n return new Response(JSON.stringify(redacted), {\n status: res.status,\n statusText: res.statusText,\n headers,\n });\n } catch {\n // Never let redaction break a request — fall back to the real\n // response (its body stream is untouched; we only ever read a clone).\n return res;\n }\n };\n\n void refreshDemoFlag();\n pollTimer = setInterval(() => void refreshDemoFlag(), 4_000);\n if (typeof pollTimer === \"object\" && \"unref\" in pollTimer) {\n try {\n (pollTimer as unknown as { unref: () => void }).unref();\n } catch {\n // unref unavailable in the browser — fine, interval is cheap.\n }\n }\n}\n"]}
@@ -14,4 +14,10 @@ export interface RedactOptions {
14
14
  }
15
15
  export declare function redactDemoString(text: string, opts?: RedactOptions): string;
16
16
  export declare function redactDemoData<T>(value: T, opts?: RedactOptions): T;
17
+ /**
18
+ * Clear the stable-mapping caches. Test-only — the caches are process-global
19
+ * (intentionally, so mappings stay stable for a tab's session), which would
20
+ * otherwise let one test's produced fakes leak into another's assertions.
21
+ */
22
+ export declare function __resetDemoRedactCacheForTests(): void;
17
23
  //# sourceMappingURL=redact.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"redact.d.ts","sourceRoot":"","sources":["../../src/demo/redact.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,MAAM,WAAW,aAAa;IAC5B,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAoYD,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,aAAa,GAAG,MAAM,CAU3E;AAuID,wBAAgB,cAAc,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,IAAI,CAAC,EAAE,aAAa,GAAG,CAAC,CAGnE"}
1
+ {"version":3,"file":"redact.d.ts","sourceRoot":"","sources":["../../src/demo/redact.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,MAAM,WAAW,aAAa;IAC5B,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAoiBD,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,aAAa,GAAG,MAAM,CAU3E;AAqJD,wBAAgB,cAAc,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,IAAI,CAAC,EAAE,aAAa,GAAG,CAAC,CAGnE;AAED;;;;GAIG;AACH,wBAAgB,8BAA8B,IAAI,IAAI,CAGrD"}
Binary file
@@ -1 +1 @@
1
- {"version":3,"file":"redact.js","sourceRoot":"","sources":["../../src/demo/redact.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Pure, dependency-free, deterministic demo-mode redactor.\n *\n * Replaces sensitive values (names, emails, free numbers) with stable fake\n * substitutes so a demo looks coherent — the same input always maps to the\n * same fake. Crucially, it NEVER rewrites identifiers, structural tokens, or\n * timestamps: under-redaction is safe, corrupting an ID is not. The string\n * redactor uses a protect-first strategy (mask IDs with opaque placeholders\n * before any transform runs, restore them byte-identical afterwards), and the\n * structure-aware walker additionally protects leaf values by key name.\n */\n\nexport interface RedactOptions {\n salt?: string;\n}\n\n/* ------------------------------------------------------------------ *\n * Seeded hash + PRNG (xmur3 seed → mulberry32 stream)\n * ------------------------------------------------------------------ */\n\nfunction xmur3(str: string): () => number {\n let h = 1779033703 ^ str.length;\n for (let i = 0; i < str.length; i++) {\n h = Math.imul(h ^ str.charCodeAt(i), 3432918353);\n h = (h << 13) | (h >>> 19);\n }\n return function () {\n h = Math.imul(h ^ (h >>> 16), 2246822507);\n h = Math.imul(h ^ (h >>> 13), 3266489909);\n h ^= h >>> 16;\n return h >>> 0;\n };\n}\n\nfunction mulberry32(seed: number): () => number {\n let a = seed >>> 0;\n return function () {\n a = (a + 0x6d2b79f5) >>> 0;\n let t = a;\n t = Math.imul(t ^ (t >>> 15), t | 1);\n t ^= t + Math.imul(t ^ (t >>> 7), t | 61);\n return ((t ^ (t >>> 14)) >>> 0) / 4294967296;\n };\n}\n\n/** Deterministic PRNG seeded by `value + salt`. */\nfunction seededRng(value: string, salt: string): () => number {\n const seedFn = xmur3(`${value}\u0000${salt}`);\n return mulberry32(seedFn());\n}\n\nfunction pick<T>(rng: () => number, pool: readonly T[]): T {\n return pool[Math.floor(rng() * pool.length) % pool.length];\n}\n\n/* ------------------------------------------------------------------ *\n * Curated name pools\n * ------------------------------------------------------------------ */\n\nconst FIRST_NAMES: readonly string[] = [\n \"Jane\",\n \"John\",\n \"Alex\",\n \"Maria\",\n \"Sam\",\n \"Olivia\",\n \"Liam\",\n \"Emma\",\n \"Noah\",\n \"Ava\",\n \"Lucas\",\n \"Mia\",\n \"Ethan\",\n \"Sofia\",\n \"Mason\",\n \"Isla\",\n \"Leo\",\n \"Aria\",\n \"Henry\",\n \"Zoe\",\n \"Owen\",\n \"Nora\",\n \"Caleb\",\n \"Lily\",\n \"Ryan\",\n \"Ruby\",\n \"Adam\",\n \"Chloe\",\n \"Eli\",\n \"Hazel\",\n \"Max\",\n \"Iris\",\n \"Ben\",\n \"Clara\",\n \"Theo\",\n \"Maya\",\n \"Felix\",\n \"Elena\",\n \"Jonah\",\n \"Greta\",\n];\n\nconst LAST_NAMES: readonly string[] = [\n \"Doe\",\n \"Smith\",\n \"Johnson\",\n \"Lee\",\n \"Brown\",\n \"Garcia\",\n \"Miller\",\n \"Davis\",\n \"Wilson\",\n \"Moore\",\n \"Taylor\",\n \"Anderson\",\n \"Thomas\",\n \"Jackson\",\n \"White\",\n \"Harris\",\n \"Martin\",\n \"Clark\",\n \"Lewis\",\n \"Walker\",\n \"Hall\",\n \"Young\",\n \"King\",\n \"Wright\",\n \"Hill\",\n \"Green\",\n \"Adams\",\n \"Baker\",\n \"Nelson\",\n \"Carter\",\n \"Mitchell\",\n \"Perez\",\n \"Roberts\",\n \"Turner\",\n \"Phillips\",\n \"Campbell\",\n \"Parker\",\n \"Evans\",\n \"Edwards\",\n \"Collins\",\n];\n\n/* ------------------------------------------------------------------ *\n * Fake value generators (deterministic in value + salt)\n * ------------------------------------------------------------------ */\n\nfunction fakeName(original: string, salt: string): string {\n const rng = seededRng(`name:${original}`, salt);\n const first = pick(rng, FIRST_NAMES);\n const last = pick(rng, LAST_NAMES);\n return `${first} ${last}`;\n}\n\nfunction fakeEmail(original: string, salt: string): string {\n // Derive the email from the same fake-name space so an address and the\n // person's name stay coherent within the demo.\n const rng = seededRng(`name:${original.toLowerCase()}`, salt);\n const first = pick(rng, FIRST_NAMES);\n const last = pick(rng, LAST_NAMES);\n return `${first.toLowerCase()}.${last.toLowerCase()}@example.com`;\n}\n\n/**\n * Replace every digit in `numericBody` with a freshly-generated digit while\n * preserving non-digit characters (grouping commas, decimal points) exactly.\n * The first digit is forced non-zero so the digit count is observable.\n */\nfunction fakeNumberBody(\n numericBody: string,\n original: string,\n salt: string,\n): string {\n const rng = seededRng(`num:${original}`, salt);\n let out = \"\";\n let seenDigit = false;\n for (const ch of numericBody) {\n if (ch >= \"0\" && ch <= \"9\") {\n let d: number;\n if (!seenDigit) {\n // Leading digit: 1-9 so length is preserved.\n d = 1 + Math.floor(rng() * 9);\n seenDigit = true;\n } else {\n d = Math.floor(rng() * 10);\n }\n out += String(d);\n } else {\n out += ch;\n }\n }\n return out;\n}\n\n/* ------------------------------------------------------------------ *\n * Protect-first tokenizer (ID-safety core)\n * ------------------------------------------------------------------ */\n\n/**\n * Patterns whose matches must be protected from ANY transform. Order matters:\n * the most structural / specific shapes come first so they win the scan.\n */\nconst PROTECT_PATTERNS: RegExp[] = [\n // URLs / URIs with a scheme.\n /\\b[a-zA-Z][a-zA-Z0-9+.-]*:\\/\\/[^\\s]+/g,\n /\\b(?:mailto|data|tel|urn):[^\\s]+/gi,\n // UUID.\n /\\b[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}\\b/g,\n // JWT — three base64url segments separated by dots.\n /\\b[A-Za-z0-9_-]{4,}\\.[A-Za-z0-9_-]{4,}\\.[A-Za-z0-9_-]{4,}\\b/g,\n // ISO datetime / date.\n /\\b\\d{4}-\\d{2}-\\d{2}(?:[T ]\\d{2}:\\d{2}(?::\\d{2})?(?:\\.\\d+)?Z?)?\\b/g,\n // Bare clock time.\n /\\b\\d{1,2}:\\d{2}(?::\\d{2})?\\b/g,\n // Path-like tokens containing a slash (but not bare prose with slashes —\n // require no spaces and at least one slash with adjacent non-space).\n /(?:\\S*\\/\\S+)+/g,\n // nanoid / hex / base64-ish blobs: an unbroken [A-Za-z0-9_-] run that is\n // either long-with-a-digit, mixes letters AND digits at length >= 10, or\n // contains a `_`/`-` inside the run (real names/numbers never look so).\n /[A-Za-z0-9_-]+/g,\n];\n\nconst PLACEHOLDER_PREFIX = \"\u0001P\";\nconst PLACEHOLDER_SUFFIX = \"\u0001\";\n\nfunction makePlaceholder(index: number): string {\n return `${PLACEHOLDER_PREFIX}${index}${PLACEHOLDER_SUFFIX}`;\n}\n\n/** True if a `[A-Za-z0-9_-]+` run is identifier-shaped (must be protected). */\nfunction looksLikeIdentifierToken(tok: string): boolean {\n if (tok.length < 3) return false;\n const hasLetter = /[A-Za-z]/.test(tok);\n const hasDigit = /[0-9]/.test(tok);\n const hasSep = /[_-]/.test(tok);\n\n // A pure number (optionally with separators handled elsewhere) is NOT an\n // identifier here — the number rule handles those.\n if (!hasLetter && !hasSep) return false;\n\n // Long hex/base64-ish blob with a digit.\n if (tok.length >= 16 && hasDigit) return true;\n // nanoid-ish: length >= 10 mixing letters AND digits.\n if (tok.length >= 10 && hasLetter && hasDigit) return true;\n // Any token that has a `_`/`-` joined inside an unbroken run AND also\n // contains a digit or is long — e.g. `order-2024-abc`, `api_key_v2`.\n if (hasSep && (hasDigit || tok.length >= 10)) return true;\n // Mixed letters+digits adjacency (e.g. `abc123`, `v2`, `step3`) — protect so\n // the number rule never bites an embedded number.\n if (hasLetter && hasDigit) return true;\n\n return false;\n}\n\ninterface Protection {\n text: string;\n restore: Map<string, string>;\n}\n\n/**\n * Walk `text`, replace every protected substring with an opaque placeholder,\n * and return the masked text plus a restore map. Non-overlapping, left-to-right\n * earliest-match-wins so a transform literally cannot see a protected value.\n */\nfunction protect(text: string): Protection {\n const restore = new Map<string, string>();\n let counter = 0;\n\n // Collect all candidate matches across patterns, then resolve overlaps by\n // earliest start (and longest on tie).\n interface Span {\n start: number;\n end: number;\n value: string;\n }\n const spans: Span[] = [];\n\n for (let p = 0; p < PROTECT_PATTERNS.length; p++) {\n const re = new RegExp(\n PROTECT_PATTERNS[p].source,\n PROTECT_PATTERNS[p].flags,\n );\n let m: RegExpExecArray | null;\n while ((m = re.exec(text)) !== null) {\n const value = m[0];\n if (value.length === 0) {\n re.lastIndex++;\n continue;\n }\n const isTokenPattern = p === PROTECT_PATTERNS.length - 1;\n if (isTokenPattern && !looksLikeIdentifierToken(value)) {\n continue;\n }\n spans.push({ start: m.index, end: m.index + value.length, value });\n }\n }\n\n if (spans.length === 0) return { text, restore };\n\n spans.sort((a, b) =>\n a.start !== b.start ? a.start - b.start : b.end - a.end,\n );\n\n let out = \"\";\n let cursor = 0;\n for (const span of spans) {\n if (span.start < cursor) continue; // overlapped by an earlier protection\n out += text.slice(cursor, span.start);\n const ph = makePlaceholder(counter++);\n restore.set(ph, span.value);\n out += ph;\n cursor = span.end;\n }\n out += text.slice(cursor);\n\n return { text: out, restore };\n}\n\nfunction unprotect(text: string, restore: Map<string, string>): string {\n if (restore.size === 0) return text;\n let out = text;\n for (const [ph, original] of restore) {\n out = out.split(ph).join(original);\n }\n return out;\n}\n\n/* ------------------------------------------------------------------ *\n * Transforms (run only on protected text)\n * ------------------------------------------------------------------ */\n\nconst EMAIL_RE = /\\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}\\b/g;\n\n// 2+ capitalized words (each starts uppercase, allows internal lowercase /\n// apostrophes / hyphens). Single-letter middle initials allowed: `Sarah J Connor`.\nconst FULL_NAME_RE = /\\b[A-Z][a-zA-Z'’-]*(?:\\s+[A-Z](?:[a-zA-Z'’-]*)?){1,3}\\b/g;\n\n// A standalone numeric token: optional currency + sign, digits with optional\n// comma grouping and a single optional decimal part. Bounded so it is NOT\n// adjacent to a letter (placeholders already removed letter-mixed tokens, but\n// this keeps the rule self-contained and safe on raw structured values too).\nconst NUMBER_RE =\n /(^|[^A-Za-z0-9_])([$€£]?)([+-]?)(\\d[\\d,]*(?:\\.\\d+)?)(?![A-Za-z0-9_])/g;\n\nfunction transformEmails(text: string, salt: string): string {\n return text.replace(EMAIL_RE, (match) => fakeEmail(match, salt));\n}\n\nfunction transformNames(text: string, salt: string): string {\n return text.replace(FULL_NAME_RE, (match) => {\n // Keep trailing/leading whitespace handling implicit; `match` is the run.\n return fakeName(match, salt);\n });\n}\n\nfunction isProbablyYear(digits: string): boolean {\n if (digits.length !== 4) return false;\n const n = Number(digits);\n return n >= 1900 && n <= 2099;\n}\n\nfunction transformNumbers(text: string, salt: string): string {\n return text.replace(\n NUMBER_RE,\n (_full, pre: string, currency: string, sign: string, body: string) => {\n const hasGrouping = body.includes(\",\");\n const hasDecimal = body.includes(\".\");\n const digitsOnly = body.replace(/[^0-9]/g, \"\");\n\n // Skip bare years like 2026.\n if (\n !currency &&\n !sign &&\n !hasGrouping &&\n !hasDecimal &&\n isProbablyYear(digitsOnly)\n ) {\n return `${pre}${currency}${sign}${body}`;\n }\n\n // Skip standalone integers < 1000 with no currency and no grouping\n // (rewriting \"3 unread\" / \"page 2\" looks broken and isn't sensitive).\n if (!currency && !hasGrouping && !hasDecimal) {\n const n = Number(digitsOnly);\n if (Number.isFinite(n) && n < 1000) {\n return `${pre}${currency}${sign}${body}`;\n }\n }\n\n const fakeBody = fakeNumberBody(body, `${currency}${sign}${body}`, salt);\n return `${pre}${currency}${sign}${fakeBody}`;\n },\n );\n}\n\n/* ------------------------------------------------------------------ *\n * Public: string redactor\n * ------------------------------------------------------------------ */\n\nexport function redactDemoString(text: string, opts?: RedactOptions): string {\n if (typeof text !== \"string\" || text.length === 0) return text;\n const salt = opts?.salt ?? \"\";\n\n const { text: masked, restore } = protect(text);\n let out = masked;\n out = transformEmails(out, salt);\n out = transformNames(out, salt);\n out = transformNumbers(out, salt);\n return unprotect(out, restore);\n}\n\n/* ------------------------------------------------------------------ *\n * Public: structure-aware redactor\n * ------------------------------------------------------------------ */\n\n// Keys whose leaf values must NEVER be transformed (still recurse into nested\n// objects/arrays under them).\nconst PROTECTED_KEY_RE =\n /^id$|(^|_)id$|Id$|Ids$|uuid|guid|slug|token|secret|password|passwd|apikey|api_key|hash|sha\\d*|etag|cursor|nonce|sessionid|messageid|threadid|nodeid|(^|_)key$|keyid|(^|_)ref$|url$|uri$|href$|src$|path$|filename$|mimetype|mime|createdat|updatedat|deletedat|expiresat|timestamp|.+at$|.+_at$/i;\n\n// Keys whose (short) leaf string should be treated as a full name even when it\n// is a single token.\nconst NAME_KEY_RE =\n /name|from$|to$|sender|recipient|author|fullName|displayName|firstName|lastName|contact/i;\n\nconst MAX_DEPTH = 64;\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n if (value === null || typeof value !== \"object\") return false;\n if (Array.isArray(value)) return false;\n if (value instanceof Date) return false;\n const proto = Object.getPrototypeOf(value);\n return proto === Object.prototype || proto === null;\n}\n\nfunction redactNameLeaf(value: string, salt: string): string {\n if (value.length === 0 || value.length > 40) {\n // Too long to be a bare name — fall back to prose redaction.\n return redactDemoStringWithSalt(value, salt);\n }\n // If it already contains an email, redact that path instead.\n if (EMAIL_RE.test(value)) {\n EMAIL_RE.lastIndex = 0;\n return redactDemoStringWithSalt(value, salt);\n }\n EMAIL_RE.lastIndex = 0;\n\n const { text: masked, restore } = protect(value);\n // If protection consumed the whole value, it was identifier-shaped — leave.\n if (masked.trim().length === 0 || restore.size > 0) {\n // Be conservative: if any part looked like an ID, prefer prose redaction\n // (which preserves the protected bits) over forcing a name.\n return redactDemoStringWithSalt(value, salt);\n }\n return fakeName(value, salt);\n}\n\nfunction redactDemoStringWithSalt(text: string, salt: string): string {\n return redactDemoString(text, { salt });\n}\n\nfunction redactNumberLeaf(value: number, salt: string): number {\n if (!Number.isFinite(value)) return value;\n const repr = String(value);\n // Re-use the string number transform but only if the representation is a\n // clean numeric token we can round-trip back to a JS number.\n const redacted = transformNumbers(repr, salt);\n if (redacted === repr) return value;\n const n = Number(redacted);\n if (!Number.isFinite(n)) return value;\n return n;\n}\n\nfunction walk(\n value: unknown,\n salt: string,\n depth: number,\n seen: WeakSet<object>,\n underNameKey: boolean,\n): unknown {\n if (depth > MAX_DEPTH) return value;\n\n if (value === null || value === undefined) return value;\n\n const t = typeof value;\n\n if (t === \"string\") {\n const str = value as string;\n if (underNameKey) return redactNameLeaf(str, salt);\n return redactDemoStringWithSalt(str, salt);\n }\n\n if (t === \"number\") {\n return redactNumberLeaf(value as number, salt);\n }\n\n if (t === \"boolean\" || t === \"bigint\" || t === \"function\" || t === \"symbol\") {\n return value;\n }\n\n if (value instanceof Date) return value;\n\n if (Array.isArray(value)) {\n if (seen.has(value)) return value;\n seen.add(value);\n const out = value.map((item) =>\n walk(item, salt, depth + 1, seen, underNameKey),\n );\n seen.delete(value);\n return out;\n }\n\n if (isPlainObject(value)) {\n if (seen.has(value)) return value;\n seen.add(value);\n const out: Record<string, unknown> = {};\n for (const [key, entry] of Object.entries(value)) {\n const keyIsProtected = PROTECTED_KEY_RE.test(key);\n if (keyIsProtected) {\n if (\n entry !== null &&\n typeof entry === \"object\" &&\n !(entry instanceof Date)\n ) {\n // Still recurse into nested structures, but the protected key does\n // not propagate name/transform semantics.\n out[key] = walk(entry, salt, depth + 1, seen, false);\n } else {\n // Leaf under a protected key: pass through completely untouched.\n out[key] = entry;\n }\n continue;\n }\n const keyIsName = NAME_KEY_RE.test(key);\n out[key] = walk(entry, salt, depth + 1, seen, keyIsName);\n }\n seen.delete(value);\n return out;\n }\n\n // Unknown object kind (Map, Set, class instance, etc.) — leave untouched.\n return value;\n}\n\nexport function redactDemoData<T>(value: T, opts?: RedactOptions): T {\n const salt = opts?.salt ?? \"\";\n return walk(value, salt, 0, new WeakSet<object>(), false) as T;\n}\n"]}
1
+ {"version":3,"file":"redact.js","sourceRoot":"","sources":["../../src/demo/redact.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Pure, dependency-free, deterministic demo-mode redactor.\n *\n * Replaces sensitive values (names, emails, free numbers) with stable fake\n * substitutes so a demo looks coherent — the same input always maps to the\n * same fake. Crucially, it NEVER rewrites identifiers, structural tokens, or\n * timestamps: under-redaction is safe, corrupting an ID is not. The string\n * redactor uses a protect-first strategy (mask IDs with opaque placeholders\n * before any transform runs, restore them byte-identical afterwards), and the\n * structure-aware walker additionally protects leaf values by key name.\n */\n\nexport interface RedactOptions {\n salt?: string;\n}\n\n/* ------------------------------------------------------------------ *\n * Seeded hash + PRNG (xmur3 seed → mulberry32 stream)\n * ------------------------------------------------------------------ */\n\nfunction xmur3(str: string): () => number {\n let h = 1779033703 ^ str.length;\n for (let i = 0; i < str.length; i++) {\n h = Math.imul(h ^ str.charCodeAt(i), 3432918353);\n h = (h << 13) | (h >>> 19);\n }\n return function () {\n h = Math.imul(h ^ (h >>> 16), 2246822507);\n h = Math.imul(h ^ (h >>> 13), 3266489909);\n h ^= h >>> 16;\n return h >>> 0;\n };\n}\n\nfunction mulberry32(seed: number): () => number {\n let a = seed >>> 0;\n return function () {\n a = (a + 0x6d2b79f5) >>> 0;\n let t = a;\n t = Math.imul(t ^ (t >>> 15), t | 1);\n t ^= t + Math.imul(t ^ (t >>> 7), t | 61);\n return ((t ^ (t >>> 14)) >>> 0) / 4294967296;\n };\n}\n\n/** Deterministic PRNG seeded by `value + salt`. */\nfunction seededRng(value: string, salt: string): () => number {\n const seedFn = xmur3(`${value}\u0000${salt}`);\n return mulberry32(seedFn());\n}\n\nfunction pick<T>(rng: () => number, pool: readonly T[]): T {\n return pool[Math.floor(rng() * pool.length) % pool.length];\n}\n\n/* ------------------------------------------------------------------ *\n * Stable mapping cache (bounded, TTL, leak-free)\n *\n * The fake values are already a pure deterministic function of\n * (kind, salt, original), so identical input is always stable. This cache\n * adds two things on top:\n *\n * 1. A consistent forward map so the same original keeps the same fake for\n * the life of the entry, even if the algorithm/salt is ever tuned.\n * 2. Idempotency: every fake we emit is remembered, so when a fake value\n * round-trips back through redaction (e.g. you edit a draft that's\n * already showing fake text, it autosaves, then refetches) it is passed\n * through UNCHANGED instead of being re-faked into something new. This\n * is what stops names/emails drifting on every edit.\n *\n * Leak-free by construction: no timers, a hard size cap, and lazy TTL purge\n * on write. Works the same per-tab in the browser and per-process on the\n * server. Memoizing a pure function across users is safe — output depends\n * only on input.\n * ------------------------------------------------------------------ */\n\nconst CACHE_TTL_MS = 60 * 60 * 1000; // 1 hour\nconst CACHE_MAX = 5000;\n\n// original-key → { fake, at }. Insertion-ordered (Map) so the oldest key is\n// first — used for cap eviction.\nconst forwardCache = new Map<string, { value: string; at: number }>();\n// produced fake → last-seen timestamp (same bound/TTL policy).\nconst producedFakes = new Map<string, number>();\n\nfunction purge(map: Map<string, { at: number } | number>): void {\n const now = Date.now();\n for (const [k, v] of map) {\n const at = typeof v === \"number\" ? v : v.at;\n if (now - at > CACHE_TTL_MS) map.delete(k);\n else break; // insertion-ordered: first fresh entry ⇒ rest are fresher\n }\n while (map.size > CACHE_MAX) {\n const oldest = map.keys().next().value as string | undefined;\n if (oldest === undefined) break;\n map.delete(oldest);\n }\n}\n\nfunction rememberProduced(fake: string): string {\n if (fake.length === 0) return fake;\n producedFakes.delete(fake);\n producedFakes.set(fake, Date.now());\n if (producedFakes.size > CACHE_MAX) purge(producedFakes);\n return fake;\n}\n\nfunction isProducedFake(value: string): boolean {\n const at = producedFakes.get(value);\n if (at === undefined) return false;\n if (Date.now() - at > CACHE_TTL_MS) {\n producedFakes.delete(value);\n return false;\n }\n return true;\n}\n\n/**\n * Memoize a deterministic fake by (kind, salt, original). On a hit, the\n * entry's recency is refreshed (LRU-ish). The generated value is also\n * registered as a produced fake so it survives a round-trip unchanged.\n */\nfunction memoFake(\n kind: string,\n original: string,\n salt: string,\n gen: () => string,\n // Numbers opt out: a fake number collides with real numbers far too often\n // to safely treat \"looks like one we emitted\" as \"leave it alone\".\n idempotent = true,\n): string {\n // Already one of our fakes? Leave it exactly as-is (round-trip stable).\n if (idempotent && isProducedFake(original)) return original;\n\n const key = `${kind}\u0000${salt}\u0000${original}`;\n const hit = forwardCache.get(key);\n const now = Date.now();\n if (hit && now - hit.at <= CACHE_TTL_MS) {\n forwardCache.delete(key);\n forwardCache.set(key, { value: hit.value, at: now });\n return hit.value;\n }\n\n const value = gen();\n forwardCache.set(key, { value, at: now });\n if (forwardCache.size > CACHE_MAX) purge(forwardCache);\n if (idempotent) rememberProduced(value);\n return value;\n}\n\n/* ------------------------------------------------------------------ *\n * Curated name pools\n * ------------------------------------------------------------------ */\n\nconst FIRST_NAMES: readonly string[] = [\n \"Jane\",\n \"John\",\n \"Alex\",\n \"Maria\",\n \"Sam\",\n \"Olivia\",\n \"Liam\",\n \"Emma\",\n \"Noah\",\n \"Ava\",\n \"Lucas\",\n \"Mia\",\n \"Ethan\",\n \"Sofia\",\n \"Mason\",\n \"Isla\",\n \"Leo\",\n \"Aria\",\n \"Henry\",\n \"Zoe\",\n \"Owen\",\n \"Nora\",\n \"Caleb\",\n \"Lily\",\n \"Ryan\",\n \"Ruby\",\n \"Adam\",\n \"Chloe\",\n \"Eli\",\n \"Hazel\",\n \"Max\",\n \"Iris\",\n \"Ben\",\n \"Clara\",\n \"Theo\",\n \"Maya\",\n \"Felix\",\n \"Elena\",\n \"Jonah\",\n \"Greta\",\n];\n\nconst LAST_NAMES: readonly string[] = [\n \"Doe\",\n \"Smith\",\n \"Johnson\",\n \"Lee\",\n \"Brown\",\n \"Garcia\",\n \"Miller\",\n \"Davis\",\n \"Wilson\",\n \"Moore\",\n \"Taylor\",\n \"Anderson\",\n \"Thomas\",\n \"Jackson\",\n \"White\",\n \"Harris\",\n \"Martin\",\n \"Clark\",\n \"Lewis\",\n \"Walker\",\n \"Hall\",\n \"Young\",\n \"King\",\n \"Wright\",\n \"Hill\",\n \"Green\",\n \"Adams\",\n \"Baker\",\n \"Nelson\",\n \"Carter\",\n \"Mitchell\",\n \"Perez\",\n \"Roberts\",\n \"Turner\",\n \"Phillips\",\n \"Campbell\",\n \"Parker\",\n \"Evans\",\n \"Edwards\",\n \"Collins\",\n];\n\n/* ------------------------------------------------------------------ *\n * Fake value generators (deterministic in value + salt)\n * ------------------------------------------------------------------ */\n\nfunction fakeName(original: string, salt: string): string {\n return memoFake(\"name\", original, salt, () => {\n const rng = seededRng(`name:${original}`, salt);\n const first = pick(rng, FIRST_NAMES);\n const last = pick(rng, LAST_NAMES);\n return `${first} ${last}`;\n });\n}\n\n// Realistic-looking but safe stand-in domains: real consumer providers (the\n// local part is fake, so no real mailbox is implied) plus the canonical\n// reserved fictional-company domains (Contoso/Fabrikam/Northwind), which read\n// as real businesses but are intentional placeholders. No example.com.\nconst FAKE_EMAIL_DOMAINS: readonly string[] = [\n \"gmail.com\",\n \"outlook.com\",\n \"icloud.com\",\n \"yahoo.com\",\n \"proton.me\",\n \"hey.com\",\n \"fastmail.com\",\n \"contoso.com\",\n \"fabrikam.com\",\n \"northwind.com\",\n \"acme.io\",\n \"globex.net\",\n];\n\nfunction fakeEmail(original: string, salt: string): string {\n return memoFake(\"email\", original.toLowerCase(), salt, () => {\n const rng = seededRng(`email:${original.toLowerCase()}`, salt);\n const first = pick(rng, FIRST_NAMES).toLowerCase();\n const last = pick(rng, LAST_NAMES).toLowerCase();\n const domain = pick(rng, FAKE_EMAIL_DOMAINS);\n // Vary the local part so addresses don't all look templated.\n const shape = Math.floor(rng() * 6);\n let local: string;\n switch (shape) {\n case 0:\n local = `${first}.${last}`;\n break;\n case 1:\n local = `${first}${last}`;\n break;\n case 2:\n local = `${first[0]}${last}`;\n break;\n case 3:\n local = `${first}.${last}${1 + Math.floor(rng() * 89)}`;\n break;\n case 4:\n local = `${first}_${last}`;\n break;\n default:\n local = `${last}.${first}`;\n break;\n }\n return `${local}@${domain}`;\n });\n}\n\n/**\n * Replace every digit in `numericBody` with a freshly-generated digit while\n * preserving non-digit characters (grouping commas, decimal points) exactly.\n * The first digit is forced non-zero so the digit count is observable.\n */\nfunction fakeNumberBody(\n numericBody: string,\n original: string,\n salt: string,\n): string {\n // Stable per original token, but NOT registered as a produced fake — a\n // fake number coincides with real numbers too often to skip on round-trip.\n return memoFake(\n \"num\",\n original,\n salt,\n () => {\n const rng = seededRng(`num:${original}`, salt);\n let out = \"\";\n let seenDigit = false;\n for (const ch of numericBody) {\n if (ch >= \"0\" && ch <= \"9\") {\n let d: number;\n if (!seenDigit) {\n // Leading digit: 1-9 so length is preserved.\n d = 1 + Math.floor(rng() * 9);\n seenDigit = true;\n } else {\n d = Math.floor(rng() * 10);\n }\n out += String(d);\n } else {\n out += ch;\n }\n }\n return out;\n },\n false,\n );\n}\n\n/* ------------------------------------------------------------------ *\n * Protect-first tokenizer (ID-safety core)\n * ------------------------------------------------------------------ */\n\n/**\n * Patterns whose matches must be protected from ANY transform. Order matters:\n * the most structural / specific shapes come first so they win the scan.\n */\nconst PROTECT_PATTERNS: RegExp[] = [\n // URLs / URIs with a scheme.\n /\\b[a-zA-Z][a-zA-Z0-9+.-]*:\\/\\/[^\\s]+/g,\n /\\b(?:mailto|data|tel|urn):[^\\s]+/gi,\n // UUID.\n /\\b[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}\\b/g,\n // JWT — three base64url segments separated by dots.\n /\\b[A-Za-z0-9_-]{4,}\\.[A-Za-z0-9_-]{4,}\\.[A-Za-z0-9_-]{4,}\\b/g,\n // ISO datetime / date.\n /\\b\\d{4}-\\d{2}-\\d{2}(?:[T ]\\d{2}:\\d{2}(?::\\d{2})?(?:\\.\\d+)?Z?)?\\b/g,\n // Bare clock time.\n /\\b\\d{1,2}:\\d{2}(?::\\d{2})?\\b/g,\n // Path-like tokens containing a slash (but not bare prose with slashes —\n // require no spaces and at least one slash with adjacent non-space).\n /(?:\\S*\\/\\S+)+/g,\n // nanoid / hex / base64-ish blobs: an unbroken [A-Za-z0-9_-] run that is\n // either long-with-a-digit, mixes letters AND digits at length >= 10, or\n // contains a `_`/`-` inside the run (real names/numbers never look so).\n /[A-Za-z0-9_-]+/g,\n];\n\nconst PLACEHOLDER_PREFIX = \"\u0001P\";\nconst PLACEHOLDER_SUFFIX = \"\u0001\";\n\nfunction makePlaceholder(index: number): string {\n return `${PLACEHOLDER_PREFIX}${index}${PLACEHOLDER_SUFFIX}`;\n}\n\n/** True if a `[A-Za-z0-9_-]+` run is identifier-shaped (must be protected). */\nfunction looksLikeIdentifierToken(tok: string): boolean {\n if (tok.length < 3) return false;\n const hasLetter = /[A-Za-z]/.test(tok);\n const hasDigit = /[0-9]/.test(tok);\n const hasSep = /[_-]/.test(tok);\n\n // A pure number (optionally with separators handled elsewhere) is NOT an\n // identifier here — the number rule handles those.\n if (!hasLetter && !hasSep) return false;\n\n // Long hex/base64-ish blob with a digit.\n if (tok.length >= 16 && hasDigit) return true;\n // nanoid-ish: length >= 10 mixing letters AND digits.\n if (tok.length >= 10 && hasLetter && hasDigit) return true;\n // Any token that has a `_`/`-` joined inside an unbroken run AND also\n // contains a digit or is long — e.g. `order-2024-abc`, `api_key_v2`.\n if (hasSep && (hasDigit || tok.length >= 10)) return true;\n // Mixed letters+digits adjacency (e.g. `abc123`, `v2`, `step3`) — protect so\n // the number rule never bites an embedded number.\n if (hasLetter && hasDigit) return true;\n\n return false;\n}\n\ninterface Protection {\n text: string;\n restore: Map<string, string>;\n}\n\n/**\n * Walk `text`, replace every protected substring with an opaque placeholder,\n * and return the masked text plus a restore map. Non-overlapping, left-to-right\n * earliest-match-wins so a transform literally cannot see a protected value.\n */\nfunction protect(text: string): Protection {\n const restore = new Map<string, string>();\n let counter = 0;\n\n // Collect all candidate matches across patterns, then resolve overlaps by\n // earliest start (and longest on tie).\n interface Span {\n start: number;\n end: number;\n value: string;\n }\n const spans: Span[] = [];\n\n for (let p = 0; p < PROTECT_PATTERNS.length; p++) {\n const re = new RegExp(\n PROTECT_PATTERNS[p].source,\n PROTECT_PATTERNS[p].flags,\n );\n let m: RegExpExecArray | null;\n while ((m = re.exec(text)) !== null) {\n const value = m[0];\n if (value.length === 0) {\n re.lastIndex++;\n continue;\n }\n const isTokenPattern = p === PROTECT_PATTERNS.length - 1;\n if (isTokenPattern && !looksLikeIdentifierToken(value)) {\n continue;\n }\n spans.push({ start: m.index, end: m.index + value.length, value });\n }\n }\n\n if (spans.length === 0) return { text, restore };\n\n spans.sort((a, b) =>\n a.start !== b.start ? a.start - b.start : b.end - a.end,\n );\n\n let out = \"\";\n let cursor = 0;\n for (const span of spans) {\n if (span.start < cursor) continue; // overlapped by an earlier protection\n out += text.slice(cursor, span.start);\n const ph = makePlaceholder(counter++);\n restore.set(ph, span.value);\n out += ph;\n cursor = span.end;\n }\n out += text.slice(cursor);\n\n return { text: out, restore };\n}\n\nfunction unprotect(text: string, restore: Map<string, string>): string {\n if (restore.size === 0) return text;\n let out = text;\n for (const [ph, original] of restore) {\n out = out.split(ph).join(original);\n }\n return out;\n}\n\n/* ------------------------------------------------------------------ *\n * Transforms (run only on protected text)\n * ------------------------------------------------------------------ */\n\nconst EMAIL_RE = /\\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}\\b/g;\n\n// 2+ capitalized words (each starts uppercase, allows internal lowercase /\n// apostrophes / hyphens). Single-letter middle initials allowed: `Sarah J Connor`.\nconst FULL_NAME_RE = /\\b[A-Z][a-zA-Z'’-]*(?:\\s+[A-Z](?:[a-zA-Z'’-]*)?){1,3}\\b/g;\n\n// The ENTIRE value is a person name: 2–4 consecutive capitalized words. Used\n// to decide whether a short string under a name-ish key (`name`, `from`,\n// `sender`, …) should be coerced wholesale to a fake name. Single tokens\n// (\"Steve\") and mixed-case label phrases (\"Important\", \"Automated\n// notifications\", \"Note to Self\", \"Other\") deliberately fail this so mail\n// folder/tab names and other labels are NOT mangled — we only fake values\n// that genuinely look like a multi-word person name.\nconst LOOKS_LIKE_FULL_NAME_RE =\n /^[A-Z][a-zA-Z'’.-]*(?:\\s+[A-Z][a-zA-Z'’.-]*){1,3}$/;\n\n// A standalone numeric token: optional currency + sign, digits with optional\n// comma grouping and a single optional decimal part. Bounded so it is NOT\n// adjacent to a letter (placeholders already removed letter-mixed tokens, but\n// this keeps the rule self-contained and safe on raw structured values too).\nconst NUMBER_RE =\n /(^|[^A-Za-z0-9_])([$€£]?)([+-]?)(\\d[\\d,]*(?:\\.\\d+)?)(?![A-Za-z0-9_])/g;\n\nfunction transformEmails(text: string, salt: string): string {\n return text.replace(EMAIL_RE, (match) => fakeEmail(match, salt));\n}\n\nfunction transformNames(text: string, salt: string): string {\n return text.replace(FULL_NAME_RE, (match) => {\n // Keep trailing/leading whitespace handling implicit; `match` is the run.\n return fakeName(match, salt);\n });\n}\n\nfunction isProbablyYear(digits: string): boolean {\n if (digits.length !== 4) return false;\n const n = Number(digits);\n return n >= 1900 && n <= 2099;\n}\n\nfunction transformNumbers(text: string, salt: string): string {\n return text.replace(\n NUMBER_RE,\n (_full, pre: string, currency: string, sign: string, body: string) => {\n const hasGrouping = body.includes(\",\");\n const hasDecimal = body.includes(\".\");\n const digitsOnly = body.replace(/[^0-9]/g, \"\");\n\n // Skip bare years like 2026.\n if (\n !currency &&\n !sign &&\n !hasGrouping &&\n !hasDecimal &&\n isProbablyYear(digitsOnly)\n ) {\n return `${pre}${currency}${sign}${body}`;\n }\n\n // Skip standalone integers < 1000 with no currency and no grouping\n // (rewriting \"3 unread\" / \"page 2\" looks broken and isn't sensitive).\n if (!currency && !hasGrouping && !hasDecimal) {\n const n = Number(digitsOnly);\n if (Number.isFinite(n) && n < 1000) {\n return `${pre}${currency}${sign}${body}`;\n }\n }\n\n const fakeBody = fakeNumberBody(body, `${currency}${sign}${body}`, salt);\n return `${pre}${currency}${sign}${fakeBody}`;\n },\n );\n}\n\n/* ------------------------------------------------------------------ *\n * Public: string redactor\n * ------------------------------------------------------------------ */\n\nexport function redactDemoString(text: string, opts?: RedactOptions): string {\n if (typeof text !== \"string\" || text.length === 0) return text;\n const salt = opts?.salt ?? \"\";\n\n const { text: masked, restore } = protect(text);\n let out = masked;\n out = transformEmails(out, salt);\n out = transformNames(out, salt);\n out = transformNumbers(out, salt);\n return unprotect(out, restore);\n}\n\n/* ------------------------------------------------------------------ *\n * Public: structure-aware redactor\n * ------------------------------------------------------------------ */\n\n// Keys whose leaf values must NEVER be transformed (still recurse into nested\n// objects/arrays under them). Besides ids/urls/timestamps this also covers\n// code/query-bearing keys (`sql`, `query`, `expression`, `formula`, `code`):\n// prose-redacting a SQL string injects fake names/numbers into it and rewrites\n// its leading tokens, producing `syntax error at or near \"Henry\"` /\n// `queries must start with SELECT or WITH`. The query must run untouched; its\n// RESULTS are what get redacted so the chart still shows fake names.\nconst PROTECTED_KEY_RE =\n /^id$|(^|_)id$|Id$|Ids$|uuid|guid|slug|token|secret|password|passwd|apikey|api_key|hash|sha\\d*|etag|cursor|nonce|sessionid|messageid|threadid|nodeid|(^|_)key$|keyid|(^|_)ref$|url$|uri$|href$|src$|path$|filename$|mimetype|mime|^sql$|sql$|query|expression|formula|^code$|createdat|updatedat|deletedat|expiresat|timestamp|.+at$|.+_at$/i;\n\n// Keys whose (short) leaf string should be treated as a full name even when it\n// is a single token.\nconst NAME_KEY_RE =\n /name|from$|to$|sender|recipient|author|fullName|displayName|firstName|lastName|contact/i;\n\nconst MAX_DEPTH = 64;\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n if (value === null || typeof value !== \"object\") return false;\n if (Array.isArray(value)) return false;\n if (value instanceof Date) return false;\n const proto = Object.getPrototypeOf(value);\n return proto === Object.prototype || proto === null;\n}\n\nfunction redactNameLeaf(value: string, salt: string): string {\n if (value.length === 0 || value.length > 40) {\n // Too long to be a bare name — fall back to prose redaction.\n return redactDemoStringWithSalt(value, salt);\n }\n // If it already contains an email, redact that path instead.\n if (EMAIL_RE.test(value)) {\n EMAIL_RE.lastIndex = 0;\n return redactDemoStringWithSalt(value, salt);\n }\n EMAIL_RE.lastIndex = 0;\n\n // Only coerce the whole value to a fake name when it actually looks like a\n // person's name (2–4 capitalized words). Single tokens (\"Steve\") and\n // label-ish phrases (\"Important\", \"Automated notifications\", \"Note to\n // Self\") are NOT names — fall back to prose redaction, which leaves them\n // intact while still faking any embedded multi-word name / email / number.\n if (!LOOKS_LIKE_FULL_NAME_RE.test(value.trim())) {\n return redactDemoStringWithSalt(value, salt);\n }\n\n const { text: masked, restore } = protect(value);\n // If protection consumed the whole value, it was identifier-shaped — leave.\n if (masked.trim().length === 0 || restore.size > 0) {\n // Be conservative: if any part looked like an ID, prefer prose redaction\n // (which preserves the protected bits) over forcing a name.\n return redactDemoStringWithSalt(value, salt);\n }\n return fakeName(value, salt);\n}\n\nfunction redactDemoStringWithSalt(text: string, salt: string): string {\n return redactDemoString(text, { salt });\n}\n\nfunction redactNumberLeaf(value: number, salt: string): number {\n if (!Number.isFinite(value)) return value;\n const repr = String(value);\n // Re-use the string number transform but only if the representation is a\n // clean numeric token we can round-trip back to a JS number.\n const redacted = transformNumbers(repr, salt);\n if (redacted === repr) return value;\n const n = Number(redacted);\n if (!Number.isFinite(n)) return value;\n return n;\n}\n\nfunction walk(\n value: unknown,\n salt: string,\n depth: number,\n seen: WeakSet<object>,\n underNameKey: boolean,\n): unknown {\n if (depth > MAX_DEPTH) return value;\n\n if (value === null || value === undefined) return value;\n\n const t = typeof value;\n\n if (t === \"string\") {\n const str = value as string;\n if (underNameKey) return redactNameLeaf(str, salt);\n return redactDemoStringWithSalt(str, salt);\n }\n\n if (t === \"number\") {\n return redactNumberLeaf(value as number, salt);\n }\n\n if (t === \"boolean\" || t === \"bigint\" || t === \"function\" || t === \"symbol\") {\n return value;\n }\n\n if (value instanceof Date) return value;\n\n if (Array.isArray(value)) {\n if (seen.has(value)) return value;\n seen.add(value);\n const out = value.map((item) =>\n walk(item, salt, depth + 1, seen, underNameKey),\n );\n seen.delete(value);\n return out;\n }\n\n if (isPlainObject(value)) {\n if (seen.has(value)) return value;\n seen.add(value);\n const out: Record<string, unknown> = {};\n for (const [key, entry] of Object.entries(value)) {\n const keyIsProtected = PROTECTED_KEY_RE.test(key);\n if (keyIsProtected) {\n if (\n entry !== null &&\n typeof entry === \"object\" &&\n !(entry instanceof Date)\n ) {\n // Still recurse into nested structures, but the protected key does\n // not propagate name/transform semantics.\n out[key] = walk(entry, salt, depth + 1, seen, false);\n } else {\n // Leaf under a protected key: pass through completely untouched.\n out[key] = entry;\n }\n continue;\n }\n const keyIsName = NAME_KEY_RE.test(key);\n out[key] = walk(entry, salt, depth + 1, seen, keyIsName);\n }\n seen.delete(value);\n return out;\n }\n\n // Unknown object kind (Map, Set, class instance, etc.) — leave untouched.\n return value;\n}\n\nexport function redactDemoData<T>(value: T, opts?: RedactOptions): T {\n const salt = opts?.salt ?? \"\";\n return walk(value, salt, 0, new WeakSet<object>(), false) as T;\n}\n\n/**\n * Clear the stable-mapping caches. Test-only — the caches are process-global\n * (intentionally, so mappings stay stable for a tab's session), which would\n * otherwise let one test's produced fakes leak into another's assertions.\n */\nexport function __resetDemoRedactCacheForTests(): void {\n forwardCache.clear();\n producedFakes.clear();\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"action-routes.d.ts","sourceRoot":"","sources":["../../src/server/action-routes.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;AAmEhE,MAAM,WAAW,wBAAwB;IACvC,gEAAgE;IAChE,iBAAiB,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,KAAK,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAC7D,8DAA8D;IAC9D,oBAAoB,CAAC,EAAE,CACrB,KAAK,EAAE,GAAG,KACP,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC;IACtD,0DAA0D;IAC1D,YAAY,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,KAAK,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;CACvE;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAC/B,QAAQ,EAAE,GAAG,EACb,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,EACpC,OAAO,CAAC,EAAE,wBAAwB,QA2KnC"}
1
+ {"version":3,"file":"action-routes.d.ts","sourceRoot":"","sources":["../../src/server/action-routes.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;AAiEhE,MAAM,WAAW,wBAAwB;IACvC,gEAAgE;IAChE,iBAAiB,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,KAAK,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAC7D,8DAA8D;IAC9D,oBAAoB,CAAC,EAAE,CACrB,KAAK,EAAE,GAAG,KACP,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC;IACtD,0DAA0D;IAC1D,YAAY,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,KAAK,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;CACvE;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAC/B,QAAQ,EAAE,GAAG,EACb,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,EACpC,OAAO,CAAC,EAAE,wBAAwB,QAkKnC"}
@@ -9,8 +9,6 @@ import { defineEventHandler, setResponseStatus, setResponseHeader, getMethod, ge
9
9
  import { readBody } from "../server/h3-helpers.js";
10
10
  import { runWithRequestContext } from "./request-context.js";
11
11
  import { recordChange } from "./poll.js";
12
- import { isDemoModeEnabled } from "../demo/config.js";
13
- import { redactDemoData, redactDemoString } from "../demo/redact.js";
14
12
  import { getAllowedCorsOrigin as resolveAllowedCorsOrigin, readCorsAllowedOrigins, } from "./cors-origins.js";
15
13
  const ROUTE_PREFIX = "/_agent-native/actions";
16
14
  /**
@@ -172,24 +170,16 @@ export function mountActionRoutes(nitroApp, actions, options) {
172
170
  // ignore
173
171
  }
174
172
  }
175
- // Demo mode: replace real names/emails/numbers with
176
- // deterministic fake data before the result leaves the server.
177
- // Gated so the (expensive) structure-aware walk only runs when
178
- // demo mode is actually on. Walking the parsed object — not the
179
- // JSON string — is what keeps IDs/dates/URLs safe.
180
- const demo = await isDemoModeEnabled();
181
173
  // If the action returned a string, try to parse as JSON for a clean response
182
174
  if (typeof result === "string") {
183
- let parsed;
184
175
  try {
185
- parsed = JSON.parse(result);
176
+ return JSON.parse(result);
186
177
  }
187
178
  catch {
188
- return demo ? redactDemoString(result) : result;
179
+ return result;
189
180
  }
190
- return demo ? redactDemoData(parsed) : parsed;
191
181
  }
192
- return demo ? redactDemoData(result) : result;
182
+ return result;
193
183
  }
194
184
  catch (err) {
195
185
  const msg = err?.message ?? String(err);
@@ -1 +1 @@
1
- {"version":3,"file":"action-routes.js","sourceRoot":"","sources":["../../src/server/action-routes.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Auto-mount actions as HTTP endpoints under /_agent-native/actions/:name.\n *\n * Actions are exposed as POST by default. Use `http: { method: \"GET\" }` in\n * defineAction to expose as GET. Use `http: false` to mark as agent-only.\n */\nimport { getH3App } from \"./framework-request-handler.js\";\nimport {\n defineEventHandler,\n setResponseStatus,\n setResponseHeader,\n getMethod,\n getQuery,\n getHeader,\n} from \"h3\";\nimport type { ActionEntry } from \"../agent/production-agent.js\";\nimport { readBody } from \"../server/h3-helpers.js\";\nimport { runWithRequestContext } from \"./request-context.js\";\nimport { recordChange } from \"./poll.js\";\nimport { isDemoModeEnabled } from \"../demo/config.js\";\nimport { redactDemoData, redactDemoString } from \"../demo/redact.js\";\nimport {\n getAllowedCorsOrigin as resolveAllowedCorsOrigin,\n readCorsAllowedOrigins,\n} from \"./cors-origins.js\";\n\nconst ROUTE_PREFIX = \"/_agent-native/actions\";\n\n/**\n * Read the caller's IANA timezone from the `x-user-timezone` header. The core\n * client sends this on every action request so server-side \"today\" fallbacks\n * can honor the user's local day.\n */\nfunction readTimezoneHeader(event: any): string | undefined {\n try {\n const raw = getHeader(event, \"x-user-timezone\");\n if (!raw || typeof raw !== \"string\") return undefined;\n const trimmed = raw.trim();\n return trimmed.length > 0 && trimmed.length < 64 ? trimmed : undefined;\n } catch {\n return undefined;\n }\n}\n\nfunction getAllowedCorsOrigin(origin: string | undefined): string | null {\n return resolveAllowedCorsOrigin(origin, {\n allowedOrigins: readCorsAllowedOrigins(),\n allowLocalhostWhenNoAllowlist: true,\n });\n}\n\nfunction handleOptionsRequest(event: any): string {\n const origin = getHeader(event, \"origin\");\n const allowedOrigin = getAllowedCorsOrigin(\n typeof origin === \"string\" ? origin : undefined,\n );\n\n if (origin && !allowedOrigin) {\n setResponseStatus(event, 403);\n return \"\";\n }\n\n if (allowedOrigin) {\n setResponseHeader(event, \"Access-Control-Allow-Origin\", allowedOrigin);\n setResponseHeader(event, \"Vary\", \"Origin\");\n setResponseHeader(event, \"Access-Control-Allow-Credentials\", \"true\");\n setResponseHeader(\n event,\n \"Access-Control-Allow-Methods\",\n \"GET,HEAD,POST,PUT,PATCH,DELETE,OPTIONS\",\n );\n setResponseHeader(\n event,\n \"Access-Control-Allow-Headers\",\n \"Content-Type,Authorization,X-Requested-With,X-Request-Source,X-Agent-Native-CSRF,X-Agent-Native-Tool-Bridge,X-Agent-Native-Tool-Id\",\n );\n }\n\n setResponseStatus(event, 204);\n return \"\";\n}\n\nexport interface MountActionRoutesOptions {\n /** Resolve owner email from the H3 event (for data scoping). */\n getOwnerFromEvent?: (event: any) => string | Promise<string>;\n /** Resolve display name from the H3 event, when available. */\n getUserNameFromEvent?: (\n event: any,\n ) => string | undefined | Promise<string | undefined>;\n /** Resolve org ID from the H3 event (for org scoping). */\n resolveOrgId?: (event: any) => string | null | Promise<string | null>;\n}\n\n/**\n * Mount discovered actions as HTTP endpoints.\n *\n * Only actions from `autoDiscoverActions` (template actions) are mounted.\n * Built-in actions (resource-*, chat-*, shell, etc.) are NOT passed here.\n */\nexport function mountActionRoutes(\n nitroApp: any,\n actions: Record<string, ActionEntry>,\n options?: MountActionRoutesOptions,\n) {\n const mounted: string[] = [];\n\n for (const [name, entry] of Object.entries(actions)) {\n // Skip agent-only actions\n if (entry.http === false) continue;\n\n const method = entry.http?.method ?? \"POST\";\n const path = entry.http?.path ?? name;\n const routePath = `${ROUTE_PREFIX}/${path}`;\n\n getH3App(nitroApp).use(\n routePath,\n defineEventHandler(async (event) => {\n const reqMethod = getMethod(event);\n const effectiveMethod =\n reqMethod === \"HEAD\" && method === \"GET\" ? \"GET\" : reqMethod;\n\n if (reqMethod === \"OPTIONS\") {\n return handleOptionsRequest(event);\n }\n\n setResponseHeader(event, \"Cache-Control\", \"no-store\");\n\n // Allow the declared method\n if (effectiveMethod !== method) {\n setResponseStatus(event, 405);\n return { error: `Method not allowed. Use ${method}.` };\n }\n\n // (audit H5) Per-action `toolCallable` opt-out for the tools-iframe\n // bridge. The bridge tags every outbound action call with\n // X-Agent-Native-Tool-Bridge: 1. When that header is present and the\n // action declares `toolCallable: false`, we 403 — used by the\n // framework's share-resource / unshare-resource /\n // set-resource-visibility for defense-in-depth on auth-adjacent\n // operations. Undefined defaults to allow: tools are intra-org and\n // typically authored by trusted teammates, so the default is to\n // trust the org-level access controls.\n // The header is set by the parent (the React host), not by the\n // iframe's user-authored content; sanitizeToolRequestOptions strips\n // iframe attempts to spoof it.\n const fromToolBridge =\n getHeader(event, \"x-agent-native-tool-bridge\") === \"1\";\n if (fromToolBridge && entry.toolCallable === false) {\n setResponseStatus(event, 403);\n return {\n error: `Action '${name}' is not callable from tools.`,\n };\n }\n\n // Resolve auth context for per-request scoping\n const userEmail = options?.getOwnerFromEvent\n ? await options.getOwnerFromEvent(event)\n : undefined;\n const userName = options?.getUserNameFromEvent\n ? await options.getUserNameFromEvent(event)\n : undefined;\n const orgId = options?.resolveOrgId\n ? ((await options.resolveOrgId(event)) ?? undefined)\n : undefined;\n const timezone = readTimezoneHeader(event);\n\n return runWithRequestContext(\n { userEmail, userName, orgId, timezone },\n async () => {\n // Parse params based on method. On web-standard runtimes (Netlify\n // Functions, CF Workers), event.req IS the web Request — use .json()\n // directly. H3's readBody fails on those runtimes because it expects\n // a Node.js stream on event.node.req.\n let params: Record<string, any>;\n try {\n if (method === \"GET\") {\n // H3 v2: prefer web Request URL, fallback to getQuery\n const webReq = (event as any).req;\n if (webReq?.url) {\n const url = new URL(webReq.url);\n params = Object.fromEntries(url.searchParams);\n } else {\n params = getQuery(event) as Record<string, any>;\n }\n } else {\n const webReq = (event as any).req;\n if (webReq && typeof webReq.json === \"function\") {\n // H3 v2: event.req is the web Request — use .json() directly\n params = (await webReq.json().catch(() => null)) ?? {};\n } else {\n // Fallback: H3's readBody (Node.js dev)\n params = (await readBody(event)) ?? {};\n }\n }\n } catch {\n params = {};\n }\n\n // Run the action\n try {\n const result = await entry.run(params);\n\n // Auto-refresh the UI after a successful mutating action. GET\n // actions and actions explicitly flagged readOnly are skipped.\n // Other tabs' useDbSync will see source:\"action\" and invalidate\n // their action queries. The calling tab already refetches via\n // useActionMutation's onSuccess, so this is mainly cross-tab\n // sync (and parity with the agent's tool-call path).\n // Explicit entry.readOnly (true OR false) wins over the method\n // heuristic. defineAction already auto-infers GET → readOnly=true,\n // so for actions registered through that path entry.readOnly is\n // always set and the fallback just guards legacy wrap paths.\n const isReadOnly =\n typeof entry.readOnly === \"boolean\"\n ? entry.readOnly\n : method === \"GET\";\n if (!isReadOnly) {\n try {\n recordChange({\n source: \"action\",\n type: \"change\",\n key: name,\n owner: userEmail,\n });\n } catch {\n // ignore\n }\n }\n\n // Demo mode: replace real names/emails/numbers with\n // deterministic fake data before the result leaves the server.\n // Gated so the (expensive) structure-aware walk only runs when\n // demo mode is actually on. Walking the parsed object — not the\n // JSON string — is what keeps IDs/dates/URLs safe.\n const demo = await isDemoModeEnabled();\n\n // If the action returned a string, try to parse as JSON for a clean response\n if (typeof result === \"string\") {\n let parsed: any;\n try {\n parsed = JSON.parse(result);\n } catch {\n return demo ? redactDemoString(result) : result;\n }\n return demo ? redactDemoData(parsed) : parsed;\n }\n\n return demo ? redactDemoData(result) : result;\n } catch (err: any) {\n const msg = err?.message ?? String(err);\n // Return 400 for validation errors, 500 for everything else\n setResponseStatus(\n event,\n msg.startsWith(\"Invalid action parameters\")\n ? 400\n : typeof err?.statusCode === \"number\"\n ? err.statusCode\n : 500,\n );\n return { error: msg };\n }\n },\n ); // end runWithRequestContext\n }),\n );\n\n mounted.push(`${method} ${routePath}`);\n }\n\n if (mounted.length > 0 && process.env.DEBUG)\n console.log(\n `[action-routes] Mounted ${mounted.length} action route(s): ${mounted.join(\", \")}`,\n );\n}\n"]}
1
+ {"version":3,"file":"action-routes.js","sourceRoot":"","sources":["../../src/server/action-routes.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Auto-mount actions as HTTP endpoints under /_agent-native/actions/:name.\n *\n * Actions are exposed as POST by default. Use `http: { method: \"GET\" }` in\n * defineAction to expose as GET. Use `http: false` to mark as agent-only.\n */\nimport { getH3App } from \"./framework-request-handler.js\";\nimport {\n defineEventHandler,\n setResponseStatus,\n setResponseHeader,\n getMethod,\n getQuery,\n getHeader,\n} from \"h3\";\nimport type { ActionEntry } from \"../agent/production-agent.js\";\nimport { readBody } from \"../server/h3-helpers.js\";\nimport { runWithRequestContext } from \"./request-context.js\";\nimport { recordChange } from \"./poll.js\";\nimport {\n getAllowedCorsOrigin as resolveAllowedCorsOrigin,\n readCorsAllowedOrigins,\n} from \"./cors-origins.js\";\n\nconst ROUTE_PREFIX = \"/_agent-native/actions\";\n\n/**\n * Read the caller's IANA timezone from the `x-user-timezone` header. The core\n * client sends this on every action request so server-side \"today\" fallbacks\n * can honor the user's local day.\n */\nfunction readTimezoneHeader(event: any): string | undefined {\n try {\n const raw = getHeader(event, \"x-user-timezone\");\n if (!raw || typeof raw !== \"string\") return undefined;\n const trimmed = raw.trim();\n return trimmed.length > 0 && trimmed.length < 64 ? trimmed : undefined;\n } catch {\n return undefined;\n }\n}\n\nfunction getAllowedCorsOrigin(origin: string | undefined): string | null {\n return resolveAllowedCorsOrigin(origin, {\n allowedOrigins: readCorsAllowedOrigins(),\n allowLocalhostWhenNoAllowlist: true,\n });\n}\n\nfunction handleOptionsRequest(event: any): string {\n const origin = getHeader(event, \"origin\");\n const allowedOrigin = getAllowedCorsOrigin(\n typeof origin === \"string\" ? origin : undefined,\n );\n\n if (origin && !allowedOrigin) {\n setResponseStatus(event, 403);\n return \"\";\n }\n\n if (allowedOrigin) {\n setResponseHeader(event, \"Access-Control-Allow-Origin\", allowedOrigin);\n setResponseHeader(event, \"Vary\", \"Origin\");\n setResponseHeader(event, \"Access-Control-Allow-Credentials\", \"true\");\n setResponseHeader(\n event,\n \"Access-Control-Allow-Methods\",\n \"GET,HEAD,POST,PUT,PATCH,DELETE,OPTIONS\",\n );\n setResponseHeader(\n event,\n \"Access-Control-Allow-Headers\",\n \"Content-Type,Authorization,X-Requested-With,X-Request-Source,X-Agent-Native-CSRF,X-Agent-Native-Tool-Bridge,X-Agent-Native-Tool-Id\",\n );\n }\n\n setResponseStatus(event, 204);\n return \"\";\n}\n\nexport interface MountActionRoutesOptions {\n /** Resolve owner email from the H3 event (for data scoping). */\n getOwnerFromEvent?: (event: any) => string | Promise<string>;\n /** Resolve display name from the H3 event, when available. */\n getUserNameFromEvent?: (\n event: any,\n ) => string | undefined | Promise<string | undefined>;\n /** Resolve org ID from the H3 event (for org scoping). */\n resolveOrgId?: (event: any) => string | null | Promise<string | null>;\n}\n\n/**\n * Mount discovered actions as HTTP endpoints.\n *\n * Only actions from `autoDiscoverActions` (template actions) are mounted.\n * Built-in actions (resource-*, chat-*, shell, etc.) are NOT passed here.\n */\nexport function mountActionRoutes(\n nitroApp: any,\n actions: Record<string, ActionEntry>,\n options?: MountActionRoutesOptions,\n) {\n const mounted: string[] = [];\n\n for (const [name, entry] of Object.entries(actions)) {\n // Skip agent-only actions\n if (entry.http === false) continue;\n\n const method = entry.http?.method ?? \"POST\";\n const path = entry.http?.path ?? name;\n const routePath = `${ROUTE_PREFIX}/${path}`;\n\n getH3App(nitroApp).use(\n routePath,\n defineEventHandler(async (event) => {\n const reqMethod = getMethod(event);\n const effectiveMethod =\n reqMethod === \"HEAD\" && method === \"GET\" ? \"GET\" : reqMethod;\n\n if (reqMethod === \"OPTIONS\") {\n return handleOptionsRequest(event);\n }\n\n setResponseHeader(event, \"Cache-Control\", \"no-store\");\n\n // Allow the declared method\n if (effectiveMethod !== method) {\n setResponseStatus(event, 405);\n return { error: `Method not allowed. Use ${method}.` };\n }\n\n // (audit H5) Per-action `toolCallable` opt-out for the tools-iframe\n // bridge. The bridge tags every outbound action call with\n // X-Agent-Native-Tool-Bridge: 1. When that header is present and the\n // action declares `toolCallable: false`, we 403 — used by the\n // framework's share-resource / unshare-resource /\n // set-resource-visibility for defense-in-depth on auth-adjacent\n // operations. Undefined defaults to allow: tools are intra-org and\n // typically authored by trusted teammates, so the default is to\n // trust the org-level access controls.\n // The header is set by the parent (the React host), not by the\n // iframe's user-authored content; sanitizeToolRequestOptions strips\n // iframe attempts to spoof it.\n const fromToolBridge =\n getHeader(event, \"x-agent-native-tool-bridge\") === \"1\";\n if (fromToolBridge && entry.toolCallable === false) {\n setResponseStatus(event, 403);\n return {\n error: `Action '${name}' is not callable from tools.`,\n };\n }\n\n // Resolve auth context for per-request scoping\n const userEmail = options?.getOwnerFromEvent\n ? await options.getOwnerFromEvent(event)\n : undefined;\n const userName = options?.getUserNameFromEvent\n ? await options.getUserNameFromEvent(event)\n : undefined;\n const orgId = options?.resolveOrgId\n ? ((await options.resolveOrgId(event)) ?? undefined)\n : undefined;\n const timezone = readTimezoneHeader(event);\n\n return runWithRequestContext(\n { userEmail, userName, orgId, timezone },\n async () => {\n // Parse params based on method. On web-standard runtimes (Netlify\n // Functions, CF Workers), event.req IS the web Request — use .json()\n // directly. H3's readBody fails on those runtimes because it expects\n // a Node.js stream on event.node.req.\n let params: Record<string, any>;\n try {\n if (method === \"GET\") {\n // H3 v2: prefer web Request URL, fallback to getQuery\n const webReq = (event as any).req;\n if (webReq?.url) {\n const url = new URL(webReq.url);\n params = Object.fromEntries(url.searchParams);\n } else {\n params = getQuery(event) as Record<string, any>;\n }\n } else {\n const webReq = (event as any).req;\n if (webReq && typeof webReq.json === \"function\") {\n // H3 v2: event.req is the web Request — use .json() directly\n params = (await webReq.json().catch(() => null)) ?? {};\n } else {\n // Fallback: H3's readBody (Node.js dev)\n params = (await readBody(event)) ?? {};\n }\n }\n } catch {\n params = {};\n }\n\n // Run the action\n try {\n const result = await entry.run(params);\n\n // Auto-refresh the UI after a successful mutating action. GET\n // actions and actions explicitly flagged readOnly are skipped.\n // Other tabs' useDbSync will see source:\"action\" and invalidate\n // their action queries. The calling tab already refetches via\n // useActionMutation's onSuccess, so this is mainly cross-tab\n // sync (and parity with the agent's tool-call path).\n // Explicit entry.readOnly (true OR false) wins over the method\n // heuristic. defineAction already auto-infers GET → readOnly=true,\n // so for actions registered through that path entry.readOnly is\n // always set and the fallback just guards legacy wrap paths.\n const isReadOnly =\n typeof entry.readOnly === \"boolean\"\n ? entry.readOnly\n : method === \"GET\";\n if (!isReadOnly) {\n try {\n recordChange({\n source: \"action\",\n type: \"change\",\n key: name,\n owner: userEmail,\n });\n } catch {\n // ignore\n }\n }\n\n // If the action returned a string, try to parse as JSON for a clean response\n if (typeof result === \"string\") {\n try {\n return JSON.parse(result);\n } catch {\n return result;\n }\n }\n\n return result;\n } catch (err: any) {\n const msg = err?.message ?? String(err);\n // Return 400 for validation errors, 500 for everything else\n setResponseStatus(\n event,\n msg.startsWith(\"Invalid action parameters\")\n ? 400\n : typeof err?.statusCode === \"number\"\n ? err.statusCode\n : 500,\n );\n return { error: msg };\n }\n },\n ); // end runWithRequestContext\n }),\n );\n\n mounted.push(`${method} ${routePath}`);\n }\n\n if (mounted.length > 0 && process.env.DEBUG)\n console.log(\n `[action-routes] Mounted ${mounted.length} action route(s): ${mounted.join(\", \")}`,\n );\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../src/server/auth.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../src/server/auth.ts"],"names":[],"mappings":"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"}
@@ -1,5 +1,5 @@
1
1
  import crypto from "node:crypto";
2
- import { defineEventHandler, getMethod, getQuery, getRequestIP, sendRedirect, setResponseHeader, setResponseStatus, getCookie, setCookie, deleteCookie, getHeader, } from "h3";
2
+ import { defineEventHandler, getMethod, getQuery, getRequestIP, setResponseHeader, setResponseStatus, getCookie, setCookie, deleteCookie, getHeader, } from "h3";
3
3
  // In h3 v2, `event.req` IS the web Request — but in Nitro's dev server (srvx
4
4
  // runtime), event.url and event.req share the same underlying URL object.
5
5
  // When registerMiddleware strips the mount prefix from event.url.pathname, it
@@ -1711,7 +1711,18 @@ async function mountBetterAuthRoutes(app, options) {
1711
1711
  });
1712
1712
  const authUrl = `https://accounts.google.com/o/oauth2/v2/auth?${params}`;
1713
1713
  if (q.redirect === "1") {
1714
- return sendRedirect(event, authUrl, 302);
1714
+ // Return a native web Response — NOT h3 v2's `sendRedirect`. Under
1715
+ // h3 `2.0.1-rc.20`, `sendRedirect = (_, loc, code) => redirect(...)`
1716
+ // ignores the event and returns a non-standard `HTTPResponse` class
1717
+ // instance; the framework request-handler shim doesn't unwrap it and
1718
+ // String()-coerces it to the literal text "[object Object]" with a
1719
+ // 200 status (no Location header), which broke the popup-based
1720
+ // Google sign-in in production. Web `Response` is the proven idiom
1721
+ // here — `oauthCallbackResponse`/`oauthErrorPage` use it and work.
1722
+ return new Response(null, {
1723
+ status: 302,
1724
+ headers: { Location: authUrl },
1725
+ });
1715
1726
  }
1716
1727
  return { url: authUrl };
1717
1728
  }));