Migration
Ledger 2 Changes
Ledger 2 accepts only reversible commands, publishes one state snapshot, and separates queued cancellation from active cooperative cancellation.
Removed APIs:
CommandCommandMetacanUndo,canRedo,historySize,historySnapshot,isProcessing,pendingCount
Rename Commands
Replace optional execute and rollback with required apply and revert.
ts
// Ledger 1
await ledger.do({
execute: () => saveNext(),
rollback: () => restorePrevious(),
});ts
// Ledger 2
await ledger.do({
apply: () => saveNext(),
revert: () => restorePrevious(),
});Move irreversible work outside Ledger commands.
Replace Individual Readables
Read history and queue state from ledger.state.
ts
// Ledger 1
if (ledger.canUndo.value) void ledger.undo();
console.log(ledger.historySnapshot.value);ts
// Ledger 2
if (ledger.state.value.undo.length > 0) void ledger.undo();
console.log(ledger.state.value.undo);state updates atomically for every Ledger transition.
Handle Cancellation
Queued cancelled work rejects LedgerCancelledError before user code starts. Active work receives context.signal and must stop cooperatively.
ts
const controller = new AbortController();
const operation = ledger.do(command, { signal: controller.signal });
controller.abort();
await operation.catch((error) => {
if (!(error instanceof LedgerCancelledError)) throw error;
});Use whenIdle() when an owner needs active work to settle after disposal.
Compose Only Reversible Steps
compose() now accepts readonly ReversibleCommand[].
ts
// Ledger 1
compose([
{ execute: updateState, rollback: restoreState },
{ execute: () => bus.emit('saved') },
]);ts
// Ledger 2
await ledger.do({ apply: updateState, revert: restoreState });
bus.emit('saved');If application and compensation both fail, inspect LedgerExecutionError.cause as AggregateError.
Upgrade Checklist
- Replace
CommandwithReversibleCommand. - Rename
execute/rollbacktoapply/revert. - Move effects and notifications outside
compose(). - Replace individual history readables with
statereads. - Catch
LedgerCancelledErrorseparately from execution failures. - Await
whenIdle()at lifecycle drain boundaries. - Update
@vielzeug/ledgerto version 3.