@runtimed/schema
v0.3.0
Published
Schema definitions for Anode - event-sourced notebook system
Readme
@runtimed/schema
LiveStore schema for Anode notebooks, defining events, tables, and types with conflict-free cell ordering and real-time collaboration support.
Usage
import { createCellBetween, events, schema, tables } from "@runtimed/schema";
// Create cells using the helper (recommended approach)
const createEvent = createCellBetween(
{
id: "cell-123",
cellType: "code",
createdBy: "my-runtime",
},
cellBefore, // CellReference | null
cellAfter // CellReference | null
);
store.commit(createEvent);
// Query tables
const cells = store.query(tables.cells.select().where({ cellType: "code" }));
const outputs = store.query(
tables.outputs.select().where({ cellId: "cell-123" })
);Core Functions
createCellBetween
Primary helper for creating cells with proper fractional indexing. Internally
uses events.cellCreated2.
createCellBetween(
cellData: {
id: string;
cellType: CellType;
createdBy: string;
},
cellBefore: CellReference | null,
cellAfter: CellReference | null,
jitterProvider?: JitterProvider
): ReturnType<typeof events.cellCreated2>Examples:
// Insert at beginning
const event = createCellBetween(cellData, null, firstCell);
// Insert at end
const event = createCellBetween(cellData, lastCell, null);
// Insert between cells
const event = createCellBetween(cellData, cell1, cell2);
// Custom jitter for testing
const event = createCellBetween(cellData, null, null, mockJitterProvider);moveCellBetween
Helper for repositioning existing cells with conflict-free ordering.
moveCellBetween(
cellId: string,
cellBefore: CellReference | null,
cellAfter: CellReference | null,
jitterProvider?: JitterProvider
): ReturnType<typeof events.cellMoved2>Schema Structure
Events
Cell Lifecycle:
cellCreated2- Created viacreateCellBetweenwith fractional indexingcellUpdated- Content or metadata changescellDeleted- Cell removalcellMoved2- Position changes viamoveCellBetween
Execution:
executionRequested- Queued for executionexecutionStarted- Runtime begins processingexecutionCompleted- Finished with success/error state
Outputs:
cellOutputAdded- Rich display data, stdout, stderrcellOutputsCleared- Remove all outputs for cell
Runtime Management:
runtimeSessionStarted- New runtime connectionruntimeSessionHeartbeat- Keep-alive signalruntimeSessionTerminated- Runtime disconnect
Tables
cells - Cell content and state
id,cellType,source,fractionalIndexcreatedAt,updatedAt,createdByexecutionCount,lastExecutedAt
outputs - Rich execution results
cellId,outputType,data(MediaBundle)executionCount,createdAt
executionQueue - Pending executions
cellId,status,requestedAtstartedAt,sessionId
runtimeSessions - Active runtimes
sessionId,runtimeType,startedAtlastHeartbeatAt,capabilities
notebook - Metadata
id,title,createdAt,updatedAt
Key Types
type CellType = "code" | "markdown" | "sql" | "ai";
interface CellData {
id: string;
cellType: CellType;
source: string;
fractionalIndex: string;
createdBy: string;
executionCount: number;
}
interface CellReference {
id: string;
fractionalIndex: string;
}
interface MediaBundle {
[mimeType: string]: string;
// e.g., "text/plain", "text/html", "image/png"
}Fractional Indexing
Cell ordering uses fractional indices to avoid conflicts during concurrent edits:
// Indices are lexicographically ordered strings
"a" < "b" < "c" < "z";
"a0" < "a1" < "a2";
"aV" < "aW" < "aX";
// Always room to insert between any two indices
fractionalIndexBetween("a", "b"); // → "aV" (example)
fractionalIndexBetween("aV", "aW"); // → "aVV"Important Constraints
Materializer Purity: All materializers must be deterministic functions. No
Date(), Math.random(), or external state access. Use event data only.
Event Immutability: Once committed, events cannot be modified. Design schema changes carefully.
Cell Creation: Always use createCellBetween instead of direct
events.cellCreated2 to ensure proper indexing.
Session Overlap: Runtime restarts create new sessionId values. Handle
overlapping sessions during transitions.
Concurrent Safety: Fractional indices prevent ordering conflicts when multiple clients create cells simultaneously.
Development Notes
Package Configuration:
package.json- npm publishing and Node.js/TypeScript compatibility
Type Safety: All events and tables are fully typed. LiveStore enforces schema at runtime.
Testing: Use createTestJitterProvider() for deterministic fractional
indices in tests.
Migration from Legacy Events
Before (deprecated):
store.commit(events.cellCreated({ cellId, cellType, source, position }));After (current):
const event = createCellBetween(
{ id: cellId, cellType, createdBy: "runtime" },
cellBefore,
cellAfter
);
store.commit(event);The helper handles fractional indexing automatically and uses the current
cellCreated2 event internally.
