/** * Serialize arbitrary AAF `globalEvent` payloads for display. Plain * `JSON.stringify` throws on BigInt and circular graphs; `String(obj)` becomes * `[object Object]`, which is what users saw for events like * `customerStateChange`, `callEnded`, etc. */ function isTypedArray(v: unknown): v is ArrayBufferView { return ( typeof ArrayBuffer !== "undefined" && ArrayBuffer.isView(v) && !(v instanceof DataView) ); } /** Deep-clone into JSON-safe values (Lossy by design — for debugging only.) */ export function toPrintableJson(value: unknown, seen: WeakSet = new WeakSet()): unknown { if (value === undefined) return undefined; if (value === null) return null; const t = typeof value; if (t === "string" || t === "number" || t === "boolean") return value; if (t === "bigint") return value.toString(); if (t === "symbol") return String(value); if (t === "function") return `[Function ${(value as Function).name || "anonymous"}]`; if (value instanceof Date) return value.toISOString(); if (t === "object") { if (seen.has(value as object)) return "[Circular]"; seen.add(value as object); try { if (Array.isArray(value)) { return value.map((item) => toPrintableJson(item, seen)); } if (isTypedArray(value)) { return Array.from(value as unknown as ArrayLike); } if (value instanceof Map) { const o: Record = {}; value.forEach((v, k) => { o[String(k)] = toPrintableJson(v, seen); }); return o; } if (value instanceof Set) { return Array.from(value, (item) => toPrintableJson(item, seen)); } const out: Record = {}; for (const key of Reflect.ownKeys(value as object)) { if (typeof key === "symbol") { out[`[${String(key)}]`] = toPrintableJson((value as Record)[key], seen); continue; } try { const v = (value as Record)[key as string]; out[String(key)] = toPrintableJson(v, seen); } catch { out[String(key)] = "[Unserializable]"; } } return out; } finally { seen.delete(value as object); } } return String(value); } /** Pretty-print any value for UI / clipboard (never returns `[object Object]` for plain objects). */ export function safeJson(value: unknown): string { if (value === undefined) return "undefined"; try { const printable = toPrintableJson(value); return JSON.stringify(printable, null, 2); } catch { try { return JSON.stringify(value, (_k, v) => { if (typeof v === "bigint") return v.toString(); return v; }, 2); } catch { return String(value); } } }