seroval 0.5.1 → 0.7.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/src/tree/sync.ts CHANGED
@@ -1,14 +1,15 @@
1
1
  /* eslint-disable @typescript-eslint/no-use-before-define */
2
2
  import assert from '../assert';
3
3
  import { Feature } from '../compat';
4
- import { createRef, getRootID, ParserContext } from '../context';
5
- import { BigIntTypedArrayValue, ServerValue, TypedArrayValue } from '../types';
4
+ import { createIndexedValue, getRootID, ParserContext } from '../context';
5
+ import { serializeString } from '../string';
6
+ import { BigIntTypedArrayValue, TypedArrayValue } from '../types';
6
7
  import {
7
8
  createBigIntNode,
8
9
  createBigIntTypedArrayNode,
9
10
  createDateNode,
10
11
  createNumberNode,
11
- createReferenceNode,
12
+ createIndexedValueNode,
12
13
  createRegExpNode,
13
14
  createStringNode,
14
15
  createTypedArrayNode,
@@ -21,18 +22,26 @@ import {
21
22
  NULL_NODE,
22
23
  TRUE_NODE,
23
24
  UNDEFINED_NODE,
25
+ createReferenceNode,
26
+ createArrayBufferNode,
27
+ createDataViewNode,
24
28
  } from './primitives';
29
+ import {
30
+ hasReferenceID,
31
+ } from './reference';
25
32
  import {
26
33
  getErrorConstructorName,
27
34
  getErrorOptions,
28
35
  getIterableOptions,
29
36
  isIterable,
30
37
  } from './shared';
31
- import { INV_SYMBOL_REF } from './symbols';
38
+ import { WellKnownSymbols } from './symbols';
32
39
  import {
33
40
  SerovalAggregateErrorNode,
34
41
  SerovalArrayNode,
35
42
  SerovalErrorNode,
43
+ SerovalFormDataNode,
44
+ SerovalHeadersNode,
36
45
  SerovalIterableNode,
37
46
  SerovalMapNode,
38
47
  SerovalNode,
@@ -42,14 +51,15 @@ import {
42
51
  SerovalObjectRecordNode,
43
52
  SerovalSetNode,
44
53
  } from './types';
54
+ import { createURLNode, createURLSearchParamsNode } from './web-api';
45
55
 
46
56
  type ObjectLikeNode = SerovalObjectNode | SerovalNullConstructorNode | SerovalIterableNode;
47
57
 
48
- function generateNodeList(ctx: ParserContext, current: ServerValue[]) {
58
+ function generateNodeList(ctx: ParserContext, current: unknown[]) {
49
59
  const size = current.length;
50
60
  const nodes = new Array<SerovalNode>(size);
51
- const deferred = new Array<ServerValue>(size);
52
- let item: ServerValue;
61
+ const deferred = new Array<unknown>(size);
62
+ let item: unknown;
53
63
  for (let i = 0; i < size; i++) {
54
64
  if (i in current) {
55
65
  item = current[i];
@@ -71,7 +81,7 @@ function generateNodeList(ctx: ParserContext, current: ServerValue[]) {
71
81
  function generateArrayNode(
72
82
  ctx: ParserContext,
73
83
  id: number,
74
- current: ServerValue[],
84
+ current: unknown[],
75
85
  ): SerovalArrayNode {
76
86
  return {
77
87
  t: SerovalNodeType.Array,
@@ -83,20 +93,21 @@ function generateArrayNode(
83
93
  d: undefined,
84
94
  a: generateNodeList(ctx, current),
85
95
  f: undefined,
96
+ b: undefined,
86
97
  };
87
98
  }
88
99
 
89
100
  function generateMapNode(
90
101
  ctx: ParserContext,
91
102
  id: number,
92
- current: Map<ServerValue, ServerValue>,
103
+ current: Map<unknown, unknown>,
93
104
  ): SerovalMapNode {
94
105
  assert(ctx.features & Feature.Map, 'Unsupported type "Map"');
95
106
  const len = current.size;
96
107
  const keyNodes = new Array<SerovalNode>(len);
97
108
  const valueNodes = new Array<SerovalNode>(len);
98
- const deferredKey = new Array<ServerValue>(len);
99
- const deferredValue = new Array<ServerValue>(len);
109
+ const deferredKey = new Array<unknown>(len);
110
+ const deferredValue = new Array<unknown>(len);
100
111
  let deferredSize = 0;
101
112
  let nodeSize = 0;
102
113
  for (const [key, value] of current.entries()) {
@@ -125,18 +136,19 @@ function generateMapNode(
125
136
  d: { k: keyNodes, v: valueNodes, s: len },
126
137
  a: undefined,
127
138
  f: undefined,
139
+ b: undefined,
128
140
  };
129
141
  }
130
142
 
131
143
  function generateSetNode(
132
144
  ctx: ParserContext,
133
145
  id: number,
134
- current: Set<ServerValue>,
146
+ current: Set<unknown>,
135
147
  ): SerovalSetNode {
136
148
  assert(ctx.features & Feature.Set, 'Unsupported type "Set"');
137
149
  const len = current.size;
138
150
  const nodes = new Array<SerovalNode>(len);
139
- const deferred = new Array<ServerValue>(len);
151
+ const deferred = new Array<unknown>(len);
140
152
  let deferredSize = 0;
141
153
  let nodeSize = 0;
142
154
  for (const item of current.keys()) {
@@ -161,30 +173,33 @@ function generateSetNode(
161
173
  d: undefined,
162
174
  a: nodes,
163
175
  f: undefined,
176
+ b: undefined,
164
177
  };
165
178
  }
166
179
 
167
180
  function generateProperties(
168
181
  ctx: ParserContext,
169
- properties: Record<string, ServerValue>,
182
+ properties: Record<string, unknown>,
170
183
  ): SerovalObjectRecordNode {
171
184
  const keys = Object.keys(properties);
172
185
  const size = keys.length;
173
186
  const keyNodes = new Array<string>(size);
174
187
  const valueNodes = new Array<SerovalNode>(size);
175
188
  const deferredKeys = new Array<string>(size);
176
- const deferredValues = new Array<ServerValue>(size);
189
+ const deferredValues = new Array<unknown>(size);
177
190
  let deferredSize = 0;
178
191
  let nodesSize = 0;
179
- let item: ServerValue;
192
+ let item: unknown;
193
+ let escaped: string;
180
194
  for (const key of keys) {
181
195
  item = properties[key];
196
+ escaped = serializeString(key);
182
197
  if (isIterable(item)) {
183
- deferredKeys[deferredSize] = key;
198
+ deferredKeys[deferredSize] = escaped;
184
199
  deferredValues[deferredSize] = item;
185
200
  deferredSize++;
186
201
  } else {
187
- keyNodes[nodesSize] = key;
202
+ keyNodes[nodesSize] = escaped;
188
203
  valueNodes[nodesSize] = parse(ctx, item);
189
204
  nodesSize++;
190
205
  }
@@ -203,7 +218,7 @@ function generateProperties(
203
218
  function generateIterableNode(
204
219
  ctx: ParserContext,
205
220
  id: number,
206
- current: Iterable<ServerValue>,
221
+ current: Iterable<unknown>,
207
222
  ): SerovalIterableNode {
208
223
  assert(ctx.features & Feature.Symbol, 'Unsupported type "Iterable"');
209
224
  const options = getIterableOptions(current);
@@ -217,17 +232,18 @@ function generateIterableNode(
217
232
  m: undefined,
218
233
  // Parse options first before the items
219
234
  d: options
220
- ? generateProperties(ctx, options as Record<string, ServerValue>)
235
+ ? generateProperties(ctx, options)
221
236
  : undefined,
222
237
  a: generateNodeList(ctx, array),
223
238
  f: undefined,
239
+ b: undefined,
224
240
  };
225
241
  }
226
242
 
227
243
  function generateObjectNode(
228
244
  ctx: ParserContext,
229
245
  id: number,
230
- current: Record<string, ServerValue> | Iterable<ServerValue>,
246
+ current: Record<string, unknown> | Iterable<unknown>,
231
247
  empty: boolean,
232
248
  ): ObjectLikeNode {
233
249
  if (Symbol.iterator in current) {
@@ -243,6 +259,7 @@ function generateObjectNode(
243
259
  d: generateProperties(ctx, current),
244
260
  a: undefined,
245
261
  f: undefined,
262
+ b: undefined,
246
263
  };
247
264
  }
248
265
 
@@ -259,12 +276,13 @@ function generateAggregateErrorNode(
259
276
  t: SerovalNodeType.AggregateError,
260
277
  i: id,
261
278
  s: undefined,
262
- l: current.errors.length,
279
+ l: undefined,
263
280
  c: undefined,
264
- m: current.message,
281
+ m: serializeString(current.message),
265
282
  d: optionsNode,
266
- a: generateNodeList(ctx, current.errors as ServerValue[]),
283
+ a: undefined,
267
284
  f: undefined,
285
+ b: undefined,
268
286
  };
269
287
  }
270
288
 
@@ -283,16 +301,65 @@ function generateErrorNode(
283
301
  s: undefined,
284
302
  l: undefined,
285
303
  c: getErrorConstructorName(current),
286
- m: current.message,
304
+ m: serializeString(current.message),
287
305
  d: optionsNode,
288
306
  a: undefined,
289
307
  f: undefined,
308
+ b: undefined,
309
+ };
310
+ }
311
+
312
+ function generateHeadersNode(
313
+ ctx: ParserContext,
314
+ id: number,
315
+ current: Headers,
316
+ ): SerovalHeadersNode {
317
+ assert(ctx.features & Feature.WebAPI, 'Unsupported type "Headers"');
318
+ const items: Record<string, string> = {};
319
+ current.forEach((value, key) => {
320
+ items[key] = value;
321
+ });
322
+ return {
323
+ t: SerovalNodeType.Headers,
324
+ i: id,
325
+ s: undefined,
326
+ l: undefined,
327
+ c: undefined,
328
+ m: undefined,
329
+ d: generateProperties(ctx, items),
330
+ a: undefined,
331
+ f: undefined,
332
+ b: undefined,
333
+ };
334
+ }
335
+
336
+ function generateFormDataNode(
337
+ ctx: ParserContext,
338
+ id: number,
339
+ current: FormData,
340
+ ): SerovalFormDataNode {
341
+ assert(ctx.features & Feature.WebAPI, 'Unsupported type "FormData"');
342
+ const items: Record<string, FormDataEntryValue> = {};
343
+ current.forEach((value, key) => {
344
+ items[key] = value;
345
+ });
346
+ return {
347
+ t: SerovalNodeType.FormData,
348
+ i: id,
349
+ s: undefined,
350
+ l: undefined,
351
+ c: undefined,
352
+ m: undefined,
353
+ d: generateProperties(ctx, items),
354
+ a: undefined,
355
+ f: undefined,
356
+ b: undefined,
290
357
  };
291
358
  }
292
359
 
293
- function parse(
360
+ function parse<T>(
294
361
  ctx: ParserContext,
295
- current: ServerValue,
362
+ current: T,
296
363
  ): SerovalNode {
297
364
  switch (typeof current) {
298
365
  case 'boolean':
@@ -305,16 +372,16 @@ function parse(
305
372
  switch (current) {
306
373
  case Infinity: return INFINITY_NODE;
307
374
  case -Infinity: return NEG_INFINITY_NODE;
308
- default: break;
309
- }
310
- // eslint-disable-next-line no-self-compare
311
- if (current !== current) {
312
- return NAN_NODE;
313
- }
314
- if (Object.is(current, -0)) {
315
- return NEG_ZERO_NODE;
375
+ default:
376
+ // eslint-disable-next-line no-self-compare
377
+ if (current !== current) {
378
+ return NAN_NODE;
379
+ }
380
+ if (Object.is(current, -0)) {
381
+ return NEG_ZERO_NODE;
382
+ }
383
+ return createNumberNode(current);
316
384
  }
317
- return createNumberNode(current);
318
385
  case 'bigint':
319
386
  return createBigIntNode(ctx, current);
320
387
  case 'object': {
@@ -323,18 +390,23 @@ function parse(
323
390
  }
324
391
  // Non-primitive values needs a reference ID
325
392
  // mostly because the values themselves are stateful
326
- const id = createRef(ctx, current);
393
+ const id = createIndexedValue(ctx, current);
327
394
  if (ctx.markedRefs.has(id)) {
328
- return createReferenceNode(id);
395
+ return createIndexedValueNode(id);
396
+ }
397
+ if (hasReferenceID(current)) {
398
+ return createReferenceNode(id, current);
329
399
  }
330
400
  if (Array.isArray(current)) {
331
401
  return generateArrayNode(ctx, id, current);
332
402
  }
333
403
  switch (current.constructor) {
334
404
  case Date:
335
- return createDateNode(id, current as Date);
405
+ return createDateNode(id, current as unknown as Date);
336
406
  case RegExp:
337
- return createRegExpNode(id, current as RegExp);
407
+ return createRegExpNode(id, current as unknown as RegExp);
408
+ case ArrayBuffer:
409
+ return createArrayBufferNode(id, current as unknown as ArrayBuffer);
338
410
  case Int8Array:
339
411
  case Int16Array:
340
412
  case Int32Array:
@@ -344,23 +416,35 @@ function parse(
344
416
  case Uint8ClampedArray:
345
417
  case Float32Array:
346
418
  case Float64Array:
347
- return createTypedArrayNode(ctx, id, current as TypedArrayValue);
419
+ return createTypedArrayNode(ctx, id, current as unknown as TypedArrayValue);
348
420
  case BigInt64Array:
349
421
  case BigUint64Array:
350
- return createBigIntTypedArrayNode(ctx, id, current as BigIntTypedArrayValue);
422
+ return createBigIntTypedArrayNode(ctx, id, current as unknown as BigIntTypedArrayValue);
423
+ case DataView:
424
+ return createDataViewNode(ctx, id, current as unknown as DataView);
351
425
  case Map:
352
- return generateMapNode(ctx, id, current as Map<ServerValue, ServerValue>);
426
+ return generateMapNode(ctx, id, current as unknown as Map<unknown, unknown>);
353
427
  case Set:
354
- return generateSetNode(ctx, id, current as Set<ServerValue>);
428
+ return generateSetNode(ctx, id, current as unknown as Set<unknown>);
355
429
  case Object:
356
- return generateObjectNode(ctx, id, current as Record<string, ServerValue>, false);
430
+ return generateObjectNode(
431
+ ctx,
432
+ id,
433
+ current as unknown as Record<string, unknown>,
434
+ false,
435
+ );
357
436
  case undefined:
358
- return generateObjectNode(ctx, id, current as Record<string, ServerValue>, true);
437
+ return generateObjectNode(
438
+ ctx,
439
+ id,
440
+ current as unknown as Record<string, unknown>,
441
+ true,
442
+ );
359
443
  case AggregateError:
360
444
  if (ctx.features & Feature.AggregateError) {
361
- return generateAggregateErrorNode(ctx, id, current as AggregateError);
445
+ return generateAggregateErrorNode(ctx, id, current as unknown as AggregateError);
362
446
  }
363
- return generateErrorNode(ctx, id, current as AggregateError);
447
+ return generateErrorNode(ctx, id, current as unknown as AggregateError);
364
448
  case Error:
365
449
  case EvalError:
366
450
  case RangeError:
@@ -368,7 +452,15 @@ function parse(
368
452
  case SyntaxError:
369
453
  case TypeError:
370
454
  case URIError:
371
- return generateErrorNode(ctx, id, current as Error);
455
+ return generateErrorNode(ctx, id, current as unknown as Error);
456
+ case URL:
457
+ return createURLNode(ctx, id, current as unknown as URL);
458
+ case URLSearchParams:
459
+ return createURLSearchParamsNode(ctx, id, current as unknown as URLSearchParams);
460
+ case Headers:
461
+ return generateHeadersNode(ctx, id, current as unknown as Headers);
462
+ case FormData:
463
+ return generateFormDataNode(ctx, id, current as unknown as FormData);
372
464
  default:
373
465
  break;
374
466
  }
@@ -383,22 +475,35 @@ function parse(
383
475
  }
384
476
  // Generator functions don't have a global constructor
385
477
  if (Symbol.iterator in current) {
386
- return generateIterableNode(ctx, id, current);
478
+ return generateIterableNode(ctx, id, current as Iterable<unknown>);
387
479
  }
388
480
  throw new Error('Unsupported type');
389
481
  }
390
482
  case 'symbol':
391
- assert(ctx.features & Feature.Symbol, 'Unsupported type "symbol"');
392
- assert(current in INV_SYMBOL_REF, 'seroval only supports well-known symbols');
393
- return createWKSymbolNode(current);
483
+ if (hasReferenceID(current)) {
484
+ const id = createIndexedValue(ctx, current);
485
+ if (ctx.markedRefs.has(id)) {
486
+ return createIndexedValueNode(id);
487
+ }
488
+ return createReferenceNode(id, current);
489
+ }
490
+ return createWKSymbolNode(ctx, current as WellKnownSymbols);
491
+ case 'function': {
492
+ assert(hasReferenceID(current), 'Cannot serialize function without reference ID.');
493
+ const id = createIndexedValue(ctx, current);
494
+ if (ctx.markedRefs.has(id)) {
495
+ return createIndexedValueNode(id);
496
+ }
497
+ return createReferenceNode(id, current);
498
+ }
394
499
  default:
395
500
  throw new Error('Unsupported type');
396
501
  }
397
502
  }
398
503
 
399
- export default function parseSync(
504
+ export default function parseSync<T>(
400
505
  ctx: ParserContext,
401
- current: ServerValue,
506
+ current: T,
402
507
  ) {
403
508
  const result = parse(ctx, current);
404
509
  const isObject = result.t === SerovalNodeType.Object