/** * Canonical JSON serialization: sorted keys at every level, 2-space indent, trailing newline. */ export type JsonPrimitive = string | number | boolean | null; export type JsonValue = JsonPrimitive | JsonValue[] | { [key: string]: JsonValue }; function sortKeys(value: unknown): unknown { if (value === null || typeof value !== 'object') return value; if (Array.isArray(value)) return value.map(sortKeys); const sorted: Record = {}; for (const key of Object.keys(value as Record).sort()) { sorted[key] = sortKeys((value as Record)[key]); } return sorted; } export function stringifyCanonical(value: unknown): string { return JSON.stringify(sortKeys(value), null, 2) + '\n'; }