JsonCrdt
A full JSON document that merges. Objects use ORMap (add-wins keys), arrays use Rga (stable insertion order), and scalar values use MVRegister (surfaces conflicts). Concurrent edits at any depth — nested objects, array items, scalar fields — all resolve automatically.
Converges to: the same document on every replica — concurrently-added keys are all preserved (add-wins), array elements keep a stable order by insertion id, and concurrent scalar writes surface together as a multi-value the caller resolves.
Structure: ORMap + RGA + MVRegister
piece recurses: nested objects merge key-by-key, arrays union their operation logs, and leaves merge as multi-value registers. The three lattice laws (idempotent, commutative, associative) hold at every depth.
Add-wins keys and multi-value leaves
A key added concurrently with a remove survives — the add wins. When two replicas write different scalars to the same key concurrently, the leaf becomes a multi-value register holding both, so no write is silently lost; the caller picks a winner.
What each edit costs to send
Renaming the title of a long document should cost about as much as the title. So set and remove hand you back the change — the one field you touched — and that is what travels to the other devices. The size of what travels does not depend on how many fields the document holds. Editing one field of a 1,000-field document sends 177 bytes rather than the 127 KB the whole document weighs.
In code, both mutators return a Patch<JsonCrdt>. Hand it to a replicator with quilter.mutate { it.set(key, node) }; to hold the resulting whole document outside a replicator, absorb the patch: doc.piece { it.set(key, node) }.
One thing this does not yet make cheap. Changing a field inside a nested object means rebuilding that object and setting it at the top, so the frame is one key whose value is the whole rebuilt subtree. A path-addressed edit would make that cost depend on the depth rather than the size of the subtree; that is issue 2469.
Code examples
Ship the field, not the document:
Set and read a scalar:
Concurrent edits to a nested object both survive:
A concurrent add wins over a remove:
Concurrent scalar writes surface as a multi-value:
When to use
JsonCrdt fits collaborative JSON documents — config, metadata, document editors — where concurrent edits to nested structure must converge. For a flat key-value map, LWWMap is lighter; for an add-wins set of keys whose values are themselves CRDTs, use ORMap directly.