Initial Commit

This commit is contained in:
2026-05-27 23:29:53 +01:00
commit fbc70f96e6
10000 changed files with 1102152 additions and 0 deletions

View File

@@ -0,0 +1,347 @@
# OpenSpec Change: enterprise-crdt
- **Change ID**: enterprise-crdt
- **Package**: `@suaveplan/crdt`
- **Category**: universal
- **Target path**: `universal/data/crdt`
- **Status**: PROPOSED
- **Date**: 2026-04-24
- **Reference spec**: `/mnt/s/web/suaveplan/enterprise/crdt.md`
---
## Summary
Deliver `@suaveplan/crdt` on the genesis branch from scratch at enterprise quality. The package provides conflict-free replicated data type primitives: G-Counter, PN-Counter, OR-Set, LWW-Register, MV-Register, 2P-Set, and RGA (Replicated Growable Array). All CRDTs implement a common `CRDT` interface with `merge`, `value`, and `toJSON`/`fromJSON` for serialization. Genesis adds structured errors via `@suaveplan/error`, event-bus integration via `@suaveplan/events` for state-change propagation, diff/patch integration via `@suaveplan/diff` for efficient delta exchange, injectable clock and ID generator, OTel instrumentation, and property-based correctness proofs.
---
## Current-state audit (genesis)
The package does not exist on genesis yet — fresh build. No prior source, no test results, no coverage data.
---
## Available workspace deps on genesis
| Package | Role |
|---|---|
| `@suaveplan/error` (`workspace:^`) | Base class for `CrdtError`; `defineCodes` for `CRDT.*` codes |
| `@suaveplan/types` (`workspace:^`) | `Clock`, `IdGenerator`, `EventBusLike` injection interfaces |
| `@suaveplan/events` (`workspace:^`) | Emit typed state-change events on merge |
| `@suaveplan/diff` (`workspace:^`) | Delta encoding and patch application for efficient state sync |
| `zod` (`catalog:forms`) | Zod schemas for all public option and serialization types |
| `@opentelemetry/api` (`catalog:observability`) | Spans and counters on merge, update, and sync operations |
| `tslib` (`catalog:build`) | Runtime helpers |
Dev deps: `vitest`, `@vitest/coverage-istanbul`, `fast-check`, `mitata` (all `catalog:testing`).
---
## Public API surface
### Common CRDT interface
```ts
interface CRDT<TValue, TState = unknown> {
readonly id: string
value(): TValue
merge(remote: TState): void
state(): TState
toJSON(): string
fromJSON(json: string): void
clone(): this
}
interface CrdtOptions {
id?: string
clock?: Clock
idGenerator?: IdGenerator
}
```
### G-Counter
```ts
class GCounter implements CRDT<number, GCounterState> {
increment(nodeId: string, delta?: number): void
get(nodeId: string): number
total(): number
}
interface GCounterState {
type: "g-counter"
counters: Record<string, number>
}
```
### PN-Counter
```ts
class PNCounter implements CRDT<number, PNCounterState> {
increment(nodeId: string, delta?: number): void
decrement(nodeId: string, delta?: number): void
total(): number
}
interface PNCounterState {
type: "pn-counter"
increments: Record<string, number>
decrements: Record<string, number>
}
```
### OR-Set
```ts
class ORSet<T> implements CRDT<Set<T>, ORSetState<T>> {
add(element: T, nodeId: string): void
remove(element: T): void
has(element: T): boolean
size(): number
}
interface ORSetState<T> {
type: "or-set"
entries: Array<{ element: T; tag: string; tombstone: boolean }>
}
```
### LWW-Register
```ts
class LWWRegister<T> implements CRDT<T | undefined, LWWRegisterState<T>> {
set(value: T, nodeId: string, timestamp?: number): void
get(): T | undefined
}
interface LWWRegisterState<T> {
type: "lww-register"
value: T | undefined
timestamp: number
nodeId: string
}
```
### MV-Register
```ts
class MVRegister<T> implements CRDT<T[], MVRegisterState<T>> {
set(value: T, nodeId: string): void
get(): T[]
conflict(): boolean
}
interface MVRegisterState<T> {
type: "mv-register"
entries: Array<{ value: T; vectorClock: Record<string, number> }>
}
```
### 2P-Set
```ts
class TwoPSet<T> implements CRDT<Set<T>, TwoPSetState<T>> {
add(element: T): void
remove(element: T): void
has(element: T): boolean
}
interface TwoPSetState<T> {
type: "2p-set"
added: T[]
removed: T[]
}
```
### RGA (Replicated Growable Array)
```ts
class RGA<T> implements CRDT<T[], RGAState<T>> {
insert(index: number, element: T, nodeId: string): void
delete(index: number): void
get(index: number): T
length(): number
}
interface RGAState<T> {
type: "rga"
elements: Array<{ id: string; value: T | null; tombstone: boolean }>
}
```
### Error handling
```ts
class CrdtError extends SuaveplanError<CrdtErrorCode>
type CrdtErrorCode =
| "CRDT_MERGE_CONFLICT"
| "CRDT_INVALID_STATE"
| "CRDT_DESERIALIZE_FAILED"
| "CRDT_OPERATION_INVALID"
function registerCrdtErrorCodes(): void
```
### Zod schemas
```ts
const GCounterStateSchema: ZodSchema
const PNCounterStateSchema: ZodSchema
const ORSetStateSchema: ZodSchema
const LWWRegisterStateSchema: ZodSchema
const MVRegisterStateSchema: ZodSchema
const TwoPSetStateSchema: ZodSchema
const RGAStateSchema: ZodSchema
```
---
## New additions for genesis
### 1. Event-bus integration
Every CRDT constructor accepts an optional `bus?: EventBusLike`. When provided, merge operations emit typed events:
- `crdt:merged` — `{ id, type, previousState, nextState }`
- `crdt:updated` — `{ id, type, operation, args }`
### 2. Delta-based sync via `@suaveplan/diff`
`CrdtSyncManager` wraps one or more CRDTs and uses `@suaveplan/diff` JSON Patch to compute minimal delta payloads between sync points. Callers exchange deltas rather than full state snapshots.
```ts
class CrdtSyncManager {
register<T extends CRDT<unknown>>(crdt: T): void
delta(since?: string): SyncDelta
apply(delta: SyncDelta): void
}
```
### 3. Vector clock utilities
```ts
class VectorClock {
increment(nodeId: string): void
merge(remote: VectorClockState): void
compare(other: VectorClockState): "before" | "after" | "concurrent" | "equal"
state(): VectorClockState
}
type VectorClockState = Record<string, number>
```
### 4. OTel instrumentation
- Per-operation spans: `crdt.<type>.merge`, `crdt.<type>.update`, `crdt.sync.delta`, `crdt.sync.apply`
- Counters: `crdt.merges_total`, `crdt.updates_total`, `crdt.conflicts_total`
- Attributes: `crdt.type`, `crdt.id`, `crdt.node_id`
### 5. Property-based tests (fast-check)
- **Commutativity**: `merge(a, b) ≡ merge(b, a)` for all CRDT types
- **Associativity**: `merge(merge(a, b), c) ≡ merge(a, merge(b, c))`
- **Idempotency**: `merge(a, a) ≡ a`
- **Monotonicity**: `value(merge(a, b)) ≥ value(a)` for counter types
- **RGA convergence**: concurrent inserts on two replicas converge after mutual merge
### 6. Benchmark suite (`src/__benchmarks__/`)
- GCounter with 100 nodes, 10k increments
- ORSet with 1k add/remove operations
- RGA with 1k concurrent inserts on two replicas
---
## File layout
```text
universal/data/crdt/
package.json
tsconfig.json
vitest.config.ts
src/
index.ts
error-codes.ts
constants/
index.ts
types/
common.ts
common.md
core/
crdt-interface/
crdt-interface.ts
crdt-interface.md
crdt-interface.types.ts
index.ts
vector-clock/
vector-clock.ts
vector-clock.md
vector-clock.types.ts
vector-clock.test.ts
index.ts
sync-manager/
sync-manager.ts
sync-manager.md
sync-manager.types.ts
sync-manager.test.ts
index.ts
crdts/
g-counter/
g-counter.ts
g-counter.md
g-counter.types.ts
g-counter.test.ts
index.ts
pn-counter/
pn-counter.ts
pn-counter.md
pn-counter.types.ts
pn-counter.test.ts
index.ts
or-set/
or-set.ts
or-set.md
or-set.types.ts
or-set.test.ts
index.ts
lww-register/
lww-register.ts
lww-register.md
lww-register.types.ts
lww-register.test.ts
index.ts
mv-register/
mv-register.ts
mv-register.md
mv-register.types.ts
mv-register.test.ts
index.ts
two-p-set/
two-p-set.ts
two-p-set.md
two-p-set.types.ts
two-p-set.test.ts
index.ts
rga/
rga.ts
rga.md
rga.types.ts
rga.test.ts
index.ts
__benchmarks__/
crdt.bench.ts
tests/
integration.test.ts
property.test.ts
```
---
## Impact
**Downstream packages**: collaborative editing, offline-first sync, and multi-peer state reconciliation features. No existing genesis package depends on it yet.
**Workspace dep prerequisites**: `@suaveplan/error`, `@suaveplan/types`, `@suaveplan/events`, `@suaveplan/diff` must be published to Verdaccio before this package can be built.
**Breaking changes**: none (new package).