@agent-native/core 0.98.2 → 0.98.4

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 (78) hide show
  1. package/corpus/README.md +2 -2
  2. package/corpus/core/CHANGELOG.md +23 -0
  3. package/corpus/core/package.json +1 -1
  4. package/corpus/core/src/agent/production-agent.ts +2 -2
  5. package/corpus/core/src/client/analytics.ts +12 -0
  6. package/corpus/core/src/client/index.ts +2 -0
  7. package/corpus/core/src/client/session-replay.ts +612 -26
  8. package/corpus/core/src/client/settings/DemoModeSection.tsx +6 -6
  9. package/corpus/core/src/client/settings/SettingsPanel.tsx +31 -211
  10. package/corpus/core/src/client/settings/agent-settings-search.ts +219 -0
  11. package/corpus/core/src/client/settings/index.ts +4 -0
  12. package/corpus/core/src/demo/actions/toggle-demo-mode.ts +2 -2
  13. package/corpus/core/src/demo/config.ts +6 -6
  14. package/corpus/core/src/demo/fetch-interceptor.ts +25 -2
  15. package/corpus/core/src/demo/redact.ts +57 -66
  16. package/corpus/templates/analytics/.agents/skills/session-replay/SKILL.md +65 -6
  17. package/corpus/templates/analytics/AGENTS.md +6 -6
  18. package/corpus/templates/analytics/app/components/layout/CommandPalette.tsx +429 -244
  19. package/corpus/templates/analytics/app/components/layout/command-palette-search.ts +93 -0
  20. package/corpus/templates/analytics/app/global.css +4 -62
  21. package/corpus/templates/analytics/app/hooks/use-replay-storage-status.ts +2 -1
  22. package/corpus/templates/analytics/app/lib/demo-chart-trend.ts +46 -33
  23. package/corpus/templates/analytics/app/pages/Settings.tsx +8 -45
  24. package/corpus/templates/analytics/app/pages/sessions/SessionDetailPage.tsx +413 -105
  25. package/corpus/templates/analytics/app/pages/sessions/SessionsPage.tsx +32 -16
  26. package/corpus/templates/analytics/app/pages/settings/settings-search.ts +126 -0
  27. package/corpus/templates/analytics/changelog/2026-07-12-command-menu-loading-placeholders-now-stay-below-available-r.md +6 -0
  28. package/corpus/templates/analytics/changelog/2026-07-12-command-menu-search-now-prioritizes-the-best-match.md +6 -0
  29. package/corpus/templates/analytics/changelog/2026-07-12-demo-charts-preserve-source-movement.md +6 -0
  30. package/corpus/templates/analytics/changelog/2026-07-12-fixed-session-replays-that-appeared-ultra-wide-hid-recorded-.md +6 -0
  31. package/corpus/templates/analytics/changelog/2026-07-12-replay-storage-settings-stay-readable-at-narrow-widths.md +6 -0
  32. package/corpus/templates/analytics/changelog/2026-07-12-session-replays-load-faster-in-demo-mode-while-visitor-email.md +6 -0
  33. package/corpus/templates/analytics/changelog/2026-07-12-session-replays-preserve-recorded-styling-and-keep-malformed.md +6 -0
  34. package/corpus/templates/calendar/app/components/calendar/DeleteEventDialog.tsx +11 -2
  35. package/corpus/templates/calendar/app/components/calendar/EventAttendeesSection.tsx +14 -2
  36. package/corpus/templates/calendar/changelog/2026-07-12-calendar-responses-can-now-be-saved-with-command-enter-or-ct.md +6 -0
  37. package/dist/agent/production-agent.js +2 -2
  38. package/dist/agent/production-agent.js.map +1 -1
  39. package/dist/client/analytics.d.ts.map +1 -1
  40. package/dist/client/analytics.js +11 -0
  41. package/dist/client/analytics.js.map +1 -1
  42. package/dist/client/index.d.ts +1 -1
  43. package/dist/client/index.d.ts.map +1 -1
  44. package/dist/client/index.js +1 -1
  45. package/dist/client/index.js.map +1 -1
  46. package/dist/client/session-replay.d.ts +12 -3
  47. package/dist/client/session-replay.d.ts.map +1 -1
  48. package/dist/client/session-replay.js +488 -23
  49. package/dist/client/session-replay.js.map +1 -1
  50. package/dist/client/settings/DemoModeSection.d.ts +3 -3
  51. package/dist/client/settings/DemoModeSection.js +4 -4
  52. package/dist/client/settings/DemoModeSection.js.map +1 -1
  53. package/dist/client/settings/SettingsPanel.d.ts.map +1 -1
  54. package/dist/client/settings/SettingsPanel.js +41 -191
  55. package/dist/client/settings/SettingsPanel.js.map +1 -1
  56. package/dist/client/settings/agent-settings-search.d.ts +16 -0
  57. package/dist/client/settings/agent-settings-search.d.ts.map +1 -0
  58. package/dist/client/settings/agent-settings-search.js +178 -0
  59. package/dist/client/settings/agent-settings-search.js.map +1 -0
  60. package/dist/client/settings/index.d.ts +1 -0
  61. package/dist/client/settings/index.d.ts.map +1 -1
  62. package/dist/client/settings/index.js +1 -0
  63. package/dist/client/settings/index.js.map +1 -1
  64. package/dist/collab/routes.d.ts +1 -1
  65. package/dist/demo/actions/toggle-demo-mode.js +2 -2
  66. package/dist/demo/actions/toggle-demo-mode.js.map +1 -1
  67. package/dist/demo/config.js +6 -6
  68. package/dist/demo/config.js.map +1 -1
  69. package/dist/demo/fetch-interceptor.d.ts +1 -0
  70. package/dist/demo/fetch-interceptor.d.ts.map +1 -1
  71. package/dist/demo/fetch-interceptor.js +19 -2
  72. package/dist/demo/fetch-interceptor.js.map +1 -1
  73. package/dist/demo/redact.d.ts +14 -5
  74. package/dist/demo/redact.d.ts.map +1 -1
  75. package/dist/demo/redact.js +30 -56
  76. package/dist/demo/redact.js.map +1 -1
  77. package/dist/provider-api/corpus-jobs.d.ts +2 -2
  78. package/package.json +2 -2
@@ -1,13 +1,15 @@
1
1
  /**
2
2
  * Pure, dependency-free, deterministic demo-mode redactor.
3
3
  *
4
- * Replaces sensitive values (names, emails, and free numbers) with stable fake
4
+ * Replaces sensitive values (emails and free numbers) with stable fake
5
5
  * substitutes so a demo looks coherent — the same input always maps to the
6
6
  * same fake. Crucially, it NEVER rewrites identifiers, structural tokens, or
7
- * timestamps: under-redaction is safe, corrupting an ID is not. The string
8
- * redactor uses a protect-first strategy (mask IDs with opaque placeholders
9
- * before any transform runs, restore them byte-identical afterwards), and the
10
- * structure-aware walker additionally protects leaf values by key name.
7
+ * timestamps. Names and other free text are deliberately left alone because
8
+ * guessing whether arbitrary text is a person's name is too inaccurate. The
9
+ * string redactor uses a protect-first strategy (mask IDs with opaque
10
+ * placeholders before any transform runs, restore them byte-identical
11
+ * afterwards), and the structure-aware walker additionally protects leaf
12
+ * values by key name.
11
13
  */
12
14
  /* ------------------------------------------------------------------ *
13
15
  * Seeded hash + PRNG (xmur3 seed → mulberry32 stream)
@@ -56,7 +58,7 @@ function pick(rng, pool) {
56
58
  * round-trips back through redaction (e.g. you edit a draft that's
57
59
  * already showing fake text, it autosaves, then refetches) it is passed
58
60
  * through UNCHANGED instead of being re-faked into something new. This
59
- * is what stops names/emails drifting on every edit.
61
+ * is what stops emails drifting on every edit.
60
62
  *
61
63
  * Leak-free by construction: no timers, a hard size cap, and lazy TTL purge
62
64
  * on write. Works the same per-tab in the browser and per-process on the
@@ -223,14 +225,6 @@ const LAST_NAMES = [
223
225
  /* ------------------------------------------------------------------ *
224
226
  * Fake value generators (deterministic in value + salt)
225
227
  * ------------------------------------------------------------------ */
226
- function fakeName(original, salt) {
227
- return memoFake("name", original, salt, () => {
228
- const rng = seededRng(`name:${original}`, salt);
229
- const first = pick(rng, FIRST_NAMES);
230
- const last = pick(rng, LAST_NAMES);
231
- return `${first} ${last}`;
232
- });
233
- }
234
228
  // Realistic-looking but safe stand-in domains: real consumer providers (the
235
229
  // local part is fake, so no real mailbox is implied) plus the canonical
236
230
  // reserved fictional-company domains (Contoso/Fabrikam/Northwind), which read
@@ -427,9 +421,6 @@ function unprotect(text, restore) {
427
421
  * Transforms (run only on protected text)
428
422
  * ------------------------------------------------------------------ */
429
423
  const EMAIL_RE = /\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}\b/g;
430
- // 2+ capitalized words (each starts uppercase, allows internal lowercase /
431
- // apostrophes / hyphens). Single-letter middle initials allowed: `Sarah J Connor`.
432
- const FULL_NAME_RE = /\b[A-Z][a-zA-Z'’-]*(?:\s+[A-Z](?:[a-zA-Z'’-]*)?){1,3}\b/g;
433
424
  // A standalone numeric token: optional currency + sign, digits with optional
434
425
  // comma grouping and a single optional decimal part. Bounded so it is NOT
435
426
  // adjacent to a letter (placeholders already removed letter-mixed tokens, but
@@ -438,9 +429,6 @@ const NUMBER_RE = /(^|[^A-Za-z0-9_])([$€£]?)([+-]?)(\d[\d,]*(?:\.\d+)?)(?![A-
438
429
  function transformEmails(text, salt) {
439
430
  return text.replace(EMAIL_RE, (match) => fakeEmail(match, salt));
440
431
  }
441
- function transformNames(text, salt) {
442
- return text.replace(FULL_NAME_RE, (match) => fakeName(match, salt));
443
- }
444
432
  function isProbablyYear(digits) {
445
433
  if (digits.length !== 4)
446
434
  return false;
@@ -475,19 +463,21 @@ function transformNumbers(text, salt) {
475
463
  /* ------------------------------------------------------------------ *
476
464
  * Public: string redactor
477
465
  * ------------------------------------------------------------------ */
478
- function redactDemoStringInternal(text, salt, redactNames) {
466
+ function redactDemoStringInternal(text, salt, redactNumbers) {
479
467
  if (typeof text !== "string" || text.length === 0)
480
468
  return text;
469
+ if (!text.includes("@") && (!redactNumbers || !/[\d$€£¥]/.test(text))) {
470
+ return text;
471
+ }
481
472
  const { text: masked, restore } = protect(text);
482
473
  let out = masked;
483
474
  out = transformEmails(out, salt);
484
- if (redactNames)
485
- out = transformNames(out, salt);
486
- out = transformNumbers(out, salt);
475
+ if (redactNumbers)
476
+ out = transformNumbers(out, salt);
487
477
  return unprotect(out, restore);
488
478
  }
489
479
  export function redactDemoString(text, opts) {
490
- return redactDemoStringInternal(text, opts?.salt ?? "", true);
480
+ return redactDemoStringInternal(text, opts?.salt ?? "", opts?.redactNumbers !== false);
491
481
  }
492
482
  /* ------------------------------------------------------------------ *
493
483
  * Public: structure-aware redactor
@@ -495,29 +485,11 @@ export function redactDemoString(text, opts) {
495
485
  // Keys whose leaf values must NEVER be transformed (still recurse into nested
496
486
  // objects/arrays under them). Besides ids/urls/timestamps this also covers
497
487
  // code/query-bearing keys (`sql`, `query`, `expression`, `formula`, `code`):
498
- // prose-redacting a SQL string injects fake emails/numbers into it and rewrites
499
- // its leading tokens, producing `syntax error at or near "Henry"` /
500
- // `queries must start with SELECT or WITH`. The query must run untouched; its
501
- // RESULTS are what get redacted so the chart still shows fake values.
488
+ // redacting a SQL string mutates literals and can make the query semantically
489
+ // wrong or invalid. The query must run untouched; its RESULTS are what get
490
+ // redacted so the chart still shows fake values.
502
491
  const PROTECTED_KEY_RE = /^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;
503
- // UI labels and chart/dashboard titles often look like person names ("Maya
504
- // Davis (First-party)", "Clicks by Henry Moore") but are structural labels. Keep
505
- // them stable while still redacting free-text and contact-style person fields.
506
- const LABEL_TEXT_KEY_RE = /^(name|title|label)$/i;
507
492
  const MAX_DEPTH = 64;
508
- function isStandalonePersonNameLabel(text) {
509
- const trimmed = text.trim();
510
- if (!trimmed)
511
- return false;
512
- return /^[A-Z][a-zA-Z'’-]+(?:\s+[A-Z]\.?)?\s+[A-Z][a-zA-Z'’-]+$/.test(trimmed);
513
- }
514
- function shouldRedactNamesForChild(key, entry) {
515
- if (!LABEL_TEXT_KEY_RE.test(key))
516
- return true;
517
- // Preserve structural labels/titles, but do not let common contact-shaped
518
- // fields like `{ name: "Olivia Parker" }` leak real person names.
519
- return typeof entry === "string" ? isStandalonePersonNameLabel(entry) : true;
520
- }
521
493
  function isPlainObject(value) {
522
494
  if (value === null || typeof value !== "object")
523
495
  return false;
@@ -528,9 +500,6 @@ function isPlainObject(value) {
528
500
  const proto = Object.getPrototypeOf(value);
529
501
  return proto === Object.prototype || proto === null;
530
502
  }
531
- function redactDemoStringWithSalt(text, salt, redactNames) {
532
- return redactDemoStringInternal(text, salt, redactNames);
533
- }
534
503
  function redactNumberLeaf(value, salt) {
535
504
  if (!Number.isFinite(value))
536
505
  return value;
@@ -545,17 +514,17 @@ function redactNumberLeaf(value, salt) {
545
514
  return value;
546
515
  return n;
547
516
  }
548
- function walk(value, salt, depth, seen, redactNames) {
517
+ function walk(value, salt, depth, seen, redactNumbers, redactProtectedEmails) {
549
518
  if (depth > MAX_DEPTH)
550
519
  return value;
551
520
  if (value === null || value === undefined)
552
521
  return value;
553
522
  const t = typeof value;
554
523
  if (t === "string") {
555
- return redactDemoStringWithSalt(value, salt, redactNames);
524
+ return redactDemoStringInternal(value, salt, redactNumbers);
556
525
  }
557
526
  if (t === "number") {
558
- return redactNumberLeaf(value, salt);
527
+ return redactNumbers ? redactNumberLeaf(value, salt) : value;
559
528
  }
560
529
  if (t === "boolean" || t === "bigint" || t === "function" || t === "symbol") {
561
530
  return value;
@@ -566,7 +535,7 @@ function walk(value, salt, depth, seen, redactNames) {
566
535
  if (seen.has(value))
567
536
  return value;
568
537
  seen.add(value);
569
- const out = value.map((item) => walk(item, salt, depth + 1, seen, redactNames));
538
+ const out = value.map((item) => walk(item, salt, depth + 1, seen, redactNumbers, redactProtectedEmails));
570
539
  seen.delete(value);
571
540
  return out;
572
541
  }
@@ -583,7 +552,12 @@ function walk(value, salt, depth, seen, redactNames) {
583
552
  !(entry instanceof Date)) {
584
553
  // Still recurse into nested structures, but the protected key does
585
554
  // not transform its own leaf value.
586
- out[key] = walk(entry, salt, depth + 1, seen, redactNames);
555
+ out[key] = walk(entry, salt, depth + 1, seen, redactNumbers, redactProtectedEmails);
556
+ }
557
+ else if (redactProtectedEmails &&
558
+ typeof entry === "string" &&
559
+ entry.includes("@")) {
560
+ out[key] = redactDemoStringInternal(entry, salt, false);
587
561
  }
588
562
  else {
589
563
  // Leaf under a protected key: pass through completely untouched.
@@ -591,7 +565,7 @@ function walk(value, salt, depth, seen, redactNames) {
591
565
  }
592
566
  continue;
593
567
  }
594
- out[key] = walk(entry, salt, depth + 1, seen, redactNames && shouldRedactNamesForChild(key, entry));
568
+ out[key] = walk(entry, salt, depth + 1, seen, redactNumbers, redactProtectedEmails);
595
569
  }
596
570
  seen.delete(value);
597
571
  return out;
@@ -601,7 +575,7 @@ function walk(value, salt, depth, seen, redactNames) {
601
575
  }
602
576
  export function redactDemoData(value, opts) {
603
577
  const salt = opts?.salt ?? "";
604
- return walk(value, salt, 0, new WeakSet(), true);
578
+ return walk(value, salt, 0, new WeakSet(), opts?.redactNumbers !== false, opts?.redactProtectedEmails === true);
605
579
  }
606
580
  /**
607
581
  * Clear the stable-mapping caches. Test-only — the caches are process-global
@@ -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, and 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}${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}${salt}${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// 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) => fakeName(match, salt));\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\nfunction redactDemoStringInternal(\n text: string,\n salt: string,\n redactNames: boolean,\n): string {\n if (typeof text !== \"string\" || text.length === 0) return text;\n\n const { text: masked, restore } = protect(text);\n let out = masked;\n out = transformEmails(out, salt);\n if (redactNames) out = transformNames(out, salt);\n out = transformNumbers(out, salt);\n return unprotect(out, restore);\n}\n\nexport function redactDemoString(text: string, opts?: RedactOptions): string {\n return redactDemoStringInternal(text, opts?.salt ?? \"\", true);\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 emails/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 values.\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// UI labels and chart/dashboard titles often look like person names (\"Maya\n// Davis (First-party)\", \"Clicks by Henry Moore\") but are structural labels. Keep\n// them stable while still redacting free-text and contact-style person fields.\nconst LABEL_TEXT_KEY_RE = /^(name|title|label)$/i;\n\nconst MAX_DEPTH = 64;\n\nfunction isStandalonePersonNameLabel(text: string): boolean {\n const trimmed = text.trim();\n if (!trimmed) return false;\n return /^[A-Z][a-zA-Z'’-]+(?:\\s+[A-Z]\\.?)?\\s+[A-Z][a-zA-Z'’-]+$/.test(\n trimmed,\n );\n}\n\nfunction shouldRedactNamesForChild(key: string, entry: unknown): boolean {\n if (!LABEL_TEXT_KEY_RE.test(key)) return true;\n // Preserve structural labels/titles, but do not let common contact-shaped\n // fields like `{ name: \"Olivia Parker\" }` leak real person names.\n return typeof entry === \"string\" ? isStandalonePersonNameLabel(entry) : true;\n}\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 redactDemoStringWithSalt(\n text: string,\n salt: string,\n redactNames: boolean,\n): string {\n return redactDemoStringInternal(text, salt, redactNames);\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 redactNames: 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 return redactDemoStringWithSalt(value as string, salt, redactNames);\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, redactNames),\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 transform its own leaf value.\n out[key] = walk(entry, salt, depth + 1, seen, redactNames);\n } else {\n // Leaf under a protected key: pass through completely untouched.\n out[key] = entry;\n }\n continue;\n }\n out[key] = walk(\n entry,\n salt,\n depth + 1,\n seen,\n redactNames && shouldRedactNamesForChild(key, entry),\n );\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>(), true) 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
+ {"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 (emails and 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. Names and other free text are deliberately left alone because\n * guessing whether arbitrary text is a person's name is too inaccurate. The\n * string redactor uses a protect-first strategy (mask IDs with opaque\n * placeholders before any transform runs, restore them byte-identical\n * afterwards), and the structure-aware walker additionally protects leaf\n * values by key name.\n */\n\nexport interface RedactOptions {\n salt?: string;\n /** Redact numeric values. Defaults to true for backward compatibility. */\n redactNumbers?: boolean;\n /**\n * Redact emails stored in otherwise protected structural fields such as\n * `userId` and `userKey`. Intended for display-only frontend responses.\n */\n redactProtectedEmails?: boolean;\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}${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 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}${salt}${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\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// 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 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\nfunction redactDemoStringInternal(\n text: string,\n salt: string,\n redactNumbers: boolean,\n): string {\n if (typeof text !== \"string\" || text.length === 0) return text;\n if (!text.includes(\"@\") && (!redactNumbers || !/[\\d$€£¥]/.test(text))) {\n return text;\n }\n\n const { text: masked, restore } = protect(text);\n let out = masked;\n out = transformEmails(out, salt);\n if (redactNumbers) out = transformNumbers(out, salt);\n return unprotect(out, restore);\n}\n\nexport function redactDemoString(text: string, opts?: RedactOptions): string {\n return redactDemoStringInternal(\n text,\n opts?.salt ?? \"\",\n opts?.redactNumbers !== false,\n );\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// redacting a SQL string mutates literals and can make the query semantically\n// wrong or invalid. The query must run untouched; its RESULTS are what get\n// redacted so the chart still shows fake values.\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\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 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 redactNumbers: boolean,\n redactProtectedEmails: 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 return redactDemoStringInternal(value as string, salt, redactNumbers);\n }\n\n if (t === \"number\") {\n return redactNumbers ? redactNumberLeaf(value as number, salt) : value;\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, redactNumbers, redactProtectedEmails),\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 transform its own leaf value.\n out[key] = walk(\n entry,\n salt,\n depth + 1,\n seen,\n redactNumbers,\n redactProtectedEmails,\n );\n } else if (\n redactProtectedEmails &&\n typeof entry === \"string\" &&\n entry.includes(\"@\")\n ) {\n out[key] = redactDemoStringInternal(entry, salt, false);\n } else {\n // Leaf under a protected key: pass through completely untouched.\n out[key] = entry;\n }\n continue;\n }\n out[key] = walk(\n entry,\n salt,\n depth + 1,\n seen,\n redactNumbers,\n redactProtectedEmails,\n );\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(\n value,\n salt,\n 0,\n new WeakSet<object>(),\n opts?.redactNumbers !== false,\n opts?.redactProtectedEmails === true,\n ) 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"]}
@@ -73,6 +73,7 @@ export declare function createProviderCorpusJobAction(options: CreateProviderCor
73
73
  offset?: unknown;
74
74
  limit?: unknown;
75
75
  }, {
76
+ deleted?: undefined;
76
77
  jobs: {
77
78
  id: string;
78
79
  name: string;
@@ -83,7 +84,6 @@ export declare function createProviderCorpusJobAction(options: CreateProviderCor
83
84
  updatedAt: string;
84
85
  }[];
85
86
  total: number;
86
- deleted?: undefined;
87
87
  jobId?: undefined;
88
88
  } | {
89
89
  jobs?: undefined;
@@ -91,9 +91,9 @@ export declare function createProviderCorpusJobAction(options: CreateProviderCor
91
91
  deleted: boolean;
92
92
  jobId: string;
93
93
  } | {
94
+ deleted?: undefined;
94
95
  jobs?: undefined;
95
96
  total?: undefined;
96
- deleted?: undefined;
97
97
  jobId?: undefined;
98
98
  hits: Record<string, unknown>[];
99
99
  offset: number;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agent-native/core",
3
- "version": "0.98.2",
3
+ "version": "0.98.4",
4
4
  "description": "Framework for agent-native application development — where AI agents and UI share SQL state, actions, and context",
5
5
  "homepage": "https://github.com/BuilderIO/agent-native#readme",
6
6
  "bugs": {
@@ -333,7 +333,7 @@
333
333
  "y-protocols": "^1.0.7",
334
334
  "yjs": "^13.6.31",
335
335
  "zod": "^4.3.6",
336
- "@agent-native/toolkit": "^0.4.7"
336
+ "@agent-native/toolkit": "^0.4.8"
337
337
  },
338
338
  "devDependencies": {
339
339
  "@ai-sdk/anthropic": "^3.0.71",