/** * @module utils/jsonSafeIndexMetadata * @description The metadata-index crossing's JSON-safety law, as a leaf * function both the coordinator and the transaction operations share. * * The seam's metadata is JSON-safe BY CONTRACT (a native provider serializes * it; u64 ints as Number corrupt above 2^53) — but `resolveVerbEndpointInts` * MIRRORS the resolved endpoint ints onto the verb object itself as BigInt * (`verb.sourceInt`/`targetInt`), so a verb object reused as index metadata * carries BigInts into JSON.stringify, which throws, aborting the whole * transaction. Endpoint ints ride their OWN op params on the graph legs — the * metadata crossing drops every BigInt-valued top-level key instead of * guessing at a lossy numeric encoding. * * WHY THIS IS A LEAF MODULE, ENFORCED AT THE CROSSING: sanitizing only at * operation-construction time is not enough. `transact()`'s delete legs pass * the SAME verb object to both the graph-retraction op (whose endpoint-int * thunk deliberately resolves at EXECUTE time, for same-batch forward refs) * and the metadata-retraction op. At plan time the verb is still clean, so a * plan-time sanitize returns the same reference — then the graph op executes * first, mirrors the BigInt ints onto the shared object, and the metadata op * crosses the seam with them (found by the first fleet adoption of the native * pair: every transact-wrapped edge delete aborted). The crossing itself is * the only place ordering cannot bypass. */ /** * A JSON-safe view of a record bound for the metadata-index crossing. * * @param metadata - The candidate index-metadata record. * @returns The same object when already JSON-safe, else a shallow copy * without the BigInt-valued keys. */ export function jsonSafeIndexMetadata(metadata: unknown): unknown { if (metadata === null || typeof metadata !== 'object') return metadata const rec = metadata as Record let hasBigint = false for (const k in rec) { if (typeof rec[k] === 'bigint') { hasBigint = true; break } } if (!hasBigint) return metadata const out: Record = {} for (const k in rec) { if (typeof rec[k] !== 'bigint') out[k] = rec[k] } return out }