AbsoluteJS

CRDT Adapters

Production CRDT backends — Yjs, Automerge, Loro — behind the same TextCrdtAdapter contract as the in-package rgaText. Swap one in and every call site stays the same.

#Why adapters

@absolutejs/sync/crdt is a small, zero-dependency RGA — great for offline-merge and moderate collaboration. Production-scale collaborative text wants the things mature CRDT libraries solve: efficient binary deltas, tombstone management, interleaving guarantees. The sync-adapters repo houses first-party- maintained wrappers around the staples so the core stays dependency-free and you only install the backend you actually use.

#The contract — one shape for every backend

Every backend (including the first-party RGA) implements TextCrdtAdapter<State>. The client controller and server engine only ever talk to that contract, which is why swapping one in is a one-line change on each side:

TS
// The contract every backend (first-party rgaText included) implements:
import type { CrdtText, TextCrdtAdapter, CrdtMergeable } from '@absolutejs/sync/crdt';

// What the engine needs server-side to auto-merge a CRDT field on write:
type CrdtMergeable<State> = {
  empty: () => State;
  merge: (a: State, b: State) => State;
};

// What a collaborative-text backend exposes:
type TextCrdtAdapter<State> = CrdtMergeable<State> & {
  create: (replica: string, initial?: State) => CrdtText<State>;
  textOf: (state: State) => string;
  compact?: (state: State) => State;       // optional, for GC-style backends
};

// The live doc the client controller drives:
type CrdtText<State> = {
  text: () => string;
  setText: (next: string) => void;
  merge: (state: State) => void;
  state: () => State;
  takeDelta?: () => State;                  // optional; the controller falls
                                            // back to state() when missing
};

#@absolutejs/sync-yjs — Yjs

The most widely-used collaborative CRDT in the web ecosystem. The adapter wraps Yjs's Y.Text, serialises state as a base64 Yjs update, and implements takeDelta via state vectors for true delta uploads:

TS
import { yjsText, createYjsText } from '@absolutejs/sync-yjs';

// Server
engine.registerCrdt('issues', { body: yjsText });

// Client — only the create factory changes
const doc = useCollaborativeText({
  url, collection: 'issues', field: 'body', id,
  create: createYjsText,
});

// State serialises as a base64 Yjs update (JSON-safe for the change feed).
// takeDelta is implemented via Y.encodeStateAsUpdate(doc, lastVector) and the
// vector advances on merge — remote ops aren't re-broadcast.

#@absolutejs/sync-automerge — Automerge

A mature, battle-tested CRDT. The adapter wraps an Automerge doc with a single text field and serialises state as a base64 Automerge document.

TS
import { automergeText, createAutomergeText } from '@absolutejs/sync-automerge';

engine.registerCrdt('issues', { body: automergeText });

const doc = useCollaborativeText({
  url, collection: 'issues', field: 'body', id,
  create: createAutomergeText,
});

// State = base64 of an Automerge doc. Notes:
// - The 'replica' option is accepted for contract symmetry; Automerge manages
//   actor identity internally.
// - Mutators concurrently creating the SAME top-level key on independent docs
//   can fork; the empty() snapshot is the shared base so clients load it for
//   a common root.

#@absolutejs/sync-loro — Loro

A fast Rust/wasm CRDT. The adapter wraps a LoroDoc's text container and serialises state as a base64 Loro snapshot.

TS
import { loroText, createLoroText } from '@absolutejs/sync-loro';

engine.registerCrdt('issues', { body: loroText });

const doc = useCollaborativeText({
  url, collection: 'issues', field: 'body', id,
  create: createLoroText,
});

// State = base64 of a Loro snapshot. Loro is a fast Rust/wasm CRDT;
// importing a snapshot or update into a doc merges. The 'replica' option is
// accepted for contract symmetry; Loro assigns a peer id internally.