Transactions and events
Transactions and events
Section intitulée « Transactions and events »A primary event is one that carries an origin: { origin: "ui" } for a user
action (later, "macro"). The bus opens a transaction on a primary event and
keeps it open for every event that cascades from it, including async handlers.
While a transaction is open:
- store writes are buffered — handlers see them via
slice.get(), but no component re-renders yet; - on success everything is published in one refresh;
- on failure everything is dropped, so the UI never shows a partial state.
Always pass { origin: "ui" } when you emit from your own UI:
onClick: () => ctx.emit("notes/document.select", { docId }, { origin: "ui" }),Commands, menus, toolbars and shell buttons already emit with a "ui" origin.
Subscribe to the boundaries to snapshot and restore your own model:
ctx.onTransaction({ onBegin: (info) => { /* snapshot your model */ }, onPrepareCommit: async (info) => { /* phase 1: persist, throw to veto */ }, onCommit: (info) => { /* writes are published: record undo, etc. */ }, onRollback: (info, error) => { /* writes were dropped: restore */ },});Lifecycle and two-phase commit
Section intitulée « Lifecycle and two-phase commit »primary event -> onBegin may be async: the event is dispatched once all resolve -> handlers cascades and async handlers are awaited -> onPrepareCommit may be async (database, file…); throw/reject to veto -> onCommit | onRollback synchronous; store published or droppedonPrepareCommit is phase 1: flush your I/O there. If any participant
rejects, the whole transaction rolls back and every participant gets
onRollback, including those that already prepared — undo your I/O there.
Store writes are dropped by the kernel, but a model kept outside the store
(Notes keeps text and tasks in memory) must be restored by the plugin.
Transactions never nest. A primary event emitted while a transaction is open
(a user click during an async save) is queued and runs in its own, fresh
transaction once the current one has settled. Emitting { origin: "ui" } from
a handler is therefore discouraged: it will not join the current work.
kernel.transactions.waitSettled() waits for the queue too.
Notes demo: Notes → Demo: make the next change fail to save, then edit a note — the fake database refuses the change after 400 ms and the note reverts.
A handler can also fail the current transaction explicitly with
tx.fail(error) from its context, or simply throw.
The kernel emits transaction/started, transaction/committed and
transaction/failed; the Events panel shows the transaction id per event.
Undo / redo belongs to the plugin
Section intitulée « Undo / redo belongs to the plugin »The kernel has no undo stack. A transaction is the natural atom for one, so a
plugin records history in onCommit. The Notes plugin keeps one stack pair per
document (title, text, tasks and precedences as they were before each committed
transaction, see packages/notes-core/history.ts) and contributes its own
notes-tsx.undo / notes-tsx.redo commands with mod+z / mod+shift+z, placed
in its menu and in the editor toolbar. Undo and redo run in a transaction too, so
onBegin ignores its own notes-tsx/document.undo and …redo events instead of
recording a new restore point.
Documentation sous licence CC BY 4.0 · © les contributeurs de SCFlow ·Retour à supplychainflow.org