/*******************************************************************************
json — utility functions for reading, writing, and stringifying JSON data with integrity-safe formatting.
- Do not handle raw content parsing beyond JSON.
- Do not manage file transport or configuration orchestration.
- RFC-1097: step 6 — compass.migrate codemod run
Mechanical v1 to v2 header migration across the workspace: 942 files rewritten — CHANGE_SUMMARY windows collapsed into history, forbidden v1 blocks stripped, KEY_DECISIONS seeded from @ai-invariant comments (5 files) or TODO placeholders (103 files), blocks reordered to canonical order.
- RFC-1097: sweep — werkstatt-engine clean
Sweep batch 4: 73 Compass headers on headerless engine files (certification, component-runtime, isolation, evolution, testing), real KEY_DECISIONS on 75 files (kernel, cache, dht, swim, gitmesh, runtime), ~80 purpose expansions (CONTRACT-02/PURPOSE-02), non-goals on 13 CONTRACT-03 files, CS-07 history literal fix repo-wide (253 files). Policy: .template.ts/.template.astro excludedPaths. werkstatt-engine now 0 diagnostics.
******************************************************************************/
/**
* JSON utilities with stable stringification for integrity files.
* Provides sorted key output for deterministic file hashes.
*/
import path from "node:path";
import { ensureDir, readText, writeText } from "./fs.ts";
function sortValue(value: unknown): unknown {
if (Array.isArray(value)) {
return value.map(sortValue);
}
if (value && typeof value === "object") {
const entries = Object.entries(value as Record)
.sort(([a], [b]) => a.localeCompare(b))
.map(([key, nested]) => [key, sortValue(nested)]);
return Object.fromEntries(entries);
}
return value;
}
export function stableStringify(value: unknown): string {
return `${JSON.stringify(sortValue(value), null, 2)}\n`;
}
export async function readJsonFile(filePath: string): Promise {
return JSON.parse(await readText(filePath)) as T;
}
export async function writeJsonFile(filePath: string, value: unknown): Promise {
await ensureDir(path.dirname(filePath));
await writeText(filePath, stableStringify(value));
}