watershed Collaborative data structures for Gleam

← watershed · Data structures / Maps

Maps

Keyed state that picks a winner, keeps an edit, or grows into a tree.

Maps are where most collaborative apps keep their state, and where the choice of conflict model is most visible. watershed's maps span that choice.

SharedMap resolves JSON and encoded-handle values by server order, following the last-write-wins design used by Fluid Framework. LWWMap lets each string key's timestamp outrank a later server SN, and keeps removals as tombstones. OR-map keeps causal dots per entry so a concurrent write survives a delete. SharedDirectory makes SharedMap recursive, with hierarchical identity that survives concurrent creation and delete-then-recreate.

SharedMap

DDS
map_kernel

A shared key/value map where the most recent write to a key wins.

Open the live Sudoku cell demo →

watershed's flagship DDS follows Fluid Framework's SharedMap kernel design. Keys map to JSON values, including supported encoded handles. Each set is sequenced, and for a given key the write with the highest sequence number wins. Its inner set, delete, and clear payloads match the @fluidframework/map operation encoding.

Concurrent writes resolve deterministically by server order rather than by a merge function. A local write renders immediately; the ack promotes it, and if a higher-SN write to the same key arrives it replaces the value. Reference-generated corpus tests cover map state, events, and convergence. Attach and summary formats remain watershed's own, so this does not imply drop-in Fluid container interoperability.

Choose SharedMap for server-ordered JSON or handle state. Its summary keeps confirmed entries in insertion order, with no CRDT tombstones. LWWMap instead keeps strings, per-key timestamps, and tombstones, and reads keys in sorted order. Both can overwrite a concurrent losing value; neither preserves the disagreement for you.

Best for

  • Shared application state and settings objects edited by many clients
  • Learning and testing server-ordered last-write-wins collaboration
  • Key-value collaboration where a clear last-writer-wins rule is acceptable
Merge rule
for each key, the most recent write wins, decided by server order
Optimistic behavior
your writes show instantly, then lock in once the server confirms them
Summary shape
confirmed entries reload with their keys and insertion order intact
Model
Distributed data structure: converges by server order

LWWMap

CRDT
lww_map_kernel

Shared string settings where a newer timestamp can beat a later server write.

Try racing two gate settings. A writes open with a newer timestamp, then B's closed gets the later server sequence number. LWWMap keeps open. SharedMap would keep closed: it follows server order, while LWWMap follows each key's timestamp through the sequenced runtime or the existing JavaScript CRDT mesh and relay paths.

At equal time, the greater writer ID wins: B's closed beats A's open even though the values point the other way alphabetically. A tombstone still beats a value at the same time. That's different from OR-map's observed-remove, add-wins rule. Removal isn't unconditional remove-wins: an older write can't resurrect a tombstone, but a newer write can restore the key.

The runtime supplies wall-clock time; the kernel advances it beyond the timestamp already observed or issued for that key, including pending edits and tombstones. Clock skew can still favor the writer whose clock is ahead. Last doesn't promise the most recent human action.

You get string keys and string values, with set and remove. An empty string is a value. Unlike SharedMap's JSON values and supported encoded handles, LWWMap has no nested JSON or handle traversal, no clear, and no tombstone-pruning API. SharedMap reloads insertion order; LWWMap retains timestamped removals and returns visible entries sorted by key.

Choose it when offline shared string settings need one deterministic winner and you can afford to lose another concurrent value. SharedMap also loses concurrent losing values. Neither is an MV register or a field-wise merge; use MvRegister when someone needs to read and resolve the disagreement.

Best for

  • Offline shared string settings with an acceptable deterministic single winner
  • Per-key labels and modes that reconcile across the JavaScript mesh or relay
  • String state where timestamps, rather than server sequence, should choose the winner
Merge rule
the greater per-key timestamp wins; at equal time, a tombstone beats a value and writer ID breaks a set/set tie
Optimistic behavior
your set or remove appears in magenta while confirmed entries stay in ink
Summary shape
values, timestamps, and retained tombstones reload together; visible entries read in sorted-key order
Model
Conflict-free replicated data type: converges by merge

OrMap

CRDT
or_map_kernel

A map where editing a key and deleting it at once won't lose the edit.

An OR-map applies the OR-set's observed-remove semantics to keyed entries. Each entry records causal dots; removing a key only tombstones the dots it has observed, so a concurrent write to the same key survives a delete (add-wins).

Values can themselves be additive tallies, which turns the map into a keyed CRDT ledger. In the demo it appears as a stockpile ledger where striking a row hides it and re-opening submits a +0 delta to surface the retained tally.

Switch the demo to string sets and each document gets a checklist. A adds draft while B adds reviewed: both members survive. Put those edits in SharedMap as separate JSON arrays instead and, in the same server order, only B's later whole array remains. A channel has one fixed, homogeneous mode: tallies, LWW registers, MV registers, or sets of strings. Switching between tallies and sets starts fresh OR-map replicas.

In set mode, removing a member clears its observed add tags; removing a key also clears its observed members. An unseen concurrent addition survives. Removing the last member keeps a present empty set, while removing an absent member creates nothing. Add a removed key again and only its new members appear, even after an old delta is replayed. Causal metadata stays behind to enforce those removals; this is different from the tally mode's retained ledger.

The ledger / string sets and MV registers views run separate instances of this same structure. Switching to MV registers preserves the ledger or checklist you've been editing.

In MV-register mode, race two revisions under gate-mode and you'll see both answers. The OR-map keeps the key alive through remove/write concurrency; the MV register keeps alternatives inside that key. Resolve observed combines the selected client's alternatives in an ordinary write. It replaces only those observed revisions, so an unseen offline writer can still bring another answer.

Best for

  • Keyed ledgers edited offline or concurrently (stockpiles, inventories, per-key counters)
  • Per-document string labels and checklists where concurrent member edits must merge
  • Maps where deleting and concurrently updating a key must not lose the update
  • A CRDT-correct alternative to SharedMap when last-write-wins would drop data
Merge rule
delete a key while someone else edits it, and the edit survives (the write wins)
Optimistic behavior
edits and removals show locally at once, with confirmed set members alongside
Summary shape
entries remember their edit history, not just the current value
Model
Conflict-free replicated data type: converges by merge

SharedDirectory

DDS
directory_kernel

SharedMap with folders: nested groups of keys, each keeping its own identity.

Open the live SharedDirectory demo →

SharedDirectory is SharedMap made recursive, modeled after Fluid Framework's SharedDirectory design. Every folder node has its own last-write-wins key/value store plus a named set of child folders, addressed by absolute path (/surveys, /surveys/intake). Storage resolves exactly like SharedMap: each set is sequenced, highest sequence number wins per key.

The hard part is hierarchical identity, not storage. A folder can be created by two clients at the same instant, deleted, and recreated under the same path, and every replica must still agree on which folder is which. The kernel models that identity explicitly from creator ids, create-sequence data, and each op's reference sequence number. A stale op targeting an old instance of a path is ignored; concurrent same-name creates merge into a single folder. A flat map cannot express that.

Best for

  • Nested, collaboratively-edited state: document trees, project/site hierarchies, scene graphs
  • Studying hierarchical identity and server-ordered folder collaboration
  • Anywhere a flat map's keys want structure (folders of readings, grouped settings)
Merge rule
like SharedMap, but with folders; each folder keeps its identity even if it's deleted and remade
Optimistic behavior
folder and key edits show immediately until the server confirms them
Summary shape
the whole folder tree reloads intact
Model
Distributed data structure: converges by server order