Complete executable lifecycle
This canonical module migrates a frozen v1 document, serializes it canonically, reparses the bytes through validation, proves the round trip is stable, and computes its semantic SHA-256.examples/docs/document-canonicalization-and-migration.ts.
Serialize deterministically
maxDocumentBytes
(64 MiB by default) throws TypeError instead of being materialized. Pass
{ limits: { maxDocumentBytes } } to choose another ceiling; malformed limits
also throw TypeError. Output within the ceiling is byte-for-byte unchanged,
including the frozen writer’s omission of an own __proto__ key. The writer
does not otherwise validate its input, so the ceiling assumes plain data whose
property reads are stable, such as builder output or a parsed document. It does
reject values JSON cannot preserve: non-finite numbers, undefined array
items, functions, symbols, and BigInts, and any object that is not an array, a
plain object (from any realm), or a null-prototype object, such as a Map,
Set, Date, typed array, or class instance, throw TypeError instead of
being written as {}.
hashDocument and cloneDocument share this writer.
Parse untrusted text
parseDocument requires a primitive string and measures its UTF-8 size before
JSON.parse, stopping at maxDocumentBytes without encoding the text. It then
captures a bounded plain snapshot and audits the raw text: an object that
repeats a member name, including an escape-equivalent spelling such as "name"
and "\u006eame", is rejected with IR_INVALID and
details.reason: "duplicate-json-member" instead of silently keeping the last
value. The raw structural and nesting ceilings also charge subtrees that
last-key-wins parsing would have erased. Finally it validates the matching
frozen version schema and graph semantics, and deep-freezes the result.
Use parseDocumentValue(value) when another parser already produced an unknown
JavaScript value. It still performs the bounded snapshot and schema checks, but
it cannot detect member names that the upstream parser already collapsed, and
neither can migrateDocument(value). Pass the original text to parseDocument
at boundaries that must reject ambiguous JSON.
Do not cast
Clone
options.limits to both steps. It is appropriate when a detached frozen copy is
needed; it is not a mutable editing API.
Migrate
Migration preserves admitted authored data from versions 1 through 6 and moves it to the current schema. It does not:- invent information a prior version could not express
- recapture topology evidence
- upgrade a stored persistent-reference protocol
- rewrite kernel fingerprints
- execute geometry
Hash a document
By default, root metadata is excluded so descriptive root changes do not alter the semantic document hash. Node metadata and every modeled field follow the protocol’s canonical representation. Pass{ includeMetadata: true } when the
root metadata is intentionally part of the hash. Like stringifyDocument,
hashDocument rejects canonical bytes above limits.maxDocumentBytes before
materializing them; the returned promise rejects with TypeError.
Schema identifiers
Theschema URI and integer version are protocol identifiers. Parsers do not
dereference the URI over the network. Treat both as exact values, not as a URL
from which runtime code should be downloaded.
Imported-body documents are not v1–v6 documents
The functions on this page read, write, hash, migrate, and evaluate Documents v1 through v6. The public imported-body workflow writes a fixed, minimal Document v7 subset instead (see Imported-body document format), and those bytes have their own entry points:parseImportedBodyDocument,
stringifyImportedBodyDocument, and Evaluator.evaluateImportedBody.
Passing Document v7 JSON, including a parsed imported-body document, to
parseDocument, parseDocumentValue, validateDocument, migrateDocument,
or Evaluator.evaluate returns one IR_INVALID diagnostic at / instead of
reinterpreting it. stringifyDocument, hashDocument, and cloneDocument
throw a TypeError with the same message. migrateDocument never produces
v7.
Editing strategy
InvariantCAD 0.2 exposes a builder for authoring and parsers for validated documents. It does not expose an in-place mutable document editor. To revise a stored document programmatically, use a reviewed transformation that produces a new value, then pass it throughparseDocumentValue before persistence or
evaluation.