Automerge
@homeostate/crdt-automerge
Automerge CRDT backend for @homeostate/core
Draft / placeholder release. 0.0.0 reserves the name while the API is still
being designed. Nothing here is stable — do not depend on it yet.
Automerge backend for
@homeostate/core.
It maps the synced state onto one key of an Automerge document (nested objects become
maps, arrays lists, strings text) and writes fine-grained operations derived from
getChanges, one Automerge change per write.
Install
npm install @homeostate/core @homeostate/crdt-automerge @automerge/automerge@automerge/automerge 3.x is a peer dependency.
Usage
import * as A from "@automerge/automerge";
import { createSyncEngine } from "@homeostate/core";
import {
createAutomergeBackend,
createAutomergeHandle,
} from "@homeostate/crdt-automerge";
const handle = createAutomergeHandle(A.init());
const engine = createSyncEngine(
createAutomergeBackend(handle, "shared"),
adapter,
);
engine.connect();Automerge documents are immutable values: every change returns a new document and leaves
the old one behind. createAutomergeHandle holds the current document so the backend and
your replication code share it:
handle.doc()returns the current document.handle.change(fn)applies a local change throughA.change.handle.update((doc) => ...)replaces the document with the result ofA.merge,A.applyChanges,A.loadIncremental,A.receiveSyncMessage, or any other Automerge call.handle.subscribe(({ doc, local }) => ...)fires whenever the heads change;localis true forchangeand false forupdate.
The synced state lives in handle.doc().shared; a middle-of-array delete, a toggle, or a
keystroke each produce one small change. Replication is yours to wire, for example:
handle.subscribe(({ doc, local }) => {
if (local) send(A.getLastLocalChange(doc));
});
onMessage((change) => handle.update((doc) => A.applyChanges(doc, [change])[0]));The engine hears about every change that did not come through its own write, imports
and local edits alike.
read() returns plain JSON. Automerge keeps every object that the last change did not
touch, so the backend caches copies per source object and a read after a toggle copies only
the containers on the path to the toggled item. Snapshots are shared between reads; treat
them as immutable.
undefined is not JSON and Automerge rejects it, so the backend drops object entries whose
value is undefined and stores undefined array items as null, as JSON.stringify does.
@automerge/automerge ships WebAssembly. Node loads it directly; for bundlers see
Automerge's documentation, or import
@automerge/automerge/slim and initialize the module yourself.