trickle-observe 0.2.131 → 0.2.133

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.
package/dist/express.js CHANGED
@@ -100,7 +100,8 @@ function sanitizeSample(value, depth = 3) {
100
100
  if (t === 'function')
101
101
  return `[Function: ${value.name || 'anonymous'}]`;
102
102
  if (Array.isArray(value)) {
103
- return value.slice(0, 5).map(item => sanitizeSample(item, depth - 1));
103
+ // Preserve full depth for the first element (used for type assertions in test-gen)
104
+ return value.slice(0, 5).map((item, i) => sanitizeSample(item, i === 0 ? depth : depth - 1));
104
105
  }
105
106
  if (t === 'object') {
106
107
  if (value instanceof Date)
package/dist/fastify.js CHANGED
@@ -87,7 +87,7 @@ function sanitizeSample(value, depth = 3) {
87
87
  if (t === 'function')
88
88
  return `[Function: ${value.name || 'anonymous'}]`;
89
89
  if (Array.isArray(value)) {
90
- return value.slice(0, 5).map(item => sanitizeSample(item, depth - 1));
90
+ return value.slice(0, 5).map((item, i) => sanitizeSample(item, i === 0 ? depth : depth - 1));
91
91
  }
92
92
  if (t === 'object') {
93
93
  if (value instanceof Date)
package/dist/hono.js CHANGED
@@ -97,7 +97,7 @@ function sanitizeSample(value, depth = 3) {
97
97
  if (t === 'function')
98
98
  return `[Function: ${value.name || 'anonymous'}]`;
99
99
  if (Array.isArray(value)) {
100
- return value.slice(0, 5).map(item => sanitizeSample(item, depth - 1));
100
+ return value.slice(0, 5).map((item, i) => sanitizeSample(item, i === 0 ? depth : depth - 1));
101
101
  }
102
102
  if (t === 'object') {
103
103
  if (value instanceof Date)
package/dist/koa.js CHANGED
@@ -90,7 +90,7 @@ function sanitizeSample(value, depth = 3) {
90
90
  if (t === 'function')
91
91
  return `[Function: ${value.name || 'anonymous'}]`;
92
92
  if (Array.isArray(value)) {
93
- return value.slice(0, 5).map(item => sanitizeSample(item, depth - 1));
93
+ return value.slice(0, 5).map((item, i) => sanitizeSample(item, i === 0 ? depth : depth - 1));
94
94
  }
95
95
  if (t === 'object') {
96
96
  if (value instanceof Date)
package/dist/trace-var.js CHANGED
@@ -240,7 +240,7 @@ function sanitizeVarSample(value, depth = 3) {
240
240
  if (depth <= 0)
241
241
  return '[...]';
242
242
  if (Array.isArray(value)) {
243
- return value.slice(0, 3).map(item => sanitizeVarSample(item, depth - 1));
243
+ return value.slice(0, 20).map(item => sanitizeVarSample(item, depth - 1));
244
244
  }
245
245
  if (t === 'object') {
246
246
  if (value instanceof Date)
@@ -1837,7 +1837,7 @@ ingestUrl) {
1837
1837
  }
1838
1838
  // Add variable tracing if needed — inlined to avoid import resolution issues in Vite SSR.
1839
1839
  if (importInsertions.length > 0 || varInsertions.length > 0 || destructInsertions.length > 0 || reassignInsertions.length > 0 || forLoopInsertions.length > 0 || catchInsertions.length > 0 || funcParamInsertions.length > 0 || jsxExprInsertions.length > 0) {
1840
- prefixLines.push(`if (!globalThis.__trickle_var_tracer) {`, ` const _cache = new Map();`, ` const _sampleCount = new Map();`, ` const _MAX_SAMPLES = 5;`, ` function _inferType(v, d) {`, ` if (d <= 0) return { kind: 'primitive', name: 'unknown' };`, ` if (v === null) return { kind: 'primitive', name: 'null' };`, ` if (v === undefined) return { kind: 'primitive', name: 'undefined' };`, ` const t = typeof v;`, ` if (t === 'string' || t === 'number' || t === 'boolean' || t === 'bigint' || t === 'symbol') return { kind: 'primitive', name: t };`, ` if (t === 'function') return { kind: 'function' };`, ` if (Array.isArray(v)) { return v.length === 0 ? { kind: 'array', element: { kind: 'primitive', name: 'unknown' } } : { kind: 'array', element: _inferType(v[0], d-1) }; }`, ` if (t === 'object') {`, ` if (v instanceof Date) return { kind: 'object', properties: { __date: { kind: 'primitive', name: 'string' } } };`, ` if (v instanceof RegExp) return { kind: 'object', properties: { __regexp: { kind: 'primitive', name: 'string' } } };`, ` if (v instanceof Error) return { kind: 'object', properties: { __error: { kind: 'primitive', name: 'string' } } };`, ` if (v instanceof Promise) return { kind: 'promise', resolved: { kind: 'primitive', name: 'unknown' } };`, ` const props = {}; const keys = Object.keys(v).slice(0, 20);`, ` for (const k of keys) { try { props[k] = _inferType(v[k], d-1); } catch(e) { props[k] = { kind: 'primitive', name: 'unknown' }; } }`, ` return { kind: 'object', properties: props };`, ` }`, ` return { kind: 'primitive', name: 'unknown' };`, ` }`, ` function _sanitize(v, d) {`, ` if (d <= 0) return '[truncated]'; if (v === null || v === undefined) return v; const t = typeof v;`, ` if (t === 'string') return v.length > 100 ? v.substring(0, 100) + '...' : v;`, ` if (t === 'number' || t === 'boolean') return v; if (t === 'bigint') return String(v);`, ` if (t === 'function') return '[Function: ' + (v.name || 'anonymous') + ']';`, ` if (Array.isArray(v)) return v.slice(0, 3).map(i => _sanitize(i, d-1));`, ` if (t === 'object') { if (v instanceof Date) return v.toISOString(); if (v instanceof RegExp) return String(v); if (v instanceof Error) return { error: v.message }; if (v instanceof Promise) return '[Promise]';`, ` const r = {}; const keys = Object.keys(v).slice(0, 10); for (const k of keys) { try { r[k] = _sanitize(v[k], d-1); } catch(e) { r[k] = '[unreadable]'; } } return r; }`, ` return String(v);`, ` }`, ` globalThis.__trickle_var_tracer = function(v, n, l, mod, file) {`, ` try {`, ` const type = _inferType(v, 3);`, ` const th = JSON.stringify(type).substring(0, 32);`, ` const sample = _sanitize(v, 2);`, ` const sv = typeof v === 'object' && v !== null ? JSON.stringify(sample).substring(0, 60) : String(v).substring(0, 60);`, ` const ck = file + ':' + l + ':' + n;`, ` const cnt = _sampleCount.get(ck) || 0;`, ` if (cnt >= _MAX_SAMPLES) return;`, ` const prev = _cache.get(ck);`, ` const now = Date.now();`, ` if (prev && prev.sv === sv && now - prev.ts < 5000) return;`, ` _cache.set(ck, { sv: sv, ts: now });`, ` _sampleCount.set(ck, cnt + 1);`, ` __trickle_send(JSON.stringify({ kind: 'variable', varName: n, line: l, module: mod, file: file, type: type, typeHash: th, sample: sample }));`, ` } catch(e) {}`, ` };`, `}`, `function __trickle_tv(v, n, l) { try { globalThis.__trickle_var_tracer(v, n, l, ${JSON.stringify(moduleName)}, ${JSON.stringify(filename)}); } catch(e) {} }`);
1840
+ prefixLines.push(`if (!globalThis.__trickle_var_tracer) {`, ` const _cache = new Map();`, ` const _sampleCount = new Map();`, ` const _MAX_SAMPLES = 5;`, ` function _inferType(v, d) {`, ` if (d <= 0) return { kind: 'primitive', name: 'unknown' };`, ` if (v === null) return { kind: 'primitive', name: 'null' };`, ` if (v === undefined) return { kind: 'primitive', name: 'undefined' };`, ` const t = typeof v;`, ` if (t === 'string' || t === 'number' || t === 'boolean' || t === 'bigint' || t === 'symbol') return { kind: 'primitive', name: t };`, ` if (t === 'function') return { kind: 'function' };`, ` if (Array.isArray(v)) { return v.length === 0 ? { kind: 'array', element: { kind: 'primitive', name: 'unknown' } } : { kind: 'array', element: _inferType(v[0], d-1) }; }`, ` if (t === 'object') {`, ` if (v instanceof Date) return { kind: 'object', properties: { __date: { kind: 'primitive', name: 'string' } } };`, ` if (v instanceof RegExp) return { kind: 'object', properties: { __regexp: { kind: 'primitive', name: 'string' } } };`, ` if (v instanceof Error) return { kind: 'object', properties: { __error: { kind: 'primitive', name: 'string' } } };`, ` if (v instanceof Promise) return { kind: 'promise', resolved: { kind: 'primitive', name: 'unknown' } };`, ` const props = {}; const keys = Object.keys(v).slice(0, 20);`, ` for (const k of keys) { try { props[k] = _inferType(v[k], d-1); } catch(e) { props[k] = { kind: 'primitive', name: 'unknown' }; } }`, ` return { kind: 'object', properties: props };`, ` }`, ` return { kind: 'primitive', name: 'unknown' };`, ` }`, ` function _sanitize(v, d) {`, ` if (d <= 0) return '[truncated]'; if (v === null || v === undefined) return v; const t = typeof v;`, ` if (t === 'string') return v.length > 100 ? v.substring(0, 100) + '...' : v;`, ` if (t === 'number' || t === 'boolean') return v; if (t === 'bigint') return String(v);`, ` if (t === 'function') return '[Function: ' + (v.name || 'anonymous') + ']';`, ` if (Array.isArray(v)) return v.slice(0, 20).map(i => _sanitize(i, d-1));`, ` if (t === 'object') { if (v instanceof Date) return v.toISOString(); if (v instanceof RegExp) return String(v); if (v instanceof Error) return { error: v.message }; if (v instanceof Promise) return '[Promise]';`, ` const r = {}; const keys = Object.keys(v).slice(0, 10); for (const k of keys) { try { r[k] = _sanitize(v[k], d-1); } catch(e) { r[k] = '[unreadable]'; } } return r; }`, ` return String(v);`, ` }`, ` globalThis.__trickle_var_tracer = function(v, n, l, mod, file) {`, ` try {`, ` const type = _inferType(v, 3);`, ` const th = JSON.stringify(type).substring(0, 32);`, ` const sample = _sanitize(v, 2);`, ` const sv = typeof v === 'object' && v !== null ? JSON.stringify(sample).substring(0, 60) : String(v).substring(0, 60);`, ` const ck = file + ':' + l + ':' + n;`, ` const cnt = _sampleCount.get(ck) || 0;`, ` if (cnt >= _MAX_SAMPLES) return;`, ` const prev = _cache.get(ck);`, ` const now = Date.now();`, ` if (prev && prev.sv === sv && now - prev.ts < 5000) return;`, ` _cache.set(ck, { sv: sv, ts: now });`, ` _sampleCount.set(ck, cnt + 1);`, ` __trickle_send(JSON.stringify({ kind: 'variable', varName: n, line: l, module: mod, file: file, type: type, typeHash: th, sample: sample }));`, ` } catch(e) {}`, ` };`, `}`, `function __trickle_tv(v, n, l) { try { globalThis.__trickle_var_tracer(v, n, l, ${JSON.stringify(moduleName)}, ${JSON.stringify(filename)}); } catch(e) {} }`);
1841
1841
  }
1842
1842
  // Add React component render tracker if needed
1843
1843
  if (bodyInsertions.length > 0 || conciseBodyInsertions.length > 0) {
@@ -1824,7 +1824,7 @@ ingestUrl) {
1824
1824
  }
1825
1825
  // Add variable tracing if needed — inlined to avoid import resolution issues in Vite SSR.
1826
1826
  if (importInsertions.length > 0 || varInsertions.length > 0 || destructInsertions.length > 0 || reassignInsertions.length > 0 || forLoopInsertions.length > 0 || catchInsertions.length > 0 || funcParamInsertions.length > 0 || jsxExprInsertions.length > 0) {
1827
- prefixLines.push(`if (!globalThis.__trickle_var_tracer) {`, ` const _cache = new Map();`, ` const _sampleCount = new Map();`, ` const _MAX_SAMPLES = 5;`, ` function _inferType(v, d) {`, ` if (d <= 0) return { kind: 'primitive', name: 'unknown' };`, ` if (v === null) return { kind: 'primitive', name: 'null' };`, ` if (v === undefined) return { kind: 'primitive', name: 'undefined' };`, ` const t = typeof v;`, ` if (t === 'string' || t === 'number' || t === 'boolean' || t === 'bigint' || t === 'symbol') return { kind: 'primitive', name: t };`, ` if (t === 'function') return { kind: 'function' };`, ` if (Array.isArray(v)) { return v.length === 0 ? { kind: 'array', element: { kind: 'primitive', name: 'unknown' } } : { kind: 'array', element: _inferType(v[0], d-1) }; }`, ` if (t === 'object') {`, ` if (v instanceof Date) return { kind: 'object', properties: { __date: { kind: 'primitive', name: 'string' } } };`, ` if (v instanceof RegExp) return { kind: 'object', properties: { __regexp: { kind: 'primitive', name: 'string' } } };`, ` if (v instanceof Error) return { kind: 'object', properties: { __error: { kind: 'primitive', name: 'string' } } };`, ` if (v instanceof Promise) return { kind: 'promise', resolved: { kind: 'primitive', name: 'unknown' } };`, ` const props = {}; const keys = Object.keys(v).slice(0, 20);`, ` for (const k of keys) { try { props[k] = _inferType(v[k], d-1); } catch(e) { props[k] = { kind: 'primitive', name: 'unknown' }; } }`, ` return { kind: 'object', properties: props };`, ` }`, ` return { kind: 'primitive', name: 'unknown' };`, ` }`, ` function _sanitize(v, d) {`, ` if (d <= 0) return '[truncated]'; if (v === null || v === undefined) return v; const t = typeof v;`, ` if (t === 'string') return v.length > 100 ? v.substring(0, 100) + '...' : v;`, ` if (t === 'number' || t === 'boolean') return v; if (t === 'bigint') return String(v);`, ` if (t === 'function') return '[Function: ' + (v.name || 'anonymous') + ']';`, ` if (Array.isArray(v)) return v.slice(0, 3).map(i => _sanitize(i, d-1));`, ` if (t === 'object') { if (v instanceof Date) return v.toISOString(); if (v instanceof RegExp) return String(v); if (v instanceof Error) return { error: v.message }; if (v instanceof Promise) return '[Promise]';`, ` const r = {}; const keys = Object.keys(v).slice(0, 10); for (const k of keys) { try { r[k] = _sanitize(v[k], d-1); } catch(e) { r[k] = '[unreadable]'; } } return r; }`, ` return String(v);`, ` }`, ` globalThis.__trickle_var_tracer = function(v, n, l, mod, file) {`, ` try {`, ` const type = _inferType(v, 3);`, ` const th = JSON.stringify(type).substring(0, 32);`, ` const sample = _sanitize(v, 2);`, ` const sv = typeof v === 'object' && v !== null ? JSON.stringify(sample).substring(0, 60) : String(v).substring(0, 60);`, ` const ck = file + ':' + l + ':' + n;`, ` const cnt = _sampleCount.get(ck) || 0;`, ` if (cnt >= _MAX_SAMPLES) return;`, ` const prev = _cache.get(ck);`, ` const now = Date.now();`, ` if (prev && prev.sv === sv && now - prev.ts < 5000) return;`, ` _cache.set(ck, { sv: sv, ts: now });`, ` _sampleCount.set(ck, cnt + 1);`, ` __trickle_send(JSON.stringify({ kind: 'variable', varName: n, line: l, module: mod, file: file, type: type, typeHash: th, sample: sample }));`, ` } catch(e) {}`, ` };`, `}`, `function __trickle_tv(v, n, l) { try { globalThis.__trickle_var_tracer(v, n, l, ${JSON.stringify(moduleName)}, ${JSON.stringify(filename)}); } catch(e) {} }`);
1827
+ prefixLines.push(`if (!globalThis.__trickle_var_tracer) {`, ` const _cache = new Map();`, ` const _sampleCount = new Map();`, ` const _MAX_SAMPLES = 5;`, ` function _inferType(v, d) {`, ` if (d <= 0) return { kind: 'primitive', name: 'unknown' };`, ` if (v === null) return { kind: 'primitive', name: 'null' };`, ` if (v === undefined) return { kind: 'primitive', name: 'undefined' };`, ` const t = typeof v;`, ` if (t === 'string' || t === 'number' || t === 'boolean' || t === 'bigint' || t === 'symbol') return { kind: 'primitive', name: t };`, ` if (t === 'function') return { kind: 'function' };`, ` if (Array.isArray(v)) { return v.length === 0 ? { kind: 'array', element: { kind: 'primitive', name: 'unknown' } } : { kind: 'array', element: _inferType(v[0], d-1) }; }`, ` if (t === 'object') {`, ` if (v instanceof Date) return { kind: 'object', properties: { __date: { kind: 'primitive', name: 'string' } } };`, ` if (v instanceof RegExp) return { kind: 'object', properties: { __regexp: { kind: 'primitive', name: 'string' } } };`, ` if (v instanceof Error) return { kind: 'object', properties: { __error: { kind: 'primitive', name: 'string' } } };`, ` if (v instanceof Promise) return { kind: 'promise', resolved: { kind: 'primitive', name: 'unknown' } };`, ` const props = {}; const keys = Object.keys(v).slice(0, 20);`, ` for (const k of keys) { try { props[k] = _inferType(v[k], d-1); } catch(e) { props[k] = { kind: 'primitive', name: 'unknown' }; } }`, ` return { kind: 'object', properties: props };`, ` }`, ` return { kind: 'primitive', name: 'unknown' };`, ` }`, ` function _sanitize(v, d) {`, ` if (d <= 0) return '[truncated]'; if (v === null || v === undefined) return v; const t = typeof v;`, ` if (t === 'string') return v.length > 100 ? v.substring(0, 100) + '...' : v;`, ` if (t === 'number' || t === 'boolean') return v; if (t === 'bigint') return String(v);`, ` if (t === 'function') return '[Function: ' + (v.name || 'anonymous') + ']';`, ` if (Array.isArray(v)) return v.slice(0, 20).map(i => _sanitize(i, d-1));`, ` if (t === 'object') { if (v instanceof Date) return v.toISOString(); if (v instanceof RegExp) return String(v); if (v instanceof Error) return { error: v.message }; if (v instanceof Promise) return '[Promise]';`, ` const r = {}; const keys = Object.keys(v).slice(0, 10); for (const k of keys) { try { r[k] = _sanitize(v[k], d-1); } catch(e) { r[k] = '[unreadable]'; } } return r; }`, ` return String(v);`, ` }`, ` globalThis.__trickle_var_tracer = function(v, n, l, mod, file) {`, ` try {`, ` const type = _inferType(v, 3);`, ` const th = JSON.stringify(type).substring(0, 32);`, ` const sample = _sanitize(v, 2);`, ` const sv = typeof v === 'object' && v !== null ? JSON.stringify(sample).substring(0, 60) : String(v).substring(0, 60);`, ` const ck = file + ':' + l + ':' + n;`, ` const cnt = _sampleCount.get(ck) || 0;`, ` if (cnt >= _MAX_SAMPLES) return;`, ` const prev = _cache.get(ck);`, ` const now = Date.now();`, ` if (prev && prev.sv === sv && now - prev.ts < 5000) return;`, ` _cache.set(ck, { sv: sv, ts: now });`, ` _sampleCount.set(ck, cnt + 1);`, ` __trickle_send(JSON.stringify({ kind: 'variable', varName: n, line: l, module: mod, file: file, type: type, typeHash: th, sample: sample }));`, ` } catch(e) {}`, ` };`, `}`, `function __trickle_tv(v, n, l) { try { globalThis.__trickle_var_tracer(v, n, l, ${JSON.stringify(moduleName)}, ${JSON.stringify(filename)}); } catch(e) {} }`);
1828
1828
  }
1829
1829
  // Add React component render tracker if needed
1830
1830
  if (bodyInsertions.length > 0 || conciseBodyInsertions.length > 0) {
@@ -708,6 +708,8 @@ function transformSource(source, url, originalSource) {
708
708
  `const __rq_express = __cr_express(import.meta.url);`,
709
709
  `const __fwExpress = __rq_express('${fiPath}');`,
710
710
  `const ${fi.factoryName} = function(...args) { const a = __OrigExpress(...args); try { __fwExpress.${instrumentFn}(a, { environment: process.env.TRICKLE_ENV || 'development' }); } catch(e) {} return a; };`,
711
+ `Object.keys(__OrigExpress).forEach(k => { ${fi.factoryName}[k] = __OrigExpress[k]; });`,
712
+ `Object.setPrototypeOf(${fi.factoryName}, Object.getPrototypeOf(__OrigExpress));`,
711
713
  );
712
714
  break;
713
715
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "trickle-observe",
3
- "version": "0.2.131",
3
+ "version": "0.2.133",
4
4
  "description": "Zero-code runtime observability for JavaScript/TypeScript. Auto-instruments Express, Fastify, Koa, Hono, OpenAI, Anthropic, Gemini, MCP. Captures functions, variables, LLM calls, agent workflows.",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
package/src/express.ts CHANGED
@@ -72,7 +72,8 @@ function sanitizeSample(value: unknown, depth: number = 3): unknown {
72
72
  if (t === 'function') return `[Function: ${(value as Function).name || 'anonymous'}]`;
73
73
 
74
74
  if (Array.isArray(value)) {
75
- return value.slice(0, 5).map(item => sanitizeSample(item, depth - 1));
75
+ // Preserve full depth for the first element (used for type assertions in test-gen)
76
+ return value.slice(0, 5).map((item, i) => sanitizeSample(item, i === 0 ? depth : depth - 1));
76
77
  }
77
78
 
78
79
  if (t === 'object') {
package/src/fastify.ts CHANGED
@@ -61,7 +61,7 @@ function sanitizeSample(value: unknown, depth: number = 3): unknown {
61
61
  if (t === 'function') return `[Function: ${(value as Function).name || 'anonymous'}]`;
62
62
 
63
63
  if (Array.isArray(value)) {
64
- return value.slice(0, 5).map(item => sanitizeSample(item, depth - 1));
64
+ return value.slice(0, 5).map((item, i) => sanitizeSample(item, i === 0 ? depth : depth - 1));
65
65
  }
66
66
 
67
67
  if (t === 'object') {
package/src/hono.ts CHANGED
@@ -71,7 +71,7 @@ function sanitizeSample(value: unknown, depth: number = 3): unknown {
71
71
  if (t === 'function') return `[Function: ${(value as Function).name || 'anonymous'}]`;
72
72
 
73
73
  if (Array.isArray(value)) {
74
- return value.slice(0, 5).map(item => sanitizeSample(item, depth - 1));
74
+ return value.slice(0, 5).map((item, i) => sanitizeSample(item, i === 0 ? depth : depth - 1));
75
75
  }
76
76
 
77
77
  if (t === 'object') {
package/src/koa.ts CHANGED
@@ -64,7 +64,7 @@ function sanitizeSample(value: unknown, depth: number = 3): unknown {
64
64
  if (t === 'function') return `[Function: ${(value as Function).name || 'anonymous'}]`;
65
65
 
66
66
  if (Array.isArray(value)) {
67
- return value.slice(0, 5).map(item => sanitizeSample(item, depth - 1));
67
+ return value.slice(0, 5).map((item, i) => sanitizeSample(item, i === 0 ? depth : depth - 1));
68
68
  }
69
69
 
70
70
  if (t === 'object') {
package/src/trace-var.ts CHANGED
@@ -226,7 +226,7 @@ function sanitizeVarSample(value: unknown, depth: number = 3): unknown {
226
226
  if (depth <= 0) return '[...]';
227
227
 
228
228
  if (Array.isArray(value)) {
229
- return value.slice(0, 3).map(item => sanitizeVarSample(item, depth - 1));
229
+ return value.slice(0, 20).map(item => sanitizeVarSample(item, depth - 1));
230
230
  }
231
231
 
232
232
  if (t === 'object') {
@@ -1865,7 +1865,7 @@ export function transformEsmSource(
1865
1865
  ` if (t === 'string') return v.length > 100 ? v.substring(0, 100) + '...' : v;`,
1866
1866
  ` if (t === 'number' || t === 'boolean') return v; if (t === 'bigint') return String(v);`,
1867
1867
  ` if (t === 'function') return '[Function: ' + (v.name || 'anonymous') + ']';`,
1868
- ` if (Array.isArray(v)) return v.slice(0, 3).map(i => _sanitize(i, d-1));`,
1868
+ ` if (Array.isArray(v)) return v.slice(0, 20).map(i => _sanitize(i, d-1));`,
1869
1869
  ` if (t === 'object') { if (v instanceof Date) return v.toISOString(); if (v instanceof RegExp) return String(v); if (v instanceof Error) return { error: v.message }; if (v instanceof Promise) return '[Promise]';`,
1870
1870
  ` const r = {}; const keys = Object.keys(v).slice(0, 10); for (const k of keys) { try { r[k] = _sanitize(v[k], d-1); } catch(e) { r[k] = '[unreadable]'; } } return r; }`,
1871
1871
  ` return String(v);`,