Skip to content

@actdim/livestruct-demo Domain Model ​

The demo app lives in demo/ of this repository; file paths in this article are relative to demo/.

1. Entity Taxonomy ​

EntityDefined inDescription
CadShapesrc/cad/types.tsOne drawing primitive: rect, circle or line.
CadDocumentsrc/cad/types.tsThe edited document: title, selection and a map of shapes. Stored as a LiveStruct document.
CadStoresrc/cad/store.tsPer-context facade: LiveStruct store, cad_documents collection, doc-default document, msgmesh bus, counters, undo/redo stacks.
CursorDragPayloadsrc/cad/types.tsMessage payload of the bus channel cad.cursor.drag.
TelemetryEventsrc/telemetry/types.tsOne row of the protocol monitor.
TelemetryMetricssrc/telemetry/types.tsAggregates shown in the monitor's metrics bar.
.cad project filesrc/cad/exportImport.tsPortable SQLite 3 file with one cad_documents row.
docs row (spike)src/livestore/schema.tsRaw LiveStore table used only by the ?spike=livestore harness.
BoundingBox, TransformHandlesrc/cad/types.tsDerived geometry for hit testing and selection rendering (src/cad/geometry.ts).

2. Front-matter Schemas ​

The demo has no Markdown entities of its own; its "records" are TypeScript and storage schemas.

2.1. CAD shape ​

typescript
export type CadShapeType = 'rect' | 'circle' | 'line';

export interface CadShape {
    id: string;
    type: CadShapeType;
    x: number;
    y: number;
    width?: number;   // rect
    height?: number;  // rect
    radius?: number;  // circle
    x2?: number;      // line end
    y2?: number;      // line end
    name?: string;
    layer?: string;   // inspector default: 'geometry'
    fill?: string;
    stroke?: string;
    strokeWidth?: number;
    rotation?: number;
}

2.2. CAD document ​

typescript
export interface CadDocument {
    id: string;
    title: string;
    selectedShapeId?: string | null;
    shapes: Record<string, CadShape>;
    activeLayer?: string;
    metadata?: Record<string, unknown>;
}

initCadStore creates the document once per store if it is missing:

FieldSeed value
collectioncad_documents
document iddoc-default
titleMechanical Assembly
selectedShapeIdrect-1
shapesDEFAULT_SHAPES or options.initialShapes

DEFAULT_SHAPES:

idtypenamegeometry
rect-1rectMain Bracketx 60, y 60, 140 x 90
circle-1circleBearing Mountx 320, y 120, radius 45
line-1lineCenter Axis(60, 240) -> (360, 240)

All mutations are partial deep patches of CadDocument; a shape key set to null removes that shape (used by undo/redo and import).

2.3. Bus channels and payloads ​

The bus is createMsgBus<CadMsgStruct>() from @actdim/msgmesh/core (or options.bus).

typescript
export interface CursorDragPayload {
    shapeId: string;
    x: number;
    y: number;
    dx: number;
    dy: number;
    timestamp: number;
    senderId?: string;
}

export type CadMsgStruct = {
    'cad.cursor.drag': { in: CursorDragPayload };
};

export const CAD_DRAG_CHANNEL = 'cad.cursor.drag' as const;

TelemetryMonitor listens on CAD_DRAG_CHANNEL with an abort signal and records each message as an ephemeral event, computing latency from payload.timestamp. Channel semantics (groups, request/response) are described on the msgmesh and msgmesh-store sites (https://actdim.github.io/msgmesh-store/).

2.4. LiveStore schema usage ​

  • CAD app: src/livestore/worker.ts runs makeWorker({ schema }) with the schema from @actdim/livestruct/engine. The demo does not define tables for the CAD document; it uses the LiveStruct collection API (liveStore.collection<CadDocument>('cad_documents'), collection.get, collection.create, doc.patch, doc.flush, doc.$, doc.isTentative). Storage layout is described at https://actdim.github.io/livestruct/.
  • Spike harness: src/livestore/schema.ts defines a standalone LiveStore schema used with makePersistedAdapter and storeId: 'spike-livestore-cad':
ElementDefinition
table docsid text primary key, body text JSON (default {}), mutation_id text, origin text
event doc.created{ id, body, mutationId, origin } -> insert row
event doc.patched{ id, patch, mutationId, origin } -> UPDATE docs SET body = json_patch(body, ?) ...
event doc.deleted{ id, mutationId, origin } -> delete row

2.5. .cad project file ​

sql
CREATE TABLE cad_documents (id TEXT PRIMARY KEY, title TEXT, data TEXT)

One row: id = document id (default doc-default), title, data = JSON.stringify(CadDocument). Import also accepts { "documents": { "cad_documents:doc-default": { "data": CadDocument } } } and a bare CadDocument JSON object.

2.6. Telemetry messages ​

typescript
export type TelemetryCategory = 'ephemeral' | 'sqlite' | 'cdc';

export interface TelemetryEvent {
    id: string;
    timestamp: number;
    category: TelemetryCategory;
    channel: string;
    latencyUs?: number;
    sizeBytes?: number;
    updatedFields?: string[];
    rev?: number;
    origin?: string;
    payloadSummary: string;
    rawPayload?: unknown;
}

Mapping from sources to monitor rows:

SourceChannelCategoryBadge
LiveStruct local_patchlivestruct.patchephemeralRAM
LiveStruct overlay_sentlivestruct.overlay.sentephemeralRAM
LiveStruct overlay_receivedlivestruct.overlay.receivedcdcCDC
LiveStruct commitsqlite.commitsqliteSQLITE COMMIT (with rev, size, fields)
LiveStruct remote_commitcdc.replicacdcCDC REPLICA (with origin)
Bus cad.cursor.dragcad.cursor.dragephemeralRAM
"Simulate CDC" buttoncdc.replica (origin tab-2)cdcCDC REPLICA

updatedFields are leaf dot paths from extractUpdatedFields(patch) (for example shapes.rect-1.x); sizeBytes is the UTF-8 JSON size from calculatePayloadSize. TelemetryMetrics holds messagesPerSec (rolling 1 s window from calculateThroughput), diskWriteCount (from CadStore.diskCommitCount when a store exists), and per-category totals. Filters accept ALL, cad.*, sqlite.*, cdc.* or free text matched against channel, category, payload summary, origin and fields.

3. Graph Relationships ​

  • CadDocument.selectedShapeId references a key of CadDocument.shapes or is null.
  • Each browsing context owns one CadStore; all contexts of one origin share the persisted cad_documents/doc-default through LiveStore.
  • Undo/redo stacks hold whole CadDocument snapshots and are not persisted.

Released under the MIT License.