seroval 0.4.0-alpha.8 → 0.4.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,63 +1,132 @@
1
1
  /* eslint-disable @typescript-eslint/no-use-before-define */
2
2
  import assert from '../assert';
3
- import { isIterable, isPrimitive, constructorCheck } from '../checks';
4
3
  import { Feature } from '../compat';
5
4
  import { createRef, ParserContext } from '../context';
6
- import { ServerValue } from '../types';
5
+ import quote from '../quote';
6
+ import { BigIntTypedArrayValue, ServerValue, TypedArrayValue } from '../types';
7
+ import {
8
+ createBigIntNode,
9
+ createBigIntTypedArrayNode,
10
+ createDateNode,
11
+ createPrimitiveNode,
12
+ createReferenceNode,
13
+ createRegExpNode,
14
+ createTypedArrayNode,
15
+ FALSE_NODE,
16
+ NEG_ZERO_NODE,
17
+ NULL_NODE,
18
+ TRUE_NODE,
19
+ UNDEFINED_NODE,
20
+ } from './primitives';
7
21
  import {
8
- generateRef,
9
- generateSemiPrimitiveValue,
10
22
  getErrorConstructor,
11
23
  getErrorOptions,
12
24
  getIterableOptions,
13
- serializePrimitive,
25
+ isIterable,
14
26
  } from './shared';
15
27
  import {
16
28
  SerovalAggregateErrorNode,
17
29
  SerovalArrayNode,
18
- SerovalDictionaryNode,
19
30
  SerovalErrorNode,
20
31
  SerovalIterableNode,
21
32
  SerovalMapNode,
22
33
  SerovalNode,
23
34
  SerovalNodeType,
35
+ SerovalNullConstructorNode,
36
+ SerovalObjectNode,
37
+ SerovalObjectRecordNode,
24
38
  SerovalSetNode,
25
39
  } from './types';
26
40
 
27
- function serializePropertiesSync(
41
+ type ObjectLikeNode = SerovalObjectNode | SerovalNullConstructorNode | SerovalIterableNode;
42
+
43
+ function generateNodeList(ctx: ParserContext, current: ServerValue[]) {
44
+ const size = current.length;
45
+ const nodes = new Array<SerovalNode>(size);
46
+ const deferred = new Array<ServerValue>(size);
47
+ let item: ServerValue;
48
+ for (let i = 0; i < size; i++) {
49
+ if (i in current) {
50
+ item = current[i];
51
+ if (isIterable(item)) {
52
+ deferred[i] = item;
53
+ } else {
54
+ nodes[i] = parse(ctx, item);
55
+ }
56
+ }
57
+ }
58
+ for (let i = 0; i < size; i++) {
59
+ if (i in deferred) {
60
+ nodes[i] = parse(ctx, deferred[i]);
61
+ }
62
+ }
63
+ return nodes;
64
+ }
65
+
66
+ function generateArrayNode(
28
67
  ctx: ParserContext,
29
- properties: Record<string, unknown>,
30
- ): SerovalDictionaryNode {
31
- const keys = Object.keys(properties);
32
- const size = keys.length;
33
- const keyNodes = new Array<string>(size);
34
- const valueNodes = new Array<SerovalNode>(size);
35
- const deferredKeys = new Array<string>(size);
36
- const deferredValues = new Array<ServerValue>(size);
68
+ id: number,
69
+ current: ServerValue[],
70
+ ): SerovalArrayNode {
71
+ return {
72
+ t: SerovalNodeType.Array,
73
+ i: id,
74
+ s: undefined,
75
+ l: undefined,
76
+ c: undefined,
77
+ m: undefined,
78
+ d: undefined,
79
+ a: generateNodeList(ctx, current),
80
+ n: undefined,
81
+ };
82
+ }
83
+
84
+ function generateMapNode(
85
+ ctx: ParserContext,
86
+ id: number,
87
+ current: Map<ServerValue, ServerValue>,
88
+ ): SerovalMapNode {
89
+ assert(ctx.features & Feature.Map, 'Unsupported type "Map"');
90
+ const len = current.size;
91
+ const keyNodes = new Array<SerovalNode>(len);
92
+ const valueNodes = new Array<SerovalNode>(len);
93
+ const deferredKey = new Array<ServerValue>(len);
94
+ const deferredValue = new Array<ServerValue>(len);
37
95
  let deferredSize = 0;
38
- let nodesSize = 0;
39
- for (const key of keys) {
40
- if (isIterable(properties[key])) {
41
- deferredKeys[deferredSize] = key;
42
- deferredValues[deferredSize] = properties[key] as ServerValue;
96
+ let nodeSize = 0;
97
+ for (const [key, value] of current.entries()) {
98
+ // Either key or value might be an iterable
99
+ if (isIterable(key) || isIterable(value)) {
100
+ deferredKey[deferredSize] = key;
101
+ deferredValue[deferredSize] = value;
43
102
  deferredSize++;
44
103
  } else {
45
- keyNodes[nodesSize] = key;
46
- valueNodes[nodesSize] = generateTreeSync(ctx, properties[key] as ServerValue);
47
- nodesSize++;
104
+ keyNodes[nodeSize] = parse(ctx, key);
105
+ valueNodes[nodeSize] = parse(ctx, value);
106
+ nodeSize++;
48
107
  }
49
108
  }
50
109
  for (let i = 0; i < deferredSize; i++) {
51
- keyNodes[nodesSize + i] = deferredKeys[i];
52
- valueNodes[nodesSize + i] = generateTreeSync(ctx, deferredValues[i]);
110
+ keyNodes[nodeSize + i] = parse(ctx, deferredKey[i]);
111
+ valueNodes[nodeSize + i] = parse(ctx, deferredValue[i]);
53
112
  }
54
- return [keyNodes, valueNodes, size];
113
+ return {
114
+ t: SerovalNodeType.Map,
115
+ i: id,
116
+ s: undefined,
117
+ l: undefined,
118
+ c: undefined,
119
+ m: undefined,
120
+ d: { k: keyNodes, v: valueNodes, s: len },
121
+ a: undefined,
122
+ n: undefined,
123
+ };
55
124
  }
56
125
 
57
126
  function generateSetNode(
58
127
  ctx: ParserContext,
59
- current: Set<ServerValue>,
60
128
  id: number,
129
+ current: Set<ServerValue>,
61
130
  ): SerovalSetNode {
62
131
  assert(ctx.features & Feature.Set, 'Unsupported type "Set"');
63
132
  const len = current.size;
@@ -70,173 +139,253 @@ function generateSetNode(
70
139
  if (isIterable(item)) {
71
140
  deferred[deferredSize++] = item;
72
141
  } else {
73
- nodes[nodeSize++] = generateTreeSync(ctx, item);
142
+ nodes[nodeSize++] = parse(ctx, item);
74
143
  }
75
144
  }
76
145
  // Parse deferred items
77
146
  for (let i = 0; i < deferredSize; i++) {
78
- nodes[nodeSize + i] = generateTreeSync(ctx, deferred[i]);
147
+ nodes[nodeSize + i] = parse(ctx, deferred[i]);
79
148
  }
80
- return [SerovalNodeType.Set, nodes, id];
149
+ return {
150
+ t: SerovalNodeType.Set,
151
+ i: id,
152
+ s: undefined,
153
+ l: undefined,
154
+ c: undefined,
155
+ m: undefined,
156
+ d: undefined,
157
+ a: nodes,
158
+ n: undefined,
159
+ };
81
160
  }
82
161
 
83
- function generateMapNode(
162
+ function generateProperties(
84
163
  ctx: ParserContext,
85
- current: Map<ServerValue, ServerValue>,
86
- id: number,
87
- ): SerovalMapNode {
88
- assert(ctx.features & Feature.Map, 'Unsupported type "Map"');
89
- const len = current.size;
90
- const keyNodes = new Array<SerovalNode>(len);
91
- const valueNodes = new Array<SerovalNode>(len);
92
- const deferredKey = new Array<ServerValue>(len);
93
- const deferredValue = new Array<ServerValue>(len);
164
+ properties: Record<string, ServerValue>,
165
+ ): SerovalObjectRecordNode {
166
+ const keys = Object.keys(properties);
167
+ const size = keys.length;
168
+ const keyNodes = new Array<string>(size);
169
+ const valueNodes = new Array<SerovalNode>(size);
170
+ const deferredKeys = new Array<string>(size);
171
+ const deferredValues = new Array<ServerValue>(size);
94
172
  let deferredSize = 0;
95
- let nodeSize = 0;
96
- for (const [key, value] of current.entries()) {
97
- // Either key or value might be an iterable
98
- if (isIterable(key) || isIterable(value)) {
99
- deferredKey[deferredSize] = key;
100
- deferredValue[deferredSize] = value;
173
+ let nodesSize = 0;
174
+ let item: ServerValue;
175
+ for (const key of keys) {
176
+ item = properties[key];
177
+ if (isIterable(item)) {
178
+ deferredKeys[deferredSize] = key;
179
+ deferredValues[deferredSize] = item;
101
180
  deferredSize++;
102
181
  } else {
103
- keyNodes[nodeSize] = generateTreeSync(ctx, key);
104
- valueNodes[nodeSize] = generateTreeSync(ctx, value);
105
- nodeSize++;
182
+ keyNodes[nodesSize] = key;
183
+ valueNodes[nodesSize] = parse(ctx, item);
184
+ nodesSize++;
106
185
  }
107
186
  }
108
187
  for (let i = 0; i < deferredSize; i++) {
109
- keyNodes[nodeSize + i] = generateTreeSync(ctx, deferredKey[i]);
110
- valueNodes[nodeSize + i] = generateTreeSync(ctx, deferredValue[i]);
188
+ keyNodes[nodesSize + i] = deferredKeys[i];
189
+ valueNodes[nodesSize + i] = parse(ctx, deferredValues[i]);
111
190
  }
112
- return [SerovalNodeType.Map, [keyNodes, valueNodes, len], id];
191
+ return {
192
+ k: keyNodes,
193
+ v: valueNodes,
194
+ s: size,
195
+ };
113
196
  }
114
197
 
115
- function generateNodeList(
198
+ function generateIterableNode(
116
199
  ctx: ParserContext,
117
- current: ServerValue[],
118
- ) {
119
- const size = current.length;
120
- const nodes = new Array<SerovalNode>(size);
121
- const deferred = new Array<ServerValue>(size);
122
- for (let i = 0; i < size; i++) {
123
- if (i in current) {
124
- if (isIterable(current[i])) {
125
- deferred[i] = current[i];
126
- } else {
127
- nodes[i] = generateTreeSync(ctx, current[i]);
128
- }
129
- }
130
- }
131
- for (let i = 0; i < size; i++) {
132
- if (i in deferred) {
133
- nodes[i] = generateTreeSync(ctx, deferred[i]);
134
- }
135
- }
136
- return nodes;
200
+ id: number,
201
+ current: Iterable<ServerValue>,
202
+ ): SerovalIterableNode {
203
+ assert(ctx.features & Feature.SymbolIterator, 'Unsupported type "Iterable"');
204
+ const options = getIterableOptions(current);
205
+ return {
206
+ t: SerovalNodeType.Iterable,
207
+ i: id,
208
+ s: undefined,
209
+ l: undefined,
210
+ c: undefined,
211
+ m: undefined,
212
+ // Parse options first before the items
213
+ d: options
214
+ ? generateProperties(ctx, options as Record<string, ServerValue>)
215
+ : undefined,
216
+ a: generateNodeList(ctx, Array.from(current)),
217
+ n: undefined,
218
+ };
137
219
  }
138
220
 
139
- function generateArrayNode(
221
+ function generateObjectNode(
140
222
  ctx: ParserContext,
141
- current: ServerValue[],
142
223
  id: number,
143
- ): SerovalArrayNode {
144
- return [SerovalNodeType.Array, generateNodeList(ctx, current), id];
224
+ current: Record<string, ServerValue> | Iterable<ServerValue>,
225
+ empty: boolean,
226
+ ): ObjectLikeNode {
227
+ if (Symbol.iterator in current) {
228
+ return generateIterableNode(ctx, id, current);
229
+ }
230
+ return {
231
+ t: empty ? SerovalNodeType.NullConstructor : SerovalNodeType.Object,
232
+ i: id,
233
+ s: undefined,
234
+ l: undefined,
235
+ c: undefined,
236
+ m: undefined,
237
+ d: generateProperties(ctx, current),
238
+ a: undefined,
239
+ n: undefined,
240
+ };
145
241
  }
146
242
 
147
243
  function generateAggregateErrorNode(
148
244
  ctx: ParserContext,
149
- current: AggregateError,
150
245
  id: number,
246
+ current: AggregateError,
151
247
  ): SerovalAggregateErrorNode {
152
- const options = getErrorOptions(current);
248
+ const options = getErrorOptions(ctx, current);
153
249
  const optionsNode = options
154
- ? serializePropertiesSync(ctx, options)
250
+ ? generateProperties(ctx, options)
155
251
  : undefined;
156
- const errorsNode = generateTreeSync(ctx, current.errors as ServerValue);
157
- return [SerovalNodeType.AggregateError, [current.message, optionsNode, errorsNode], id];
252
+ return {
253
+ t: SerovalNodeType.AggregateError,
254
+ i: id,
255
+ s: undefined,
256
+ l: undefined,
257
+ c: undefined,
258
+ m: current.message,
259
+ d: optionsNode,
260
+ a: undefined,
261
+ n: parse(ctx, current.errors),
262
+ };
158
263
  }
159
264
 
160
265
  function generateErrorNode(
161
266
  ctx: ParserContext,
162
- current: Error,
163
267
  id: number,
268
+ current: Error,
164
269
  ): SerovalErrorNode {
165
- const options = getErrorOptions(current);
270
+ const options = getErrorOptions(ctx, current);
166
271
  const optionsNode = options
167
- ? serializePropertiesSync(ctx, options)
272
+ ? generateProperties(ctx, options)
168
273
  : undefined;
169
- return [
170
- SerovalNodeType.Error,
171
- [getErrorConstructor(current), current.message, optionsNode],
172
- id,
173
- ];
174
- }
175
-
176
- function generateIterableNode(
177
- ctx: ParserContext,
178
- current: Iterable<ServerValue>,
179
- id: number,
180
- ): SerovalIterableNode {
181
- assert(ctx.features & Feature.SymbolIterator, 'Unsupported type "Iterable"');
182
- const options = getIterableOptions(current);
183
- return [SerovalNodeType.Iterable, [
184
- // Parse options first before the items
185
- options ? serializePropertiesSync(ctx, options) : undefined,
186
- generateNodeList(ctx, Array.from(current)),
187
- ], id];
274
+ return {
275
+ t: SerovalNodeType.Error,
276
+ i: id,
277
+ s: undefined,
278
+ l: undefined,
279
+ c: getErrorConstructor(current),
280
+ m: current.message,
281
+ d: optionsNode,
282
+ a: undefined,
283
+ n: undefined,
284
+ };
188
285
  }
189
286
 
190
- function generateTreeSync(
287
+ function parse(
191
288
  ctx: ParserContext,
192
289
  current: ServerValue,
193
290
  ): SerovalNode {
194
- if (isPrimitive(current)) {
195
- return [SerovalNodeType.Primitive, serializePrimitive(current)];
196
- }
197
- // Non-primitive values needs a reference ID
198
- // mostly because the values themselves are stateful
199
- const id = generateRef(ctx, current);
200
- if (Array.isArray(id)) {
201
- return id;
202
- }
203
- const semiPrimitive = generateSemiPrimitiveValue(ctx, current, id);
204
- if (semiPrimitive) {
205
- return semiPrimitive;
206
- }
207
- if (constructorCheck<Set<ServerValue>>(current, Set)) {
208
- return generateSetNode(ctx, current, id);
209
- }
210
- if (constructorCheck<Map<ServerValue, ServerValue>>(current, Map)) {
211
- return generateMapNode(ctx, current, id);
212
- }
213
- if (Array.isArray(current)) {
214
- return generateArrayNode(ctx, current, id);
215
- }
216
- if (current instanceof AggregateError && ctx.features & Feature.AggregateError) {
217
- return generateAggregateErrorNode(ctx, current, id);
218
- }
219
- if (current instanceof Error) {
220
- return generateErrorNode(ctx, current, id);
221
- }
222
- if (isIterable(current)) {
223
- return generateIterableNode(ctx, current, id);
224
- }
225
- const empty = current.constructor == null;
226
- if (current.constructor === Object || empty) {
227
- return [
228
- empty ? SerovalNodeType.NullConstructor : SerovalNodeType.Object,
229
- serializePropertiesSync(ctx, current as Record<string, unknown>),
230
- id,
231
- ];
291
+ switch (typeof current) {
292
+ case 'boolean':
293
+ return current ? TRUE_NODE : FALSE_NODE;
294
+ case 'undefined':
295
+ return UNDEFINED_NODE;
296
+ case 'string':
297
+ return createPrimitiveNode(quote(current));
298
+ case 'number':
299
+ if (Object.is(current, -0)) {
300
+ return NEG_ZERO_NODE;
301
+ }
302
+ if (Object.is(current, Infinity)) {
303
+ return createPrimitiveNode('1/0');
304
+ }
305
+ if (Object.is(current, -Infinity)) {
306
+ return createPrimitiveNode('-1/0');
307
+ }
308
+ return createPrimitiveNode(current);
309
+ case 'bigint':
310
+ return createBigIntNode(ctx, current);
311
+ case 'object': {
312
+ if (!current) {
313
+ return NULL_NODE;
314
+ }
315
+ // Non-primitive values needs a reference ID
316
+ // mostly because the values themselves are stateful
317
+ const id = createRef(ctx, current, true);
318
+ if (ctx.markedRefs.has(id)) {
319
+ return createReferenceNode(id);
320
+ }
321
+ if (Array.isArray(current)) {
322
+ return generateArrayNode(ctx, id, current);
323
+ }
324
+ switch (current.constructor) {
325
+ case Date:
326
+ return createDateNode(id, current as Date);
327
+ case RegExp:
328
+ return createRegExpNode(id, current as RegExp);
329
+ case Int8Array:
330
+ case Int16Array:
331
+ case Int32Array:
332
+ case Uint8Array:
333
+ case Uint16Array:
334
+ case Uint32Array:
335
+ case Uint8ClampedArray:
336
+ case Float32Array:
337
+ case Float64Array:
338
+ return createTypedArrayNode(ctx, id, current as TypedArrayValue);
339
+ case BigInt64Array:
340
+ case BigUint64Array:
341
+ return createBigIntTypedArrayNode(ctx, id, current as BigIntTypedArrayValue);
342
+ case Map:
343
+ return generateMapNode(ctx, id, current as Map<ServerValue, ServerValue>);
344
+ case Set:
345
+ return generateSetNode(ctx, id, current as Set<ServerValue>);
346
+ case Object:
347
+ return generateObjectNode(ctx, id, current as Record<string, ServerValue>, false);
348
+ case undefined:
349
+ return generateObjectNode(ctx, id, current as Record<string, ServerValue>, true);
350
+ case AggregateError:
351
+ if (ctx.features & Feature.AggregateError) {
352
+ return generateAggregateErrorNode(ctx, id, current as AggregateError);
353
+ }
354
+ return generateErrorNode(ctx, id, current as AggregateError);
355
+ case Error:
356
+ case EvalError:
357
+ case RangeError:
358
+ case ReferenceError:
359
+ case SyntaxError:
360
+ case TypeError:
361
+ case URIError:
362
+ return generateErrorNode(ctx, id, current as Error);
363
+ default:
364
+ break;
365
+ }
366
+ if (current instanceof AggregateError && ctx.features & Feature.AggregateError) {
367
+ return generateAggregateErrorNode(ctx, id, current);
368
+ }
369
+ if (current instanceof Error) {
370
+ return generateErrorNode(ctx, id, current);
371
+ }
372
+ // Generator functions don't have a global constructor
373
+ if (Symbol.iterator in current) {
374
+ return generateIterableNode(ctx, id, current);
375
+ }
376
+ throw new Error('Unsupported type');
377
+ }
378
+ default:
379
+ throw new Error('Unsupported type');
232
380
  }
233
- throw new Error('Unsupported value');
234
381
  }
235
382
 
236
383
  export default function parseSync(
237
384
  ctx: ParserContext,
238
385
  current: ServerValue,
239
386
  ) {
240
- const result = generateTreeSync(ctx, current);
241
- return [result, createRef(ctx, current), result[0] === SerovalNodeType.Object] as const;
387
+ const result = parse(ctx, current);
388
+ const isObject = result.t === SerovalNodeType.Object
389
+ || result.t === SerovalNodeType.Iterable;
390
+ return [result, createRef(ctx, current, false), isObject] as const;
242
391
  }