Move docs/ package mirror into document-navigator plugin

Relocate the ~6k-file docs tree from the repo root into
plugins/document-navigator/docs/ so the plugin is self-contained and
portable. docs/superpowers/ (specs and plans) stays at the repo root.

Moved via cp+rm rather than git mv because directory rename() fails with
EPERM on this WSL /mnt DrvFs mount; git rename-detection pairs the
identical-content files at commit time.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-30 01:10:00 +01:00
parent fe3fedf42e
commit 9bf56a806b
6022 changed files with 12437 additions and 0 deletions

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,139 @@
# sync-manager
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## Purpose
`CrdtSyncManager` is a delta-exchange coordinator that manages a heterogeneous
collection of CRDT instances and provides the primitives needed for peer-to-peer
state synchronization. It maintains a per-replica vector clock to track causal
history and exposes `exportDeltas` / `sync` operations that can be wired into
any transport layer (WebSocket, HTTP, MessageChannel, or in-process).
## Features
- Registry for any number of heterogeneous CRDT instances keyed by their id
- Local `VectorClock` that advances on `tick()` (call after batching mutations)
- `exportDeltas()` — snapshot all CRDT states with current clock for transport
- `sync(deltas)` — merge incoming deltas and advance local clock causally
- `syncWith(peer)` — convenience bidirectional in-process sync for testing
- `{ merged, skipped }` result counts for observability
- Injectable `nodeId` for deterministic replica identity in tests
## Usage: Basic Example
```ts
import { CrdtSyncManager } from "@suaveplan/crdt/core/sync-manager/sync-manager";
import { GCounter } from "@suaveplan/crdt/crdts/g-counter/g-counter";
const mgr = new CrdtSyncManager({ nodeId: "replica-1" });
const counter = new GCounter("replica-1");
mgr.register(counter);
counter.increment("replica-1");
mgr.tick(); // advance clock after local mutations
const deltas = mgr.exportDeltas();
// send `deltas` to a remote peer over any transport
```
## Usage: Advanced Example
```ts
import { CrdtSyncManager } from "@suaveplan/crdt/core/sync-manager/sync-manager";
import { GCounter } from "@suaveplan/crdt/crdts/g-counter/g-counter";
import { ORSet } from "@suaveplan/crdt/crdts/or-set/or-set";
const a = new CrdtSyncManager({ nodeId: "a" });
const b = new CrdtSyncManager({ nodeId: "b" });
const counterA = new GCounter("a", "shared-counter");
const counterB = new GCounter("b", "shared-counter");
const setA = new ORSet<string>("shared-set");
const setB = new ORSet<string>("shared-set");
a.register(counterA);
a.register(setA);
b.register(counterB);
b.register(setB);
counterA.increment("a", 3);
setA.add("item-1", "a");
a.tick();
counterB.increment("b", 2);
b.tick();
// Bidirectional sync
const { local, remote } = a.syncWith(b);
console.log(local); // { merged: 2, skipped: 0 }
console.log(remote); // { merged: 2, skipped: 0 }
// Both managers now have the same CRDT state
console.log(counterA.total()); // 5
console.log(counterB.total()); // 5
```
## API Reference
### `new CrdtSyncManager(options?)`
Creates a new manager. `options.nodeId` defaults to a random UUID.
### `register(crdt): CrdtRegistration`
Adds a CRDT to the registry using `crdt.id` as the key.
Returns a `CrdtRegistration` with `{ id, crdt }`.
### `unregister(id): void`
Removes a CRDT from the registry.
### `get<TValue, TState>(id): CRDT | undefined`
Returns the registered CRDT for the given id, or `undefined`.
### `ids(): string[]`
Returns all registered CRDT ids.
### `tick(): void`
Increments the local vector clock counter for this replica's node id.
Should be called after applying a batch of local mutations before exporting.
### `clockState(): Record<string, number>`
Returns the current local vector clock snapshot.
### `exportDeltas(): SyncDelta[]`
Produces one `SyncDelta` per registered CRDT, each containing:
- `crdtId` — CRDT identifier
- `state` — current CRDT state snapshot
- `vectorClock` — current local clock
### `sync(deltas): SyncResult`
Integrates incoming deltas from a remote peer. Advances local clock causally.
Returns `{ merged: number, skipped: number }`.
### `syncWith(peer): { local: SyncResult; remote: SyncResult }`
Bidirectional in-process sync: exports each manager's state, applies it to the
other, and returns both results.
## Implementation Notes
The vector clock is used for observability and causal bookkeeping rather than
delta filtering. Full-state exchange is deliberately chosen over delta-only
exchange to keep the implementation straightforward and avoid the need for
operation logs. For high-frequency or bandwidth-constrained deployments, a
delta-log approach should be layered on top of this manager.
The `sync` method merges the remote clock unconditionally after processing all
deltas, even for `skipped` entries. This ensures the local clock always reflects
the maximum causal horizon seen from the peer, preventing re-delivery of already
seen states on future syncs.

View File

@@ -0,0 +1,59 @@
# vector-clock
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## Purpose
Provides a `VectorClock` implementation for tracking causality in distributed systems. Vector clocks are used by `MVRegister` to detect concurrent writes and are a foundational primitive for any CRDT that needs to track event ordering across multiple nodes.
## Features
- `increment(nodeId)` — increments the local counter for the given node
- `merge(remote)` — element-wise maximum merge (satisfies monotonicity)
- `compare(other)` — returns `"before" | "after" | "concurrent" | "equal"`
- `state()` — returns a snapshot `Record<string, number>`
- `clone()` — deep copies the clock
## Usage: Basic Example
```ts
import { VectorClock } from "@suaveplan/crdt";
const clock = new VectorClock();
clock.increment("node-1");
clock.increment("node-1");
console.log(clock.state()); // { "node-1": 2 }
```
## Usage: Advanced Example
```ts
import { VectorClock } from "@suaveplan/crdt";
const a = new VectorClock();
a.increment("node-1");
const b = new VectorClock();
b.increment("node-2");
console.log(a.compare(b.state())); // "concurrent"
a.merge(b.state());
console.log(a.compare(b.state())); // "after"
```
## API Reference
### `VectorClock`
- `constructor(initial?: VectorClockState)` — optional initial state
- `increment(nodeId: string): void`
- `merge(remote: VectorClockState): void` — element-wise max
- `compare(other: VectorClockState): "before" | "after" | "concurrent" | "equal"`
- `state(): VectorClockState` — snapshot
- `clone(): VectorClock`
## Implementation Notes
The comparison algorithm iterates all keys in the union of both clocks. Missing keys are treated as 0. Two clocks are concurrent when neither is strictly greater in all dimensions.

View File

@@ -0,0 +1,60 @@
# g-counter
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## Purpose
Provides `GCounter`, a grow-only counter CRDT. Each node in a distributed system maintains an independent counter; the global value is the sum of all node counters. Merge takes the element-wise maximum, ensuring convergence regardless of message ordering or duplicates.
## Features
- `increment(nodeId, delta?)` — adds `delta` (default 1) to the node's counter; rejects non-positive deltas
- `get(nodeId)` — returns the counter for a specific node
- `total()` — returns the sum across all nodes (equivalent to `value()`)
- `merge(state)` — element-wise maximum merge; idempotent and commutative
- `toJSON()` / `fromJSON()` — round-trip serialization
- `clone()` — deep copy
## Usage: Basic Example
```ts
import { GCounter } from "@suaveplan/crdt";
const counter = new GCounter();
counter.increment("node-1", 5);
counter.increment("node-2", 3);
console.log(counter.total()); // 8
```
## Usage: Advanced Example
```ts
import { GCounter } from "@suaveplan/crdt";
const a = new GCounter("ctr-1");
a.increment("n1", 10);
const b = new GCounter("ctr-1");
b.increment("n2", 5);
a.merge(b.state());
console.log(a.total()); // 15
```
## API Reference
### `GCounter`
- `constructor(id?)` — optional stable ID
- `increment(nodeId, delta?)` — throws `OPERATION_INVALID` if delta ≤ 0
- `get(nodeId): number`
- `total(): number` — sum of all node counters
- `merge(state: GCounterState): void`
- `state(): GCounterState`
- `toJSON(): string` / `fromJSON(json: string): void`
- `clone(): this`
## Implementation Notes
The merge operation is O(k) where k is the number of distinct nodes. The `clone()` method uses the same `GCounter` constructor — the `as this` cast is needed because TypeScript cannot statically know the concrete subclass type.

View File

@@ -0,0 +1,54 @@
# lww-register
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## Purpose
Provides `LWWRegister<T>`, a Last-Write-Wins Register CRDT. The register holds a single value; when two replicas have different values, the one with the higher timestamp wins during merge. Ties are broken by node ID lexicographic order, ensuring deterministic convergence.
## Features
- `set(value, timestamp?)` — sets the value with optional explicit timestamp (defaults to `Date.now()`)
- `get()` — returns the current value or `undefined`
- `merge(state)` — higher timestamp wins; tie-broken by nodeId lexicographic order
- `toJSON()` / `fromJSON()` — serialization round-trip
- `clone()` — deep copy
## Usage: Basic Example
```ts
import { LWWRegister } from "@suaveplan/crdt";
const reg = new LWWRegister<string>("node-1");
reg.set("hello");
console.log(reg.get()); // "hello"
```
## Usage: Advanced Example
```ts
import { LWWRegister } from "@suaveplan/crdt";
const a = new LWWRegister<number>("n1");
a.set(42, 1000);
const b = new LWWRegister<number>("n2");
b.set(99, 2000);
a.merge(b.state());
console.log(a.get()); // 99 (higher timestamp)
```
## API Reference
### `LWWRegister<T>`
- `constructor(nodeId, id?)` — nodeId is required for tie-breaking
- `set(value, timestamp?)` — sets value with timestamp
- `get(): T | undefined` — current value
- `merge(state: LWWRegisterState<T>): void`
## Implementation Notes
Wall-clock timestamps from `Date.now()` can cause issues if clocks are not synchronized. For distributed deployments, use a monotonic or hybrid logical clock and pass the timestamp explicitly via `set(value, timestamp)`.

View File

@@ -0,0 +1,58 @@
# mv-register
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## Purpose
Provides `MVRegister<T>`, a Multi-Value Register CRDT that preserves all concurrent writes rather than silently dropping them. When replicas diverge and merge, all concurrent values are returned by `get()`, allowing the application to detect and resolve conflicts. This makes MVRegister appropriate for collaborative editing scenarios where silent loss-of-data is unacceptable.
## Features
- `set(value)` — writes a new value, incrementing the node's vector clock
- `get()` — returns all current values (multiple on conflict, single on convergence)
- `conflict()` — returns `true` when more than one value is present
- `merge(state)` — keeps entries not dominated by any remote entry; drops dominated entries
- `toJSON()` / `fromJSON()` — serialization round-trip
- `clone()` — deep copy
## Usage: Basic Example
```ts
import { MVRegister } from "@suaveplan/crdt";
const reg = new MVRegister<string>("node-1");
reg.set("hello");
console.log(reg.get()); // ["hello"]
console.log(reg.conflict()); // false
```
## Usage: Advanced Example
```ts
import { MVRegister } from "@suaveplan/crdt";
const a = new MVRegister<number>("n1");
a.set(42);
const b = new MVRegister<number>("n2");
b.set(99);
a.merge(b.state());
console.log(a.conflict()); // true
console.log(a.get()); // [42, 99] — both concurrent values preserved
```
## API Reference
### `MVRegister<T>`
- `constructor(nodeId, id?)` — nodeId for vector clock tracking
- `set(value: T): void` — advances vector clock, replaces local entries
- `get(): T[]` — returns all current values
- `conflict(): boolean`
- `merge(state: MVRegisterState<T>): void`
## Implementation Notes
The merge algorithm uses vector clock dominance: an entry is dropped if any remote entry has a strictly greater vector clock (happened after). Entries with concurrent clocks are both preserved. This guarantees that all concurrent writes survive the merge.

View File

@@ -0,0 +1,62 @@
# or-set
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## Purpose
Provides `ORSet<T>`, an Observe-Remove Set CRDT. Unlike a 2P-Set, an OR-Set handles concurrent add and remove operations correctly: each addition is tagged with a unique identifier, and removal only tombstones the specific tags that were observed at the time of removal. Concurrent adds survive removes.
## Features
- `add(element, nodeId)` — tags the element with a unique per-node identifier
- `remove(element)` — tombstones all current (non-tombstoned) tags for the element
- `has(element)` — returns true if any live (non-tombstoned) tag exists
- `size()` — count of live unique elements
- `value()` — returns a `Set<T>` of all live elements
- `merge(state)` — merges remote entries; tombstone state wins on conflict
- `toJSON()` / `fromJSON()` — serialization round-trip
- `clone()` — deep copy
## Usage: Basic Example
```ts
import { ORSet } from "@suaveplan/crdt";
const set = new ORSet<string>();
set.add("alice", "node-1");
set.add("bob", "node-1");
set.remove("alice");
console.log(set.has("alice")); // false
console.log([...set.value()]); // ["bob"]
```
## Usage: Advanced Example
```ts
// Concurrent add and remove converge correctly
const a = new ORSet<string>();
a.add("x", "n1");
const b = a.clone();
a.remove("x"); // n1 removes x
b.add("x", "n2"); // n2 re-adds x concurrently
a.merge(b.state());
console.log(a.has("x")); // true — concurrent add survives
```
## API Reference
### `ORSet<T>`
- `add(element: T, nodeId: string)` — adds with unique tag
- `remove(element: T)` — tombstones all current live tags
- `has(element: T): boolean`
- `size(): number`
- `value(): Set<T>`
- `merge(state: ORSetState<T>): void`
## Implementation Notes
Elements are compared with `===` (reference equality). For object elements, ensure consistent references or use string/number keys.

View File

@@ -0,0 +1,55 @@
# pn-counter
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## Purpose
Provides `PNCounter`, a positive-negative counter CRDT that supports both increment and decrement operations. Built from two G-Counters (one for increments, one for decrements), the net value is `sum(increments) - sum(decrements)`. All CRDT invariants (commutativity, associativity, idempotency) are preserved.
## Features
- `increment(nodeId, delta?)` — adds to the increment counter for `nodeId`
- `decrement(nodeId, delta?)` — adds to the decrement counter for `nodeId`
- `total()` — net value: sum of all increments minus sum of all decrements
- `merge(state)` — element-wise maximum on both increment and decrement counters
- `toJSON()` / `fromJSON()` — serialization round-trip
- `clone()` — deep copy
## Usage: Basic Example
```ts
import { PNCounter } from "@suaveplan/crdt";
const counter = new PNCounter();
counter.increment("n1", 10);
counter.decrement("n1", 3);
console.log(counter.total()); // 7
```
## Usage: Advanced Example
```ts
import { PNCounter } from "@suaveplan/crdt";
const a = new PNCounter("likes");
a.increment("user-1", 5);
const b = new PNCounter("likes");
b.decrement("user-2", 2);
a.merge(b.state());
console.log(a.total()); // 3 (5 - 2)
```
## API Reference
### `PNCounter`
- `increment(nodeId, delta?)` — throws `OPERATION_INVALID` if delta ≤ 0
- `decrement(nodeId, delta?)` — throws `OPERATION_INVALID` if delta ≤ 0
- `total(): number` — net counter value
## Implementation Notes
The value can be negative if decrements exceed increments. This is intentional — PNCounter does not enforce a floor of 0.

View File

@@ -0,0 +1,136 @@
# rga
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## Purpose
Implements the Replicated Growable Array (RGA) CRDT, a list-oriented conflict-free
replicated data type that supports concurrent insertions and deletions while
guaranteeing convergence to the same element order on all replicas. Each element
is tagged with a unique (sequence-number, node-id) identifier so that causally
ordered operations are reflected correctly and concurrent siblings are resolved
deterministically without coordination.
## Features
- Concurrent `insert` and `remove` operations with causal ordering
- Tombstone-based removal that preserves relative positions for later merges
- Tie-breaking via `(seq DESC, nodeId DESC)` for concurrent sibling inserts
- `idAt(index)` and `indexOf(id)` for positional queries on the visible array
- Full `merge`, `toJSON`/`fromJSON`, `clone`, and `state` CRDT interface
- `fromJSON` rebuilds from serialized state and advances internal seq counter
## Usage: Basic Example
```ts
import { RGA } from "@suaveplan/crdt/crdts/rga/rga";
const doc = new RGA<string>("replicaA");
const id0 = doc.insert("hello");
const id1 = doc.insert("world", id0);
console.log(doc.value()); // ["hello", "world"]
doc.remove(id0);
console.log(doc.value()); // ["world"]
```
## Usage: Advanced Example
```ts
import { RGA } from "@suaveplan/crdt/crdts/rga/rga";
// Two replicas insert concurrently after the same predecessor
const a = new RGA<string>("nodeA");
const b = new RGA<string>("nodeB");
const rootId = a.insert("root");
b.merge(a.state()); // b now knows about "root"
// Concurrent inserts after "root"
const aId = a.insert("from-A", rootId);
const bId = b.insert("from-B", rootId);
// Merge both ways
a.merge(b.state());
b.merge(a.state());
// Both replicas converge to the same order
console.log(a.value()); // deterministic total order: e.g. ["root", "from-B", "from-A"]
console.log(b.value()); // identical
```
## API Reference
### `new RGA<T>(nodeId?, id?)`
Creates a new RGA instance for the given replica node. `nodeId` defaults to
`"node"` and `id` defaults to a random UUID.
### `insert(value, afterId?): string`
Inserts `value` after the element identified by `afterId` (null or omitted = prepend).
Returns the new element's unique id.
### `remove(id): void`
Marks the element with `id` as a tombstone. The element is hidden from `value()`
but retained for future merge convergence.
### `value(): T[]`
Returns all non-tombstoned elements in document order.
### `idAt(index): string | undefined`
Returns the id of the element at the given visible index, or `undefined` if out
of range.
### `indexOf(id): number`
Returns the visible index of the element with `id`, or `-1` if tombstoned or
absent.
### `merge(remote: RGAState<T>): void`
Integrates a remote replica's state. New elements are inserted at their correct
positions; tombstones propagate (tombstone wins).
Throws `CrdtError(DESERIALIZE_FAILED)` if the remote type field does not match.
### `state(): RGAState<T>`
Returns a snapshot of the internal sequence including tombstones.
### `toJSON(): string`
Serializes state to a JSON string.
### `fromJSON(json): void`
Resets the instance and rebuilds from a JSON string.
Throws `CrdtError(DESERIALIZE_FAILED)` on malformed JSON or wrong type field.
### `clone(): this`
Returns an independent deep copy of the RGA instance.
## Implementation Notes
The predecessor-based insertion strategy is derived from the original RGA paper
(Roh et al., 2011). Each node records which element it was inserted after rather
than an absolute index, making the algorithm position-independent and robust to
concurrent inserts at the same predecessor.
Tie-breaking compares `(seq DESC, nodeId DESC)` — a higher sequence number wins,
and among equal sequences the lexicographically larger node-id wins. This matches
common RGA implementations and is consistent across all replicas given the same
inputs.
Tombstones are never removed (no garbage collection) to ensure replicas that
receive operations out of order can still converge. In production systems with
bounded document lifetime, a GC epoch mechanism should be layered on top.
The `predecessorFromElements` helper reconstructs predecessor links from the
flat ordered element list produced by `state()`. This keeps the wire format
simple (no extra predecessor field) at the cost of an O(n) scan per element
during merge — acceptable for document sizes encountered in practice.

View File

@@ -0,0 +1,56 @@
# two-p-set
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## Purpose
Provides `TwoPSet<T>`, a Two-Phase Set CRDT. Elements can be added and removed exactly once; removed elements can never be re-added. The set value is the difference between the add-set and the remove-set. This guarantees eventual consistency at the cost of restricting re-addition.
## Features
- `add(element)` — adds to the A-set; throws `OPERATION_INVALID` if element was previously removed
- `remove(element)` — moves element to the R-set; no-op if not in A-set
- `has(element)` — true if in A-set but not R-set
- `value()` — returns a `Set<T>` of live elements
- `merge(state)` — union-merges both A-set and R-set
- `toJSON()` / `fromJSON()` — serialization round-trip
- `clone()` — deep copy
## Usage: Basic Example
```ts
import { TwoPSet } from "@suaveplan/crdt";
const set = new TwoPSet<string>();
set.add("alice");
set.remove("alice");
console.log(set.has("alice")); // false
```
## Usage: Advanced Example
```ts
import { TwoPSet } from "@suaveplan/crdt";
const a = new TwoPSet<number>();
a.add(1);
const b = new TwoPSet<number>();
b.add(2);
a.merge(b.state());
console.log([...a.value()]); // [1, 2]
```
## API Reference
### `TwoPSet<T>`
- `add(element)` — throws `OPERATION_INVALID` if previously removed
- `remove(element)` — no-op if element not in A-set
- `has(element): boolean`
- `value(): Set<T>`
- `merge(state: TwoPSetState<T>): void`
## Implementation Notes
Elements are serialised to JSON for key comparison, enabling support for object elements with structural equality. The restriction that removed elements cannot be re-added is a fundamental invariant of 2P-Set that enables simple merge logic.

View File

@@ -0,0 +1,52 @@
# crdt-error
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## Purpose
Defines the structured error type and stable error-code registry for `@suaveplan/crdt`. CRDT operations should never throw in normal use — these errors guard against invalid states, deserialization failures, and programmer errors (invalid operations).
## Features
- `CRDT_CODES` registry via `@suaveplan/error`'s `defineCodes` — codes namespaced `CRDT.*`
- `CrdtError` extends `SuaveplanError` for cause-chain traversal
- Four codes: `MERGE_CONFLICT`, `INVALID_STATE`, `DESERIALIZE_FAILED`, `OPERATION_INVALID`
- `CrdtCode` union type for exhaustive switch statements
## Usage: Basic Example
```ts
import { CrdtError, CRDT_CODES } from "@suaveplan/crdt";
throw new CrdtError(CRDT_CODES.DESERIALIZE_FAILED, "Unknown CRDT type in JSON");
```
## Usage: Advanced Example
```ts
import { CrdtError, CRDT_CODES } from "@suaveplan/crdt";
function validateMerge(state: unknown): void {
if (typeof state !== "object" || state === null) {
throw new CrdtError(CRDT_CODES.INVALID_STATE, "State must be an object");
}
}
```
## API Reference
### `CRDT_CODES`
- `CRDT_CODES.MERGE_CONFLICT` — incompatible state types during merge
- `CRDT_CODES.INVALID_STATE` — deserialized or constructed state is structurally invalid
- `CRDT_CODES.DESERIALIZE_FAILED` — JSON deserialization failed
- `CRDT_CODES.OPERATION_INVALID` — attempt to remove from G-Set or similar invariant violation
### `CrdtError`
Extends `SuaveplanError<CrdtCode>`. Constructor: `(code, message, options?)`.
## Implementation Notes
Codes are prefixed with `"CRDT."` by `defineCodes`. Most CRDT methods are synchronous and do not throw in normal use — errors indicate programmer mistakes or data corruption.

View File

@@ -0,0 +1,48 @@
# crdt.types
## Purpose
Shared TypeScript type definitions for the `@suaveplan/crdt` package. Defines the `CRDT<TValue, TState>` base interface, all serializable state shapes, and construction options. All CRDT implementations depend on these types for consistent merge, serialization, and cloning contracts.
## Features
- `CRDT<TValue, TState>` — base interface with `value()`, `merge()`, `state()`, `toJSON()`, `fromJSON()`, `clone()`
- State types: `GCounterState`, `PNCounterState`, `ORSetState<T>`, `LWWRegisterState<T>`, `MVRegisterState<T>`, `TwoPSetState<T>`, `RGAState<T>`
- `VectorClockState` — alias for `Record<string, number>`
- `CrdtOptions` — base constructor options with optional `id` and `nodeId`
## Usage: Basic Example
```ts
import type { CRDT, GCounterState } from "@suaveplan/crdt";
function merge<T>(a: CRDT<T>, b: CRDT<T>): void {
a.merge(b.state());
}
```
## Usage: Advanced Example
```ts
import type { ORSetState } from "@suaveplan/crdt";
function serialize<T>(state: ORSetState<T>): string {
return JSON.stringify(state);
}
```
## API Reference
### `CRDT<TValue, TState>`
- `id: string` — stable identifier
- `value(): TValue` — current computed value
- `merge(remote: TState): void` — merges remote state
- `state(): TState` — serializable snapshot
- `toJSON(): string` — JSON serialisation
- `fromJSON(json: string): void` — deserialises into this instance
- `clone(): this` — deep copy
## Implementation Notes
All state types include a `type` discriminator string for runtime identification during deserialization. The `RGAElement<T>` uses `value: T | null` where `null` represents a tombstoned element.