5.7 KiB
5.7 KiB
OpenSpec Change: enterprise-serialize
- Change ID: enterprise-serialize
- Package:
@suaveplan/serialize - Category: universal
- Target path:
packages/universal/serialize - Status: IN PROGRESS
- Date: 2026-04-18
- Reference spec:
/mnt/s/web/suaveplan/enterprise/serialize.md
Summary
Greenfield implementation of @suaveplan/serialize for the genesis branch. The existing main-branch serialize package provides the baseline public-API surface to preserve; this change delivers round-trip-preserving structured serialization — tagged JSON (preserving Date, Map, Set, BigInt, Uint8Array, typed arrays, URL, RegExp, user-defined tags) plus MessagePack binary profile and streaming variants. Eval-safe by default; opt-in legacy adapters isolated under @suaveplan/serialize/unsafe.
Current-state audit (genesis)
The package does not exist on genesis yet — this is a fresh build.
Available workspace deps on genesis
| Package | Available |
|---|---|
@suaveplan/error |
✅ |
@suaveplan/types |
✅ |
@suaveplan/streams |
❌ not yet built — implement chunked stream via Web Streams API internally |
@suaveplan/opentelemetry |
❌ not yet built — use @opentelemetry/api directly |
@suaveplan/feature-flags |
❌ not yet built — skip |
@suaveplan/schema-registry |
❌ not yet built — export Zod schemas directly |
External deps:
zod→catalog:forms@opentelemetry/api→catalog:observability@msgpack/msgpack→ add tocatalog:serialization(new catalog group) at^3.0.0tslib→catalog:build
Unsafe-subpath-only (isolated, not in main bundle):
serialize-javascript→ add tocatalog:serializationat^6.0.0— only imported bysrc/adapters/eval-safe/which lives under the/unsafesubpath export
Dev deps (via catalog):
vitest,@vitest/coverage-istanbul→catalog:testingfast-check→catalog:testingmitata→catalog:testing
Preserved public API surface (from main branch)
Main export (@suaveplan/serialize)
Codec
serialize(value, opts?)→string— tagged JSON serializationdeserialize(json, opts?)→unknown— tagged JSON deserializationencodeBinary(value, opts?)→Uint8Array— MessagePack encodingdecodeBinary(bytes, opts?)→unknown— MessagePack decodingencodeStream(value, opts?)→ReadableStream<Uint8Array>— streaming MessagePackdecodeStream(stream, opts?)→ReadableStream<unknown>— streaming decodedeepClone(value)→unknown— structured clone via serialize/deserialize round-tripSerializeOptions,DeserializeOptions,Codec(types)
Type registry
registerType(def)— register a custom type tagunregisterType(tag)— remove a custom type taglistTypes()→TypeDefinition[]— all registered typesgetType(tag)→TypeDefinition | undefinedgetTypeByCtor(ctor)→TypeDefinition | undefinedTypeDefinition—{ tag: string; ctor: Function; encode: (v) => unknown; decode: (v) => unknown }
Errors
SerializationErrorextendsSuaveplanErrorCircularReferenceErrorextendsSerializationErrorMaxDepthExceededErrorextendsSerializationErrorMaxSizeExceededErrorextendsSerializationErrorUnsupportedTypeErrorextendsSerializationErrorSerializationErrorCode,SerializationErrorContext,SerializationErrorOptions(types)
Error codes (re-exported)
registerSerializeErrorCodes(),SERIALIZE_ERROR_METADATA,SerializeErrorCode
Legacy
legacySerialize(value)→string— old format compat (no new tags; for migration only)legacyDeserialize(json)→unknown— old format compat
Unsafe subpath (@suaveplan/serialize/unsafe)
serializeEvalSafe(value, opts?)→string— usesserialize-javascriptunder the hood; emits a security warning in devdeserializeEvalSafe(json, opts?)→unknownEvalSafeOptions(type)
New additions for genesis
Catalog entry
-
Add
serializationcatalog group to rootpackage.json:"serialization": { "@msgpack/msgpack": "^3.0.0", "serialize-javascript": "^6.0.0" }
Structured errors
SerializationErrorextendsSuaveplanErrorwith codes underSERIALIZE.*namespace- Codes:
SERIALIZE.CODEC.CIRCULAR_REFERENCE,SERIALIZE.CODEC.MAX_DEPTH,SERIALIZE.CODEC.MAX_SIZE,SERIALIZE.CODEC.UNKNOWN_TAG,SERIALIZE.REGISTRY.DUPLICATE_TAG,SERIALIZE.LEGACY.REFUSED
Zod schemas
SerializeOptionsSchema,DeserializeOptionsSchema,TypeDefinitionSchema— all public config types validated at boundary
AbortSignal support
encodeStreamanddecodeStreamhonourAbortSignalpassed via options
OTel instrumentation
- Histogram:
serialize.payload_bytes(encode + decode, by format label json/msgpack) - Counter:
serialize.error.total(by error code label)
Property-based tests (fast-check)
- Round-trip for every built-in type:
deserialize(serialize(x)) deep-equals x - Binary round-trip:
decodeBinary(encodeBinary(x)) deep-equals x - JSON–binary structural equivalence: both formats produce semantically equal output
- Cycle detection: any circular graph always throws
CircularReferenceError - Custom tag round-trip: registered tag encodes and decodes without data loss
Benchmarks (mitata)
serialize/deserialize10 KB nested objectencodeBinary/decodeBinary10 KB nested objectdeepClone10 KB object vsstructuredClonebaseline
Impact
- Adds one new package:
packages/universal/serialize - Adds
serializationcatalog group to rootpackage.json - Adds one new spec capability:
serialization - No existing package consumers on genesis yet