seroval 0.3.0 → 0.4.0-alpha.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 +88 -3
- package/dist/cjs/development/index.cjs +599 -250
- package/dist/cjs/development/index.cjs.map +3 -3
- package/dist/cjs/production/index.cjs +2 -2
- package/dist/esm/development/index.mjs +599 -250
- package/dist/esm/development/index.mjs.map +3 -3
- package/dist/esm/production/index.mjs +2 -2
- package/dist/types/assert.d.ts +1 -0
- package/dist/types/compat.d.ts +37 -0
- package/dist/types/index.d.ts +3 -2
- package/dist/types/tree.d.ts +44 -25
- package/package.json +2 -2
- package/src/assert.ts +5 -0
- package/src/compat.ts +247 -0
- package/src/index.ts +34 -16
- package/src/tree.ts +484 -269
- package/dist/types/serialize-primitive.d.ts +0 -2
- package/src/serialize-primitive.ts +0 -35
package/src/tree.ts
CHANGED
|
@@ -16,7 +16,8 @@ import {
|
|
|
16
16
|
TypedArrayValue,
|
|
17
17
|
} from './types';
|
|
18
18
|
import getIdentifier from './get-identifier';
|
|
19
|
-
import
|
|
19
|
+
import { Feature, parseTargets } from './compat';
|
|
20
|
+
import assert from './assert';
|
|
20
21
|
|
|
21
22
|
interface IndexAssignment {
|
|
22
23
|
type: 'index';
|
|
@@ -55,9 +56,21 @@ export interface SerializationContext {
|
|
|
55
56
|
vars: string[];
|
|
56
57
|
// Array of assignments to be done (used for recursion)
|
|
57
58
|
assignments: Assignment[];
|
|
59
|
+
// Supported features
|
|
60
|
+
features: Set<Feature>;
|
|
58
61
|
}
|
|
59
62
|
|
|
60
|
-
export
|
|
63
|
+
export interface Options {
|
|
64
|
+
target: string | string[];
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const DEFAULT_OPTIONS: Options = {
|
|
68
|
+
target: 'es2023',
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
export function createSerializationContext(options: Partial<Options> = {}): SerializationContext {
|
|
72
|
+
// eslint-disable-next-line prefer-object-spread
|
|
73
|
+
const result = Object.assign({}, DEFAULT_OPTIONS, options || {});
|
|
61
74
|
return {
|
|
62
75
|
markedRefs: [],
|
|
63
76
|
stack: [],
|
|
@@ -65,6 +78,7 @@ export function createSerializationContext(): SerializationContext {
|
|
|
65
78
|
vars: [],
|
|
66
79
|
assignments: [],
|
|
67
80
|
validRefs: new Map(),
|
|
81
|
+
features: parseTargets(result.target),
|
|
68
82
|
};
|
|
69
83
|
}
|
|
70
84
|
|
|
@@ -81,12 +95,12 @@ function getAssignmentExpression(assignment: Assignment): string {
|
|
|
81
95
|
}
|
|
82
96
|
}
|
|
83
97
|
|
|
84
|
-
function mergeAssignments(
|
|
98
|
+
function mergeAssignments(assignments: Assignment[]) {
|
|
85
99
|
const newAssignments = [];
|
|
86
|
-
let current =
|
|
100
|
+
let current = assignments[0];
|
|
87
101
|
let prev = current;
|
|
88
|
-
for (let i = 1, len =
|
|
89
|
-
const item =
|
|
102
|
+
for (let i = 1, len = assignments.length; i < len; i++) {
|
|
103
|
+
const item = assignments[i];
|
|
90
104
|
if (item.type === prev.type) {
|
|
91
105
|
if (item.type === 'index' && item.value === prev.value) {
|
|
92
106
|
// Merge if the right-hand value is the same
|
|
@@ -128,15 +142,20 @@ function mergeAssignments(ctx: SerializationContext) {
|
|
|
128
142
|
return newAssignments;
|
|
129
143
|
}
|
|
130
144
|
|
|
131
|
-
|
|
132
|
-
if (
|
|
145
|
+
function resolveAssignments(assignments: Assignment[]) {
|
|
146
|
+
if (assignments.length) {
|
|
133
147
|
let result = '';
|
|
134
|
-
|
|
135
|
-
|
|
148
|
+
const merged = mergeAssignments(assignments);
|
|
149
|
+
for (let i = 0, len = merged.length; i < len; i++) {
|
|
150
|
+
result += `${getAssignmentExpression(merged[i])},`;
|
|
136
151
|
}
|
|
137
152
|
return result;
|
|
138
153
|
}
|
|
139
|
-
return
|
|
154
|
+
return undefined;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
export function resolvePatches(ctx: SerializationContext) {
|
|
158
|
+
return resolveAssignments(ctx.assignments);
|
|
140
159
|
}
|
|
141
160
|
|
|
142
161
|
/**
|
|
@@ -303,14 +322,9 @@ function getErrorConstructor(error: ErrorValue) {
|
|
|
303
322
|
return 'Error';
|
|
304
323
|
}
|
|
305
324
|
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
stack: true,
|
|
310
|
-
message: true,
|
|
311
|
-
};
|
|
312
|
-
|
|
313
|
-
function getErrorOptions(error: Error) {
|
|
325
|
+
function getErrorOptions(
|
|
326
|
+
error: Error,
|
|
327
|
+
) {
|
|
314
328
|
let options: Record<string, any> | undefined;
|
|
315
329
|
const constructor = getErrorConstructor(error);
|
|
316
330
|
// Name has been modified
|
|
@@ -323,7 +337,7 @@ function getErrorOptions(error: Error) {
|
|
|
323
337
|
}
|
|
324
338
|
const names = Object.getOwnPropertyNames(error);
|
|
325
339
|
for (const name of names) {
|
|
326
|
-
if (
|
|
340
|
+
if (name !== 'name' && name !== 'message') {
|
|
327
341
|
options = options || {};
|
|
328
342
|
options[name] = error[name as keyof Error];
|
|
329
343
|
}
|
|
@@ -367,48 +381,66 @@ type SerovalSemiPrimitiveNode =
|
|
|
367
381
|
| [type: SerovalNodeType.RegExp, value: RegExp, id: number]
|
|
368
382
|
| [
|
|
369
383
|
type: SerovalNodeType.TypedArray,
|
|
370
|
-
value:
|
|
384
|
+
value: [constructor: string, array: TypedArrayValue],
|
|
371
385
|
id: number
|
|
372
386
|
];
|
|
373
387
|
|
|
388
|
+
type SerovalDictionaryNode = [key: string[], value: SerovalNode[], size: number];
|
|
389
|
+
type SerovalSetNode = [type: SerovalNodeType.Set, value: SerovalNode[], id: number];
|
|
390
|
+
type SerovalMapNode = [
|
|
391
|
+
type: SerovalNodeType.Map,
|
|
392
|
+
value: [key: SerovalNode[], value: SerovalNode[], size: number],
|
|
393
|
+
id: number
|
|
394
|
+
];
|
|
395
|
+
type SerovalArrayNode = [type: SerovalNodeType.Array, value: SerovalNode[], id: number];
|
|
396
|
+
type SerovalObjectNode = [type: SerovalNodeType.Object, value: SerovalDictionaryNode, id: number];
|
|
397
|
+
type SerovalNullConstructorNode = [
|
|
398
|
+
type: SerovalNodeType.NullConstructor,
|
|
399
|
+
value: SerovalDictionaryNode,
|
|
400
|
+
id: number,
|
|
401
|
+
];
|
|
402
|
+
type SerovalPromiseNode = [type: SerovalNodeType.Promise, value: SerovalNode, id: number];
|
|
403
|
+
type SerovalErrorNode = [
|
|
404
|
+
type: SerovalNodeType.Error,
|
|
405
|
+
value: [
|
|
406
|
+
constructor: string,
|
|
407
|
+
message: string,
|
|
408
|
+
options?: SerovalDictionaryNode,
|
|
409
|
+
],
|
|
410
|
+
id: number
|
|
411
|
+
];
|
|
412
|
+
type SerovalAggregateErrorNode = [
|
|
413
|
+
type: SerovalNodeType.AggregateError,
|
|
414
|
+
value: [
|
|
415
|
+
message: string,
|
|
416
|
+
options: SerovalDictionaryNode | undefined,
|
|
417
|
+
errors: SerovalNode,
|
|
418
|
+
],
|
|
419
|
+
id: number
|
|
420
|
+
];
|
|
421
|
+
|
|
422
|
+
type SerovalIterableNode = [
|
|
423
|
+
type: SerovalNodeType.Iterable,
|
|
424
|
+
value: [
|
|
425
|
+
options: SerovalDictionaryNode | undefined,
|
|
426
|
+
items: SerovalNode,
|
|
427
|
+
],
|
|
428
|
+
id: number,
|
|
429
|
+
];
|
|
430
|
+
|
|
374
431
|
type SerovalNode =
|
|
375
432
|
| SerovalPrimitiveNode
|
|
376
433
|
| SerovalReferenceNode
|
|
377
434
|
| SerovalSemiPrimitiveNode
|
|
378
|
-
|
|
|
379
|
-
|
|
|
380
|
-
|
|
|
381
|
-
|
|
|
382
|
-
|
|
|
383
|
-
|
|
|
384
|
-
|
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
constructor: string;
|
|
388
|
-
message: string;
|
|
389
|
-
options?: SerovalNode;
|
|
390
|
-
cause?: SerovalNode;
|
|
391
|
-
},
|
|
392
|
-
id: number
|
|
393
|
-
]
|
|
394
|
-
| [
|
|
395
|
-
type: SerovalNodeType.AggregateError,
|
|
396
|
-
value: {
|
|
397
|
-
message: string;
|
|
398
|
-
options?: SerovalNode;
|
|
399
|
-
cause?: SerovalNode;
|
|
400
|
-
errors: SerovalNode;
|
|
401
|
-
},
|
|
402
|
-
id: number
|
|
403
|
-
]
|
|
404
|
-
| [
|
|
405
|
-
type: SerovalNodeType.Iterable,
|
|
406
|
-
value: {
|
|
407
|
-
items: SerovalNode,
|
|
408
|
-
options?: SerovalNode,
|
|
409
|
-
},
|
|
410
|
-
id: number,
|
|
411
|
-
];
|
|
435
|
+
| SerovalSetNode
|
|
436
|
+
| SerovalMapNode
|
|
437
|
+
| SerovalArrayNode
|
|
438
|
+
| SerovalObjectNode
|
|
439
|
+
| SerovalNullConstructorNode
|
|
440
|
+
| SerovalPromiseNode
|
|
441
|
+
| SerovalErrorNode
|
|
442
|
+
| SerovalAggregateErrorNode
|
|
443
|
+
| SerovalIterableNode;
|
|
412
444
|
|
|
413
445
|
function isReferenceInStack(
|
|
414
446
|
ctx: SerializationContext,
|
|
@@ -438,6 +470,7 @@ function generateRef(
|
|
|
438
470
|
}
|
|
439
471
|
|
|
440
472
|
function generateSemiPrimitiveValue(
|
|
473
|
+
ctx: SerializationContext,
|
|
441
474
|
current: unknown,
|
|
442
475
|
id: number,
|
|
443
476
|
): SerovalNode | undefined {
|
|
@@ -448,37 +481,56 @@ function generateSemiPrimitiveValue(
|
|
|
448
481
|
return [SerovalNodeType.RegExp, current, id];
|
|
449
482
|
}
|
|
450
483
|
if (constructorCheck<Int8Array>(current, Int8Array)) {
|
|
451
|
-
|
|
484
|
+
assert(ctx.features.has('typed-arrays'), 'Unsupported value type "Int8Array"');
|
|
485
|
+
return [SerovalNodeType.TypedArray, ['Int8Array', current], id];
|
|
452
486
|
}
|
|
453
487
|
if (constructorCheck<Int16Array>(current, Int16Array)) {
|
|
454
|
-
|
|
488
|
+
assert(ctx.features.has('typed-arrays'), 'Unsupported value type "Int16Array"');
|
|
489
|
+
return [SerovalNodeType.TypedArray, ['Int16Array', current], id];
|
|
455
490
|
}
|
|
456
491
|
if (constructorCheck<Int32Array>(current, Int32Array)) {
|
|
457
|
-
|
|
492
|
+
assert(ctx.features.has('typed-arrays'), 'Unsupported value type "Int32Array"');
|
|
493
|
+
return [SerovalNodeType.TypedArray, ['Int32Array', current], id];
|
|
458
494
|
}
|
|
459
495
|
if (constructorCheck<Uint8Array>(current, Uint8Array)) {
|
|
460
|
-
|
|
496
|
+
assert(ctx.features.has('typed-arrays'), 'Unsupported value type "Uint8Array"');
|
|
497
|
+
return [SerovalNodeType.TypedArray, ['Uint8Array', current], id];
|
|
461
498
|
}
|
|
462
499
|
if (constructorCheck<Uint16Array>(current, Uint16Array)) {
|
|
463
|
-
|
|
500
|
+
assert(ctx.features.has('typed-arrays'), 'Unsupported value type "Uint16Array"');
|
|
501
|
+
return [SerovalNodeType.TypedArray, ['Uint16Array', current], id];
|
|
464
502
|
}
|
|
465
503
|
if (constructorCheck<Uint32Array>(current, Uint32Array)) {
|
|
466
|
-
|
|
504
|
+
assert(ctx.features.has('typed-arrays'), 'Unsupported value type "Uint32Array"');
|
|
505
|
+
return [SerovalNodeType.TypedArray, ['Uint32Array', current], id];
|
|
467
506
|
}
|
|
468
507
|
if (constructorCheck<Uint8ClampedArray>(current, Uint8ClampedArray)) {
|
|
469
|
-
|
|
508
|
+
assert(ctx.features.has('typed-arrays'), 'Unsupported value type "Uint8ClampedArray"');
|
|
509
|
+
return [SerovalNodeType.TypedArray, ['Uint8ClampedArray', current], id];
|
|
470
510
|
}
|
|
471
511
|
if (constructorCheck<Float32Array>(current, Float32Array)) {
|
|
472
|
-
|
|
512
|
+
assert(ctx.features.has('typed-arrays'), 'Unsupported value type "Float32Array"');
|
|
513
|
+
return [SerovalNodeType.TypedArray, ['Float32Array', current], id];
|
|
473
514
|
}
|
|
474
515
|
if (constructorCheck<Float64Array>(current, Float64Array)) {
|
|
475
|
-
|
|
516
|
+
assert(ctx.features.has('typed-arrays'), 'Unsupported value type "Float64Array"');
|
|
517
|
+
return [SerovalNodeType.TypedArray, ['Float64Array', current], id];
|
|
476
518
|
}
|
|
477
519
|
if (constructorCheck<BigInt64Array>(current, BigInt64Array)) {
|
|
478
|
-
|
|
520
|
+
assert(
|
|
521
|
+
ctx.features.has('typed-arrays')
|
|
522
|
+
&& ctx.features.has('bigint'),
|
|
523
|
+
'Unsupported value type "BigInt64Array"',
|
|
524
|
+
);
|
|
525
|
+
return [SerovalNodeType.TypedArray, ['BigInt64Array', current], id];
|
|
479
526
|
}
|
|
480
527
|
if (constructorCheck<BigUint64Array>(current, BigUint64Array)) {
|
|
481
|
-
|
|
528
|
+
assert(
|
|
529
|
+
ctx.features.has('typed-arrays')
|
|
530
|
+
&& ctx.features.has('bigint'),
|
|
531
|
+
'Unsupported value type "BigUint64Array"',
|
|
532
|
+
);
|
|
533
|
+
return [SerovalNodeType.TypedArray, ['BigUint64Array', current], id];
|
|
482
534
|
}
|
|
483
535
|
return undefined;
|
|
484
536
|
}
|
|
@@ -487,6 +539,31 @@ export function generateTreeSync(
|
|
|
487
539
|
ctx: SerializationContext,
|
|
488
540
|
current: ServerValue,
|
|
489
541
|
): SerovalNode {
|
|
542
|
+
function serializeProperties(
|
|
543
|
+
properties: Record<string, unknown>,
|
|
544
|
+
): SerovalDictionaryNode {
|
|
545
|
+
const keyNodes: string[] = [];
|
|
546
|
+
const valueNodes: SerovalNode[] = [];
|
|
547
|
+
const deferredKeys: string[] = [];
|
|
548
|
+
const deferredValues: ServerValue[] = [];
|
|
549
|
+
const keys = Object.keys(properties);
|
|
550
|
+
let deferredSize = 0;
|
|
551
|
+
for (const key of keys) {
|
|
552
|
+
if (isIterable(properties[key])) {
|
|
553
|
+
deferredKeys.push(key);
|
|
554
|
+
deferredValues.push(properties[key] as ServerValue);
|
|
555
|
+
deferredSize++;
|
|
556
|
+
} else {
|
|
557
|
+
keyNodes.push(key);
|
|
558
|
+
valueNodes.push(generateTreeSync(ctx, properties[key] as ServerValue));
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
for (let i = 0; i < deferredSize; i++) {
|
|
562
|
+
keyNodes.push(deferredKeys[i]);
|
|
563
|
+
valueNodes.push(generateTreeSync(ctx, deferredValues[i]));
|
|
564
|
+
}
|
|
565
|
+
return [keyNodes, valueNodes, keys.length];
|
|
566
|
+
}
|
|
490
567
|
if (isPrimitive(current)) {
|
|
491
568
|
return [SerovalNodeType.Primitive, current];
|
|
492
569
|
}
|
|
@@ -496,11 +573,12 @@ export function generateTreeSync(
|
|
|
496
573
|
if (Array.isArray(id)) {
|
|
497
574
|
return id;
|
|
498
575
|
}
|
|
499
|
-
const semiPrimitive = generateSemiPrimitiveValue(current, id);
|
|
576
|
+
const semiPrimitive = generateSemiPrimitiveValue(ctx, current, id);
|
|
500
577
|
if (semiPrimitive) {
|
|
501
578
|
return semiPrimitive;
|
|
502
579
|
}
|
|
503
580
|
if (constructorCheck<Set<ServerValue>>(current, Set)) {
|
|
581
|
+
assert(ctx.features.has('set'), 'Unsupported type "Set"');
|
|
504
582
|
const nodes: SerovalNode[] = [];
|
|
505
583
|
const deferred: ServerValue[] = [];
|
|
506
584
|
for (const item of current.keys()) {
|
|
@@ -512,98 +590,88 @@ export function generateTreeSync(
|
|
|
512
590
|
}
|
|
513
591
|
}
|
|
514
592
|
// Parse deferred items
|
|
515
|
-
for (
|
|
516
|
-
nodes.push(generateTreeSync(ctx,
|
|
593
|
+
for (let i = 0, len = deferred.length; i < len; i++) {
|
|
594
|
+
nodes.push(generateTreeSync(ctx, deferred[i]));
|
|
517
595
|
}
|
|
518
596
|
return [SerovalNodeType.Set, nodes, id];
|
|
519
597
|
}
|
|
520
598
|
if (constructorCheck<Map<ServerValue, ServerValue>>(current, Map)) {
|
|
521
|
-
|
|
522
|
-
const
|
|
599
|
+
assert(ctx.features.has('map'), 'Unsupported type "Map"');
|
|
600
|
+
const keyNodes: SerovalNode[] = [];
|
|
601
|
+
const valueNodes: SerovalNode[] = [];
|
|
602
|
+
const deferredKey: ServerValue[] = [];
|
|
603
|
+
const deferredValue: ServerValue[] = [];
|
|
604
|
+
let deferredSize = 0;
|
|
523
605
|
for (const [key, value] of current.entries()) {
|
|
524
606
|
// Either key or value might be an iterable
|
|
525
607
|
if (isIterable(key) || isIterable(value)) {
|
|
526
|
-
|
|
608
|
+
deferredKey.push(key);
|
|
609
|
+
deferredValue.push(value);
|
|
610
|
+
deferredSize++;
|
|
527
611
|
} else {
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
nodes.push([keyNode, valueNode]);
|
|
612
|
+
keyNodes.push(generateTreeSync(ctx, key));
|
|
613
|
+
valueNodes.push(generateTreeSync(ctx, value));
|
|
531
614
|
}
|
|
532
615
|
}
|
|
533
|
-
for (
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
nodes.push([keyNode, valueNode]);
|
|
616
|
+
for (let i = 0; i < deferredSize; i++) {
|
|
617
|
+
keyNodes.push(generateTreeSync(ctx, deferredKey[i]));
|
|
618
|
+
valueNodes.push(generateTreeSync(ctx, deferredValue[i]));
|
|
537
619
|
}
|
|
538
|
-
return [SerovalNodeType.Map,
|
|
620
|
+
return [SerovalNodeType.Map, [keyNodes, valueNodes, current.size], id];
|
|
539
621
|
}
|
|
540
622
|
if (Array.isArray(current)) {
|
|
541
|
-
const
|
|
542
|
-
const
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
if (
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
623
|
+
const size = current.length;
|
|
624
|
+
const nodes = new Array<SerovalNode>(size);
|
|
625
|
+
const deferred = new Array<ServerValue>(size);
|
|
626
|
+
for (let i = 0; i < size; i++) {
|
|
627
|
+
if (i in current) {
|
|
628
|
+
if (isIterable(current[i])) {
|
|
629
|
+
deferred[i] = current[i];
|
|
630
|
+
} else {
|
|
631
|
+
nodes[i] = generateTreeSync(ctx, current[i]);
|
|
632
|
+
}
|
|
549
633
|
}
|
|
550
634
|
}
|
|
551
|
-
for (
|
|
552
|
-
|
|
635
|
+
for (let i = 0; i < size; i++) {
|
|
636
|
+
if (i in deferred) {
|
|
637
|
+
nodes[i] = generateTreeSync(ctx, deferred[i]);
|
|
638
|
+
}
|
|
553
639
|
}
|
|
554
640
|
return [SerovalNodeType.Array, nodes, id];
|
|
555
641
|
}
|
|
556
|
-
if (current instanceof AggregateError) {
|
|
642
|
+
if (current instanceof AggregateError && ctx.features.has('aggregate-error')) {
|
|
557
643
|
const options = getErrorOptions(current);
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
cause: 'cause' in current
|
|
564
|
-
? generateTreeSync(ctx, { cause: current.cause as ServerValue })
|
|
565
|
-
: undefined,
|
|
566
|
-
errors: generateTreeSync(ctx, current.errors as ServerValue),
|
|
567
|
-
}, id];
|
|
644
|
+
const optionsNode = options
|
|
645
|
+
? serializeProperties(options)
|
|
646
|
+
: undefined;
|
|
647
|
+
const errorsNode = generateTreeSync(ctx, current.errors as ServerValue);
|
|
648
|
+
return [SerovalNodeType.AggregateError, [current.message, optionsNode, errorsNode], id];
|
|
568
649
|
}
|
|
569
650
|
if (current instanceof Error) {
|
|
570
651
|
const options = getErrorOptions(current);
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
652
|
+
const optionsNode = options
|
|
653
|
+
? serializeProperties(options)
|
|
654
|
+
: undefined;
|
|
655
|
+
return [
|
|
656
|
+
SerovalNodeType.Error,
|
|
657
|
+
[getErrorConstructor(current), current.message, optionsNode],
|
|
658
|
+
id,
|
|
659
|
+
];
|
|
579
660
|
}
|
|
580
661
|
if (isIterable(current)) {
|
|
662
|
+
assert(ctx.features.has('symbol-iterator'), 'Unsupported type "Iterable"');
|
|
581
663
|
const options = getIterableOptions(current);
|
|
582
|
-
return [SerovalNodeType.Iterable,
|
|
664
|
+
return [SerovalNodeType.Iterable, [
|
|
583
665
|
// Parse options first before the items
|
|
584
|
-
options:
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
items: generateTreeSync(ctx, Array.from(current)),
|
|
588
|
-
}, id];
|
|
666
|
+
options ? serializeProperties(options) : undefined,
|
|
667
|
+
generateTreeSync(ctx, Array.from(current)),
|
|
668
|
+
], id];
|
|
589
669
|
}
|
|
590
670
|
const empty = current.constructor == null;
|
|
591
671
|
if (current.constructor === Object || empty) {
|
|
592
|
-
const nodes: Record<string, SerovalNode> = {};
|
|
593
|
-
const deferred: Record<string, ServerValue> = {};
|
|
594
|
-
for (const [key, item] of Object.entries(current)) {
|
|
595
|
-
if (isIterable(item)) {
|
|
596
|
-
deferred[key] = item as ServerValue;
|
|
597
|
-
} else {
|
|
598
|
-
nodes[key] = generateTreeSync(ctx, item as ServerValue);
|
|
599
|
-
}
|
|
600
|
-
}
|
|
601
|
-
for (const [key, item] of Object.entries(deferred)) {
|
|
602
|
-
nodes[key] = generateTreeSync(ctx, item);
|
|
603
|
-
}
|
|
604
672
|
return [
|
|
605
673
|
empty ? SerovalNodeType.NullConstructor : SerovalNodeType.Object,
|
|
606
|
-
|
|
674
|
+
serializeProperties(current as Record<string, unknown>),
|
|
607
675
|
id,
|
|
608
676
|
];
|
|
609
677
|
}
|
|
@@ -614,6 +682,31 @@ export async function generateTreeAsync(
|
|
|
614
682
|
ctx: SerializationContext,
|
|
615
683
|
current: AsyncServerValue,
|
|
616
684
|
): Promise<SerovalNode> {
|
|
685
|
+
async function serializeProperties(
|
|
686
|
+
properties: Record<string, unknown>,
|
|
687
|
+
): Promise<SerovalDictionaryNode> {
|
|
688
|
+
const keyNodes: string[] = [];
|
|
689
|
+
const valueNodes: SerovalNode[] = [];
|
|
690
|
+
const deferredKeys: string[] = [];
|
|
691
|
+
const deferredValues: ServerValue[] = [];
|
|
692
|
+
const keys = Object.keys(properties);
|
|
693
|
+
let deferredSize = 0;
|
|
694
|
+
for (const key of keys) {
|
|
695
|
+
if (isIterable(properties[key])) {
|
|
696
|
+
deferredKeys.push(key);
|
|
697
|
+
deferredValues.push(properties[key] as ServerValue);
|
|
698
|
+
deferredSize++;
|
|
699
|
+
} else {
|
|
700
|
+
keyNodes.push(key);
|
|
701
|
+
valueNodes.push(await generateTreeAsync(ctx, properties[key] as ServerValue));
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
for (let i = 0; i < deferredSize; i++) {
|
|
705
|
+
keyNodes.push(deferredKeys[i]);
|
|
706
|
+
valueNodes.push(await generateTreeAsync(ctx, deferredValues[i]));
|
|
707
|
+
}
|
|
708
|
+
return [keyNodes, valueNodes, keys.length];
|
|
709
|
+
}
|
|
617
710
|
if (isPrimitive(current)) {
|
|
618
711
|
return [SerovalNodeType.Primitive, current];
|
|
619
712
|
}
|
|
@@ -621,20 +714,22 @@ export async function generateTreeAsync(
|
|
|
621
714
|
if (Array.isArray(id)) {
|
|
622
715
|
return id;
|
|
623
716
|
}
|
|
624
|
-
const semiPrimitive = generateSemiPrimitiveValue(current, id);
|
|
717
|
+
const semiPrimitive = generateSemiPrimitiveValue(ctx, current, id);
|
|
625
718
|
if (semiPrimitive) {
|
|
626
719
|
return semiPrimitive;
|
|
627
720
|
}
|
|
628
721
|
if (isPromise(current)) {
|
|
722
|
+
assert(ctx.features.has('promise'), 'Unsupported type "Promise"');
|
|
629
723
|
return current.then(async (value) => [
|
|
630
724
|
SerovalNodeType.Promise,
|
|
631
725
|
await generateTreeAsync(ctx, value),
|
|
632
726
|
id,
|
|
633
727
|
]);
|
|
634
728
|
}
|
|
635
|
-
if (constructorCheck<Set<
|
|
729
|
+
if (constructorCheck<Set<AsyncServerValue>>(current, Set)) {
|
|
730
|
+
assert(ctx.features.has('set'), 'Unsupported type "Set"');
|
|
636
731
|
const nodes: SerovalNode[] = [];
|
|
637
|
-
const deferred:
|
|
732
|
+
const deferred: AsyncServerValue[] = [];
|
|
638
733
|
for (const item of current.keys()) {
|
|
639
734
|
if (isIterable(item)) {
|
|
640
735
|
deferred.push(item);
|
|
@@ -642,109 +737,209 @@ export async function generateTreeAsync(
|
|
|
642
737
|
nodes.push(await generateTreeAsync(ctx, item));
|
|
643
738
|
}
|
|
644
739
|
}
|
|
645
|
-
for (
|
|
646
|
-
nodes.push(await generateTreeAsync(ctx,
|
|
740
|
+
for (let i = 0, len = deferred.length; i < len; i++) {
|
|
741
|
+
nodes.push(await generateTreeAsync(ctx, deferred[i]));
|
|
647
742
|
}
|
|
648
743
|
return [SerovalNodeType.Set, nodes, id];
|
|
649
744
|
}
|
|
650
|
-
if (constructorCheck<Map<
|
|
651
|
-
|
|
652
|
-
const
|
|
745
|
+
if (constructorCheck<Map<AsyncServerValue, AsyncServerValue>>(current, Map)) {
|
|
746
|
+
assert(ctx.features.has('map'), 'Unsupported type "Map"');
|
|
747
|
+
const keyNodes: SerovalNode[] = [];
|
|
748
|
+
const valueNodes: SerovalNode[] = [];
|
|
749
|
+
const deferredKey: AsyncServerValue[] = [];
|
|
750
|
+
const deferredValue: AsyncServerValue[] = [];
|
|
751
|
+
let deferredSize = 0;
|
|
653
752
|
for (const [key, value] of current.entries()) {
|
|
654
753
|
if (isIterable(key) || isIterable(value)) {
|
|
655
|
-
|
|
754
|
+
deferredKey.push(key);
|
|
755
|
+
deferredValue.push(value);
|
|
756
|
+
deferredSize++;
|
|
656
757
|
} else {
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
nodes.push([keyNode, valueNode]);
|
|
758
|
+
keyNodes.push(await generateTreeAsync(ctx, key));
|
|
759
|
+
valueNodes.push(await generateTreeAsync(ctx, value));
|
|
660
760
|
}
|
|
661
761
|
}
|
|
662
|
-
for (
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
nodes.push([keyNode, valueNode]);
|
|
762
|
+
for (let i = 0; i < deferredSize; i++) {
|
|
763
|
+
keyNodes.push(await generateTreeAsync(ctx, deferredKey[i]));
|
|
764
|
+
valueNodes.push(await generateTreeAsync(ctx, deferredValue[i]));
|
|
666
765
|
}
|
|
667
|
-
return [SerovalNodeType.Map,
|
|
766
|
+
return [SerovalNodeType.Map, [keyNodes, valueNodes, current.size], id];
|
|
668
767
|
}
|
|
669
768
|
if (Array.isArray(current)) {
|
|
670
|
-
const
|
|
671
|
-
const
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
769
|
+
const size = current.length;
|
|
770
|
+
const nodes = new Array<SerovalNode>(size);
|
|
771
|
+
const deferred = new Array<AsyncServerValue>(size);
|
|
772
|
+
for (let i = 0; i < size; i++) {
|
|
773
|
+
const item = current[i];
|
|
774
|
+
if (i in current) {
|
|
775
|
+
if (isIterable(item)) {
|
|
776
|
+
deferred[i] = item;
|
|
777
|
+
} else {
|
|
778
|
+
nodes[i] = await generateTreeAsync(ctx, item);
|
|
779
|
+
}
|
|
678
780
|
}
|
|
679
781
|
}
|
|
680
|
-
for (
|
|
681
|
-
|
|
782
|
+
for (let i = 0; i < size; i++) {
|
|
783
|
+
if (i in deferred) {
|
|
784
|
+
nodes[i] = await generateTreeAsync(ctx, deferred[i]);
|
|
785
|
+
}
|
|
682
786
|
}
|
|
683
787
|
return [SerovalNodeType.Array, nodes, id];
|
|
684
788
|
}
|
|
685
789
|
if (current instanceof AggregateError) {
|
|
686
790
|
const options = getErrorOptions(current);
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
cause: 'cause' in current
|
|
693
|
-
? await generateTreeAsync(ctx, { cause: current.cause as ServerValue })
|
|
694
|
-
: undefined,
|
|
695
|
-
errors: await generateTreeAsync(ctx, current.errors as ServerValue),
|
|
696
|
-
}, id];
|
|
791
|
+
const optionsNode = options
|
|
792
|
+
? await serializeProperties(options)
|
|
793
|
+
: undefined;
|
|
794
|
+
const errorsNode = await generateTreeAsync(ctx, current.errors as ServerValue);
|
|
795
|
+
return [SerovalNodeType.AggregateError, [current.message, optionsNode, errorsNode], id];
|
|
697
796
|
}
|
|
698
797
|
if (current instanceof Error) {
|
|
699
798
|
const options = getErrorOptions(current);
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
799
|
+
const optionsNode = options
|
|
800
|
+
? await serializeProperties(options)
|
|
801
|
+
: undefined;
|
|
802
|
+
return [
|
|
803
|
+
SerovalNodeType.Error,
|
|
804
|
+
[getErrorConstructor(current), current.message, optionsNode],
|
|
805
|
+
id,
|
|
806
|
+
];
|
|
708
807
|
}
|
|
709
808
|
if (isIterable(current)) {
|
|
809
|
+
assert(ctx.features.has('symbol-iterator'), 'Unsupported type "Iterable"');
|
|
710
810
|
const options = getIterableOptions(current);
|
|
711
|
-
return [SerovalNodeType.Iterable,
|
|
712
|
-
options
|
|
713
|
-
? await
|
|
811
|
+
return [SerovalNodeType.Iterable, [
|
|
812
|
+
options
|
|
813
|
+
? await serializeProperties(options)
|
|
714
814
|
: undefined,
|
|
715
|
-
|
|
716
|
-
|
|
815
|
+
await generateTreeAsync(ctx, Array.from(current)),
|
|
816
|
+
], id];
|
|
717
817
|
}
|
|
718
818
|
const empty = current.constructor == null;
|
|
719
819
|
if (current.constructor === Object || empty) {
|
|
720
|
-
const nodes: Record<string, SerovalNode> = {};
|
|
721
|
-
const deferred: Record<string, AsyncServerValue> = {};
|
|
722
|
-
for (const [key, item] of Object.entries(current)) {
|
|
723
|
-
if (isIterable(item)) {
|
|
724
|
-
deferred[key] = item as ServerValue;
|
|
725
|
-
} else {
|
|
726
|
-
nodes[key] = await generateTreeAsync(ctx, item as ServerValue);
|
|
727
|
-
}
|
|
728
|
-
}
|
|
729
|
-
for (const [key, item] of Object.entries(deferred)) {
|
|
730
|
-
nodes[key] = await generateTreeAsync(ctx, item);
|
|
731
|
-
}
|
|
732
820
|
return [
|
|
733
821
|
empty ? SerovalNodeType.NullConstructor : SerovalNodeType.Object,
|
|
734
|
-
|
|
822
|
+
await serializeProperties(current as Record<string, unknown>),
|
|
735
823
|
id,
|
|
736
824
|
];
|
|
737
825
|
}
|
|
738
826
|
throw new Error('Unsupported value');
|
|
739
827
|
}
|
|
740
828
|
|
|
829
|
+
export function serializePrimitive(
|
|
830
|
+
ctx: SerializationContext,
|
|
831
|
+
value: PrimitiveValue,
|
|
832
|
+
): string {
|
|
833
|
+
// Shortened forms
|
|
834
|
+
if (value === true) {
|
|
835
|
+
return '!0';
|
|
836
|
+
}
|
|
837
|
+
if (value === false) {
|
|
838
|
+
return '!1';
|
|
839
|
+
}
|
|
840
|
+
if (value === undefined) {
|
|
841
|
+
return 'void 0';
|
|
842
|
+
}
|
|
843
|
+
if (value === null) {
|
|
844
|
+
return 'null';
|
|
845
|
+
}
|
|
846
|
+
if (typeof value === 'bigint') {
|
|
847
|
+
assert(ctx.features.has('bigint'), 'Unsupported type "BigInt"');
|
|
848
|
+
return `${value}n`;
|
|
849
|
+
}
|
|
850
|
+
if (typeof value === 'string') {
|
|
851
|
+
return quote(value);
|
|
852
|
+
}
|
|
853
|
+
// negative 0 isn't the same as 0
|
|
854
|
+
if (Object.is(value, -0)) {
|
|
855
|
+
return '-0';
|
|
856
|
+
}
|
|
857
|
+
if (Object.is(value, Infinity)) {
|
|
858
|
+
return '1/0';
|
|
859
|
+
}
|
|
860
|
+
if (Object.is(value, -Infinity)) {
|
|
861
|
+
return '-1/0';
|
|
862
|
+
}
|
|
863
|
+
return String(value);
|
|
864
|
+
}
|
|
865
|
+
|
|
741
866
|
export function serializeTree(
|
|
742
867
|
ctx: SerializationContext,
|
|
743
868
|
[type, value, id]: SerovalNode,
|
|
744
869
|
): string {
|
|
870
|
+
function serializeAssignments(
|
|
871
|
+
targetRef: number,
|
|
872
|
+
[keys, values, size]: SerovalDictionaryNode,
|
|
873
|
+
) {
|
|
874
|
+
ctx.stack.push(targetRef);
|
|
875
|
+
const mainAssignments: Assignment[] = [];
|
|
876
|
+
for (let i = 0; i < size; i++) {
|
|
877
|
+
const parentStack = ctx.stack;
|
|
878
|
+
ctx.stack = [];
|
|
879
|
+
const refParam = serializeTree(ctx, values[i]);
|
|
880
|
+
ctx.stack = parentStack;
|
|
881
|
+
const key = keys[i];
|
|
882
|
+
const check = Number(key);
|
|
883
|
+
// Test if key is a valid number or JS identifier
|
|
884
|
+
// so that we don't have to serialize the key and wrap with brackets
|
|
885
|
+
const parentAssignment = ctx.assignments;
|
|
886
|
+
ctx.assignments = mainAssignments;
|
|
887
|
+
if (
|
|
888
|
+
check >= 0
|
|
889
|
+
|| (Number.isNaN(check) && /^([$A-Z_][0-9A-Z_$]*)$/i.test(key))
|
|
890
|
+
) {
|
|
891
|
+
if (!Number.isNaN(check)) {
|
|
892
|
+
createObjectComputedAssign(ctx, targetRef, key, refParam);
|
|
893
|
+
} else {
|
|
894
|
+
createObjectIdentifierAssign(ctx, targetRef, key, refParam);
|
|
895
|
+
}
|
|
896
|
+
} else {
|
|
897
|
+
createObjectComputedAssign(ctx, targetRef, quote(key), refParam);
|
|
898
|
+
}
|
|
899
|
+
ctx.assignments = parentAssignment;
|
|
900
|
+
}
|
|
901
|
+
ctx.stack.pop();
|
|
902
|
+
return resolveAssignments(mainAssignments);
|
|
903
|
+
}
|
|
904
|
+
function serializeObject(
|
|
905
|
+
sourceID: number,
|
|
906
|
+
[keys, values, size]: SerovalDictionaryNode,
|
|
907
|
+
) {
|
|
908
|
+
let result = '';
|
|
909
|
+
ctx.stack.push(sourceID);
|
|
910
|
+
for (let i = 0; i < size; i++) {
|
|
911
|
+
const key = keys[i];
|
|
912
|
+
const val = values[i];
|
|
913
|
+
const check = Number(key);
|
|
914
|
+
// Test if key is a valid number or JS identifier
|
|
915
|
+
// so that we don't have to serialize the key and wrap with brackets
|
|
916
|
+
if (
|
|
917
|
+
check >= 0
|
|
918
|
+
|| (Number.isNaN(check) && /^([$A-Z_][0-9A-Z_$]*)$/i.test(key))
|
|
919
|
+
) {
|
|
920
|
+
if (isReferenceInStack(ctx, val)) {
|
|
921
|
+
const refParam = getRefParam(ctx, val[1]);
|
|
922
|
+
if (!Number.isNaN(check)) {
|
|
923
|
+
createObjectComputedAssign(ctx, sourceID, key, refParam);
|
|
924
|
+
} else {
|
|
925
|
+
createObjectIdentifierAssign(ctx, sourceID, key, refParam);
|
|
926
|
+
}
|
|
927
|
+
} else {
|
|
928
|
+
result += `${key}:${serializeTree(ctx, val)},`;
|
|
929
|
+
}
|
|
930
|
+
} else if (isReferenceInStack(ctx, val)) {
|
|
931
|
+
const refParam = getRefParam(ctx, val[1]);
|
|
932
|
+
createObjectComputedAssign(ctx, sourceID, quote(key), refParam);
|
|
933
|
+
} else {
|
|
934
|
+
result += `${quote(key)}:${serializeTree(ctx, val)},`;
|
|
935
|
+
}
|
|
936
|
+
}
|
|
937
|
+
ctx.stack.pop();
|
|
938
|
+
return `{${result.substring(0, result.length - 1)}}`;
|
|
939
|
+
}
|
|
745
940
|
switch (type) {
|
|
746
941
|
case SerovalNodeType.Primitive:
|
|
747
|
-
return serializePrimitive(value);
|
|
942
|
+
return serializePrimitive(ctx, value);
|
|
748
943
|
case SerovalNodeType.Reference:
|
|
749
944
|
return getRefParam(ctx, value);
|
|
750
945
|
case SerovalNodeType.Promise: {
|
|
@@ -771,40 +966,48 @@ export function serializeTree(
|
|
|
771
966
|
return assignRef(ctx, id, String(value));
|
|
772
967
|
case SerovalNodeType.TypedArray: {
|
|
773
968
|
// BigInt typed arrays are broken for toString()
|
|
774
|
-
|
|
775
|
-
let
|
|
776
|
-
|
|
777
|
-
|
|
969
|
+
let values = '';
|
|
970
|
+
for (let i = 0, len = value[1].length; i < len; i++) {
|
|
971
|
+
values += `${serializePrimitive(ctx, value[1][i])},`;
|
|
972
|
+
}
|
|
973
|
+
let args = values ? `[${values.substring(0, values.length - 1)}]` : '[]';
|
|
974
|
+
if (value[1].byteOffset !== 0) {
|
|
975
|
+
args += `,${value[1].byteOffset}`;
|
|
778
976
|
}
|
|
779
|
-
return assignRef(ctx, id, `new ${value
|
|
977
|
+
return assignRef(ctx, id, `new ${value[0]}(${args})`);
|
|
780
978
|
}
|
|
781
979
|
case SerovalNodeType.Set: {
|
|
782
980
|
let serialized = 'new Set';
|
|
783
|
-
|
|
784
|
-
|
|
981
|
+
const size = value.length;
|
|
982
|
+
if (size) {
|
|
983
|
+
let result = '';
|
|
785
984
|
ctx.stack.push(id);
|
|
786
|
-
for (
|
|
985
|
+
for (let i = 0; i < size; i++) {
|
|
986
|
+
const item = value[i];
|
|
787
987
|
if (isReferenceInStack(ctx, item)) {
|
|
788
988
|
createSetAdd(ctx, id, getRefParam(ctx, item[1]));
|
|
789
989
|
} else {
|
|
790
990
|
// Push directly
|
|
791
|
-
|
|
991
|
+
result += `${serializeTree(ctx, item)},`;
|
|
792
992
|
}
|
|
793
993
|
}
|
|
794
994
|
ctx.stack.pop();
|
|
795
|
-
if (
|
|
796
|
-
serialized += `([${
|
|
995
|
+
if (result) {
|
|
996
|
+
serialized += `([${result.substring(0, result.length - 1)}])`;
|
|
797
997
|
}
|
|
798
998
|
}
|
|
799
999
|
return assignRef(ctx, id, serialized);
|
|
800
1000
|
}
|
|
801
1001
|
case SerovalNodeType.Map: {
|
|
802
1002
|
let serialized = 'new Map';
|
|
803
|
-
|
|
804
|
-
|
|
1003
|
+
const size = value[2];
|
|
1004
|
+
if (size) {
|
|
1005
|
+
let result = '';
|
|
805
1006
|
ctx.stack.push(id);
|
|
806
|
-
for (
|
|
1007
|
+
for (let i = 0; i < size; i++) {
|
|
807
1008
|
// Check if key is a parent
|
|
1009
|
+
const key = value[0][i];
|
|
1010
|
+
const val = value[1][i];
|
|
808
1011
|
if (isReferenceInStack(ctx, key)) {
|
|
809
1012
|
// Create reference for the map instance
|
|
810
1013
|
const keyRef = getRefParam(ctx, key[1]);
|
|
@@ -835,15 +1038,15 @@ export function serializeTree(
|
|
|
835
1038
|
createMapSet(ctx, id, serializeTree(ctx, key), valueRef);
|
|
836
1039
|
ctx.stack = parent;
|
|
837
1040
|
} else {
|
|
838
|
-
|
|
1041
|
+
result += `[${serializeTree(ctx, key)},${serializeTree(ctx, val)}],`;
|
|
839
1042
|
}
|
|
840
1043
|
}
|
|
841
1044
|
ctx.stack.pop();
|
|
842
1045
|
// Check if there are any values
|
|
843
1046
|
// so that the empty Map constructor
|
|
844
1047
|
// can be used instead
|
|
845
|
-
if (
|
|
846
|
-
serialized += `([${
|
|
1048
|
+
if (result) {
|
|
1049
|
+
serialized += `([${result.substring(0, result.length - 1)}])`;
|
|
847
1050
|
}
|
|
848
1051
|
}
|
|
849
1052
|
return assignRef(ctx, id, serialized);
|
|
@@ -852,10 +1055,10 @@ export function serializeTree(
|
|
|
852
1055
|
// This is different than Map and Set
|
|
853
1056
|
// because we also need to serialize
|
|
854
1057
|
// the holes of the Array
|
|
1058
|
+
const size = value.length;
|
|
855
1059
|
let values = '';
|
|
856
|
-
|
|
857
1060
|
ctx.stack.push(id);
|
|
858
|
-
for (let i = 0
|
|
1061
|
+
for (let i = 0; i < size; i++) {
|
|
859
1062
|
const item = value[i];
|
|
860
1063
|
// Check if index is a hole
|
|
861
1064
|
if (i in value) {
|
|
@@ -865,7 +1068,7 @@ export function serializeTree(
|
|
|
865
1068
|
values += ',';
|
|
866
1069
|
} else {
|
|
867
1070
|
values += serializeTree(ctx, item);
|
|
868
|
-
if (i <
|
|
1071
|
+
if (i < size - 1) {
|
|
869
1072
|
values += ',';
|
|
870
1073
|
}
|
|
871
1074
|
}
|
|
@@ -879,84 +1082,96 @@ export function serializeTree(
|
|
|
879
1082
|
}
|
|
880
1083
|
case SerovalNodeType.AggregateError: {
|
|
881
1084
|
// Serialize the required arguments
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
args.push(serializeTree(ctx, value.cause));
|
|
886
|
-
}
|
|
887
|
-
let serialized = `new AggregateError(${args.join(',')})`;
|
|
1085
|
+
ctx.stack.push(id);
|
|
1086
|
+
let serialized = `new AggregateError(${serializeTree(ctx, value[2])},${quote(value[0])})`;
|
|
1087
|
+
ctx.stack.pop();
|
|
888
1088
|
// `AggregateError` might've been extended
|
|
889
1089
|
// either through class or custom properties
|
|
890
1090
|
// Make sure to assign extra properties
|
|
891
|
-
if (value
|
|
892
|
-
|
|
893
|
-
|
|
1091
|
+
if (value[1]) {
|
|
1092
|
+
if (ctx.features.has('object-assign')) {
|
|
1093
|
+
const options = serializeObject(id, value[1]);
|
|
1094
|
+
serialized = `Object.assign(${serialized},${options})`;
|
|
1095
|
+
} else {
|
|
1096
|
+
markRef(ctx, id);
|
|
1097
|
+
const assignments = serializeAssignments(id, value[1]);
|
|
1098
|
+
if (assignments) {
|
|
1099
|
+
const ref = getRefParam(ctx, id);
|
|
1100
|
+
return `(${assignRef(ctx, id, serialized)},${assignments}${ref})`;
|
|
1101
|
+
}
|
|
1102
|
+
}
|
|
894
1103
|
}
|
|
895
1104
|
return assignRef(ctx, id, serialized);
|
|
896
1105
|
}
|
|
897
1106
|
case SerovalNodeType.Error: {
|
|
898
|
-
|
|
899
|
-
if (value
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
1107
|
+
let serialized = `new ${value[0]}(${quote(value[1])})`;
|
|
1108
|
+
if (value[2]) {
|
|
1109
|
+
if (ctx.features.has('object-assign')) {
|
|
1110
|
+
const options = serializeObject(id, value[2]);
|
|
1111
|
+
serialized = `Object.assign(${serialized},${options})`;
|
|
1112
|
+
} else {
|
|
1113
|
+
markRef(ctx, id);
|
|
1114
|
+
const assignments = serializeAssignments(id, value[2]);
|
|
1115
|
+
if (assignments) {
|
|
1116
|
+
const ref = getRefParam(ctx, id);
|
|
1117
|
+
return `(${assignRef(ctx, id, serialized)},${assignments}${ref})`;
|
|
1118
|
+
}
|
|
1119
|
+
}
|
|
906
1120
|
}
|
|
907
1121
|
return assignRef(ctx, id, serialized);
|
|
908
1122
|
}
|
|
909
1123
|
case SerovalNodeType.Iterable: {
|
|
910
1124
|
const parent = ctx.stack;
|
|
911
1125
|
ctx.stack = [];
|
|
912
|
-
const values = serializeTree(ctx, value
|
|
1126
|
+
const values = serializeTree(ctx, value[1]);
|
|
913
1127
|
ctx.stack = parent;
|
|
914
|
-
let serialized
|
|
915
|
-
if (
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
serialized = `Object.assign(${serialized},${options})`;
|
|
1128
|
+
let serialized: string;
|
|
1129
|
+
if (ctx.features.has('array-values')) {
|
|
1130
|
+
serialized = `${values}.values()`;
|
|
1131
|
+
} else {
|
|
1132
|
+
serialized = `${values}[Symbol.iterator]()`;
|
|
920
1133
|
}
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
if (
|
|
933
|
-
check >= 0
|
|
934
|
-
|| (Number.isNaN(check) && /^([$A-Z_][0-9A-Z_$]*)$/i.test(key))
|
|
935
|
-
) {
|
|
936
|
-
if (isReferenceInStack(ctx, val)) {
|
|
937
|
-
const refParam = getRefParam(ctx, val[1]);
|
|
938
|
-
if (!Number.isNaN(check)) {
|
|
939
|
-
createObjectComputedAssign(ctx, id, key, refParam);
|
|
940
|
-
} else {
|
|
941
|
-
createObjectIdentifierAssign(ctx, id, key, refParam);
|
|
942
|
-
}
|
|
943
|
-
} else {
|
|
944
|
-
values.push(`${key}:${serializeTree(ctx, val)}`);
|
|
945
|
-
}
|
|
946
|
-
} else if (isReferenceInStack(ctx, val)) {
|
|
947
|
-
const refParam = getRefParam(ctx, val[1]);
|
|
948
|
-
createObjectComputedAssign(ctx, id, quote(key), refParam);
|
|
1134
|
+
if (ctx.features.has('arrow-function')) {
|
|
1135
|
+
serialized = `{[Symbol.iterator]:()=>${serialized}}`;
|
|
1136
|
+
} else if (ctx.features.has('method-shorthand')) {
|
|
1137
|
+
serialized = `{[Symbol.iterator](){return ${serialized}}}`;
|
|
1138
|
+
} else {
|
|
1139
|
+
serialized = `{[Symbol.iterator]:function(){return ${serialized}}}`;
|
|
1140
|
+
}
|
|
1141
|
+
if (value[0]) {
|
|
1142
|
+
if (ctx.features.has('object-assign')) {
|
|
1143
|
+
const options = serializeObject(id, value[0]);
|
|
1144
|
+
serialized = `Object.assign(${serialized},${options})`;
|
|
949
1145
|
} else {
|
|
950
|
-
|
|
1146
|
+
markRef(ctx, id);
|
|
1147
|
+
const assignments = serializeAssignments(id, value[0]);
|
|
1148
|
+
if (assignments) {
|
|
1149
|
+
const ref = getRefParam(ctx, id);
|
|
1150
|
+
return `(${assignRef(ctx, id, serialized)},${assignments}${ref})`;
|
|
1151
|
+
}
|
|
951
1152
|
}
|
|
952
1153
|
}
|
|
953
|
-
ctx
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
1154
|
+
return assignRef(ctx, id, serialized);
|
|
1155
|
+
}
|
|
1156
|
+
case SerovalNodeType.NullConstructor: {
|
|
1157
|
+
let serialized = 'Object.create(null)';
|
|
1158
|
+
if (ctx.features.has('object-assign')) {
|
|
1159
|
+
const fields = serializeObject(id, value);
|
|
1160
|
+
if (fields !== '{}') {
|
|
1161
|
+
serialized = `Object.assign(${serialized},${fields})`;
|
|
1162
|
+
}
|
|
1163
|
+
} else {
|
|
1164
|
+
markRef(ctx, id);
|
|
1165
|
+
const assignments = serializeAssignments(id, value);
|
|
1166
|
+
if (assignments) {
|
|
1167
|
+
const ref = getRefParam(ctx, id);
|
|
1168
|
+
return `(${assignRef(ctx, id, serialized)},${assignments}${ref})`;
|
|
1169
|
+
}
|
|
957
1170
|
}
|
|
958
1171
|
return assignRef(ctx, id, serialized);
|
|
959
1172
|
}
|
|
1173
|
+
case SerovalNodeType.Object:
|
|
1174
|
+
return assignRef(ctx, id, serializeObject(id, value));
|
|
960
1175
|
default:
|
|
961
1176
|
throw new Error('Unsupported type');
|
|
962
1177
|
}
|