seroval 0.4.0-alpha.9 → 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,16 +1,28 @@
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,
@@ -20,84 +32,59 @@ import {
20
32
  SerovalMapNode,
21
33
  SerovalNode,
22
34
  SerovalNodeType,
35
+ SerovalNullConstructorNode,
36
+ SerovalObjectNode,
23
37
  SerovalObjectRecordNode,
24
38
  SerovalSetNode,
25
39
  } from './types';
26
40
 
27
- function serializePropertiesSync(
28
- ctx: ParserContext,
29
- properties: Record<string, unknown>,
30
- ): SerovalObjectRecordNode {
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);
37
- 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;
43
- deferredSize++;
44
- } else {
45
- keyNodes[nodesSize] = key;
46
- valueNodes[nodesSize] = generateTreeSync(ctx, properties[key] as ServerValue);
47
- nodesSize++;
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
+ }
48
56
  }
49
57
  }
50
- for (let i = 0; i < deferredSize; i++) {
51
- keyNodes[nodesSize + i] = deferredKeys[i];
52
- valueNodes[nodesSize + i] = generateTreeSync(ctx, deferredValues[i]);
58
+ for (let i = 0; i < size; i++) {
59
+ if (i in deferred) {
60
+ nodes[i] = parse(ctx, deferred[i]);
61
+ }
53
62
  }
54
- return {
55
- k: keyNodes,
56
- v: valueNodes,
57
- s: size,
58
- };
63
+ return nodes;
59
64
  }
60
65
 
61
- function generateSetNode(
66
+ function generateArrayNode(
62
67
  ctx: ParserContext,
63
- current: Set<ServerValue>,
64
68
  id: number,
65
- ): SerovalSetNode {
66
- assert(ctx.features & Feature.Set, 'Unsupported type "Set"');
67
- const len = current.size;
68
- const nodes = new Array<SerovalNode>(len);
69
- const deferred = new Array<ServerValue>(len);
70
- let deferredSize = 0;
71
- let nodeSize = 0;
72
- for (const item of current.keys()) {
73
- // Iterables are lazy, so the evaluation must be deferred
74
- if (isIterable(item)) {
75
- deferred[deferredSize++] = item;
76
- } else {
77
- nodes[nodeSize++] = generateTreeSync(ctx, item);
78
- }
79
- }
80
- // Parse deferred items
81
- for (let i = 0; i < deferredSize; i++) {
82
- nodes[nodeSize + i] = generateTreeSync(ctx, deferred[i]);
83
- }
69
+ current: ServerValue[],
70
+ ): SerovalArrayNode {
84
71
  return {
85
- t: SerovalNodeType.Set,
72
+ t: SerovalNodeType.Array,
86
73
  i: id,
87
- a: nodes,
88
74
  s: undefined,
89
75
  l: undefined,
90
- m: undefined,
91
76
  c: undefined,
77
+ m: undefined,
92
78
  d: undefined,
79
+ a: generateNodeList(ctx, current),
93
80
  n: undefined,
94
81
  };
95
82
  }
96
83
 
97
84
  function generateMapNode(
98
85
  ctx: ParserContext,
99
- current: Map<ServerValue, ServerValue>,
100
86
  id: number,
87
+ current: Map<ServerValue, ServerValue>,
101
88
  ): SerovalMapNode {
102
89
  assert(ctx.features & Feature.Map, 'Unsupported type "Map"');
103
90
  const len = current.size;
@@ -114,203 +101,291 @@ function generateMapNode(
114
101
  deferredValue[deferredSize] = value;
115
102
  deferredSize++;
116
103
  } else {
117
- keyNodes[nodeSize] = generateTreeSync(ctx, key);
118
- valueNodes[nodeSize] = generateTreeSync(ctx, value);
104
+ keyNodes[nodeSize] = parse(ctx, key);
105
+ valueNodes[nodeSize] = parse(ctx, value);
119
106
  nodeSize++;
120
107
  }
121
108
  }
122
109
  for (let i = 0; i < deferredSize; i++) {
123
- keyNodes[nodeSize + i] = generateTreeSync(ctx, deferredKey[i]);
124
- valueNodes[nodeSize + i] = generateTreeSync(ctx, deferredValue[i]);
110
+ keyNodes[nodeSize + i] = parse(ctx, deferredKey[i]);
111
+ valueNodes[nodeSize + i] = parse(ctx, deferredValue[i]);
125
112
  }
126
113
  return {
127
114
  t: SerovalNodeType.Map,
128
115
  i: id,
129
- a: undefined,
130
116
  s: undefined,
131
117
  l: undefined,
132
- m: undefined,
133
118
  c: undefined,
119
+ m: undefined,
134
120
  d: { k: keyNodes, v: valueNodes, s: len },
121
+ a: undefined,
135
122
  n: undefined,
136
123
  };
137
124
  }
138
125
 
139
- function generateNodeList(
126
+ function generateSetNode(
140
127
  ctx: ParserContext,
141
- current: ServerValue[],
142
- ) {
143
- const size = current.length;
144
- const nodes = new Array<SerovalNode>(size);
145
- const deferred = new Array<ServerValue>(size);
146
- for (let i = 0; i < size; i++) {
147
- if (i in current) {
148
- if (isIterable(current[i])) {
149
- deferred[i] = current[i];
150
- } else {
151
- nodes[i] = generateTreeSync(ctx, current[i]);
152
- }
128
+ id: number,
129
+ current: Set<ServerValue>,
130
+ ): SerovalSetNode {
131
+ assert(ctx.features & Feature.Set, 'Unsupported type "Set"');
132
+ const len = current.size;
133
+ const nodes = new Array<SerovalNode>(len);
134
+ const deferred = new Array<ServerValue>(len);
135
+ let deferredSize = 0;
136
+ let nodeSize = 0;
137
+ for (const item of current.keys()) {
138
+ // Iterables are lazy, so the evaluation must be deferred
139
+ if (isIterable(item)) {
140
+ deferred[deferredSize++] = item;
141
+ } else {
142
+ nodes[nodeSize++] = parse(ctx, item);
153
143
  }
154
144
  }
155
- for (let i = 0; i < size; i++) {
156
- if (i in deferred) {
157
- nodes[i] = generateTreeSync(ctx, deferred[i]);
145
+ // Parse deferred items
146
+ for (let i = 0; i < deferredSize; i++) {
147
+ nodes[nodeSize + i] = parse(ctx, deferred[i]);
148
+ }
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
+ };
160
+ }
161
+
162
+ function generateProperties(
163
+ ctx: ParserContext,
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);
172
+ let deferredSize = 0;
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;
180
+ deferredSize++;
181
+ } else {
182
+ keyNodes[nodesSize] = key;
183
+ valueNodes[nodesSize] = parse(ctx, item);
184
+ nodesSize++;
158
185
  }
159
186
  }
160
- return nodes;
187
+ for (let i = 0; i < deferredSize; i++) {
188
+ keyNodes[nodesSize + i] = deferredKeys[i];
189
+ valueNodes[nodesSize + i] = parse(ctx, deferredValues[i]);
190
+ }
191
+ return {
192
+ k: keyNodes,
193
+ v: valueNodes,
194
+ s: size,
195
+ };
161
196
  }
162
197
 
163
- function generateArrayNode(
198
+ function generateIterableNode(
164
199
  ctx: ParserContext,
165
- current: ServerValue[],
166
200
  id: number,
167
- ): SerovalArrayNode {
201
+ current: Iterable<ServerValue>,
202
+ ): SerovalIterableNode {
203
+ assert(ctx.features & Feature.SymbolIterator, 'Unsupported type "Iterable"');
204
+ const options = getIterableOptions(current);
168
205
  return {
169
- t: SerovalNodeType.Array,
206
+ t: SerovalNodeType.Iterable,
170
207
  i: id,
171
- a: generateNodeList(ctx, current),
172
208
  s: undefined,
173
209
  l: undefined,
210
+ c: undefined,
174
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
+ };
219
+ }
220
+
221
+ function generateObjectNode(
222
+ ctx: ParserContext,
223
+ id: number,
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,
175
235
  c: undefined,
176
- d: undefined,
236
+ m: undefined,
237
+ d: generateProperties(ctx, current),
238
+ a: undefined,
177
239
  n: undefined,
178
240
  };
179
241
  }
180
242
 
181
243
  function generateAggregateErrorNode(
182
244
  ctx: ParserContext,
183
- current: AggregateError,
184
245
  id: number,
246
+ current: AggregateError,
185
247
  ): SerovalAggregateErrorNode {
186
- const options = getErrorOptions(current);
248
+ const options = getErrorOptions(ctx, current);
187
249
  const optionsNode = options
188
- ? serializePropertiesSync(ctx, options)
250
+ ? generateProperties(ctx, options)
189
251
  : undefined;
190
252
  return {
191
253
  t: SerovalNodeType.AggregateError,
192
254
  i: id,
193
- a: undefined,
194
255
  s: undefined,
195
256
  l: undefined,
196
- m: current.message,
197
257
  c: undefined,
258
+ m: current.message,
198
259
  d: optionsNode,
199
- n: generateTreeSync(ctx, current.errors as ServerValue),
260
+ a: undefined,
261
+ n: parse(ctx, current.errors),
200
262
  };
201
263
  }
202
264
 
203
265
  function generateErrorNode(
204
266
  ctx: ParserContext,
205
- current: Error,
206
267
  id: number,
268
+ current: Error,
207
269
  ): SerovalErrorNode {
208
- const options = getErrorOptions(current);
270
+ const options = getErrorOptions(ctx, current);
209
271
  const optionsNode = options
210
- ? serializePropertiesSync(ctx, options)
272
+ ? generateProperties(ctx, options)
211
273
  : undefined;
212
274
  return {
213
275
  t: SerovalNodeType.Error,
214
276
  i: id,
215
- a: undefined,
216
277
  s: undefined,
217
278
  l: undefined,
218
- m: current.message,
219
279
  c: getErrorConstructor(current),
280
+ m: current.message,
220
281
  d: optionsNode,
282
+ a: undefined,
221
283
  n: undefined,
222
284
  };
223
285
  }
224
286
 
225
- function generateIterableNode(
226
- ctx: ParserContext,
227
- current: Iterable<ServerValue>,
228
- id: number,
229
- ): SerovalIterableNode {
230
- assert(ctx.features & Feature.SymbolIterator, 'Unsupported type "Iterable"');
231
- const options = getIterableOptions(current);
232
- return {
233
- t: SerovalNodeType.Iterable,
234
- i: id,
235
- s: undefined,
236
- l: undefined,
237
- m: undefined,
238
- c: undefined,
239
- // Parse options first before the items
240
- d: options ? serializePropertiesSync(ctx, options) : undefined,
241
- a: generateNodeList(ctx, Array.from(current)),
242
- n: undefined,
243
- };
244
- }
245
-
246
- function generateTreeSync(
287
+ function parse(
247
288
  ctx: ParserContext,
248
289
  current: ServerValue,
249
290
  ): SerovalNode {
250
- if (isPrimitive(current)) {
251
- return {
252
- t: SerovalNodeType.Primitive,
253
- i: undefined,
254
- s: serializePrimitive(current),
255
- l: undefined,
256
- m: undefined,
257
- c: undefined,
258
- // Parse options first before the items
259
- d: undefined,
260
- a: undefined,
261
- n: undefined,
262
- };
263
- }
264
- // Non-primitive values needs a reference ID
265
- // mostly because the values themselves are stateful
266
- const id = generateRef(ctx, current);
267
- if (typeof id !== 'number') {
268
- return id;
269
- }
270
- const semiPrimitive = generateSemiPrimitiveValue(ctx, current, id);
271
- if (semiPrimitive) {
272
- return semiPrimitive;
273
- }
274
- if (constructorCheck<Set<ServerValue>>(current, Set)) {
275
- return generateSetNode(ctx, current, id);
276
- }
277
- if (constructorCheck<Map<ServerValue, ServerValue>>(current, Map)) {
278
- return generateMapNode(ctx, current, id);
279
- }
280
- if (Array.isArray(current)) {
281
- return generateArrayNode(ctx, current, id);
282
- }
283
- if (current instanceof AggregateError && ctx.features & Feature.AggregateError) {
284
- return generateAggregateErrorNode(ctx, current, id);
285
- }
286
- if (current instanceof Error) {
287
- return generateErrorNode(ctx, current, id);
288
- }
289
- if (isIterable(current)) {
290
- return generateIterableNode(ctx, current, id);
291
- }
292
- const empty = current.constructor == null;
293
- if (current.constructor === Object || empty) {
294
- return {
295
- t: empty ? SerovalNodeType.NullConstructor : SerovalNodeType.Object,
296
- i: id,
297
- s: undefined,
298
- l: undefined,
299
- m: undefined,
300
- c: undefined,
301
- // Parse options first before the items
302
- d: serializePropertiesSync(ctx, current as Record<string, unknown>),
303
- a: undefined,
304
- n: undefined,
305
- };
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');
306
380
  }
307
- throw new Error('Unsupported value');
308
381
  }
309
382
 
310
383
  export default function parseSync(
311
384
  ctx: ParserContext,
312
385
  current: ServerValue,
313
386
  ) {
314
- const result = generateTreeSync(ctx, current);
315
- return [result, createRef(ctx, current), result.t === 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;
316
391
  }
package/src/types.ts CHANGED
@@ -25,7 +25,9 @@ export type TypedArrayValue =
25
25
  | Uint32Array
26
26
  | Uint8ClampedArray
27
27
  | Float32Array
28
- | Float64Array
28
+ | Float64Array;
29
+
30
+ export type BigIntTypedArrayValue =
29
31
  | BigInt64Array
30
32
  | BigUint64Array;
31
33
 
@@ -33,6 +35,7 @@ export type SemiPrimitiveValue =
33
35
  | RegExp
34
36
  | Date
35
37
  | TypedArrayValue
38
+ | BigIntTypedArrayValue
36
39
  | bigint;
37
40
 
38
41
  export type CommonServerValue =
@@ -1,5 +0,0 @@
1
- import { AsyncServerValue, PrimitiveValue } from './types';
2
- export declare function isPrimitive(current: unknown): current is PrimitiveValue;
3
- export declare function isPromise<T>(obj: unknown): obj is PromiseLike<T>;
4
- export declare function constructorCheck<T extends NonNullable<AsyncServerValue>>(value: unknown, constructor: unknown): value is T;
5
- export declare function isIterable<T>(obj: unknown): obj is Iterable<T>;
package/src/checks.ts DELETED
@@ -1,40 +0,0 @@
1
- import { AsyncServerValue, PrimitiveValue } from './types';
2
-
3
- export function isPrimitive(current: unknown): current is PrimitiveValue {
4
- if (!current || current === true) {
5
- return true;
6
- }
7
- const type = typeof current;
8
- return type === 'number'
9
- || type === 'string';
10
- }
11
-
12
- export function isPromise<T>(obj: unknown): obj is PromiseLike<T> {
13
- return !!obj
14
- && (typeof obj === 'object' || typeof obj === 'function')
15
- && 'then' in obj
16
- && typeof obj.then === 'function';
17
- }
18
-
19
- export function constructorCheck<T extends NonNullable<AsyncServerValue>>(
20
- value: unknown,
21
- constructor: unknown,
22
- ): value is T {
23
- return (value as { constructor: unknown }).constructor === constructor;
24
- }
25
-
26
- function isIterableBuiltin(obj: unknown) {
27
- return (
28
- Array.isArray(obj)
29
- || constructorCheck<Set<any>>(obj, Set)
30
- || constructorCheck<Map<any, any>>(obj, Map)
31
- );
32
- }
33
-
34
- export function isIterable<T>(obj: unknown): obj is Iterable<T> {
35
- return !!obj
36
- && typeof obj === 'object'
37
- && !isIterableBuiltin(obj)
38
- && Symbol.iterator in obj
39
- && typeof obj[Symbol.iterator] === 'function';
40
- }