# registry _(no submodules)_ ## Purpose Maintains a global singleton map of type definitions that the JSON and MessagePack codecs consult to encode and decode non-primitive JavaScript values in a round-trip-safe way. Each entry maps a string tag to an encoder/decoder pair and a constructor reference, enabling both forward lookup (value → tag) and reverse lookup (tag → value reconstruction). The registry ships with 17 built-in entries covering the types most commonly lost by `JSON.stringify`: `Date`, `Map`, `Set`, `BigInt`, the six TypedArray variants, `URL`, `RegExp`, and three special sentinel tags for `undefined`, `NaN`, and `±Infinity`. ## Features - `registerType(def)` — add a custom type; throws `SERIALIZE.REGISTRY.DUPLICATE_TAG` on tag collision with a different constructor; idempotent for identical re-registrations - `unregisterType(tag)` — remove by tag; silent if absent (useful for test cleanup) - `listTypes()` — enumerate all current entries as an array of `TypeDefinition` - `getType(tag)` — look up by tag string; returns `undefined` if absent - `getTypeByCtor(ctor)` — look up by constructor; returns `undefined` if absent - 14 built-in type definitions registered at module load time - Three special sentinel definitions for `undefined`, `NaN`, and `±Infinity` ## Usage: Basic Example ```typescript import { registerType, unregisterType } from "@suaveplan/serialize"; class Color { constructor(public hex: string) {} } registerType({ tag: "__Color", ctor: Color, encode: (v) => (v as Color).hex, decode: (enc) => new Color(enc as string), }); // After registration, serialize/deserialize will handle Color instances. // Clean up in tests: unregisterType("__Color"); ``` ## Usage: Advanced Example ```typescript import { getType, getTypeByCtor, listTypes, registerType, } from "@suaveplan/serialize"; class Vector2 { constructor(public x: number, public y: number) {} } registerType({ tag: "__Vector2", ctor: Vector2, encode: (v) => { const vec = v as Vector2; return [vec.x, vec.y]; }, decode: (enc) => { const [x, y] = enc as [number, number]; return new Vector2(x, y); }, }); // Look up by tag const def = getType("__Vector2"); console.log(def?.tag); // "__Vector2" // Look up by ctor const def2 = getTypeByCtor(Vector2); console.log(def2?.tag); // "__Vector2" // Enumerate all registered types const all = listTypes(); console.log(all.map((d) => d.tag)); ``` ## API Reference ### `registerType(def: TypeDefinition): void` Registers a custom type. Throws `SerializationError` with code `SERIALIZE.REGISTRY.DUPLICATE_TAG` if the same `tag` string is already registered for a _different_ constructor. Registering the same tag+ctor pair again is a silent no-op (idempotent). ### `unregisterType(tag: string): void` Removes the registration for `tag` and its associated constructor from both internal maps. Silent if the tag was never registered. ### `listTypes(): TypeDefinition[]` Returns a snapshot array of all registered `TypeDefinition` objects at the time of the call. ### `getType(tag: string): TypeDefinition | undefined` Returns the `TypeDefinition` for the given tag, or `undefined` if no entry exists. ### `getTypeByCtor(ctor: Function): TypeDefinition | undefined` Returns the `TypeDefinition` whose `ctor` reference matches, or `undefined` if none is registered for that constructor. ### `TypeDefinition` ```typescript interface TypeDefinition { tag: string; ctor: Function; encode: (value: unknown) => unknown; decode: (encoded: unknown) => unknown; } ``` ## Implementation Notes - Two parallel `Map` instances (`_byTag` and `_byCtor`) are kept in sync to give O(1) lookup in both directions without requiring full list iteration. - Built-in registrations use an internal `registerBuiltin` helper that force-sets both maps, making them immune to the duplicate-tag guard that external callers hit. This allows the module to be loaded multiple times in test environments without throwing. - The sentinel entries for `undefined`, `NaN`, and `±Infinity` use placeholder constructors (`Object`, `Number`, `Boolean`) that will never match a real value via `getTypeByCtor`; they are only reached via the `getType(tag)` path during deserialization. - TypedArray `Float32Array` and `Float64Array` encode to plain number arrays; precision loss for `Float32Array` is intentional and matches the original in-memory representation.