seroval 1.4.1 → 1.4.2

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 (50) hide show
  1. package/dist/cjs/development/index.cjs +54 -11
  2. package/dist/cjs/development/index.cjs.map +2 -2
  3. package/dist/cjs/production/index.cjs +2 -2
  4. package/dist/esm/development/index.mjs +54 -11
  5. package/dist/esm/development/index.mjs.map +2 -2
  6. package/dist/esm/production/index.mjs +2 -2
  7. package/dist/types/core/base-primitives.d.ts.map +1 -1
  8. package/dist/types/core/context/async-parser.d.ts.map +1 -1
  9. package/dist/types/core/context/deserializer.d.ts.map +1 -1
  10. package/dist/types/core/context/parser.d.ts.map +1 -1
  11. package/dist/types/core/context/sync-parser.d.ts.map +1 -1
  12. package/dist/types/core/literals.d.ts.map +1 -1
  13. package/dist/types/core/node.d.ts +1 -1
  14. package/dist/types/core/node.d.ts.map +1 -1
  15. package/dist/types/core/types.d.ts +4 -0
  16. package/dist/types/core/types.d.ts.map +1 -1
  17. package/package.json +3 -2
  18. package/src/core/Serializer.ts +119 -0
  19. package/src/core/base-primitives.ts +514 -0
  20. package/src/core/compat.ts +23 -0
  21. package/src/core/constants.ts +201 -0
  22. package/src/core/constructors.ts +270 -0
  23. package/src/core/context/async-parser.ts +638 -0
  24. package/src/core/context/deserializer.ts +810 -0
  25. package/src/core/context/parser.ts +335 -0
  26. package/src/core/context/serializer.ts +1406 -0
  27. package/src/core/context/sync-parser.ts +907 -0
  28. package/src/core/cross/index.ts +188 -0
  29. package/src/core/errors.ts +196 -0
  30. package/src/core/function-string.ts +32 -0
  31. package/src/core/keys.ts +17 -0
  32. package/src/core/literals.ts +43 -0
  33. package/src/core/node.ts +40 -0
  34. package/src/core/opaque-reference.ts +9 -0
  35. package/src/core/plugin.ts +100 -0
  36. package/src/core/reference.ts +66 -0
  37. package/src/core/special-reference.ts +42 -0
  38. package/src/core/stream.ts +71 -0
  39. package/src/core/string.ts +86 -0
  40. package/src/core/symbols.ts +18 -0
  41. package/src/core/tree/index.ts +134 -0
  42. package/src/core/types.ts +366 -0
  43. package/src/core/utils/error.ts +73 -0
  44. package/src/core/utils/get-identifier.ts +18 -0
  45. package/src/core/utils/get-object-flag.ts +14 -0
  46. package/src/core/utils/is-valid-identifier.ts +12 -0
  47. package/src/core/utils/iterator-to-sequence.ts +44 -0
  48. package/src/core/utils/promise-to-result.ts +9 -0
  49. package/src/core/utils/typed-array.ts +56 -0
  50. package/src/index.ts +36 -0
@@ -0,0 +1,1406 @@
1
+ import { Feature } from '../compat';
2
+ import {
3
+ CONSTANT_STRING,
4
+ ERROR_CONSTRUCTOR_STRING,
5
+ NIL,
6
+ SerovalNodeType,
7
+ SerovalObjectFlags,
8
+ SYMBOL_STRING,
9
+ } from '../constants';
10
+ import {
11
+ SERIALIZED_ASYNC_ITERATOR_CONSTRUCTOR,
12
+ SERIALIZED_ITERATOR_CONSTRUCTOR,
13
+ } from '../constructors';
14
+ import {
15
+ SerovalMissingPluginError,
16
+ SerovalSerializationError,
17
+ SerovalUnsupportedNodeError,
18
+ } from '../errors';
19
+ import { createEffectfulFunction, createFunction } from '../function-string';
20
+ import { GLOBAL_CONTEXT_REFERENCES, REFERENCES_KEY } from '../keys';
21
+ import type { PluginAccessOptions } from '../plugin';
22
+ import { SerovalMode } from '../plugin';
23
+ import { SPECIAL_REF_STRING } from '../special-reference';
24
+ import { serializeString } from '../string';
25
+ import type {
26
+ SerovalAggregateErrorNode,
27
+ SerovalArrayBufferNode,
28
+ SerovalArrayNode,
29
+ SerovalAsyncIteratorFactoryInstanceNode,
30
+ SerovalAsyncIteratorFactoryNode,
31
+ SerovalBigIntTypedArrayNode,
32
+ SerovalBoxedNode,
33
+ SerovalDataViewNode,
34
+ SerovalDateNode,
35
+ SerovalErrorNode,
36
+ SerovalIndexedValueNode,
37
+ SerovalIteratorFactoryInstanceNode,
38
+ SerovalIteratorFactoryNode,
39
+ SerovalMapNode,
40
+ SerovalNode,
41
+ SerovalNodeWithID,
42
+ SerovalNullConstructorNode,
43
+ SerovalObjectNode,
44
+ SerovalObjectRecordKey,
45
+ SerovalObjectRecordNode,
46
+ SerovalPluginNode,
47
+ SerovalPromiseConstructorNode,
48
+ SerovalPromiseNode,
49
+ SerovalPromiseRejectNode,
50
+ SerovalPromiseResolveNode,
51
+ SerovalReferenceNode,
52
+ SerovalRegExpNode,
53
+ SerovalSetNode,
54
+ SerovalStreamConstructorNode,
55
+ SerovalStreamNextNode,
56
+ SerovalStreamReturnNode,
57
+ SerovalStreamThrowNode,
58
+ SerovalTypedArrayNode,
59
+ } from '../types';
60
+ import getIdentifier from '../utils/get-identifier';
61
+ import { isValidIdentifier } from '../utils/is-valid-identifier';
62
+
63
+ const enum AssignmentType {
64
+ Index = 0,
65
+ Add = 1,
66
+ Set = 2,
67
+ Delete = 3,
68
+ }
69
+
70
+ interface IndexAssignment {
71
+ t: AssignmentType.Index;
72
+ s: string;
73
+ k: undefined;
74
+ v: string;
75
+ }
76
+
77
+ interface SetAssignment {
78
+ t: AssignmentType.Set;
79
+ s: string;
80
+ k: string;
81
+ v: string;
82
+ }
83
+
84
+ interface AddAssignment {
85
+ t: AssignmentType.Add;
86
+ s: string;
87
+ k: undefined;
88
+ v: string;
89
+ }
90
+
91
+ interface DeleteAssignment {
92
+ t: AssignmentType.Delete;
93
+ s: string;
94
+ k: string;
95
+ v: undefined;
96
+ }
97
+
98
+ // Array of assignments to be done (used for recursion)
99
+ type Assignment =
100
+ | IndexAssignment
101
+ | AddAssignment
102
+ | SetAssignment
103
+ | DeleteAssignment;
104
+
105
+ export interface FlaggedObject {
106
+ type: SerovalObjectFlags;
107
+ value: string;
108
+ }
109
+
110
+ function getAssignmentExpression(assignment: Assignment): string {
111
+ switch (assignment.t) {
112
+ case AssignmentType.Index:
113
+ return assignment.s + '=' + assignment.v;
114
+ case AssignmentType.Set:
115
+ return assignment.s + '.set(' + assignment.k + ',' + assignment.v + ')';
116
+ case AssignmentType.Add:
117
+ return assignment.s + '.add(' + assignment.v + ')';
118
+ case AssignmentType.Delete:
119
+ return assignment.s + '.delete(' + assignment.k + ')';
120
+ }
121
+ }
122
+
123
+ function mergeAssignments(assignments: Assignment[]): Assignment[] {
124
+ const newAssignments: Assignment[] = [];
125
+ let current = assignments[0];
126
+ for (
127
+ let i = 1, len = assignments.length, item: Assignment, prev = current;
128
+ i < len;
129
+ i++
130
+ ) {
131
+ item = assignments[i];
132
+ if (item.t === AssignmentType.Index && item.v === prev.v) {
133
+ // Merge if the right-hand value is the same
134
+ // saves at least 2 chars
135
+ current = {
136
+ t: AssignmentType.Index,
137
+ s: item.s,
138
+ k: NIL,
139
+ v: getAssignmentExpression(current),
140
+ } as IndexAssignment;
141
+ } else if (item.t === AssignmentType.Set && item.s === prev.s) {
142
+ // Maps has chaining methods, merge if source is the same
143
+ current = {
144
+ t: AssignmentType.Set,
145
+ s: getAssignmentExpression(current),
146
+ k: item.k,
147
+ v: item.v,
148
+ } as SetAssignment;
149
+ } else if (item.t === AssignmentType.Add && item.s === prev.s) {
150
+ // Sets has chaining methods too
151
+ current = {
152
+ t: AssignmentType.Add,
153
+ s: getAssignmentExpression(current),
154
+ k: NIL,
155
+ v: item.v,
156
+ } as AddAssignment;
157
+ } else if (item.t === AssignmentType.Delete && item.s === prev.s) {
158
+ // Maps has chaining methods, merge if source is the same
159
+ current = {
160
+ t: AssignmentType.Delete,
161
+ s: getAssignmentExpression(current),
162
+ k: item.k,
163
+ v: NIL,
164
+ } as DeleteAssignment;
165
+ } else {
166
+ // Different assignment, push current
167
+ newAssignments.push(current);
168
+ current = item;
169
+ }
170
+ prev = item;
171
+ }
172
+
173
+ newAssignments.push(current);
174
+
175
+ return newAssignments;
176
+ }
177
+
178
+ function resolveAssignments(assignments: Assignment[]): string | undefined {
179
+ if (assignments.length) {
180
+ let result = '';
181
+ const merged = mergeAssignments(assignments);
182
+ for (let i = 0, len = merged.length; i < len; i++) {
183
+ result += getAssignmentExpression(merged[i]) + ',';
184
+ }
185
+ return result;
186
+ }
187
+ return NIL;
188
+ }
189
+
190
+ const NULL_CONSTRUCTOR = 'Object.create(null)';
191
+ const SET_CONSTRUCTOR = 'new Set';
192
+ const MAP_CONSTRUCTOR = 'new Map';
193
+
194
+ const PROMISE_RESOLVE = 'Promise.resolve';
195
+ const PROMISE_REJECT = 'Promise.reject';
196
+
197
+ const OBJECT_FLAG_CONSTRUCTOR: Record<SerovalObjectFlags, string | undefined> =
198
+ {
199
+ [SerovalObjectFlags.Frozen]: 'Object.freeze',
200
+ [SerovalObjectFlags.Sealed]: 'Object.seal',
201
+ [SerovalObjectFlags.NonExtensible]: 'Object.preventExtensions',
202
+ [SerovalObjectFlags.None]: NIL,
203
+ };
204
+
205
+ type SerovalNodeWithProperties =
206
+ | SerovalObjectNode
207
+ | SerovalNullConstructorNode
208
+ | SerovalAggregateErrorNode
209
+ | SerovalErrorNode;
210
+
211
+ export interface BaseSerializerContextOptions extends PluginAccessOptions {
212
+ features: number;
213
+ markedRefs: number[] | Set<number>;
214
+ }
215
+
216
+ export interface BaseSerializerContext extends PluginAccessOptions {
217
+ readonly mode: SerovalMode;
218
+
219
+ features: number;
220
+ /*
221
+ * To check if an object is synchronously referencing itself
222
+ */
223
+ stack: number[];
224
+ /**
225
+ * Array of object mutations
226
+ */
227
+ flags: FlaggedObject[];
228
+ /**
229
+ * Array of assignments to be done (used for recursion)
230
+ */
231
+ assignments: Assignment[];
232
+ /**
233
+ * Refs that are...referenced
234
+ */
235
+ marked: Set<number>;
236
+ }
237
+
238
+ export interface CrossContextOptions {
239
+ scopeId?: string;
240
+ }
241
+
242
+ export function createBaseSerializerContext(
243
+ mode: SerovalMode,
244
+ options: BaseSerializerContextOptions,
245
+ ): BaseSerializerContext {
246
+ return {
247
+ mode,
248
+ plugins: options.plugins,
249
+ features: options.features,
250
+ marked: new Set(options.markedRefs),
251
+ stack: [],
252
+ flags: [],
253
+ assignments: [],
254
+ };
255
+ }
256
+
257
+ export interface VanillaSerializerState {
258
+ valid: Map<number, number>;
259
+ vars: string[];
260
+ }
261
+
262
+ function createVanillaSerializerState(): VanillaSerializerState {
263
+ return {
264
+ valid: new Map(),
265
+ vars: [],
266
+ };
267
+ }
268
+
269
+ export interface VanillaSerializerContext {
270
+ mode: SerovalMode.Vanilla;
271
+ base: BaseSerializerContext;
272
+ state: VanillaSerializerState;
273
+ child: SerializePluginContext | undefined;
274
+ }
275
+
276
+ export type VanillaSerializerContextOptions = BaseSerializerContextOptions;
277
+
278
+ export function createVanillaSerializerContext(
279
+ options: VanillaSerializerContextOptions,
280
+ ): VanillaSerializerContext {
281
+ return {
282
+ mode: SerovalMode.Vanilla,
283
+ base: createBaseSerializerContext(SerovalMode.Vanilla, options),
284
+ state: createVanillaSerializerState(),
285
+ child: NIL,
286
+ };
287
+ }
288
+
289
+ export interface CrossSerializerContext {
290
+ mode: SerovalMode.Cross;
291
+ base: BaseSerializerContext;
292
+ state: CrossContextOptions;
293
+ child: SerializePluginContext | undefined;
294
+ }
295
+
296
+ export interface CrossSerializerContextOptions
297
+ extends BaseSerializerContextOptions,
298
+ CrossContextOptions {
299
+ // empty
300
+ }
301
+
302
+ export function createCrossSerializerContext(
303
+ options: CrossSerializerContextOptions,
304
+ ): CrossSerializerContext {
305
+ return {
306
+ mode: SerovalMode.Cross,
307
+ base: createBaseSerializerContext(SerovalMode.Cross, options),
308
+ state: options,
309
+ child: NIL,
310
+ };
311
+ }
312
+
313
+ type SerializerContext = VanillaSerializerContext | CrossSerializerContext;
314
+
315
+ export class SerializePluginContext {
316
+ constructor(private _p: SerializerContext) {}
317
+
318
+ serialize(node: SerovalNode) {
319
+ return serialize(this._p, node);
320
+ }
321
+ }
322
+
323
+ /**
324
+ * Creates the reference param (identifier) from the given reference ID
325
+ * Calling this function means the value has been referenced somewhere
326
+ */
327
+ function getVanillaRefParam(
328
+ state: VanillaSerializerState,
329
+ index: number,
330
+ ): string {
331
+ /**
332
+ * Creates a new reference ID from a given reference ID
333
+ * This new reference ID means that the reference itself
334
+ * has been referenced at least once, and is used to generate
335
+ * the variables
336
+ */
337
+ let actualIndex = state.valid.get(index);
338
+ if (actualIndex == null) {
339
+ actualIndex = state.valid.size;
340
+ state.valid.set(index, actualIndex);
341
+ }
342
+ let identifier = state.vars[actualIndex];
343
+ if (identifier == null) {
344
+ identifier = getIdentifier(actualIndex);
345
+ state.vars[actualIndex] = identifier;
346
+ }
347
+ return identifier;
348
+ }
349
+
350
+ function getCrossRefParam(id: number): string {
351
+ return GLOBAL_CONTEXT_REFERENCES + '[' + id + ']';
352
+ }
353
+
354
+ /**
355
+ * Converts the ID of a reference into a identifier string
356
+ * that is used to refer to the object instance in the
357
+ * generated script.
358
+ */
359
+ function getRefParam(ctx: SerializerContext, id: number): string {
360
+ return ctx.mode === SerovalMode.Vanilla
361
+ ? getVanillaRefParam(ctx.state, id)
362
+ : getCrossRefParam(id);
363
+ }
364
+
365
+ function markSerializerRef(ctx: BaseSerializerContext, id: number): void {
366
+ ctx.marked.add(id);
367
+ }
368
+
369
+ function isSerializerRefMarked(
370
+ ctx: BaseSerializerContext,
371
+ id: number,
372
+ ): boolean {
373
+ return ctx.marked.has(id);
374
+ }
375
+
376
+ function pushObjectFlag(
377
+ ctx: SerializerContext,
378
+ flag: SerovalObjectFlags,
379
+ id: number,
380
+ ): void {
381
+ if (flag !== SerovalObjectFlags.None) {
382
+ markSerializerRef(ctx.base, id);
383
+ ctx.base.flags.push({
384
+ type: flag,
385
+ value: getRefParam(ctx, id),
386
+ });
387
+ }
388
+ }
389
+
390
+ function resolveFlags(ctx: BaseSerializerContext): string | undefined {
391
+ let result = '';
392
+ for (let i = 0, current = ctx.flags, len = current.length; i < len; i++) {
393
+ const flag = current[i];
394
+ result += OBJECT_FLAG_CONSTRUCTOR[flag.type] + '(' + flag.value + '),';
395
+ }
396
+ return result;
397
+ }
398
+
399
+ function resolvePatches(ctx: BaseSerializerContext): string | undefined {
400
+ const assignments = resolveAssignments(ctx.assignments);
401
+ const flags = resolveFlags(ctx);
402
+ if (assignments) {
403
+ if (flags) {
404
+ return assignments + flags;
405
+ }
406
+ return assignments;
407
+ }
408
+ return flags;
409
+ }
410
+
411
+ /**
412
+ * Generates the inlined assignment for the reference
413
+ * This is different from the assignments array as this one
414
+ * signifies creation rather than mutation
415
+ */
416
+ function createAssignment(
417
+ ctx: BaseSerializerContext,
418
+ source: string,
419
+ value: string,
420
+ ): void {
421
+ ctx.assignments.push({
422
+ t: AssignmentType.Index,
423
+ s: source,
424
+ k: NIL,
425
+ v: value,
426
+ });
427
+ }
428
+
429
+ function createAddAssignment(
430
+ ctx: SerializerContext,
431
+ ref: number,
432
+ value: string,
433
+ ): void {
434
+ ctx.base.assignments.push({
435
+ t: AssignmentType.Add,
436
+ s: getRefParam(ctx, ref),
437
+ k: NIL,
438
+ v: value,
439
+ });
440
+ }
441
+
442
+ function createSetAssignment(
443
+ ctx: SerializerContext,
444
+ ref: number,
445
+ key: string,
446
+ value: string,
447
+ ): void {
448
+ ctx.base.assignments.push({
449
+ t: AssignmentType.Set,
450
+ s: getRefParam(ctx, ref),
451
+ k: key,
452
+ v: value,
453
+ });
454
+ }
455
+
456
+ function createDeleteAssignment(
457
+ ctx: SerializerContext,
458
+ ref: number,
459
+ key: string,
460
+ ): void {
461
+ ctx.base.assignments.push({
462
+ t: AssignmentType.Delete,
463
+ s: getRefParam(ctx, ref),
464
+ k: key,
465
+ v: NIL,
466
+ });
467
+ }
468
+
469
+ function createArrayAssign(
470
+ ctx: SerializerContext,
471
+ ref: number,
472
+ index: number | string,
473
+ value: string,
474
+ ): void {
475
+ createAssignment(ctx.base, getRefParam(ctx, ref) + '[' + index + ']', value);
476
+ }
477
+
478
+ function createObjectAssign(
479
+ ctx: SerializerContext,
480
+ ref: number,
481
+ key: string,
482
+ value: string,
483
+ ): void {
484
+ createAssignment(ctx.base, getRefParam(ctx, ref) + '.' + key, value);
485
+ }
486
+
487
+ /**
488
+ * Checks if the value is in the stack. Stack here is a reference
489
+ * structure to know if a object is to be accessed in a TDZ.
490
+ */
491
+ function isIndexedValueInStack(
492
+ ctx: BaseSerializerContext,
493
+ node: SerovalNode,
494
+ ): boolean {
495
+ return node.t === SerovalNodeType.IndexedValue && ctx.stack.includes(node.i);
496
+ }
497
+
498
+ /**
499
+ * Produces an assignment expression. `id` generates a reference
500
+ * parameter (through `getRefParam`) and has the option to
501
+ * return the reference parameter directly or assign a value to
502
+ * it.
503
+ */
504
+ function assignIndexedValue(
505
+ ctx: SerializerContext,
506
+ index: number,
507
+ value: string,
508
+ ): string {
509
+ if (
510
+ ctx.mode === SerovalMode.Vanilla &&
511
+ !isSerializerRefMarked(ctx.base, index)
512
+ ) {
513
+ return value;
514
+ }
515
+ /**
516
+ * In cross-reference, we have to assume that
517
+ * every reference are going to be referenced
518
+ * in the future, and so we need to store
519
+ * all of it into the reference array.
520
+ *
521
+ * otherwise in vanilla, we only do this if it
522
+ * is actually referenced
523
+ */
524
+ return getRefParam(ctx, index) + '=' + value;
525
+ }
526
+
527
+ function serializeReference(node: SerovalReferenceNode): string {
528
+ return REFERENCES_KEY + '.get("' + node.s + '")';
529
+ }
530
+
531
+ function serializeArrayItem(
532
+ ctx: SerializerContext,
533
+ id: number,
534
+ item: SerovalNode | 0,
535
+ index: number,
536
+ ): string {
537
+ // Check if index is a hole
538
+ if (item) {
539
+ // Check if item is a parent
540
+ if (isIndexedValueInStack(ctx.base, item)) {
541
+ markSerializerRef(ctx.base, id);
542
+ createArrayAssign(
543
+ ctx,
544
+ id,
545
+ index,
546
+ getRefParam(ctx, (item as SerovalIndexedValueNode).i),
547
+ );
548
+ return '';
549
+ }
550
+ return serialize(ctx, item);
551
+ }
552
+ return '';
553
+ }
554
+
555
+ function serializeArray(
556
+ ctx: SerializerContext,
557
+ node: SerovalArrayNode,
558
+ ): string {
559
+ const id = node.i;
560
+ const list = node.a;
561
+ const len = list.length;
562
+ if (len > 0) {
563
+ ctx.base.stack.push(id);
564
+ let values = serializeArrayItem(ctx, id, list[0], 0);
565
+ // This is different than Map and Set
566
+ // because we also need to serialize
567
+ // the holes of the Array
568
+ let isHoley = values === '';
569
+ for (let i = 1, item: string; i < len; i++) {
570
+ item = serializeArrayItem(ctx, id, list[i], i);
571
+ values += ',' + item;
572
+ isHoley = item === '';
573
+ }
574
+ ctx.base.stack.pop();
575
+ pushObjectFlag(ctx, node.o, node.i);
576
+ return '[' + values + (isHoley ? ',]' : ']');
577
+ }
578
+ return '[]';
579
+ }
580
+
581
+ function serializeProperty(
582
+ ctx: SerializerContext,
583
+ source: SerovalNodeWithProperties,
584
+ key: SerovalObjectRecordKey,
585
+ val: SerovalNode,
586
+ ): string {
587
+ if (typeof key === 'string') {
588
+ const check = Number(key);
589
+ const isIdentifier =
590
+ // Test if key is a valid positive number or JS identifier
591
+ // so that we don't have to serialize the key and wrap with brackets
592
+ (check >= 0 &&
593
+ // It's also important to consider that if the key is
594
+ // indeed numeric, we need to make sure that when
595
+ // converted back into a string, it's still the same
596
+ // to the original key. This allows us to differentiate
597
+ // keys that has numeric formats but in a different
598
+ // format, which can cause unintentional key declaration
599
+ // Example: { 0x1: 1 } vs { '0x1': 1 }
600
+ check.toString() === key) ||
601
+ isValidIdentifier(key);
602
+ if (isIndexedValueInStack(ctx.base, val)) {
603
+ const refParam = getRefParam(ctx, (val as SerovalIndexedValueNode).i);
604
+ markSerializerRef(ctx.base, source.i);
605
+ // Strict identifier check, make sure
606
+ // that it isn't numeric (except NaN)
607
+ if (isIdentifier && check !== check) {
608
+ createObjectAssign(ctx, source.i, key, refParam);
609
+ } else {
610
+ createArrayAssign(
611
+ ctx,
612
+ source.i,
613
+ isIdentifier ? key : '"' + key + '"',
614
+ refParam,
615
+ );
616
+ }
617
+ return '';
618
+ }
619
+ return (isIdentifier ? key : '"' + key + '"') + ':' + serialize(ctx, val);
620
+ }
621
+ return '[' + serialize(ctx, key) + ']:' + serialize(ctx, val);
622
+ }
623
+
624
+ function serializeProperties(
625
+ ctx: SerializerContext,
626
+ source: SerovalNodeWithProperties,
627
+ record: SerovalObjectRecordNode,
628
+ ): string {
629
+ const keys = record.k;
630
+ const len = keys.length;
631
+ if (len > 0) {
632
+ const values = record.v;
633
+ ctx.base.stack.push(source.i);
634
+ let result = serializeProperty(ctx, source, keys[0], values[0]);
635
+ for (let i = 1, item = result; i < len; i++) {
636
+ item = serializeProperty(ctx, source, keys[i], values[i]);
637
+ result += (item && result && ',') + item;
638
+ }
639
+ ctx.base.stack.pop();
640
+ return '{' + result + '}';
641
+ }
642
+ return '{}';
643
+ }
644
+
645
+ function serializeObject(
646
+ ctx: SerializerContext,
647
+ node: SerovalObjectNode,
648
+ ): string {
649
+ pushObjectFlag(ctx, node.o, node.i);
650
+ return serializeProperties(ctx, node, node.p);
651
+ }
652
+
653
+ function serializeWithObjectAssign(
654
+ ctx: SerializerContext,
655
+ source: SerovalNodeWithProperties,
656
+ value: SerovalObjectRecordNode,
657
+ serialized: string,
658
+ ): string {
659
+ const fields = serializeProperties(ctx, source, value);
660
+ if (fields !== '{}') {
661
+ return 'Object.assign(' + serialized + ',' + fields + ')';
662
+ }
663
+ return serialized;
664
+ }
665
+
666
+ function serializeStringKeyAssignment(
667
+ ctx: SerializerContext,
668
+ source: SerovalNodeWithProperties,
669
+ mainAssignments: Assignment[],
670
+ key: string,
671
+ value: SerovalNode,
672
+ ): void {
673
+ const base = ctx.base;
674
+ const serialized = serialize(ctx, value);
675
+ const check = Number(key);
676
+ const isIdentifier =
677
+ // Test if key is a valid positive number or JS identifier
678
+ // so that we don't have to serialize the key and wrap with brackets
679
+ (check >= 0 &&
680
+ // It's also important to consider that if the key is
681
+ // indeed numeric, we need to make sure that when
682
+ // converted back into a string, it's still the same
683
+ // to the original key. This allows us to differentiate
684
+ // keys that has numeric formats but in a different
685
+ // format, which can cause unintentional key declaration
686
+ // Example: { 0x1: 1 } vs { '0x1': 1 }
687
+ check.toString() === key) ||
688
+ isValidIdentifier(key);
689
+ if (isIndexedValueInStack(base, value)) {
690
+ // Strict identifier check, make sure
691
+ // that it isn't numeric (except NaN)
692
+ if (isIdentifier && check !== check) {
693
+ createObjectAssign(ctx, source.i, key, serialized);
694
+ } else {
695
+ createArrayAssign(
696
+ ctx,
697
+ source.i,
698
+ isIdentifier ? key : '"' + key + '"',
699
+ serialized,
700
+ );
701
+ }
702
+ } else {
703
+ const parentAssignment = base.assignments;
704
+ base.assignments = mainAssignments;
705
+ if (isIdentifier && check !== check) {
706
+ createObjectAssign(ctx, source.i, key, serialized);
707
+ } else {
708
+ createArrayAssign(
709
+ ctx,
710
+ source.i,
711
+ isIdentifier ? key : '"' + key + '"',
712
+ serialized,
713
+ );
714
+ }
715
+ base.assignments = parentAssignment;
716
+ }
717
+ }
718
+
719
+ function serializeAssignment(
720
+ ctx: SerializerContext,
721
+ source: SerovalNodeWithProperties,
722
+ mainAssignments: Assignment[],
723
+ key: SerovalObjectRecordKey,
724
+ value: SerovalNode,
725
+ ): void {
726
+ if (typeof key === 'string') {
727
+ serializeStringKeyAssignment(ctx, source, mainAssignments, key, value);
728
+ } else {
729
+ const base = ctx.base;
730
+ const parent = base.stack;
731
+ base.stack = [];
732
+ const serialized = serialize(ctx, value);
733
+ base.stack = parent;
734
+ const parentAssignment = base.assignments;
735
+ base.assignments = mainAssignments;
736
+ createArrayAssign(ctx, source.i, serialize(ctx, key), serialized);
737
+ base.assignments = parentAssignment;
738
+ }
739
+ }
740
+
741
+ function serializeAssignments(
742
+ ctx: SerializerContext,
743
+ source: SerovalNodeWithProperties,
744
+ node: SerovalObjectRecordNode,
745
+ ): string | undefined {
746
+ const keys = node.k;
747
+ const len = keys.length;
748
+ if (len > 0) {
749
+ const mainAssignments: Assignment[] = [];
750
+ const values = node.v;
751
+ ctx.base.stack.push(source.i);
752
+ for (let i = 0; i < len; i++) {
753
+ serializeAssignment(ctx, source, mainAssignments, keys[i], values[i]);
754
+ }
755
+ ctx.base.stack.pop();
756
+ return resolveAssignments(mainAssignments);
757
+ }
758
+ return NIL;
759
+ }
760
+
761
+ function serializeDictionary(
762
+ ctx: SerializerContext,
763
+ node: SerovalNodeWithProperties,
764
+ init: string,
765
+ ): string {
766
+ if (node.p) {
767
+ const base = ctx.base;
768
+ if (base.features & Feature.ObjectAssign) {
769
+ init = serializeWithObjectAssign(ctx, node, node.p, init);
770
+ } else {
771
+ markSerializerRef(base, node.i);
772
+ const assignments = serializeAssignments(ctx, node, node.p);
773
+ if (assignments) {
774
+ return (
775
+ '(' +
776
+ assignIndexedValue(ctx, node.i, init) +
777
+ ',' +
778
+ assignments +
779
+ getRefParam(ctx, node.i) +
780
+ ')'
781
+ );
782
+ }
783
+ }
784
+ }
785
+ return init;
786
+ }
787
+
788
+ function serializeNullConstructor(
789
+ ctx: SerializerContext,
790
+ node: SerovalNullConstructorNode,
791
+ ): string {
792
+ pushObjectFlag(ctx, node.o, node.i);
793
+ return serializeDictionary(ctx, node, NULL_CONSTRUCTOR);
794
+ }
795
+
796
+ function serializeDate(node: SerovalDateNode): string {
797
+ return 'new Date("' + node.s + '")';
798
+ }
799
+
800
+ function serializeRegExp(
801
+ ctx: SerializerContext,
802
+ node: SerovalRegExpNode,
803
+ ): string {
804
+ if (ctx.base.features & Feature.RegExp) {
805
+ return '/' + node.c + '/' + node.m;
806
+ }
807
+ throw new SerovalUnsupportedNodeError(node);
808
+ }
809
+
810
+ function serializeSetItem(
811
+ ctx: SerializerContext,
812
+ id: number,
813
+ item: SerovalNode,
814
+ ): string {
815
+ const base = ctx.base;
816
+ if (isIndexedValueInStack(base, item)) {
817
+ markSerializerRef(base, id);
818
+ createAddAssignment(
819
+ ctx,
820
+ id,
821
+ getRefParam(ctx, (item as SerovalIndexedValueNode).i),
822
+ );
823
+ return '';
824
+ }
825
+ return serialize(ctx, item);
826
+ }
827
+
828
+ function serializeSet(ctx: SerializerContext, node: SerovalSetNode): string {
829
+ let serialized = SET_CONSTRUCTOR;
830
+ const items = node.a;
831
+ const size = items.length;
832
+ const id = node.i;
833
+ if (size > 0) {
834
+ ctx.base.stack.push(id);
835
+ let result = serializeSetItem(ctx, id, items[0]);
836
+ for (let i = 1, item = result; i < size; i++) {
837
+ item = serializeSetItem(ctx, id, items[i]);
838
+ result += (item && result && ',') + item;
839
+ }
840
+ ctx.base.stack.pop();
841
+ if (result) {
842
+ serialized += '([' + result + '])';
843
+ }
844
+ }
845
+ return serialized;
846
+ }
847
+
848
+ function serializeMapEntry(
849
+ ctx: SerializerContext,
850
+ id: number,
851
+ key: SerovalNode,
852
+ val: SerovalNode,
853
+ sentinel: string,
854
+ ): string {
855
+ const base = ctx.base;
856
+ if (isIndexedValueInStack(base, key)) {
857
+ // Create reference for the map instance
858
+ const keyRef = getRefParam(ctx, (key as SerovalIndexedValueNode).i);
859
+ markSerializerRef(base, id);
860
+ // Check if value is a parent
861
+ if (isIndexedValueInStack(base, val)) {
862
+ const valueRef = getRefParam(ctx, (val as SerovalIndexedValueNode).i);
863
+ // Register an assignment since
864
+ // both key and value are a parent of this
865
+ // Map instance
866
+ createSetAssignment(ctx, id, keyRef, valueRef);
867
+ return '';
868
+ }
869
+ // Reset the stack
870
+ // This is required because the serialized
871
+ // value is no longer part of the expression
872
+ // tree and has been moved to the deferred
873
+ // assignment
874
+ if (
875
+ val.t !== SerovalNodeType.IndexedValue &&
876
+ val.i != null &&
877
+ isSerializerRefMarked(base, val.i)
878
+ ) {
879
+ // We use a trick here using sequence (or comma) expressions
880
+ // basically we serialize the intended object in place WITHOUT
881
+ // actually returning it, this is by returning a placeholder
882
+ // value that we will remove sometime after.
883
+ const serialized =
884
+ '(' + serialize(ctx, val) + ',[' + sentinel + ',' + sentinel + '])';
885
+ createSetAssignment(ctx, id, keyRef, getRefParam(ctx, val.i));
886
+ createDeleteAssignment(ctx, id, sentinel);
887
+ return serialized;
888
+ }
889
+ const parent = base.stack;
890
+ base.stack = [];
891
+ createSetAssignment(ctx, id, keyRef, serialize(ctx, val));
892
+ base.stack = parent;
893
+ return '';
894
+ }
895
+ if (isIndexedValueInStack(base, val)) {
896
+ // Create ref for the Map instance
897
+ const valueRef = getRefParam(ctx, (val as SerovalIndexedValueNode).i);
898
+ markSerializerRef(base, id);
899
+ if (
900
+ key.t !== SerovalNodeType.IndexedValue &&
901
+ key.i != null &&
902
+ isSerializerRefMarked(base, key.i)
903
+ ) {
904
+ const serialized =
905
+ '(' + serialize(ctx, key) + ',[' + sentinel + ',' + sentinel + '])';
906
+ createSetAssignment(ctx, id, getRefParam(ctx, key.i), valueRef);
907
+ createDeleteAssignment(ctx, id, sentinel);
908
+ return serialized;
909
+ }
910
+ // Reset stack for the key serialization
911
+ const parent = base.stack;
912
+ base.stack = [];
913
+ createSetAssignment(ctx, id, serialize(ctx, key), valueRef);
914
+ base.stack = parent;
915
+ return '';
916
+ }
917
+
918
+ return '[' + serialize(ctx, key) + ',' + serialize(ctx, val) + ']';
919
+ }
920
+
921
+ function serializeMap(ctx: SerializerContext, node: SerovalMapNode): string {
922
+ let serialized = MAP_CONSTRUCTOR;
923
+ const keys = node.e.k;
924
+ const size = keys.length;
925
+ const id = node.i;
926
+ const sentinel = node.f;
927
+ const sentinelId = getRefParam(ctx, sentinel.i);
928
+ const base = ctx.base;
929
+ if (size > 0) {
930
+ const vals = node.e.v;
931
+ base.stack.push(id);
932
+ let result = serializeMapEntry(ctx, id, keys[0], vals[0], sentinelId);
933
+ for (let i = 1, item = result; i < size; i++) {
934
+ item = serializeMapEntry(ctx, id, keys[i], vals[i], sentinelId);
935
+ result += (item && result && ',') + item;
936
+ }
937
+ base.stack.pop();
938
+ // Check if there are any values
939
+ // so that the empty Map constructor
940
+ // can be used instead
941
+ if (result) {
942
+ serialized += '([' + result + '])';
943
+ }
944
+ }
945
+ if (sentinel.t === SerovalNodeType.SpecialReference) {
946
+ markSerializerRef(base, sentinel.i);
947
+ serialized = '(' + serialize(ctx, sentinel) + ',' + serialized + ')';
948
+ }
949
+ return serialized;
950
+ }
951
+
952
+ function serializeArrayBuffer(
953
+ ctx: SerializerContext,
954
+ node: SerovalArrayBufferNode,
955
+ ): string {
956
+ return getConstructor(ctx, node.f) + '("' + node.s + '")';
957
+ }
958
+
959
+ function serializeTypedArray(
960
+ ctx: SerializerContext,
961
+ node: SerovalTypedArrayNode | SerovalBigIntTypedArrayNode,
962
+ ): string {
963
+ return 'new ' + node.c + '(' + serialize(ctx, node.f) + ',' + node.b + ',' + node.l + ')';
964
+ }
965
+
966
+ function serializeDataView(
967
+ ctx: SerializerContext,
968
+ node: SerovalDataViewNode,
969
+ ): string {
970
+ return 'new DataView(' + serialize(ctx, node.f) + ',' + node.b + ',' + node.l + ')';
971
+ }
972
+
973
+ function serializeAggregateError(
974
+ ctx: SerializerContext,
975
+ node: SerovalAggregateErrorNode,
976
+ ): string {
977
+ const id = node.i;
978
+ // `AggregateError` might've been extended
979
+ // either through class or custom properties
980
+ // Make sure to assign extra properties
981
+ ctx.base.stack.push(id);
982
+ const serialized = serializeDictionary(
983
+ ctx,
984
+ node,
985
+ 'new AggregateError([],"' + node.m + '")',
986
+ );
987
+ ctx.base.stack.pop();
988
+ return serialized;
989
+ }
990
+
991
+ function serializeError(
992
+ ctx: SerializerContext,
993
+ node: SerovalErrorNode,
994
+ ): string {
995
+ return serializeDictionary(
996
+ ctx,
997
+ node,
998
+ 'new ' + ERROR_CONSTRUCTOR_STRING[node.s] + '("' + node.m + '")',
999
+ );
1000
+ }
1001
+
1002
+ function serializePromise(
1003
+ ctx: SerializerContext,
1004
+ node: SerovalPromiseNode,
1005
+ ): string {
1006
+ let serialized: string;
1007
+ // Check if resolved value is a parent expression
1008
+ const fulfilled = node.f;
1009
+ const id = node.i;
1010
+ const promiseConstructor = node.s ? PROMISE_RESOLVE : PROMISE_REJECT;
1011
+ const base = ctx.base;
1012
+ if (isIndexedValueInStack(base, fulfilled)) {
1013
+ // A Promise trick, reference the value
1014
+ // inside the `then` expression so that
1015
+ // the Promise evaluates after the parent
1016
+ // has initialized
1017
+ const ref = getRefParam(ctx, (fulfilled as SerovalIndexedValueNode).i);
1018
+ serialized =
1019
+ promiseConstructor +
1020
+ (node.s
1021
+ ? '().then(' + createFunction([], ref) + ')'
1022
+ : '().catch(' + createEffectfulFunction([], 'throw ' + ref) + ')');
1023
+ } else {
1024
+ base.stack.push(id);
1025
+ const result = serialize(ctx, fulfilled);
1026
+ base.stack.pop();
1027
+ // just inline the value/reference here
1028
+ serialized = promiseConstructor + '(' + result + ')';
1029
+ }
1030
+ return serialized;
1031
+ }
1032
+
1033
+ function serializeBoxed(
1034
+ ctx: SerializerContext,
1035
+ node: SerovalBoxedNode,
1036
+ ): string {
1037
+ return 'Object(' + serialize(ctx, node.f) + ')';
1038
+ }
1039
+
1040
+ function getConstructor(
1041
+ ctx: SerializerContext,
1042
+ node: SerovalNodeWithID,
1043
+ ): string {
1044
+ const current = serialize(ctx, node);
1045
+ return node.t === SerovalNodeType.IndexedValue
1046
+ ? current
1047
+ : '(' + current + ')';
1048
+ }
1049
+
1050
+ function serializePromiseConstructor(
1051
+ ctx: SerializerContext,
1052
+ node: SerovalPromiseConstructorNode,
1053
+ ): string {
1054
+ if (ctx.mode === SerovalMode.Vanilla) {
1055
+ throw new SerovalUnsupportedNodeError(node);
1056
+ }
1057
+ const resolver = assignIndexedValue(
1058
+ ctx,
1059
+ node.s,
1060
+ getConstructor(ctx, node.f) + '()',
1061
+ );
1062
+ return '(' + resolver + ').p';
1063
+ }
1064
+
1065
+ function serializePromiseResolve(
1066
+ ctx: SerializerContext,
1067
+ node: SerovalPromiseResolveNode,
1068
+ ): string {
1069
+ if (ctx.mode === SerovalMode.Vanilla) {
1070
+ throw new SerovalUnsupportedNodeError(node);
1071
+ }
1072
+ return (
1073
+ getConstructor(ctx, node.a[0]) +
1074
+ '(' +
1075
+ getRefParam(ctx, node.i) +
1076
+ ',' +
1077
+ serialize(ctx, node.a[1]) +
1078
+ ')'
1079
+ );
1080
+ }
1081
+
1082
+ function serializePromiseReject(
1083
+ ctx: SerializerContext,
1084
+ node: SerovalPromiseRejectNode,
1085
+ ): string {
1086
+ if (ctx.mode === SerovalMode.Vanilla) {
1087
+ throw new SerovalUnsupportedNodeError(node);
1088
+ }
1089
+ return (
1090
+ getConstructor(ctx, node.a[0]) +
1091
+ '(' +
1092
+ getRefParam(ctx, node.i) +
1093
+ ',' +
1094
+ serialize(ctx, node.a[1]) +
1095
+ ')'
1096
+ );
1097
+ }
1098
+
1099
+ function serializePlugin(
1100
+ ctx: SerializerContext,
1101
+ node: SerovalPluginNode,
1102
+ ): string {
1103
+ const currentPlugins = ctx.base.plugins;
1104
+ if (currentPlugins) {
1105
+ for (let i = 0, len = currentPlugins.length; i < len; i++) {
1106
+ const plugin = currentPlugins[i];
1107
+ if (plugin.tag === node.c) {
1108
+ if (ctx.child == null) {
1109
+ ctx.child = new SerializePluginContext(ctx);
1110
+ }
1111
+ return plugin.serialize(node.s, ctx.child, {
1112
+ id: node.i,
1113
+ });
1114
+ }
1115
+ }
1116
+ }
1117
+ throw new SerovalMissingPluginError(node.c);
1118
+ }
1119
+
1120
+ function serializeIteratorFactory(
1121
+ ctx: SerializerContext,
1122
+ node: SerovalIteratorFactoryNode,
1123
+ ): string {
1124
+ let result = '';
1125
+ let initialized = false;
1126
+ if (node.f.t !== SerovalNodeType.IndexedValue) {
1127
+ markSerializerRef(ctx.base, node.f.i);
1128
+ result = '(' + serialize(ctx, node.f) + ',';
1129
+ initialized = true;
1130
+ }
1131
+ result += assignIndexedValue(
1132
+ ctx,
1133
+ node.i,
1134
+ '(' +
1135
+ SERIALIZED_ITERATOR_CONSTRUCTOR +
1136
+ ')(' +
1137
+ getRefParam(ctx, node.f.i) +
1138
+ ')',
1139
+ );
1140
+ if (initialized) {
1141
+ result += ')';
1142
+ }
1143
+ return result;
1144
+ }
1145
+
1146
+ function serializeIteratorFactoryInstance(
1147
+ ctx: SerializerContext,
1148
+ node: SerovalIteratorFactoryInstanceNode,
1149
+ ): string {
1150
+ return getConstructor(ctx, node.a[0]) + '(' + serialize(ctx, node.a[1]) + ')';
1151
+ }
1152
+
1153
+ function serializeAsyncIteratorFactory(
1154
+ ctx: SerializerContext,
1155
+ node: SerovalAsyncIteratorFactoryNode,
1156
+ ): string {
1157
+ const promise = node.a[0];
1158
+ const symbol = node.a[1];
1159
+ const base = ctx.base;
1160
+
1161
+ let result = '';
1162
+
1163
+ if (promise.t !== SerovalNodeType.IndexedValue) {
1164
+ markSerializerRef(base, promise.i);
1165
+ result += '(' + serialize(ctx, promise);
1166
+ }
1167
+ if (symbol.t !== SerovalNodeType.IndexedValue) {
1168
+ markSerializerRef(base, symbol.i);
1169
+ result += (result ? ',' : '(') + serialize(ctx, symbol);
1170
+ }
1171
+ if (result) {
1172
+ result += ',';
1173
+ }
1174
+
1175
+ const iterator = assignIndexedValue(
1176
+ ctx,
1177
+ node.i,
1178
+ '(' +
1179
+ SERIALIZED_ASYNC_ITERATOR_CONSTRUCTOR +
1180
+ ')(' +
1181
+ getRefParam(ctx, symbol.i) +
1182
+ ',' +
1183
+ getRefParam(ctx, promise.i) +
1184
+ ')',
1185
+ );
1186
+
1187
+ if (result) {
1188
+ return result + iterator + ')';
1189
+ }
1190
+
1191
+ return iterator;
1192
+ }
1193
+
1194
+ function serializeAsyncIteratorFactoryInstance(
1195
+ ctx: SerializerContext,
1196
+ node: SerovalAsyncIteratorFactoryInstanceNode,
1197
+ ): string {
1198
+ return getConstructor(ctx, node.a[0]) + '(' + serialize(ctx, node.a[1]) + ')';
1199
+ }
1200
+
1201
+ function serializeStreamConstructor(
1202
+ ctx: SerializerContext,
1203
+ node: SerovalStreamConstructorNode,
1204
+ ): string {
1205
+ const result = assignIndexedValue(
1206
+ ctx,
1207
+ node.i,
1208
+ getConstructor(ctx, node.f) + '()',
1209
+ );
1210
+ const len = node.a.length;
1211
+ if (len) {
1212
+ let values = serialize(ctx, node.a[0]);
1213
+ for (let i = 1; i < len; i++) {
1214
+ values += ',' + serialize(ctx, node.a[i]);
1215
+ }
1216
+ return '(' + result + ',' + values + ',' + getRefParam(ctx, node.i) + ')';
1217
+ }
1218
+ return result;
1219
+ }
1220
+
1221
+ function serializeStreamNext(
1222
+ ctx: SerializerContext,
1223
+ node: SerovalStreamNextNode,
1224
+ ): string {
1225
+ return getRefParam(ctx, node.i) + '.next(' + serialize(ctx, node.f) + ')';
1226
+ }
1227
+
1228
+ function serializeStreamThrow(
1229
+ ctx: SerializerContext,
1230
+ node: SerovalStreamThrowNode,
1231
+ ): string {
1232
+ return getRefParam(ctx, node.i) + '.throw(' + serialize(ctx, node.f) + ')';
1233
+ }
1234
+
1235
+ function serializeStreamReturn(
1236
+ ctx: SerializerContext,
1237
+ node: SerovalStreamReturnNode,
1238
+ ): string {
1239
+ return getRefParam(ctx, node.i) + '.return(' + serialize(ctx, node.f) + ')';
1240
+ }
1241
+
1242
+ function serializeAssignable(
1243
+ ctx: SerializerContext,
1244
+ node: SerovalNode,
1245
+ ): string {
1246
+ switch (node.t) {
1247
+ case SerovalNodeType.WKSymbol:
1248
+ return SYMBOL_STRING[node.s];
1249
+ case SerovalNodeType.Reference:
1250
+ return serializeReference(node);
1251
+ case SerovalNodeType.Array:
1252
+ return serializeArray(ctx, node);
1253
+ case SerovalNodeType.Object:
1254
+ return serializeObject(ctx, node);
1255
+ case SerovalNodeType.NullConstructor:
1256
+ return serializeNullConstructor(ctx, node);
1257
+ case SerovalNodeType.Date:
1258
+ return serializeDate(node);
1259
+ case SerovalNodeType.RegExp:
1260
+ return serializeRegExp(ctx, node);
1261
+ case SerovalNodeType.Set:
1262
+ return serializeSet(ctx, node);
1263
+ case SerovalNodeType.Map:
1264
+ return serializeMap(ctx, node);
1265
+ case SerovalNodeType.ArrayBuffer:
1266
+ return serializeArrayBuffer(ctx, node);
1267
+ case SerovalNodeType.BigIntTypedArray:
1268
+ case SerovalNodeType.TypedArray:
1269
+ return serializeTypedArray(ctx, node);
1270
+ case SerovalNodeType.DataView:
1271
+ return serializeDataView(ctx, node);
1272
+ case SerovalNodeType.AggregateError:
1273
+ return serializeAggregateError(ctx, node);
1274
+ case SerovalNodeType.Error:
1275
+ return serializeError(ctx, node);
1276
+ case SerovalNodeType.Promise:
1277
+ return serializePromise(ctx, node);
1278
+ case SerovalNodeType.Boxed:
1279
+ return serializeBoxed(ctx, node);
1280
+ case SerovalNodeType.PromiseConstructor:
1281
+ return serializePromiseConstructor(ctx, node);
1282
+ case SerovalNodeType.Plugin:
1283
+ return serializePlugin(ctx, node);
1284
+ case SerovalNodeType.SpecialReference:
1285
+ return SPECIAL_REF_STRING[node.s];
1286
+ default:
1287
+ throw new SerovalUnsupportedNodeError(node);
1288
+ }
1289
+ }
1290
+
1291
+ function serialize(ctx: SerializerContext, node: SerovalNode): string {
1292
+ switch (node.t) {
1293
+ case SerovalNodeType.Constant:
1294
+ return CONSTANT_STRING[node.s];
1295
+ case SerovalNodeType.Number:
1296
+ return '' + node.s;
1297
+ case SerovalNodeType.String:
1298
+ return '"' + node.s + '"';
1299
+ case SerovalNodeType.BigInt:
1300
+ return node.s + 'n';
1301
+ case SerovalNodeType.IndexedValue:
1302
+ return getRefParam(ctx, node.i);
1303
+ case SerovalNodeType.PromiseSuccess:
1304
+ return serializePromiseResolve(ctx, node);
1305
+ case SerovalNodeType.PromiseFailure:
1306
+ return serializePromiseReject(ctx, node);
1307
+ case SerovalNodeType.IteratorFactory:
1308
+ return serializeIteratorFactory(ctx, node);
1309
+ case SerovalNodeType.IteratorFactoryInstance:
1310
+ return serializeIteratorFactoryInstance(ctx, node);
1311
+ case SerovalNodeType.AsyncIteratorFactory:
1312
+ return serializeAsyncIteratorFactory(ctx, node);
1313
+ case SerovalNodeType.AsyncIteratorFactoryInstance:
1314
+ return serializeAsyncIteratorFactoryInstance(ctx, node);
1315
+ case SerovalNodeType.StreamConstructor:
1316
+ return serializeStreamConstructor(ctx, node);
1317
+ case SerovalNodeType.StreamNext:
1318
+ return serializeStreamNext(ctx, node);
1319
+ case SerovalNodeType.StreamThrow:
1320
+ return serializeStreamThrow(ctx, node);
1321
+ case SerovalNodeType.StreamReturn:
1322
+ return serializeStreamReturn(ctx, node);
1323
+ default:
1324
+ return assignIndexedValue(ctx, node.i, serializeAssignable(ctx, node));
1325
+ }
1326
+ }
1327
+
1328
+ export function serializeRoot(
1329
+ ctx: SerializerContext,
1330
+ node: SerovalNode,
1331
+ ): string {
1332
+ try {
1333
+ return serialize(ctx, node);
1334
+ } catch (error) {
1335
+ throw error instanceof SerovalSerializationError
1336
+ ? error
1337
+ : new SerovalSerializationError(error);
1338
+ }
1339
+ }
1340
+
1341
+ export function serializeTopVanilla(
1342
+ ctx: VanillaSerializerContext,
1343
+ tree: SerovalNode,
1344
+ ): string {
1345
+ const result = serialize(ctx, tree);
1346
+ // Shared references detected
1347
+ if (tree.i != null && ctx.state.vars.length) {
1348
+ const patches = resolvePatches(ctx.base);
1349
+ let body = result;
1350
+ if (patches) {
1351
+ // Get (or create) a ref from the source
1352
+ const index = getRefParam(ctx, tree.i);
1353
+ body = result + ',' + patches + index;
1354
+ if (!result.startsWith(index + '=')) {
1355
+ body = index + '=' + body;
1356
+ }
1357
+ body = '(' + body + ')';
1358
+ }
1359
+ return '(' + createFunction(ctx.state.vars, body) + ')()';
1360
+ }
1361
+ if (tree.t === SerovalNodeType.Object) {
1362
+ return '(' + result + ')';
1363
+ }
1364
+ return result;
1365
+ }
1366
+
1367
+ export function serializeTopCross(
1368
+ ctx: CrossSerializerContext,
1369
+ tree: SerovalNode,
1370
+ ): string {
1371
+ // Get the serialized result
1372
+ const result = serialize(ctx, tree);
1373
+ // If the node is a non-reference, return
1374
+ // the result immediately
1375
+ const id = tree.i;
1376
+ if (id == null) {
1377
+ return result;
1378
+ }
1379
+ // Get the patches
1380
+ const patches = resolvePatches(ctx.base);
1381
+ // Get the variable that represents the root
1382
+ const ref = getRefParam(ctx, id);
1383
+ const scopeId = ctx.state.scopeId;
1384
+ // Parameters needed for scoping
1385
+ const params = scopeId == null ? '' : GLOBAL_CONTEXT_REFERENCES;
1386
+ // If there are patches, append it after the result
1387
+ const body = patches ? '(' + result + ',' + patches + ref + ')' : result;
1388
+ // If there are no params, there's no need to generate a function
1389
+ if (params === '') {
1390
+ if (tree.t === SerovalNodeType.Object && !patches) {
1391
+ return '(' + body + ')';
1392
+ }
1393
+ return body;
1394
+ }
1395
+ // Get the arguments for the IIFE
1396
+ const args =
1397
+ scopeId == null
1398
+ ? '()'
1399
+ : '(' +
1400
+ GLOBAL_CONTEXT_REFERENCES +
1401
+ '["' +
1402
+ serializeString(scopeId) +
1403
+ '"])';
1404
+ // Create the IIFE
1405
+ return '(' + createFunction([params], body) + ')' + args;
1406
+ }