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/README.md +38 -0
- package/dist/cjs/development/index.cjs +236 -72
- package/dist/cjs/development/index.cjs.map +4 -4
- package/dist/cjs/production/index.cjs +3 -3
- package/dist/esm/development/index.mjs +236 -72
- package/dist/esm/development/index.mjs.map +4 -4
- package/dist/esm/production/index.mjs +3 -3
- package/dist/types/compat.d.ts +3 -2
- package/dist/types/context.d.ts +3 -4
- package/dist/types/index.d.ts +9 -9
- package/dist/types/tree/async.d.ts +1 -2
- package/dist/types/tree/deserialize.d.ts +1 -2
- package/dist/types/tree/primitives.d.ts +4 -3
- package/dist/types/tree/reference.d.ts +6 -0
- package/dist/types/tree/sync.d.ts +1 -2
- package/dist/types/tree/types.d.ts +23 -5
- package/dist/types/tree/web-api.d.ts +4 -0
- package/dist/types/types.d.ts +2 -1
- package/package.json +3 -3
- package/src/compat.ts +2 -1
- package/src/context.ts +5 -6
- package/src/index.ts +10 -16
- package/src/tree/async.ts +82 -59
- package/src/tree/deserialize.ts +31 -20
- package/src/tree/primitives.ts +25 -3
- package/src/tree/reference.ts +61 -0
- package/src/tree/serialize.ts +30 -23
- package/src/tree/sync.ts +88 -52
- package/src/tree/types.ts +29 -5
- package/src/tree/web-api.ts +42 -0
- package/src/types.ts +6 -1
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 {
|
|
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
|
-
|
|
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 {
|
|
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,
|
|
60
|
-
| Iterable<
|
|
61
|
-
| PromiseLike<
|
|
62
|
+
| Record<string, unknown>
|
|
63
|
+
| Iterable<unknown>
|
|
64
|
+
| PromiseLike<unknown>;
|
|
62
65
|
|
|
63
66
|
async function generateNodeList(
|
|
64
67
|
ctx: ParserContext,
|
|
65
|
-
current:
|
|
68
|
+
current: unknown[],
|
|
66
69
|
) {
|
|
67
70
|
const size = current.length;
|
|
68
71
|
const nodes = new Array<SerovalNode>(size);
|
|
69
|
-
const deferred = new Array<
|
|
70
|
-
let item:
|
|
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:
|
|
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<
|
|
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<
|
|
117
|
-
const deferredValue = new Array<
|
|
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<
|
|
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<
|
|
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,
|
|
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<
|
|
197
|
+
const deferredValues = new Array<unknown>(size);
|
|
195
198
|
let deferredSize = 0;
|
|
196
199
|
let nodesSize = 0;
|
|
197
|
-
let item:
|
|
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<
|
|
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
|
|
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<
|
|
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<
|
|
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,
|
|
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
|
|
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:
|
|
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:
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
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 =
|
|
370
|
+
const id = createIndexedValue(ctx, current);
|
|
368
371
|
if (ctx.markedRefs.has(id)) {
|
|
369
|
-
return
|
|
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<
|
|
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<
|
|
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<
|
|
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,
|
|
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,
|
|
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<
|
|
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
|
-
|
|
460
|
-
|
|
461
|
-
|
|
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:
|
|
492
|
+
current: T,
|
|
470
493
|
) {
|
|
471
494
|
const result = await parse(ctx, current);
|
|
472
495
|
const isObject = result.t === SerovalNodeType.Object
|
package/src/tree/deserialize.ts
CHANGED
|
@@ -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
|
|
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:
|
|
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[] =
|
|
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,
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
123
|
+
const result = assignIndexedValue(
|
|
119
124
|
ctx,
|
|
120
125
|
node.i,
|
|
121
|
-
new Map<
|
|
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 =
|
|
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 =
|
|
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:
|
|
177
|
-
promise: Promise<
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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
|
-
):
|
|
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.
|
|
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
|
|
270
|
+
return assignIndexedValue(ctx, node.i, new Date(node.s));
|
|
266
271
|
case SerovalNodeType.RegExp:
|
|
267
|
-
return
|
|
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
|
}
|
package/src/tree/primitives.ts
CHANGED
|
@@ -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
|
-
|
|
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
|
|
164
|
+
export function createIndexedValueNode(id: number): SerovalIndexedValueNode {
|
|
163
165
|
return {
|
|
164
|
-
t: SerovalNodeType.
|
|
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
|
+
}
|