seroval 0.5.0 → 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/sync.ts CHANGED
@@ -1,14 +1,15 @@
1
1
  /* eslint-disable @typescript-eslint/no-use-before-define */
2
2
  import assert from '../assert';
3
3
  import { Feature } from '../compat';
4
- import { createRef, ParserContext } from '../context';
5
- import { BigIntTypedArrayValue, ServerValue, TypedArrayValue } from '../types';
4
+ import { createIndexedValue, getRootID, ParserContext } from '../context';
5
+ import quote from '../quote';
6
+ import { BigIntTypedArrayValue, TypedArrayValue } from '../types';
6
7
  import {
7
8
  createBigIntNode,
8
9
  createBigIntTypedArrayNode,
9
10
  createDateNode,
10
11
  createNumberNode,
11
- createReferenceNode,
12
+ createIndexedValueNode,
12
13
  createRegExpNode,
13
14
  createStringNode,
14
15
  createTypedArrayNode,
@@ -21,14 +22,18 @@ import {
21
22
  NULL_NODE,
22
23
  TRUE_NODE,
23
24
  UNDEFINED_NODE,
25
+ createReferenceNode,
24
26
  } from './primitives';
27
+ import {
28
+ hasReferenceID,
29
+ } from './reference';
25
30
  import {
26
31
  getErrorConstructorName,
27
32
  getErrorOptions,
28
33
  getIterableOptions,
29
34
  isIterable,
30
35
  } from './shared';
31
- import { INV_SYMBOL_REF } from './symbols';
36
+ import { WellKnownSymbols } from './symbols';
32
37
  import {
33
38
  SerovalAggregateErrorNode,
34
39
  SerovalArrayNode,
@@ -42,14 +47,15 @@ import {
42
47
  SerovalObjectRecordNode,
43
48
  SerovalSetNode,
44
49
  } from './types';
50
+ import { createURLNode, createURLSearchParamsNode } from './web-api';
45
51
 
46
52
  type ObjectLikeNode = SerovalObjectNode | SerovalNullConstructorNode | SerovalIterableNode;
47
53
 
48
- function generateNodeList(ctx: ParserContext, current: ServerValue[]) {
54
+ function generateNodeList(ctx: ParserContext, current: unknown[]) {
49
55
  const size = current.length;
50
56
  const nodes = new Array<SerovalNode>(size);
51
- const deferred = new Array<ServerValue>(size);
52
- let item: ServerValue;
57
+ const deferred = new Array<unknown>(size);
58
+ let item: unknown;
53
59
  for (let i = 0; i < size; i++) {
54
60
  if (i in current) {
55
61
  item = current[i];
@@ -71,7 +77,7 @@ function generateNodeList(ctx: ParserContext, current: ServerValue[]) {
71
77
  function generateArrayNode(
72
78
  ctx: ParserContext,
73
79
  id: number,
74
- current: ServerValue[],
80
+ current: unknown[],
75
81
  ): SerovalArrayNode {
76
82
  return {
77
83
  t: SerovalNodeType.Array,
@@ -82,21 +88,21 @@ function generateArrayNode(
82
88
  m: undefined,
83
89
  d: undefined,
84
90
  a: generateNodeList(ctx, current),
85
- n: undefined,
91
+ f: undefined,
86
92
  };
87
93
  }
88
94
 
89
95
  function generateMapNode(
90
96
  ctx: ParserContext,
91
97
  id: number,
92
- current: Map<ServerValue, ServerValue>,
98
+ current: Map<unknown, unknown>,
93
99
  ): SerovalMapNode {
94
100
  assert(ctx.features & Feature.Map, 'Unsupported type "Map"');
95
101
  const len = current.size;
96
102
  const keyNodes = new Array<SerovalNode>(len);
97
103
  const valueNodes = new Array<SerovalNode>(len);
98
- const deferredKey = new Array<ServerValue>(len);
99
- const deferredValue = new Array<ServerValue>(len);
104
+ const deferredKey = new Array<unknown>(len);
105
+ const deferredValue = new Array<unknown>(len);
100
106
  let deferredSize = 0;
101
107
  let nodeSize = 0;
102
108
  for (const [key, value] of current.entries()) {
@@ -124,19 +130,19 @@ function generateMapNode(
124
130
  m: undefined,
125
131
  d: { k: keyNodes, v: valueNodes, s: len },
126
132
  a: undefined,
127
- n: undefined,
133
+ f: undefined,
128
134
  };
129
135
  }
130
136
 
131
137
  function generateSetNode(
132
138
  ctx: ParserContext,
133
139
  id: number,
134
- current: Set<ServerValue>,
140
+ current: Set<unknown>,
135
141
  ): SerovalSetNode {
136
142
  assert(ctx.features & Feature.Set, 'Unsupported type "Set"');
137
143
  const len = current.size;
138
144
  const nodes = new Array<SerovalNode>(len);
139
- const deferred = new Array<ServerValue>(len);
145
+ const deferred = new Array<unknown>(len);
140
146
  let deferredSize = 0;
141
147
  let nodeSize = 0;
142
148
  for (const item of current.keys()) {
@@ -160,23 +166,23 @@ function generateSetNode(
160
166
  m: undefined,
161
167
  d: undefined,
162
168
  a: nodes,
163
- n: undefined,
169
+ f: undefined,
164
170
  };
165
171
  }
166
172
 
167
173
  function generateProperties(
168
174
  ctx: ParserContext,
169
- properties: Record<string, ServerValue>,
175
+ properties: Record<string, unknown>,
170
176
  ): SerovalObjectRecordNode {
171
177
  const keys = Object.keys(properties);
172
178
  const size = keys.length;
173
179
  const keyNodes = new Array<string>(size);
174
180
  const valueNodes = new Array<SerovalNode>(size);
175
181
  const deferredKeys = new Array<string>(size);
176
- const deferredValues = new Array<ServerValue>(size);
182
+ const deferredValues = new Array<unknown>(size);
177
183
  let deferredSize = 0;
178
184
  let nodesSize = 0;
179
- let item: ServerValue;
185
+ let item: unknown;
180
186
  for (const key of keys) {
181
187
  item = properties[key];
182
188
  if (isIterable(item)) {
@@ -203,7 +209,7 @@ function generateProperties(
203
209
  function generateIterableNode(
204
210
  ctx: ParserContext,
205
211
  id: number,
206
- current: Iterable<ServerValue>,
212
+ current: Iterable<unknown>,
207
213
  ): SerovalIterableNode {
208
214
  assert(ctx.features & Feature.Symbol, 'Unsupported type "Iterable"');
209
215
  const options = getIterableOptions(current);
@@ -217,17 +223,17 @@ function generateIterableNode(
217
223
  m: undefined,
218
224
  // Parse options first before the items
219
225
  d: options
220
- ? generateProperties(ctx, options as Record<string, ServerValue>)
226
+ ? generateProperties(ctx, options)
221
227
  : undefined,
222
228
  a: generateNodeList(ctx, array),
223
- n: undefined,
229
+ f: undefined,
224
230
  };
225
231
  }
226
232
 
227
233
  function generateObjectNode(
228
234
  ctx: ParserContext,
229
235
  id: number,
230
- current: Record<string, ServerValue> | Iterable<ServerValue>,
236
+ current: Record<string, unknown> | Iterable<unknown>,
231
237
  empty: boolean,
232
238
  ): ObjectLikeNode {
233
239
  if (Symbol.iterator in current) {
@@ -242,7 +248,7 @@ function generateObjectNode(
242
248
  m: undefined,
243
249
  d: generateProperties(ctx, current),
244
250
  a: undefined,
245
- n: undefined,
251
+ f: undefined,
246
252
  };
247
253
  }
248
254
 
@@ -261,10 +267,10 @@ function generateAggregateErrorNode(
261
267
  s: undefined,
262
268
  l: current.errors.length,
263
269
  c: undefined,
264
- m: current.message,
270
+ m: quote(current.message),
265
271
  d: optionsNode,
266
- a: generateNodeList(ctx, current.errors as ServerValue[]),
267
- n: undefined,
272
+ a: generateNodeList(ctx, current.errors as unknown[]),
273
+ f: undefined,
268
274
  };
269
275
  }
270
276
 
@@ -283,16 +289,16 @@ function generateErrorNode(
283
289
  s: undefined,
284
290
  l: undefined,
285
291
  c: getErrorConstructorName(current),
286
- m: current.message,
292
+ m: quote(current.message),
287
293
  d: optionsNode,
288
294
  a: undefined,
289
- n: undefined,
295
+ f: undefined,
290
296
  };
291
297
  }
292
298
 
293
- function parse(
299
+ function parse<T>(
294
300
  ctx: ParserContext,
295
- current: ServerValue,
301
+ current: T,
296
302
  ): SerovalNode {
297
303
  switch (typeof current) {
298
304
  case 'boolean':
@@ -302,20 +308,19 @@ function parse(
302
308
  case 'string':
303
309
  return createStringNode(current);
304
310
  case 'number':
305
- // eslint-disable-next-line no-self-compare
306
- if (current !== current) {
307
- return NAN_NODE;
308
- }
309
- if (Object.is(current, -0)) {
310
- return NEG_ZERO_NODE;
311
- }
312
- if (Object.is(current, Infinity)) {
313
- return INFINITY_NODE;
314
- }
315
- if (Object.is(current, -Infinity)) {
316
- return NEG_INFINITY_NODE;
311
+ switch (current) {
312
+ case Infinity: return INFINITY_NODE;
313
+ case -Infinity: return NEG_INFINITY_NODE;
314
+ default:
315
+ // eslint-disable-next-line no-self-compare
316
+ if (current !== current) {
317
+ return NAN_NODE;
318
+ }
319
+ if (Object.is(current, -0)) {
320
+ return NEG_ZERO_NODE;
321
+ }
322
+ return createNumberNode(current);
317
323
  }
318
- return createNumberNode(current);
319
324
  case 'bigint':
320
325
  return createBigIntNode(ctx, current);
321
326
  case 'object': {
@@ -324,18 +329,21 @@ function parse(
324
329
  }
325
330
  // Non-primitive values needs a reference ID
326
331
  // mostly because the values themselves are stateful
327
- const id = createRef(ctx, current, true);
332
+ const id = createIndexedValue(ctx, current);
328
333
  if (ctx.markedRefs.has(id)) {
329
- return createReferenceNode(id);
334
+ return createIndexedValueNode(id);
335
+ }
336
+ if (hasReferenceID(current)) {
337
+ return createReferenceNode(id, current);
330
338
  }
331
339
  if (Array.isArray(current)) {
332
340
  return generateArrayNode(ctx, id, current);
333
341
  }
334
342
  switch (current.constructor) {
335
343
  case Date:
336
- return createDateNode(id, current as Date);
344
+ return createDateNode(id, current as unknown as Date);
337
345
  case RegExp:
338
- return createRegExpNode(id, current as RegExp);
346
+ return createRegExpNode(id, current as unknown as RegExp);
339
347
  case Int8Array:
340
348
  case Int16Array:
341
349
  case Int32Array:
@@ -345,23 +353,33 @@ function parse(
345
353
  case Uint8ClampedArray:
346
354
  case Float32Array:
347
355
  case Float64Array:
348
- return createTypedArrayNode(ctx, id, current as TypedArrayValue);
356
+ return createTypedArrayNode(ctx, id, current as unknown as TypedArrayValue);
349
357
  case BigInt64Array:
350
358
  case BigUint64Array:
351
- return createBigIntTypedArrayNode(ctx, id, current as BigIntTypedArrayValue);
359
+ return createBigIntTypedArrayNode(ctx, id, current as unknown as BigIntTypedArrayValue);
352
360
  case Map:
353
- return generateMapNode(ctx, id, current as Map<ServerValue, ServerValue>);
361
+ return generateMapNode(ctx, id, current as unknown as Map<unknown, unknown>);
354
362
  case Set:
355
- return generateSetNode(ctx, id, current as Set<ServerValue>);
363
+ return generateSetNode(ctx, id, current as unknown as Set<unknown>);
356
364
  case Object:
357
- return generateObjectNode(ctx, id, current as Record<string, ServerValue>, false);
365
+ return generateObjectNode(
366
+ ctx,
367
+ id,
368
+ current as unknown as Record<string, unknown>,
369
+ false,
370
+ );
358
371
  case undefined:
359
- return generateObjectNode(ctx, id, current as Record<string, ServerValue>, true);
372
+ return generateObjectNode(
373
+ ctx,
374
+ id,
375
+ current as unknown as Record<string, unknown>,
376
+ true,
377
+ );
360
378
  case AggregateError:
361
379
  if (ctx.features & Feature.AggregateError) {
362
- return generateAggregateErrorNode(ctx, id, current as AggregateError);
380
+ return generateAggregateErrorNode(ctx, id, current as unknown as AggregateError);
363
381
  }
364
- return generateErrorNode(ctx, id, current as AggregateError);
382
+ return generateErrorNode(ctx, id, current as unknown as AggregateError);
365
383
  case Error:
366
384
  case EvalError:
367
385
  case RangeError:
@@ -369,37 +387,57 @@ function parse(
369
387
  case SyntaxError:
370
388
  case TypeError:
371
389
  case URIError:
372
- return generateErrorNode(ctx, id, current as Error);
390
+ return generateErrorNode(ctx, id, current as unknown as Error);
391
+ case URL:
392
+ return createURLNode(ctx, id, current as unknown as URL);
393
+ case URLSearchParams:
394
+ return createURLSearchParamsNode(ctx, id, current as unknown as URLSearchParams);
373
395
  default:
374
396
  break;
375
397
  }
376
- if (current instanceof AggregateError && ctx.features & Feature.AggregateError) {
377
- return generateAggregateErrorNode(ctx, id, current);
398
+ if (current instanceof AggregateError) {
399
+ if (ctx.features & Feature.AggregateError) {
400
+ return generateAggregateErrorNode(ctx, id, current);
401
+ }
402
+ return generateErrorNode(ctx, id, current);
378
403
  }
379
404
  if (current instanceof Error) {
380
405
  return generateErrorNode(ctx, id, current);
381
406
  }
382
407
  // Generator functions don't have a global constructor
383
408
  if (Symbol.iterator in current) {
384
- return generateIterableNode(ctx, id, current);
409
+ return generateIterableNode(ctx, id, current as Iterable<unknown>);
385
410
  }
386
411
  throw new Error('Unsupported type');
387
412
  }
388
413
  case 'symbol':
389
- assert(ctx.features & Feature.Symbol, 'Unsupported type "symbol"');
390
- assert(current in INV_SYMBOL_REF, 'seroval only supports well-known symbols');
391
- return createWKSymbolNode(current);
414
+ if (hasReferenceID(current)) {
415
+ const id = createIndexedValue(ctx, current);
416
+ if (ctx.markedRefs.has(id)) {
417
+ return createIndexedValueNode(id);
418
+ }
419
+ return createReferenceNode(id, current);
420
+ }
421
+ return createWKSymbolNode(ctx, current as WellKnownSymbols);
422
+ case 'function': {
423
+ assert(hasReferenceID(current), 'Cannot serialize function without reference ID.');
424
+ const id = createIndexedValue(ctx, current);
425
+ if (ctx.markedRefs.has(id)) {
426
+ return createIndexedValueNode(id);
427
+ }
428
+ return createReferenceNode(id, current);
429
+ }
392
430
  default:
393
431
  throw new Error('Unsupported type');
394
432
  }
395
433
  }
396
434
 
397
- export default function parseSync(
435
+ export default function parseSync<T>(
398
436
  ctx: ParserContext,
399
- current: ServerValue,
437
+ current: T,
400
438
  ) {
401
439
  const result = parse(ctx, current);
402
440
  const isObject = result.t === SerovalNodeType.Object
403
441
  || result.t === SerovalNodeType.Iterable;
404
- return [result, createRef(ctx, current, false), isObject] as const;
442
+ return [result, getRootID(ctx, current), isObject] as const;
405
443
  }
package/src/tree/types.ts CHANGED
@@ -11,7 +11,7 @@ export const enum SerovalNodeType {
11
11
  NegativeInfinity,
12
12
  NaN,
13
13
  BigInt,
14
- Reference,
14
+ IndexedValue,
15
15
  Date,
16
16
  RegExp,
17
17
  Set,
@@ -26,6 +26,9 @@ export const enum SerovalNodeType {
26
26
  TypedArray,
27
27
  BigIntTypedArray,
28
28
  WKSymbol,
29
+ URL,
30
+ URLSearchParams,
31
+ Reference,
29
32
  }
30
33
 
31
34
  export interface SerovalBaseNode {
@@ -45,8 +48,8 @@ export interface SerovalBaseNode {
45
48
  d: SerovalDictionaryNode | undefined;
46
49
  // array of nodes
47
50
  a: SerovalNode[] | undefined;
48
- // next node
49
- n: SerovalNode | undefined;
51
+ // fulfilled node
52
+ f: SerovalNode | undefined;
50
53
  }
51
54
 
52
55
  export interface SerovalObjectRecordNode {
@@ -115,8 +118,8 @@ export type SerovalPrimitiveNode =
115
118
  | SerovalInfinityNode
116
119
  | SerovalNaNNode;
117
120
 
118
- export interface SerovalReferenceNode extends SerovalBaseNode {
119
- t: SerovalNodeType.Reference;
121
+ export interface SerovalIndexedValueNode extends SerovalBaseNode {
122
+ t: SerovalNodeType.IndexedValue;
120
123
  i: number;
121
124
  }
122
125
 
@@ -197,7 +200,7 @@ export interface SerovalNullConstructorNode extends SerovalBaseNode {
197
200
 
198
201
  export interface SerovalPromiseNode extends SerovalBaseNode {
199
202
  t: SerovalNodeType.Promise;
200
- n: SerovalNode;
203
+ f: SerovalNode;
201
204
  i: number;
202
205
  }
203
206
 
@@ -231,9 +234,27 @@ export interface SerovalWKSymbolNode extends SerovalBaseNode {
231
234
  s: Symbols;
232
235
  }
233
236
 
237
+ export interface SerovalURLNode extends SerovalBaseNode {
238
+ t: SerovalNodeType.URL;
239
+ i: number;
240
+ s: string;
241
+ }
242
+
243
+ export interface SerovalURLSearchParamsNode extends SerovalBaseNode {
244
+ t: SerovalNodeType.URLSearchParams;
245
+ i: number;
246
+ s: string;
247
+ }
248
+
249
+ export interface SerovalReferenceNode extends SerovalBaseNode {
250
+ t: SerovalNodeType.Reference;
251
+ i: number;
252
+ s: string;
253
+ }
254
+
234
255
  export type SerovalNode =
235
256
  | SerovalPrimitiveNode
236
- | SerovalReferenceNode
257
+ | SerovalIndexedValueNode
237
258
  | SerovalSemiPrimitiveNode
238
259
  | SerovalSetNode
239
260
  | SerovalMapNode
@@ -244,4 +265,7 @@ export type SerovalNode =
244
265
  | SerovalErrorNode
245
266
  | SerovalAggregateErrorNode
246
267
  | SerovalIterableNode
247
- | SerovalWKSymbolNode;
268
+ | SerovalWKSymbolNode
269
+ | SerovalURLNode
270
+ | SerovalURLSearchParamsNode
271
+ | SerovalReferenceNode;
@@ -0,0 +1,42 @@
1
+ import assert from '../assert';
2
+ import { Feature } from '../compat';
3
+ import { ParserContext } from '../context';
4
+ import { SerovalNodeType, SerovalURLNode, SerovalURLSearchParamsNode } from './types';
5
+
6
+ export function createURLNode(
7
+ ctx: ParserContext,
8
+ id: number,
9
+ current: URL,
10
+ ): SerovalURLNode {
11
+ assert(ctx.features & Feature.WebAPI, 'Unsupported type "URL"');
12
+ return {
13
+ t: SerovalNodeType.URL,
14
+ i: id,
15
+ s: current.href,
16
+ l: undefined,
17
+ c: undefined,
18
+ m: undefined,
19
+ d: undefined,
20
+ f: undefined,
21
+ a: undefined,
22
+ };
23
+ }
24
+
25
+ export function createURLSearchParamsNode(
26
+ ctx: ParserContext,
27
+ id: number,
28
+ current: URLSearchParams,
29
+ ): SerovalURLSearchParamsNode {
30
+ assert(ctx.features & Feature.WebAPI, 'Unsupported type "URLSearchParams"');
31
+ return {
32
+ t: SerovalNodeType.URLSearchParams,
33
+ i: id,
34
+ s: current.toString(),
35
+ l: undefined,
36
+ c: undefined,
37
+ m: undefined,
38
+ d: undefined,
39
+ f: undefined,
40
+ a: undefined,
41
+ };
42
+ }
package/src/types.ts CHANGED
@@ -34,12 +34,17 @@ export type BigIntTypedArrayValue =
34
34
  | BigInt64Array
35
35
  | BigUint64Array;
36
36
 
37
+ export type WebAPIValue =
38
+ | URL
39
+ | URLSearchParams;
40
+
37
41
  export type SemiPrimitiveValue =
38
42
  | RegExp
39
43
  | Date
40
44
  | TypedArrayValue
41
45
  | BigIntTypedArrayValue
42
- | bigint;
46
+ | bigint
47
+ | WebAPIValue;
43
48
 
44
49
  export type CommonServerValue =
45
50
  | PrimitiveValue