seroval 0.4.0-alpha.13 → 0.4.0-alpha.14

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/src/index.ts CHANGED
@@ -4,10 +4,11 @@ import {
4
4
  SerializationContext,
5
5
  getRefParam,
6
6
  Options,
7
- ParserContext,
7
+ createParserContext,
8
+ createSerializationContext,
8
9
  } from './context';
9
10
  import parseAsync from './tree/async';
10
- import serializeTree, { resolvePatches } from './tree/serialize';
11
+ import SerovalSerializer, { resolvePatches } from './tree/serialize';
11
12
  import parseSync from './tree/sync';
12
13
  import { SerovalNode } from './tree/types';
13
14
  import {
@@ -69,10 +70,10 @@ export function serialize<T extends ServerValue>(
69
70
  source: T,
70
71
  options?: Partial<Options>,
71
72
  ) {
72
- const ctx = new ParserContext(options);
73
+ const ctx = createParserContext(options);
73
74
  const [tree, rootID, isObject] = parseSync(ctx, source);
74
- const serial = new SerializationContext(ctx);
75
- const result = serializeTree(serial, tree);
75
+ const serial = createSerializationContext(ctx);
76
+ const result = new SerovalSerializer(serial).serialize(tree);
76
77
  return finalize(serial, rootID, isObject, result);
77
78
  }
78
79
 
@@ -80,10 +81,10 @@ export async function serializeAsync<T extends AsyncServerValue>(
80
81
  source: T,
81
82
  options?: Partial<Options>,
82
83
  ) {
83
- const ctx = new ParserContext(options);
84
+ const ctx = createParserContext(options);
84
85
  const [tree, rootID, isObject] = await parseAsync(ctx, source);
85
- const serial = new SerializationContext(ctx);
86
- const result = serializeTree(serial, tree);
86
+ const serial = createSerializationContext(ctx);
87
+ const result = new SerovalSerializer(serial).serialize(tree);
87
88
  return finalize(serial, rootID, isObject, result);
88
89
  }
89
90
 
@@ -92,52 +93,52 @@ export function deserialize<T extends AsyncServerValue>(source: string): T {
92
93
  return (0, eval)(source) as T;
93
94
  }
94
95
 
95
- type SerovalJSON = [
96
- tree: SerovalNode,
97
- root: number,
98
- isObject: boolean,
99
- feature: number,
100
- markedRefs: Record<number, number>,
101
- ];
96
+ interface SerovalJSON {
97
+ t: SerovalNode,
98
+ r: number,
99
+ i: boolean,
100
+ f: number,
101
+ m: number[],
102
+ }
102
103
 
103
104
  export function toJSON<T extends ServerValue>(
104
105
  source: T,
105
106
  options?: Partial<Options>,
106
107
  ) {
107
- const ctx = new ParserContext(options);
108
+ const ctx = createParserContext(options);
108
109
  const [tree, root, isObject] = parseSync(ctx, source);
109
- return JSON.stringify([
110
- tree,
111
- root,
112
- isObject,
113
- ctx.features,
114
- ctx.markedRefs,
115
- ]);
110
+ return JSON.stringify({
111
+ t: tree,
112
+ r: root,
113
+ i: isObject,
114
+ f: ctx.features,
115
+ m: Array.from(ctx.markedRefs),
116
+ });
116
117
  }
117
118
 
118
119
  export async function toJSONAsync<T extends AsyncServerValue>(
119
120
  source: T,
120
121
  options?: Partial<Options>,
121
122
  ) {
122
- const ctx = new ParserContext(options);
123
+ const ctx = createParserContext(options);
123
124
  const [tree, root, isObject] = await parseAsync(ctx, source);
124
- return JSON.stringify([
125
- tree,
126
- root,
127
- isObject,
128
- ctx.features,
129
- ctx.markedRefs,
130
- ]);
125
+ return JSON.stringify({
126
+ t: tree,
127
+ r: root,
128
+ i: isObject,
129
+ f: ctx.features,
130
+ m: Array.from(ctx.markedRefs),
131
+ });
131
132
  }
132
133
 
133
134
  export function compileJSON(source: string): string {
134
135
  const parsed = JSON.parse(source) as SerovalJSON;
135
- const serial = new SerializationContext({
136
- features: parsed[3],
137
- markedRefs: parsed[4],
136
+ const serial = createSerializationContext({
137
+ features: parsed.f,
138
+ markedRefs: parsed.m,
138
139
  });
139
- const result = serializeTree(serial, parsed[0]);
140
- return finalize(serial, parsed[1], parsed[2], result);
140
+ const result = new SerovalSerializer(serial).serialize(parsed.t);
141
+ return finalize(serial, parsed.r, parsed.i, result);
141
142
  }
142
143
 
143
144
  export function fromJSON<T extends AsyncServerValue>(source: string): T {
package/src/tree/async.ts CHANGED
@@ -10,9 +10,14 @@ import {
10
10
  TypedArrayValue,
11
11
  } from '../types';
12
12
  import {
13
+ createBigIntNode,
14
+ createBigIntTypedArrayNode,
15
+ createDateNode,
16
+ createPrimitiveNode,
17
+ createReferenceNode,
18
+ createRegExpNode,
19
+ createTypedArrayNode,
13
20
  FALSE_NODE,
14
- INFINITY_NODE,
15
- NEG_INFINITY_NODE,
16
21
  NEG_ZERO_NODE,
17
22
  NULL_NODE,
18
23
  TRUE_NODE,
@@ -27,24 +32,16 @@ import {
27
32
  import {
28
33
  SerovalAggregateErrorNode,
29
34
  SerovalArrayNode,
30
- SerovalBigIntNode,
31
- SerovalBigIntTypedArrayNode,
32
- SerovalDateNode,
33
35
  SerovalErrorNode,
34
36
  SerovalIterableNode,
35
37
  SerovalMapNode,
36
- SerovalMapRecordNode,
37
38
  SerovalNode,
38
39
  SerovalNodeType,
39
40
  SerovalNullConstructorNode,
40
41
  SerovalObjectNode,
41
42
  SerovalObjectRecordNode,
42
- SerovalPrimitiveNode,
43
43
  SerovalPromiseNode,
44
- SerovalReferenceNode,
45
- SerovalRegExpNode,
46
44
  SerovalSetNode,
47
- SerovalTypedArrayNode,
48
45
  } from './types';
49
46
 
50
47
  type ObjectLikeNode =
@@ -94,7 +91,17 @@ class AsyncParser {
94
91
  id: number,
95
92
  current: AsyncServerValue[],
96
93
  ): Promise<SerovalArrayNode> {
97
- return new SerovalArrayNode(id, await this.generateNodeList(current));
94
+ return {
95
+ t: SerovalNodeType.Array,
96
+ i: id,
97
+ s: undefined,
98
+ l: undefined,
99
+ c: undefined,
100
+ m: undefined,
101
+ d: undefined,
102
+ a: await this.generateNodeList(current),
103
+ n: undefined,
104
+ };
98
105
  }
99
106
 
100
107
  async generateMapNode(
@@ -125,7 +132,17 @@ class AsyncParser {
125
132
  keyNodes[nodeSize + i] = await this.parse(deferredKey[i]);
126
133
  valueNodes[nodeSize + i] = await this.parse(deferredValue[i]);
127
134
  }
128
- return new SerovalMapNode(id, new SerovalMapRecordNode(keyNodes, valueNodes, len));
135
+ return {
136
+ t: SerovalNodeType.Map,
137
+ i: id,
138
+ s: undefined,
139
+ l: undefined,
140
+ c: undefined,
141
+ m: undefined,
142
+ d: { k: keyNodes, v: valueNodes, s: len },
143
+ a: undefined,
144
+ n: undefined,
145
+ };
129
146
  }
130
147
 
131
148
  async generateSetNode(
@@ -150,7 +167,17 @@ class AsyncParser {
150
167
  for (let i = 0; i < deferredSize; i++) {
151
168
  nodes[nodeSize + i] = await this.parse(deferred[i]);
152
169
  }
153
- return new SerovalSetNode(id, nodes);
170
+ return {
171
+ t: SerovalNodeType.Set,
172
+ i: id,
173
+ s: undefined,
174
+ l: undefined,
175
+ c: undefined,
176
+ m: undefined,
177
+ d: undefined,
178
+ a: nodes,
179
+ n: undefined,
180
+ };
154
181
  }
155
182
 
156
183
  async generateProperties(
@@ -181,7 +208,11 @@ class AsyncParser {
181
208
  keyNodes[nodesSize + i] = deferredKeys[i];
182
209
  valueNodes[nodesSize + i] = await this.parse(deferredValues[i]);
183
210
  }
184
- return new SerovalObjectRecordNode(keyNodes, valueNodes, size);
211
+ return {
212
+ k: keyNodes,
213
+ v: valueNodes,
214
+ s: size,
215
+ };
185
216
  }
186
217
 
187
218
  async generateIterableNode(
@@ -190,14 +221,20 @@ class AsyncParser {
190
221
  ): Promise<SerovalIterableNode> {
191
222
  assert(this.ctx.features & Feature.SymbolIterator, 'Unsupported type "Iterable"');
192
223
  const options = getIterableOptions(current);
193
- return new SerovalIterableNode(
194
- id,
224
+ return {
225
+ t: SerovalNodeType.Iterable,
226
+ i: id,
227
+ s: undefined,
228
+ l: undefined,
229
+ c: undefined,
230
+ m: undefined,
195
231
  // Parse options first before the items
196
- options
232
+ d: options
197
233
  ? await this.generateProperties(options as Record<string, AsyncServerValue>)
198
234
  : undefined,
199
- await this.generateNodeList(Array.from(current)),
200
- );
235
+ a: await this.generateNodeList(Array.from(current)),
236
+ n: undefined,
237
+ };
201
238
  }
202
239
 
203
240
  async generatePromiseNode(
@@ -205,7 +242,18 @@ class AsyncParser {
205
242
  current: PromiseLike<AsyncServerValue>,
206
243
  ): Promise<SerovalPromiseNode> {
207
244
  assert(this.ctx.features & Feature.Promise, 'Unsupported type "Promise"');
208
- return current.then(async (value) => new SerovalPromiseNode(id, await this.parse(value)));
245
+ return current.then(async (value) => ({
246
+ t: SerovalNodeType.Promise,
247
+ i: id,
248
+ s: undefined,
249
+ l: undefined,
250
+ c: undefined,
251
+ m: undefined,
252
+ // Parse options first before the items
253
+ d: undefined,
254
+ a: undefined,
255
+ n: await this.parse(value),
256
+ }));
209
257
  }
210
258
 
211
259
  async generateObjectNode(
@@ -219,11 +267,17 @@ class AsyncParser {
219
267
  if ('then' in current && typeof current.then === 'function') {
220
268
  return this.generatePromiseNode(id, current as PromiseLike<AsyncServerValue>);
221
269
  }
222
- const properties = await this.generateProperties(current as Record<string, AsyncServerValue>);
223
- if (empty) {
224
- return new SerovalNullConstructorNode(id, properties);
225
- }
226
- return new SerovalObjectNode(id, properties);
270
+ return {
271
+ t: empty ? SerovalNodeType.NullConstructor : SerovalNodeType.Object,
272
+ i: id,
273
+ s: undefined,
274
+ l: undefined,
275
+ c: undefined,
276
+ m: undefined,
277
+ d: await this.generateProperties(current as Record<string, AsyncServerValue>),
278
+ a: undefined,
279
+ n: undefined,
280
+ };
227
281
  }
228
282
 
229
283
  async generateAggregateErrorNode(
@@ -234,12 +288,17 @@ class AsyncParser {
234
288
  const optionsNode = options
235
289
  ? await this.generateProperties(options)
236
290
  : undefined;
237
- return new SerovalAggregateErrorNode(
238
- id,
239
- current.message,
240
- optionsNode,
241
- await this.parse(current.errors),
242
- );
291
+ return {
292
+ t: SerovalNodeType.AggregateError,
293
+ i: id,
294
+ s: undefined,
295
+ l: undefined,
296
+ c: undefined,
297
+ m: current.message,
298
+ d: optionsNode,
299
+ a: undefined,
300
+ n: await this.parse(current.errors),
301
+ };
243
302
  }
244
303
 
245
304
  async generateErrorNode(
@@ -250,7 +309,17 @@ class AsyncParser {
250
309
  const optionsNode = options
251
310
  ? await this.generateProperties(options)
252
311
  : undefined;
253
- return new SerovalErrorNode(id, getErrorConstructor(current), current.message, optionsNode);
312
+ return {
313
+ t: SerovalNodeType.Error,
314
+ i: id,
315
+ s: undefined,
316
+ l: undefined,
317
+ c: getErrorConstructor(current),
318
+ m: current.message,
319
+ d: optionsNode,
320
+ a: undefined,
321
+ n: undefined,
322
+ };
254
323
  }
255
324
 
256
325
  async parse(current: AsyncServerValue): Promise<SerovalNode> {
@@ -260,21 +329,20 @@ class AsyncParser {
260
329
  case 'undefined':
261
330
  return UNDEFINED_NODE;
262
331
  case 'string':
263
- return new SerovalPrimitiveNode(quote(current));
332
+ return createPrimitiveNode(quote(current));
264
333
  case 'number':
265
334
  if (Object.is(current, -0)) {
266
335
  return NEG_ZERO_NODE;
267
336
  }
268
337
  if (Object.is(current, Infinity)) {
269
- return INFINITY_NODE;
338
+ return createPrimitiveNode('1/0');
270
339
  }
271
340
  if (Object.is(current, -Infinity)) {
272
- return NEG_INFINITY_NODE;
341
+ return createPrimitiveNode('-1/0');
273
342
  }
274
- return new SerovalPrimitiveNode(current);
343
+ return createPrimitiveNode(current);
275
344
  case 'bigint':
276
- assert(this.ctx.features & Feature.BigInt, 'Unsupported type "BigInt"');
277
- return new SerovalBigIntNode(current);
345
+ return createBigIntNode(this.ctx, current);
278
346
  case 'object': {
279
347
  if (!current) {
280
348
  return NULL_NODE;
@@ -282,17 +350,17 @@ class AsyncParser {
282
350
  // Non-primitive values needs a reference ID
283
351
  // mostly because the values themselves are stateful
284
352
  const id = createRef(this.ctx, current, true);
285
- if (this.ctx.markedRefs[id]) {
286
- return new SerovalReferenceNode(id);
353
+ if (this.ctx.markedRefs.has(id)) {
354
+ return createReferenceNode(id);
287
355
  }
288
356
  if (Array.isArray(current)) {
289
357
  return this.generateArrayNode(id, current);
290
358
  }
291
359
  switch (current.constructor) {
292
360
  case Date:
293
- return new SerovalDateNode(id, current as Date);
361
+ return createDateNode(id, current as Date);
294
362
  case RegExp:
295
- return new SerovalRegExpNode(id, current as RegExp);
363
+ return createRegExpNode(id, current as RegExp);
296
364
  case Promise:
297
365
  return this.generatePromiseNode(id, current as Promise<AsyncServerValue>);
298
366
  case Int8Array:
@@ -304,15 +372,10 @@ class AsyncParser {
304
372
  case Uint8ClampedArray:
305
373
  case Float32Array:
306
374
  case Float64Array:
307
- assert(this.ctx.features & Feature.TypedArray, `Unsupported value type "${current.constructor.name}"`);
308
- return new SerovalTypedArrayNode(id, current as TypedArrayValue);
375
+ return createTypedArrayNode(this.ctx, id, current as TypedArrayValue);
309
376
  case BigInt64Array:
310
377
  case BigUint64Array:
311
- assert(
312
- this.ctx.features & (Feature.BigIntTypedArray),
313
- `Unsupported value type "${current.constructor.name}"`,
314
- );
315
- return new SerovalBigIntTypedArrayNode(id, current as BigIntTypedArrayValue);
378
+ return createBigIntTypedArrayNode(this.ctx, id, current as BigIntTypedArrayValue);
316
379
  case Map:
317
380
  return this.generateMapNode(id, current as Map<AsyncServerValue, AsyncServerValue>);
318
381
  case Set:
@@ -1,11 +1,147 @@
1
+ import assert from '../assert';
2
+ import { Feature } from '../compat';
3
+ import { ParserContext } from '../context';
4
+ import { BigIntTypedArrayValue, TypedArrayValue } from '../types';
1
5
  import {
6
+ SerovalBigIntNode,
7
+ SerovalBigIntTypedArrayNode,
8
+ SerovalDateNode,
9
+ SerovalNodeType,
2
10
  SerovalPrimitiveNode,
11
+ SerovalReferenceNode,
12
+ SerovalRegExpNode,
13
+ SerovalTypedArrayNode,
3
14
  } from './types';
4
15
 
5
- export const TRUE_NODE = new SerovalPrimitiveNode('!0');
6
- export const FALSE_NODE = new SerovalPrimitiveNode('!1');
7
- export const UNDEFINED_NODE = new SerovalPrimitiveNode('void 0');
8
- export const NULL_NODE = new SerovalPrimitiveNode(null);
9
- export const NEG_ZERO_NODE = new SerovalPrimitiveNode('-0');
10
- export const INFINITY_NODE = new SerovalPrimitiveNode('1/0');
11
- export const NEG_INFINITY_NODE = new SerovalPrimitiveNode('-1/0');
16
+ export function createPrimitiveNode(
17
+ value: string | number | null,
18
+ ): SerovalPrimitiveNode {
19
+ return {
20
+ t: SerovalNodeType.Primitive,
21
+ i: undefined,
22
+ s: value,
23
+ l: undefined,
24
+ c: undefined,
25
+ m: undefined,
26
+ d: undefined,
27
+ a: undefined,
28
+ n: undefined,
29
+ };
30
+ }
31
+
32
+ export const TRUE_NODE = createPrimitiveNode('!0');
33
+ export const FALSE_NODE = createPrimitiveNode('!1');
34
+ export const UNDEFINED_NODE = createPrimitiveNode('void 0');
35
+ export const NULL_NODE = createPrimitiveNode(null);
36
+ export const NEG_ZERO_NODE = createPrimitiveNode('-0');
37
+ export const INFINITY_NODE = createPrimitiveNode('1/0');
38
+ export const NEG_INFINITY_NODE = createPrimitiveNode('-1/0');
39
+
40
+ export function createBigIntNode(
41
+ ctx: ParserContext,
42
+ current: bigint,
43
+ ): SerovalBigIntNode {
44
+ assert(ctx.features & Feature.BigInt, 'Unsupported type "BigInt"');
45
+ return {
46
+ t: SerovalNodeType.BigInt,
47
+ i: undefined,
48
+ s: `${current}n`,
49
+ l: undefined,
50
+ c: undefined,
51
+ m: undefined,
52
+ d: undefined,
53
+ a: undefined,
54
+ n: undefined,
55
+ };
56
+ }
57
+
58
+ export function createReferenceNode(id: number): SerovalReferenceNode {
59
+ return {
60
+ t: SerovalNodeType.Reference,
61
+ i: id,
62
+ s: undefined,
63
+ l: undefined,
64
+ c: undefined,
65
+ m: undefined,
66
+ d: undefined,
67
+ a: undefined,
68
+ n: undefined,
69
+ };
70
+ }
71
+
72
+ export function createDateNode(id: number, current: Date): SerovalDateNode {
73
+ return {
74
+ t: SerovalNodeType.Date,
75
+ i: id,
76
+ s: current.toISOString(),
77
+ l: undefined,
78
+ c: undefined,
79
+ m: undefined,
80
+ d: undefined,
81
+ n: undefined,
82
+ a: undefined,
83
+ };
84
+ }
85
+
86
+ export function createRegExpNode(id: number, current: RegExp): SerovalRegExpNode {
87
+ return {
88
+ t: SerovalNodeType.RegExp,
89
+ i: id,
90
+ s: String(current),
91
+ l: undefined,
92
+ c: undefined,
93
+ m: undefined,
94
+ d: undefined,
95
+ a: undefined,
96
+ n: undefined,
97
+ };
98
+ }
99
+
100
+ export function createTypedArrayNode(
101
+ ctx: ParserContext,
102
+ id: number,
103
+ current: TypedArrayValue,
104
+ ): SerovalTypedArrayNode {
105
+ const constructor = current.constructor.name;
106
+ assert(ctx.features & Feature.TypedArray, `Unsupported value type "${constructor}"`);
107
+ return {
108
+ t: SerovalNodeType.TypedArray,
109
+ i: id,
110
+ s: current.toString(),
111
+ l: current.byteOffset,
112
+ c: constructor,
113
+ m: undefined,
114
+ d: undefined,
115
+ a: undefined,
116
+ n: undefined,
117
+ };
118
+ }
119
+
120
+ export function createBigIntTypedArrayNode(
121
+ ctx: ParserContext,
122
+ id: number,
123
+ current: BigIntTypedArrayValue,
124
+ ): SerovalBigIntTypedArrayNode {
125
+ const constructor = current.constructor.name;
126
+ assert(
127
+ ctx.features & (Feature.BigIntTypedArray),
128
+ `Unsupported value type "${constructor}"`,
129
+ );
130
+ let result = '';
131
+ const cap = current.length - 1;
132
+ for (let i = 0; i < cap; i++) {
133
+ result += `${current[i]}n,`;
134
+ }
135
+ result += `"${current[cap]}"`;
136
+ return {
137
+ t: SerovalNodeType.BigIntTypedArray,
138
+ i: id,
139
+ s: result,
140
+ l: (current as BigInt64Array).byteOffset,
141
+ c: constructor,
142
+ m: undefined,
143
+ d: undefined,
144
+ a: undefined,
145
+ n: undefined,
146
+ };
147
+ }