Rich text / foldkit-richtext

Loading and limits

Use decodeDocument(input, limits?) at a persistence boundary. It rejects excess fields, duplicate IDs, empty or duplicate marks, invalid structure, unknown nodes, and unsupported versions — but preserves unknown mark strings verbatim and round-trips them. findUnknownMarks(document) lists them per text run for a publishing gate; unknown nodes are still rejected, not preserved. Then enforces limits (default DefaultDocumentLimits: 10,000 blocks, 50,000 text runs, 5,000,000 UTF-16 text units). A violation throws an Error naming the exceeded bound. Tighten per document with { ...DefaultDocumentLimits, maxBlocks: 100 }. The exported Schemas also compose into an application's Model; when decoding them directly, pass { onExcessProperty: 'error' } for the same strict policy.

apply does not enforce limits: size-check untrusted operation payloads (notably inserted text) before applying, and apply byte-size limits before decoding untrusted payloads. Replicated is the pure core of collaboration; examples/pages wires it to foldkit-sync and foldkit-durable. Retain rejected source content for recovery; do not replace it with an empty document.

A transaction validates only the top-level blocks it has not seen validated before, so a container is checked again whenever anything inside it changes, and indexes its text runs. Edits copy the affected arrays and preserve untouched nodes. The performance section records the measured cases, the collaborative path included (bench/replicated.bench.ts).