/** * rgui core — GraphCrdt: a dependency-free, transport-agnostic CRDT for graph * syncing. Pure data + a deterministic merge; how states travel is the host * app's business (per the scope guard: rgui does no I/O). * * Design (v1, deliberately minimal): * - Node/edge PRESENCE is an observed-remove set without tombstones (ORSWOT): * each element keeps its live add-dots; the state-wide `clock` (a version * vector) is the causal context. On merge, an element's dot survives iff * both sides hold it, or one side holds it and the other has NOT seen it * (dot not covered by the other's clock). Concurrent add wins over remove. * - FIELDS are deterministic Lamport registers: {value, dot} merged by the * total order (seq, then actor). This converges deterministically but is * NOT wall-clock recency. Opt-in typed joins replace LWW per field key: * "max"/"min" over numbers, "any"/"all" over booleans — the only rules of * the display-oriented MergeRule algebra that are true semilattice joins. * - REMOVE kills presence only. Field registers survive, so a re-added * element resurfaces with its last-known fields (minimal, predictable). * - EDGES are identified by their endpoints ("a:out->b:in"); parallel edges * between the same port pair are not representable (matching rgui's Graph). * An edge is materialized only while both endpoint nodes are live. * - Replicas must share the same `joins` schema: merge() throws on mismatch * instead of silently diverging projections. * * No Date.now, no randomness: the host supplies a stable unique `actor` id, * sequence numbers are per-state Lamport counters advanced by ops and merges. */ /** one write event: a per-actor Lamport sequence number */ export interface Dot { actor: string; seq: number; } /** version vector — highest seq observed per actor (the causal context) */ export type Clock = Record; /** typed semilattice joins usable instead of LWW for a field key */ export type JoinRule = "max" | "min" | "any" | "all"; /** JSON-safe field value (registers hold plain data, never functions) */ export type CrdtValue = string | number | boolean | null | CrdtValue[] | { [key: string]: CrdtValue; }; export interface CrdtRegister { value: CrdtValue; dot: Dot; } interface CrdtElement { /** live add-dots — empty means removed (element record may linger; it is * presence-dead and skipped at materialization) */ dots: Dot[]; fields: Record; } export interface CrdtEdgeEnd { node: string; port: string; } export interface GraphCrdtState { /** schema tag: per-field join rules; must match across replicas */ joins: Record; clock: Clock; nodes: Record; edges: Record; } /** materialized plain-data view (host maps this onto a render Graph) */ export interface CrdtGraph { nodes: { id: string; fields: Record; }[]; edges: { id: string; from: CrdtEdgeEnd; to: CrdtEdgeEnd; }[]; } export declare function newGraphCrdt(joins?: Record): GraphCrdtState; /** * Deterministic endpoint-derived edge id (no parallel edges by design). * JSON-array encoded so it stays injective for ARBITRARY node/port strings — * a plain `a:b->c:d` template would collide when ids contain the delimiters. */ export declare function crdtEdgeId(from: CrdtEdgeEnd, to: CrdtEdgeEnd): string; /** add a node (or revive a removed one), optionally writing fields */ export declare function crdtAddNode(state: GraphCrdtState, actor: string, id: string, fields?: Record): void; /** write fields on a live node (no-op on unknown/removed nodes) */ export declare function crdtSetFields(state: GraphCrdtState, actor: string, id: string, fields: Record): void; export declare function crdtRemoveNode(state: GraphCrdtState, id: string): void; export declare function crdtAddEdge(state: GraphCrdtState, actor: string, from: CrdtEdgeEnd, to: CrdtEdgeEnd, fields?: Record): string; export declare function crdtRemoveEdge(state: GraphCrdtState, from: CrdtEdgeEnd, to: CrdtEdgeEnd): void; /** * Deterministic state merge — commutative, associative, idempotent. Inputs * are not mutated. Throws if the two states declare different join schemas * (silently merging mismatched schemas would diverge projections). */ export declare function mergeGraphCrdt(a: GraphCrdtState, b: GraphCrdtState): GraphCrdtState; /** * Materialize the plain-data graph view: live nodes with their current field * values, and live edges whose BOTH endpoint nodes are live. Output ordering * and field-key ordering are sorted, so equal states materialize into * byte-identical views regardless of merge order. */ export declare function crdtToGraph(state: GraphCrdtState): CrdtGraph; export {}; //# sourceMappingURL=crdt.d.ts.map