first commit
This commit is contained in:
111
docs/foundation/flow/undo/src/errors/undo-error.md
Normal file
111
docs/foundation/flow/undo/src/errors/undo-error.md
Normal file
@@ -0,0 +1,111 @@
|
||||
# undo-error
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
## 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<string, unknown>` to satisfy the generic base signature without a breaking cast.
|
||||
Reference in New Issue
Block a user