createPersistedEditor
Create a Nabu editor instance with automatic IndexedDB persistence.
Related guide: Persistence and sync →
Async counterpart of createEditor() — same { preset, extensions } signature,
extended with a required docId for storage.
On first call, the document is empty (only the default paragraph from ParagraphExtension). On subsequent calls, the document state is restored from the last saved snapshot.
The bytes of the files (images, videos…) are NOT saved in the snapshot (ADR 014): they go to an IndexedDBAssetStore
(base nabu, object store assets, shared by every document of the origin), unless files.store provides another one.
The document only keeps references and a small metadata index; the store also records which files each document references.
With a host (files.onBeforePersist) the uploads still to do are listed in an IndexedDBOutbox (object store outbox, unless
files.outbox provides another one): an upload interrupted by a closed tab RESUMES when the document is opened again.
History (ADR 014 §3.6): the default history: 'full' NEVER compacts or trims anything (the whole evolution of the document
is kept and saved, whatever the number of sessions). history: { mode: 'compact', retain, onBeforeCompact } opts in to a
compaction when the document is OPENED (after loading, migration and upload reconciliation; never mid-session): history older
than retain is dropped and the files only that history referenced are released. See CompactOptions and nabu.compact().
Timestamps of the changes are recorded (doc.setRecordTimestamp(true)) so a retention window can be honoured.
Robustness (ADR 010, theme 8): the autosave also happens at most every maxWait ms during a continuous burst, and immediately when
the page is hidden (visibilitychange → hidden, pagehide). Every save keeps the snapshot it replaces as a BACKUP. A stored
snapshot that cannot be read is moved aside (never deleted: the quarantine object store), the backup is tried, else the editor
starts empty — it always boots, and nabu.persistence.recovery / onRecover tell the host what happened. When IndexedDB is not
usable the editor boots IN MEMORY (nabu.persistence.available === false, status 'unavailable'): createPersistedEditor does not reject.
Several tabs (ADR 010, P-94): two tabs on the same docId never overwrite each other. EVERY save first merges what is stored (read and
written in one IndexedDB transaction: the changes another tab saved are imported into the live document, not as undo steps, the caret
stays on its character), and, with sync (default), the tabs also exchange their changes LIVE through a BroadcastChannel
(nabu:<docId>): an edit made in one tab appears in the others at once.
The instance gains extra members:
nabu.persistence— reactive state:status('saved' | 'pending' | 'saving' | 'error' | 'unavailable'),lastSavedAt,lastError,available,recovery,sync, anddispose()(stop autosave, remove the page listeners, close the sync channel and the connection)nabu.saveNow()— force an immediate save (returns a Promise; rejects if the save fails)nabu.compact({ retain, onBeforeCompact })— explicit compaction (returns aCompactResult, never throws for a refusal)nabu.clearPersistence()— delete the stored snapshot and release the document's files (returns a Promise)nabu.isNew—trueif no previous snapshot was found (fresh document, or nothing readable was left:persistence.recovery.kind === 'empty')
Signature
import { createPersistedEditor } from '@aionbuilders/nabu';function createPersistedEditor(options?: object): Promise<Nabu>Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
optionsoptional | object | ... | n/a |
options.debounceoptional | number | 2000 | Auto-save debounce delay in ms (default: 2000) |
options.docId | string | n/a | Unique identifier for this document in IndexedDB |
options.extensionsoptional | Extension[] | [] | Additional extensions to append |
options.filesoptional | FilesConfig | n/a | File handling; |
options.historyoptional | 'full' | { mode: 'full' } | { mode: 'compact'; onBeforeCompact?: (fullSnapshot: Uint8Array) => void | Promise<void>; retain: string | number } | n/a |
|
options.langoptional | string | n/a | Forwarded to Nabu (BCP 47 tag of the content) |
options.maxWaitoptional | number | n/a | Longest time (ms) a continuous burst of edits goes without a save (default: 3000, never shorter than
|
options.onRecoveroptional | (recovery: Recovery) => void | n/a | Called at boot when the stored document could not be read (see |
options.onSaveErroroptional | (error: unknown) => void | n/a | Called when an AUTOMATIC save fails (quota…); |
options.placeholdersoptional | PlaceholdersOption | n/a | Forwarded to Nabu
(any other Nabu init option, e.g. |
options.presetoptional | Extension[] | FullPreset | Extension preset (default: FullPreset) |
options.spellcheckoptional | boolean | n/a | Forwarded to Nabu (default |
options.syncoptional | boolean | true | Live sync between the tabs that edit the same |
Returns
Promise<Nabu>
Examples
// Fresh or restored editor, full preset
const engine = await createPersistedEditor({ docId: 'my-note' });// Document preset with custom extension
const engine = await createPersistedEditor({
docId: 'article-42',
preset: DocumentPreset,
extensions: [MyCustomExtension],
});Source: src/lib/persistence/persistence.js:105