Collaboration
@nuforge/collaboration syncs the AST between peers in real time over the Loro CRDT. Each peer runs its own Nu runtime, and a sync provider keeps every peer's tree converging on the same state without manual conflict resolution.
Getting started
A LoroSyncProvider binds a Nu runtime to a LoroDoc. The first peer seeds a fresh doc from its local state; any later peer imports that doc and hydrates its local state from it:
import { LoroSyncProvider, createCollabExtension } from '@nuforge/collaboration';
import { LoroDoc } from 'loro-crdt';
// Peer 1 seeds a fresh doc from its local state
const doc1 = new LoroDoc();
const provider1 = new LoroSyncProvider(nu1, doc1);
provider1.init();
// Peer 2 imports peer 1's doc, then loads its (empty) state from it
const doc2 = new LoroDoc();
doc2.import(doc1.export({ mode: 'snapshot' }));
const provider2 = new LoroSyncProvider(nu2, doc2);
provider2.init();
How it works
LoroSyncProvider(nu, doc) keeps a two-way sync between the Nu runtime and a LoroDoc. Calling init() does one of two things depending on the doc:
- If the doc is empty, it seeds the doc from the runtime's current local state.
- If the doc already has content, it hydrates the local runtime state from the doc.
Each AST node is stored as JSON under its id in a Loro map. This is node-level CRDT with last-write-wins per node, which avoids the reorder and move corruption seen with naive text-CRDT approaches — a node either moves as a whole or it does not, so the tree never tears.
Merges are tagged with a source, so a peer does not echo its own changes back into a loop. When an incoming update is applied, the provider recognizes its origin and skips re-broadcasting it.
As an extension
You can wire the exact same sync as a nuforge extension instead of constructing a provider directly. Pass createCollabExtension(doc) when creating the runtime:
Nu.create({ extensions: [createCollabExtension(doc)] });
Transport
The provider does not own the network. You exchange updates between peers yourself over any transport — WebSocket, WebRTC, or anything else — by sending the bytes from doc.export(...) and applying them on the other side with doc.import(...). This keeps collaboration transport-agnostic: wire it through your existing realtime channel without adopting a new server.
Next steps
- Extensions — the extension system
createCollabExtensionplugs into. - Runtime — the Nu runtime each peer syncs.