Version v2.2.1 Size 3.8 KB gzip
Why Ledger?
Undo and redo require more than array manipulation when operations are asynchronous, cancellable, and visible in a UI. Ledger serializes only reversible commands, owns queue lifecycle, and publishes one atomic state snapshot.
ts
// Before
const undo = () => changes.pop()?.revert();
// After
import { createLedger } from '@vielzeug/ledger';
const ledger = createLedger();
await ledger.do({ apply: saveNext, revert: restorePrevious });
await ledger.undo();| Feature | Roll your own | Ledger |
|---|---|---|
| Bundle size | 0 B | 3.8 KB |
| Reversible history | Manual arrays | |
| Serialized async work | Manual queue | |
| Queue cancellation | Manual ownership | Abort-aware lifecycle |
| Reactive state | Manual events | Readable<LedgerState> |
| Composition | Custom transaction code | compose() |
Use Ledger when you own reversible asynchronous state transitions and need undo, redo, or history UI.
Consider direct application code when work is irreversible, fire-and-forget, or does not need history.
Installation
sh
pnpm add @vielzeug/ledgersh
npm install @vielzeug/ledgersh
yarn add @vielzeug/ledgerQuick Start
Submit a reversible command, read state, then dispose its owner.
ts
import { createLedger } from '@vielzeug/ledger';
let value = 'before';
const ledger = createLedger();
await ledger.do({
apply: () => { value = 'after'; },
label: 'Rename value',
revert: () => { value = 'before'; },
});
await ledger.undo();
console.log(ledger.state.value.undo.length); // 0
ledger.dispose();Features
createLedger()— Create serialized reversible command historystate— Read atomic queue, undo, redo, and acceptance statecompose()— Combine reversible commands into one reversible commandwhenIdle()— Await queued and active operation settlementLedgerCancelledError— Distinguish cancellation from execution failuremaxHistory— Keep a non-negative safe-integer undo depth[Symbol.dispose]()— Seal, abort, and clear a ledger owner