seroval 0.5.1 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/index.ts CHANGED
@@ -12,14 +12,6 @@ import deserializeTree from './tree/deserialize';
12
12
  import serializeTree, { resolvePatches } from './tree/serialize';
13
13
  import parseSync from './tree/sync';
14
14
  import { SerovalNode } from './tree/types';
15
- import {
16
- AsyncServerValue,
17
- PrimitiveValue,
18
- ServerValue,
19
- CommonServerValue,
20
- SemiPrimitiveValue,
21
- ErrorValue,
22
- } from './types';
23
15
 
24
16
  export {
25
17
  AsyncServerValue,
@@ -28,8 +20,8 @@ export {
28
20
  CommonServerValue,
29
21
  SemiPrimitiveValue,
30
22
  ErrorValue,
31
- Feature,
32
- };
23
+ } from './types';
24
+ export { Feature } from './compat';
33
25
 
34
26
  function finalize(
35
27
  ctx: SerializationContext,
@@ -67,7 +59,7 @@ function finalize(
67
59
  return result;
68
60
  }
69
61
 
70
- export function serialize<T extends ServerValue>(
62
+ export function serialize<T>(
71
63
  source: T,
72
64
  options?: Partial<Options>,
73
65
  ) {
@@ -78,7 +70,7 @@ export function serialize<T extends ServerValue>(
78
70
  return finalize(serial, rootID, isObject, result);
79
71
  }
80
72
 
81
- export async function serializeAsync<T extends AsyncServerValue>(
73
+ export async function serializeAsync<T>(
82
74
  source: T,
83
75
  options?: Partial<Options>,
84
76
  ) {
@@ -89,7 +81,7 @@ export async function serializeAsync<T extends AsyncServerValue>(
89
81
  return finalize(serial, rootID, isObject, result);
90
82
  }
91
83
 
92
- export function deserialize<T extends AsyncServerValue>(source: string): T {
84
+ export function deserialize<T>(source: string): T {
93
85
  // eslint-disable-next-line no-eval
94
86
  return (0, eval)(source) as T;
95
87
  }
@@ -102,7 +94,7 @@ export interface SerovalJSON {
102
94
  m: number[],
103
95
  }
104
96
 
105
- export function toJSON<T extends ServerValue>(
97
+ export function toJSON<T>(
106
98
  source: T,
107
99
  options?: Partial<Options>,
108
100
  ): SerovalJSON {
@@ -117,7 +109,7 @@ export function toJSON<T extends ServerValue>(
117
109
  };
118
110
  }
119
111
 
120
- export async function toJSONAsync<T extends AsyncServerValue>(
112
+ export async function toJSONAsync<T>(
121
113
  source: T,
122
114
  options?: Partial<Options>,
123
115
  ): Promise<SerovalJSON> {
@@ -141,7 +133,7 @@ export function compileJSON(source: SerovalJSON): string {
141
133
  return finalize(serial, source.r, source.i, result);
142
134
  }
143
135
 
144
- export function fromJSON<T extends AsyncServerValue>(source: SerovalJSON): T {
136
+ export function fromJSON<T>(source: SerovalJSON): T {
145
137
  const serial = createSerializationContext({
146
138
  features: source.f,
147
139
  markedRefs: source.m,
@@ -150,3 +142,5 @@ export function fromJSON<T extends AsyncServerValue>(source: SerovalJSON): T {
150
142
  }
151
143
 
152
144
  export default serialize;
145
+
146
+ export { createReference } from './tree/reference';
@@ -1,40 +1,34 @@
1
+ export function serializeChar(str: string) {
2
+ switch (str) {
3
+ case '"': return '\\"';
4
+ case '\\': return '\\\\';
5
+ case '\n': return '\\n';
6
+ case '\r': return '\\r';
7
+ case '\b': return '\\b';
8
+ case '\t': return '\\t';
9
+ case '\f': return '\\f';
10
+ case '<': return '\\x3C';
11
+ case '\u2028': return '\\u2028';
12
+ case '\u2029': return '\\u2029';
13
+ default: return undefined;
14
+ }
15
+ }
16
+
1
17
  // Written by https://github.com/DylanPiercey and is distributed under the MIT license.
2
18
  // Creates a JavaScript double quoted string and escapes all characters
3
19
  // not listed as DoubleStringCharacters on
4
20
  // Also includes "<" to escape "</script>" and "\" to avoid invalid escapes in the output.
5
21
  // http://www.ecma-international.org/ecma-262/5.1/#sec-7.8.4
6
- export default function quote(str: string) {
22
+ export function serializeString(str: string) {
7
23
  let result = '';
8
24
  let lastPos = 0;
25
+ let replacement: string | undefined;
9
26
  for (let i = 0, len = str.length; i < len; i++) {
10
- let replacement;
11
- switch (str[i]) {
12
- case '"':
13
- replacement = '\\"';
14
- break;
15
- case '\\':
16
- replacement = '\\\\';
17
- break;
18
- case '<':
19
- replacement = '\\x3C';
20
- break;
21
- case '\n':
22
- replacement = '\\n';
23
- break;
24
- case '\r':
25
- replacement = '\\r';
26
- break;
27
- case '\u2028':
28
- replacement = '\\u2028';
29
- break;
30
- case '\u2029':
31
- replacement = '\\u2029';
32
- break;
33
- default:
34
- continue;
27
+ replacement = serializeChar(str[i]);
28
+ if (replacement) {
29
+ result += str.slice(lastPos, i) + replacement;
30
+ lastPos = i + 1;
35
31
  }
36
- result += str.slice(lastPos, i) + replacement;
37
- lastPos = i + 1;
38
32
  }
39
33
  if (lastPos === 0) {
40
34
  result = str;
@@ -44,13 +38,16 @@ export default function quote(str: string) {
44
38
  return result;
45
39
  }
46
40
 
47
- export function invQuote(str: string): string {
41
+ export function deserializeString(str: string): string {
48
42
  return str
49
43
  .replace(/\\"/g, '"')
50
44
  .replace(/\\\\/g, '\\')
51
- .replace(/\\x3C/g, '<')
52
45
  .replace(/\\n/g, '\n')
53
46
  .replace(/\\r/g, '\r')
47
+ .replace(/\\b/g, '\b')
48
+ .replace(/\\t/g, '\t')
49
+ .replace(/\\f/g, '\f')
50
+ .replace(/\\x3C/g, '<')
54
51
  .replace(/\\u2028/g, '\u2028')
55
52
  .replace(/\\u2029/g, '\u2029');
56
53
  }
package/src/tree/async.ts CHANGED
@@ -2,9 +2,9 @@
2
2
  /* eslint-disable @typescript-eslint/no-use-before-define */
3
3
  import assert from '../assert';
4
4
  import { Feature } from '../compat';
5
- import { createRef, getRootID, ParserContext } from '../context';
5
+ import { createIndexedValue, getRootID, ParserContext } from '../context';
6
+ import { serializeString } from '../string';
6
7
  import {
7
- AsyncServerValue,
8
8
  BigIntTypedArrayValue,
9
9
  TypedArrayValue,
10
10
  } from '../types';
@@ -13,7 +13,7 @@ import {
13
13
  createBigIntTypedArrayNode,
14
14
  createDateNode,
15
15
  createNumberNode,
16
- createReferenceNode,
16
+ createIndexedValueNode,
17
17
  createRegExpNode,
18
18
  createStringNode,
19
19
  createTypedArrayNode,
@@ -26,18 +26,24 @@ import {
26
26
  NULL_NODE,
27
27
  TRUE_NODE,
28
28
  UNDEFINED_NODE,
29
+ createReferenceNode,
30
+ createArrayBufferNode,
31
+ createDataViewNode,
29
32
  } from './primitives';
33
+ import { hasReferenceID } from './reference';
30
34
  import {
31
35
  getErrorConstructorName,
32
36
  getErrorOptions,
33
37
  getIterableOptions,
34
38
  isIterable,
35
39
  } from './shared';
36
- import { INV_SYMBOL_REF } from './symbols';
40
+ import { WellKnownSymbols } from './symbols';
37
41
  import {
38
42
  SerovalAggregateErrorNode,
39
43
  SerovalArrayNode,
40
44
  SerovalErrorNode,
45
+ SerovalFormDataNode,
46
+ SerovalHeadersNode,
41
47
  SerovalIterableNode,
42
48
  SerovalMapNode,
43
49
  SerovalNode,
@@ -48,6 +54,12 @@ import {
48
54
  SerovalPromiseNode,
49
55
  SerovalSetNode,
50
56
  } from './types';
57
+ import {
58
+ createBlobNode,
59
+ createFileNode,
60
+ createURLNode,
61
+ createURLSearchParamsNode,
62
+ } from './web-api';
51
63
 
52
64
  type ObjectLikeNode =
53
65
  | SerovalObjectNode
@@ -56,18 +68,18 @@ type ObjectLikeNode =
56
68
  | SerovalPromiseNode;
57
69
 
58
70
  type ObjectLikeValue =
59
- | Record<string, AsyncServerValue>
60
- | Iterable<AsyncServerValue>
61
- | PromiseLike<AsyncServerValue>;
71
+ | Record<string, unknown>
72
+ | Iterable<unknown>
73
+ | PromiseLike<unknown>;
62
74
 
63
75
  async function generateNodeList(
64
76
  ctx: ParserContext,
65
- current: AsyncServerValue[],
77
+ current: unknown[],
66
78
  ) {
67
79
  const size = current.length;
68
80
  const nodes = new Array<SerovalNode>(size);
69
- const deferred = new Array<AsyncServerValue>(size);
70
- let item: AsyncServerValue;
81
+ const deferred = new Array<unknown>(size);
82
+ let item: unknown;
71
83
  for (let i = 0; i < size; i++) {
72
84
  if (i in current) {
73
85
  item = current[i];
@@ -89,7 +101,7 @@ async function generateNodeList(
89
101
  async function generateArrayNode(
90
102
  ctx: ParserContext,
91
103
  id: number,
92
- current: AsyncServerValue[],
104
+ current: unknown[],
93
105
  ): Promise<SerovalArrayNode> {
94
106
  return {
95
107
  t: SerovalNodeType.Array,
@@ -101,20 +113,21 @@ async function generateArrayNode(
101
113
  d: undefined,
102
114
  a: await generateNodeList(ctx, current),
103
115
  f: undefined,
116
+ b: undefined,
104
117
  };
105
118
  }
106
119
 
107
120
  async function generateMapNode(
108
121
  ctx: ParserContext,
109
122
  id: number,
110
- current: Map<AsyncServerValue, AsyncServerValue>,
123
+ current: Map<unknown, unknown>,
111
124
  ): Promise<SerovalMapNode> {
112
125
  assert(ctx.features & Feature.Map, 'Unsupported type "Map"');
113
126
  const len = current.size;
114
127
  const keyNodes = new Array<SerovalNode>(len);
115
128
  const valueNodes = new Array<SerovalNode>(len);
116
- const deferredKey = new Array<AsyncServerValue>(len);
117
- const deferredValue = new Array<AsyncServerValue>(len);
129
+ const deferredKey = new Array<unknown>(len);
130
+ const deferredValue = new Array<unknown>(len);
118
131
  let deferredSize = 0;
119
132
  let nodeSize = 0;
120
133
  for (const [key, value] of current.entries()) {
@@ -143,18 +156,19 @@ async function generateMapNode(
143
156
  d: { k: keyNodes, v: valueNodes, s: len },
144
157
  a: undefined,
145
158
  f: undefined,
159
+ b: undefined,
146
160
  };
147
161
  }
148
162
 
149
163
  async function generateSetNode(
150
164
  ctx: ParserContext,
151
165
  id: number,
152
- current: Set<AsyncServerValue>,
166
+ current: Set<unknown>,
153
167
  ): Promise<SerovalSetNode> {
154
168
  assert(ctx.features & Feature.Set, 'Unsupported type "Set"');
155
169
  const len = current.size;
156
170
  const nodes = new Array<SerovalNode>(len);
157
- const deferred = new Array<AsyncServerValue>(len);
171
+ const deferred = new Array<unknown>(len);
158
172
  let deferredSize = 0;
159
173
  let nodeSize = 0;
160
174
  for (const item of current.keys()) {
@@ -179,30 +193,33 @@ async function generateSetNode(
179
193
  d: undefined,
180
194
  a: nodes,
181
195
  f: undefined,
196
+ b: undefined,
182
197
  };
183
198
  }
184
199
 
185
200
  async function generateProperties(
186
201
  ctx: ParserContext,
187
- properties: Record<string, AsyncServerValue>,
202
+ properties: Record<string, unknown>,
188
203
  ): Promise<SerovalObjectRecordNode> {
189
204
  const keys = Object.keys(properties);
190
205
  const size = keys.length;
191
206
  const keyNodes = new Array<string>(size);
192
207
  const valueNodes = new Array<SerovalNode>(size);
193
208
  const deferredKeys = new Array<string>(size);
194
- const deferredValues = new Array<AsyncServerValue>(size);
209
+ const deferredValues = new Array<unknown>(size);
195
210
  let deferredSize = 0;
196
211
  let nodesSize = 0;
197
- let item: AsyncServerValue;
212
+ let item: unknown;
213
+ let escaped: string;
198
214
  for (const key of keys) {
199
215
  item = properties[key];
216
+ escaped = serializeString(key);
200
217
  if (isIterable(item)) {
201
- deferredKeys[deferredSize] = key;
218
+ deferredKeys[deferredSize] = escaped;
202
219
  deferredValues[deferredSize] = item;
203
220
  deferredSize++;
204
221
  } else {
205
- keyNodes[nodesSize] = key;
222
+ keyNodes[nodesSize] = escaped;
206
223
  valueNodes[nodesSize] = await parse(ctx, item);
207
224
  nodesSize++;
208
225
  }
@@ -221,7 +238,7 @@ async function generateProperties(
221
238
  async function generateIterableNode(
222
239
  ctx: ParserContext,
223
240
  id: number,
224
- current: Iterable<AsyncServerValue>,
241
+ current: Iterable<unknown>,
225
242
  ): Promise<SerovalIterableNode> {
226
243
  assert(ctx.features & Feature.Symbol, 'Unsupported type "Iterable"');
227
244
  const options = getIterableOptions(current);
@@ -235,17 +252,18 @@ async function generateIterableNode(
235
252
  m: undefined,
236
253
  // Parse options first before the items
237
254
  d: options
238
- ? await generateProperties(ctx, options as Record<string, AsyncServerValue>)
255
+ ? await generateProperties(ctx, options)
239
256
  : undefined,
240
257
  a: await generateNodeList(ctx, array),
241
258
  f: undefined,
259
+ b: undefined,
242
260
  };
243
261
  }
244
262
 
245
263
  async function generatePromiseNode(
246
264
  ctx: ParserContext,
247
265
  id: number,
248
- current: PromiseLike<AsyncServerValue>,
266
+ current: PromiseLike<unknown>,
249
267
  ): Promise<SerovalPromiseNode> {
250
268
  assert(ctx.features & Feature.Promise, 'Unsupported type "Promise"');
251
269
  return current.then(async (value) => ({
@@ -259,6 +277,7 @@ async function generatePromiseNode(
259
277
  d: undefined,
260
278
  a: undefined,
261
279
  f: await parse(ctx, value),
280
+ b: undefined,
262
281
  }));
263
282
  }
264
283
 
@@ -272,7 +291,7 @@ async function generateObjectNode(
272
291
  return generateIterableNode(ctx, id, current);
273
292
  }
274
293
  if ('then' in current && typeof current.then === 'function') {
275
- return generatePromiseNode(ctx, id, current as PromiseLike<AsyncServerValue>);
294
+ return generatePromiseNode(ctx, id, current as PromiseLike<unknown>);
276
295
  }
277
296
  return {
278
297
  t: empty ? SerovalNodeType.NullConstructor : SerovalNodeType.Object,
@@ -281,9 +300,10 @@ async function generateObjectNode(
281
300
  l: undefined,
282
301
  c: undefined,
283
302
  m: undefined,
284
- d: await generateProperties(ctx, current as Record<string, AsyncServerValue>),
303
+ d: await generateProperties(ctx, current as Record<string, unknown>),
285
304
  a: undefined,
286
305
  f: undefined,
306
+ b: undefined,
287
307
  };
288
308
  }
289
309
 
@@ -300,12 +320,13 @@ async function generateAggregateErrorNode(
300
320
  t: SerovalNodeType.AggregateError,
301
321
  i: id,
302
322
  s: undefined,
303
- l: current.errors.length,
323
+ l: undefined,
304
324
  c: undefined,
305
- m: current.message,
325
+ m: serializeString(current.message),
306
326
  d: optionsNode,
307
- a: await generateNodeList(ctx, current.errors as AsyncServerValue[]),
327
+ a: undefined,
308
328
  f: undefined,
329
+ b: undefined,
309
330
  };
310
331
  }
311
332
 
@@ -324,16 +345,65 @@ async function generateErrorNode(
324
345
  s: undefined,
325
346
  l: undefined,
326
347
  c: getErrorConstructorName(current),
327
- m: current.message,
348
+ m: serializeString(current.message),
328
349
  d: optionsNode,
329
350
  a: undefined,
330
351
  f: undefined,
352
+ b: undefined,
353
+ };
354
+ }
355
+
356
+ async function generateHeadersNode(
357
+ ctx: ParserContext,
358
+ id: number,
359
+ current: Headers,
360
+ ): Promise<SerovalHeadersNode> {
361
+ assert(ctx.features & Feature.WebAPI, 'Unsupported type "Headers"');
362
+ const items: Record<string, string> = {};
363
+ current.forEach((value, key) => {
364
+ items[key] = value;
365
+ });
366
+ return {
367
+ t: SerovalNodeType.Headers,
368
+ i: id,
369
+ s: undefined,
370
+ l: undefined,
371
+ c: undefined,
372
+ m: undefined,
373
+ d: await generateProperties(ctx, items),
374
+ a: undefined,
375
+ f: undefined,
376
+ b: undefined,
377
+ };
378
+ }
379
+
380
+ async function generateFormDataNode(
381
+ ctx: ParserContext,
382
+ id: number,
383
+ current: FormData,
384
+ ): Promise<SerovalFormDataNode> {
385
+ assert(ctx.features & Feature.WebAPI, 'Unsupported type "FormData"');
386
+ const items: Record<string, FormDataEntryValue> = {};
387
+ current.forEach((value, key) => {
388
+ items[key] = value;
389
+ });
390
+ return {
391
+ t: SerovalNodeType.FormData,
392
+ i: id,
393
+ s: undefined,
394
+ l: undefined,
395
+ c: undefined,
396
+ m: undefined,
397
+ d: await generateProperties(ctx, items),
398
+ a: undefined,
399
+ f: undefined,
400
+ b: undefined,
331
401
  };
332
402
  }
333
403
 
334
- async function parse(
404
+ async function parse<T>(
335
405
  ctx: ParserContext,
336
- current: AsyncServerValue,
406
+ current: T,
337
407
  ): Promise<SerovalNode> {
338
408
  switch (typeof current) {
339
409
  case 'boolean':
@@ -346,16 +416,16 @@ async function parse(
346
416
  switch (current) {
347
417
  case Infinity: return INFINITY_NODE;
348
418
  case -Infinity: return NEG_INFINITY_NODE;
349
- default: break;
350
- }
351
- // eslint-disable-next-line no-self-compare
352
- if (current !== current) {
353
- return NAN_NODE;
354
- }
355
- if (Object.is(current, -0)) {
356
- return NEG_ZERO_NODE;
419
+ default:
420
+ // eslint-disable-next-line no-self-compare
421
+ if (current !== current) {
422
+ return NAN_NODE;
423
+ }
424
+ if (Object.is(current, -0)) {
425
+ return NEG_ZERO_NODE;
426
+ }
427
+ return createNumberNode(current);
357
428
  }
358
- return createNumberNode(current);
359
429
  case 'bigint':
360
430
  return createBigIntNode(ctx, current);
361
431
  case 'object': {
@@ -364,20 +434,25 @@ async function parse(
364
434
  }
365
435
  // Non-primitive values needs a reference ID
366
436
  // mostly because the values themselves are stateful
367
- const id = createRef(ctx, current);
437
+ const id = createIndexedValue(ctx, current);
368
438
  if (ctx.markedRefs.has(id)) {
369
- return createReferenceNode(id);
439
+ return createIndexedValueNode(id);
440
+ }
441
+ if (hasReferenceID(current)) {
442
+ return createReferenceNode(id, current);
370
443
  }
371
444
  if (Array.isArray(current)) {
372
445
  return generateArrayNode(ctx, id, current);
373
446
  }
374
447
  switch (current.constructor) {
375
448
  case Date:
376
- return createDateNode(id, current as Date);
449
+ return createDateNode(id, current as unknown as Date);
377
450
  case RegExp:
378
- return createRegExpNode(id, current as RegExp);
451
+ return createRegExpNode(id, current as unknown as RegExp);
379
452
  case Promise:
380
- return generatePromiseNode(ctx, id, current as Promise<AsyncServerValue>);
453
+ return generatePromiseNode(ctx, id, current as unknown as Promise<unknown>);
454
+ case ArrayBuffer:
455
+ return createArrayBufferNode(id, current as unknown as ArrayBuffer);
381
456
  case Int8Array:
382
457
  case Int16Array:
383
458
  case Int32Array:
@@ -387,41 +462,43 @@ async function parse(
387
462
  case Uint8ClampedArray:
388
463
  case Float32Array:
389
464
  case Float64Array:
390
- return createTypedArrayNode(ctx, id, current as TypedArrayValue);
465
+ return createTypedArrayNode(ctx, id, current as unknown as TypedArrayValue);
391
466
  case BigInt64Array:
392
467
  case BigUint64Array:
393
- return createBigIntTypedArrayNode(ctx, id, current as BigIntTypedArrayValue);
468
+ return createBigIntTypedArrayNode(ctx, id, current as unknown as BigIntTypedArrayValue);
469
+ case DataView:
470
+ return createDataViewNode(ctx, id, current as unknown as DataView);
394
471
  case Map:
395
472
  return generateMapNode(
396
473
  ctx,
397
474
  id,
398
- current as Map<AsyncServerValue, AsyncServerValue>,
475
+ current as unknown as Map<unknown, unknown>,
399
476
  );
400
477
  case Set:
401
478
  return generateSetNode(
402
479
  ctx,
403
480
  id,
404
- current as Set<AsyncServerValue>,
481
+ current as unknown as Set<unknown>,
405
482
  );
406
483
  case Object:
407
484
  return generateObjectNode(
408
485
  ctx,
409
486
  id,
410
- current as Record<string, AsyncServerValue>,
487
+ current as Record<string, unknown>,
411
488
  false,
412
489
  );
413
490
  case undefined:
414
491
  return generateObjectNode(
415
492
  ctx,
416
493
  id,
417
- current as Record<string, AsyncServerValue>,
494
+ current as Record<string, unknown>,
418
495
  true,
419
496
  );
420
497
  case AggregateError:
421
498
  if (ctx.features & Feature.AggregateError) {
422
- return generateAggregateErrorNode(ctx, id, current as AggregateError);
499
+ return generateAggregateErrorNode(ctx, id, current as unknown as AggregateError);
423
500
  }
424
- return generateErrorNode(ctx, id, current as AggregateError);
501
+ return generateErrorNode(ctx, id, current as unknown as AggregateError);
425
502
  case Error:
426
503
  case EvalError:
427
504
  case RangeError:
@@ -429,7 +506,19 @@ async function parse(
429
506
  case SyntaxError:
430
507
  case TypeError:
431
508
  case URIError:
432
- return generateErrorNode(ctx, id, current as Error);
509
+ return generateErrorNode(ctx, id, current as unknown as Error);
510
+ case URL:
511
+ return createURLNode(ctx, id, current as unknown as URL);
512
+ case URLSearchParams:
513
+ return createURLSearchParamsNode(ctx, id, current as unknown as URLSearchParams);
514
+ case Blob:
515
+ return createBlobNode(ctx, id, current as unknown as Blob);
516
+ case File:
517
+ return createFileNode(ctx, id, current as unknown as File);
518
+ case Headers:
519
+ return generateHeadersNode(ctx, id, current as unknown as Headers);
520
+ case FormData:
521
+ return generateFormDataNode(ctx, id, current as unknown as FormData);
433
522
  default:
434
523
  break;
435
524
  }
@@ -447,26 +536,39 @@ async function parse(
447
536
  }
448
537
  // Generator functions don't have a global constructor
449
538
  if (Symbol.iterator in current) {
450
- return generateIterableNode(ctx, id, current);
539
+ return generateIterableNode(ctx, id, current as Iterable<unknown>);
451
540
  }
452
541
  // For Promise-like objects
453
542
  if ('then' in current && typeof current.then === 'function') {
454
- return generatePromiseNode(ctx, id, current as PromiseLike<AsyncServerValue>);
543
+ return generatePromiseNode(ctx, id, current as PromiseLike<unknown>);
455
544
  }
456
545
  throw new Error('Unsupported type');
457
546
  }
458
547
  case 'symbol':
459
- assert(ctx.features & Feature.Symbol, 'Unsupported type "symbol"');
460
- assert(current in INV_SYMBOL_REF, 'seroval only supports well-known symbols');
461
- return createWKSymbolNode(current);
548
+ if (hasReferenceID(current)) {
549
+ const id = createIndexedValue(ctx, current);
550
+ if (ctx.markedRefs.has(id)) {
551
+ return createIndexedValueNode(id);
552
+ }
553
+ return createReferenceNode(id, current);
554
+ }
555
+ return createWKSymbolNode(ctx, current as WellKnownSymbols);
556
+ case 'function': {
557
+ assert(hasReferenceID(current), 'Cannot serialize function without reference ID.');
558
+ const id = createIndexedValue(ctx, current);
559
+ if (ctx.markedRefs.has(id)) {
560
+ return createIndexedValueNode(id);
561
+ }
562
+ return createReferenceNode(id, current);
563
+ }
462
564
  default:
463
565
  throw new Error('Unsupported type');
464
566
  }
465
567
  }
466
568
 
467
- export default async function parseAsync(
569
+ export default async function parseAsync<T>(
468
570
  ctx: ParserContext,
469
- current: AsyncServerValue,
571
+ current: T,
470
572
  ) {
471
573
  const result = await parse(ctx, current);
472
574
  const isObject = result.t === SerovalNodeType.Object