| 112 | // --------------------------------------------------------------------------- |
| 113 | |
| 114 | export function sanitizeForTrace(value: unknown, depth = 0): unknown { |
| 115 | if (value === null || value === undefined) return value; |
| 116 | const t = typeof value; |
| 117 | if (t === 'number' || t === 'boolean') return value; |
| 118 | if (t === 'string') { |
| 119 | const s = value as string; |
| 120 | if (BASE64ISH_RE.test(s)) return `[base64-like, ${s.length} chars omitted]`; |
| 121 | if (s.length > MAX_STRING) return `${s.slice(0, MAX_STRING)}… [+${s.length - MAX_STRING} chars]`; |
| 122 | return s; |
| 123 | } |
| 124 | if (t !== 'object') return String(value as bigint | symbol | (() => unknown)); |
| 125 | if (depth >= MAX_DEPTH) return '[max depth]'; |
| 126 | if (Array.isArray(value)) { |
| 127 | const out: unknown[] = value |
| 128 | .slice(0, MAX_ARRAY_ITEMS) |
| 129 | .map((v) => sanitizeForTrace(v, depth + 1)); |
| 130 | if (value.length > MAX_ARRAY_ITEMS) out.push(`[+${value.length - MAX_ARRAY_ITEMS} more items]`); |
| 131 | return out; |
| 132 | } |
| 133 | const out: Record<string, unknown> = {}; |
| 134 | for (const [k, v] of Object.entries(value as Record<string, unknown>)) { |
| 135 | out[k] = SENSITIVE_KEY_RE.test(k) ? '[redacted]' : sanitizeForTrace(v, depth + 1); |
| 136 | } |
| 137 | return out; |
| 138 | } |
| 139 | |
| 140 | /** Sanitize, then hard-cap the serialized size of one entry's detail. */ |
| 141 | function detailOf(value: unknown): unknown { |