@zudojs/serialization 1.1.1 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -70,13 +70,37 @@ const registry = new TransformerRegistry();
70
70
  registry.register({
71
71
  type: "Money",
72
72
  canSerialize: (v): v is Money => v instanceof Money,
73
- serialize: (v) => ({ $type: "Money", $value: v.toString() }),
73
+ serialize: (v) => (v as Money).toString(), // wrapped as { $type: "Money", $value: "..." }
74
74
  deserialize: (v) => Money.parse((v as { $value: string }).$value),
75
75
  });
76
76
 
77
77
  const serializer = new JSONSerializer({ transformers: registry });
78
+ // Money uses your transformer; Date, BigInt, Map, Set, Buffer and Error still
79
+ // use the built-ins.
78
80
  ```
79
81
 
82
+ Your registry is consulted first, so registering a tag the built-ins use (say
83
+ `"Date"`) replaces that built-in. Transformers you add to the registry later are
84
+ picked up.
85
+
86
+ **The transformer contract.** On the wire a transformed value is always
87
+ `{ "$type": <type>, "$value": ... }`.
88
+
89
+ - `serialize` returns either just the value to store, which the serializer
90
+ wraps as `{ $type: type, $value: <returned> }`, or that full tagged object
91
+ itself (as the built-ins do). A plain object with its own string `$type` is
92
+ taken as the full tagged object; anything else is wrapped. Special values
93
+ nested inside what you return (a `Date`, a `BigInt`) are transformed too.
94
+ - `deserialize` always receives the full tagged object, with nested values
95
+ already restored, whichever form `serialize` returned. Read `$value` from it.
96
+
97
+ **Opting out of the built-ins.** Pass `builtins: false` (to `JSONSerializer`
98
+ or `createSerializer`) to use only your registry. A value that no transformer
99
+ handles and that JSON would write as `{}` (a `Map`, `Set`, `WeakMap`, `Error`,
100
+ `RegExp`, `ArrayBuffer`, ...) then throws `SerializeError` under
101
+ `preserveTypes` instead of being silently emptied. To compose a registry by
102
+ hand, start from `createBuiltinTransformers()`.
103
+
80
104
  ## Untrusted input
81
105
 
82
106
  `deserialize` is the boundary that matters — the string arrives from a queue, an
package/dist/index.d.ts CHANGED
@@ -26,11 +26,13 @@
26
26
  */
27
27
  export type { SerializationFormat, SerializedValue, SerializeOptions, DeserializeOptions, SerializationMetadata, SerializedEnvelope, Serializer, TypeTransformer, } from "./serializerTypes/index.js";
28
28
  export { JSONSerializer, ESCAPED_OBJECT_TAG } from "./serializerJson/index.js";
29
+ export type { JSONSerializerOptions } from "./serializerJson/index.js";
29
30
  export { TransformerRegistry } from "./serializerTransforms/index.js";
30
31
  export { DateTransformer } from "./serializerTransforms/index.js";
31
32
  export { BigIntTransformer } from "./serializerTransforms/index.js";
32
33
  export { MapTransformer } from "./serializerTransforms/index.js";
33
34
  export { SetTransformer } from "./serializerTransforms/index.js";
35
+ export { createBuiltinTransformers } from "./serializerTransformPolicy/index.js";
34
36
  export { BufferTransformer } from "./serializerTransformsExt/index.js";
35
37
  export { ErrorTransformer } from "./serializerTransformsExt/index.js";
36
38
  export { toBase64, fromBase64, encodeUtf8, decodeUtf8, } from "./serializerTransformsExt/index.js";
package/dist/index.js CHANGED
@@ -32,6 +32,7 @@ export { DateTransformer } from "./serializerTransforms/index.js";
32
32
  export { BigIntTransformer } from "./serializerTransforms/index.js";
33
33
  export { MapTransformer } from "./serializerTransforms/index.js";
34
34
  export { SetTransformer } from "./serializerTransforms/index.js";
35
+ export { createBuiltinTransformers } from "./serializerTransformPolicy/index.js";
35
36
  // ─── Extended Transformers ────────────────────────────────────
36
37
  export { BufferTransformer } from "./serializerTransformsExt/index.js";
37
38
  export { ErrorTransformer } from "./serializerTransformsExt/index.js";
@@ -4,6 +4,6 @@
4
4
  * JSON serializer with fast path (native JSON) and advanced path
5
5
  * with type preservation via transformer registry.
6
6
  */
7
- export { JSONSerializer } from "./jsonSerializer.core.js";
7
+ export { JSONSerializer, type JSONSerializerOptions, } from "./jsonSerializer.core.js";
8
8
  export { ESCAPED_OBJECT_TAG } from "./jsonSerializer.escape.js";
9
9
  //# sourceMappingURL=index.d.ts.map
@@ -4,6 +4,6 @@
4
4
  * JSON serializer with fast path (native JSON) and advanced path
5
5
  * with type preservation via transformer registry.
6
6
  */
7
- export { JSONSerializer } from "./jsonSerializer.core.js";
7
+ export { JSONSerializer, } from "./jsonSerializer.core.js";
8
8
  export { ESCAPED_OBJECT_TAG } from "./jsonSerializer.escape.js";
9
9
  //# sourceMappingURL=index.js.map
@@ -7,6 +7,25 @@
7
7
  */
8
8
  import type { Serializer, SerializeOptions, DeserializeOptions } from "../serializerTypes/index.js";
9
9
  import { TransformerRegistry } from "../serializerTransforms/index.js";
10
+ /** Options accepted by the {@link JSONSerializer} constructor. */
11
+ export interface JSONSerializerOptions {
12
+ /**
13
+ * Your transformers. They are consulted first; the built-in transformers
14
+ * (`Date`, `BigInt`, `Map`, `Set`, `Uint8Array`/`Buffer`, `Error`) still
15
+ * handle every type your registry does not, unless `builtins` is `false`.
16
+ * Transformers registered on it later are picked up.
17
+ */
18
+ readonly transformers?: TransformerRegistry;
19
+ /**
20
+ * Include the built-in transformers behind `transformers` (default
21
+ * `true`). With `false` only your registry is used, and a value no
22
+ * transformer handles that JSON would write as `{}` (a `Map`, `Set`,
23
+ * `Error`, ...) throws `SerializeError` under `preserveTypes`.
24
+ */
25
+ readonly builtins?: boolean;
26
+ /** Default options merged into every call. */
27
+ readonly defaults?: SerializeOptions & DeserializeOptions;
28
+ }
10
29
  /**
11
30
  * JSON serializer. With `preserveTypes`, plain objects that carry their own
12
31
  * string `$type` key are escaped as `{"$type":"Object","$value":{...}}` on
@@ -16,13 +35,10 @@ import { TransformerRegistry } from "../serializerTransforms/index.js";
16
35
  export declare class JSONSerializer implements Serializer<unknown, string> {
17
36
  readonly name = "json";
18
37
  readonly contentType = "application/json";
38
+ private readonly registry;
19
39
  private readonly transformers;
20
40
  private readonly defaults;
21
- constructor(options?: {
22
- readonly transformers?: TransformerRegistry;
23
- /** Default options merged into every call. */
24
- readonly defaults?: SerializeOptions & DeserializeOptions;
25
- });
41
+ constructor(options?: JSONSerializerOptions);
26
42
  /**
27
43
  * Register a custom type transformer.
28
44
  *
@@ -5,8 +5,8 @@
5
5
  * Advanced path (preserveTypes: true): recursive traversal with
6
6
  * transformer-based type preservation.
7
7
  */
8
- import { TransformerRegistry, DateTransformer, BigIntTransformer, MapTransformer, SetTransformer, } from "../serializerTransforms/index.js";
9
- import { BufferTransformer, ErrorTransformer, } from "../serializerTransformsExt/index.js";
8
+ import { TransformerRegistry } from "../serializerTransforms/index.js";
9
+ import { createBuiltinTransformers, layerTransformers, } from "../serializerTransformPolicy/index.js";
10
10
  import { SerializationLimits } from "@zudojs/constants";
11
11
  import { InvalidSerializedDataError, TransformerError } from "@zudojs/errors";
12
12
  import { assertNoCircularReference, assertDepthWithinLimit, } from "@zudojs/validation";
@@ -14,17 +14,6 @@ import { ESCAPED_OBJECT_TAG } from "./jsonSerializer.escape.js";
14
14
  import { assertByteSize } from "./jsonSerializer.keys.js";
15
15
  import { transformValue } from "./jsonSerializer.transform.js";
16
16
  import { restoreValue } from "./jsonSerializer.restore.js";
17
- /** Default transformer registry with all built-in transformers. */
18
- function createDefaultTransformers() {
19
- const registry = new TransformerRegistry();
20
- registry.register(DateTransformer);
21
- registry.register(BigIntTransformer);
22
- registry.register(MapTransformer);
23
- registry.register(SetTransformer);
24
- registry.register(BufferTransformer);
25
- registry.register(ErrorTransformer);
26
- return registry;
27
- }
28
17
  /**
29
18
  * JSON serializer. With `preserveTypes`, plain objects that carry their own
30
19
  * string `$type` key are escaped as `{"$type":"Object","$value":{...}}` on
@@ -34,10 +23,18 @@ function createDefaultTransformers() {
34
23
  export class JSONSerializer {
35
24
  name = "json";
36
25
  contentType = "application/json";
26
+ registry;
37
27
  transformers;
38
28
  defaults;
39
29
  constructor(options) {
40
- this.transformers = options?.transformers ?? createDefaultTransformers();
30
+ const builtins = options?.builtins !== false;
31
+ const custom = options?.transformers;
32
+ this.registry =
33
+ custom ?? (builtins ? createBuiltinTransformers() : new TransformerRegistry());
34
+ this.transformers =
35
+ custom && builtins
36
+ ? layerTransformers(custom, createBuiltinTransformers())
37
+ : this.registry;
41
38
  this.defaults = options?.defaults ?? {};
42
39
  }
43
40
  /**
@@ -49,7 +46,7 @@ export class JSONSerializer {
49
46
  if (transformer.type === ESCAPED_OBJECT_TAG) {
50
47
  throw new TransformerError(transformer.type, `"${ESCAPED_OBJECT_TAG}" is reserved for escaped plain objects.`);
51
48
  }
52
- this.transformers.register(transformer);
49
+ this.registry.register(transformer);
53
50
  }
54
51
  serialize(value, options) {
55
52
  const opts = { ...this.defaults, ...options };
@@ -5,10 +5,10 @@
5
5
  * plain objects (see `jsonSerializer.escape.ts`).
6
6
  */
7
7
  import type { DeserializeOptions } from "../serializerTypes/index.js";
8
- import type { TransformerRegistry } from "../serializerTransforms/index.js";
8
+ import type { TransformerLookup } from "../serializerTransformPolicy/index.js";
9
9
  /** What the walker needs besides the value. */
10
10
  export interface RestoreWalk {
11
- readonly transformers: TransformerRegistry;
11
+ readonly transformers: TransformerLookup;
12
12
  readonly maxDepth: number;
13
13
  readonly options: DeserializeOptions;
14
14
  }
@@ -5,10 +5,10 @@
5
5
  * and escapes plain objects that would otherwise read back as a tag.
6
6
  */
7
7
  import type { SerializeOptions } from "../serializerTypes/index.js";
8
- import type { TransformerRegistry } from "../serializerTransforms/index.js";
8
+ import { type TransformerLookup } from "../serializerTransformPolicy/index.js";
9
9
  /** What the walker needs besides the value. */
10
10
  export interface TransformWalk {
11
- readonly transformers: TransformerRegistry;
11
+ readonly transformers: TransformerLookup;
12
12
  readonly maxDepth: number;
13
13
  readonly options: SerializeOptions;
14
14
  }
@@ -6,6 +6,7 @@
6
6
  */
7
7
  import { SerializationDepthError } from "@zudojs/errors";
8
8
  import { isPlainObject } from "@zudojs/types";
9
+ import { assertNotLossy, toTaggedOutput, } from "../serializerTransformPolicy/index.js";
9
10
  import { escapeObject, needsEscape } from "./jsonSerializer.escape.js";
10
11
  import { defineKey } from "./jsonSerializer.keys.js";
11
12
  /** Converts a value into its JSON-safe, tagged representation. */
@@ -15,7 +16,7 @@ export function transformValue(walk, value, depth) {
15
16
  if (typeof value === "bigint") {
16
17
  const transformer = walk.transformers.findForValue(value);
17
18
  return transformer
18
- ? transformer.serialize(value, walk.options)
19
+ ? transformEntries(walk, toTaggedOutput(transformer, transformer.serialize(value, walk.options)), depth + 1)
19
20
  : value.toString();
20
21
  }
21
22
  if (typeof value !== "object")
@@ -35,17 +36,17 @@ export function transformValue(walk, value, depth) {
35
36
  }
36
37
  const transformer = walk.transformers.findForValue(value);
37
38
  if (transformer) {
38
- const raw = transformer.serialize(value, walk.options);
39
39
  // The transformer's own tag object is not user data: its children are
40
- // walked, but it is never escaped.
41
- return isPlainObject(raw)
42
- ? transformEntries(walk, raw, depth + 1)
43
- : transformValue(walk, raw, depth + 1);
40
+ // walked, but it is never escaped. A bare return value is wrapped in
41
+ // `{ $type, $value }` for the transformer author.
42
+ const raw = transformer.serialize(value, walk.options);
43
+ return transformEntries(walk, toTaggedOutput(transformer, raw), depth + 1);
44
44
  }
45
45
  if (isPlainObject(value)) {
46
46
  const body = transformEntries(walk, value, depth);
47
47
  return needsEscape(value) ? escapeObject(body) : body;
48
48
  }
49
+ assertNotLossy(value);
49
50
  return value;
50
51
  }
51
52
  /** Transforms each own enumerable key of a plain object. */
@@ -17,8 +17,13 @@ import { SerializerRegistry } from "./serializerRegistry.core.js";
17
17
  * default that each call can still override.
18
18
  */
19
19
  export interface CreateSerializerOptions extends SerializeOptions, DeserializeOptions {
20
- /** Transformer registry to use instead of the built-in one. */
20
+ /**
21
+ * Your transformers, consulted before the built-in ones (which still
22
+ * handle every other type unless `builtins` is `false`).
23
+ */
21
24
  readonly transformers?: TransformerRegistry;
25
+ /** Include the built-in transformers (default `true`). */
26
+ readonly builtins?: boolean;
22
27
  /** Pretty-print by default. Overridable per call. */
23
28
  readonly pretty?: boolean;
24
29
  /** Preserve special JS types by default. Overridable per call. */
@@ -15,9 +15,13 @@ export function createSerializer(format, options) {
15
15
  case Format.JSON: {
16
16
  // Only `pretty` and `preserveTypes` used to be forwarded, so instance
17
17
  // limits such as `maxDepth`/`maxSize`/`strict` were silently dropped.
18
- const { transformers, ...rest } = options ?? {};
18
+ const { transformers, builtins, ...rest } = options ?? {};
19
19
  const defaults = Object.fromEntries(Object.entries(rest).filter(([, value]) => value !== undefined));
20
- return new JSONSerializer({ transformers, defaults });
20
+ return new JSONSerializer({
21
+ ...(transformers !== undefined ? { transformers } : {}),
22
+ ...(builtins !== undefined ? { builtins } : {}),
23
+ defaults,
24
+ });
21
25
  }
22
26
  default:
23
27
  throw new UnsupportedSerializationFormatError(format);
@@ -0,0 +1,10 @@
1
+ /**
2
+ * @zudojs/serialization — Transformer policy.
3
+ *
4
+ * How the serializer resolves transformers (a caller registry layered over
5
+ * the built-ins), normalises what a transformer's `serialize` returns, and
6
+ * refuses values it would otherwise silently write as `{}`.
7
+ */
8
+ export { createBuiltinTransformers, layerTransformers, type TransformerLookup, } from "./transformerLookup.core.js";
9
+ export { assertNotLossy, toTaggedOutput } from "./transformerOutput.helper.js";
10
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,10 @@
1
+ /**
2
+ * @zudojs/serialization — Transformer policy.
3
+ *
4
+ * How the serializer resolves transformers (a caller registry layered over
5
+ * the built-ins), normalises what a transformer's `serialize` returns, and
6
+ * refuses values it would otherwise silently write as `{}`.
7
+ */
8
+ export { createBuiltinTransformers, layerTransformers, } from "./transformerLookup.core.js";
9
+ export { assertNotLossy, toTaggedOutput } from "./transformerOutput.helper.js";
10
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,31 @@
1
+ /**
2
+ * @zudojs/serialization — Transformer lookup.
3
+ *
4
+ * The serializer resolves transformers through a lookup rather than a single
5
+ * registry, so a caller-supplied registry can sit in front of the built-ins
6
+ * instead of replacing them.
7
+ */
8
+ import type { TypeTransformer } from "../serializerTypes/index.js";
9
+ import { TransformerRegistry } from "../serializerTransforms/index.js";
10
+ /** The read side of a transformer registry, as the walkers use it. */
11
+ export interface TransformerLookup {
12
+ /** Find a transformer that can serialize the given value. */
13
+ findForValue(value: unknown): TypeTransformer | undefined;
14
+ /** Returns true when a transformer is registered for the type tag. */
15
+ has(type: string): boolean;
16
+ /** Retrieve a transformer by type tag (throws when absent). */
17
+ get(type: string): TypeTransformer;
18
+ }
19
+ /**
20
+ * Creates a registry holding every built-in transformer: `Date`, `BigInt`,
21
+ * `Map`, `Set`, `Uint8Array`/`Buffer` and `Error`.
22
+ */
23
+ export declare function createBuiltinTransformers(): TransformerRegistry;
24
+ /**
25
+ * Layers `primary` over `fallback`: every lookup consults `primary` first,
26
+ * so a caller's transformer for a tag or a value wins over the built-in
27
+ * one. `primary` is read live, so transformers registered on it later are
28
+ * seen too.
29
+ */
30
+ export declare function layerTransformers(primary: TransformerLookup, fallback: TransformerLookup): TransformerLookup;
31
+ //# sourceMappingURL=transformerLookup.core.d.ts.map
@@ -0,0 +1,37 @@
1
+ /**
2
+ * @zudojs/serialization — Transformer lookup.
3
+ *
4
+ * The serializer resolves transformers through a lookup rather than a single
5
+ * registry, so a caller-supplied registry can sit in front of the built-ins
6
+ * instead of replacing them.
7
+ */
8
+ import { BigIntTransformer, DateTransformer, MapTransformer, SetTransformer, TransformerRegistry, } from "../serializerTransforms/index.js";
9
+ import { BufferTransformer, ErrorTransformer, } from "../serializerTransformsExt/index.js";
10
+ /**
11
+ * Creates a registry holding every built-in transformer: `Date`, `BigInt`,
12
+ * `Map`, `Set`, `Uint8Array`/`Buffer` and `Error`.
13
+ */
14
+ export function createBuiltinTransformers() {
15
+ const registry = new TransformerRegistry();
16
+ registry.register(DateTransformer);
17
+ registry.register(BigIntTransformer);
18
+ registry.register(MapTransformer);
19
+ registry.register(SetTransformer);
20
+ registry.register(BufferTransformer);
21
+ registry.register(ErrorTransformer);
22
+ return registry;
23
+ }
24
+ /**
25
+ * Layers `primary` over `fallback`: every lookup consults `primary` first,
26
+ * so a caller's transformer for a tag or a value wins over the built-in
27
+ * one. `primary` is read live, so transformers registered on it later are
28
+ * seen too.
29
+ */
30
+ export function layerTransformers(primary, fallback) {
31
+ return {
32
+ findForValue: (value) => primary.findForValue(value) ?? fallback.findForValue(value),
33
+ has: (type) => primary.has(type) || fallback.has(type),
34
+ get: (type) => (primary.has(type) ? primary.get(type) : fallback.get(type)),
35
+ };
36
+ }
37
+ //# sourceMappingURL=transformerLookup.core.js.map
@@ -0,0 +1,24 @@
1
+ /**
2
+ * @zudojs/serialization — Transformer output and lossy-value checks.
3
+ */
4
+ import type { TypeTransformer } from "../serializerTypes/index.js";
5
+ /**
6
+ * Normalises what a transformer's `serialize` returned into the tagged
7
+ * `{ $type, $value }` form that is written to the wire.
8
+ *
9
+ * A transformer may return the full tagged object (the built-ins do; any
10
+ * plain object with its own string `$type` is taken as-is), or just the
11
+ * value to store, which is wrapped as `{ $type: transformer.type, $value }`.
12
+ * Before, a bare return was written untagged and could never be revived.
13
+ */
14
+ export declare function toTaggedOutput(transformer: TypeTransformer, raw: unknown): Record<string, unknown>;
15
+ /**
16
+ * Throws when a value that no transformer handles would be written as `{}`
17
+ * (a `Map`, `Set`, `Error`, `RegExp`, ...), instead of silently losing its
18
+ * contents. Only reachable when the built-in transformers are disabled or
19
+ * a type has no transformer.
20
+ *
21
+ * @throws {SerializeError} naming the type and how to fix it.
22
+ */
23
+ export declare function assertNotLossy(value: object): void;
24
+ //# sourceMappingURL=transformerOutput.helper.d.ts.map
@@ -0,0 +1,58 @@
1
+ /**
2
+ * @zudojs/serialization — Transformer output and lossy-value checks.
3
+ */
4
+ import { SerializationTags } from "@zudojs/constants";
5
+ import { SerializeError } from "@zudojs/errors";
6
+ import { isPlainObject } from "@zudojs/types";
7
+ /**
8
+ * Normalises what a transformer's `serialize` returned into the tagged
9
+ * `{ $type, $value }` form that is written to the wire.
10
+ *
11
+ * A transformer may return the full tagged object (the built-ins do; any
12
+ * plain object with its own string `$type` is taken as-is), or just the
13
+ * value to store, which is wrapped as `{ $type: transformer.type, $value }`.
14
+ * Before, a bare return was written untagged and could never be revived.
15
+ */
16
+ export function toTaggedOutput(transformer, raw) {
17
+ if (isPlainObject(raw) &&
18
+ Object.hasOwn(raw, SerializationTags.TYPE) &&
19
+ typeof raw[SerializationTags.TYPE] === "string") {
20
+ return raw;
21
+ }
22
+ return {
23
+ [SerializationTags.TYPE]: transformer.type,
24
+ [SerializationTags.VALUE]: raw,
25
+ };
26
+ }
27
+ /** Built-in objects whose JSON form silently drops their contents. */
28
+ const LOSSY_TYPES = [
29
+ ["Map", (value) => value instanceof Map],
30
+ ["Set", (value) => value instanceof Set],
31
+ ["WeakMap", (value) => value instanceof WeakMap],
32
+ ["WeakSet", (value) => value instanceof WeakSet],
33
+ ["WeakRef", (value) => value instanceof WeakRef],
34
+ ["Promise", (value) => value instanceof Promise],
35
+ ["RegExp", (value) => value instanceof RegExp],
36
+ ["Error", (value) => value instanceof Error],
37
+ ["ArrayBuffer", (value) => value instanceof ArrayBuffer],
38
+ ["DataView", (value) => value instanceof DataView],
39
+ ];
40
+ /**
41
+ * Throws when a value that no transformer handles would be written as `{}`
42
+ * (a `Map`, `Set`, `Error`, `RegExp`, ...), instead of silently losing its
43
+ * contents. Only reachable when the built-in transformers are disabled or
44
+ * a type has no transformer.
45
+ *
46
+ * @throws {SerializeError} naming the type and how to fix it.
47
+ */
48
+ export function assertNotLossy(value) {
49
+ for (const [name, matches] of LOSSY_TYPES) {
50
+ if (matches(value)) {
51
+ throw new SerializeError(`Cannot serialize a ${name} with preserveTypes: no transformer is ` +
52
+ `registered for it, and JSON would write it as {} and lose its ` +
53
+ `contents. Register a transformer for ${name}, or keep the ` +
54
+ `built-in transformers enabled (do not pass builtins: false).`, { format: "json" });
55
+ }
56
+ }
57
+ }
58
+ //# sourceMappingURL=transformerOutput.helper.js.map
@@ -86,15 +86,34 @@ export interface Serializer<TValue = unknown, TSerialized = SerializedValue> {
86
86
  /** Deserialize a value from the target format. */
87
87
  deserialize<T = TValue>(value: TSerialized, options?: DeserializeOptions): T;
88
88
  }
89
- /** Transforms a specific JS type during serialization. */
89
+ /**
90
+ * Transforms a specific JS type during serialization.
91
+ *
92
+ * On the wire a transformed value is always the tagged object
93
+ * `{ "$type": type, "$value": ... }`.
94
+ */
90
95
  export interface TypeTransformer<TValue = unknown> {
91
96
  /** Tag name used in tagged representations (e.g., "Date"). */
92
97
  readonly type: string;
93
98
  /** Returns true when this transformer handles the given value. */
94
99
  canSerialize(value: unknown): value is TValue;
95
- /** Convert the value into a JSON-safe representation. */
100
+ /**
101
+ * Convert the value into a JSON-safe representation.
102
+ *
103
+ * Return either just the value to store (for example
104
+ * `v.toString()`), which the serializer wraps as
105
+ * `{ $type: type, $value: <returned> }`, or the full tagged object
106
+ * `{ $type: type, $value: ... }` yourself (as the built-ins do). A plain
107
+ * object with its own string `$type` is taken to be the full tagged
108
+ * object; anything else is wrapped. Nested special values inside the
109
+ * returned value are transformed too.
110
+ */
96
111
  serialize(value: TValue, options?: SerializeOptions): unknown;
97
- /** Reconstruct the original value from the serialized form. */
112
+ /**
113
+ * Reconstruct the original value. Always receives the full tagged object
114
+ * `{ $type, $value }` (with nested values already restored), whichever
115
+ * form `serialize` returned, so read `$value` from it.
116
+ */
98
117
  deserialize(value: unknown, options?: DeserializeOptions): TValue;
99
118
  }
100
119
  //# sourceMappingURL=serializer.type.d.ts.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zudojs/serialization",
3
- "version": "1.1.1",
3
+ "version": "1.2.0",
4
4
  "description": "Data translation layer with JSON serializer, type transformers, envelopes, and registry for Zudojs applications.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -19,14 +19,14 @@
19
19
  "LICENSE"
20
20
  ],
21
21
  "dependencies": {
22
- "@zudojs/constants": "1.1.1",
23
- "@zudojs/errors": "1.2.0",
24
- "@zudojs/types": "1.1.1",
25
- "@zudojs/validation": "1.0.3"
22
+ "@zudojs/constants": "1.1.2",
23
+ "@zudojs/errors": "1.3.0",
24
+ "@zudojs/types": "1.2.0",
25
+ "@zudojs/validation": "1.1.0"
26
26
  },
27
27
  "devDependencies": {
28
28
  "typescript": "7.0.2",
29
- "vitest": "^4.1.11"
29
+ "vitest": "^5.0.1"
30
30
  },
31
31
  "license": "MIT",
32
32
  "author": {
@@ -46,7 +46,7 @@
46
46
  "node": ">=24.0.0"
47
47
  },
48
48
  "module": "./dist/index.js",
49
- "homepage": "https://github.com/oyinlola-tech/zudo#readme",
49
+ "homepage": "https://zudojs.oyinlola.site/docs/packages-serialization",
50
50
  "bugs": {
51
51
  "url": "https://github.com/oyinlola-tech/zudo/issues"
52
52
  },