seroval 0.4.1 → 0.5.1

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/tree/async.ts CHANGED
@@ -2,8 +2,7 @@
2
2
  /* eslint-disable @typescript-eslint/no-use-before-define */
3
3
  import assert from '../assert';
4
4
  import { Feature } from '../compat';
5
- import { createRef, ParserContext } from '../context';
6
- import quote from '../quote';
5
+ import { createRef, getRootID, ParserContext } from '../context';
7
6
  import {
8
7
  AsyncServerValue,
9
8
  BigIntTypedArrayValue,
@@ -13,22 +12,28 @@ import {
13
12
  createBigIntNode,
14
13
  createBigIntTypedArrayNode,
15
14
  createDateNode,
16
- createPrimitiveNode,
15
+ createNumberNode,
17
16
  createReferenceNode,
18
17
  createRegExpNode,
18
+ createStringNode,
19
19
  createTypedArrayNode,
20
+ createWKSymbolNode,
20
21
  FALSE_NODE,
22
+ INFINITY_NODE,
23
+ NAN_NODE,
24
+ NEG_INFINITY_NODE,
21
25
  NEG_ZERO_NODE,
22
26
  NULL_NODE,
23
27
  TRUE_NODE,
24
28
  UNDEFINED_NODE,
25
29
  } from './primitives';
26
30
  import {
27
- getErrorConstructor,
31
+ getErrorConstructorName,
28
32
  getErrorOptions,
29
33
  getIterableOptions,
30
34
  isIterable,
31
35
  } from './shared';
36
+ import { INV_SYMBOL_REF } from './symbols';
32
37
  import {
33
38
  SerovalAggregateErrorNode,
34
39
  SerovalArrayNode,
@@ -90,12 +95,12 @@ async function generateArrayNode(
90
95
  t: SerovalNodeType.Array,
91
96
  i: id,
92
97
  s: undefined,
93
- l: undefined,
98
+ l: current.length,
94
99
  c: undefined,
95
100
  m: undefined,
96
101
  d: undefined,
97
102
  a: await generateNodeList(ctx, current),
98
- n: undefined,
103
+ f: undefined,
99
104
  };
100
105
  }
101
106
 
@@ -137,7 +142,7 @@ async function generateMapNode(
137
142
  m: undefined,
138
143
  d: { k: keyNodes, v: valueNodes, s: len },
139
144
  a: undefined,
140
- n: undefined,
145
+ f: undefined,
141
146
  };
142
147
  }
143
148
 
@@ -168,12 +173,12 @@ async function generateSetNode(
168
173
  t: SerovalNodeType.Set,
169
174
  i: id,
170
175
  s: undefined,
171
- l: undefined,
176
+ l: len,
172
177
  c: undefined,
173
178
  m: undefined,
174
179
  d: undefined,
175
180
  a: nodes,
176
- n: undefined,
181
+ f: undefined,
177
182
  };
178
183
  }
179
184
 
@@ -218,21 +223,22 @@ async function generateIterableNode(
218
223
  id: number,
219
224
  current: Iterable<AsyncServerValue>,
220
225
  ): Promise<SerovalIterableNode> {
221
- assert(ctx.features & Feature.SymbolIterator, 'Unsupported type "Iterable"');
226
+ assert(ctx.features & Feature.Symbol, 'Unsupported type "Iterable"');
222
227
  const options = getIterableOptions(current);
228
+ const array = Array.from(current);
223
229
  return {
224
230
  t: SerovalNodeType.Iterable,
225
231
  i: id,
226
232
  s: undefined,
227
- l: undefined,
233
+ l: array.length,
228
234
  c: undefined,
229
235
  m: undefined,
230
236
  // Parse options first before the items
231
237
  d: options
232
238
  ? await generateProperties(ctx, options as Record<string, AsyncServerValue>)
233
239
  : undefined,
234
- a: await generateNodeList(ctx, Array.from(current)),
235
- n: undefined,
240
+ a: await generateNodeList(ctx, array),
241
+ f: undefined,
236
242
  };
237
243
  }
238
244
 
@@ -252,7 +258,7 @@ async function generatePromiseNode(
252
258
  // Parse options first before the items
253
259
  d: undefined,
254
260
  a: undefined,
255
- n: await parse(ctx, value),
261
+ f: await parse(ctx, value),
256
262
  }));
257
263
  }
258
264
 
@@ -277,7 +283,7 @@ async function generateObjectNode(
277
283
  m: undefined,
278
284
  d: await generateProperties(ctx, current as Record<string, AsyncServerValue>),
279
285
  a: undefined,
280
- n: undefined,
286
+ f: undefined,
281
287
  };
282
288
  }
283
289
 
@@ -294,12 +300,12 @@ async function generateAggregateErrorNode(
294
300
  t: SerovalNodeType.AggregateError,
295
301
  i: id,
296
302
  s: undefined,
297
- l: undefined,
303
+ l: current.errors.length,
298
304
  c: undefined,
299
305
  m: current.message,
300
306
  d: optionsNode,
301
- a: undefined,
302
- n: await parse(ctx, current.errors),
307
+ a: await generateNodeList(ctx, current.errors as AsyncServerValue[]),
308
+ f: undefined,
303
309
  };
304
310
  }
305
311
 
@@ -317,11 +323,11 @@ async function generateErrorNode(
317
323
  i: id,
318
324
  s: undefined,
319
325
  l: undefined,
320
- c: getErrorConstructor(current),
326
+ c: getErrorConstructorName(current),
321
327
  m: current.message,
322
328
  d: optionsNode,
323
329
  a: undefined,
324
- n: undefined,
330
+ f: undefined,
325
331
  };
326
332
  }
327
333
 
@@ -335,18 +341,21 @@ async function parse(
335
341
  case 'undefined':
336
342
  return UNDEFINED_NODE;
337
343
  case 'string':
338
- return createPrimitiveNode(quote(current));
344
+ return createStringNode(current);
339
345
  case 'number':
340
- if (Object.is(current, -0)) {
341
- return NEG_ZERO_NODE;
346
+ switch (current) {
347
+ case Infinity: return INFINITY_NODE;
348
+ case -Infinity: return NEG_INFINITY_NODE;
349
+ default: break;
342
350
  }
343
- if (Object.is(current, Infinity)) {
344
- return createPrimitiveNode('1/0');
351
+ // eslint-disable-next-line no-self-compare
352
+ if (current !== current) {
353
+ return NAN_NODE;
345
354
  }
346
- if (Object.is(current, -Infinity)) {
347
- return createPrimitiveNode('-1/0');
355
+ if (Object.is(current, -0)) {
356
+ return NEG_ZERO_NODE;
348
357
  }
349
- return createPrimitiveNode(current);
358
+ return createNumberNode(current);
350
359
  case 'bigint':
351
360
  return createBigIntNode(ctx, current);
352
361
  case 'object': {
@@ -355,7 +364,7 @@ async function parse(
355
364
  }
356
365
  // Non-primitive values needs a reference ID
357
366
  // mostly because the values themselves are stateful
358
- const id = createRef(ctx, current, true);
367
+ const id = createRef(ctx, current);
359
368
  if (ctx.markedRefs.has(id)) {
360
369
  return createReferenceNode(id);
361
370
  }
@@ -424,8 +433,11 @@ async function parse(
424
433
  default:
425
434
  break;
426
435
  }
427
- if (current instanceof AggregateError && ctx.features & Feature.AggregateError) {
428
- return generateAggregateErrorNode(ctx, id, current);
436
+ if (current instanceof AggregateError) {
437
+ if (ctx.features & Feature.AggregateError) {
438
+ return generateAggregateErrorNode(ctx, id, current);
439
+ }
440
+ return generateErrorNode(ctx, id, current);
429
441
  }
430
442
  if (current instanceof Error) {
431
443
  return generateErrorNode(ctx, id, current);
@@ -443,6 +455,10 @@ async function parse(
443
455
  }
444
456
  throw new Error('Unsupported type');
445
457
  }
458
+ case 'symbol':
459
+ assert(ctx.features & Feature.Symbol, 'Unsupported type "symbol"');
460
+ assert(current in INV_SYMBOL_REF, 'seroval only supports well-known symbols');
461
+ return createWKSymbolNode(current);
446
462
  default:
447
463
  throw new Error('Unsupported type');
448
464
  }
@@ -455,5 +471,5 @@ export default async function parseAsync(
455
471
  const result = await parse(ctx, current);
456
472
  const isObject = result.t === SerovalNodeType.Object
457
473
  || result.t === SerovalNodeType.Iterable;
458
- return [result, createRef(ctx, current, false), isObject] as const;
474
+ return [result, getRootID(ctx, current), isObject] as const;
459
475
  }
@@ -0,0 +1,288 @@
1
+ /* eslint-disable @typescript-eslint/no-use-before-define */
2
+ import {
3
+ SerializationContext,
4
+ } from '../context';
5
+ import { invQuote } from '../quote';
6
+ import { AsyncServerValue } from '../types';
7
+ import { getErrorConstructor, getTypedArrayConstructor } from './shared';
8
+ import { SYMBOL_REF } from './symbols';
9
+ import {
10
+ SerovalAggregateErrorNode,
11
+ SerovalArrayNode,
12
+ SerovalBigIntTypedArrayNode,
13
+ SerovalErrorNode,
14
+ SerovalIterableNode,
15
+ SerovalMapNode,
16
+ SerovalNode,
17
+ SerovalNodeType,
18
+ SerovalNullConstructorNode,
19
+ SerovalObjectNode,
20
+ SerovalObjectRecordNode,
21
+ SerovalPromiseNode,
22
+ SerovalSetNode,
23
+ SerovalTypedArrayNode,
24
+ } from './types';
25
+
26
+ function assignRef<T extends AsyncServerValue>(
27
+ ctx: SerializationContext,
28
+ index: number,
29
+ value: T,
30
+ ) {
31
+ ctx.valueMap.set(index, value);
32
+ return value;
33
+ }
34
+
35
+ function deserializeNodeList(
36
+ ctx: SerializationContext,
37
+ node: SerovalArrayNode | SerovalIterableNode | SerovalAggregateErrorNode,
38
+ result: AsyncServerValue[],
39
+ ) {
40
+ let item: SerovalNode;
41
+ for (let i = 0, len = node.a.length; i < len; i++) {
42
+ item = node.a[i];
43
+ if (item) {
44
+ result[i] = deserializeTree(ctx, item);
45
+ }
46
+ }
47
+ return result;
48
+ }
49
+
50
+ function deserializeArray(
51
+ ctx: SerializationContext,
52
+ node: SerovalArrayNode,
53
+ ) {
54
+ const result: AsyncServerValue[] = assignRef(
55
+ ctx,
56
+ node.i,
57
+ new Array<AsyncServerValue>(node.l),
58
+ );
59
+ ctx.stack.push(node.i);
60
+ deserializeNodeList(ctx, node, result);
61
+ ctx.stack.pop();
62
+ return result;
63
+ }
64
+
65
+ function deserializeProperties(
66
+ ctx: SerializationContext,
67
+ node: SerovalObjectRecordNode,
68
+ result: Record<string, AsyncServerValue>,
69
+ ) {
70
+ if (node.s === 0) {
71
+ return {};
72
+ }
73
+ for (let i = 0; i < node.s; i++) {
74
+ result[node.k[i]] = deserializeTree(ctx, node.v[i]);
75
+ }
76
+ return result;
77
+ }
78
+
79
+ function deserializeNullConstructor(
80
+ ctx: SerializationContext,
81
+ node: SerovalNullConstructorNode,
82
+ ) {
83
+ const result = assignRef(ctx, node.i, Object.create(null) as Record<string, AsyncServerValue>);
84
+ ctx.stack.push(node.i);
85
+ deserializeProperties(ctx, node.d, result);
86
+ ctx.stack.pop();
87
+ return result;
88
+ }
89
+
90
+ function deserializeObject(
91
+ ctx: SerializationContext,
92
+ node: SerovalObjectNode,
93
+ ) {
94
+ const result = assignRef(ctx, node.i, {} as Record<string, AsyncServerValue>);
95
+ ctx.stack.push(node.i);
96
+ deserializeProperties(ctx, node.d, result);
97
+ ctx.stack.pop();
98
+ return result;
99
+ }
100
+
101
+ function deserializeSet(
102
+ ctx: SerializationContext,
103
+ node: SerovalSetNode,
104
+ ) {
105
+ const result = assignRef(ctx, node.i, new Set<AsyncServerValue>());
106
+ ctx.stack.push(node.i);
107
+ for (let i = 0, len = node.a.length; i < len; i++) {
108
+ result.add(deserializeTree(ctx, node.a[i]));
109
+ }
110
+ ctx.stack.pop();
111
+ return result;
112
+ }
113
+
114
+ function deserializeMap(
115
+ ctx: SerializationContext,
116
+ node: SerovalMapNode,
117
+ ) {
118
+ const result = assignRef(
119
+ ctx,
120
+ node.i,
121
+ new Map<AsyncServerValue, AsyncServerValue>(),
122
+ );
123
+ ctx.stack.push(node.i);
124
+ for (let i = 0; i < node.d.s; i++) {
125
+ result.set(
126
+ deserializeTree(ctx, node.d.k[i]),
127
+ deserializeTree(ctx, node.d.v[i]),
128
+ );
129
+ }
130
+ ctx.stack.pop();
131
+ return result;
132
+ }
133
+
134
+ type AssignableValue = AggregateError | Error | Iterable<AsyncServerValue>
135
+ type AssignableNode = SerovalAggregateErrorNode | SerovalErrorNode | SerovalIterableNode;
136
+
137
+ function deserializeDictionary<T extends AssignableValue>(
138
+ ctx: SerializationContext,
139
+ node: AssignableNode,
140
+ result: T,
141
+ ) {
142
+ if (node.d) {
143
+ ctx.stack.push(node.i);
144
+ const fields = deserializeProperties(ctx, node.d, {});
145
+ ctx.stack.pop();
146
+ Object.assign(result, fields);
147
+ }
148
+ return result;
149
+ }
150
+
151
+ function deserializeAggregateError(
152
+ ctx: SerializationContext,
153
+ node: SerovalAggregateErrorNode,
154
+ ) {
155
+ // Serialize the required arguments
156
+ const result = assignRef(ctx, node.i, new AggregateError([], node.m));
157
+ ctx.stack.push(node.i);
158
+ result.errors = deserializeNodeList(ctx, node, new Array<AsyncServerValue>(node.l));
159
+ ctx.stack.pop();
160
+ // `AggregateError` might've been extended
161
+ // either through class or custom properties
162
+ // Make sure to assign extra properties
163
+ return deserializeDictionary(ctx, node, result);
164
+ }
165
+
166
+ function deserializeError(
167
+ ctx: SerializationContext,
168
+ node: SerovalErrorNode,
169
+ ) {
170
+ const ErrorConstructor = getErrorConstructor(node.c);
171
+ const result = assignRef(ctx, node.i, new ErrorConstructor(node.m));
172
+ return deserializeDictionary(ctx, node, result);
173
+ }
174
+
175
+ interface Deferred {
176
+ resolve(value: AsyncServerValue): void;
177
+ promise: Promise<AsyncServerValue>;
178
+ }
179
+
180
+ function createDeferred(): Deferred {
181
+ let resolver: Deferred['resolve'];
182
+ return {
183
+ resolve(v) {
184
+ resolver(v);
185
+ },
186
+ promise: new Promise((res) => {
187
+ resolver = res as Deferred['resolve'];
188
+ }),
189
+ };
190
+ }
191
+
192
+ function deserializePromise(
193
+ ctx: SerializationContext,
194
+ node: SerovalPromiseNode,
195
+ ) {
196
+ const deferred = createDeferred();
197
+ const result = assignRef(ctx, node.i, deferred.promise);
198
+ deferred.resolve(deserializeTree(ctx, node.f));
199
+ return result;
200
+ }
201
+
202
+ function deserializeTypedArray(
203
+ ctx: SerializationContext,
204
+ node: SerovalTypedArrayNode | SerovalBigIntTypedArrayNode,
205
+ ) {
206
+ const TypedArray = getTypedArrayConstructor(node.c);
207
+ const dummy = new TypedArray();
208
+ const result = assignRef(ctx, node.i, new TypedArray(
209
+ dummy.buffer,
210
+ node.l,
211
+ ));
212
+ for (let i = 0, len = node.s.length; i < len; i++) {
213
+ result[i] = node.t === SerovalNodeType.BigIntTypedArray
214
+ ? BigInt(node.s[i])
215
+ : Number(node.s[i]);
216
+ }
217
+ return result;
218
+ }
219
+
220
+ function deserializeIterable(
221
+ ctx: SerializationContext,
222
+ node: SerovalIterableNode,
223
+ ) {
224
+ const values: AsyncServerValue[] = [];
225
+ deserializeNodeList(ctx, node, values);
226
+ const result = assignRef(ctx, node.i, {
227
+ [Symbol.iterator]: () => values.values(),
228
+ });
229
+ return deserializeDictionary(ctx, node, result);
230
+ }
231
+
232
+ export default function deserializeTree(
233
+ ctx: SerializationContext,
234
+ node: SerovalNode,
235
+ ): AsyncServerValue {
236
+ switch (node.t) {
237
+ case SerovalNodeType.Number:
238
+ case SerovalNodeType.Boolean:
239
+ return node.s;
240
+ case SerovalNodeType.String:
241
+ return invQuote(node.s);
242
+ case SerovalNodeType.Undefined:
243
+ return undefined;
244
+ case SerovalNodeType.Null:
245
+ return null;
246
+ case SerovalNodeType.NegativeZero:
247
+ return -0;
248
+ case SerovalNodeType.Infinity:
249
+ return Infinity;
250
+ case SerovalNodeType.NegativeInfinity:
251
+ return -Infinity;
252
+ case SerovalNodeType.NaN:
253
+ return NaN;
254
+ case SerovalNodeType.BigInt:
255
+ return BigInt(node.s);
256
+ case SerovalNodeType.Reference:
257
+ return ctx.valueMap.get(node.i);
258
+ case SerovalNodeType.Array:
259
+ return deserializeArray(ctx, node);
260
+ case SerovalNodeType.Object:
261
+ return deserializeObject(ctx, node);
262
+ case SerovalNodeType.NullConstructor:
263
+ return deserializeNullConstructor(ctx, node);
264
+ case SerovalNodeType.Date:
265
+ return assignRef(ctx, node.i, new Date(node.s));
266
+ case SerovalNodeType.RegExp:
267
+ return assignRef(ctx, node.i, new RegExp(node.c, node.m));
268
+ case SerovalNodeType.Set:
269
+ return deserializeSet(ctx, node);
270
+ case SerovalNodeType.Map:
271
+ return deserializeMap(ctx, node);
272
+ case SerovalNodeType.BigIntTypedArray:
273
+ case SerovalNodeType.TypedArray:
274
+ return deserializeTypedArray(ctx, node);
275
+ case SerovalNodeType.AggregateError:
276
+ return deserializeAggregateError(ctx, node);
277
+ case SerovalNodeType.Error:
278
+ return deserializeError(ctx, node);
279
+ case SerovalNodeType.Iterable:
280
+ return deserializeIterable(ctx, node);
281
+ case SerovalNodeType.Promise:
282
+ return deserializePromise(ctx, node);
283
+ case SerovalNodeType.WKSymbol:
284
+ return SYMBOL_REF[node.s];
285
+ default:
286
+ throw new Error('Unsupported type');
287
+ }
288
+ }
@@ -0,0 +1,14 @@
1
+ export default function createIterator<T>(
2
+ values: Array<T>,
3
+ ): Iterator<T> {
4
+ let index = 0;
5
+ const len = values.length;
6
+ return {
7
+ next() {
8
+ return {
9
+ value: values[index++],
10
+ done: index >= len,
11
+ };
12
+ },
13
+ };
14
+ }