npm / loro-crdt
loro-crdt
What changed in loro-crdt 1, from its official release notes, upgrade guides and advisories. Sources checked 2026-10-30.
11 breaking1 security6 new APIs and options3 performance27 fixes and other
See which of these apply to your code, ranked, in your agent:
npx @unstale/cli initloro-crdt 148 changes
Breaking changes 11
The error gains HistoryUnreadable(LoroError) and is non-exhaustive, so downstream exhaustive matches must add a wildcard arm.
This changes from a unit struct to a struct with a private field plus a same-named constant; downstream callers must use Err(_) rather than an exhaustive Err(FrontiersNotIncluded) match.
Fallible history operations return decode errors for unreadable change blocks instead of panicking or wrapping the failure as Unknown; affected Rust methods now return LoroResult.
Other serde deserialization of LoroValue now rejects nesting beyond 128 levels with an error.
Undo and redo return application errors after rolling back failed inverse operations, preserving document state and history for retry; failed operations emit no events.
Failed calls leave the document, history, and subscribers unchanged, while preserving prior uncommitted edits. Applying a diff with a missing tree parent now throws.
Malformed movable-list operations and out-of-bounds Text operations return errors without applying the import or leaving the document unusable. Invalid sequence positions at or above 1,073,741,822 are rejected during decoding.
Imports that reuse existing operation IDs for different content, skip peer counters, or contain invalid operations fail without changing the document; rejected imports leave it usable.
Malformed movable-list operations return errors without applying the import, including when an operation depends on another change in the same import.
Malformed movable-list operations and out-of-bounds Text operations return errors without applying the import or leaving the document unusable.
Shallow and state-only snapshots no longer retain map/list child containers deleted before the shallow root; tree-node metadata remains available for revival.
Security 1
Encoded cursors with negative position counters and directly constructed invalid cursors are rejected with readable decode errors before document history is accessed.
New APIs and options 6
The error is now non-exhaustive and gains a HistoryUnreadable variant for unreadable history.
Exports remote updates waiting for missing causal dependencies in the existing updates format, so they can be persisted and later imported after a separately saved snapshot.
Applications can set a finite decoded container value cache limit; zero disables the cache, lowering the limit safely evicts cached values, and forks inherit an independent local setting.
Reads visible container trees as one JSON string built in Rust, avoiding per-value WASM-to-JS calls while preserving node shapes, IDs, formatting, and root filtering.
The error gains a DecodeError variant.
Pass
{ fullState: true }toapplyDiffwhen restoring a mergeable child that may still exist as hidden state, aligning its full content instead of appending to it.
Performance 3
When a multi-blob batch starts with a full snapshot in an empty, attached document, it uses lazy fast-snapshot import and applies remaining updates in one checkout while preserving rollback.
Imports of List and Tree operations no longer copy the peer's last change block on every import to keep a rollback copy; many small imports are about 30% faster.
The first import concurrent with a local edit to a map on a shallow document adds each shallow-root map's entries only when a diff needs that map, avoiding eager decoding of every map.
Fixes and other changes 27
Restores the Rust method’s return type to VersionVector for source compatibility with versions before 1.17.0; it panics on resolution failure after releasing document locks.
The TypeScript declaration return type is corrected to match runtime behavior.
fork() and forkAt() preserve the full text style configuration, including defaults for previously unused style keys.
The TypeScript declaration callback parameter is corrected to match runtime behavior.
Undo and redo use element identities for native moves, sets, and inverse insertion deletes, fixing duplicate scalar elements and lost gaps after concurrent moves.
Pending changes are retained when importing a snapshot into a document that holds only pending changes.
The TypeScript declaration key type is corrected to match runtime behavior.
Each undo or redo call consumes at most one recorded item; a consumed no-op returns false and creates no opposite-stack item, leaving other history available.
Tree checkouts between concurrent versions no longer retain nodes absent from the target or panic when a node's child is created without the node.
The
updates-in-rangeexport mode no longer panics when spans contain several non-contiguous or overlapping ranges from one peer; all ranges are exported.Documents forked or snapshotted at a version with an unclosed style mark can be read without trapping; the start anchor styles nothing until its end arrives.
Returns the correct change for lamports loaded from snapshots, including peers with many stored blocks.
Returns
undefinedfor cursors on Map, Tree, Counter, or unknown containers instead of trapping.Checkout updates the last editor for map entries and movable-list items whose values are unchanged between versions, so the reported writer is correct.
A root container touched by document history is retained after importing that history even if its operations produce an empty value.
Tree-node metadata under a deleted ancestor and movable-list children no longer remain incorrectly deleted after the subtree or child is revived.
applyDiffnow keeps every child container moved by a diff and preserves their order; undo and redo use the same corrected behavior.A map event containing an unknown container value is delivered with its known keys; only the unknown child's entry is omitted.
Throws a readable error when a container's parent has an unknown type, rather than trapping.
Failed calls leave the document, history, and subscribers unchanged, while preserving prior uncommitted edits. Reverting a mergeable child no longer duplicates its content.
Text insertion beside style anchors now respects the mark boundary, and WASM deletes spanning separate inserts record the correct character IDs when astral characters are involved.
Snapshots and forked documents can resolve tree-node metadata parents created under deleted parents; corrupted change blocks no longer make queries trap, and later history operations report a parse error.
Callbacks retain representable events in a batch that touches an unknown container instead of losing the whole batch.
Committing a transaction that merges backspaces over astral characters no longer panics in WASM when the document has a subscriber or an UndoManager.
Importing moves out of deleted subtrees now emits events for restored descendant nodes, and moves under deleted nodes emit the applicable delete event.
Undo and redo no longer duplicate mergeable-child content, and a peer's own edit remains undoable after a remote undo restores the child it edited.
Undoing a step that would recreate an unknown container throws without changing the document; the step is dropped and its edits remain for subsequent rebased undo operations.