/** * Pure helpers extracted from the docmost-sync Phase-0 idempotency harness * (`src/roundtrip.ts`). Only the IO-free comparison utilities are vendored — * the CLI scaffold (`--fixture`/`--page`/`--corpus`, `loadSettings`, the * `DocmostClient` live path and `process.exit`) is NOT vendored (plan §2.1: * the roundtrip harness moves into the package's tests, not the engine). */ /** * Recursively strip every `attrs.id` from a ProseMirror node tree. Block ids * are regenerated by `markdownToProseMirror` (SPEC §11), so they must be * ignored when comparing the semantic shape of two documents. Returns a NEW * tree; the input is not mutated. */ export function stripBlockIds(node: any): any { if (Array.isArray(node)) { return node.map(stripBlockIds); } if (node && typeof node === "object") { const out: any = {}; for (const key of Object.keys(node)) { if (key === "attrs" && node.attrs && typeof node.attrs === "object") { // Drop the `id` attr; keep every other attribute. const { id, ...rest } = node.attrs as Record; void id; out.attrs = stripBlockIds(rest); } else { out[key] = stripBlockIds(node[key]); } } return out; } return node; } /** * Find the first divergence between two values via a recursive deep compare. * Returns a short path + the two differing values, or null if they are equal. */ export function firstDivergence( a: any, b: any, path = "$", ): { path: string; a: any; b: any } | null { if (a === b) return null; const ta = typeof a; const tb = typeof b; if (ta !== tb || a === null || b === null) { return { path, a, b }; } if (ta !== "object") { return { path, a, b }; } const aIsArr = Array.isArray(a); const bIsArr = Array.isArray(b); if (aIsArr !== bIsArr) return { path, a, b }; if (aIsArr) { if (a.length !== b.length) { return { path: `${path}.length`, a: a.length, b: b.length }; } for (let i = 0; i < a.length; i++) { const d = firstDivergence(a[i], b[i], `${path}[${i}]`); if (d) return d; } return null; } const keys = new Set([...Object.keys(a), ...Object.keys(b)]); for (const k of keys) { const d = firstDivergence(a[k], b[k], `${path}.${k}`); if (d) return d; } return null; }