56 lines
3.6 KiB
Markdown
56 lines
3.6 KiB
Markdown
# serialization
|
|
|
|
## Purpose
|
|
|
|
Round-trip-preserving structured serialization for the Suaveplan monorepo — tagged JSON (Date, Map, Set, BigInt, Uint8Array, URL, RegExp, custom tags), MessagePack binary profile, streaming encode/decode via Web Streams, and a pluggable type registry. Eval-safe by default with the `serialize-javascript` adapter isolated to the opt-in `/unsafe` subpath; all errors flow through `SerializationError` codes registered under the `SERIALIZE.*` namespace.
|
|
|
|
## Requirements
|
|
|
|
### SZ-01 — Eval-safe by default
|
|
|
|
The main export (`@suaveplan/serialize`) MUST NEVER execute arbitrary code during parse. All reconstruction happens via the type registry's `decode` functions. The `serialize-javascript` adapter is isolated to the `@suaveplan/serialize/unsafe` subpath and MUST NOT appear in the main bundle (enforced by tree-shake test).
|
|
|
|
### SZ-02 — Round-trip fidelity for built-in types
|
|
|
|
`serialize → deserialize` MUST produce a deep-equal value for all built-in types: `Date`, `Map`, `Set`, `BigInt`, `Uint8Array`, `Int8Array`, `Int16Array`, `Int32Array`, `Uint16Array`, `Uint32Array`, `Float32Array`, `Float64Array`, `URL`, `RegExp`. Plain objects, arrays, `null`, `undefined`, `string`, `number`, `boolean` MUST also round-trip. `NaN` and `Infinity` MUST be tagged (JSON `null` fallback is not acceptable).
|
|
|
|
### SZ-03 — Tagged JSON format
|
|
|
|
The serialized JSON format MUST use a `{ "__t": "<tag>", "v": <encoded> }` envelope for non-primitive types. Tags are short stable strings registered in the type registry. The format MUST be human-readable and diff-friendly.
|
|
|
|
### SZ-04 — Cycle detection
|
|
|
|
Any value containing a circular reference MUST throw `CircularReferenceError` (code `SERIALIZE.CODEC.CIRCULAR_REFERENCE`) before any output is produced. Detection uses a `WeakSet` and is depth-bounded to `maxDepth` (default: 100).
|
|
|
|
### SZ-05 — Type registry
|
|
|
|
`registerType(def)` MUST be idempotent for the same tag+ctor pair and MUST throw `SerializationError` (code `SERIALIZE.REGISTRY.DUPLICATE_TAG`) if the same tag is registered for a different constructor.
|
|
|
|
### SZ-06 — MessagePack profile
|
|
|
|
`encodeBinary → decodeBinary` MUST produce bit-identical output for identical inputs (field ordering canonicalised via sorted keys). Custom tags MUST be encoded as MessagePack extension types.
|
|
|
|
### SZ-07 — Streaming
|
|
|
|
`encodeStream` MUST chunk output into frames compatible with `decodeStream`. The Web Streams `ReadableStream` API is used (no Node.js stream dependency). Both MUST honour `AbortSignal`.
|
|
|
|
### SZ-08 — Unsafe subpath isolation
|
|
|
|
The `@suaveplan/serialize/unsafe` subpath MUST be a separate entry point. Any code importing only `@suaveplan/serialize` (main) MUST NOT have `serialize-javascript` in its bundle. This is verified by an automated tree-shake test.
|
|
|
|
### SZ-09 — Structured errors
|
|
|
|
All thrown errors MUST extend `SerializationError` which extends `SuaveplanError`. Subclasses: `CircularReferenceError`, `MaxDepthExceededError`, `MaxSizeExceededError`, `UnsupportedTypeError`. Codes under `SERIALIZE.*` namespace.
|
|
|
|
### SZ-10 — Zod schemas for options
|
|
|
|
`SerializeOptions` and `DeserializeOptions` MUST have corresponding Zod schemas. Inputs are validated at the public boundary (invalid options throw `SerializationError` with context).
|
|
|
|
### SZ-11 — OTel instrumentation
|
|
|
|
`serialize.payload_bytes` histogram (by format label) and `serialize.error.total` counter (by code label) MUST be emitted via `@opentelemetry/api`. No-ops without exporter.
|
|
|
|
### SZ-12 — sideEffects: false + coverage 100%
|
|
|
|
`"sideEffects": false`. Tree-shake test. Vitest thresholds at 100/100/100/100. No `.skip` / `.only` in `src/`.
|