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
@@ -22,7 +22,11 @@ import {
22
22
  } from '../base-primitives';
23
23
  import { Feature } from '../compat';
24
24
  import { NIL, SerovalNodeType } from '../constants';
25
- import { SerovalParserError, SerovalUnsupportedTypeError } from '../errors';
25
+ import {
26
+ SerovalDepthLimitError,
27
+ SerovalParserError,
28
+ SerovalUnsupportedTypeError,
29
+ } from '../errors';
26
30
  import { FALSE_NODE, NULL_NODE, TRUE_NODE, UNDEFINED_NODE } from '../literals';
27
31
  import { createSerovalNode } from '../node';
28
32
  import { OpaqueReference } from '../opaque-reference';
@@ -103,15 +107,18 @@ export function createSyncParserContext(
103
107
  return {
104
108
  type: ParserMode.Sync,
105
109
  base: createBaseParserContext(mode, options),
106
- child: undefined,
110
+ child: NIL,
107
111
  };
108
112
  }
109
113
 
110
114
  export class SyncParsePluginContext {
111
- constructor(private _p: SyncParserContext) {}
115
+ constructor(
116
+ private _p: SyncParserContext,
117
+ private depth: number,
118
+ ) {}
112
119
 
113
120
  parse<T>(current: T): SerovalNode {
114
- return parseSOS(this._p, current);
121
+ return parseSOS(this._p, this.depth, current);
115
122
  }
116
123
  }
117
124
 
@@ -124,18 +131,20 @@ export interface StreamParserContextOptions extends SyncParserContextOptions {
124
131
  export interface StreamParserContext {
125
132
  type: ParserMode.Stream;
126
133
  base: BaseParserContext;
127
- child: StreamParsePluginContext | undefined;
128
134
  state: StreamParserState;
129
135
  }
130
136
  export class StreamParsePluginContext {
131
- constructor(private _p: StreamParserContext) {}
137
+ constructor(
138
+ private _p: StreamParserContext,
139
+ private depth: number,
140
+ ) {}
132
141
 
133
142
  parse<T>(current: T): SerovalNode {
134
- return parseSOS(this._p, current);
143
+ return parseSOS(this._p, this.depth, current);
135
144
  }
136
145
 
137
146
  parseWithError<T>(current: T): SerovalNode | undefined {
138
- return parseWithError(this._p, current);
147
+ return parseWithError(this._p, this.depth, current);
139
148
  }
140
149
 
141
150
  isAlive(): boolean {
@@ -194,18 +203,23 @@ export function createStreamParserContext(
194
203
  return {
195
204
  type: ParserMode.Stream,
196
205
  base: createBaseParserContext(SerovalMode.Cross, options),
197
- child: undefined,
198
206
  state: createStreamParserState(options),
199
207
  };
200
208
  }
201
209
 
202
210
  type SOSParserContext = SyncParserContext | StreamParserContext;
203
211
 
204
- function parseItems(ctx: SOSParserContext, current: unknown[]): SerovalNode[] {
205
- const nodes = [];
212
+ function parseItems(
213
+ ctx: SOSParserContext,
214
+ depth: number,
215
+ current: unknown[],
216
+ ): (SerovalNode | 0)[] {
217
+ const nodes: (SerovalNode | 0)[] = [];
206
218
  for (let i = 0, len = current.length; i < len; i++) {
207
219
  if (i in current) {
208
- nodes[i] = parseSOS(ctx, current[i]);
220
+ nodes[i] = parseSOS(ctx, depth, current[i]);
221
+ } else {
222
+ nodes[i] = 0;
209
223
  }
210
224
  }
211
225
  return nodes;
@@ -213,14 +227,16 @@ function parseItems(ctx: SOSParserContext, current: unknown[]): SerovalNode[] {
213
227
 
214
228
  function parseArray(
215
229
  ctx: SOSParserContext,
230
+ depth: number,
216
231
  id: number,
217
232
  current: unknown[],
218
233
  ): SerovalArrayNode {
219
- return createArrayNode(id, current, parseItems(ctx, current));
234
+ return createArrayNode(id, current, parseItems(ctx, depth, current));
220
235
  }
221
236
 
222
237
  function parseProperties(
223
238
  ctx: SOSParserContext,
239
+ depth: number,
224
240
  properties: Record<string | symbol, unknown>,
225
241
  ): SerovalObjectRecordNode {
226
242
  const entries = Object.entries(properties);
@@ -228,7 +244,7 @@ function parseProperties(
228
244
  const valueNodes: SerovalNode[] = [];
229
245
  for (let i = 0, len = entries.length; i < len; i++) {
230
246
  keyNodes.push(serializeString(entries[i][0]));
231
- valueNodes.push(parseSOS(ctx, entries[i][1]));
247
+ valueNodes.push(parseSOS(ctx, depth, entries[i][1]));
232
248
  }
233
249
  // Check special properties, symbols in this case
234
250
  if (SYM_ITERATOR in properties) {
@@ -238,6 +254,7 @@ function parseProperties(
238
254
  parseIteratorFactory(ctx.base),
239
255
  parseSOS(
240
256
  ctx,
257
+ depth,
241
258
  iteratorToSequence(properties as unknown as Iterable<unknown>),
242
259
  ),
243
260
  ),
@@ -250,6 +267,7 @@ function parseProperties(
250
267
  parseAsyncIteratorFactory(ctx.base),
251
268
  parseSOS(
252
269
  ctx,
270
+ depth,
253
271
  ctx.type === ParserMode.Sync
254
272
  ? createStream()
255
273
  : createStreamFromAsyncIterable(
@@ -272,53 +290,71 @@ function parseProperties(
272
290
  return {
273
291
  k: keyNodes,
274
292
  v: valueNodes,
275
- s: keyNodes.length,
276
293
  };
277
294
  }
278
295
 
279
296
  function parsePlainObject(
280
297
  ctx: SOSParserContext,
298
+ depth: number,
281
299
  id: number,
282
300
  current: Record<string, unknown>,
283
301
  empty: boolean,
284
302
  ): ObjectLikeNode {
285
- return createObjectNode(id, current, empty, parseProperties(ctx, current));
303
+ return createObjectNode(
304
+ id,
305
+ current,
306
+ empty,
307
+ parseProperties(ctx, depth, current),
308
+ );
286
309
  }
287
310
 
288
311
  function parseBoxed(
289
312
  ctx: SOSParserContext,
313
+ depth: number,
290
314
  id: number,
291
315
  current: object,
292
316
  ): SerovalBoxedNode {
293
- return createBoxedNode(id, parseSOS(ctx, current.valueOf()));
317
+ return createBoxedNode(id, parseSOS(ctx, depth, current.valueOf()));
294
318
  }
295
319
 
296
320
  function parseTypedArray(
297
321
  ctx: SOSParserContext,
322
+ depth: number,
298
323
  id: number,
299
324
  current: TypedArrayValue,
300
325
  ): SerovalTypedArrayNode {
301
- return createTypedArrayNode(id, current, parseSOS(ctx, current.buffer));
326
+ return createTypedArrayNode(
327
+ id,
328
+ current,
329
+ parseSOS(ctx, depth, current.buffer),
330
+ );
302
331
  }
303
332
 
304
333
  function parseBigIntTypedArray(
305
334
  ctx: SOSParserContext,
335
+ depth: number,
306
336
  id: number,
307
337
  current: BigIntTypedArrayValue,
308
338
  ): SerovalBigIntTypedArrayNode {
309
- return createBigIntTypedArrayNode(id, current, parseSOS(ctx, current.buffer));
339
+ return createBigIntTypedArrayNode(
340
+ id,
341
+ current,
342
+ parseSOS(ctx, depth, current.buffer),
343
+ );
310
344
  }
311
345
 
312
346
  function parseDataView(
313
347
  ctx: SOSParserContext,
348
+ depth: number,
314
349
  id: number,
315
350
  current: DataView,
316
351
  ): SerovalDataViewNode {
317
- return createDataViewNode(id, current, parseSOS(ctx, current.buffer));
352
+ return createDataViewNode(id, current, parseSOS(ctx, depth, current.buffer));
318
353
  }
319
354
 
320
355
  function parseError(
321
356
  ctx: SOSParserContext,
357
+ depth: number,
322
358
  id: number,
323
359
  current: Error,
324
360
  ): SerovalErrorNode {
@@ -326,12 +362,13 @@ function parseError(
326
362
  return createErrorNode(
327
363
  id,
328
364
  current,
329
- options ? parseProperties(ctx, options) : NIL,
365
+ options ? parseProperties(ctx, depth, options) : NIL,
330
366
  );
331
367
  }
332
368
 
333
369
  function parseAggregateError(
334
370
  ctx: SOSParserContext,
371
+ depth: number,
335
372
  id: number,
336
373
  current: AggregateError,
337
374
  ): SerovalAggregateErrorNode {
@@ -339,38 +376,41 @@ function parseAggregateError(
339
376
  return createAggregateErrorNode(
340
377
  id,
341
378
  current,
342
- options ? parseProperties(ctx, options) : NIL,
379
+ options ? parseProperties(ctx, depth, options) : NIL,
343
380
  );
344
381
  }
345
382
 
346
383
  function parseMap(
347
384
  ctx: SOSParserContext,
385
+ depth: number,
348
386
  id: number,
349
387
  current: Map<unknown, unknown>,
350
388
  ): SerovalMapNode {
351
389
  const keyNodes: SerovalNode[] = [];
352
390
  const valueNodes: SerovalNode[] = [];
353
391
  for (const [key, value] of current.entries()) {
354
- keyNodes.push(parseSOS(ctx, key));
355
- valueNodes.push(parseSOS(ctx, value));
392
+ keyNodes.push(parseSOS(ctx, depth, key));
393
+ valueNodes.push(parseSOS(ctx, depth, value));
356
394
  }
357
- return createMapNode(ctx.base, id, keyNodes, valueNodes, current.size);
395
+ return createMapNode(ctx.base, id, keyNodes, valueNodes);
358
396
  }
359
397
 
360
398
  function parseSet(
361
399
  ctx: SOSParserContext,
400
+ depth: number,
362
401
  id: number,
363
402
  current: Set<unknown>,
364
403
  ): SerovalSetNode {
365
404
  const items: SerovalNode[] = [];
366
405
  for (const item of current.keys()) {
367
- items.push(parseSOS(ctx, item));
406
+ items.push(parseSOS(ctx, depth, item));
368
407
  }
369
- return createSetNode(id, current.size, items);
408
+ return createSetNode(id, items);
370
409
  }
371
410
 
372
411
  function parseStream(
373
412
  ctx: SOSParserContext,
413
+ depth: number,
374
414
  id: number,
375
415
  current: Stream<unknown>,
376
416
  ): SerovalNode {
@@ -386,7 +426,7 @@ function parseStream(
386
426
  current.on({
387
427
  next: value => {
388
428
  if (ctx.state.alive) {
389
- const parsed = parseWithError(ctx, value);
429
+ const parsed = parseWithError(ctx, depth, value);
390
430
  if (parsed) {
391
431
  onParse(ctx, createStreamNextNode(id, parsed));
392
432
  }
@@ -394,7 +434,7 @@ function parseStream(
394
434
  },
395
435
  throw: value => {
396
436
  if (ctx.state.alive) {
397
- const parsed = parseWithError(ctx, value);
437
+ const parsed = parseWithError(ctx, depth, value);
398
438
  if (parsed) {
399
439
  onParse(ctx, createStreamThrowNode(id, parsed));
400
440
  }
@@ -403,7 +443,7 @@ function parseStream(
403
443
  },
404
444
  return: value => {
405
445
  if (ctx.state.alive) {
406
- const parsed = parseWithError(ctx, value);
446
+ const parsed = parseWithError(ctx, depth, value);
407
447
  if (parsed) {
408
448
  onParse(ctx, createStreamReturnNode(id, parsed));
409
449
  }
@@ -417,10 +457,11 @@ function parseStream(
417
457
  function handlePromiseSuccess(
418
458
  this: StreamParserContext,
419
459
  id: number,
460
+ depth: number,
420
461
  data: unknown,
421
462
  ): void {
422
463
  if (this.state.alive) {
423
- const parsed = parseWithError(this, data);
464
+ const parsed = parseWithError(this, depth, data);
424
465
  if (parsed) {
425
466
  onParse(
426
467
  this,
@@ -432,7 +473,6 @@ function handlePromiseSuccess(
432
473
  NIL,
433
474
  NIL,
434
475
  NIL,
435
- NIL,
436
476
  [
437
477
  parseSpecialReference(this.base, SpecialReference.PromiseSuccess),
438
478
  parsed,
@@ -440,6 +480,7 @@ function handlePromiseSuccess(
440
480
  NIL,
441
481
  NIL,
442
482
  NIL,
483
+ NIL,
443
484
  ),
444
485
  );
445
486
  }
@@ -450,10 +491,11 @@ function handlePromiseSuccess(
450
491
  function handlePromiseFailure(
451
492
  this: StreamParserContext,
452
493
  id: number,
494
+ depth: number,
453
495
  data: unknown,
454
496
  ): void {
455
497
  if (this.state.alive) {
456
- const parsed = parseWithError(this, data);
498
+ const parsed = parseWithError(this, depth, data);
457
499
  if (parsed) {
458
500
  onParse(
459
501
  this,
@@ -465,7 +507,6 @@ function handlePromiseFailure(
465
507
  NIL,
466
508
  NIL,
467
509
  NIL,
468
- NIL,
469
510
  [
470
511
  parseSpecialReference(this.base, SpecialReference.PromiseFailure),
471
512
  parsed,
@@ -473,6 +514,7 @@ function handlePromiseFailure(
473
514
  NIL,
474
515
  NIL,
475
516
  NIL,
517
+ NIL,
476
518
  ),
477
519
  );
478
520
  }
@@ -482,6 +524,7 @@ function handlePromiseFailure(
482
524
 
483
525
  function parsePromise(
484
526
  ctx: SOSParserContext,
527
+ depth: number,
485
528
  id: number,
486
529
  current: Promise<unknown>,
487
530
  ): SerovalPromiseConstructorNode {
@@ -490,8 +533,8 @@ function parsePromise(
490
533
  if (ctx.type === ParserMode.Stream) {
491
534
  pushPendingState(ctx);
492
535
  current.then(
493
- handlePromiseSuccess.bind(ctx, resolver),
494
- handlePromiseFailure.bind(ctx, resolver),
536
+ handlePromiseSuccess.bind(ctx, resolver, depth),
537
+ handlePromiseFailure.bind(ctx, resolver, depth),
495
538
  );
496
539
  }
497
540
  return createPromiseConstructorNode(ctx.base, id, resolver);
@@ -499,6 +542,7 @@ function parsePromise(
499
542
 
500
543
  function parsePluginSync(
501
544
  ctx: SyncParserContext,
545
+ depth: number,
502
546
  id: number,
503
547
  current: unknown,
504
548
  currentPlugins: Plugin<any, any>[],
@@ -506,23 +550,21 @@ function parsePluginSync(
506
550
  for (let i = 0, len = currentPlugins.length; i < len; i++) {
507
551
  const plugin = currentPlugins[i];
508
552
  if (plugin.parse.sync && plugin.test(current)) {
509
- if (ctx.child == null) {
510
- ctx.child = new SyncParsePluginContext(ctx);
511
- }
512
553
  return createPluginNode(
513
554
  id,
514
555
  plugin.tag,
515
- plugin.parse.sync(current, ctx.child, {
556
+ plugin.parse.sync(current, new SyncParsePluginContext(ctx, depth), {
516
557
  id,
517
558
  }),
518
559
  );
519
560
  }
520
561
  }
521
- return undefined;
562
+ return NIL;
522
563
  }
523
564
 
524
565
  function parsePluginStream(
525
566
  ctx: StreamParserContext,
567
+ depth: number,
526
568
  id: number,
527
569
  current: unknown,
528
570
  currentPlugins: Plugin<any, any>[],
@@ -530,37 +572,36 @@ function parsePluginStream(
530
572
  for (let i = 0, len = currentPlugins.length; i < len; i++) {
531
573
  const plugin = currentPlugins[i];
532
574
  if (plugin.parse.stream && plugin.test(current)) {
533
- if (ctx.child == null) {
534
- ctx.child = new StreamParsePluginContext(ctx);
535
- }
536
575
  return createPluginNode(
537
576
  id,
538
577
  plugin.tag,
539
- plugin.parse.stream(current, ctx.child, {
578
+ plugin.parse.stream(current, new StreamParsePluginContext(ctx, depth), {
540
579
  id,
541
580
  }),
542
581
  );
543
582
  }
544
583
  }
545
- return undefined;
584
+ return NIL;
546
585
  }
547
586
 
548
587
  function parsePlugin(
549
588
  ctx: SOSParserContext,
589
+ depth: number,
550
590
  id: number,
551
591
  current: unknown,
552
592
  ): SerovalPluginNode | undefined {
553
593
  const currentPlugins = ctx.base.plugins;
554
594
  if (currentPlugins) {
555
595
  return ctx.type === ParserMode.Sync
556
- ? parsePluginSync(ctx, id, current, currentPlugins)
557
- : parsePluginStream(ctx, id, current, currentPlugins);
596
+ ? parsePluginSync(ctx, depth, id, current, currentPlugins)
597
+ : parsePluginStream(ctx, depth, id, current, currentPlugins);
558
598
  }
559
- return undefined;
599
+ return NIL;
560
600
  }
561
601
 
562
602
  function parseObjectPhase2(
563
603
  ctx: SOSParserContext,
604
+ depth: number,
564
605
  id: number,
565
606
  current: object,
566
607
  currentClass: unknown,
@@ -569,21 +610,21 @@ function parseObjectPhase2(
569
610
  case Object:
570
611
  return parsePlainObject(
571
612
  ctx,
613
+ depth,
572
614
  id,
573
615
  current as Record<string, unknown>,
574
616
  false,
575
617
  );
576
- case undefined:
618
+ case NIL:
577
619
  return parsePlainObject(
578
620
  ctx,
621
+ depth,
579
622
  id,
580
623
  current as Record<string, unknown>,
581
624
  true,
582
625
  );
583
626
  case Date:
584
627
  return createDateNode(id, current as unknown as Date);
585
- case RegExp:
586
- return createRegExpNode(id, current as unknown as RegExp);
587
628
  case Error:
588
629
  case EvalError:
589
630
  case RangeError:
@@ -591,12 +632,12 @@ function parseObjectPhase2(
591
632
  case SyntaxError:
592
633
  case TypeError:
593
634
  case URIError:
594
- return parseError(ctx, id, current as unknown as Error);
635
+ return parseError(ctx, depth, id, current as unknown as Error);
595
636
  case Number:
596
637
  case Boolean:
597
638
  case String:
598
639
  case BigInt:
599
- return parseBoxed(ctx, id, current);
640
+ return parseBoxed(ctx, depth, id, current);
600
641
  case ArrayBuffer:
601
642
  return createArrayBufferNode(
602
643
  ctx.base,
@@ -612,21 +653,34 @@ function parseObjectPhase2(
612
653
  case Uint8ClampedArray:
613
654
  case Float32Array:
614
655
  case Float64Array:
615
- return parseTypedArray(ctx, id, current as unknown as TypedArrayValue);
656
+ return parseTypedArray(
657
+ ctx,
658
+ depth,
659
+ id,
660
+ current as unknown as TypedArrayValue,
661
+ );
616
662
  case DataView:
617
- return parseDataView(ctx, id, current as unknown as DataView);
663
+ return parseDataView(ctx, depth, id, current as unknown as DataView);
618
664
  case Map:
619
- return parseMap(ctx, id, current as unknown as Map<unknown, unknown>);
665
+ return parseMap(
666
+ ctx,
667
+ depth,
668
+ id,
669
+ current as unknown as Map<unknown, unknown>,
670
+ );
620
671
  case Set:
621
- return parseSet(ctx, id, current as unknown as Set<unknown>);
672
+ return parseSet(ctx, depth, id, current as unknown as Set<unknown>);
622
673
  default:
623
674
  break;
624
675
  }
625
676
  // Promises
626
677
  if (currentClass === Promise || current instanceof Promise) {
627
- return parsePromise(ctx, id, current as unknown as Promise<unknown>);
678
+ return parsePromise(ctx, depth, id, current as unknown as Promise<unknown>);
628
679
  }
629
680
  const currentFeatures = ctx.base.features;
681
+ if (currentFeatures & Feature.RegExp && currentClass === RegExp) {
682
+ return createRegExpNode(id, current as unknown as RegExp);
683
+ }
630
684
  // BigInt Typed Arrays
631
685
  if (currentFeatures & Feature.BigIntTypedArray) {
632
686
  switch (currentClass) {
@@ -634,6 +688,7 @@ function parseObjectPhase2(
634
688
  case BigUint64Array:
635
689
  return parseBigIntTypedArray(
636
690
  ctx,
691
+ depth,
637
692
  id,
638
693
  current as unknown as BigIntTypedArrayValue,
639
694
  );
@@ -646,59 +701,77 @@ function parseObjectPhase2(
646
701
  typeof AggregateError !== 'undefined' &&
647
702
  (currentClass === AggregateError || current instanceof AggregateError)
648
703
  ) {
649
- return parseAggregateError(ctx, id, current as unknown as AggregateError);
704
+ return parseAggregateError(
705
+ ctx,
706
+ depth,
707
+ id,
708
+ current as unknown as AggregateError,
709
+ );
650
710
  }
651
711
  // Slow path. We only need to handle Errors and Iterators
652
712
  // since they have very broad implementations.
653
713
  if (current instanceof Error) {
654
- return parseError(ctx, id, current);
714
+ return parseError(ctx, depth, id, current);
655
715
  }
656
716
  // Generator functions don't have a global constructor
657
717
  // despite existing
658
718
  if (SYM_ITERATOR in current || SYM_ASYNC_ITERATOR in current) {
659
- return parsePlainObject(ctx, id, current, !!currentClass);
719
+ return parsePlainObject(ctx, depth, id, current, !!currentClass);
660
720
  }
661
721
  throw new SerovalUnsupportedTypeError(current);
662
722
  }
663
723
 
664
724
  function parseObject(
665
725
  ctx: SOSParserContext,
726
+ depth: number,
666
727
  id: number,
667
728
  current: object,
668
729
  ): SerovalNode {
669
730
  if (Array.isArray(current)) {
670
- return parseArray(ctx, id, current);
731
+ return parseArray(ctx, depth, id, current);
671
732
  }
672
733
  if (isStream(current)) {
673
- return parseStream(ctx, id, current);
734
+ return parseStream(ctx, depth, id, current);
674
735
  }
675
736
  const currentClass = current.constructor;
676
737
  if (currentClass === OpaqueReference) {
677
738
  return parseSOS(
678
739
  ctx,
740
+ depth,
679
741
  (current as OpaqueReference<unknown, unknown>).replacement,
680
742
  );
681
743
  }
682
- const parsed = parsePlugin(ctx, id, current);
744
+ const parsed = parsePlugin(ctx, depth, id, current);
683
745
  if (parsed) {
684
746
  return parsed;
685
747
  }
686
- return parseObjectPhase2(ctx, id, current, currentClass);
748
+ return parseObjectPhase2(ctx, depth, id, current, currentClass);
687
749
  }
688
750
 
689
- function parseFunction(ctx: SOSParserContext, current: unknown): SerovalNode {
751
+ function parseFunction(
752
+ ctx: SOSParserContext,
753
+ depth: number,
754
+ current: unknown,
755
+ ): SerovalNode {
690
756
  const ref = getReferenceNode(ctx.base, current);
691
757
  if (ref.type !== ParserNodeType.Fresh) {
692
758
  return ref.value;
693
759
  }
694
- const plugin = parsePlugin(ctx, ref.value, current);
760
+ const plugin = parsePlugin(ctx, depth, ref.value, current);
695
761
  if (plugin) {
696
762
  return plugin;
697
763
  }
698
764
  throw new SerovalUnsupportedTypeError(current);
699
765
  }
700
766
 
701
- export function parseSOS<T>(ctx: SOSParserContext, current: T): SerovalNode {
767
+ export function parseSOS<T>(
768
+ ctx: SOSParserContext,
769
+ depth: number,
770
+ current: T,
771
+ ): SerovalNode {
772
+ if (depth >= ctx.base.depthLimit) {
773
+ throw new SerovalDepthLimitError(ctx.base.depthLimit);
774
+ }
702
775
  switch (typeof current) {
703
776
  case 'boolean':
704
777
  return current ? TRUE_NODE : FALSE_NODE;
@@ -714,7 +787,7 @@ export function parseSOS<T>(ctx: SOSParserContext, current: T): SerovalNode {
714
787
  if (current) {
715
788
  const ref = getReferenceNode(ctx.base, current);
716
789
  return ref.type === ParserNodeType.Fresh
717
- ? parseObject(ctx, ref.value, current as object)
790
+ ? parseObject(ctx, depth + 1, ref.value, current as object)
718
791
  : ref.value;
719
792
  }
720
793
  return NULL_NODE;
@@ -722,7 +795,7 @@ export function parseSOS<T>(ctx: SOSParserContext, current: T): SerovalNode {
722
795
  case 'symbol':
723
796
  return parseWellKnownSymbol(ctx.base, current);
724
797
  case 'function': {
725
- return parseFunction(ctx, current);
798
+ return parseFunction(ctx, depth, current);
726
799
  }
727
800
  default:
728
801
  throw new SerovalUnsupportedTypeError(current);
@@ -731,7 +804,7 @@ export function parseSOS<T>(ctx: SOSParserContext, current: T): SerovalNode {
731
804
 
732
805
  export function parseTop<T>(ctx: SyncParserContext, current: T): SerovalNode {
733
806
  try {
734
- return parseSOS(ctx, current);
807
+ return parseSOS(ctx, 0, current);
735
808
  } catch (error) {
736
809
  throw error instanceof SerovalParserError
737
810
  ? error
@@ -787,12 +860,13 @@ function popPendingState(ctx: StreamParserContext): void {
787
860
  }
788
861
  }
789
862
 
790
- export function parseWithError<T>(
863
+ function parseWithError<T>(
791
864
  ctx: StreamParserContext,
865
+ depth: number,
792
866
  current: T,
793
867
  ): SerovalNode | undefined {
794
868
  try {
795
- return parseSOS(ctx, current);
869
+ return parseSOS(ctx, depth, current);
796
870
  } catch (err) {
797
871
  onError(ctx, err);
798
872
  return NIL;
@@ -803,7 +877,7 @@ export function startStreamParse<T>(
803
877
  ctx: StreamParserContext,
804
878
  current: T,
805
879
  ): void {
806
- const parsed = parseWithError(ctx, current);
880
+ const parsed = parseWithError(ctx, 0, current);
807
881
  if (parsed) {
808
882
  onParseInternal(ctx, parsed, true);
809
883
  ctx.state.initial = false;
@@ -181,6 +181,8 @@ export function fromCrossJSON<T>(
181
181
  const ctx = createCrossDeserializerContext({
182
182
  plugins,
183
183
  refs: options.refs,
184
+ features: options.features,
185
+ disabledFeatures: options.disabledFeatures,
184
186
  });
185
187
  return deserializeTop(ctx, source) as T;
186
188
  }