first commit

This commit is contained in:
2026-05-29 08:20:56 +01:00
commit 949299f58d
6368 changed files with 527176 additions and 0 deletions

View 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.

View 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.