first commit
This commit is contained in:
92
docs/foundation/data/serialize/src/errors/codes.md
Normal file
92
docs/foundation/data/serialize/src/errors/codes.md
Normal file
@@ -0,0 +1,92 @@
|
||||
# codes
|
||||
|
||||
## Purpose
|
||||
|
||||
`codes` defines the canonical set of error codes for the `@suaveplan/serialize` package using `defineCodes` from `@suaveplan/error`. Each code encodes both the package origin (`SERIALIZE`) and the subsystem-plus-reason (`CODEC.CIRCULAR_REFERENCE`, `REGISTRY.DUPLICATE_TAG`, etc.), producing fully-qualified strings that are globally unique across the project's error registry.
|
||||
|
||||
By centralising all error codes in this single file, the entire `serialize` package — codecs, registry, legacy adapter, and streaming layer — can import a single frozen object rather than scattering magic strings across modules. The exported `SerializationErrorCode` union provides complete type coverage for `SuaveplanError.code` narrowing in catch blocks.
|
||||
|
||||
## Features
|
||||
|
||||
- Registers six error codes via `defineCodes("SERIALIZE", [...])`:
|
||||
- `CODEC.CIRCULAR_REFERENCE` — JSON/MessagePack encode detected a circular object reference
|
||||
- `CODEC.MAX_DEPTH` — object nesting exceeded the configured depth limit
|
||||
- `CODEC.MAX_SIZE` — encoded output exceeded the configured size limit
|
||||
- `CODEC.UNKNOWN_TAG` — decode encountered a tagged token whose type is not in the registry
|
||||
- `REGISTRY.DUPLICATE_TAG` — a type was registered under a tag already claimed by another handler
|
||||
- `LEGACY.REFUSED` — a legacy deserialisation was attempted on a value the adapter cannot handle
|
||||
- Exports `SERIALIZE_CODES` as a frozen record for use throughout the package
|
||||
- Exports `SerializationErrorCode` union type for typed error narrowing
|
||||
- Zero runtime side effects beyond the `defineCodes` call — safe to import anywhere
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```ts
|
||||
import { SERIALIZE_CODES } from "@suaveplan/serialize/errors/codes";
|
||||
import { SerializeError } from "@suaveplan/serialize/errors";
|
||||
|
||||
throw new SerializeError(
|
||||
SERIALIZE_CODES["CODEC.CIRCULAR_REFERENCE"],
|
||||
"Cannot serialise: circular reference detected in ctx.payload"
|
||||
);
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```ts
|
||||
import { SERIALIZE_CODES, type SerializationErrorCode } from "@suaveplan/serialize/errors/codes";
|
||||
import { SuaveplanError } from "@suaveplan/error";
|
||||
|
||||
const CODEC_CODES = new Set<string>([
|
||||
SERIALIZE_CODES["CODEC.CIRCULAR_REFERENCE"],
|
||||
SERIALIZE_CODES["CODEC.MAX_DEPTH"],
|
||||
SERIALIZE_CODES["CODEC.MAX_SIZE"],
|
||||
SERIALIZE_CODES["CODEC.UNKNOWN_TAG"],
|
||||
]);
|
||||
|
||||
function isCodecError(err: unknown): boolean {
|
||||
return err instanceof SuaveplanError && CODEC_CODES.has(err.code);
|
||||
}
|
||||
|
||||
// In a catch block:
|
||||
try {
|
||||
const encoded = jsonEncode(value);
|
||||
} catch (err) {
|
||||
if (isCodecError(err)) {
|
||||
logger.warn("Encoding failed — falling back to string coercion", { err });
|
||||
return String(value);
|
||||
}
|
||||
throw err;
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `SERIALIZE_CODES`
|
||||
|
||||
```ts
|
||||
const SERIALIZE_CODES: {
|
||||
"CODEC.CIRCULAR_REFERENCE": "SERIALIZE.CODEC.CIRCULAR_REFERENCE";
|
||||
"CODEC.MAX_DEPTH": "SERIALIZE.CODEC.MAX_DEPTH";
|
||||
"CODEC.MAX_SIZE": "SERIALIZE.CODEC.MAX_SIZE";
|
||||
"CODEC.UNKNOWN_TAG": "SERIALIZE.CODEC.UNKNOWN_TAG";
|
||||
"REGISTRY.DUPLICATE_TAG": "SERIALIZE.REGISTRY.DUPLICATE_TAG";
|
||||
"LEGACY.REFUSED": "SERIALIZE.LEGACY.REFUSED";
|
||||
}
|
||||
```
|
||||
|
||||
The object is frozen by `defineCodes` and safe to use as a constant map. Each key is the short, human-readable code; each value is the fully-qualified string registered in the global error table.
|
||||
|
||||
### `type SerializationErrorCode`
|
||||
|
||||
```ts
|
||||
type SerializationErrorCode = (typeof SERIALIZE_CODES)[keyof typeof SERIALIZE_CODES];
|
||||
```
|
||||
|
||||
Union of all six qualified code strings. Use with `SuaveplanError.code` to narrow errors originating from this package.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
`defineCodes` is idempotent for the same namespace+key pair — re-importing this module from multiple paths (or from both `error-codes.ts` and directly) does not register duplicates or throw. The frozen return value ensures the constants are not accidentally mutated at runtime.
|
||||
|
||||
The code taxonomy uses two-segment keys (`SUBSYSTEM.REASON`) rather than flat keys to make the error origin immediately scannable in logs and to allow future wildcard matching on the subsystem prefix without regex.
|
||||
151
docs/foundation/data/serialize/src/errors/serialize-error.md
Normal file
151
docs/foundation/data/serialize/src/errors/serialize-error.md
Normal file
@@ -0,0 +1,151 @@
|
||||
# serialize-error
|
||||
|
||||
## Purpose
|
||||
|
||||
Defines the error hierarchy for `@suaveplan/serialize`. All errors thrown by this package extend `SerializationError`, which in turn extends `SuaveplanError` from `@suaveplan/error`. This design ensures every thrown error carries a stable, machine-readable code string (`SERIALIZE.*`) so callers can programmatically distinguish between circular references, depth overflows, size overflows, and unknown type tags without parsing human-readable messages.
|
||||
|
||||
The contextual payload (`SerializationErrorContext`) transports structural metadata — tag names, recursion depths, byte sizes, and dot-path strings — that would otherwise require expensive re-traversal to reconstruct after the fact.
|
||||
|
||||
## Features
|
||||
|
||||
- `SerializationError` — base class; extends `SuaveplanError<SerializationErrorCode>` with an optional `SerializationErrorContext`
|
||||
- `CircularReferenceError` — emitted when a cycle is detected in the input object graph; includes the dot-path where the cycle was encountered
|
||||
- `MaxDepthExceededError` — emitted when recursion depth exceeds the caller-configured `maxDepth`; includes the depth value
|
||||
- `MaxSizeExceededError` — emitted when the serialized byte count exceeds `maxSize`; includes the approximate size in bytes
|
||||
- `UnsupportedTypeError` — emitted during deserialization when a `__t` tag has no registered decoder; includes the unknown tag string
|
||||
- Exported `SerializationErrorCode` union type for exhaustive `switch` narrowing
|
||||
- Re-exported `SERIALIZE_CODES` constant map for use in tests and error construction
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```typescript
|
||||
import {
|
||||
CircularReferenceError,
|
||||
MaxDepthExceededError,
|
||||
SerializationError,
|
||||
} from "@suaveplan/serialize";
|
||||
|
||||
try {
|
||||
// serialization call here
|
||||
} catch (err) {
|
||||
if (err instanceof CircularReferenceError) {
|
||||
console.error("Cycle at", err.context?.path);
|
||||
} else if (err instanceof SerializationError) {
|
||||
console.error("Serialization failed:", err.code);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```typescript
|
||||
import {
|
||||
SERIALIZE_CODES,
|
||||
SerializationError,
|
||||
UnsupportedTypeError,
|
||||
type SerializationErrorCode,
|
||||
} from "@suaveplan/serialize";
|
||||
|
||||
function handleSerializeError(err: unknown): never {
|
||||
if (!(err instanceof SerializationError)) throw err;
|
||||
|
||||
const code: SerializationErrorCode = err.code;
|
||||
switch (code) {
|
||||
case SERIALIZE_CODES["CODEC.CIRCULAR_REFERENCE"]:
|
||||
throw new Error(`Cycle at path: ${err.context?.path ?? "unknown"}`);
|
||||
case SERIALIZE_CODES["CODEC.MAX_DEPTH"]:
|
||||
throw new Error(`Too deep: ${err.context?.depth}`);
|
||||
case SERIALIZE_CODES["CODEC.MAX_SIZE"]:
|
||||
throw new Error(`Too large: ${err.context?.size} bytes`);
|
||||
case SERIALIZE_CODES["CODEC.UNKNOWN_TAG"]:
|
||||
throw new Error(`No decoder for tag: ${err.context?.tag}`);
|
||||
default:
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
// UnsupportedTypeError carries the unknown tag
|
||||
const e = new UnsupportedTypeError("__MyCustomClass");
|
||||
console.log(e.context?.tag); // "__MyCustomClass"
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `SerializationError`
|
||||
|
||||
```typescript
|
||||
class SerializationError extends SuaveplanError<SerializationErrorCode> {
|
||||
constructor(
|
||||
code: SerializationErrorCode,
|
||||
messageKey: string,
|
||||
options?: SerializationErrorOptions,
|
||||
): void
|
||||
}
|
||||
```
|
||||
|
||||
- `code` — one of the `SERIALIZE.*` codes
|
||||
- `messageKey` — i18n key resolved via `@suaveplan/error` resolver
|
||||
- `options.cause` — underlying error, chained via `Error.cause`
|
||||
- `options.context` — `SerializationErrorContext` metadata
|
||||
|
||||
### `CircularReferenceError`
|
||||
|
||||
```typescript
|
||||
new CircularReferenceError(path?: string, cause?: unknown)
|
||||
```
|
||||
|
||||
Code: `SERIALIZE.CODEC.CIRCULAR_REFERENCE`. `context.path` is the dot-path string where the cycle was detected.
|
||||
|
||||
### `MaxDepthExceededError`
|
||||
|
||||
```typescript
|
||||
new MaxDepthExceededError(depth: number, cause?: unknown)
|
||||
```
|
||||
|
||||
Code: `SERIALIZE.CODEC.MAX_DEPTH`. `context.depth` is the depth value at which encoding was aborted.
|
||||
|
||||
### `MaxSizeExceededError`
|
||||
|
||||
```typescript
|
||||
new MaxSizeExceededError(size: number, cause?: unknown)
|
||||
```
|
||||
|
||||
Code: `SERIALIZE.CODEC.MAX_SIZE`. `context.size` is the approximate byte count.
|
||||
|
||||
### `UnsupportedTypeError`
|
||||
|
||||
```typescript
|
||||
new UnsupportedTypeError(tag: string, cause?: unknown)
|
||||
```
|
||||
|
||||
Code: `SERIALIZE.CODEC.UNKNOWN_TAG`. `context.tag` is the unrecognised tag string.
|
||||
|
||||
### `SerializationErrorContext`
|
||||
|
||||
```typescript
|
||||
interface SerializationErrorContext {
|
||||
tag?: string;
|
||||
depth?: number;
|
||||
size?: number;
|
||||
path?: string;
|
||||
}
|
||||
```
|
||||
|
||||
### `SerializationErrorOptions`
|
||||
|
||||
```typescript
|
||||
interface SerializationErrorOptions {
|
||||
cause?: unknown;
|
||||
context?: SerializationErrorContext;
|
||||
}
|
||||
```
|
||||
|
||||
### `SerializationErrorCode`
|
||||
|
||||
Union type of all `SERIALIZE.*` code strings; use with exhaustive `switch` statements.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
- `SerializationError` maps `SerializationErrorOptions.context` to the generic `Record<string, unknown>` shape expected by `SuaveplanErrorOptions` without widening the exported interface — callers always get the narrow `SerializationErrorContext` type.
|
||||
- Each subclass uses a specific constructor signature (only the meaningful fields, not the full code/key/options triple) to make throw sites readable and to prevent code/message mismatches.
|
||||
- The `SERIALIZE_CODES` constant is defined in a separate `codes.ts` module and re-exported from here so that the error file is self-contained for consumers who only import errors.
|
||||
Reference in New Issue
Block a user