# 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 `UndoErrorContext` carrying optional `commandId`, `branchId`, and `conflictIds` - `instanceof UndoError` discrimination across module boundaries - `error.code` discrimination without `instanceof` (safe for cross-realm use) - Extends `SuaveplanError` for full cause-chain traversal and OTel span-event adapter support ## Usage: Basic Example ```ts 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 ```ts 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` ```ts 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` ```ts 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` to satisfy the generic base signature without a breaking cast.