Editor state and persistence
The CDK editor runtime revolves around State (@modula/editor).
State model
A State instance stores an ordered collection of Node blocks and a transaction history.
Key runtime concepts:
State.blocks: current ordered block collection.State.transaction(...): atomic mutation unit.State.undo()/State.redo(): history traversal.State.apply(...): apply operation batches (useful for remote updates).State.toJSON(): serialize current state for persistence.
Transaction operations
Transactions emit change events and record operations of type:
addremovechangemetadatamove
Example:
editorState.transaction((tx) => {
tx.add(new Node(Node.uuid(), 'text', { text: { title: 'Intro', body: '<p>Hello</p>' } }));
}); Save format
Use editorState.toJSON() to persist editor-native state.
Serialized shape:
type NodeJSON = {
id?: string;
type: string;
data: Record<string, unknown>;
meta?: Record<string, unknown>;
}; Persisted payload example:
[
{
"id": "a8f7f2f0-7f70-4a61-9f58-7b35039f3f78",
"type": "text",
"data": {
"text": {
"title": "An Epic Journey",
"body": "<p>...</p>"
}
},
"meta": {
"showAbstract": true
}
}
] Restore flow
const state = State.fromJSON(savedNodes); fromJSON reconstructs runtime Node objects, preserving id, type, data, and meta.
Saving modules (versioned backend)
If you save to the Modula REST modules endpoint, send your module content to:
POST /api/modules/:id
with body:
{
"data": { "title": "...", "subtitle": "...", "abstract": "...", "blocks": [] },
"rev": "optional-previous-revision-id"
} A stale rev can return 409 Conflict (optimistic concurrency).
Persistence best practices
- Save only JSON snapshots, never runtime object references.
- Keep
idstable across saves to preserve move/change semantics. - Use optimistic concurrency (
rev) for multi-client editing. - For autosave, debounce writes and keep explicit Save for user confidence.