4.1 KiB
undo-error
(no submodules)
Purpose
Provides the structured error class UndoError and its stable code registry UNDO_CODES for @suaveplan/undo. All errors thrown by this package are instances of UndoError, enabling callers to discriminate failures precisely via error.code without relying on string-matching error messages.
The codes are registered via defineCodes from @suaveplan/error, which guarantees namespace-scoped uniqueness, prevents accidental collisions with other packages, and produces stable string identifiers (e.g., "UNDO.EMPTY_STACK") that survive minification and serialization.
Features
- Six error codes covering every failure scenario in the package
- Typed
UndoErrorContextcarrying optionalcommandId,branchId, andconflictIds instanceof UndoErrordiscrimination across module boundarieserror.codediscrimination withoutinstanceof(safe for cross-realm use)- Extends
SuaveplanErrorfor full cause-chain traversal and OTel span-event adapter support
Usage: Basic Example
import { UndoError, UNDO_CODES } from "@suaveplan/undo/errors/undo-error";
import { UndoStack } from "@suaveplan/undo/stack/stack";
const stack = new UndoStack(0);
try {
stack.undo(); // nothing to undo
} catch (err) {
if (err instanceof UndoError && err.code === UNDO_CODES.EMPTY_STACK) {
console.warn("Nothing to undo");
}
}
Usage: Advanced Example
import { UndoError, UNDO_CODES } from "@suaveplan/undo/errors/undo-error";
import { SelectiveUndoStack } from "@suaveplan/undo/selective/selective-undo";
const stack = new SelectiveUndoStack({ x: 0 }, { throwOnConflict: true });
// Simulate two dependent commands
const cmd1 = { id: "c1", label: "Set x", /* ... */ };
const cmd2 = { id: "c2", label: "Double x", metadata: { dependsOn: "c1" } /* ... */ };
try {
stack.undoById("c1");
} catch (err) {
if (err instanceof UndoError && err.code === UNDO_CODES.DEPENDENCY_CONFLICT) {
const ctx = err.context as { conflictIds?: string[] };
console.error("Conflict with:", ctx.conflictIds?.join(", "));
}
}
// Transaction guard
try {
stack.beginTransaction();
stack.beginTransaction(); // second call throws
} catch (err) {
if (err instanceof UndoError && err.code === UNDO_CODES.TRANSACTION_ACTIVE) {
console.error("Transaction already in progress");
}
}
API Reference
UNDO_CODES
| Code | Value | Thrown By |
|---|---|---|
UNDO_CODES.EMPTY_STACK |
"UNDO.EMPTY_STACK" |
undo() / redo() when nothing available |
UNDO_CODES.AT_BRANCH_ROOT |
"UNDO.AT_BRANCH_ROOT" |
BranchingHistory.switchBranch for unknown branch |
UNDO_CODES.DEPENDENCY_CONFLICT |
"UNDO.DEPENDENCY_CONFLICT" |
SelectiveUndoStack.undoById when throwOnConflict is set |
UNDO_CODES.COMMAND_FAILED |
"UNDO.COMMAND_FAILED" |
AsyncUndoStack when async command rejects |
UNDO_CODES.TRANSACTION_ACTIVE |
"UNDO.TRANSACTION_ACTIVE" |
beginTransaction() when one is already open |
UNDO_CODES.TRANSACTION_INACTIVE |
"UNDO.TRANSACTION_INACTIVE" |
handle.add/commit/rollback after close |
UndoError
new UndoError(code: UndoCode, message: string, options?: {
cause?: Error;
context?: UndoErrorContext;
})
| Property | Type | Description |
|---|---|---|
code |
UndoCode |
Stable string code |
message |
string |
Human-readable description |
context |
UndoErrorContext | undefined |
Structured context data |
cause |
Error | undefined |
Original error (for wrapping) |
name |
"UndoError" |
Always set |
UndoErrorContext
interface UndoErrorContext {
commandId?: string;
branchId?: string;
conflictIds?: string[];
}
Implementation Notes
defineCodes ensures each code string is globally unique per prefix within the process lifetime. The UNDO_CODES object is frozen and the union type UndoCode is derived via typeof UNDO_CODES[keyof typeof UNDO_CODES], making exhaustive switch-case type-safe. Context fields are typed as UndoErrorContext but passed to the base class as Record<string, unknown> to satisfy the generic base signature without a breaking cast.