first commit
This commit is contained in:
116
docs/foundation/data/diff/src/errors/diff-error.md
Normal file
116
docs/foundation/data/diff/src/errors/diff-error.md
Normal file
@@ -0,0 +1,116 @@
|
||||
# diff-error
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
## Purpose
|
||||
|
||||
`diff-error.ts` defines the error substrate for the `@suaveplan/diff` package. Every public API surface in the package throws a `DiffError` rather than a plain `Error`, giving callers a stable, machine-readable `code` property drawn from the `DIFF_CODES` registry. This makes error handling robust: you can match on `err.code` without parsing human-readable message text, and you can always narrow to a `DiffError` with `instanceof`.
|
||||
|
||||
The file intentionally avoids inheriting from `@suaveplan/error` so that `@suaveplan/diff` remains a true leaf package with no workspace runtime dependencies.
|
||||
|
||||
## Features
|
||||
|
||||
- `DIFF_CODES` constant object — five stable string codes covering all failure modes
|
||||
- `DiffCode` union type — narrows `string` to only valid diff error codes
|
||||
- `DiffErrorContext` interface — optional `path`, `operation`, and `reason` fields
|
||||
- `DiffError` class — extends `Error`, sets `name = "DiffError"`, attaches context
|
||||
- Fully typed: `readonly` fields prevent accidental mutation after construction
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```ts
|
||||
import { DiffError, DIFF_CODES } from "@suaveplan/diff/errors";
|
||||
|
||||
try {
|
||||
throw new DiffError("path not found", DIFF_CODES.PATH_INVALID, {
|
||||
path: ["a", "b"],
|
||||
operation: "remove",
|
||||
});
|
||||
} catch (err) {
|
||||
if (err instanceof DiffError) {
|
||||
console.error(err.code); // "DIFF.PATH_INVALID"
|
||||
console.error(err.path); // ["a", "b"]
|
||||
console.error(err.operation); // "remove"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```ts
|
||||
import { DiffError, DIFF_CODES, type DiffCode } from "@suaveplan/diff/errors";
|
||||
|
||||
function assertNoDiffError(err: unknown): asserts err is never {
|
||||
if (err instanceof DiffError) {
|
||||
const ctx = [err.operation, err.path?.join("/"), err.reason]
|
||||
.filter(Boolean)
|
||||
.join(" | ");
|
||||
throw new Error(`DiffError [${err.code}]: ${err.message} — ${ctx}`);
|
||||
}
|
||||
}
|
||||
|
||||
// Wrapping lower-level errors
|
||||
function wrapApplyError(inner: unknown, path: string[]): DiffError {
|
||||
const reason = inner instanceof Error ? inner.message : String(inner);
|
||||
return new DiffError(`apply failed at /${path.join("/")}`, DIFF_CODES.APPLY_FAILED, {
|
||||
path,
|
||||
operation: "apply",
|
||||
reason,
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `DIFF_CODES`
|
||||
|
||||
```ts
|
||||
const DIFF_CODES: {
|
||||
readonly INVALID_PATCH: "DIFF.INVALID_PATCH";
|
||||
readonly TEST_FAILED: "DIFF.TEST_FAILED";
|
||||
readonly PATH_INVALID: "DIFF.PATH_INVALID";
|
||||
readonly APPLY_FAILED: "DIFF.APPLY_FAILED";
|
||||
readonly INVERT_FAILED: "DIFF.INVERT_FAILED";
|
||||
};
|
||||
```
|
||||
|
||||
Stable string code registry. Always import codes from this object rather than inlining string literals.
|
||||
|
||||
### `DiffCode`
|
||||
|
||||
```ts
|
||||
type DiffCode = (typeof DIFF_CODES)[keyof typeof DIFF_CODES];
|
||||
```
|
||||
|
||||
Union of all valid diff error code strings.
|
||||
|
||||
### `DiffErrorContext`
|
||||
|
||||
```ts
|
||||
interface DiffErrorContext {
|
||||
path?: Path; // segment array of the JSON Pointer that failed
|
||||
operation?: string; // RFC 6902 op name ("add", "remove", "replace", etc.)
|
||||
reason?: string; // free-text explanation
|
||||
}
|
||||
```
|
||||
|
||||
### `DiffError`
|
||||
|
||||
```ts
|
||||
class DiffError extends Error {
|
||||
readonly code: DiffCode;
|
||||
readonly path?: Path;
|
||||
readonly operation?: string;
|
||||
readonly reason?: string;
|
||||
constructor(message: string, code: DiffCode, context?: DiffErrorContext): DiffError;
|
||||
}
|
||||
```
|
||||
|
||||
All context fields are `readonly` after construction. `name` is set to `"DiffError"` for reliable stack-trace display.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
- Inherits directly from `Error` (not `@suaveplan/error`) to keep this package dependency-free at runtime.
|
||||
- Context fields are assigned only when defined to avoid sparse objects with explicit `undefined` values; this keeps `JSON.stringify` output clean.
|
||||
- The `name = "DiffError"` assignment is in the constructor body (not a class field) to ensure the prototype chain is established first, which matters for `instanceof` checks across realm boundaries in bundled output.
|
||||
Reference in New Issue
Block a user