Skip to main content
Reference status

This page predates the current package split. Use the current package guides for checked installation, public imports, and onboarding examples. The detailed examples below have not all been revalidated.

DataStore API

The DataStore API provides the core storage layer for managing nodes, transactions, and schema validation.

DataStore Class​

The main storage class that manages nodes with transactional overlay and lock support.

Constructor​

new DataStore(rootNodeId?: string, schema?: Schema, sessionId?: number)

Parameters:

  • rootNodeId?: string - Optional root node ID
  • schema?: Schema - Optional schema for validation
  • sessionId?: number - Optional session ID (default: 0)

Example:

import { DataStore } from '@barocss/datastore';
import { createSchema } from '@barocss/schema';

const schema = createSchema('my-doc', { /* ... */ });
const dataStore = new DataStore(undefined, schema, 0);

Properties​

core: CoreOperations​

Core operations instance (read-only).

query: QueryOperations​

Query operations instance (read-only).

content: ContentOperations​

Content operations instance (read-only).

splitMerge: SplitMergeOperations​

Split/merge operations instance (read-only).

marks: MarkOperations​

Mark operations instance (read-only).

decorators: DecoratorOperations​

Decorator operations instance (read-only).

utility: UtilityOperations​

Utility operations instance (read-only).

range: RangeOperations​

Range operations instance (read-only).

serialization: SerializationOperations​

Serialization operations instance (read-only).

Core Methods​

getNode(nodeId: string): INode | undefined​

Gets a node by ID (overlay-aware).

Parameters:

  • nodeId: string - Node ID (supports alias)

Returns:

  • INode | undefined - Node or undefined

Read Path:

  1. Check deletedNodeIds → return undefined if deleted
  2. Check overlayNodes → return overlay version if exists
  3. Fallback to baseNodes → return base version

Example:

const node = dataStore.getNode('text-1');

setNode(node: INode, validate?: boolean): void​

Sets a node (creates or updates).

Parameters:

  • node: INode - Node to set (assigns sid if missing)
  • validate?: boolean - Whether to validate (default: true)

Behavior:

  • Assigns sid if missing using generateId()
  • Validates against schema if validate=true and schema exists
  • Converts object children in content to IDs recursively
  • Overlay-aware: writes go to overlay if transaction active
  • Emits 'create' or 'update' operation event

Example:

dataStore.setNode({
sid: 'p1',
stype: 'paragraph',
text: 'Hello',
content: []
});

updateNode(nodeId: string, updates: Partial<INode>, validate?: boolean): { valid: boolean; errors: string[] } | null​

Updates a node with partial changes.

Parameters:

  • nodeId: string - Node ID to update
  • updates: Partial<INode> - Partial node data to apply
  • validate?: boolean - Whether to validate (default: true)

Returns:

  • Validation result: { valid: boolean; errors: string[] } or null

Behavior:

  • Merges fields (attributes shallow-merge)
  • No-op suppression: skips if all fields are unchanged
  • Validates against schema if validate=true and not content update
  • Content updates bypass validation (IDs only)
  • Overlay-aware: writes go to overlay if transaction active
  • Emits 'update' operation event

Example:

const result = dataStore.updateNode('text-1', { 
text: 'Updated text'
});

deleteNode(nodeId: string): boolean​

Deletes a node.

Parameters:

  • nodeId: string - Node ID to delete

Returns:

  • boolean - true if deleted, false if node not found

Behavior:

  • Cannot delete root node (throws error)
  • Removes node from parent's content array
  • Emits 'delete' operation event
  • Overlay-aware

Example:

const deleted = dataStore.deleteNode('node-1');

createNodeWithChildren(node: INode, schema?: Schema): INode​

Creates a node with all its children recursively.

Parameters:

  • node: INode - Node with nested children (objects)
  • schema?: Schema - Optional schema for validation

Returns:

  • INode - Created node with assigned IDs

Behavior:

  • Recursively creates all child nodes
  • Assigns IDs to all nodes
  • Converts object children to ID arrays
  • Validates against schema
  • Overlay-aware

Example:

const root = dataStore.createNodeWithChildren({
stype: 'document',
content: [
{
stype: 'paragraph',
content: [
{ stype: 'inline-text', text: 'Hello' }
]
}
]
});

Transaction Management​

Transaction Overlay (Copy-on-Write)​

DataStore uses a transactional overlay system for atomic operations.

begin(): void​

Starts a transaction (activates overlay).

Behavior:

  • Initializes overlay if not exists
  • Activates overlay state
  • All subsequent writes go to overlay
  • Initializes overlay alias map

Example:

dataStore.begin();
// All writes now go to overlay
dataStore.setNode(node);

end(): AtomicOperation[]​

Ends transaction collection and returns operations.

Returns:

  • AtomicOperation[] - Array of collected operations

Behavior:

  • Returns snapshot of collected operations
  • Overlay remains active until commit() or rollback()
  • Does not apply changes to base

Example:

dataStore.begin();
dataStore.setNode(node1);
dataStore.setNode(node2);
const operations = dataStore.end();
// operations: [{ type: 'create', nodeId: '...', ... }, ...]

commit(): void​

Commits transaction to base storage.

Behavior:

  • Applies overlay operations to base in order: create → update → move → delete
  • Merges fields for update operations (shallow-merge attributes)
  • Applies overlay root change if present
  • Clears overlay and alias map
  • Operations are now permanent

Example:

dataStore.begin();
dataStore.setNode(node);
dataStore.end();
dataStore.commit(); // Changes are now permanent

rollback(): void​

Rolls back transaction.

Behavior:

  • Discards overlay state
  • Clears overlay and alias map
  • No events emitted

Example:

dataStore.begin();
dataStore.setNode(node);
dataStore.rollback(); // Changes discarded

getCollectedOperations(): AtomicOperation[]​

Gets currently collected operations (without ending transaction).

Returns:

  • AtomicOperation[] - Array of collected operations

Example:

dataStore.begin();
dataStore.setNode(node);
const ops = dataStore.getCollectedOperations();
// ops: [{ type: 'create', ... }]

Lock System​

DataStore uses a global lock system to prevent concurrent modifications.

acquireLock(ownerId?: string): Promise<string>​

Acquires a global write lock.

Parameters:

  • ownerId?: string - Owner identifier (default: 'unknown')

Returns:

  • Promise<string> - Lock ID

Behavior:

  • Waits if lock is already held
  • Returns lock ID on acquisition
  • Times out after 5 seconds (default)
  • Queues requests if lock is busy

Example:

const lockId = await dataStore.acquireLock('my-transaction');
try {
// Perform operations
} finally {
dataStore.releaseLock(lockId);
}

releaseLock(lockId?: string): void​

Releases a global write lock.

Parameters:

  • lockId?: string - Lock ID to release (optional, releases current lock if not provided)

Behavior:

  • Releases lock
  • Processes next queued request if any

Example:

const lockId = await dataStore.acquireLock();
// ... operations ...
dataStore.releaseLock(lockId);

getLockStats(): LockStats​

Gets lock statistics.

Returns:

{
totalAcquisitions: number;
totalReleases: number;
totalTimeouts: number;
averageWaitTime: number;
}

Example:

const stats = dataStore.getLockStats();
console.log('Lock stats:', stats);

Schema Management​

registerSchema(schema: Schema): void​

Registers a schema.

Parameters:

  • schema: Schema - Schema to register

Example:

const schema = createSchema('my-doc', { /* ... */ });
dataStore.registerSchema(schema);

setActiveSchema(schema: Schema): void​

Sets the active schema.

Parameters:

  • schema: Schema - Schema to activate

Behavior:

  • Sets as active schema
  • Registers schema if not already registered

Example:

dataStore.setActiveSchema(schema);

getActiveSchema(): Schema | undefined​

Gets the active schema.

Returns:

  • Schema | undefined - Active schema or undefined

Example:

const schema = dataStore.getActiveSchema();

validateNode(node: INode, schema?: Schema): { valid: boolean; errors: string[] }​

Validates a node against schema.

Parameters:

  • node: INode - Node to validate
  • schema?: Schema - Optional schema (uses active schema if not provided)

Returns:

  • { valid: boolean; errors: string[] } - Validation result

Example:

const result = dataStore.validateNode(node);
if (!result.valid) {
console.error('Validation errors:', result.errors);
}

Node Management​

getRootNode(): INode | undefined​

Gets the root node.

Returns:

  • INode | undefined - Root node or undefined

Behavior:

  • If rootNodeId not set, infers and sets first node
  • Returns root node of current context (including overlay)

Example:

const root = dataStore.getRootNode();

setRootNodeId(nodeId: string): void​

Sets the root node ID.

Parameters:

  • nodeId: string - Root node ID

Behavior:

  • Applied immediately when overlay is inactive
  • setRoot() reflects in overlay when overlay is active

setRoot(rootId: string): void​

Sets root based on transaction overlay.

Parameters:

  • rootId: string - Root node ID

Behavior:

  • When overlay is active: reflected in overlay, applied on commit()
  • When overlay is inactive: immediately reflected in base

getRootNodeId(): string | undefined​

Gets the root node ID.

Returns:

  • string | undefined - Root node ID or undefined

ID Generation​

generateId(): string​

Generates a unique node ID (Figma style).

Returns:

  • string - Unique ID (format: sessionId:counter, e.g., '0:1', '0:2')

Example:

const id = dataStore.generateId();
// '0:1', '0:2', etc.

getSessionId(): number​

Gets the current session ID.

Returns:

  • number - Session ID

setSessionId(sessionId: number): void​

Sets the session ID.

Parameters:

  • sessionId: number - New session ID

Example:

dataStore.setSessionId(1);
dataStore.generateId(); // '1:1', '1:2', ...

Alias System​

Aliases provide temporary names for nodes during transactions.

setAlias(alias: string, id: string): void​

Registers an alias (overlay-scoped).

Parameters:

  • alias: string - Alias name
  • id: string - Node ID

Behavior:

  • Alias must be unique within overlay scope
  • Throws error if duplicate registration with different ID
  • Cleared on commit() or rollback()

Example:

dataStore.begin();
dataStore.setAlias('new-paragraph', 'p-123');
// Use alias in operations
dataStore.getNode('new-paragraph'); // Returns node with ID 'p-123'

resolveAlias(idOrAlias: string): string​

Resolves alias to actual ID.

Parameters:

  • idOrAlias: string - Alias or actual ID

Returns:

  • string - Actual node ID

Example:

dataStore.setAlias('my-node', 'node-1');
const actualId = dataStore.resolveAlias('my-node'); // 'node-1'

deleteAlias(alias: string): void​

Removes an alias mapping.

Parameters:

  • alias: string - Alias to remove

clearAliases(): void​

Clears all alias mappings.

getAliases(): ReadonlyMap<string, string>​

Gets all aliases as read-only map.

Returns:

  • ReadonlyMap<string, string> - Alias map

Query Methods​

Query methods delegate to QueryOperations. See DataStore Operations API for details.

findNodes(predicate: (node: INode) => boolean): INode[]​

Finds nodes matching a predicate.

findNodesByType(stype: string): INode[]​

Finds nodes by schema type.

findNodesByAttribute(key: string, value: any): INode[]​

Finds nodes by attribute value.

findNodesByText(text: string): INode[]​

Finds nodes containing text.

findChildrenByParentId(parentId: string): INode[]​

Gets direct children of a node.

findRootNodes(): INode[]​

Finds all root nodes.

getNodeChildren(nodeId: string): INode[]​

Gets direct children as object array.

getNodeChildrenDeep(nodeId: string): INode[]​

Gets all descendants recursively.

getNodeWithChildren(nodeId: string): INode | null​

Gets node with children as object array.

getAllNodesWithChildren(): INode[]​

Gets all nodes with children as object arrays.

searchText(query: string): INode[]​

Searches for nodes containing text.


Utility Methods​

Utility methods delegate to UtilityOperations. See DataStore Operations API for details.

getChildren(nodeId: string): INode[]​

Gets direct children.

getParent(nodeId: string): INode | undefined​

Gets parent node.

getSiblings(nodeId: string): INode[]​

Gets sibling nodes.

getSiblingIndex(nodeId: string): number​

Gets sibling index.

getPreviousSibling(nodeId: string): string | null​

Gets previous sibling ID.

getNextSibling(nodeId: string): string | null​

Gets next sibling ID.

getFirstChild(nodeId: string): string | null​

Gets first child ID.

getLastChild(nodeId: string): string | null​

Gets last child ID.

getCommonAncestor(nodeId1: string, nodeId2: string): string | null​

Gets common ancestor of two nodes.

getDistance(nodeId1: string, nodeId2: string): number​

Calculates distance between two nodes.

getNodePath(nodeId: string): string[]​

Gets node path from root.

getNodeDepth(nodeId: string): number​

Gets node depth.

isDescendant(nodeId: string, ancestorId: string): boolean​

Checks if node is descendant of ancestor.

getAllDescendants(nodeId: string): INode[]​

Gets all descendants.

getAllAncestors(nodeId: string): INode[]​

Gets all ancestors.

getNodeCount(): number​

Gets total node count.

clone(): DataStore​

Clones DataStore instance.

hasNode(nodeId: string): boolean​

Checks if node exists.

getChildCount(nodeId: string): number​

Gets child count.

isLeafNode(nodeId: string): boolean​

Checks if node is leaf.

isRootNode(nodeId: string): boolean​

Checks if node is root.

isEditableNode(nodeId: string): boolean​

Checks if node is editable.

compareDocumentOrder(nodeId1: string, nodeId2: string): number​

Compares document order of two nodes.

getNextNode(nodeId: string): string | null​

Gets next node in document order.

getPreviousNode(nodeId: string): string | null​

Gets previous node in document order.


Content Operations​

Content operations delegate to ContentOperations. See DataStore Operations API for details.

addChild(parentId: string, child: INode | string, position?: number): string​

Adds a child to parent.

removeChild(parentId: string, childId: string): boolean​

Removes a child from parent.

moveNode(nodeId: string, newParentId: string, position?: number): void​

Moves a node to new parent.

reorderChildren(parentId: string, childIds: string[]): void​

Reorders children.

copyNode(nodeId: string, newParentId?: string): string​

Copies a node.

cloneNodeWithChildren(nodeId: string, newParentId?: string): string​

Clones a node with all children.

moveBlockUp(nodeId: string): boolean​

Moves block up.

moveBlockDown(nodeId: string): boolean​

Moves block down.

transformNode(nodeId: string, newType: string, newAttrs?: Record<string, any>): { valid: boolean; errors: string[]; newNodeId?: string }​

Transforms a node to different type.


Range Operations​

Range operations delegate to RangeOperations. See DataStore Operations API for details.

insertText(contentRange: ModelSelection, text: string): string​

Inserts text at range.

deleteText(contentRange: ModelSelection): string​

Deletes text in range.

replaceText(contentRange: ModelSelection, newText: string): string​

Replaces text in range.

extractText(contentRange: ModelSelection): string​

Extracts text from range.

applyMark(contentRange: ModelSelection, mark: IMark): IMark​

Applies mark to range.

removeMark(contentRange: ModelSelection, markType: string): number​

Removes mark from range.

toggleMark(contentRange: ModelSelection, markType: string, attrs?: Record<string, any>): void​

Toggles mark on/off.

clearFormatting(contentRange: ModelSelection): number​

Clears all formatting.


Mark Operations​

Mark operations delegate to MarkOperations. See DataStore Operations API for details.

normalizeMarks(nodeId: string): void​

Normalizes marks for a node.

normalizeAllMarks(): number​

Normalizes marks for all nodes.

getMarkStatistics(nodeId: string): MarkStatistics​

Gets mark statistics.

removeEmptyMarks(nodeId: string): number​

Removes empty marks.


Split/Merge Operations​

Split/merge operations delegate to SplitMergeOperations. See DataStore Operations API for details.

splitTextNode(nodeId: string, splitPosition: number): string​

Splits a text node.

mergeTextNodes(leftNodeId: string, rightNodeId: string): string​

Merges two text nodes.

splitBlockNode(nodeId: string, splitPosition: number): string​

Splits a block node.

mergeBlockNodes(leftNodeId: string, rightNodeId: string): string​

Merges two block nodes.


Serialization​

serializeRange(range: ModelSelection): INode[]​

Serializes selection range to INode array.

Parameters:

  • range: ModelSelection - Selection range

Returns:

  • INode[] - Array of nodes in range

Example:

const nodes = dataStore.serializeRange({
startNodeId: 'text-1',
startOffset: 0,
endNodeId: 'text-1',
endOffset: 5
});

deserializeNodes(nodes: INode[], targetParentId: string, targetPosition?: number): string[]​

Deserializes INode array and inserts at position.

Parameters:

  • nodes: INode[] - Array of nodes to insert
  • targetParentId: string - Target parent ID
  • targetPosition?: number - Optional position

Returns:

  • string[] - Array of inserted node IDs

Example:

const nodeIds = dataStore.deserializeNodes(nodes, 'parent-1', 0);

Document Management​

saveDocumentInternal(document: Document, validate?: boolean): { valid: boolean; errors: string[] }​

Saves a document internally.

Parameters:

  • document: Document - Document to save
  • validate?: boolean - Whether to validate (default: true)

Returns:

  • { valid: boolean; errors: string[] } - Validation result

getDocument(documentId: string): Document | undefined​

Gets a document.

Parameters:

  • documentId: string - Document ID (or 'root')

Returns:

  • Document | undefined - Document or undefined

updateDocument(documentId: string, updates: Partial<Document>, validate?: boolean): { valid: boolean; errors: string[] }​

Updates a document.

Parameters:

  • documentId: string - Document ID
  • updates: Partial<Document> - Updates to apply
  • validate?: boolean - Whether to validate (default: true)

Returns:

  • { valid: boolean; errors: string[] } - Validation result

deleteDocument(documentId: string): boolean​

Deletes a document.

Parameters:

  • documentId: string - Document ID

Returns:

  • boolean - true if deleted

Behavior:

  • Cannot delete root document (throws error)

getAllDocuments(): Document[]​

Gets all documents.

Returns:

  • Document[] - Array of documents

Version Management​

getVersion(): number​

Gets current version.

Returns:

  • number - Version number

setVersion(version: number): void​

Sets version.

Parameters:

  • version: number - New version

Operation Events​

emitOperation(operation: AtomicOperation): void​

Emits an operation event.

Parameters:

  • operation: AtomicOperation - Operation to emit

Behavior:

  • Records in overlay if overlay active
  • Emits 'operation' event

onOperation(callback: (operation: AtomicOperation) => void): void​

Subscribes to operation events.

Parameters:

  • callback: Function - Callback function

Example:

dataStore.onOperation((operation) => {
console.log('Operation:', operation.type, operation.nodeId);
});

offOperation(callback: (operation: AtomicOperation) => void): void​

Unsubscribes from operation events.

Parameters:

  • callback: Function - Callback function to remove

Internal Methods​

getNodes(): Map<string, INode>​

Gets internal nodes map (read-only).

Returns:

  • Map<string, INode> - Nodes map

setNodes(nodes: Map<string, INode>): void​

Sets internal nodes map (internal use).

Parameters:

  • nodes: Map<string, INode> - Nodes map

clear(): void​

Clears all nodes.

Behavior:

  • Clears nodes map
  • Resets root node ID
  • Resets version to 1

restoreFromSnapshot(nodes: Map<string, INode>, rootNodeId?: string, version?: number): void​

Restores from snapshot.

Parameters:

  • nodes: Map<string, INode> - Nodes map
  • rootNodeId?: string - Root node ID
  • version?: number - Version (default: 1)

Complete Example​

import { DataStore } from '@barocss/datastore';
import { createSchema } from '@barocss/schema';

// Create schema
const schema = createSchema('article', {
topNode: 'document',
nodes: {
document: { name: 'document', group: 'document', content: 'block+' },
paragraph: { name: 'paragraph', group: 'block', content: 'inline*' },
'inline-text': { name: 'inline-text', group: 'inline' }
}
});

// Create DataStore
const dataStore = new DataStore(undefined, schema, 0);

// Set active schema
dataStore.setActiveSchema(schema);

// Acquire lock
const lockId = await dataStore.acquireLock('my-transaction');

try {
// Begin transaction
dataStore.begin();

// Create nodes
const paragraph = dataStore.createNodeWithChildren({
stype: 'paragraph',
content: [
{ stype: 'inline-text', text: 'Hello World' }
]
});

const document = dataStore.createNodeWithChildren({
stype: 'document',
content: [paragraph.sid!]
});

// Set root
dataStore.setRoot(document.sid!);

// End transaction
const operations = dataStore.end();

// Commit
dataStore.commit();

console.log('Document created:', document.sid);
console.log('Operations:', operations);
} finally {
// Release lock
dataStore.releaseLock(lockId);
}

// Query nodes
const paragraphs = dataStore.findNodesByType('paragraph');
const root = dataStore.getRootNode();

// Listen to operations
dataStore.onOperation((operation) => {
console.log('Operation:', operation.type, operation.nodeId);
});