seroval 1.2.0 → 1.3.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.
Files changed (52) hide show
  1. package/LICENSE +1 -1
  2. package/dist/cjs/development/index.cjs +283 -398
  3. package/dist/cjs/development/index.cjs.map +3 -3
  4. package/dist/cjs/production/index.cjs +5 -5
  5. package/dist/esm/development/index.mjs +283 -398
  6. package/dist/esm/development/index.mjs.map +3 -3
  7. package/dist/esm/production/index.mjs +5 -5
  8. package/dist/types/core/base-primitives.d.ts +1 -2
  9. package/dist/types/core/base-primitives.d.ts.map +1 -1
  10. package/dist/types/core/compat.d.ts +2 -3
  11. package/dist/types/core/compat.d.ts.map +1 -1
  12. package/dist/types/core/constants.d.ts +3 -6
  13. package/dist/types/core/constants.d.ts.map +1 -1
  14. package/dist/types/core/context/deserializer.d.ts +0 -3
  15. package/dist/types/core/context/deserializer.d.ts.map +1 -1
  16. package/dist/types/core/context/parser/async.d.ts +2 -2
  17. package/dist/types/core/context/parser/async.d.ts.map +1 -1
  18. package/dist/types/core/context/parser/stream.d.ts +8 -7
  19. package/dist/types/core/context/parser/stream.d.ts.map +1 -1
  20. package/dist/types/core/context/parser/sync.d.ts +3 -3
  21. package/dist/types/core/context/parser/sync.d.ts.map +1 -1
  22. package/dist/types/core/context/parser.d.ts +10 -12
  23. package/dist/types/core/context/parser.d.ts.map +1 -1
  24. package/dist/types/core/context/serializer.d.ts +1 -5
  25. package/dist/types/core/context/serializer.d.ts.map +1 -1
  26. package/dist/types/core/cross/index.d.ts.map +1 -1
  27. package/dist/types/core/function-string.d.ts +3 -0
  28. package/dist/types/core/function-string.d.ts.map +1 -0
  29. package/dist/types/core/special-reference.d.ts +4 -5
  30. package/dist/types/core/special-reference.d.ts.map +1 -1
  31. package/dist/types/core/tree/serializer.d.ts +1 -2
  32. package/dist/types/core/tree/serializer.d.ts.map +1 -1
  33. package/dist/types/core/types.d.ts +5 -19
  34. package/dist/types/core/types.d.ts.map +1 -1
  35. package/package.json +9 -9
  36. package/src/core/base-primitives.ts +2 -22
  37. package/src/core/compat.ts +6 -2
  38. package/src/core/constants.ts +2 -5
  39. package/src/core/context/deserializer.ts +2 -35
  40. package/src/core/context/parser/async.ts +67 -84
  41. package/src/core/context/parser/stream.ts +64 -109
  42. package/src/core/context/parser/sync.ts +66 -83
  43. package/src/core/context/parser.ts +24 -56
  44. package/src/core/context/serializer.ts +12 -128
  45. package/src/core/cross/index.ts +2 -6
  46. package/src/core/function-string.ts +31 -0
  47. package/src/core/special-reference.ts +96 -8
  48. package/src/core/tree/serializer.ts +0 -7
  49. package/src/core/types.ts +5 -27
  50. package/dist/types/core/abort-signal.d.ts +0 -2
  51. package/dist/types/core/abort-signal.d.ts.map +0 -1
  52. package/src/core/abort-signal.ts +0 -13
@@ -1,4 +1,3 @@
1
- import { abortSignalToPromise } from '../../abort-signal';
2
1
  import {
3
2
  createAggregateErrorNode,
4
3
  createArrayBufferNode,
@@ -38,7 +37,6 @@ import type { Stream } from '../../stream';
38
37
  import { createStreamFromAsyncIterable, isStream } from '../../stream';
39
38
  import { serializeString } from '../../string';
40
39
  import type {
41
- SerovalAbortSignalSyncNode,
42
40
  SerovalAggregateErrorNode,
43
41
  SerovalArrayNode,
44
42
  SerovalBigIntTypedArrayNode,
@@ -64,7 +62,7 @@ import type {
64
62
  BigIntTypedArrayValue,
65
63
  TypedArrayValue,
66
64
  } from '../../utils/typed-array';
67
- import { BaseParserContext } from '../parser';
65
+ import { BaseParserContext, ParserNodeType } from '../parser';
68
66
 
69
67
  type ObjectLikeNode =
70
68
  | SerovalObjectNode
@@ -77,7 +75,7 @@ export default abstract class BaseAsyncParserContext extends BaseParserContext {
77
75
  for (let i = 0, len = current.length; i < len; i++) {
78
76
  // For consistency in holes
79
77
  if (i in current) {
80
- nodes[i] = await this.parse(current[i]);
78
+ nodes[i] = await this.parseTop(current[i]);
81
79
  }
82
80
  }
83
81
  return nodes;
@@ -98,7 +96,7 @@ export default abstract class BaseAsyncParserContext extends BaseParserContext {
98
96
  const valueNodes: SerovalNode[] = [];
99
97
  for (let i = 0, len = entries.length; i < len; i++) {
100
98
  keyNodes.push(serializeString(entries[i][0]));
101
- valueNodes.push(await this.parse(entries[i][1]));
99
+ valueNodes.push(await this.parseTop(entries[i][1]));
102
100
  }
103
101
  // Check special properties
104
102
  let symbol = Symbol.iterator;
@@ -107,7 +105,7 @@ export default abstract class BaseAsyncParserContext extends BaseParserContext {
107
105
  valueNodes.push(
108
106
  createIteratorFactoryInstanceNode(
109
107
  this.parseIteratorFactory(),
110
- await this.parse(
108
+ await this.parseTop(
111
109
  iteratorToSequence(properties as unknown as Iterable<unknown>),
112
110
  ),
113
111
  ),
@@ -119,7 +117,7 @@ export default abstract class BaseAsyncParserContext extends BaseParserContext {
119
117
  valueNodes.push(
120
118
  createAsyncIteratorFactoryInstanceNode(
121
119
  this.parseAsyncIteratorFactory(),
122
- await this.parse(
120
+ await this.parseTop(
123
121
  createStreamFromAsyncIterable(
124
122
  properties as unknown as AsyncIterable<unknown>,
125
123
  ),
@@ -161,14 +159,18 @@ export default abstract class BaseAsyncParserContext extends BaseParserContext {
161
159
  id: number,
162
160
  current: object,
163
161
  ): Promise<SerovalBoxedNode> {
164
- return createBoxedNode(id, await this.parse(current.valueOf()));
162
+ return createBoxedNode(id, await this.parseTop(current.valueOf()));
165
163
  }
166
164
 
167
165
  private async parseTypedArray(
168
166
  id: number,
169
167
  current: TypedArrayValue,
170
168
  ): Promise<SerovalTypedArrayNode> {
171
- return createTypedArrayNode(id, current, await this.parse(current.buffer));
169
+ return createTypedArrayNode(
170
+ id,
171
+ current,
172
+ await this.parseTop(current.buffer),
173
+ );
172
174
  }
173
175
 
174
176
  private async parseBigIntTypedArray(
@@ -178,7 +180,7 @@ export default abstract class BaseAsyncParserContext extends BaseParserContext {
178
180
  return createBigIntTypedArrayNode(
179
181
  id,
180
182
  current,
181
- await this.parse(current.buffer),
183
+ await this.parseTop(current.buffer),
182
184
  );
183
185
  }
184
186
 
@@ -186,7 +188,7 @@ export default abstract class BaseAsyncParserContext extends BaseParserContext {
186
188
  id: number,
187
189
  current: DataView,
188
190
  ): Promise<SerovalDataViewNode> {
189
- return createDataViewNode(id, current, await this.parse(current.buffer));
191
+ return createDataViewNode(id, current, await this.parseTop(current.buffer));
190
192
  }
191
193
 
192
194
  private async parseError(
@@ -220,8 +222,8 @@ export default abstract class BaseAsyncParserContext extends BaseParserContext {
220
222
  const keyNodes: SerovalNode[] = [];
221
223
  const valueNodes: SerovalNode[] = [];
222
224
  for (const [key, value] of current.entries()) {
223
- keyNodes.push(await this.parse(key));
224
- valueNodes.push(await this.parse(value));
225
+ keyNodes.push(await this.parseTop(key));
226
+ valueNodes.push(await this.parseTop(value));
225
227
  }
226
228
  return this.createMapNode(id, keyNodes, valueNodes, current.size);
227
229
  }
@@ -232,7 +234,7 @@ export default abstract class BaseAsyncParserContext extends BaseParserContext {
232
234
  ): Promise<SerovalSetNode> {
233
235
  const items: SerovalNode[] = [];
234
236
  for (const item of current.keys()) {
235
- items.push(await this.parse(item));
237
+ items.push(await this.parseTop(item));
236
238
  }
237
239
  return createSetNode(id, current.size, items);
238
240
  }
@@ -253,7 +255,7 @@ export default abstract class BaseAsyncParserContext extends BaseParserContext {
253
255
  NIL,
254
256
  NIL,
255
257
  NIL,
256
- await this.parse(result),
258
+ await this.parseTop(result),
257
259
  NIL,
258
260
  NIL,
259
261
  );
@@ -293,7 +295,7 @@ export default abstract class BaseAsyncParserContext extends BaseParserContext {
293
295
  const cleanup = current.on({
294
296
  next: value => {
295
297
  this.markRef(id);
296
- this.parse(value).then(
298
+ this.parseTop(value).then(
297
299
  data => {
298
300
  sequence.push(createStreamNextNode(id, data));
299
301
  },
@@ -305,7 +307,7 @@ export default abstract class BaseAsyncParserContext extends BaseParserContext {
305
307
  },
306
308
  throw: value => {
307
309
  this.markRef(id);
308
- this.parse(value).then(
310
+ this.parseTop(value).then(
309
311
  data => {
310
312
  sequence.push(createStreamThrowNode(id, data));
311
313
  resolve(sequence);
@@ -319,7 +321,7 @@ export default abstract class BaseAsyncParserContext extends BaseParserContext {
319
321
  },
320
322
  return: value => {
321
323
  this.markRef(id);
322
- this.parse(value).then(
324
+ this.parseTop(value).then(
323
325
  data => {
324
326
  sequence.push(createStreamReturnNode(id, data));
325
327
  resolve(sequence);
@@ -336,36 +338,6 @@ export default abstract class BaseAsyncParserContext extends BaseParserContext {
336
338
  );
337
339
  }
338
340
 
339
- private async parseAbortSignalSync(
340
- id: number,
341
- current: AbortSignal,
342
- ): Promise<SerovalAbortSignalSyncNode> {
343
- return createSerovalNode(
344
- SerovalNodeType.AbortSignalSync,
345
- id,
346
- NIL,
347
- NIL,
348
- NIL,
349
- NIL,
350
- NIL,
351
- NIL,
352
- NIL,
353
- await this.parse(current.reason),
354
- NIL,
355
- NIL,
356
- );
357
- }
358
-
359
- private async parseAbortSignal(
360
- id: number,
361
- current: AbortSignal,
362
- ): Promise<SerovalAbortSignalSyncNode> {
363
- if (!current.aborted) {
364
- await abortSignalToPromise(current);
365
- }
366
- return this.parseAbortSignalSync(id, current);
367
- }
368
-
369
341
  // biome-ignore lint/complexity/noExcessiveCognitiveComplexity: ehh
370
342
  private async parseObject(id: number, current: object): Promise<SerovalNode> {
371
343
  if (Array.isArray(current)) {
@@ -376,7 +348,7 @@ export default abstract class BaseAsyncParserContext extends BaseParserContext {
376
348
  }
377
349
  const currentClass = current.constructor;
378
350
  if (currentClass === OpaqueReference) {
379
- return this.parse(
351
+ return this.parseTop(
380
352
  (current as OpaqueReference<unknown, unknown>).replacement,
381
353
  );
382
354
  }
@@ -440,13 +412,6 @@ export default abstract class BaseAsyncParserContext extends BaseParserContext {
440
412
  return this.parsePromise(id, current as unknown as Promise<unknown>);
441
413
  }
442
414
  const currentFeatures = this.features;
443
- if (
444
- currentFeatures & Feature.AbortSignal &&
445
- typeof AbortSignal !== 'undefined' &&
446
- currentClass === AbortSignal
447
- ) {
448
- return this.parseAbortSignal(id, current as unknown as AbortSignal);
449
- }
450
415
  // BigInt Typed Arrays
451
416
  if (currentFeatures & Feature.BigIntTypedArray) {
452
417
  switch (currentClass) {
@@ -480,37 +445,55 @@ export default abstract class BaseAsyncParserContext extends BaseParserContext {
480
445
  throw new SerovalUnsupportedTypeError(current);
481
446
  }
482
447
 
483
- async parse<T>(current: T): Promise<SerovalNode> {
484
- try {
485
- switch (typeof current) {
486
- case 'boolean':
487
- return current ? TRUE_NODE : FALSE_NODE;
488
- case 'undefined':
489
- return UNDEFINED_NODE;
490
- case 'string':
491
- return createStringNode(current as string);
492
- case 'number':
493
- return createNumberNode(current as number);
494
- case 'bigint':
495
- return createBigIntNode(current as bigint);
496
- case 'object': {
497
- if (current) {
498
- const ref = this.getReference(current);
499
- return ref.type === 0
500
- ? await this.parseObject(ref.value, current as object)
501
- : ref.value;
502
- }
503
- return NULL_NODE;
448
+ protected async parseFunction(current: unknown): Promise<SerovalNode> {
449
+ const ref = this.getReference(current);
450
+ if (ref.type !== ParserNodeType.Fresh) {
451
+ return ref.value;
452
+ }
453
+ const plugin = await this.parsePlugin(ref.value, current);
454
+ if (plugin) {
455
+ return plugin;
456
+ }
457
+ throw new SerovalUnsupportedTypeError(current);
458
+ }
459
+
460
+ async parseTop<T>(current: T): Promise<SerovalNode> {
461
+ switch (typeof current) {
462
+ case 'boolean':
463
+ return current ? TRUE_NODE : FALSE_NODE;
464
+ case 'undefined':
465
+ return UNDEFINED_NODE;
466
+ case 'string':
467
+ return createStringNode(current as string);
468
+ case 'number':
469
+ return createNumberNode(current as number);
470
+ case 'bigint':
471
+ return createBigIntNode(current as bigint);
472
+ case 'object': {
473
+ if (current) {
474
+ const ref = this.getReference(current);
475
+ return ref.type === 0
476
+ ? await this.parseObject(ref.value, current as object)
477
+ : ref.value;
504
478
  }
505
- case 'symbol':
506
- return this.parseWellKnownSymbol(current);
507
- case 'function':
508
- return this.parseFunction(current as (...args: unknown[]) => unknown);
509
- default:
510
- throw new SerovalUnsupportedTypeError(current);
479
+ return NULL_NODE;
511
480
  }
481
+ case 'symbol':
482
+ return this.parseWellKnownSymbol(current);
483
+ case 'function':
484
+ return this.parseFunction(current);
485
+ default:
486
+ throw new SerovalUnsupportedTypeError(current);
487
+ }
488
+ }
489
+
490
+ async parse<T>(current: T): Promise<SerovalNode> {
491
+ try {
492
+ return await this.parseTop(current);
512
493
  } catch (error) {
513
- throw new SerovalParserError(error);
494
+ throw error instanceof SerovalParserError
495
+ ? error
496
+ : new SerovalParserError(error);
514
497
  }
515
498
  }
516
499
  }
@@ -16,8 +16,6 @@ import type { Stream } from '../../stream';
16
16
  import { createStreamFromAsyncIterable } from '../../stream';
17
17
  import { serializeString } from '../../string';
18
18
  import type {
19
- SerovalAbortSignalConstructorNode,
20
- SerovalAbortSignalSyncNode,
21
19
  SerovalNode,
22
20
  SerovalObjectRecordKey,
23
21
  SerovalObjectRecordNode,
@@ -73,7 +71,7 @@ export default abstract class BaseStreamParserContext extends BaseSyncParserCont
73
71
  }
74
72
  }
75
73
 
76
- private onParse(node: SerovalNode): void {
74
+ onParse(node: SerovalNode): void {
77
75
  if (this.initial) {
78
76
  this.buffer.push(node);
79
77
  } else {
@@ -81,7 +79,7 @@ export default abstract class BaseStreamParserContext extends BaseSyncParserCont
81
79
  }
82
80
  }
83
81
 
84
- private onError(error: unknown): void {
82
+ onError(error: unknown): void {
85
83
  if (this.onErrorCallback) {
86
84
  this.onErrorCallback(error);
87
85
  } else {
@@ -95,11 +93,11 @@ export default abstract class BaseStreamParserContext extends BaseSyncParserCont
95
93
  }
96
94
  }
97
95
 
98
- private pushPendingState(): void {
96
+ pushPendingState(): void {
99
97
  this.pending++;
100
98
  }
101
99
 
102
- private popPendingState(): void {
100
+ popPendingState(): void {
103
101
  if (--this.pending <= 0) {
104
102
  this.onDone();
105
103
  }
@@ -113,7 +111,7 @@ export default abstract class BaseStreamParserContext extends BaseSyncParserCont
113
111
  const valueNodes: SerovalNode[] = [];
114
112
  for (let i = 0, len = entries.length; i < len; i++) {
115
113
  keyNodes.push(serializeString(entries[i][0]));
116
- valueNodes.push(this.parse(entries[i][1]));
114
+ valueNodes.push(this.parseTop(entries[i][1]));
117
115
  }
118
116
  // Check special properties, symbols in this case
119
117
  let symbol = Symbol.iterator;
@@ -122,7 +120,7 @@ export default abstract class BaseStreamParserContext extends BaseSyncParserCont
122
120
  valueNodes.push(
123
121
  createIteratorFactoryInstanceNode(
124
122
  this.parseIteratorFactory(),
125
- this.parse(
123
+ this.parseTop(
126
124
  iteratorToSequence(properties as unknown as Iterable<unknown>),
127
125
  ),
128
126
  ),
@@ -134,7 +132,7 @@ export default abstract class BaseStreamParserContext extends BaseSyncParserCont
134
132
  valueNodes.push(
135
133
  createAsyncIteratorFactoryInstanceNode(
136
134
  this.parseAsyncIteratorFactory(),
137
- this.parse(
135
+ this.parseTop(
138
136
  createStreamFromAsyncIterable(
139
137
  properties as unknown as AsyncIterable<unknown>,
140
138
  ),
@@ -159,66 +157,68 @@ export default abstract class BaseStreamParserContext extends BaseSyncParserCont
159
157
  };
160
158
  }
161
159
 
160
+ protected handlePromiseSuccess(id: number, data: unknown): void {
161
+ const parsed = this.parseWithError(data);
162
+ if (parsed) {
163
+ this.onParse(
164
+ createSerovalNode(
165
+ SerovalNodeType.PromiseSuccess,
166
+ id,
167
+ NIL,
168
+ NIL,
169
+ NIL,
170
+ NIL,
171
+ NIL,
172
+ NIL,
173
+ [this.parseSpecialReference(SpecialReference.PromiseSuccess), parsed],
174
+ NIL,
175
+ NIL,
176
+ NIL,
177
+ ),
178
+ );
179
+ }
180
+ this.popPendingState();
181
+ }
182
+
183
+ protected handlePromiseFailure(id: number, data: unknown): void {
184
+ if (this.alive) {
185
+ const parsed = this.parseWithError(data);
186
+ if (parsed) {
187
+ this.onParse(
188
+ createSerovalNode(
189
+ SerovalNodeType.PromiseFailure,
190
+ id,
191
+ NIL,
192
+ NIL,
193
+ NIL,
194
+ NIL,
195
+ NIL,
196
+ NIL,
197
+ [
198
+ this.parseSpecialReference(SpecialReference.PromiseFailure),
199
+ parsed,
200
+ ],
201
+ NIL,
202
+ NIL,
203
+ NIL,
204
+ ),
205
+ );
206
+ }
207
+ }
208
+ this.popPendingState();
209
+ }
210
+
162
211
  protected parsePromise(
163
212
  id: number,
164
213
  current: Promise<unknown>,
165
214
  ): SerovalPromiseConstructorNode {
215
+ const resolver = this.createIndex({});
166
216
  current.then(
167
- data => {
168
- const parsed = this.parseWithError(data);
169
- if (parsed) {
170
- this.onParse(
171
- createSerovalNode(
172
- SerovalNodeType.PromiseResolve,
173
- id,
174
- NIL,
175
- NIL,
176
- NIL,
177
- NIL,
178
- NIL,
179
- NIL,
180
- [
181
- this.parseSpecialReference(SpecialReference.PromiseResolve),
182
- parsed,
183
- ],
184
- NIL,
185
- NIL,
186
- NIL,
187
- ),
188
- );
189
- }
190
- this.popPendingState();
191
- },
192
- data => {
193
- if (this.alive) {
194
- const parsed = this.parseWithError(data);
195
- if (parsed) {
196
- this.onParse(
197
- createSerovalNode(
198
- SerovalNodeType.PromiseReject,
199
- id,
200
- NIL,
201
- NIL,
202
- NIL,
203
- NIL,
204
- NIL,
205
- NIL,
206
- [
207
- this.parseSpecialReference(SpecialReference.PromiseReject),
208
- parsed,
209
- ],
210
- NIL,
211
- NIL,
212
- NIL,
213
- ),
214
- );
215
- }
216
- }
217
- this.popPendingState();
218
- },
217
+ this.handlePromiseSuccess.bind(this, resolver),
218
+ this.handlePromiseFailure.bind(this, resolver),
219
219
  );
220
220
  this.pushPendingState();
221
- return this.createPromiseConstructorNode(id);
221
+ return this.createPromiseConstructorNode(id, resolver);
222
222
  }
223
223
 
224
224
  protected parsePlugin(
@@ -281,54 +281,9 @@ export default abstract class BaseStreamParserContext extends BaseSyncParserCont
281
281
  return result;
282
282
  }
283
283
 
284
- protected parseAbortSignal(
285
- id: number,
286
- current: AbortSignal,
287
- ): SerovalAbortSignalConstructorNode | SerovalAbortSignalSyncNode {
288
- if (current.aborted) {
289
- return this.parseAbortSignalSync(id, current);
290
- }
291
-
292
- this.pushPendingState();
293
-
294
- current.addEventListener(
295
- 'abort',
296
- () => {
297
- if (this.alive) {
298
- const parsed = this.parseWithError(current.reason);
299
- if (parsed) {
300
- this.onParse(
301
- createSerovalNode(
302
- SerovalNodeType.AbortSignalAbort,
303
- id,
304
- NIL,
305
- NIL,
306
- NIL,
307
- NIL,
308
- NIL,
309
- NIL,
310
- [
311
- this.parseSpecialReference(SpecialReference.AbortSignalAbort),
312
- parsed,
313
- ],
314
- NIL,
315
- NIL,
316
- NIL,
317
- ),
318
- );
319
- }
320
- }
321
- this.popPendingState();
322
- },
323
- { once: true },
324
- );
325
-
326
- return this.createAbortSignalConstructorNode(id);
327
- }
328
-
329
- private parseWithError<T>(current: T): SerovalNode | undefined {
284
+ parseWithError<T>(current: T): SerovalNode | undefined {
330
285
  try {
331
- return this.parse(current);
286
+ return this.parseTop(current);
332
287
  } catch (err) {
333
288
  this.onError(err);
334
289
  return NIL;