seroval 0.5.1 → 0.6.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/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 quote from '../quote';
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,14 +26,16 @@ import {
26
26
  NULL_NODE,
27
27
  TRUE_NODE,
28
28
  UNDEFINED_NODE,
29
+ createReferenceNode,
29
30
  } from './primitives';
31
+ import { hasReferenceID } from './reference';
30
32
  import {
31
33
  getErrorConstructorName,
32
34
  getErrorOptions,
33
35
  getIterableOptions,
34
36
  isIterable,
35
37
  } from './shared';
36
- import { INV_SYMBOL_REF } from './symbols';
38
+ import { WellKnownSymbols } from './symbols';
37
39
  import {
38
40
  SerovalAggregateErrorNode,
39
41
  SerovalArrayNode,
@@ -48,6 +50,7 @@ import {
48
50
  SerovalPromiseNode,
49
51
  SerovalSetNode,
50
52
  } from './types';
53
+ import { createURLNode, createURLSearchParamsNode } from './web-api';
51
54
 
52
55
  type ObjectLikeNode =
53
56
  | SerovalObjectNode
@@ -56,18 +59,18 @@ type ObjectLikeNode =
56
59
  | SerovalPromiseNode;
57
60
 
58
61
  type ObjectLikeValue =
59
- | Record<string, AsyncServerValue>
60
- | Iterable<AsyncServerValue>
61
- | PromiseLike<AsyncServerValue>;
62
+ | Record<string, unknown>
63
+ | Iterable<unknown>
64
+ | PromiseLike<unknown>;
62
65
 
63
66
  async function generateNodeList(
64
67
  ctx: ParserContext,
65
- current: AsyncServerValue[],
68
+ current: unknown[],
66
69
  ) {
67
70
  const size = current.length;
68
71
  const nodes = new Array<SerovalNode>(size);
69
- const deferred = new Array<AsyncServerValue>(size);
70
- let item: AsyncServerValue;
72
+ const deferred = new Array<unknown>(size);
73
+ let item: unknown;
71
74
  for (let i = 0; i < size; i++) {
72
75
  if (i in current) {
73
76
  item = current[i];
@@ -89,7 +92,7 @@ async function generateNodeList(
89
92
  async function generateArrayNode(
90
93
  ctx: ParserContext,
91
94
  id: number,
92
- current: AsyncServerValue[],
95
+ current: unknown[],
93
96
  ): Promise<SerovalArrayNode> {
94
97
  return {
95
98
  t: SerovalNodeType.Array,
@@ -107,14 +110,14 @@ async function generateArrayNode(
107
110
  async function generateMapNode(
108
111
  ctx: ParserContext,
109
112
  id: number,
110
- current: Map<AsyncServerValue, AsyncServerValue>,
113
+ current: Map<unknown, unknown>,
111
114
  ): Promise<SerovalMapNode> {
112
115
  assert(ctx.features & Feature.Map, 'Unsupported type "Map"');
113
116
  const len = current.size;
114
117
  const keyNodes = new Array<SerovalNode>(len);
115
118
  const valueNodes = new Array<SerovalNode>(len);
116
- const deferredKey = new Array<AsyncServerValue>(len);
117
- const deferredValue = new Array<AsyncServerValue>(len);
119
+ const deferredKey = new Array<unknown>(len);
120
+ const deferredValue = new Array<unknown>(len);
118
121
  let deferredSize = 0;
119
122
  let nodeSize = 0;
120
123
  for (const [key, value] of current.entries()) {
@@ -149,12 +152,12 @@ async function generateMapNode(
149
152
  async function generateSetNode(
150
153
  ctx: ParserContext,
151
154
  id: number,
152
- current: Set<AsyncServerValue>,
155
+ current: Set<unknown>,
153
156
  ): Promise<SerovalSetNode> {
154
157
  assert(ctx.features & Feature.Set, 'Unsupported type "Set"');
155
158
  const len = current.size;
156
159
  const nodes = new Array<SerovalNode>(len);
157
- const deferred = new Array<AsyncServerValue>(len);
160
+ const deferred = new Array<unknown>(len);
158
161
  let deferredSize = 0;
159
162
  let nodeSize = 0;
160
163
  for (const item of current.keys()) {
@@ -184,17 +187,17 @@ async function generateSetNode(
184
187
 
185
188
  async function generateProperties(
186
189
  ctx: ParserContext,
187
- properties: Record<string, AsyncServerValue>,
190
+ properties: Record<string, unknown>,
188
191
  ): Promise<SerovalObjectRecordNode> {
189
192
  const keys = Object.keys(properties);
190
193
  const size = keys.length;
191
194
  const keyNodes = new Array<string>(size);
192
195
  const valueNodes = new Array<SerovalNode>(size);
193
196
  const deferredKeys = new Array<string>(size);
194
- const deferredValues = new Array<AsyncServerValue>(size);
197
+ const deferredValues = new Array<unknown>(size);
195
198
  let deferredSize = 0;
196
199
  let nodesSize = 0;
197
- let item: AsyncServerValue;
200
+ let item: unknown;
198
201
  for (const key of keys) {
199
202
  item = properties[key];
200
203
  if (isIterable(item)) {
@@ -221,7 +224,7 @@ async function generateProperties(
221
224
  async function generateIterableNode(
222
225
  ctx: ParserContext,
223
226
  id: number,
224
- current: Iterable<AsyncServerValue>,
227
+ current: Iterable<unknown>,
225
228
  ): Promise<SerovalIterableNode> {
226
229
  assert(ctx.features & Feature.Symbol, 'Unsupported type "Iterable"');
227
230
  const options = getIterableOptions(current);
@@ -235,7 +238,7 @@ async function generateIterableNode(
235
238
  m: undefined,
236
239
  // Parse options first before the items
237
240
  d: options
238
- ? await generateProperties(ctx, options as Record<string, AsyncServerValue>)
241
+ ? await generateProperties(ctx, options)
239
242
  : undefined,
240
243
  a: await generateNodeList(ctx, array),
241
244
  f: undefined,
@@ -245,7 +248,7 @@ async function generateIterableNode(
245
248
  async function generatePromiseNode(
246
249
  ctx: ParserContext,
247
250
  id: number,
248
- current: PromiseLike<AsyncServerValue>,
251
+ current: PromiseLike<unknown>,
249
252
  ): Promise<SerovalPromiseNode> {
250
253
  assert(ctx.features & Feature.Promise, 'Unsupported type "Promise"');
251
254
  return current.then(async (value) => ({
@@ -272,7 +275,7 @@ async function generateObjectNode(
272
275
  return generateIterableNode(ctx, id, current);
273
276
  }
274
277
  if ('then' in current && typeof current.then === 'function') {
275
- return generatePromiseNode(ctx, id, current as PromiseLike<AsyncServerValue>);
278
+ return generatePromiseNode(ctx, id, current as PromiseLike<unknown>);
276
279
  }
277
280
  return {
278
281
  t: empty ? SerovalNodeType.NullConstructor : SerovalNodeType.Object,
@@ -281,7 +284,7 @@ async function generateObjectNode(
281
284
  l: undefined,
282
285
  c: undefined,
283
286
  m: undefined,
284
- d: await generateProperties(ctx, current as Record<string, AsyncServerValue>),
287
+ d: await generateProperties(ctx, current as Record<string, unknown>),
285
288
  a: undefined,
286
289
  f: undefined,
287
290
  };
@@ -302,9 +305,9 @@ async function generateAggregateErrorNode(
302
305
  s: undefined,
303
306
  l: current.errors.length,
304
307
  c: undefined,
305
- m: current.message,
308
+ m: quote(current.message),
306
309
  d: optionsNode,
307
- a: await generateNodeList(ctx, current.errors as AsyncServerValue[]),
310
+ a: await generateNodeList(ctx, current.errors as unknown[]),
308
311
  f: undefined,
309
312
  };
310
313
  }
@@ -324,16 +327,16 @@ async function generateErrorNode(
324
327
  s: undefined,
325
328
  l: undefined,
326
329
  c: getErrorConstructorName(current),
327
- m: current.message,
330
+ m: quote(current.message),
328
331
  d: optionsNode,
329
332
  a: undefined,
330
333
  f: undefined,
331
334
  };
332
335
  }
333
336
 
334
- async function parse(
337
+ async function parse<T>(
335
338
  ctx: ParserContext,
336
- current: AsyncServerValue,
339
+ current: T,
337
340
  ): Promise<SerovalNode> {
338
341
  switch (typeof current) {
339
342
  case 'boolean':
@@ -346,16 +349,16 @@ async function parse(
346
349
  switch (current) {
347
350
  case Infinity: return INFINITY_NODE;
348
351
  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;
352
+ default:
353
+ // eslint-disable-next-line no-self-compare
354
+ if (current !== current) {
355
+ return NAN_NODE;
356
+ }
357
+ if (Object.is(current, -0)) {
358
+ return NEG_ZERO_NODE;
359
+ }
360
+ return createNumberNode(current);
357
361
  }
358
- return createNumberNode(current);
359
362
  case 'bigint':
360
363
  return createBigIntNode(ctx, current);
361
364
  case 'object': {
@@ -364,20 +367,23 @@ async function parse(
364
367
  }
365
368
  // Non-primitive values needs a reference ID
366
369
  // mostly because the values themselves are stateful
367
- const id = createRef(ctx, current);
370
+ const id = createIndexedValue(ctx, current);
368
371
  if (ctx.markedRefs.has(id)) {
369
- return createReferenceNode(id);
372
+ return createIndexedValueNode(id);
373
+ }
374
+ if (hasReferenceID(current)) {
375
+ return createReferenceNode(id, current);
370
376
  }
371
377
  if (Array.isArray(current)) {
372
378
  return generateArrayNode(ctx, id, current);
373
379
  }
374
380
  switch (current.constructor) {
375
381
  case Date:
376
- return createDateNode(id, current as Date);
382
+ return createDateNode(id, current as unknown as Date);
377
383
  case RegExp:
378
- return createRegExpNode(id, current as RegExp);
384
+ return createRegExpNode(id, current as unknown as RegExp);
379
385
  case Promise:
380
- return generatePromiseNode(ctx, id, current as Promise<AsyncServerValue>);
386
+ return generatePromiseNode(ctx, id, current as unknown as Promise<unknown>);
381
387
  case Int8Array:
382
388
  case Int16Array:
383
389
  case Int32Array:
@@ -387,41 +393,41 @@ async function parse(
387
393
  case Uint8ClampedArray:
388
394
  case Float32Array:
389
395
  case Float64Array:
390
- return createTypedArrayNode(ctx, id, current as TypedArrayValue);
396
+ return createTypedArrayNode(ctx, id, current as unknown as TypedArrayValue);
391
397
  case BigInt64Array:
392
398
  case BigUint64Array:
393
- return createBigIntTypedArrayNode(ctx, id, current as BigIntTypedArrayValue);
399
+ return createBigIntTypedArrayNode(ctx, id, current as unknown as BigIntTypedArrayValue);
394
400
  case Map:
395
401
  return generateMapNode(
396
402
  ctx,
397
403
  id,
398
- current as Map<AsyncServerValue, AsyncServerValue>,
404
+ current as unknown as Map<unknown, unknown>,
399
405
  );
400
406
  case Set:
401
407
  return generateSetNode(
402
408
  ctx,
403
409
  id,
404
- current as Set<AsyncServerValue>,
410
+ current as unknown as Set<unknown>,
405
411
  );
406
412
  case Object:
407
413
  return generateObjectNode(
408
414
  ctx,
409
415
  id,
410
- current as Record<string, AsyncServerValue>,
416
+ current as Record<string, unknown>,
411
417
  false,
412
418
  );
413
419
  case undefined:
414
420
  return generateObjectNode(
415
421
  ctx,
416
422
  id,
417
- current as Record<string, AsyncServerValue>,
423
+ current as Record<string, unknown>,
418
424
  true,
419
425
  );
420
426
  case AggregateError:
421
427
  if (ctx.features & Feature.AggregateError) {
422
- return generateAggregateErrorNode(ctx, id, current as AggregateError);
428
+ return generateAggregateErrorNode(ctx, id, current as unknown as AggregateError);
423
429
  }
424
- return generateErrorNode(ctx, id, current as AggregateError);
430
+ return generateErrorNode(ctx, id, current as unknown as AggregateError);
425
431
  case Error:
426
432
  case EvalError:
427
433
  case RangeError:
@@ -429,7 +435,11 @@ async function parse(
429
435
  case SyntaxError:
430
436
  case TypeError:
431
437
  case URIError:
432
- return generateErrorNode(ctx, id, current as Error);
438
+ return generateErrorNode(ctx, id, current as unknown as Error);
439
+ case URL:
440
+ return createURLNode(ctx, id, current as unknown as URL);
441
+ case URLSearchParams:
442
+ return createURLSearchParamsNode(ctx, id, current as unknown as URLSearchParams);
433
443
  default:
434
444
  break;
435
445
  }
@@ -447,26 +457,39 @@ async function parse(
447
457
  }
448
458
  // Generator functions don't have a global constructor
449
459
  if (Symbol.iterator in current) {
450
- return generateIterableNode(ctx, id, current);
460
+ return generateIterableNode(ctx, id, current as Iterable<unknown>);
451
461
  }
452
462
  // For Promise-like objects
453
463
  if ('then' in current && typeof current.then === 'function') {
454
- return generatePromiseNode(ctx, id, current as PromiseLike<AsyncServerValue>);
464
+ return generatePromiseNode(ctx, id, current as PromiseLike<unknown>);
455
465
  }
456
466
  throw new Error('Unsupported type');
457
467
  }
458
468
  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);
469
+ if (hasReferenceID(current)) {
470
+ const id = createIndexedValue(ctx, current);
471
+ if (ctx.markedRefs.has(id)) {
472
+ return createIndexedValueNode(id);
473
+ }
474
+ return createReferenceNode(id, current);
475
+ }
476
+ return createWKSymbolNode(ctx, current as WellKnownSymbols);
477
+ case 'function': {
478
+ assert(hasReferenceID(current), 'Cannot serialize function without reference ID.');
479
+ const id = createIndexedValue(ctx, current);
480
+ if (ctx.markedRefs.has(id)) {
481
+ return createIndexedValueNode(id);
482
+ }
483
+ return createReferenceNode(id, current);
484
+ }
462
485
  default:
463
486
  throw new Error('Unsupported type');
464
487
  }
465
488
  }
466
489
 
467
- export default async function parseAsync(
490
+ export default async function parseAsync<T>(
468
491
  ctx: ParserContext,
469
- current: AsyncServerValue,
492
+ current: T,
470
493
  ) {
471
494
  const result = await parse(ctx, current);
472
495
  const isObject = result.t === SerovalNodeType.Object
@@ -4,6 +4,7 @@ import {
4
4
  } from '../context';
5
5
  import { invQuote } from '../quote';
6
6
  import { AsyncServerValue } from '../types';
7
+ import { getReference } from './reference';
7
8
  import { getErrorConstructor, getTypedArrayConstructor } from './shared';
8
9
  import { SYMBOL_REF } from './symbols';
9
10
  import {
@@ -23,7 +24,7 @@ import {
23
24
  SerovalTypedArrayNode,
24
25
  } from './types';
25
26
 
26
- function assignRef<T extends AsyncServerValue>(
27
+ function assignIndexedValue<T>(
27
28
  ctx: SerializationContext,
28
29
  index: number,
29
30
  value: T,
@@ -35,7 +36,7 @@ function assignRef<T extends AsyncServerValue>(
35
36
  function deserializeNodeList(
36
37
  ctx: SerializationContext,
37
38
  node: SerovalArrayNode | SerovalIterableNode | SerovalAggregateErrorNode,
38
- result: AsyncServerValue[],
39
+ result: unknown[],
39
40
  ) {
40
41
  let item: SerovalNode;
41
42
  for (let i = 0, len = node.a.length; i < len; i++) {
@@ -51,7 +52,7 @@ function deserializeArray(
51
52
  ctx: SerializationContext,
52
53
  node: SerovalArrayNode,
53
54
  ) {
54
- const result: AsyncServerValue[] = assignRef(
55
+ const result: AsyncServerValue[] = assignIndexedValue(
55
56
  ctx,
56
57
  node.i,
57
58
  new Array<AsyncServerValue>(node.l),
@@ -65,7 +66,7 @@ function deserializeArray(
65
66
  function deserializeProperties(
66
67
  ctx: SerializationContext,
67
68
  node: SerovalObjectRecordNode,
68
- result: Record<string, AsyncServerValue>,
69
+ result: Record<string, unknown>,
69
70
  ) {
70
71
  if (node.s === 0) {
71
72
  return {};
@@ -80,7 +81,11 @@ function deserializeNullConstructor(
80
81
  ctx: SerializationContext,
81
82
  node: SerovalNullConstructorNode,
82
83
  ) {
83
- const result = assignRef(ctx, node.i, Object.create(null) as Record<string, AsyncServerValue>);
84
+ const result = assignIndexedValue(
85
+ ctx,
86
+ node.i,
87
+ Object.create(null) as Record<string, AsyncServerValue>,
88
+ );
84
89
  ctx.stack.push(node.i);
85
90
  deserializeProperties(ctx, node.d, result);
86
91
  ctx.stack.pop();
@@ -91,7 +96,7 @@ function deserializeObject(
91
96
  ctx: SerializationContext,
92
97
  node: SerovalObjectNode,
93
98
  ) {
94
- const result = assignRef(ctx, node.i, {} as Record<string, AsyncServerValue>);
99
+ const result = assignIndexedValue(ctx, node.i, {} as Record<string, AsyncServerValue>);
95
100
  ctx.stack.push(node.i);
96
101
  deserializeProperties(ctx, node.d, result);
97
102
  ctx.stack.pop();
@@ -102,7 +107,7 @@ function deserializeSet(
102
107
  ctx: SerializationContext,
103
108
  node: SerovalSetNode,
104
109
  ) {
105
- const result = assignRef(ctx, node.i, new Set<AsyncServerValue>());
110
+ const result = assignIndexedValue(ctx, node.i, new Set<unknown>());
106
111
  ctx.stack.push(node.i);
107
112
  for (let i = 0, len = node.a.length; i < len; i++) {
108
113
  result.add(deserializeTree(ctx, node.a[i]));
@@ -115,10 +120,10 @@ function deserializeMap(
115
120
  ctx: SerializationContext,
116
121
  node: SerovalMapNode,
117
122
  ) {
118
- const result = assignRef(
123
+ const result = assignIndexedValue(
119
124
  ctx,
120
125
  node.i,
121
- new Map<AsyncServerValue, AsyncServerValue>(),
126
+ new Map<unknown, unknown>(),
122
127
  );
123
128
  ctx.stack.push(node.i);
124
129
  for (let i = 0; i < node.d.s; i++) {
@@ -153,7 +158,7 @@ function deserializeAggregateError(
153
158
  node: SerovalAggregateErrorNode,
154
159
  ) {
155
160
  // Serialize the required arguments
156
- const result = assignRef(ctx, node.i, new AggregateError([], node.m));
161
+ const result = assignIndexedValue(ctx, node.i, new AggregateError([], invQuote(node.m)));
157
162
  ctx.stack.push(node.i);
158
163
  result.errors = deserializeNodeList(ctx, node, new Array<AsyncServerValue>(node.l));
159
164
  ctx.stack.pop();
@@ -168,13 +173,13 @@ function deserializeError(
168
173
  node: SerovalErrorNode,
169
174
  ) {
170
175
  const ErrorConstructor = getErrorConstructor(node.c);
171
- const result = assignRef(ctx, node.i, new ErrorConstructor(node.m));
176
+ const result = assignIndexedValue(ctx, node.i, new ErrorConstructor(invQuote(node.m)));
172
177
  return deserializeDictionary(ctx, node, result);
173
178
  }
174
179
 
175
180
  interface Deferred {
176
- resolve(value: AsyncServerValue): void;
177
- promise: Promise<AsyncServerValue>;
181
+ resolve(value: unknown): void;
182
+ promise: Promise<unknown>;
178
183
  }
179
184
 
180
185
  function createDeferred(): Deferred {
@@ -194,7 +199,7 @@ function deserializePromise(
194
199
  node: SerovalPromiseNode,
195
200
  ) {
196
201
  const deferred = createDeferred();
197
- const result = assignRef(ctx, node.i, deferred.promise);
202
+ const result = assignIndexedValue(ctx, node.i, deferred.promise);
198
203
  deferred.resolve(deserializeTree(ctx, node.f));
199
204
  return result;
200
205
  }
@@ -205,7 +210,7 @@ function deserializeTypedArray(
205
210
  ) {
206
211
  const TypedArray = getTypedArrayConstructor(node.c);
207
212
  const dummy = new TypedArray();
208
- const result = assignRef(ctx, node.i, new TypedArray(
213
+ const result = assignIndexedValue(ctx, node.i, new TypedArray(
209
214
  dummy.buffer,
210
215
  node.l,
211
216
  ));
@@ -223,7 +228,7 @@ function deserializeIterable(
223
228
  ) {
224
229
  const values: AsyncServerValue[] = [];
225
230
  deserializeNodeList(ctx, node, values);
226
- const result = assignRef(ctx, node.i, {
231
+ const result = assignIndexedValue(ctx, node.i, {
227
232
  [Symbol.iterator]: () => values.values(),
228
233
  });
229
234
  return deserializeDictionary(ctx, node, result);
@@ -232,7 +237,7 @@ function deserializeIterable(
232
237
  export default function deserializeTree(
233
238
  ctx: SerializationContext,
234
239
  node: SerovalNode,
235
- ): AsyncServerValue {
240
+ ): unknown {
236
241
  switch (node.t) {
237
242
  case SerovalNodeType.Number:
238
243
  case SerovalNodeType.Boolean:
@@ -253,7 +258,7 @@ export default function deserializeTree(
253
258
  return NaN;
254
259
  case SerovalNodeType.BigInt:
255
260
  return BigInt(node.s);
256
- case SerovalNodeType.Reference:
261
+ case SerovalNodeType.IndexedValue:
257
262
  return ctx.valueMap.get(node.i);
258
263
  case SerovalNodeType.Array:
259
264
  return deserializeArray(ctx, node);
@@ -262,9 +267,9 @@ export default function deserializeTree(
262
267
  case SerovalNodeType.NullConstructor:
263
268
  return deserializeNullConstructor(ctx, node);
264
269
  case SerovalNodeType.Date:
265
- return assignRef(ctx, node.i, new Date(node.s));
270
+ return assignIndexedValue(ctx, node.i, new Date(node.s));
266
271
  case SerovalNodeType.RegExp:
267
- return assignRef(ctx, node.i, new RegExp(node.c, node.m));
272
+ return assignIndexedValue(ctx, node.i, new RegExp(node.c, node.m));
268
273
  case SerovalNodeType.Set:
269
274
  return deserializeSet(ctx, node);
270
275
  case SerovalNodeType.Map:
@@ -282,6 +287,12 @@ export default function deserializeTree(
282
287
  return deserializePromise(ctx, node);
283
288
  case SerovalNodeType.WKSymbol:
284
289
  return SYMBOL_REF[node.s];
290
+ case SerovalNodeType.URL:
291
+ return assignIndexedValue(ctx, node.i, new URL(node.s));
292
+ case SerovalNodeType.URLSearchParams:
293
+ return assignIndexedValue(ctx, node.i, new URLSearchParams(node.s));
294
+ case SerovalNodeType.Reference:
295
+ return assignIndexedValue(ctx, node.i, getReference(invQuote(node.s)));
285
296
  default:
286
297
  throw new Error('Unsupported type');
287
298
  }
@@ -3,6 +3,7 @@ import { Feature } from '../compat';
3
3
  import { ParserContext } from '../context';
4
4
  import quote from '../quote';
5
5
  import { BigIntTypedArrayValue, TypedArrayValue } from '../types';
6
+ import { getReferenceID } from './reference';
6
7
  import { INV_SYMBOL_REF, WellKnownSymbols } from './symbols';
7
8
  import {
8
9
  SerovalBigIntNode,
@@ -16,12 +17,13 @@ import {
16
17
  SerovalNodeType,
17
18
  SerovalNullNode,
18
19
  SerovalNumberNode,
19
- SerovalReferenceNode,
20
+ SerovalIndexedValueNode,
20
21
  SerovalRegExpNode,
21
22
  SerovalStringNode,
22
23
  SerovalTypedArrayNode,
23
24
  SerovalUndefinedNode,
24
25
  SerovalWKSymbolNode,
26
+ SerovalReferenceNode,
25
27
  } from './types';
26
28
 
27
29
  export const TRUE_NODE: SerovalBooleanNode = {
@@ -159,9 +161,9 @@ export function createBigIntNode(
159
161
  };
160
162
  }
161
163
 
162
- export function createReferenceNode(id: number): SerovalReferenceNode {
164
+ export function createIndexedValueNode(id: number): SerovalIndexedValueNode {
163
165
  return {
164
- t: SerovalNodeType.Reference,
166
+ t: SerovalNodeType.IndexedValue,
165
167
  i: id,
166
168
  s: undefined,
167
169
  l: undefined,
@@ -257,8 +259,11 @@ export function createBigIntTypedArrayNode(
257
259
  }
258
260
 
259
261
  export function createWKSymbolNode(
262
+ ctx: ParserContext,
260
263
  current: WellKnownSymbols,
261
264
  ): SerovalWKSymbolNode {
265
+ assert(ctx.features & Feature.Symbol, 'Unsupported type "symbol"');
266
+ assert(current in INV_SYMBOL_REF, 'seroval only supports well-known symbols');
262
267
  return {
263
268
  t: SerovalNodeType.WKSymbol,
264
269
  i: undefined,
@@ -271,3 +276,20 @@ export function createWKSymbolNode(
271
276
  f: undefined,
272
277
  };
273
278
  }
279
+
280
+ export function createReferenceNode<T>(
281
+ id: number,
282
+ ref: T,
283
+ ): SerovalReferenceNode {
284
+ return {
285
+ t: SerovalNodeType.Reference,
286
+ i: id,
287
+ s: quote(getReferenceID(ref)),
288
+ l: undefined,
289
+ c: undefined,
290
+ m: undefined,
291
+ d: undefined,
292
+ a: undefined,
293
+ f: undefined,
294
+ };
295
+ }
@@ -0,0 +1,61 @@
1
+ /* eslint-disable no-restricted-globals */
2
+ import assert from '../assert';
3
+
4
+ const REFERENCE = new Map<unknown, string>();
5
+ const INV_REFERENCE = new Map<string, unknown>();
6
+
7
+ export function createReference<T>(id: string, value: T): T {
8
+ REFERENCE.set(value, id);
9
+ INV_REFERENCE.set(id, value);
10
+ return value;
11
+ }
12
+
13
+ export function hasReferenceID<T>(value: T): boolean {
14
+ return REFERENCE.has(value);
15
+ }
16
+
17
+ export function hasReference(id: string): boolean {
18
+ return INV_REFERENCE.has(id);
19
+ }
20
+
21
+ export function getReferenceID<T>(value: T): string {
22
+ assert(hasReferenceID(value), 'Missing reference id');
23
+ return REFERENCE.get(value) as string;
24
+ }
25
+
26
+ export function getReference<T>(id: string): T {
27
+ assert(hasReference(id), 'Missing reference for id:' + id);
28
+ return INV_REFERENCE.get(id) as T;
29
+ }
30
+
31
+ export const GLOBAL_KEY = '__SEROVAL__';
32
+
33
+ if (typeof globalThis !== undefined) {
34
+ Object.defineProperty(globalThis, GLOBAL_KEY, {
35
+ value: INV_REFERENCE,
36
+ configurable: true,
37
+ writable: false,
38
+ enumerable: false,
39
+ });
40
+ } else if (typeof window !== undefined) {
41
+ Object.defineProperty(window, GLOBAL_KEY, {
42
+ value: INV_REFERENCE,
43
+ configurable: true,
44
+ writable: false,
45
+ enumerable: false,
46
+ });
47
+ } else if (typeof self !== undefined) {
48
+ Object.defineProperty(self, GLOBAL_KEY, {
49
+ value: INV_REFERENCE,
50
+ configurable: true,
51
+ writable: false,
52
+ enumerable: false,
53
+ });
54
+ } else if (typeof global !== undefined) {
55
+ Object.defineProperty(global, GLOBAL_KEY, {
56
+ value: INV_REFERENCE,
57
+ configurable: true,
58
+ writable: false,
59
+ enumerable: false,
60
+ });
61
+ }