import { canonicalizeForReceipt } from "../receipt"; export function renderAdapterJson(value: unknown): string { return canonicalizeForReceipt(toAdapterJsonValue(value)); } export function cloneAdapterJsonValue(value: T): T { return JSON.parse(renderAdapterJson(value)) as T; } function toAdapterJsonValue(value: unknown): unknown { if (value === null) { return null; } switch (typeof value) { case "boolean": case "string": return value; case "number": if (!Number.isFinite(value)) { throw new TypeError("adapter JSON cannot contain non-finite numbers"); } return value; case "object": if (Array.isArray(value)) { return value.map((item) => { if (item === undefined) { throw new TypeError("adapter JSON arrays cannot contain undefined"); } return toAdapterJsonValue(item); }); } if (!isPlainRecord(value)) { throw new TypeError("adapter JSON cannot contain non-plain objects"); } return normalizeRecord(value); case "undefined": case "bigint": case "function": case "symbol": throw new TypeError(`adapter JSON cannot contain ${typeof value}`); } } function normalizeRecord(value: Readonly>): unknown { const normalized: Record = {}; for (const key of Object.keys(value)) { const item = value[key]; if (item !== undefined) { normalized[key] = toAdapterJsonValue(item); } } return normalized; } function isPlainRecord(value: unknown): value is Readonly> { if (typeof value !== "object" || value === null || Array.isArray(value)) { return false; } const prototype = Object.getPrototypeOf(value); return prototype === Object.prototype || prototype === null; }