seroval 1.4.0 → 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 (48) hide show
  1. package/dist/cjs/development/index.cjs +553 -350
  2. package/dist/cjs/development/index.cjs.map +3 -3
  3. package/dist/cjs/production/index.cjs +3 -3
  4. package/dist/esm/development/index.mjs +553 -350
  5. package/dist/esm/development/index.mjs.map +3 -3
  6. package/dist/esm/production/index.mjs +3 -3
  7. package/dist/types/core/base-primitives.d.ts +2 -2
  8. package/dist/types/core/base-primitives.d.ts.map +1 -1
  9. package/dist/types/core/compat.d.ts +2 -1
  10. package/dist/types/core/compat.d.ts.map +1 -1
  11. package/dist/types/core/constants.d.ts +2 -2
  12. package/dist/types/core/constants.d.ts.map +1 -1
  13. package/dist/types/core/constructors.d.ts +1 -1
  14. package/dist/types/core/constructors.d.ts.map +1 -1
  15. package/dist/types/core/context/async-parser.d.ts +5 -4
  16. package/dist/types/core/context/async-parser.d.ts.map +1 -1
  17. package/dist/types/core/context/deserializer.d.ts +7 -2
  18. package/dist/types/core/context/deserializer.d.ts.map +1 -1
  19. package/dist/types/core/context/parser.d.ts +3 -1
  20. package/dist/types/core/context/parser.d.ts.map +1 -1
  21. package/dist/types/core/context/serializer.d.ts.map +1 -1
  22. package/dist/types/core/context/sync-parser.d.ts +5 -5
  23. package/dist/types/core/context/sync-parser.d.ts.map +1 -1
  24. package/dist/types/core/cross/index.d.ts.map +1 -1
  25. package/dist/types/core/errors.d.ts +9 -0
  26. package/dist/types/core/errors.d.ts.map +1 -1
  27. package/dist/types/core/node.d.ts +1 -1
  28. package/dist/types/core/string.d.ts.map +1 -1
  29. package/dist/types/core/tree/index.d.ts +4 -1
  30. package/dist/types/core/tree/index.d.ts.map +1 -1
  31. package/dist/types/core/types.d.ts +6 -11
  32. package/dist/types/core/types.d.ts.map +1 -1
  33. package/package.json +1 -1
  34. package/src/core/base-primitives.ts +17 -18
  35. package/src/core/compat.ts +3 -1
  36. package/src/core/constants.ts +18 -4
  37. package/src/core/constructors.ts +2 -1
  38. package/src/core/context/async-parser.ts +102 -50
  39. package/src/core/context/deserializer.ts +269 -105
  40. package/src/core/context/parser.ts +12 -10
  41. package/src/core/context/serializer.ts +31 -36
  42. package/src/core/context/sync-parser.ts +149 -75
  43. package/src/core/cross/index.ts +2 -0
  44. package/src/core/errors.ts +33 -0
  45. package/src/core/node.ts +2 -2
  46. package/src/core/string.ts +3 -1
  47. package/src/core/tree/index.ts +10 -1
  48. package/src/core/types.ts +10 -19
@@ -1,6 +1,8 @@
1
+ import { ALL_ENABLED, Feature } from '../compat';
1
2
  import {
2
3
  CONSTANT_VAL,
3
4
  ERROR_CONSTRUCTOR,
5
+ NIL,
4
6
  SerovalNodeType,
5
7
  SerovalObjectFlags,
6
8
  SYMBOL_REF,
@@ -11,7 +13,9 @@ import {
11
13
  type PromiseConstructorResolver,
12
14
  } from '../constructors';
13
15
  import {
16
+ SerovalDepthLimitError,
14
17
  SerovalDeserializationError,
18
+ SerovalMalformedNodeError,
15
19
  SerovalMissingInstanceError,
16
20
  SerovalMissingPluginError,
17
21
  SerovalUnsupportedNodeError,
@@ -20,8 +24,14 @@ import type { PluginAccessOptions } from '../plugin';
20
24
  import { SerovalMode } from '../plugin';
21
25
  import { getReference } from '../reference';
22
26
  import type { Stream } from '../stream';
23
- import { createStream, streamToAsyncIterable } from '../stream';
27
+ import { createStream, isStream, streamToAsyncIterable } from '../stream';
24
28
  import { deserializeString } from '../string';
29
+ import {
30
+ SYM_ASYNC_ITERATOR,
31
+ SYM_IS_CONCAT_SPREADABLE,
32
+ SYM_ITERATOR,
33
+ SYM_TO_STRING_TAG,
34
+ } from '../symbols';
25
35
  import type {
26
36
  SerovalAggregateErrorNode,
27
37
  SerovalArrayBufferNode,
@@ -39,7 +49,6 @@ import type {
39
49
  SerovalNode,
40
50
  SerovalNullConstructorNode,
41
51
  SerovalObjectNode,
42
- SerovalObjectRecordKey,
43
52
  SerovalObjectRecordNode,
44
53
  SerovalPluginNode,
45
54
  SerovalPromiseConstructorNode,
@@ -63,6 +72,10 @@ import type {
63
72
  } from '../utils/typed-array';
64
73
  import { getTypedArrayConstructor } from '../utils/typed-array';
65
74
 
75
+ const MAX_BASE64_LENGTH = 1_000_000; // ~0.75MB decoded
76
+ const MAX_BIGINT_LENGTH = 10_000;
77
+ const MAX_REGEXP_SOURCE_LENGTH = 20_000;
78
+
66
79
  function applyObjectFlag(obj: unknown, flag: SerovalObjectFlags): unknown {
67
80
  switch (flag) {
68
81
  case SerovalObjectFlags.Frozen:
@@ -81,6 +94,9 @@ type AssignableNode = SerovalAggregateErrorNode | SerovalErrorNode;
81
94
 
82
95
  export interface BaseDeserializerContextOptions extends PluginAccessOptions {
83
96
  refs?: Map<number, unknown>;
97
+ features?: number;
98
+ disabledFeatures?: number;
99
+ depthLimit?: number;
84
100
  }
85
101
 
86
102
  export interface BaseDeserializerContext extends PluginAccessOptions {
@@ -89,8 +105,12 @@ export interface BaseDeserializerContext extends PluginAccessOptions {
89
105
  * Mapping ids to values
90
106
  */
91
107
  refs: Map<number, unknown>;
108
+ features: number;
109
+ depthLimit: number;
92
110
  }
93
111
 
112
+ const DEFAULT_DEPTH_LIMIT = 1000;
113
+
94
114
  export function createBaseDeserializerContext(
95
115
  mode: SerovalMode,
96
116
  options: BaseDeserializerContextOptions,
@@ -99,6 +119,8 @@ export function createBaseDeserializerContext(
99
119
  mode,
100
120
  plugins: options.plugins,
101
121
  refs: options.refs || new Map(),
122
+ features: options.features ?? ALL_ENABLED ^ (options.disabledFeatures || 0),
123
+ depthLimit: options.depthLimit || DEFAULT_DEPTH_LIMIT,
102
124
  };
103
125
  }
104
126
 
@@ -124,7 +146,7 @@ export function createVanillaDeserializerContext(
124
146
  return {
125
147
  mode: SerovalMode.Vanilla,
126
148
  base: createBaseDeserializerContext(SerovalMode.Vanilla, options),
127
- child: undefined,
149
+ child: NIL,
128
150
  state: {
129
151
  marked: new Set(options.markedRefs),
130
152
  },
@@ -145,7 +167,7 @@ export function createCrossDeserializerContext(
145
167
  return {
146
168
  mode: SerovalMode.Cross,
147
169
  base: createBaseDeserializerContext(SerovalMode.Cross, options),
148
- child: undefined,
170
+ child: NIL,
149
171
  };
150
172
  }
151
173
 
@@ -154,10 +176,25 @@ type DeserializerContext =
154
176
  | CrossDeserializerContext;
155
177
 
156
178
  export class DeserializePluginContext {
157
- constructor(private _p: DeserializerContext) {}
179
+ constructor(
180
+ private _p: DeserializerContext,
181
+ private depth: number,
182
+ ) {}
158
183
 
159
184
  deserialize<T>(node: SerovalNode): T {
160
- return deserialize(this._p, node) as T;
185
+ return deserialize(this._p, this.depth, node) as T;
186
+ }
187
+ }
188
+
189
+ function guardIndexedValue(ctx: BaseDeserializerContext, id: number): void {
190
+ if (id < 0 || !Number.isFinite(id) || !Number.isInteger(id)) {
191
+ throw new SerovalMalformedNodeError({
192
+ t: SerovalNodeType.IndexedValue,
193
+ i: id,
194
+ } as SerovalNode);
195
+ }
196
+ if (ctx.refs.has(id)) {
197
+ throw new Error('Conflicted ref id: ' + id);
161
198
  }
162
199
  }
163
200
 
@@ -166,6 +203,7 @@ function assignIndexedValueVanilla<T>(
166
203
  id: number,
167
204
  value: T,
168
205
  ): T {
206
+ guardIndexedValue(ctx.base, id);
169
207
  if (ctx.state.marked.has(id)) {
170
208
  ctx.base.refs.set(id, value);
171
209
  }
@@ -177,9 +215,8 @@ function assignIndexedValueCross<T>(
177
215
  id: number,
178
216
  value: T,
179
217
  ): T {
180
- if (!ctx.base.refs.has(id)) {
181
- ctx.base.refs.set(id, value);
182
- }
218
+ guardIndexedValue(ctx.base, id);
219
+ ctx.base.refs.set(id, value);
183
220
  return value;
184
221
  }
185
222
 
@@ -193,6 +230,16 @@ function assignIndexedValue<T>(
193
230
  : assignIndexedValueCross(ctx, id, value);
194
231
  }
195
232
 
233
+ function deserializeKnownValue<
234
+ T extends Record<string, unknown>,
235
+ K extends keyof T,
236
+ >(node: SerovalNode, record: T, key: K): T[K] {
237
+ if (Object.hasOwn(record, key)) {
238
+ return record[key];
239
+ }
240
+ throw new SerovalMalformedNodeError(node);
241
+ }
242
+
196
243
  function deserializeReference(
197
244
  ctx: DeserializerContext,
198
245
  node: SerovalReferenceNode,
@@ -206,41 +253,108 @@ function deserializeReference(
206
253
 
207
254
  function deserializeArray(
208
255
  ctx: DeserializerContext,
256
+ depth: number,
209
257
  node: SerovalArrayNode,
210
258
  ): unknown[] {
211
- const len = node.l;
259
+ const items = node.a;
260
+ const len = items.length;
212
261
  const result: unknown[] = assignIndexedValue(
213
262
  ctx,
214
263
  node.i,
215
264
  new Array<unknown>(len),
216
265
  );
217
- let item: SerovalNode | undefined;
218
- for (let i = 0; i < len; i++) {
219
- item = node.a[i];
266
+ for (let i = 0, item: SerovalNode | 0; i < len; i++) {
267
+ item = items[i];
220
268
  if (item) {
221
- result[i] = deserialize(ctx, item);
269
+ result[i] = deserialize(ctx, depth, item);
222
270
  }
223
271
  }
224
272
  applyObjectFlag(result, node.o);
225
273
  return result;
226
274
  }
227
275
 
276
+ function isValidKey(key: string): boolean {
277
+ switch (key) {
278
+ case 'constructor':
279
+ case '__proto__':
280
+ case 'prototype':
281
+ case '__defineGetter__':
282
+ case '__defineSetter__':
283
+ case '__lookupGetter__':
284
+ case '__lookupSetter__':
285
+ // case 'then':
286
+ return false;
287
+ default:
288
+ return true;
289
+ }
290
+ }
291
+
292
+ function isValidSymbol(symbol: symbol): boolean {
293
+ switch (symbol) {
294
+ case SYM_ASYNC_ITERATOR:
295
+ case SYM_IS_CONCAT_SPREADABLE:
296
+ case SYM_TO_STRING_TAG:
297
+ case SYM_ITERATOR:
298
+ return true;
299
+ default:
300
+ return false;
301
+ }
302
+ }
303
+
304
+ function assignStringProperty(
305
+ object: Record<string | symbol, unknown>,
306
+ key: string,
307
+ value: unknown,
308
+ ): void {
309
+ if (isValidKey(key)) {
310
+ object[key] = value;
311
+ } else {
312
+ Object.defineProperty(object, key, {
313
+ value,
314
+ configurable: true,
315
+ enumerable: true,
316
+ writable: true,
317
+ });
318
+ }
319
+ }
320
+
321
+ function assignProperty(
322
+ ctx: DeserializerContext,
323
+ depth: number,
324
+ object: Record<string | symbol, unknown>,
325
+ key: string | SerovalNode,
326
+ value: SerovalNode,
327
+ ): void {
328
+ if (typeof key === 'string') {
329
+ assignStringProperty(object, key, deserialize(ctx, depth, value));
330
+ } else {
331
+ const actual = deserialize(ctx, depth, key);
332
+ switch (typeof actual) {
333
+ case 'string':
334
+ assignStringProperty(object, actual, deserialize(ctx, depth, value));
335
+ break;
336
+ case 'symbol':
337
+ if (isValidSymbol(actual)) {
338
+ object[actual] = deserialize(ctx, depth, value);
339
+ }
340
+ break;
341
+ default:
342
+ throw new SerovalMalformedNodeError(key);
343
+ }
344
+ }
345
+ }
346
+
228
347
  function deserializeProperties(
229
348
  ctx: DeserializerContext,
349
+ depth: number,
230
350
  node: SerovalObjectRecordNode,
231
351
  result: Record<string | symbol, unknown>,
232
352
  ): Record<string | symbol, unknown> {
233
- const len = node.s;
234
- if (len) {
235
- const keys = node.k;
236
- const vals = node.v;
237
- for (let i = 0, key: SerovalObjectRecordKey; i < len; i++) {
238
- key = keys[i];
239
- if (typeof key === 'string') {
240
- result[deserializeString(key)] = deserialize(ctx, vals[i]);
241
- } else {
242
- result[deserialize(ctx, key) as symbol] = deserialize(ctx, vals[i]);
243
- }
353
+ const keys = node.k;
354
+ const len = keys.length;
355
+ if (len > 0) {
356
+ for (let i = 0, vals = node.v, len = keys.length; i < len; i++) {
357
+ assignProperty(ctx, depth, result, keys[i], vals[i]);
244
358
  }
245
359
  }
246
360
  return result;
@@ -248,6 +362,7 @@ function deserializeProperties(
248
362
 
249
363
  function deserializeObject(
250
364
  ctx: DeserializerContext,
365
+ depth: number,
251
366
  node: SerovalObjectNode | SerovalNullConstructorNode,
252
367
  ): Record<string, unknown> {
253
368
  const result = assignIndexedValue(
@@ -258,7 +373,7 @@ function deserializeObject(
258
373
  unknown
259
374
  >,
260
375
  );
261
- deserializeProperties(ctx, node.p, result);
376
+ deserializeProperties(ctx, depth, node.p, result);
262
377
  applyObjectFlag(result, node.o);
263
378
  return result;
264
379
  }
@@ -274,34 +389,43 @@ function deserializeRegExp(
274
389
  ctx: DeserializerContext,
275
390
  node: SerovalRegExpNode,
276
391
  ): RegExp {
277
- return assignIndexedValue(
278
- ctx,
279
- node.i,
280
- new RegExp(deserializeString(node.c), node.m),
281
- );
392
+ if (ctx.base.features & Feature.RegExp) {
393
+ const source = deserializeString(node.c);
394
+ if (source.length > MAX_REGEXP_SOURCE_LENGTH) {
395
+ throw new SerovalMalformedNodeError(node);
396
+ }
397
+ return assignIndexedValue(ctx, node.i, new RegExp(source, node.m));
398
+ }
399
+ throw new SerovalUnsupportedNodeError(node);
282
400
  }
283
401
 
284
402
  function deserializeSet(
285
403
  ctx: DeserializerContext,
404
+ depth: number,
286
405
  node: SerovalSetNode,
287
406
  ): Set<unknown> {
288
407
  const result = assignIndexedValue(ctx, node.i, new Set<unknown>());
289
- const items = node.a;
290
- for (let i = 0, len = node.l; i < len; i++) {
291
- result.add(deserialize(ctx, items[i]));
408
+ for (let i = 0, items = node.a, len = items.length; i < len; i++) {
409
+ result.add(deserialize(ctx, depth, items[i]));
292
410
  }
293
411
  return result;
294
412
  }
295
413
 
296
414
  function deserializeMap(
297
415
  ctx: DeserializerContext,
416
+ depth: number,
298
417
  node: SerovalMapNode,
299
418
  ): Map<unknown, unknown> {
300
419
  const result = assignIndexedValue(ctx, node.i, new Map<unknown, unknown>());
301
- const keys = node.e.k;
302
- const vals = node.e.v;
303
- for (let i = 0, len = node.e.s; i < len; i++) {
304
- result.set(deserialize(ctx, keys[i]), deserialize(ctx, vals[i]));
420
+ for (
421
+ let i = 0, keys = node.e.k, vals = node.e.v, len = keys.length;
422
+ i < len;
423
+ i++
424
+ ) {
425
+ result.set(
426
+ deserialize(ctx, depth, keys[i]),
427
+ deserialize(ctx, depth, vals[i]),
428
+ );
305
429
  }
306
430
  return result;
307
431
  }
@@ -310,55 +434,70 @@ function deserializeArrayBuffer(
310
434
  ctx: DeserializerContext,
311
435
  node: SerovalArrayBufferNode,
312
436
  ): ArrayBuffer {
437
+ if (node.s.length > MAX_BASE64_LENGTH) {
438
+ throw new SerovalMalformedNodeError(node);
439
+ }
313
440
  const result = assignIndexedValue(
314
441
  ctx,
315
442
  node.i,
316
- ARRAY_BUFFER_CONSTRUCTOR(node.l, deserializeString(node.s)),
443
+ ARRAY_BUFFER_CONSTRUCTOR(deserializeString(node.s)),
317
444
  );
318
445
  return result;
319
446
  }
320
447
 
321
448
  function deserializeTypedArray(
322
449
  ctx: DeserializerContext,
450
+ depth: number,
323
451
  node: SerovalTypedArrayNode | SerovalBigIntTypedArrayNode,
324
452
  ): TypedArrayValue | BigIntTypedArrayValue {
325
453
  const construct = getTypedArrayConstructor(node.c) as Int8ArrayConstructor;
326
- const source = deserialize(ctx, node.f) as ArrayBuffer;
454
+ const source = deserialize(ctx, depth, node.f) as ArrayBuffer;
455
+ const offset = node.b ?? 0;
456
+ if (offset < 0 || offset > source.byteLength) {
457
+ throw new SerovalMalformedNodeError(node);
458
+ }
327
459
  const result = assignIndexedValue(
328
460
  ctx,
329
461
  node.i,
330
- new construct(source, node.b, node.l),
462
+ new construct(source, offset, node.l),
331
463
  );
332
464
  return result;
333
465
  }
334
466
 
335
467
  function deserializeDataView(
336
468
  ctx: DeserializerContext,
469
+ depth: number,
337
470
  node: SerovalDataViewNode,
338
471
  ): DataView {
339
- const source = deserialize(ctx, node.f) as ArrayBuffer;
472
+ const source = deserialize(ctx, depth, node.f) as ArrayBuffer;
473
+ const offset = node.b ?? 0;
474
+ if (offset < 0 || offset > source.byteLength) {
475
+ throw new SerovalMalformedNodeError(node);
476
+ }
340
477
  const result = assignIndexedValue(
341
478
  ctx,
342
479
  node.i,
343
- new DataView(source, node.b, node.l),
480
+ new DataView(source, offset, node.l),
344
481
  );
345
482
  return result;
346
483
  }
347
484
 
348
485
  function deserializeDictionary<T extends AssignableValue>(
349
486
  ctx: DeserializerContext,
487
+ depth: number,
350
488
  node: AssignableNode,
351
489
  result: T,
352
490
  ): T {
353
491
  if (node.p) {
354
- const fields = deserializeProperties(ctx, node.p, {});
355
- Object.assign(result, fields);
492
+ const fields = deserializeProperties(ctx, depth, node.p, {});
493
+ Object.defineProperties(result, Object.getOwnPropertyDescriptors(fields));
356
494
  }
357
495
  return result;
358
496
  }
359
497
 
360
498
  function deserializeAggregateError(
361
499
  ctx: DeserializerContext,
500
+ depth: number,
362
501
  node: SerovalAggregateErrorNode,
363
502
  ): AggregateError {
364
503
  // Serialize the required arguments
@@ -370,29 +509,31 @@ function deserializeAggregateError(
370
509
  // `AggregateError` might've been extended
371
510
  // either through class or custom properties
372
511
  // Make sure to assign extra properties
373
- return deserializeDictionary(ctx, node, result);
512
+ return deserializeDictionary(ctx, depth, node, result);
374
513
  }
375
514
 
376
515
  function deserializeError(
377
516
  ctx: DeserializerContext,
517
+ depth: number,
378
518
  node: SerovalErrorNode,
379
519
  ): Error {
380
- const construct = ERROR_CONSTRUCTOR[node.s];
520
+ const construct = deserializeKnownValue(node, ERROR_CONSTRUCTOR, node.s);
381
521
  const result = assignIndexedValue(
382
522
  ctx,
383
523
  node.i,
384
524
  new construct(deserializeString(node.m)),
385
525
  );
386
- return deserializeDictionary(ctx, node, result);
526
+ return deserializeDictionary(ctx, depth, node, result);
387
527
  }
388
528
 
389
529
  function deserializePromise(
390
530
  ctx: DeserializerContext,
531
+ depth: number,
391
532
  node: SerovalPromiseNode,
392
533
  ): Promise<unknown> {
393
534
  const deferred = PROMISE_CONSTRUCTOR();
394
535
  const result = assignIndexedValue(ctx, node.i, deferred.p);
395
- const deserialized = deserialize(ctx, node.f);
536
+ const deserialized = deserialize(ctx, depth, node.f);
396
537
  if (node.s) {
397
538
  deferred.s(deserialized);
398
539
  } else {
@@ -403,14 +544,20 @@ function deserializePromise(
403
544
 
404
545
  function deserializeBoxed(
405
546
  ctx: DeserializerContext,
547
+ depth: number,
406
548
  node: SerovalBoxedNode,
407
549
  ): unknown {
408
- // biome-ignore lint/style/useConsistentBuiltinInstantiation: intended
409
- return assignIndexedValue(ctx, node.i, Object(deserialize(ctx, node.f)));
550
+ return assignIndexedValue(
551
+ ctx,
552
+ node.i,
553
+ // biome-ignore lint/style/useConsistentBuiltinInstantiation: intended
554
+ Object(deserialize(ctx, depth, node.f)),
555
+ );
410
556
  }
411
557
 
412
558
  function deserializePlugin(
413
559
  ctx: DeserializerContext,
560
+ depth: number,
414
561
  node: SerovalPluginNode,
415
562
  ): unknown {
416
563
  const currentPlugins = ctx.base.plugins;
@@ -419,13 +566,10 @@ function deserializePlugin(
419
566
  for (let i = 0, len = currentPlugins.length; i < len; i++) {
420
567
  const plugin = currentPlugins[i];
421
568
  if (plugin.tag === tag) {
422
- if (ctx.child == null) {
423
- ctx.child = new DeserializePluginContext(ctx);
424
- }
425
569
  return assignIndexedValue(
426
570
  ctx,
427
571
  node.i,
428
- plugin.deserialize(node.s, ctx.child, {
572
+ plugin.deserialize(node.s, new DeserializePluginContext(ctx, depth), {
429
573
  id: node.i,
430
574
  }),
431
575
  );
@@ -448,59 +592,65 @@ function deserializePromiseConstructor(
448
592
 
449
593
  function deserializePromiseResolve(
450
594
  ctx: DeserializerContext,
595
+ depth: number,
451
596
  node: SerovalPromiseResolveNode,
452
597
  ): unknown {
453
598
  const deferred = ctx.base.refs.get(node.i) as
454
599
  | PromiseConstructorResolver
455
600
  | undefined;
456
601
  if (deferred) {
457
- deferred.s(deserialize(ctx, node.a[1]));
458
- return undefined;
602
+ deferred.s(deserialize(ctx, depth, node.a[1]));
603
+ return NIL;
459
604
  }
460
605
  throw new SerovalMissingInstanceError('Promise');
461
606
  }
462
607
 
463
608
  function deserializePromiseReject(
464
609
  ctx: DeserializerContext,
610
+ depth: number,
465
611
  node: SerovalPromiseRejectNode,
466
612
  ): unknown {
467
613
  const deferred = ctx.base.refs.get(node.i) as
468
614
  | PromiseConstructorResolver
469
615
  | undefined;
470
616
  if (deferred) {
471
- deferred.f(deserialize(ctx, node.a[1]));
472
- return undefined;
617
+ deferred.f(deserialize(ctx, depth, node.a[1]));
618
+ return NIL;
473
619
  }
474
620
  throw new SerovalMissingInstanceError('Promise');
475
621
  }
476
622
 
477
623
  function deserializeIteratorFactoryInstance(
478
624
  ctx: DeserializerContext,
625
+ depth: number,
479
626
  node: SerovalIteratorFactoryInstanceNode,
480
627
  ): unknown {
481
- deserialize(ctx, node.a[0]);
482
- const source = deserialize(ctx, node.a[1]);
628
+ deserialize(ctx, depth, node.a[0]);
629
+ const source = deserialize(ctx, depth, node.a[1]);
483
630
  return sequenceToIterator(source as Sequence);
484
631
  }
485
632
 
486
633
  function deserializeAsyncIteratorFactoryInstance(
487
634
  ctx: DeserializerContext,
635
+ depth: number,
488
636
  node: SerovalAsyncIteratorFactoryInstanceNode,
489
637
  ): unknown {
490
- deserialize(ctx, node.a[0]);
491
- const source = deserialize(ctx, node.a[1]);
638
+ deserialize(ctx, depth, node.a[0]);
639
+ const source = deserialize(ctx, depth, node.a[1]);
492
640
  return streamToAsyncIterable(source as Stream<any>);
493
641
  }
494
642
 
495
643
  function deserializeStreamConstructor(
496
644
  ctx: DeserializerContext,
645
+ depth: number,
497
646
  node: SerovalStreamConstructorNode,
498
647
  ): unknown {
499
648
  const result = assignIndexedValue(ctx, node.i, createStream());
500
- const len = node.a.length;
649
+ const items = node.a;
650
+ const len = items.length;
501
651
  if (len) {
502
652
  for (let i = 0; i < len; i++) {
503
- deserialize(ctx, node.a[i]);
653
+ deserialize(ctx, depth, items[i]);
504
654
  }
505
655
  }
506
656
  return result;
@@ -508,126 +658,140 @@ function deserializeStreamConstructor(
508
658
 
509
659
  function deserializeStreamNext(
510
660
  ctx: DeserializerContext,
661
+ depth: number,
511
662
  node: SerovalStreamNextNode,
512
663
  ): unknown {
513
664
  const deferred = ctx.base.refs.get(node.i) as Stream<unknown> | undefined;
514
- if (deferred) {
515
- deferred.next(deserialize(ctx, node.f));
516
- return undefined;
665
+ if (deferred && isStream(deferred)) {
666
+ deferred.next(deserialize(ctx, depth, node.f));
667
+ return NIL;
517
668
  }
518
669
  throw new SerovalMissingInstanceError('Stream');
519
670
  }
520
671
 
521
672
  function deserializeStreamThrow(
522
673
  ctx: DeserializerContext,
674
+ depth: number,
523
675
  node: SerovalStreamThrowNode,
524
676
  ): unknown {
525
677
  const deferred = ctx.base.refs.get(node.i) as Stream<unknown> | undefined;
526
- if (deferred) {
527
- deferred.throw(deserialize(ctx, node.f));
528
- return undefined;
678
+ if (deferred && isStream(deferred)) {
679
+ deferred.throw(deserialize(ctx, depth, node.f));
680
+ return NIL;
529
681
  }
530
682
  throw new SerovalMissingInstanceError('Stream');
531
683
  }
532
684
 
533
685
  function deserializeStreamReturn(
534
686
  ctx: DeserializerContext,
687
+ depth: number,
535
688
  node: SerovalStreamReturnNode,
536
689
  ): unknown {
537
690
  const deferred = ctx.base.refs.get(node.i) as Stream<unknown> | undefined;
538
- if (deferred) {
539
- deferred.return(deserialize(ctx, node.f));
540
- return undefined;
691
+ if (deferred && isStream(deferred)) {
692
+ deferred.return(deserialize(ctx, depth, node.f));
693
+ return NIL;
541
694
  }
542
695
  throw new SerovalMissingInstanceError('Stream');
543
696
  }
544
697
 
545
698
  function deserializeIteratorFactory(
546
699
  ctx: DeserializerContext,
700
+ depth: number,
547
701
  node: SerovalIteratorFactoryNode,
548
702
  ): unknown {
549
- deserialize(ctx, node.f);
550
- return undefined;
703
+ deserialize(ctx, depth, node.f);
704
+ return NIL;
551
705
  }
552
706
 
553
707
  function deserializeAsyncIteratorFactory(
554
708
  ctx: DeserializerContext,
709
+ depth: number,
555
710
  node: SerovalAsyncIteratorFactoryNode,
556
711
  ): unknown {
557
- deserialize(ctx, node.a[1]);
558
- return undefined;
712
+ deserialize(ctx, depth, node.a[1]);
713
+ return NIL;
559
714
  }
560
- export function deserialize(
715
+
716
+ function deserialize(
561
717
  ctx: DeserializerContext,
718
+ depth: number,
562
719
  node: SerovalNode,
563
720
  ): unknown {
721
+ if (depth > ctx.base.depthLimit) {
722
+ throw new SerovalDepthLimitError(ctx.base.depthLimit);
723
+ }
724
+ depth += 1;
564
725
  switch (node.t) {
565
726
  case SerovalNodeType.Constant:
566
- return CONSTANT_VAL[node.s];
727
+ return deserializeKnownValue(node, CONSTANT_VAL, node.s);
567
728
  case SerovalNodeType.Number:
568
- return node.s;
729
+ return Number(node.s);
569
730
  case SerovalNodeType.String:
570
- return deserializeString(node.s);
731
+ return deserializeString(String(node.s));
571
732
  case SerovalNodeType.BigInt:
733
+ if (String(node.s).length > MAX_BIGINT_LENGTH) {
734
+ throw new SerovalMalformedNodeError(node);
735
+ }
572
736
  return BigInt(node.s);
573
737
  case SerovalNodeType.IndexedValue:
574
738
  return ctx.base.refs.get(node.i);
575
739
  case SerovalNodeType.Reference:
576
740
  return deserializeReference(ctx, node);
577
741
  case SerovalNodeType.Array:
578
- return deserializeArray(ctx, node);
742
+ return deserializeArray(ctx, depth, node);
579
743
  case SerovalNodeType.Object:
580
744
  case SerovalNodeType.NullConstructor:
581
- return deserializeObject(ctx, node);
745
+ return deserializeObject(ctx, depth, node);
582
746
  case SerovalNodeType.Date:
583
747
  return deserializeDate(ctx, node);
584
748
  case SerovalNodeType.RegExp:
585
749
  return deserializeRegExp(ctx, node);
586
750
  case SerovalNodeType.Set:
587
- return deserializeSet(ctx, node);
751
+ return deserializeSet(ctx, depth, node);
588
752
  case SerovalNodeType.Map:
589
- return deserializeMap(ctx, node);
753
+ return deserializeMap(ctx, depth, node);
590
754
  case SerovalNodeType.ArrayBuffer:
591
755
  return deserializeArrayBuffer(ctx, node);
592
756
  case SerovalNodeType.BigIntTypedArray:
593
757
  case SerovalNodeType.TypedArray:
594
- return deserializeTypedArray(ctx, node);
758
+ return deserializeTypedArray(ctx, depth, node);
595
759
  case SerovalNodeType.DataView:
596
- return deserializeDataView(ctx, node);
760
+ return deserializeDataView(ctx, depth, node);
597
761
  case SerovalNodeType.AggregateError:
598
- return deserializeAggregateError(ctx, node);
762
+ return deserializeAggregateError(ctx, depth, node);
599
763
  case SerovalNodeType.Error:
600
- return deserializeError(ctx, node);
764
+ return deserializeError(ctx, depth, node);
601
765
  case SerovalNodeType.Promise:
602
- return deserializePromise(ctx, node);
766
+ return deserializePromise(ctx, depth, node);
603
767
  case SerovalNodeType.WKSymbol:
604
- return SYMBOL_REF[node.s];
768
+ return deserializeKnownValue(node, SYMBOL_REF, node.s);
605
769
  case SerovalNodeType.Boxed:
606
- return deserializeBoxed(ctx, node);
770
+ return deserializeBoxed(ctx, depth, node);
607
771
  case SerovalNodeType.Plugin:
608
- return deserializePlugin(ctx, node);
772
+ return deserializePlugin(ctx, depth, node);
609
773
  case SerovalNodeType.PromiseConstructor:
610
774
  return deserializePromiseConstructor(ctx, node);
611
775
  case SerovalNodeType.PromiseSuccess:
612
- return deserializePromiseResolve(ctx, node);
776
+ return deserializePromiseResolve(ctx, depth, node);
613
777
  case SerovalNodeType.PromiseFailure:
614
- return deserializePromiseReject(ctx, node);
778
+ return deserializePromiseReject(ctx, depth, node);
615
779
  case SerovalNodeType.IteratorFactoryInstance:
616
- return deserializeIteratorFactoryInstance(ctx, node);
780
+ return deserializeIteratorFactoryInstance(ctx, depth, node);
617
781
  case SerovalNodeType.AsyncIteratorFactoryInstance:
618
- return deserializeAsyncIteratorFactoryInstance(ctx, node);
782
+ return deserializeAsyncIteratorFactoryInstance(ctx, depth, node);
619
783
  case SerovalNodeType.StreamConstructor:
620
- return deserializeStreamConstructor(ctx, node);
784
+ return deserializeStreamConstructor(ctx, depth, node);
621
785
  case SerovalNodeType.StreamNext:
622
- return deserializeStreamNext(ctx, node);
786
+ return deserializeStreamNext(ctx, depth, node);
623
787
  case SerovalNodeType.StreamThrow:
624
- return deserializeStreamThrow(ctx, node);
788
+ return deserializeStreamThrow(ctx, depth, node);
625
789
  case SerovalNodeType.StreamReturn:
626
- return deserializeStreamReturn(ctx, node);
790
+ return deserializeStreamReturn(ctx, depth, node);
627
791
  case SerovalNodeType.IteratorFactory:
628
- return deserializeIteratorFactory(ctx, node);
792
+ return deserializeIteratorFactory(ctx, depth, node);
629
793
  case SerovalNodeType.AsyncIteratorFactory:
630
- return deserializeAsyncIteratorFactory(ctx, node);
794
+ return deserializeAsyncIteratorFactory(ctx, depth, node);
631
795
  // case SerovalNodeType.SpecialReference:
632
796
  default:
633
797
  throw new SerovalUnsupportedNodeError(node);
@@ -639,7 +803,7 @@ export function deserializeTop(
639
803
  node: SerovalNode,
640
804
  ): unknown {
641
805
  try {
642
- return deserialize(ctx, node);
806
+ return deserialize(ctx, 0, node);
643
807
  } catch (error) {
644
808
  throw new SerovalDeserializationError(error);
645
809
  }