@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 +25 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1 -0
- package/dist/serializerJson/index.d.ts +1 -1
- package/dist/serializerJson/index.js +1 -1
- package/dist/serializerJson/jsonSerializer.core.d.ts +21 -5
- package/dist/serializerJson/jsonSerializer.core.js +12 -15
- package/dist/serializerJson/jsonSerializer.restore.d.ts +2 -2
- package/dist/serializerJson/jsonSerializer.transform.d.ts +2 -2
- package/dist/serializerJson/jsonSerializer.transform.js +7 -6
- package/dist/serializerRegistry/serializerRegistry.factory.d.ts +6 -1
- package/dist/serializerRegistry/serializerRegistry.factory.js +6 -2
- package/dist/serializerTransformPolicy/index.d.ts +10 -0
- package/dist/serializerTransformPolicy/index.js +10 -0
- package/dist/serializerTransformPolicy/transformerLookup.core.d.ts +31 -0
- package/dist/serializerTransformPolicy/transformerLookup.core.js +37 -0
- package/dist/serializerTransformPolicy/transformerOutput.helper.d.ts +24 -0
- package/dist/serializerTransformPolicy/transformerOutput.helper.js +58 -0
- package/dist/serializerTypes/serializer.type.d.ts +22 -3
- package/package.json +7 -7
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:
|
|
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
|
|
9
|
-
import {
|
|
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
|
-
|
|
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.
|
|
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 {
|
|
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:
|
|
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
|
|
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:
|
|
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
|
-
|
|
42
|
-
|
|
43
|
-
|
|
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
|
-
/**
|
|
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({
|
|
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
|
-
/**
|
|
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
|
-
/**
|
|
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
|
-
/**
|
|
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.
|
|
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.
|
|
23
|
-
"@zudojs/errors": "1.
|
|
24
|
-
"@zudojs/types": "1.
|
|
25
|
-
"@zudojs/validation": "1.0
|
|
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": "^
|
|
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://
|
|
49
|
+
"homepage": "https://zudojs.oyinlola.site/docs/packages-serialization",
|
|
50
50
|
"bugs": {
|
|
51
51
|
"url": "https://github.com/oyinlola-tech/zudo/issues"
|
|
52
52
|
},
|