seroval 0.7.0 → 0.9.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.
Files changed (42) hide show
  1. package/dist/cjs/development/index.cjs +1247 -1069
  2. package/dist/cjs/development/index.cjs.map +4 -4
  3. package/dist/cjs/production/index.cjs +3 -3
  4. package/dist/esm/development/index.mjs +1247 -1069
  5. package/dist/esm/development/index.mjs.map +4 -4
  6. package/dist/esm/production/index.mjs +3 -3
  7. package/dist/types/assert.d.ts +1 -1
  8. package/dist/types/context.d.ts +8 -2
  9. package/dist/types/index.d.ts +3 -4
  10. package/dist/types/string.d.ts +1 -1
  11. package/dist/types/tree/UnsupportedTypeError.d.ts +3 -0
  12. package/dist/types/tree/async.d.ts +3 -3
  13. package/dist/types/tree/constants.d.ts +11 -0
  14. package/dist/types/tree/deserialize.d.ts +2 -2
  15. package/dist/types/tree/primitives.d.ts +9 -15
  16. package/dist/types/tree/serialize.d.ts +2 -2
  17. package/dist/types/tree/shared.d.ts +12 -8
  18. package/dist/types/tree/sync.d.ts +3 -3
  19. package/dist/types/tree/types.d.ts +74 -71
  20. package/dist/types/tree/web-api.d.ts +2 -2
  21. package/dist/types/types.d.ts +4 -4
  22. package/package.json +11 -9
  23. package/src/assert.ts +2 -2
  24. package/src/compat.ts +0 -1
  25. package/src/context.ts +20 -7
  26. package/src/get-identifier.ts +1 -1
  27. package/src/index.ts +35 -26
  28. package/src/string.ts +2 -2
  29. package/src/tree/UnsupportedTypeError.ts +8 -0
  30. package/src/tree/async.ts +256 -233
  31. package/src/tree/constants.ts +73 -0
  32. package/src/tree/deserialize.ts +112 -93
  33. package/src/tree/primitives.ts +93 -134
  34. package/src/tree/reference.ts +3 -3
  35. package/src/tree/serialize.ts +307 -201
  36. package/src/tree/shared.ts +45 -22
  37. package/src/tree/symbols.ts +13 -13
  38. package/src/tree/sync.ts +236 -198
  39. package/src/tree/types.ts +87 -94
  40. package/src/tree/web-api.ts +14 -7
  41. package/src/types.ts +7 -3
  42. package/src/tree/iterator.ts +0 -14
@@ -1,26 +1,26 @@
1
1
  /* eslint-disable @typescript-eslint/no-use-before-define */
2
2
  import { Feature } from '../compat';
3
- import {
3
+ import type {
4
4
  SerializationContext,
5
5
  Assignment,
6
+ } from '../context';
7
+ import {
6
8
  getRefParam,
7
9
  markRef,
8
10
  } from '../context';
11
+ import { serializeConstant } from './constants';
9
12
  import { GLOBAL_KEY } from './reference';
10
13
  import { isValidIdentifier } from './shared';
11
14
  import { SYMBOL_STRING } from './symbols';
12
- import {
15
+ import type {
13
16
  SerovalAggregateErrorNode,
14
17
  SerovalArrayNode,
15
18
  SerovalBigIntTypedArrayNode,
16
19
  SerovalErrorNode,
17
- SerovalIterableNode,
18
20
  SerovalMapNode,
19
21
  SerovalNode,
20
- SerovalNodeType,
21
22
  SerovalNullConstructorNode,
22
23
  SerovalObjectNode,
23
- SerovalObjectRecordNode,
24
24
  SerovalPromiseNode,
25
25
  SerovalIndexedValueNode,
26
26
  SerovalSetNode,
@@ -36,6 +36,16 @@ import {
36
36
  SerovalURLSearchParamsNode,
37
37
  SerovalReferenceNode,
38
38
  SerovalFormDataNode,
39
+ SerovalObjectRecordNode,
40
+ SerovalObjectRecordKey,
41
+ SerovalBoxedNode,
42
+ SerovalWKSymbolNode,
43
+ } from './types';
44
+ import {
45
+ SerovalObjectFlags,
46
+
47
+ SerovalNodeType,
48
+ SerovalObjectRecordSpecialKey,
39
49
  } from './types';
40
50
 
41
51
  function getAssignmentExpression(assignment: Assignment): string {
@@ -53,8 +63,15 @@ function getAssignmentExpression(assignment: Assignment): string {
53
63
  }
54
64
  }
55
65
 
56
- function mergeAssignments(assignments: Assignment[]) {
57
- const newAssignments = [];
66
+ const OBJECT_FLAG_CONSTRUCTOR: Record<SerovalObjectFlags, string | undefined> = {
67
+ [SerovalObjectFlags.Frozen]: 'Object.freeze',
68
+ [SerovalObjectFlags.Sealed]: 'Object.seal',
69
+ [SerovalObjectFlags.NonExtensible]: 'Object.preventExtensions',
70
+ [SerovalObjectFlags.None]: undefined,
71
+ };
72
+
73
+ function mergeAssignments(assignments: Assignment[]): Assignment[] {
74
+ const newAssignments: Assignment[] = [];
58
75
  let current = assignments[0];
59
76
  let prev = current;
60
77
  let item: Assignment;
@@ -128,7 +145,7 @@ function mergeAssignments(assignments: Assignment[]) {
128
145
  return newAssignments;
129
146
  }
130
147
 
131
- function resolveAssignments(assignments: Assignment[]) {
148
+ function resolveAssignments(assignments: Assignment[]): string | undefined {
132
149
  if (assignments.length) {
133
150
  let result = '';
134
151
  const merged = mergeAssignments(assignments);
@@ -140,8 +157,35 @@ function resolveAssignments(assignments: Assignment[]) {
140
157
  return undefined;
141
158
  }
142
159
 
143
- export function resolvePatches(ctx: SerializationContext) {
144
- return resolveAssignments(ctx.assignments);
160
+ function pushObjectFlag(ctx: SerializationContext, flag: SerovalObjectFlags, id: number): void {
161
+ if (flag !== SerovalObjectFlags.None) {
162
+ markRef(ctx, id);
163
+ ctx.flags.push({
164
+ type: flag,
165
+ value: getRefParam(ctx, id),
166
+ });
167
+ }
168
+ }
169
+
170
+ function resolveFlags(ctx: SerializationContext): string | undefined {
171
+ let result = '';
172
+ for (let i = 0, len = ctx.flags.length; i < len; i++) {
173
+ const flag = ctx.flags[i];
174
+ result += OBJECT_FLAG_CONSTRUCTOR[flag.type] + '(' + flag.value + '),';
175
+ }
176
+ return result;
177
+ }
178
+
179
+ export function resolvePatches(ctx: SerializationContext): string | undefined {
180
+ const assignments = resolveAssignments(ctx.assignments);
181
+ const flags = resolveFlags(ctx);
182
+ if (assignments) {
183
+ if (flags) {
184
+ return assignments + flags;
185
+ }
186
+ return assignments;
187
+ }
188
+ return flags;
145
189
  }
146
190
 
147
191
  /**
@@ -154,7 +198,7 @@ function createAssignment(
154
198
  ctx: SerializationContext,
155
199
  source: string,
156
200
  value: string,
157
- ) {
201
+ ): void {
158
202
  ctx.assignments.push({
159
203
  t: 'index',
160
204
  s: source,
@@ -167,7 +211,7 @@ function createAddAssignment(
167
211
  ctx: SerializationContext,
168
212
  ref: number,
169
213
  value: string,
170
- ) {
214
+ ): void {
171
215
  ctx.assignments.push({
172
216
  t: 'add',
173
217
  s: getRefParam(ctx, ref),
@@ -181,7 +225,7 @@ function createSetAssignment(
181
225
  ref: number,
182
226
  key: string,
183
227
  value: string,
184
- ) {
228
+ ): void {
185
229
  ctx.assignments.push({
186
230
  t: 'set',
187
231
  s: getRefParam(ctx, ref),
@@ -195,7 +239,7 @@ function createAppendAssignment(
195
239
  ref: number,
196
240
  key: string,
197
241
  value: string,
198
- ) {
242
+ ): void {
199
243
  ctx.assignments.push({
200
244
  t: 'append',
201
245
  s: getRefParam(ctx, ref),
@@ -209,7 +253,7 @@ function createArrayAssign(
209
253
  ref: number,
210
254
  index: number | string,
211
255
  value: string,
212
- ) {
256
+ ): void {
213
257
  createAssignment(ctx, getRefParam(ctx, ref) + '[' + index + ']', value);
214
258
  }
215
259
 
@@ -218,7 +262,7 @@ function createObjectAssign(
218
262
  ref: number,
219
263
  key: string,
220
264
  value: string,
221
- ) {
265
+ ): void {
222
266
  markRef(ctx, ref);
223
267
  createAssignment(ctx, getRefParam(ctx, ref) + '.' + key, value);
224
268
  }
@@ -227,7 +271,7 @@ function assignIndexedValue(
227
271
  ctx: SerializationContext,
228
272
  index: number,
229
273
  value: string,
230
- ) {
274
+ ): string {
231
275
  if (ctx.markedRefs.has(index)) {
232
276
  return getRefParam(ctx, index) + '=' + value;
233
277
  }
@@ -241,33 +285,31 @@ function isIndexedValueInStack(
241
285
  return node.t === SerovalNodeType.IndexedValue && ctx.stack.includes(node.i);
242
286
  }
243
287
 
244
- type SerovalNodeListNode =
245
- | SerovalArrayNode
246
- | SerovalIterableNode;
247
-
248
- function serializeNodeList(
288
+ function serializeArray(
249
289
  ctx: SerializationContext,
250
- node: SerovalNodeListNode,
251
- ) {
290
+ node: SerovalArrayNode,
291
+ ): string {
292
+ const id = node.i;
293
+ ctx.stack.push(id);
252
294
  // This is different than Map and Set
253
295
  // because we also need to serialize
254
296
  // the holes of the Array
255
- const size = node.l;
256
297
  let values = '';
257
- let item: SerovalNode;
298
+ let item: SerovalNode | undefined;
258
299
  let isHoley = false;
259
- for (let i = 0; i < size; i++) {
300
+ const list = node.a;
301
+ for (let i = 0, len = node.l; i < len; i++) {
260
302
  if (i !== 0) {
261
303
  // Add an empty item
262
304
  values += ',';
263
305
  }
264
- item = node.a[i];
306
+ item = list[i];
265
307
  // Check if index is a hole
266
308
  if (item) {
267
309
  // Check if item is a parent
268
310
  if (isIndexedValueInStack(ctx, item)) {
269
- markRef(ctx, node.i);
270
- createArrayAssign(ctx, node.i, i, getRefParam(ctx, item.i));
311
+ markRef(ctx, id);
312
+ createArrayAssign(ctx, id, i, getRefParam(ctx, item.i));
271
313
  isHoley = true;
272
314
  } else {
273
315
  values += serializeTree(ctx, item);
@@ -277,30 +319,47 @@ function serializeNodeList(
277
319
  isHoley = true;
278
320
  }
279
321
  }
280
- return '[' + values + (isHoley ? ',]' : ']');
322
+ ctx.stack.pop();
323
+ pushObjectFlag(ctx, node.o, node.i);
324
+ return assignIndexedValue(ctx, id, '[' + values + (isHoley ? ',]' : ']'));
281
325
  }
282
326
 
283
- function serializeArray(
327
+ function getIterableAccess(ctx: SerializationContext): string {
328
+ return ctx.features & Feature.ArrayPrototypeValues
329
+ ? '.values()'
330
+ : '[Symbol.iterator]()';
331
+ }
332
+
333
+ function serializeIterable(
284
334
  ctx: SerializationContext,
285
- node: SerovalArrayNode,
286
- ) {
287
- ctx.stack.push(node.i);
288
- const result = serializeNodeList(ctx, node);
289
- ctx.stack.pop();
290
- return assignIndexedValue(ctx, node.i, result);
335
+ node: SerovalNode,
336
+ ): string {
337
+ const parent = ctx.stack;
338
+ ctx.stack = [];
339
+ let serialized = serializeTree(ctx, node) + getIterableAccess(ctx);
340
+ ctx.stack = parent;
341
+ if (ctx.features & Feature.ArrowFunction) {
342
+ serialized = '[Symbol.iterator]:()=>' + serialized;
343
+ } else if (ctx.features & Feature.MethodShorthand) {
344
+ serialized = '[Symbol.iterator](){return ' + serialized + '}';
345
+ } else {
346
+ serialized = '[Symbol.iterator]:function(){return ' + serialized + '}';
347
+ }
348
+ return serialized;
291
349
  }
292
350
 
293
351
  function serializeProperties(
294
352
  ctx: SerializationContext,
295
353
  sourceID: number,
296
354
  node: SerovalObjectRecordNode,
297
- ) {
298
- if (node.s === 0) {
355
+ ): string {
356
+ const len = node.s;
357
+ if (len === 0) {
299
358
  return '{}';
300
359
  }
301
360
  let result = '';
302
361
  ctx.stack.push(sourceID);
303
- let key: string;
362
+ let key: SerovalObjectRecordKey;
304
363
  let val: SerovalNode;
305
364
  let check: number;
306
365
  let isIdentifier: boolean;
@@ -308,27 +367,34 @@ function serializeProperties(
308
367
  let hasPrev = false;
309
368
  const keys = node.k;
310
369
  const values = node.v;
311
- for (let i = 0, len = node.s; i < len; i++) {
370
+ for (let i = 0; i < len; i++) {
312
371
  key = keys[i];
313
372
  val = values[i];
314
- check = Number(key);
315
- // Test if key is a valid number or JS identifier
316
- // so that we don't have to serialize the key and wrap with brackets
317
- isIdentifier = check >= 0 || isValidIdentifier(key);
318
- if (isIndexedValueInStack(ctx, val)) {
319
- refParam = getRefParam(ctx, val.i);
320
- if (isIdentifier && Number.isNaN(check)) {
321
- markRef(ctx, sourceID);
322
- createObjectAssign(ctx, sourceID, key, refParam);
323
- } else {
324
- markRef(ctx, sourceID);
325
- createArrayAssign(ctx, sourceID, isIdentifier ? key : ('"' + key + '"'), refParam);
326
- }
327
- } else {
328
- result += (hasPrev ? ',' : '')
329
- + (isIdentifier ? key : ('"' + key + '"'))
330
- + ':' + serializeTree(ctx, val);
331
- hasPrev = true;
373
+ switch (key) {
374
+ case SerovalObjectRecordSpecialKey.SymbolIterator:
375
+ result += (hasPrev ? ',' : '') + serializeIterable(ctx, val);
376
+ hasPrev = true;
377
+ break;
378
+ default:
379
+ check = Number(key);
380
+ // Test if key is a valid number or JS identifier
381
+ // so that we don't have to serialize the key and wrap with brackets
382
+ isIdentifier = check >= 0 || isValidIdentifier(key);
383
+ if (isIndexedValueInStack(ctx, val)) {
384
+ refParam = getRefParam(ctx, val.i);
385
+ markRef(ctx, sourceID);
386
+ if (isIdentifier && Number.isNaN(check)) {
387
+ createObjectAssign(ctx, sourceID, key, refParam);
388
+ } else {
389
+ createArrayAssign(ctx, sourceID, isIdentifier ? key : ('"' + key + '"'), refParam);
390
+ }
391
+ } else {
392
+ result += (hasPrev ? ',' : '')
393
+ + (isIdentifier ? key : ('"' + key + '"'))
394
+ + ':' + serializeTree(ctx, val);
395
+ hasPrev = true;
396
+ }
397
+ break;
332
398
  }
333
399
  }
334
400
  ctx.stack.pop();
@@ -340,7 +406,7 @@ function serializeWithObjectAssign(
340
406
  value: SerovalObjectRecordNode,
341
407
  id: number,
342
408
  serialized: string,
343
- ) {
409
+ ): string {
344
410
  const fields = serializeProperties(ctx, id, value);
345
411
  if (fields !== '{}') {
346
412
  return 'Object.assign(' + serialized + ',' + fields + ')';
@@ -352,35 +418,62 @@ function serializeAssignments(
352
418
  ctx: SerializationContext,
353
419
  sourceID: number,
354
420
  node: SerovalObjectRecordNode,
355
- ) {
421
+ ): string | undefined {
356
422
  ctx.stack.push(sourceID);
357
423
  const mainAssignments: Assignment[] = [];
358
- let parentStack: number[];
359
- let refParam: string;
360
- let key: string;
361
- let check: number;
424
+ let key: SerovalObjectRecordKey;
425
+ let value: SerovalNode;
362
426
  let parentAssignment: Assignment[];
363
- let isIdentifier: boolean;
364
427
  const keys = node.k;
365
428
  const values = node.v;
366
429
  for (let i = 0, len = node.s; i < len; i++) {
367
- parentStack = ctx.stack;
368
- ctx.stack = [];
369
- refParam = serializeTree(ctx, values[i]);
370
- ctx.stack = parentStack;
371
430
  key = keys[i];
372
- check = Number(key);
373
- parentAssignment = ctx.assignments;
374
- ctx.assignments = mainAssignments;
375
- // Test if key is a valid number or JS identifier
376
- // so that we don't have to serialize the key and wrap with brackets
377
- isIdentifier = check >= 0 || isValidIdentifier(key);
378
- if (isIdentifier && Number.isNaN(check)) {
379
- createObjectAssign(ctx, sourceID, key, refParam);
380
- } else {
381
- createArrayAssign(ctx, sourceID, isIdentifier ? key : ('"' + key + '"'), refParam);
431
+ value = values[i];
432
+ switch (key) {
433
+ case SerovalObjectRecordSpecialKey.SymbolIterator: {
434
+ const parent = ctx.stack;
435
+ ctx.stack = [];
436
+ const serialized = serializeTree(ctx, value) + getIterableAccess(ctx);
437
+ ctx.stack = parent;
438
+ parentAssignment = ctx.assignments;
439
+ ctx.assignments = mainAssignments;
440
+ createArrayAssign(
441
+ ctx,
442
+ sourceID,
443
+ 'Symbol.iterator',
444
+ ctx.features & Feature.ArrowFunction
445
+ ? '()=>' + serialized
446
+ : 'function(){return ' + serialized + '}',
447
+ );
448
+ ctx.assignments = parentAssignment;
449
+ }
450
+ break;
451
+ default: {
452
+ const serialized = serializeTree(ctx, value);
453
+ const check = Number(key);
454
+ const isIdentifier = check >= 0 || isValidIdentifier(key);
455
+ if (isIndexedValueInStack(ctx, value)) {
456
+ // Test if key is a valid number or JS identifier
457
+ // so that we don't have to serialize the key and wrap with brackets
458
+ if (isIdentifier && Number.isNaN(check)) {
459
+ createObjectAssign(ctx, sourceID, key, serialized);
460
+ } else {
461
+ createArrayAssign(ctx, sourceID, isIdentifier ? key : ('"' + key + '"'), serialized);
462
+ }
463
+ } else {
464
+ // Test if key is a valid number or JS identifier
465
+ // so that we don't have to serialize the key and wrap with brackets
466
+ parentAssignment = ctx.assignments;
467
+ ctx.assignments = mainAssignments;
468
+ if (isIdentifier && Number.isNaN(check)) {
469
+ createObjectAssign(ctx, sourceID, key, serialized);
470
+ } else {
471
+ createArrayAssign(ctx, sourceID, isIdentifier ? key : ('"' + key + '"'), serialized);
472
+ }
473
+ ctx.assignments = parentAssignment;
474
+ }
475
+ }
382
476
  }
383
- ctx.assignments = parentAssignment;
384
477
  }
385
478
  ctx.stack.pop();
386
479
  return resolveAssignments(mainAssignments);
@@ -391,7 +484,7 @@ function serializeDictionary(
391
484
  i: number,
392
485
  d: SerovalObjectRecordNode | undefined,
393
486
  init: string,
394
- ) {
487
+ ): string {
395
488
  if (d) {
396
489
  if (ctx.features & Feature.ObjectAssign) {
397
490
  init = serializeWithObjectAssign(ctx, d, i, init);
@@ -406,36 +499,42 @@ function serializeDictionary(
406
499
  return assignIndexedValue(ctx, i, init);
407
500
  }
408
501
 
502
+ const NULL_CONSTRUCTOR = 'Object.create(null)';
503
+
409
504
  function serializeNullConstructor(
410
505
  ctx: SerializationContext,
411
506
  node: SerovalNullConstructorNode,
412
- ) {
413
- return serializeDictionary(ctx, node.i, node.d, 'Object.create(null)');
507
+ ): string {
508
+ pushObjectFlag(ctx, node.o, node.i);
509
+ return serializeDictionary(ctx, node.i, node.d, NULL_CONSTRUCTOR);
414
510
  }
415
511
 
416
512
  function serializeObject(
417
513
  ctx: SerializationContext,
418
514
  node: SerovalObjectNode,
419
- ) {
515
+ ): string {
516
+ pushObjectFlag(ctx, node.o, node.i);
420
517
  return assignIndexedValue(ctx, node.i, serializeProperties(ctx, node.i, node.d));
421
518
  }
422
519
 
423
520
  function serializeSet(
424
521
  ctx: SerializationContext,
425
522
  node: SerovalSetNode,
426
- ) {
523
+ ): string {
427
524
  let serialized = 'new Set';
428
525
  const size = node.l;
526
+ const id = node.i;
429
527
  if (size) {
430
528
  let result = '';
431
- ctx.stack.push(node.i);
432
529
  let item: SerovalNode;
433
530
  let hasPrev = false;
531
+ const items = node.a;
532
+ ctx.stack.push(id);
434
533
  for (let i = 0; i < size; i++) {
435
- item = node.a[i];
534
+ item = items[i];
436
535
  if (isIndexedValueInStack(ctx, item)) {
437
- markRef(ctx, node.i);
438
- createAddAssignment(ctx, node.i, getRefParam(ctx, item.i));
536
+ markRef(ctx, id);
537
+ createAddAssignment(ctx, id, getRefParam(ctx, item.i));
439
538
  } else {
440
539
  // Push directly
441
540
  result += (hasPrev ? ',' : '') + serializeTree(ctx, item);
@@ -447,38 +546,42 @@ function serializeSet(
447
546
  serialized += '([' + result + '])';
448
547
  }
449
548
  }
450
- return assignIndexedValue(ctx, node.i, serialized);
549
+ return assignIndexedValue(ctx, id, serialized);
451
550
  }
452
551
 
453
552
  function serializeMap(
454
553
  ctx: SerializationContext,
455
554
  node: SerovalMapNode,
456
- ) {
555
+ ): string {
457
556
  let serialized = 'new Map';
458
- if (node.d.s) {
557
+ const size = node.d.s;
558
+ const id = node.i;
559
+ if (size) {
459
560
  let result = '';
460
- ctx.stack.push(node.i);
461
561
  let key: SerovalNode;
462
562
  let val: SerovalNode;
463
563
  let keyRef: string;
464
564
  let valueRef: string;
465
565
  let parent: number[];
466
566
  let hasPrev = false;
467
- for (let i = 0; i < node.d.s; i++) {
567
+ const keys = node.d.k;
568
+ const vals = node.d.v;
569
+ ctx.stack.push(id);
570
+ for (let i = 0; i < size; i++) {
468
571
  // Check if key is a parent
469
- key = node.d.k[i];
470
- val = node.d.v[i];
572
+ key = keys[i];
573
+ val = vals[i];
471
574
  if (isIndexedValueInStack(ctx, key)) {
472
575
  // Create reference for the map instance
473
- keyRef = getRefParam(ctx, key.i);
474
- markRef(ctx, node.i);
576
+ keyRef = getRefParam(ctx, id);
577
+ markRef(ctx, id);
475
578
  // Check if value is a parent
476
579
  if (isIndexedValueInStack(ctx, val)) {
477
580
  valueRef = getRefParam(ctx, val.i);
478
581
  // Register an assignment since
479
582
  // both key and value are a parent of this
480
583
  // Map instance
481
- createSetAssignment(ctx, node.i, keyRef, valueRef);
584
+ createSetAssignment(ctx, id, keyRef, valueRef);
482
585
  } else {
483
586
  // Reset the stack
484
587
  // This is required because the serialized
@@ -487,17 +590,17 @@ function serializeMap(
487
590
  // assignment
488
591
  parent = ctx.stack;
489
592
  ctx.stack = [];
490
- createSetAssignment(ctx, node.i, keyRef, serializeTree(ctx, val));
593
+ createSetAssignment(ctx, id, keyRef, serializeTree(ctx, val));
491
594
  ctx.stack = parent;
492
595
  }
493
596
  } else if (isIndexedValueInStack(ctx, val)) {
494
597
  // Create ref for the Map instance
495
598
  valueRef = getRefParam(ctx, val.i);
496
- markRef(ctx, node.i);
599
+ markRef(ctx, id);
497
600
  // Reset stack for the key serialization
498
601
  parent = ctx.stack;
499
602
  ctx.stack = [];
500
- createSetAssignment(ctx, node.i, serializeTree(ctx, key), valueRef);
603
+ createSetAssignment(ctx, id, serializeTree(ctx, key), valueRef);
501
604
  ctx.stack = parent;
502
605
  } else {
503
606
  result += (hasPrev ? ',[' : '[') + serializeTree(ctx, key) + ',' + serializeTree(ctx, val) + ']';
@@ -512,67 +615,71 @@ function serializeMap(
512
615
  serialized += '([' + result + '])';
513
616
  }
514
617
  }
515
- return assignIndexedValue(ctx, node.i, serialized);
618
+ return assignIndexedValue(ctx, id, serialized);
516
619
  }
517
620
 
518
621
  function serializeAggregateError(
519
622
  ctx: SerializationContext,
520
623
  node: SerovalAggregateErrorNode,
521
- ) {
624
+ ): string {
522
625
  // Serialize the required arguments
523
- ctx.stack.push(node.i);
626
+ const id = node.i;
627
+ ctx.stack.push(id);
524
628
  const serialized = 'new AggregateError([],"' + node.m + '")';
525
629
  ctx.stack.pop();
526
630
  // `AggregateError` might've been extended
527
631
  // either through class or custom properties
528
632
  // Make sure to assign extra properties
529
- return serializeDictionary(ctx, node.i, node.d, serialized);
633
+ return serializeDictionary(ctx, id, node.d, serialized);
530
634
  }
531
635
 
532
636
  function serializeError(
533
637
  ctx: SerializationContext,
534
638
  node: SerovalErrorNode,
535
- ) {
536
- const serialized = 'new ' + node.c + '("' + node.m + '")';
537
- return serializeDictionary(ctx, node.i, node.d, serialized);
639
+ ): string {
640
+ return serializeDictionary(ctx, node.i, node.d, 'new ' + node.c + '("' + node.m + '")');
538
641
  }
539
642
 
540
643
  function serializePromise(
541
644
  ctx: SerializationContext,
542
645
  node: SerovalPromiseNode,
543
- ) {
646
+ ): string {
544
647
  let serialized: string;
545
648
  // Check if resolved value is a parent expression
546
- if (isIndexedValueInStack(ctx, node.f)) {
649
+ const fulfilled = node.f;
650
+ const id = node.i;
651
+ if (isIndexedValueInStack(ctx, fulfilled)) {
547
652
  // A Promise trick, reference the value
548
653
  // inside the `then` expression so that
549
654
  // the Promise evaluates after the parent
550
655
  // has initialized
551
- const ref = getRefParam(ctx, node.f.i);
656
+ const ref = getRefParam(ctx, fulfilled.i);
552
657
  if (ctx.features & Feature.ArrowFunction) {
553
658
  serialized = 'Promise.resolve().then(()=>' + ref + ')';
554
659
  } else {
555
660
  serialized = 'Promise.resolve().then(function(){return ' + ref + '})';
556
661
  }
557
662
  } else {
558
- ctx.stack.push(node.i);
559
- const result = serializeTree(ctx, node.f);
663
+ ctx.stack.push(id);
664
+ const result = serializeTree(ctx, fulfilled);
560
665
  ctx.stack.pop();
561
666
  // just inline the value/reference here
562
667
  serialized = 'Promise.resolve(' + result + ')';
563
668
  }
564
- return assignIndexedValue(ctx, node.i, serialized);
669
+ return assignIndexedValue(ctx, id, serialized);
565
670
  }
566
671
 
567
672
  function serializeArrayBuffer(
568
673
  ctx: SerializationContext,
569
674
  node: SerovalArrayBufferNode,
570
- ) {
675
+ ): string {
571
676
  let result = 'new Uint8Array(';
572
- if (node.s.length) {
677
+ const buffer = node.s;
678
+ const len = buffer.length;
679
+ if (len) {
573
680
  result += '[';
574
- for (let i = 0, len = node.s.length; i < len; i++) {
575
- result += ((i > 0) ? ',' : '') + node.s[i];
681
+ for (let i = 0; i < len; i++) {
682
+ result += ((i > 0) ? ',' : '') + buffer[i];
576
683
  }
577
684
  result += ']';
578
685
  }
@@ -582,121 +689,115 @@ function serializeArrayBuffer(
582
689
  function serializeTypedArray(
583
690
  ctx: SerializationContext,
584
691
  node: SerovalTypedArrayNode | SerovalBigIntTypedArrayNode,
585
- ) {
586
- const args = serializeTree(ctx, node.f) + ',' + node.b + ',' + node.l;
587
- return assignIndexedValue(ctx, node.i, 'new ' + node.c + '(' + args + ')');
588
- }
589
-
590
- function serializeIterable(
591
- ctx: SerializationContext,
592
- node: SerovalIterableNode,
593
- ) {
594
- const parent = ctx.stack;
595
- ctx.stack = [];
596
- const values = serializeNodeList(ctx, node);
597
- ctx.stack = parent;
598
- let serialized = values;
599
- if (ctx.features & Feature.ArrayPrototypeValues) {
600
- serialized += '.values()';
601
- } else {
602
- serialized += '[Symbol.iterator]()';
603
- }
604
- if (ctx.features & Feature.ArrowFunction) {
605
- serialized = '{[Symbol.iterator]:()=>' + serialized + '}';
606
- } else if (ctx.features & Feature.MethodShorthand) {
607
- serialized = '{[Symbol.iterator](){return ' + serialized + '}}';
608
- } else {
609
- serialized = '{[Symbol.iterator]:function(){return ' + serialized + '}}';
610
- }
611
- return serializeDictionary(ctx, node.i, node.d, serialized);
692
+ ): string {
693
+ return assignIndexedValue(
694
+ ctx,
695
+ node.i,
696
+ 'new ' + node.c + '(' + serializeTree(ctx, node.f) + ',' + node.b + ',' + node.l + ')',
697
+ );
612
698
  }
613
699
 
614
700
  function serializeDate(
615
701
  ctx: SerializationContext,
616
702
  node: SerovalDateNode,
617
- ) {
703
+ ): string {
618
704
  return assignIndexedValue(ctx, node.i, 'new Date("' + node.s + '")');
619
705
  }
620
706
 
621
707
  function serializeRegExp(
622
708
  ctx: SerializationContext,
623
709
  node: SerovalRegExpNode,
624
- ) {
710
+ ): string {
625
711
  return assignIndexedValue(ctx, node.i, '/' + node.c + '/' + node.m);
626
712
  }
627
713
 
628
714
  function serializeURL(
629
715
  ctx: SerializationContext,
630
716
  node: SerovalURLNode,
631
- ) {
717
+ ): string {
632
718
  return assignIndexedValue(ctx, node.i, 'new URL("' + node.s + '")');
633
719
  }
634
720
 
635
721
  function serializeURLSearchParams(
636
722
  ctx: SerializationContext,
637
723
  node: SerovalURLSearchParamsNode,
638
- ) {
639
- return assignIndexedValue(ctx, node.i, node.s ? 'new URLSearchParams("' + node.s + '")' : 'new URLSearchParams');
724
+ ): string {
725
+ return assignIndexedValue(
726
+ ctx,
727
+ node.i,
728
+ node.s ? 'new URLSearchParams("' + node.s + '")' : 'new URLSearchParams',
729
+ );
640
730
  }
641
731
 
642
732
  function serializeReference(
643
733
  ctx: SerializationContext,
644
734
  node: SerovalReferenceNode,
645
- ) {
735
+ ): string {
646
736
  return assignIndexedValue(ctx, node.i, GLOBAL_KEY + '.get("' + node.s + '")');
647
737
  }
648
738
 
649
739
  function serializeDataView(
650
740
  ctx: SerializationContext,
651
741
  node: SerovalDataViewNode,
652
- ) {
653
- const args = serializeTree(ctx, node.f) + ',' + node.b + ',' + node.l;
654
- return assignIndexedValue(ctx, node.i, 'new DataView(' + args + ')');
742
+ ): string {
743
+ return assignIndexedValue(
744
+ ctx,
745
+ node.i,
746
+ 'new DataView(' + serializeTree(ctx, node.f) + ',' + node.b + ',' + node.l + ')',
747
+ );
655
748
  }
656
749
 
657
750
  function serializeBlob(
658
751
  ctx: SerializationContext,
659
752
  node: SerovalBlobNode,
660
- ) {
661
- const args = '[' + serializeTree(ctx, node.f) + '],{type:"' + node.c + '"}';
662
- return assignIndexedValue(ctx, node.i, 'new Blob(' + args + ')');
753
+ ): string {
754
+ return assignIndexedValue(
755
+ ctx,
756
+ node.i,
757
+ 'new Blob([' + serializeTree(ctx, node.f) + '],{type:"' + node.c + '"})',
758
+ );
663
759
  }
664
760
 
665
761
  function serializeFile(
666
762
  ctx: SerializationContext,
667
763
  node: SerovalFileNode,
668
- ) {
669
- const options = '{type:"' + node.c + '",lastModified:' + node.b + '}';
670
- const args = '[' + serializeTree(ctx, node.f) + '],"' + node.m + '",' + options;
671
- return assignIndexedValue(ctx, node.i, 'new File(' + args + ')');
764
+ ): string {
765
+ return assignIndexedValue(
766
+ ctx,
767
+ node.i,
768
+ 'new File([' + serializeTree(ctx, node.f) + '],"' + node.m + '",{type:"' + node.c + '",lastModified:' + node.b + '})',
769
+ );
672
770
  }
673
771
 
674
772
  function serializeHeaders(
675
773
  ctx: SerializationContext,
676
774
  node: SerovalHeadersNode,
677
- ) {
678
- return assignIndexedValue(ctx, node.i, 'new Headers(' + serializeProperties(ctx, node.i, node.d) + ')');
775
+ ): string {
776
+ return assignIndexedValue(
777
+ ctx,
778
+ node.i,
779
+ 'new Headers(' + serializeProperties(ctx, node.i, node.d) + ')',
780
+ );
679
781
  }
680
782
 
681
783
  function serializeFormDataEntries(
682
784
  ctx: SerializationContext,
683
785
  node: SerovalFormDataNode,
684
- ) {
685
- ctx.stack.push(node.i);
686
- const mainAssignments: Assignment[] = [];
687
- let parentStack: number[];
786
+ ): string | undefined {
688
787
  let value: string;
689
788
  let key: string;
789
+ const keys = node.d.k;
790
+ const vals = node.d.v;
791
+ const id = node.i;
792
+ const mainAssignments: Assignment[] = [];
690
793
  let parentAssignment: Assignment[];
691
- for (let i = 0; i < node.d.s; i++) {
692
- parentStack = ctx.stack;
693
- ctx.stack = [];
694
- value = serializeTree(ctx, node.d.v[i]);
695
- key = node.d.k[i];
696
- ctx.stack = parentStack;
794
+ ctx.stack.push(id);
795
+ for (let i = 0, len = node.d.s; i < len; i++) {
796
+ key = keys[i];
797
+ value = serializeTree(ctx, vals[i]);
697
798
  parentAssignment = ctx.assignments;
698
799
  ctx.assignments = mainAssignments;
699
- createAppendAssignment(ctx, node.i, '"' + key + '"', value);
800
+ createAppendAssignment(ctx, id, '"' + key + '"', value);
700
801
  ctx.assignments = parentAssignment;
701
802
  }
702
803
  ctx.stack.pop();
@@ -706,17 +807,34 @@ function serializeFormDataEntries(
706
807
  function serializeFormData(
707
808
  ctx: SerializationContext,
708
809
  node: SerovalFormDataNode,
709
- ) {
710
- if (node.d.s) {
711
- markRef(ctx, node.i);
810
+ ): string {
811
+ const size = node.d.s;
812
+ const id = node.i;
813
+ if (size) {
814
+ markRef(ctx, id);
712
815
  }
713
- const result = assignIndexedValue(ctx, node.i, 'new FormData()');
714
- if (node.d.s) {
715
- return '(' + result + ',' + serializeFormDataEntries(ctx, node) + getRefParam(ctx, node.i) + ')';
816
+ const result = assignIndexedValue(ctx, id, 'new FormData()');
817
+ if (size) {
818
+ const entries = serializeFormDataEntries(ctx, node);
819
+ return '(' + result + ',' + (entries == null ? '' : entries) + getRefParam(ctx, id) + ')';
716
820
  }
717
821
  return result;
718
822
  }
719
823
 
824
+ function serializeBoxed(
825
+ ctx: SerializationContext,
826
+ node: SerovalBoxedNode,
827
+ ): string {
828
+ return assignIndexedValue(ctx, node.i, 'Object(' + serializeTree(ctx, node.f) + ')');
829
+ }
830
+
831
+ function serializeWKSymbol(
832
+ ctx: SerializationContext,
833
+ node: SerovalWKSymbolNode,
834
+ ): string {
835
+ return assignIndexedValue(ctx, node.i, SYMBOL_STRING[node.s]);
836
+ }
837
+
720
838
  export default function serializeTree(
721
839
  ctx: SerializationContext,
722
840
  node: SerovalNode,
@@ -726,20 +844,8 @@ export default function serializeTree(
726
844
  return '' + node.s;
727
845
  case SerovalNodeType.String:
728
846
  return '"' + node.s + '"';
729
- case SerovalNodeType.Boolean:
730
- return node.s ? '!0' : '!1';
731
- case SerovalNodeType.Undefined:
732
- return 'void 0';
733
- case SerovalNodeType.Null:
734
- return 'null';
735
- case SerovalNodeType.NegativeZero:
736
- return '-0';
737
- case SerovalNodeType.Infinity:
738
- return '1/0';
739
- case SerovalNodeType.NegativeInfinity:
740
- return '-1/0';
741
- case SerovalNodeType.NaN:
742
- return 'NaN';
847
+ case SerovalNodeType.Constant:
848
+ return serializeConstant(node);
743
849
  case SerovalNodeType.BigInt:
744
850
  return node.s + 'n';
745
851
  case SerovalNodeType.IndexedValue:
@@ -769,12 +875,10 @@ export default function serializeTree(
769
875
  return serializeAggregateError(ctx, node);
770
876
  case SerovalNodeType.Error:
771
877
  return serializeError(ctx, node);
772
- case SerovalNodeType.Iterable:
773
- return serializeIterable(ctx, node);
774
878
  case SerovalNodeType.Promise:
775
879
  return serializePromise(ctx, node);
776
880
  case SerovalNodeType.WKSymbol:
777
- return SYMBOL_STRING[node.s];
881
+ return serializeWKSymbol(ctx, node);
778
882
  case SerovalNodeType.URL:
779
883
  return serializeURL(ctx, node);
780
884
  case SerovalNodeType.URLSearchParams:
@@ -789,7 +893,9 @@ export default function serializeTree(
789
893
  return serializeHeaders(ctx, node);
790
894
  case SerovalNodeType.FormData:
791
895
  return serializeFormData(ctx, node);
896
+ case SerovalNodeType.Boxed:
897
+ return serializeBoxed(ctx, node);
792
898
  default:
793
- throw new Error('Unsupported type');
899
+ throw new Error('invariant');
794
900
  }
795
901
  }