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.
Transactions
Every document modification in Barocss goes through a transaction — an atomic, all-or-nothing unit of work. Transactions ensure data integrity, enable undo/redo, and coordinate with collaboration.
Transaction Lifecycle
Key Properties
| Property | Description |
|---|---|
| Atomic | All operations succeed or all are rolled back |
| Locked | Only one transaction runs at a time via global lock |
| Overlayed | Changes are buffered in a COW overlay before commit |
| Undoable | Forward and inverse operations are stored in history |
| Observable | Extensions receive onTransaction hooks after commit |
Creating Transactions
Using the Transaction DSL
The most common way to create transactions:
import { transaction, control, insertText, toggleMark } from '@barocss/model';
const result = await transaction(editor, [
...control('text-1', [
insertText({ text: 'Hello', offset: 0 }),
toggleMark('bold', [0, 5])
])
]).commit();
if (!result.success) {
console.error('Transaction failed:', result.errors);
}
Using executeTransaction
For programmatic use within extensions:
const result = await editor.executeTransaction([
{ type: 'insertText', payload: { nodeId: 'text-1', text: 'Hello', offset: 0 } },
{ type: 'toggleMark', payload: { nodeId: 'text-1', markType: 'bold', range: [0, 5] } }
]);
Copy-on-Write Overlay
The DataStore uses a Copy-on-Write (COW) overlay during transactions. This means the base data is never modified until commit():
Benefits:
- Reads during a transaction see the latest writes
- If the transaction fails, rollback is instant (just discard the overlay)
- No partial state is ever visible to other parts of the system
Global Lock
Only one transaction can execute at a time. The DataStore.acquireLock() system ensures serialized access:
// Internally, TransactionManager does:
const lockId = await dataStore.acquireLock('transaction-execution');
try {
// ... execute operations ...
} finally {
dataStore.releaseLock(lockId);
}
If another transaction is already running, acquireLock queues the request and waits.
TransactionResult
Every transaction returns a result:
interface TransactionResult {
success: boolean;
errors: string[];
transactionId?: string;
operations?: TransactionOperation[];
selectionBefore?: ModelSelection | null;
selectionAfter?: ModelSelection | null;
}
Error Handling and Rollback
If any operation fails, the overlay is rolled back and no changes are applied:
try {
// operations execute in overlay...
} catch (error) {
dataStore.rollback(); // discard overlay
return { success: false, errors: [error.message] };
}
Transaction Options
interface TransactionOptions {
applySelectionToView?: boolean; // sync selection to DOM (default: true)
preserveSelectionInHistory?: boolean; // store selection in history entry (default: true)
}
applySelectionToView: false— useful for remote sync or programmatic changes where you don't want to move the user's cursorpreserveSelectionInHistory: false— for operations where selection restoration doesn't make sense
Alias System
During a transaction, operations can create temporary aliases for newly created nodes. This is useful when a later operation needs to reference a node whose SID is generated at execution time:
// The insertParagraph operation creates a new node with a generated SID
// and stores it as an alias: dataStore.setAlias('$newBlock', generatedSid)
// The next operation can reference '$newBlock'
const result = await transaction(editor, [
insertParagraph({ after: 'p1' }),
// internally references '$newBlock' alias
]).commit();
Aliases are overlay-scoped — they exist only within the transaction and are cleared after commit or rollback.
Extension Hooks
Extensions can intercept transactions before they are committed:
class MyExtension implements Extension {
onBeforeTransaction(editor: Editor, tx: Transaction): Transaction | null {
// Return null to cancel
// Return modified transaction to change operations
// Return original to pass through
return tx;
}
onTransaction(editor: Editor, tx: Transaction): void {
// Called after commit (notification only)
}
}
See Before Hooks Use Cases for detailed patterns.
Next Steps
- Learn about History — Undo/redo powered by transactions
- Learn about Editor Core — Command system that creates transactions
- See Custom Operations — Defining your own operations
- See Architecture: DataStore — Overlay/COW implementation details