GitHub

TypeScript Types Reference

Complete reference for all exported TypeScript types in Vuer-RTC.

Core Message Types

CRDTMessage

The envelope containing CRDT metadata and a batch of operations. This is the fundamental unit of communication in Vuer-RTC.

interface CRDTMessage {
  // CRDT Metadata (wrapper)
  id: string;              // Message ID (UUID)
  sessionId: string;       // Session that created this message
  clock: VectorClock;      // Vector clock for causal ordering
  lamportTime: number;     // Lamport timestamp for total ordering
  timestamp: number;       // Wall-clock time (seconds since epoch)

  // Operations (batch)
  ops: Operation[];        // One or more operations
}

Example:

const message: CRDTMessage = {
  id: 'msg-uuid-123',
  sessionId: 'session-abc',
  clock: { 'session-abc': 5, 'session-xyz': 3 },
  lamportTime: 42,
  timestamp: 1708989600,
  ops: [
    { otype: 'vector3.set', key: 'cube', path: 'position', value: [1, 2, 3] }
  ]
};

Operation

Union type of all operation types. Every operation extends BaseOp.

type Operation =
  | NumberSetOp | NumberAddOp | NumberMultiplyOp | NumberMinOp | NumberMaxOp
  | StringSetOp | StringConcatOp
  | TextInitOp | TextInsertOp | TextDeleteOp | TextReplaceOp
  | BooleanSetOp | BooleanOrOp | BooleanAndOp
  | Vector3SetOp | Vector3AddOp | Vector3MultiplyOp | Vector3ApplyEulerOp | Vector3ApplyQuaternionOp
  | EulerSetOp | EulerAddOp
  | QuaternionSetOp | QuaternionMultiplyOp
  | ColorSetOp | ColorBlendOp
  | ArraySetOp | ArrayPushOp | ArrayUnionOp | ArrayRemoveOp
  | ObjectSetOp | ObjectMergeOp
  | NodeInsertOp | NodeRemoveOp | NodeMoveOp
  | MetaUndoOp | MetaRedoOp;

BaseOp

Base interface extended by all operations.

interface BaseOp {
  key: string;     // Node key (human-friendly, e.g., 'cube-1', 'player', 'scene')
  otype: string;   // dtype operation: 'number.set', 'vector3.add', etc.
  path: string;    // Property path: 'color', 'transform.position', etc.
}

Number Operations

NumberSetOp

interface NumberSetOp extends BaseOp {
  otype: 'number.set';
  value: number;
}

Example:

{ otype: 'number.set', key: 'light-1', path: 'intensity', value: 0.8 }

NumberAddOp

interface NumberAddOp extends BaseOp {
  otype: 'number.add';
  value: number;
}

Example:

{ otype: 'number.add', key: 'player', path: 'score', value: 100 }

NumberMultiplyOp

interface NumberMultiplyOp extends BaseOp {
  otype: 'number.multiply';
  value: number;
}

NumberMinOp

interface NumberMinOp extends BaseOp {
  otype: 'number.min';
  value: number;
}

NumberMaxOp

interface NumberMaxOp extends BaseOp {
  otype: 'number.max';
  value: number;
}

String Operations

StringSetOp

interface StringSetOp extends BaseOp {
  otype: 'string.set';
  value: string;
}

StringConcatOp

interface StringConcatOp extends BaseOp {
  otype: 'string.concat';
  value: string;
  separator?: string;  // Optional separator (default: '')
}

Text CRDT Operations

Text operations support collaborative character-level editing using a CRDT.

TextInitOp

Initialize a CRDT-backed text property. Must be called before using text operations.

interface TextInitOp extends BaseOp {
  otype: 'text.init';
  value?: string;  // Optional initial content
}

Example:

{ otype: 'text.init', key: 'doc', path: 'content', value: 'Hello world' }

TextInsertOp

Insert text at a position. Supports two formats:

  1. Position-based (local operations): { position, value }
  2. CRDT metadata (network sync): { id, content, parentId, seq }
interface TextInsertOp extends BaseOp {
  otype: 'text.insert';
  // Position-based format (local convenience)
  position?: number;
  value?: string;
  // CRDT metadata format (network sync)
  id?: { agent: string; seq: number };
  content?: string;
  parentId?: { agent: string; seq: number } | null;
  seq?: number;  // Lamport timestamp
}

Examples:

// Local format
{ otype: 'text.insert', key: 'doc', path: 'content', position: 5, value: 'world' }

// CRDT format (generated internally)
{
  otype: 'text.insert',
  key: 'doc',
  path: 'content',
  id: { agent: 'session-abc', seq: 10 },
  content: 'world',
  parentId: { agent: 'session-abc', seq: 9 },
  seq: 42
}

TextDeleteOp

Delete text at a position. Supports two formats:

  1. Position-based: { position, length }
  2. CRDT metadata: { deletions }
interface TextDeleteOp extends BaseOp {
  otype: 'text.delete';
  // Position-based format
  position?: number;
  length?: number;
  // CRDT metadata format
  deletions?: Array<{ id: { agent: string; seq: number }; length: number }>;
}

Examples:

// Local format
{ otype: 'text.delete', key: 'doc', path: 'content', position: 5, length: 3 }

// CRDT format
{
  otype: 'text.delete',
  key: 'doc',
  path: 'content',
  deletions: [
    { id: { agent: 'session-abc', seq: 10 }, length: 3 }
  ]
}

TextReplaceOp

Atomic delete + insert (for select-and-type).

interface TextReplaceOp extends BaseOp {
  otype: 'text.replace';
  // Position-based format
  position?: number;
  length?: number;    // chars to delete
  value?: string;     // chars to insert
  // CRDT metadata format
  deletions?: Array<{ id: { agent: string; seq: number }; length: number }>;
  id?: { agent: string; seq: number };
  content?: string;
  parentId?: { agent: string; seq: number } | null;
  seq?: number;
}

Boolean Operations

BooleanSetOp

interface BooleanSetOp extends BaseOp {
  otype: 'boolean.set';
  value: boolean;
}

BooleanOrOp

interface BooleanOrOp extends BaseOp {
  otype: 'boolean.or';
  value: boolean;
}

BooleanAndOp

interface BooleanAndOp extends BaseOp {
  otype: 'boolean.and';
  value: boolean;
}

Vector3 Operations

Vector3SetOp

interface Vector3SetOp extends BaseOp {
  otype: 'vector3.set';
  value: [number, number, number];
}

Example:

{ otype: 'vector3.set', key: 'cube', path: 'position', value: [1, 2, 3] }

Vector3AddOp

interface Vector3AddOp extends BaseOp {
  otype: 'vector3.add';
  value: [number, number, number];
}

Vector3MultiplyOp

interface Vector3MultiplyOp extends BaseOp {
  otype: 'vector3.multiply';
  value: [number, number, number];
}

Vector3ApplyEulerOp

Rotate a vector by euler angles.

type EulerOrder = 'XYZ' | 'XZY' | 'YXZ' | 'YZX' | 'ZXY' | 'ZYX';

interface Vector3ApplyEulerOp extends BaseOp {
  otype: 'vector3.applyEuler';
  value: [number, number, number];  // [x, y, z] rotation angles in radians
  order?: EulerOrder;               // Default: 'XYZ'
  intrinsic?: boolean;              // Default: true (axes move with body)
}

Example:

// Intrinsic YXZ rotation (common for FPS cameras)
{
  otype: 'vector3.applyEuler',
  key: 'camera',
  path: 'direction',
  value: [0, Math.PI/2, 0],
  order: 'YXZ'
}

Vector3ApplyQuaternionOp

interface Vector3ApplyQuaternionOp extends BaseOp {
  otype: 'vector3.applyQuaternion';
  value: [number, number, number, number];  // [x, y, z, w]
}

Euler Operations

EulerSetOp

interface EulerSetOp extends BaseOp {
  otype: 'euler.set';
  value: [number, number, number];  // [x, y, z] in radians
}

EulerAddOp

interface EulerAddOp extends BaseOp {
  otype: 'euler.add';
  value: [number, number, number];  // [x, y, z] in radians
}

Quaternion Operations

QuaternionSetOp

interface QuaternionSetOp extends BaseOp {
  otype: 'quaternion.set';
  value: [number, number, number, number];  // [x, y, z, w]
}

QuaternionMultiplyOp

interface QuaternionMultiplyOp extends BaseOp {
  otype: 'quaternion.multiply';
  value: [number, number, number, number];  // [x, y, z, w]
}

Color Operations

ColorSetOp

interface ColorSetOp extends BaseOp {
  otype: 'color.set';
  value: string;  // Hex color (e.g., '#ff0000')
}

ColorBlendOp

interface ColorBlendOp extends BaseOp {
  otype: 'color.blend';
  value: string;  // Hex color to blend towards
}

Array Operations

ArraySetOp

interface ArraySetOp extends BaseOp {
  otype: 'array.set';
  value: any[];
}

ArrayPushOp

interface ArrayPushOp extends BaseOp {
  otype: 'array.push';
  value: any;
}

ArrayUnionOp

interface ArrayUnionOp extends BaseOp {
  otype: 'array.union';
  value: any[];  // Add unique items
}

ArrayRemoveOp

interface ArrayRemoveOp extends BaseOp {
  otype: 'array.remove';
  value: any;
}

Object Operations

ObjectSetOp

interface ObjectSetOp extends BaseOp {
  otype: 'object.set';
  value: Record<string, any>;
}

ObjectMergeOp

interface ObjectMergeOp extends BaseOp {
  otype: 'object.merge';
  value: Record<string, any>;
}

Scene Graph Operations

NodeInsertOp

Create a new node as a child of parent.

interface NodeInsertOp extends BaseOp {
  key: string;           // Parent node's key
  otype: 'node.insert';
  path: 'children';
  value: {
    key: string;         // New node's key (also used as id)
    tag: string;         // 'Scene' | 'Mesh' | 'Group' | ...
    name: string;        // Display name
    [key: string]: any;  // Initial properties
  };
}

Example:

{
  otype: 'node.insert',
  key: 'scene',
  path: 'children',
  value: {
    key: 'cube-1',
    tag: 'Mesh',
    name: 'Red Cube',
    position: [0, 0, 0],
    color: '#ff0000'
  }
}

NodeRemoveOp

Remove a node from parent (tombstone).

interface NodeRemoveOp extends BaseOp {
  key: string;           // Parent node's key
  otype: 'node.remove';
  path: 'children';
  value: string;         // Node key to remove
}

NodeMoveOp

Move a node from one parent to another (reparent).

interface NodeMoveOp extends BaseOp {
  key: string;           // Old parent node's key
  otype: 'node.move';
  path: 'children';
  value: {
    nodeKey: string;     // Node key to move
    newParent: string;   // New parent node key
  };
}

Meta Operations (Undo/Redo)

MetaUndoOp

Mark a message as undone.

interface MetaUndoOp {
  key: '_meta';
  otype: 'meta.undo';
  path: '_meta';
  targetMsgId: string;  // Message ID to undo
}

MetaRedoOp

Unmark a message as undone.

interface MetaRedoOp {
  key: '_meta';
  otype: 'meta.redo';
  path: '_meta';
  targetMsgId: string;  // Message ID to redo
}

Scene Graph State

SceneNode

A node in the flattened scene graph. Properties are stored directly with dot-notation paths.

interface SceneNode {
  // Identity
  key: string;           // Unique key in scene (human-friendly)
  tag: string;           // Node type
  name: string;          // Display name

  // Tree structure
  children: string[];    // Child node keys

  // CRDT metadata envelope
  _crdt: CRDTEnvelope;

  // Dynamic properties (flat with dot-notation paths)
  // e.g., 'transform.position', 'color', 'opacity'
  [path: string]: unknown;
}

Example:

const node: SceneNode = {
  key: 'cube-1',
  tag: 'Mesh',
  name: 'Red Cube',
  children: [],
  _crdt: {
    clock: { 'session-abc': 5 },
    lamportTime: 42,
    createdAt: 1708989600,
    updatedAt: 1708989650,
    lww: {
      'position': { lamportTime: 42, sessionId: 'session-abc' },
      'color': { lamportTime: 43, sessionId: 'session-abc' }
    }
  },
  'position': [1, 2, 3],
  'color': '#ff0000',
  'opacity': 0.8
};

CRDTEnvelope

All CRDT metadata for a SceneNode, stored under _crdt.

interface CRDTEnvelope {
  clock: VectorClock;
  lamportTime: number;
  createdAt: number;              // Timestamp when node was created
  updatedAt: number;              // Last update timestamp
  deletedAt?: number;             // Tombstone for soft delete
  lww: Record<string, PropertyLWW>;  // Per-property LWW metadata
}

PropertyLWW

Per-property Last-Write-Wins metadata.

interface PropertyLWW {
  lamportTime: number;
  sessionId: string;
}

SceneGraph

Complete scene representation.

interface SceneGraph {
  nodes: Record<string, SceneNode>;  // Flattened map by key
  rootKey: string;                    // Root node key
}

Example:

const graph: SceneGraph = {
  rootKey: 'scene',
  nodes: {
    'scene': {
      key: 'scene',
      tag: 'Scene',
      name: 'Root',
      children: ['cube-1', 'light-1'],
      _crdt: { /* ... */ }
    },
    'cube-1': {
      key: 'cube-1',
      tag: 'Mesh',
      name: 'Red Cube',
      children: [],
      _crdt: { /* ... */ },
      'position': [1, 2, 3]
    }
  }
};

Client State

ClientState

Complete client-side state.

interface ClientState {
  // Current computed value (derived from snapshot + journal + edits)
  graph: SceneGraph;

  // Committed edits (sent or pending acknowledgement)
  journal: JournalEntry[];

  // Uncommitted operations (in-progress edits)
  edits: EditBuffer;

  // Checkpoint for fast replay
  snapshot: Snapshot;

  // Clocks
  lamportTime: number;
  vectorClock: VectorClock;
  sessionId: string;
}

JournalEntry

A committed message with metadata.

interface JournalEntry {
  msg: CRDTMessage;
  ack: boolean;           // Has server acknowledged?
  deletedAt?: number;     // If set, message is "undone"
}

EditBuffer

Uncommitted operations.

interface EditBuffer {
  ops: Operation[];                // Pending operations (merged)
  baseGraph: SceneGraph | null;    // Graph state when edits started
}

Snapshot

Checkpoint for fast replay.

interface Snapshot {
  graph: SceneGraph;
  vectorClock: VectorClock;
  lamportTime: number;    // Max lamport time baked in
  journalIndex: number;   // How many entries are baked in
}

GraphStore

The public API for the graph store.

interface GraphStore {
  // State access
  getState: () => ClientState;
  subscribe: (listener: () => void) => () => void;

  // Edit operations
  edit: (op: Operation) => void;
  commit: (description?: string) => CRDTMessage | null;

  // Server communication
  receive: (msg: CRDTMessage) => void;
  ack: (msgId: string) => void;
  loadServerState: (snapshot: Snapshot, journal: CRDTMessage[]) => void;

  // Undo/redo
  undo: () => { msg: CRDTMessage | null };
  redo: () => { msg: CRDTMessage | null };

  // Maintenance
  compact: () => void;
  compactToWatermark: (watermark: VectorClock) => void;
}

CreateGraphOptions

Options for creating a graph store.

interface CreateGraphOptions {
  sessionId: string;
  initialSnapshot?: Snapshot;
  onSend?: (msg: CRDTMessage) => void;
  onStateChange?: (state: ClientState) => void;
}

Vector Clock

VectorClock

CRDT-inspired vector clock for causal ordering.

type VectorClock = Record<string, number>;

Example:

const clock: VectorClock = {
  'session-abc': 5,
  'session-xyz': 3,
  'session-123': 7
};

Vector clocks track causality:

  • Each session has a counter
  • Counters increment with each operation
  • Clocks are merged to detect concurrent operations

TextRope Types

Types for the collaborative text editing CRDT.

ItemId

Identifier for a text item.

interface ItemId {
  agent: string;  // Session ID
  seq: number;    // Sequence number
}

Item

A text item in the rope.

interface Item {
  id: ItemId;
  content: string;
  isDeleted: boolean;
  parentId: ItemId | null;
  seq: number;  // Lamport timestamp
}

InsertOp

Insert operation for text rope.

interface InsertOp {
  id: ItemId;
  content: string;
  parentId: ItemId | null;
  seq: number;
}

DeleteOp

Delete operation for text rope.

interface DeleteOp {
  deletions: Array<{ id: ItemId; length: number }>;
}

ReplaceOp

Replace operation (atomic delete + insert).

interface ReplaceOp {
  delete: DeleteOp;
  insert: InsertOp;
}

MoveOp

Move operation (delete + insert at new position).

interface MoveOp {
  delete: DeleteOp;
  insert: InsertOp;
}

TextRope

High-performance text CRDT based on RGA/YATA.

class TextRope {
  _tree: ItemTree;
  _agentIndex: Map<string, SpanEntry[]>;
  agentId: string;
  clock: number;
  maxSeq: number;
}

Sync Types

SyncDigest

Bloom filter digest for sync reconciliation.

interface SyncDigest {
  mtype: 'sync';
  vectorClock: VectorClock;  // Covers compacted (snapshot) entries
  filter: Uint8Array;        // Bloom filter bytes (covers journal entries)
  count: number;             // Journal length (diagnostics)
}

Usage:

import { buildSyncDigest } from 'vuer-rtc';

const digest = buildSyncDigest(clientState);
// Send to server for efficient sync

Type Usage Examples

Creating a CRDTMessage

import { createGraph } from 'vuer-rtc';
import type { CRDTMessage, Operation } from 'vuer-rtc';

const store = createGraph({
  sessionId: 'session-abc',
  onSend: (msg: CRDTMessage) => {
    // Send to server
    console.log(`Sending ${msg.ops.length} operations`);
  }
});

// Edit operations are batched
store.edit({ otype: 'vector3.set', key: 'cube', path: 'position', value: [1, 2, 3] });
store.edit({ otype: 'color.set', key: 'cube', path: 'color', value: '#ff0000' });

// Commit creates a CRDTMessage with both ops
const msg = store.commit('Update cube');

Working with SceneGraph

import type { SceneGraph, SceneNode } from 'vuer-rtc';

const graph: SceneGraph = store.getState().graph;

// Access nodes
const cube: SceneNode | undefined = graph.nodes['cube-1'];

// Traverse children
const rootNode = graph.nodes[graph.rootKey];
rootNode.children.forEach(childKey => {
  const child = graph.nodes[childKey];
  console.log(`Child: ${child.name}`);
});

// Access properties with type safety
if (cube) {
  const position = cube['position'] as [number, number, number];
  const color = cube['color'] as string;
}

Implementing Custom Operations

import type { Operation, Vector3SetOp, ColorSetOp } from 'vuer-rtc';

function updateCubeColor(store: GraphStore, key: string, color: string) {
  const op: ColorSetOp = {
    otype: 'color.set',
    key,
    path: 'color',
    value: color
  };
  store.edit(op);
}

function moveCube(store: GraphStore, key: string, delta: [number, number, number]) {
  const op: Vector3AddOp = {
    otype: 'vector3.add',
    key,
    path: 'position',
    value: delta
  };
  store.edit(op);
}

Handling Text CRDT

import type { TextInitOp, TextInsertOp, TextDeleteOp } from 'vuer-rtc';

// Initialize text property
const initOp: TextInitOp = {
  otype: 'text.init',
  key: 'doc',
  path: 'content',
  value: 'Hello world'
};
store.edit(initOp);

// Insert text
const insertOp: TextInsertOp = {
  otype: 'text.insert',
  key: 'doc',
  path: 'content',
  position: 6,
  value: 'beautiful '
};
store.edit(insertOp);

// Delete text
const deleteOp: TextDeleteOp = {
  otype: 'text.delete',
  key: 'doc',
  path: 'content',
  position: 0,
  length: 5
};
store.edit(deleteOp);