Time Travel
StateMesh can record all state changes and replay to any point in time. Useful for debugging, testing, and audit trails.
Enable Time Travel
const mesh = createMesh({
state: { /* ... */ },
timeTravel: { maxEntries: 1000 } // Ring buffer size (default: 1000)
});| Option | Default | Description |
|---|---|---|
maxEntries | 1000 | Ring buffer size. Oldest entries are evicted when exceeded. |
API
mesh.enableTimeTravel(); // Start recording
mesh.disableTimeTravel(); // Stop recording (preserves log)
mesh.isTimeTravelEnabled; // true when recording
mesh.getTimeTravelLog(); // Return a copy of the recorded log
mesh.replayTo(index); // Restore state at log entry index
mesh.replayToTimestamp(ts); // Restore state nearest to timestamp (O(log n))
mesh.clearTimeTravelLog(); // Clear the log and free memoryUsage
mesh.enableTimeTravel();
mesh.setPath("count", 1);
mesh.setPath("count", 2);
mesh.setPath("count", 3);
const log = mesh.getTimeTravelLog();
// [
// { index: 0, event: ..., stateBefore: {count:0}, stateAfter: {count:1}, timestamp: ... },
// { index: 1, event: ..., stateBefore: {count:1}, stateAfter: {count:2}, timestamp: ... },
// { index: 2, event: ..., stateBefore: {count:2}, stateAfter: {count:3}, timestamp: ... }
// ]
mesh.replayTo(0); // Restores state to { count: 1 }
mesh.replayToTimestamp(Date.now() - 5000); // Restores state from 5 seconds agoLog Entries
Each log entry contains:
| Field | Type | Description |
|---|---|---|
index | number | Sequential index in the log |
event | MeshEvent | The event that caused the state change |
stateBefore | TState | Deep clone of state before the change |
stateAfter | TState | Deep clone of state after the change |
timestamp | number | Date.now() when the change occurred |
Replay Safety
- Replay does not trigger undo bookkeeping
- Replay does not re-record the replay as a new time travel entry
- Replay emits a
state.changedevent withmetadata.phaseset to"replay"
Timestamp Lookup
replayToTimestamp uses binary search for O(log n) lookup:
// Restore state from 10 seconds ago
const tenSecondsAgo = Date.now() - 10000;
mesh.replayToTimestamp(tenSecondsAgo);
// Restore state from a specific moment
mesh.replayToTimestamp(specificTimestamp);Ring Buffer
The time travel log uses a ring buffer. When the log exceeds maxEntries, the oldest entry is evicted:
const mesh = createMesh({
state: { /* ... */ },
timeTravel: { maxEntries: 100 } // Keep only the last 100 changes
});
// After 101 changes, entry 0 is evicted
// mesh.replayTo(0) would fail — the entry no longer existsWARNING
Each log entry stores deep clones of state before and after the change. With large state trees and high maxEntries, this can use significant memory. Set maxEntries to a reasonable value for your use case.
Disable and Preserve
mesh.disableTimeTravel(); // Stop recording
// Existing log is preserved
const log = mesh.getTimeTravelLog(); // Still available
// Can still replay
mesh.replayTo(0);Important Notes
TIP
Time travel is opt-in. No memory or CPU cost unless you enable it.
TIP
Use clearTimeTravelLog() to free memory when the log is no longer needed.
WARNING
Time travel and undo/redo are independent features. Time travel records all changes; undo/redo tracks snapshots for reverting. You can use either or both.
Next Steps
- Undo / Redo — State history navigation
- Middleware Pipelines — Composable middleware
- DevTools — Visual time travel in the DevTools dock
