feat(crdt): add @robonen/crdt package
Hand-built CRDT primitives: Lamport clock + version vectors, op-log, LWW register/map, RGA sequence, fractional indexing, marks store, sync encode, and doc/replica. Includes eslint flat config + composite tsconfig.
This commit is contained in:
@@ -0,0 +1,20 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import { VersionVector, opId } from '../../clock';
|
||||
import { decodeOps, decodeStateVector, encodeOps, encodeStateVector } from '..';
|
||||
|
||||
describe('sync encoding', () => {
|
||||
it('round-trips a version vector through bytes', () => {
|
||||
const vv = new VersionVector();
|
||||
vv.observe(opId('a', 3));
|
||||
vv.observe(opId('b', 1));
|
||||
|
||||
const restored = decodeStateVector(encodeStateVector(vv));
|
||||
expect(restored.get('a')).toBe(3);
|
||||
expect(restored.get('b')).toBe(1);
|
||||
});
|
||||
|
||||
it('round-trips an op batch through bytes', () => {
|
||||
const ops = [{ id: opId('a', 1), kind: 'insert', value: 'x' }];
|
||||
expect(decodeOps(encodeOps(ops))).toEqual(ops);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,34 @@
|
||||
import { VersionVector } from '../clock';
|
||||
|
||||
const encoder = new TextEncoder();
|
||||
const decoder = new TextDecoder();
|
||||
|
||||
/**
|
||||
* Transport-agnostic wire encoding. v1 is JSON-over-bytes — simple and
|
||||
* debuggable; a compact varint format is a later optimization with no API change.
|
||||
*/
|
||||
export function encodeJson(value: unknown): Uint8Array {
|
||||
return encoder.encode(JSON.stringify(value));
|
||||
}
|
||||
|
||||
export function decodeJson<T>(bytes: Uint8Array): T {
|
||||
return JSON.parse(decoder.decode(bytes)) as T;
|
||||
}
|
||||
|
||||
/** Encode a version vector for a "what do you have?" sync handshake. */
|
||||
export function encodeStateVector(vv: VersionVector): Uint8Array {
|
||||
return encodeJson(vv.toJSON());
|
||||
}
|
||||
|
||||
export function decodeStateVector(bytes: Uint8Array): VersionVector {
|
||||
return VersionVector.fromJSON(decodeJson(bytes));
|
||||
}
|
||||
|
||||
/** Encode a batch of ops (the delta or a full snapshot). */
|
||||
export function encodeOps<Op>(ops: readonly Op[]): Uint8Array {
|
||||
return encodeJson(ops);
|
||||
}
|
||||
|
||||
export function decodeOps<Op>(bytes: Uint8Array): Op[] {
|
||||
return decodeJson<Op[]>(bytes);
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
export * from './encode';
|
||||
Reference in New Issue
Block a user