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/README.md +51 -1
- package/dist/cjs/development/index.cjs +762 -228
- package/dist/cjs/development/index.cjs.map +4 -4
- package/dist/cjs/production/index.cjs +3 -3
- package/dist/esm/development/index.mjs +762 -228
- 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 +14 -9
- package/dist/types/index.d.ts +9 -9
- package/dist/types/string.d.ts +3 -0
- 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 +7 -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 +73 -12
- package/dist/types/tree/web-api.d.ts +6 -0
- package/dist/types/types.d.ts +2 -1
- package/package.json +4 -3
- package/src/compat.ts +2 -1
- package/src/context.ts +19 -12
- package/src/index.ts +10 -16
- package/src/{quote.ts → string.ts} +27 -30
- package/src/tree/async.ts +164 -62
- package/src/tree/deserialize.ts +195 -67
- package/src/tree/primitives.ts +108 -22
- package/src/tree/reference.ts +61 -0
- package/src/tree/serialize.ts +236 -59
- package/src/tree/sync.ts +160 -55
- package/src/tree/types.ts +139 -14
- package/src/tree/web-api.ts +92 -0
- package/src/types.ts +6 -1
- package/dist/types/quote.d.ts +0 -2
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 {
|
|
5
|
-
import {
|
|
4
|
+
import { createIndexedValue, getRootID, ParserContext } from '../context';
|
|
5
|
+
import { serializeString } from '../string';
|
|
6
|
+
import { BigIntTypedArrayValue, TypedArrayValue } from '../types';
|
|
6
7
|
import {
|
|
7
8
|
createBigIntNode,
|
|
8
9
|
createBigIntTypedArrayNode,
|
|
9
10
|
createDateNode,
|
|
10
11
|
createNumberNode,
|
|
11
|
-
|
|
12
|
+
createIndexedValueNode,
|
|
12
13
|
createRegExpNode,
|
|
13
14
|
createStringNode,
|
|
14
15
|
createTypedArrayNode,
|
|
@@ -21,18 +22,26 @@ import {
|
|
|
21
22
|
NULL_NODE,
|
|
22
23
|
TRUE_NODE,
|
|
23
24
|
UNDEFINED_NODE,
|
|
25
|
+
createReferenceNode,
|
|
26
|
+
createArrayBufferNode,
|
|
27
|
+
createDataViewNode,
|
|
24
28
|
} from './primitives';
|
|
29
|
+
import {
|
|
30
|
+
hasReferenceID,
|
|
31
|
+
} from './reference';
|
|
25
32
|
import {
|
|
26
33
|
getErrorConstructorName,
|
|
27
34
|
getErrorOptions,
|
|
28
35
|
getIterableOptions,
|
|
29
36
|
isIterable,
|
|
30
37
|
} from './shared';
|
|
31
|
-
import {
|
|
38
|
+
import { WellKnownSymbols } from './symbols';
|
|
32
39
|
import {
|
|
33
40
|
SerovalAggregateErrorNode,
|
|
34
41
|
SerovalArrayNode,
|
|
35
42
|
SerovalErrorNode,
|
|
43
|
+
SerovalFormDataNode,
|
|
44
|
+
SerovalHeadersNode,
|
|
36
45
|
SerovalIterableNode,
|
|
37
46
|
SerovalMapNode,
|
|
38
47
|
SerovalNode,
|
|
@@ -42,14 +51,15 @@ import {
|
|
|
42
51
|
SerovalObjectRecordNode,
|
|
43
52
|
SerovalSetNode,
|
|
44
53
|
} from './types';
|
|
54
|
+
import { createURLNode, createURLSearchParamsNode } from './web-api';
|
|
45
55
|
|
|
46
56
|
type ObjectLikeNode = SerovalObjectNode | SerovalNullConstructorNode | SerovalIterableNode;
|
|
47
57
|
|
|
48
|
-
function generateNodeList(ctx: ParserContext, current:
|
|
58
|
+
function generateNodeList(ctx: ParserContext, current: unknown[]) {
|
|
49
59
|
const size = current.length;
|
|
50
60
|
const nodes = new Array<SerovalNode>(size);
|
|
51
|
-
const deferred = new Array<
|
|
52
|
-
let item:
|
|
61
|
+
const deferred = new Array<unknown>(size);
|
|
62
|
+
let item: unknown;
|
|
53
63
|
for (let i = 0; i < size; i++) {
|
|
54
64
|
if (i in current) {
|
|
55
65
|
item = current[i];
|
|
@@ -71,7 +81,7 @@ function generateNodeList(ctx: ParserContext, current: ServerValue[]) {
|
|
|
71
81
|
function generateArrayNode(
|
|
72
82
|
ctx: ParserContext,
|
|
73
83
|
id: number,
|
|
74
|
-
current:
|
|
84
|
+
current: unknown[],
|
|
75
85
|
): SerovalArrayNode {
|
|
76
86
|
return {
|
|
77
87
|
t: SerovalNodeType.Array,
|
|
@@ -83,20 +93,21 @@ function generateArrayNode(
|
|
|
83
93
|
d: undefined,
|
|
84
94
|
a: generateNodeList(ctx, current),
|
|
85
95
|
f: undefined,
|
|
96
|
+
b: undefined,
|
|
86
97
|
};
|
|
87
98
|
}
|
|
88
99
|
|
|
89
100
|
function generateMapNode(
|
|
90
101
|
ctx: ParserContext,
|
|
91
102
|
id: number,
|
|
92
|
-
current: Map<
|
|
103
|
+
current: Map<unknown, unknown>,
|
|
93
104
|
): SerovalMapNode {
|
|
94
105
|
assert(ctx.features & Feature.Map, 'Unsupported type "Map"');
|
|
95
106
|
const len = current.size;
|
|
96
107
|
const keyNodes = new Array<SerovalNode>(len);
|
|
97
108
|
const valueNodes = new Array<SerovalNode>(len);
|
|
98
|
-
const deferredKey = new Array<
|
|
99
|
-
const deferredValue = new Array<
|
|
109
|
+
const deferredKey = new Array<unknown>(len);
|
|
110
|
+
const deferredValue = new Array<unknown>(len);
|
|
100
111
|
let deferredSize = 0;
|
|
101
112
|
let nodeSize = 0;
|
|
102
113
|
for (const [key, value] of current.entries()) {
|
|
@@ -125,18 +136,19 @@ function generateMapNode(
|
|
|
125
136
|
d: { k: keyNodes, v: valueNodes, s: len },
|
|
126
137
|
a: undefined,
|
|
127
138
|
f: undefined,
|
|
139
|
+
b: undefined,
|
|
128
140
|
};
|
|
129
141
|
}
|
|
130
142
|
|
|
131
143
|
function generateSetNode(
|
|
132
144
|
ctx: ParserContext,
|
|
133
145
|
id: number,
|
|
134
|
-
current: Set<
|
|
146
|
+
current: Set<unknown>,
|
|
135
147
|
): SerovalSetNode {
|
|
136
148
|
assert(ctx.features & Feature.Set, 'Unsupported type "Set"');
|
|
137
149
|
const len = current.size;
|
|
138
150
|
const nodes = new Array<SerovalNode>(len);
|
|
139
|
-
const deferred = new Array<
|
|
151
|
+
const deferred = new Array<unknown>(len);
|
|
140
152
|
let deferredSize = 0;
|
|
141
153
|
let nodeSize = 0;
|
|
142
154
|
for (const item of current.keys()) {
|
|
@@ -161,30 +173,33 @@ function generateSetNode(
|
|
|
161
173
|
d: undefined,
|
|
162
174
|
a: nodes,
|
|
163
175
|
f: undefined,
|
|
176
|
+
b: undefined,
|
|
164
177
|
};
|
|
165
178
|
}
|
|
166
179
|
|
|
167
180
|
function generateProperties(
|
|
168
181
|
ctx: ParserContext,
|
|
169
|
-
properties: Record<string,
|
|
182
|
+
properties: Record<string, unknown>,
|
|
170
183
|
): SerovalObjectRecordNode {
|
|
171
184
|
const keys = Object.keys(properties);
|
|
172
185
|
const size = keys.length;
|
|
173
186
|
const keyNodes = new Array<string>(size);
|
|
174
187
|
const valueNodes = new Array<SerovalNode>(size);
|
|
175
188
|
const deferredKeys = new Array<string>(size);
|
|
176
|
-
const deferredValues = new Array<
|
|
189
|
+
const deferredValues = new Array<unknown>(size);
|
|
177
190
|
let deferredSize = 0;
|
|
178
191
|
let nodesSize = 0;
|
|
179
|
-
let item:
|
|
192
|
+
let item: unknown;
|
|
193
|
+
let escaped: string;
|
|
180
194
|
for (const key of keys) {
|
|
181
195
|
item = properties[key];
|
|
196
|
+
escaped = serializeString(key);
|
|
182
197
|
if (isIterable(item)) {
|
|
183
|
-
deferredKeys[deferredSize] =
|
|
198
|
+
deferredKeys[deferredSize] = escaped;
|
|
184
199
|
deferredValues[deferredSize] = item;
|
|
185
200
|
deferredSize++;
|
|
186
201
|
} else {
|
|
187
|
-
keyNodes[nodesSize] =
|
|
202
|
+
keyNodes[nodesSize] = escaped;
|
|
188
203
|
valueNodes[nodesSize] = parse(ctx, item);
|
|
189
204
|
nodesSize++;
|
|
190
205
|
}
|
|
@@ -203,7 +218,7 @@ function generateProperties(
|
|
|
203
218
|
function generateIterableNode(
|
|
204
219
|
ctx: ParserContext,
|
|
205
220
|
id: number,
|
|
206
|
-
current: Iterable<
|
|
221
|
+
current: Iterable<unknown>,
|
|
207
222
|
): SerovalIterableNode {
|
|
208
223
|
assert(ctx.features & Feature.Symbol, 'Unsupported type "Iterable"');
|
|
209
224
|
const options = getIterableOptions(current);
|
|
@@ -217,17 +232,18 @@ function generateIterableNode(
|
|
|
217
232
|
m: undefined,
|
|
218
233
|
// Parse options first before the items
|
|
219
234
|
d: options
|
|
220
|
-
? generateProperties(ctx, options
|
|
235
|
+
? generateProperties(ctx, options)
|
|
221
236
|
: undefined,
|
|
222
237
|
a: generateNodeList(ctx, array),
|
|
223
238
|
f: undefined,
|
|
239
|
+
b: undefined,
|
|
224
240
|
};
|
|
225
241
|
}
|
|
226
242
|
|
|
227
243
|
function generateObjectNode(
|
|
228
244
|
ctx: ParserContext,
|
|
229
245
|
id: number,
|
|
230
|
-
current: Record<string,
|
|
246
|
+
current: Record<string, unknown> | Iterable<unknown>,
|
|
231
247
|
empty: boolean,
|
|
232
248
|
): ObjectLikeNode {
|
|
233
249
|
if (Symbol.iterator in current) {
|
|
@@ -243,6 +259,7 @@ function generateObjectNode(
|
|
|
243
259
|
d: generateProperties(ctx, current),
|
|
244
260
|
a: undefined,
|
|
245
261
|
f: undefined,
|
|
262
|
+
b: undefined,
|
|
246
263
|
};
|
|
247
264
|
}
|
|
248
265
|
|
|
@@ -259,12 +276,13 @@ function generateAggregateErrorNode(
|
|
|
259
276
|
t: SerovalNodeType.AggregateError,
|
|
260
277
|
i: id,
|
|
261
278
|
s: undefined,
|
|
262
|
-
l:
|
|
279
|
+
l: undefined,
|
|
263
280
|
c: undefined,
|
|
264
|
-
m: current.message,
|
|
281
|
+
m: serializeString(current.message),
|
|
265
282
|
d: optionsNode,
|
|
266
|
-
a:
|
|
283
|
+
a: undefined,
|
|
267
284
|
f: undefined,
|
|
285
|
+
b: undefined,
|
|
268
286
|
};
|
|
269
287
|
}
|
|
270
288
|
|
|
@@ -283,16 +301,65 @@ function generateErrorNode(
|
|
|
283
301
|
s: undefined,
|
|
284
302
|
l: undefined,
|
|
285
303
|
c: getErrorConstructorName(current),
|
|
286
|
-
m: current.message,
|
|
304
|
+
m: serializeString(current.message),
|
|
287
305
|
d: optionsNode,
|
|
288
306
|
a: undefined,
|
|
289
307
|
f: undefined,
|
|
308
|
+
b: undefined,
|
|
309
|
+
};
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
function generateHeadersNode(
|
|
313
|
+
ctx: ParserContext,
|
|
314
|
+
id: number,
|
|
315
|
+
current: Headers,
|
|
316
|
+
): SerovalHeadersNode {
|
|
317
|
+
assert(ctx.features & Feature.WebAPI, 'Unsupported type "Headers"');
|
|
318
|
+
const items: Record<string, string> = {};
|
|
319
|
+
current.forEach((value, key) => {
|
|
320
|
+
items[key] = value;
|
|
321
|
+
});
|
|
322
|
+
return {
|
|
323
|
+
t: SerovalNodeType.Headers,
|
|
324
|
+
i: id,
|
|
325
|
+
s: undefined,
|
|
326
|
+
l: undefined,
|
|
327
|
+
c: undefined,
|
|
328
|
+
m: undefined,
|
|
329
|
+
d: generateProperties(ctx, items),
|
|
330
|
+
a: undefined,
|
|
331
|
+
f: undefined,
|
|
332
|
+
b: undefined,
|
|
333
|
+
};
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
function generateFormDataNode(
|
|
337
|
+
ctx: ParserContext,
|
|
338
|
+
id: number,
|
|
339
|
+
current: FormData,
|
|
340
|
+
): SerovalFormDataNode {
|
|
341
|
+
assert(ctx.features & Feature.WebAPI, 'Unsupported type "FormData"');
|
|
342
|
+
const items: Record<string, FormDataEntryValue> = {};
|
|
343
|
+
current.forEach((value, key) => {
|
|
344
|
+
items[key] = value;
|
|
345
|
+
});
|
|
346
|
+
return {
|
|
347
|
+
t: SerovalNodeType.FormData,
|
|
348
|
+
i: id,
|
|
349
|
+
s: undefined,
|
|
350
|
+
l: undefined,
|
|
351
|
+
c: undefined,
|
|
352
|
+
m: undefined,
|
|
353
|
+
d: generateProperties(ctx, items),
|
|
354
|
+
a: undefined,
|
|
355
|
+
f: undefined,
|
|
356
|
+
b: undefined,
|
|
290
357
|
};
|
|
291
358
|
}
|
|
292
359
|
|
|
293
|
-
function parse(
|
|
360
|
+
function parse<T>(
|
|
294
361
|
ctx: ParserContext,
|
|
295
|
-
current:
|
|
362
|
+
current: T,
|
|
296
363
|
): SerovalNode {
|
|
297
364
|
switch (typeof current) {
|
|
298
365
|
case 'boolean':
|
|
@@ -305,16 +372,16 @@ function parse(
|
|
|
305
372
|
switch (current) {
|
|
306
373
|
case Infinity: return INFINITY_NODE;
|
|
307
374
|
case -Infinity: return NEG_INFINITY_NODE;
|
|
308
|
-
default:
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
375
|
+
default:
|
|
376
|
+
// eslint-disable-next-line no-self-compare
|
|
377
|
+
if (current !== current) {
|
|
378
|
+
return NAN_NODE;
|
|
379
|
+
}
|
|
380
|
+
if (Object.is(current, -0)) {
|
|
381
|
+
return NEG_ZERO_NODE;
|
|
382
|
+
}
|
|
383
|
+
return createNumberNode(current);
|
|
316
384
|
}
|
|
317
|
-
return createNumberNode(current);
|
|
318
385
|
case 'bigint':
|
|
319
386
|
return createBigIntNode(ctx, current);
|
|
320
387
|
case 'object': {
|
|
@@ -323,18 +390,23 @@ function parse(
|
|
|
323
390
|
}
|
|
324
391
|
// Non-primitive values needs a reference ID
|
|
325
392
|
// mostly because the values themselves are stateful
|
|
326
|
-
const id =
|
|
393
|
+
const id = createIndexedValue(ctx, current);
|
|
327
394
|
if (ctx.markedRefs.has(id)) {
|
|
328
|
-
return
|
|
395
|
+
return createIndexedValueNode(id);
|
|
396
|
+
}
|
|
397
|
+
if (hasReferenceID(current)) {
|
|
398
|
+
return createReferenceNode(id, current);
|
|
329
399
|
}
|
|
330
400
|
if (Array.isArray(current)) {
|
|
331
401
|
return generateArrayNode(ctx, id, current);
|
|
332
402
|
}
|
|
333
403
|
switch (current.constructor) {
|
|
334
404
|
case Date:
|
|
335
|
-
return createDateNode(id, current as Date);
|
|
405
|
+
return createDateNode(id, current as unknown as Date);
|
|
336
406
|
case RegExp:
|
|
337
|
-
return createRegExpNode(id, current as RegExp);
|
|
407
|
+
return createRegExpNode(id, current as unknown as RegExp);
|
|
408
|
+
case ArrayBuffer:
|
|
409
|
+
return createArrayBufferNode(id, current as unknown as ArrayBuffer);
|
|
338
410
|
case Int8Array:
|
|
339
411
|
case Int16Array:
|
|
340
412
|
case Int32Array:
|
|
@@ -344,23 +416,35 @@ function parse(
|
|
|
344
416
|
case Uint8ClampedArray:
|
|
345
417
|
case Float32Array:
|
|
346
418
|
case Float64Array:
|
|
347
|
-
return createTypedArrayNode(ctx, id, current as TypedArrayValue);
|
|
419
|
+
return createTypedArrayNode(ctx, id, current as unknown as TypedArrayValue);
|
|
348
420
|
case BigInt64Array:
|
|
349
421
|
case BigUint64Array:
|
|
350
|
-
return createBigIntTypedArrayNode(ctx, id, current as BigIntTypedArrayValue);
|
|
422
|
+
return createBigIntTypedArrayNode(ctx, id, current as unknown as BigIntTypedArrayValue);
|
|
423
|
+
case DataView:
|
|
424
|
+
return createDataViewNode(ctx, id, current as unknown as DataView);
|
|
351
425
|
case Map:
|
|
352
|
-
return generateMapNode(ctx, id, current as Map<
|
|
426
|
+
return generateMapNode(ctx, id, current as unknown as Map<unknown, unknown>);
|
|
353
427
|
case Set:
|
|
354
|
-
return generateSetNode(ctx, id, current as Set<
|
|
428
|
+
return generateSetNode(ctx, id, current as unknown as Set<unknown>);
|
|
355
429
|
case Object:
|
|
356
|
-
return generateObjectNode(
|
|
430
|
+
return generateObjectNode(
|
|
431
|
+
ctx,
|
|
432
|
+
id,
|
|
433
|
+
current as unknown as Record<string, unknown>,
|
|
434
|
+
false,
|
|
435
|
+
);
|
|
357
436
|
case undefined:
|
|
358
|
-
return generateObjectNode(
|
|
437
|
+
return generateObjectNode(
|
|
438
|
+
ctx,
|
|
439
|
+
id,
|
|
440
|
+
current as unknown as Record<string, unknown>,
|
|
441
|
+
true,
|
|
442
|
+
);
|
|
359
443
|
case AggregateError:
|
|
360
444
|
if (ctx.features & Feature.AggregateError) {
|
|
361
|
-
return generateAggregateErrorNode(ctx, id, current as AggregateError);
|
|
445
|
+
return generateAggregateErrorNode(ctx, id, current as unknown as AggregateError);
|
|
362
446
|
}
|
|
363
|
-
return generateErrorNode(ctx, id, current as AggregateError);
|
|
447
|
+
return generateErrorNode(ctx, id, current as unknown as AggregateError);
|
|
364
448
|
case Error:
|
|
365
449
|
case EvalError:
|
|
366
450
|
case RangeError:
|
|
@@ -368,7 +452,15 @@ function parse(
|
|
|
368
452
|
case SyntaxError:
|
|
369
453
|
case TypeError:
|
|
370
454
|
case URIError:
|
|
371
|
-
return generateErrorNode(ctx, id, current as Error);
|
|
455
|
+
return generateErrorNode(ctx, id, current as unknown as Error);
|
|
456
|
+
case URL:
|
|
457
|
+
return createURLNode(ctx, id, current as unknown as URL);
|
|
458
|
+
case URLSearchParams:
|
|
459
|
+
return createURLSearchParamsNode(ctx, id, current as unknown as URLSearchParams);
|
|
460
|
+
case Headers:
|
|
461
|
+
return generateHeadersNode(ctx, id, current as unknown as Headers);
|
|
462
|
+
case FormData:
|
|
463
|
+
return generateFormDataNode(ctx, id, current as unknown as FormData);
|
|
372
464
|
default:
|
|
373
465
|
break;
|
|
374
466
|
}
|
|
@@ -383,22 +475,35 @@ function parse(
|
|
|
383
475
|
}
|
|
384
476
|
// Generator functions don't have a global constructor
|
|
385
477
|
if (Symbol.iterator in current) {
|
|
386
|
-
return generateIterableNode(ctx, id, current);
|
|
478
|
+
return generateIterableNode(ctx, id, current as Iterable<unknown>);
|
|
387
479
|
}
|
|
388
480
|
throw new Error('Unsupported type');
|
|
389
481
|
}
|
|
390
482
|
case 'symbol':
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
483
|
+
if (hasReferenceID(current)) {
|
|
484
|
+
const id = createIndexedValue(ctx, current);
|
|
485
|
+
if (ctx.markedRefs.has(id)) {
|
|
486
|
+
return createIndexedValueNode(id);
|
|
487
|
+
}
|
|
488
|
+
return createReferenceNode(id, current);
|
|
489
|
+
}
|
|
490
|
+
return createWKSymbolNode(ctx, current as WellKnownSymbols);
|
|
491
|
+
case 'function': {
|
|
492
|
+
assert(hasReferenceID(current), 'Cannot serialize function without reference ID.');
|
|
493
|
+
const id = createIndexedValue(ctx, current);
|
|
494
|
+
if (ctx.markedRefs.has(id)) {
|
|
495
|
+
return createIndexedValueNode(id);
|
|
496
|
+
}
|
|
497
|
+
return createReferenceNode(id, current);
|
|
498
|
+
}
|
|
394
499
|
default:
|
|
395
500
|
throw new Error('Unsupported type');
|
|
396
501
|
}
|
|
397
502
|
}
|
|
398
503
|
|
|
399
|
-
export default function parseSync(
|
|
504
|
+
export default function parseSync<T>(
|
|
400
505
|
ctx: ParserContext,
|
|
401
|
-
current:
|
|
506
|
+
current: T,
|
|
402
507
|
) {
|
|
403
508
|
const result = parse(ctx, current);
|
|
404
509
|
const isObject = result.t === SerovalNodeType.Object
|