seroval 1.1.0 → 1.2.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 (55) hide show
  1. package/dist/cjs/development/index.cjs +710 -508
  2. package/dist/cjs/development/index.cjs.map +3 -3
  3. package/dist/cjs/production/index.cjs +7 -7
  4. package/dist/esm/development/index.mjs +710 -508
  5. package/dist/esm/development/index.mjs.map +3 -3
  6. package/dist/esm/production/index.mjs +7 -7
  7. package/dist/types/core/abort-signal.d.ts +2 -0
  8. package/dist/types/core/abort-signal.d.ts.map +1 -0
  9. package/dist/types/core/base-primitives.d.ts +2 -1
  10. package/dist/types/core/base-primitives.d.ts.map +1 -1
  11. package/dist/types/core/compat.d.ts +3 -2
  12. package/dist/types/core/compat.d.ts.map +1 -1
  13. package/dist/types/core/constants.d.ts +5 -1
  14. package/dist/types/core/constants.d.ts.map +1 -1
  15. package/dist/types/core/context/deserializer.d.ts +3 -0
  16. package/dist/types/core/context/deserializer.d.ts.map +1 -1
  17. package/dist/types/core/context/parser/async.d.ts +2 -0
  18. package/dist/types/core/context/parser/async.d.ts.map +1 -1
  19. package/dist/types/core/context/parser/stream.d.ts +2 -1
  20. package/dist/types/core/context/parser/stream.d.ts.map +1 -1
  21. package/dist/types/core/context/parser/sync.d.ts +3 -1
  22. package/dist/types/core/context/parser/sync.d.ts.map +1 -1
  23. package/dist/types/core/context/parser.d.ts +2 -1
  24. package/dist/types/core/context/parser.d.ts.map +1 -1
  25. package/dist/types/core/context/serializer.d.ts +4 -1
  26. package/dist/types/core/context/serializer.d.ts.map +1 -1
  27. package/dist/types/core/cross/serializer.d.ts.map +1 -1
  28. package/dist/types/core/literals.d.ts.map +1 -1
  29. package/dist/types/core/node.d.ts +8 -0
  30. package/dist/types/core/node.d.ts.map +1 -0
  31. package/dist/types/core/special-reference.d.ts +6 -1
  32. package/dist/types/core/special-reference.d.ts.map +1 -1
  33. package/dist/types/core/tree/serializer.d.ts +2 -1
  34. package/dist/types/core/tree/serializer.d.ts.map +1 -1
  35. package/dist/types/core/types.d.ts +20 -2
  36. package/dist/types/core/types.d.ts.map +1 -1
  37. package/dist/types/core/utils/iterator-to-sequence.d.ts.map +1 -1
  38. package/package.json +8 -8
  39. package/src/core/abort-signal.ts +13 -0
  40. package/src/core/base-primitives.ts +359 -337
  41. package/src/core/compat.ts +2 -1
  42. package/src/core/constants.ts +5 -0
  43. package/src/core/context/deserializer.ts +34 -1
  44. package/src/core/context/parser/async.ts +61 -20
  45. package/src/core/context/parser/stream.ts +89 -37
  46. package/src/core/context/parser/sync.ts +44 -3
  47. package/src/core/context/parser.ts +106 -85
  48. package/src/core/context/serializer.ts +56 -10
  49. package/src/core/cross/serializer.ts +6 -2
  50. package/src/core/literals.ts +16 -15
  51. package/src/core/node.ts +40 -0
  52. package/src/core/special-reference.ts +7 -0
  53. package/src/core/tree/serializer.ts +9 -1
  54. package/src/core/types.ts +32 -2
  55. package/src/core/utils/iterator-to-sequence.ts +3 -1
@@ -10,6 +10,7 @@ export const enum Feature {
10
10
  ErrorPrototypeStack = 0x04,
11
11
  ObjectAssign = 0x08,
12
12
  BigIntTypedArray = 0x10,
13
+ AbortSignal = 0x20,
13
14
  }
14
15
 
15
- export const ALL_ENABLED = 0x1f;
16
+ export const ALL_ENABLED = 0x2f;
@@ -45,6 +45,9 @@ export const enum SerovalNodeType {
45
45
  StreamNext = 32,
46
46
  StreamThrow = 33,
47
47
  StreamReturn = 34,
48
+ AbortSignalConstructor = 35,
49
+ AbortSignalAbort = 36,
50
+ AbortSignalSync = 37,
48
51
  }
49
52
 
50
53
  export const enum SerovalObjectFlags {
@@ -181,3 +184,5 @@ export const ERROR_CONSTRUCTOR: Record<ErrorConstructorTag, ErrorConstructors> =
181
184
  [ErrorConstructorTag.TypeError]: TypeError,
182
185
  [ErrorConstructorTag.URIError]: URIError,
183
186
  };
187
+
188
+ export const NIL = undefined;
@@ -17,6 +17,9 @@ import type { Stream } from '../stream';
17
17
  import { createStream, streamToAsyncIterable } from '../stream';
18
18
  import { deserializeString } from '../string';
19
19
  import type {
20
+ SerovalAbortSignalAbortNode,
21
+ SerovalAbortSignalConstructorNode,
22
+ SerovalAbortSignalSyncNode,
20
23
  SerovalAggregateErrorNode,
21
24
  SerovalArrayBufferNode,
22
25
  SerovalArrayNode,
@@ -198,7 +201,7 @@ export default abstract class BaseDeserializerContext
198
201
  private deserializeTypedArray(
199
202
  node: SerovalTypedArrayNode | SerovalBigIntTypedArrayNode,
200
203
  ): TypedArrayValue | BigIntTypedArrayValue {
201
- const construct = getTypedArrayConstructor(node.c);
204
+ const construct = getTypedArrayConstructor(node.c) as Int8ArrayConstructor;
202
205
  const source = this.deserialize(node.f) as ArrayBuffer;
203
206
  const result = this.assignIndexedValue(
204
207
  node.i,
@@ -369,6 +372,30 @@ export default abstract class BaseDeserializerContext
369
372
  return undefined;
370
373
  }
371
374
 
375
+ private deserializeAbortSignalConstructor(
376
+ node: SerovalAbortSignalConstructorNode,
377
+ ): unknown {
378
+ return this.assignIndexedValue(node.i, new AbortController()).signal;
379
+ }
380
+
381
+ private deserializeAbortSignalAbort(
382
+ node: SerovalAbortSignalAbortNode,
383
+ ): unknown {
384
+ const controller = this.refs.get(node.i) as AbortController | undefined;
385
+ assert(controller, new SerovalMissingInstanceError('AbortSignal'));
386
+ controller.abort(this.deserialize(node.a[1]));
387
+ return undefined;
388
+ }
389
+
390
+ private deserializeAbortSignalSync(
391
+ node: SerovalAbortSignalSyncNode,
392
+ ): unknown {
393
+ return this.assignIndexedValue(
394
+ node.i,
395
+ AbortSignal.abort(this.deserialize(node.f)),
396
+ );
397
+ }
398
+
372
399
  deserialize(node: SerovalNode): unknown {
373
400
  try {
374
401
  switch (node.t) {
@@ -438,6 +465,12 @@ export default abstract class BaseDeserializerContext
438
465
  return this.deserializeIteratorFactory(node);
439
466
  case SerovalNodeType.AsyncIteratorFactory:
440
467
  return this.deserializeAsyncIteratorFactory(node);
468
+ case SerovalNodeType.AbortSignalAbort:
469
+ return this.deserializeAbortSignalAbort(node);
470
+ case SerovalNodeType.AbortSignalConstructor:
471
+ return this.deserializeAbortSignalConstructor(node);
472
+ case SerovalNodeType.AbortSignalSync:
473
+ return this.deserializeAbortSignalSync(node);
441
474
  // case SerovalNodeType.SpecialReference:
442
475
  default:
443
476
  throw new SerovalUnsupportedNodeError(node);
@@ -1,3 +1,4 @@
1
+ import { abortSignalToPromise } from '../../abort-signal';
1
2
  import {
2
3
  createAggregateErrorNode,
3
4
  createArrayBufferNode,
@@ -22,7 +23,7 @@ import {
22
23
  createTypedArrayNode,
23
24
  } from '../../base-primitives';
24
25
  import { Feature } from '../../compat';
25
- import { SerovalNodeType } from '../../constants';
26
+ import { NIL, SerovalNodeType } from '../../constants';
26
27
  import { SerovalParserError, SerovalUnsupportedTypeError } from '../../errors';
27
28
  import {
28
29
  FALSE_NODE,
@@ -30,12 +31,14 @@ import {
30
31
  TRUE_NODE,
31
32
  UNDEFINED_NODE,
32
33
  } from '../../literals';
34
+ import { createSerovalNode } from '../../node';
33
35
  import { OpaqueReference } from '../../opaque-reference';
34
36
  import { SpecialReference } from '../../special-reference';
35
37
  import type { Stream } from '../../stream';
36
38
  import { createStreamFromAsyncIterable, isStream } from '../../stream';
37
39
  import { serializeString } from '../../string';
38
40
  import type {
41
+ SerovalAbortSignalSyncNode,
39
42
  SerovalAggregateErrorNode,
40
43
  SerovalArrayNode,
41
44
  SerovalBigIntTypedArrayNode,
@@ -194,7 +197,7 @@ export default abstract class BaseAsyncParserContext extends BaseParserContext {
194
197
  return createErrorNode(
195
198
  id,
196
199
  current,
197
- options ? await this.parseProperties(options) : undefined,
200
+ options ? await this.parseProperties(options) : NIL,
198
201
  );
199
202
  }
200
203
 
@@ -206,7 +209,7 @@ export default abstract class BaseAsyncParserContext extends BaseParserContext {
206
209
  return createAggregateErrorNode(
207
210
  id,
208
211
  current,
209
- options ? await this.parseProperties(options) : undefined,
212
+ options ? await this.parseProperties(options) : NIL,
210
213
  );
211
214
  }
212
215
 
@@ -239,20 +242,21 @@ export default abstract class BaseAsyncParserContext extends BaseParserContext {
239
242
  current: Promise<unknown>,
240
243
  ): Promise<SerovalPromiseNode> {
241
244
  const [status, result] = await promiseToResult(current);
242
- return {
243
- t: SerovalNodeType.Promise,
244
- i: id,
245
- s: status,
246
- l: undefined,
247
- c: undefined,
248
- m: undefined,
249
- p: undefined,
250
- e: undefined,
251
- a: undefined,
252
- f: await this.parse(result),
253
- b: undefined,
254
- o: undefined,
255
- };
245
+
246
+ return createSerovalNode(
247
+ SerovalNodeType.Promise,
248
+ id,
249
+ status,
250
+ NIL,
251
+ NIL,
252
+ NIL,
253
+ NIL,
254
+ NIL,
255
+ NIL,
256
+ await this.parse(result),
257
+ NIL,
258
+ NIL,
259
+ );
256
260
  }
257
261
 
258
262
  private async parsePlugin(
@@ -274,7 +278,7 @@ export default abstract class BaseAsyncParserContext extends BaseParserContext {
274
278
  }
275
279
  }
276
280
  }
277
- return undefined;
281
+ return NIL;
278
282
  }
279
283
 
280
284
  private async parseStream(
@@ -332,7 +336,37 @@ export default abstract class BaseAsyncParserContext extends BaseParserContext {
332
336
  );
333
337
  }
334
338
 
335
- // biome-ignore lint/complexity/noExcessiveCognitiveComplexity: <explanation>
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
+ // biome-ignore lint/complexity/noExcessiveCognitiveComplexity: ehh
336
370
  private async parseObject(id: number, current: object): Promise<SerovalNode> {
337
371
  if (Array.isArray(current)) {
338
372
  return this.parseArray(id, current);
@@ -357,7 +391,7 @@ export default abstract class BaseAsyncParserContext extends BaseParserContext {
357
391
  current as Record<string, unknown>,
358
392
  false,
359
393
  );
360
- case undefined:
394
+ case NIL:
361
395
  return this.parsePlainObject(
362
396
  id,
363
397
  current as Record<string, unknown>,
@@ -406,6 +440,13 @@ export default abstract class BaseAsyncParserContext extends BaseParserContext {
406
440
  return this.parsePromise(id, current as unknown as Promise<unknown>);
407
441
  }
408
442
  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
+ }
409
450
  // BigInt Typed Arrays
410
451
  if (currentFeatures & Feature.BigIntTypedArray) {
411
452
  switch (currentClass) {
@@ -8,13 +8,16 @@ import {
8
8
  createStreamThrowNode,
9
9
  createStringNode,
10
10
  } from '../../base-primitives';
11
- import { SerovalNodeType } from '../../constants';
11
+ import { NIL, SerovalNodeType } from '../../constants';
12
12
  import { FALSE_NODE, TRUE_NODE } from '../../literals';
13
+ import { createSerovalNode } from '../../node';
13
14
  import { SpecialReference } from '../../special-reference';
14
15
  import type { Stream } from '../../stream';
15
16
  import { createStreamFromAsyncIterable } from '../../stream';
16
17
  import { serializeString } from '../../string';
17
18
  import type {
19
+ SerovalAbortSignalConstructorNode,
20
+ SerovalAbortSignalSyncNode,
18
21
  SerovalNode,
19
22
  SerovalObjectRecordKey,
20
23
  SerovalObjectRecordNode,
@@ -164,23 +167,25 @@ export default abstract class BaseStreamParserContext extends BaseSyncParserCont
164
167
  data => {
165
168
  const parsed = this.parseWithError(data);
166
169
  if (parsed) {
167
- this.onParse({
168
- t: SerovalNodeType.PromiseResolve,
169
- i: id,
170
- s: undefined,
171
- l: undefined,
172
- c: undefined,
173
- m: undefined,
174
- p: undefined,
175
- e: undefined,
176
- a: [
177
- this.parseSpecialReference(SpecialReference.PromiseResolve),
178
- parsed,
179
- ],
180
- f: undefined,
181
- b: undefined,
182
- o: undefined,
183
- });
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
+ );
184
189
  }
185
190
  this.popPendingState();
186
191
  },
@@ -188,23 +193,25 @@ export default abstract class BaseStreamParserContext extends BaseSyncParserCont
188
193
  if (this.alive) {
189
194
  const parsed = this.parseWithError(data);
190
195
  if (parsed) {
191
- this.onParse({
192
- t: SerovalNodeType.PromiseReject,
193
- i: id,
194
- s: undefined,
195
- l: undefined,
196
- c: undefined,
197
- m: undefined,
198
- p: undefined,
199
- e: undefined,
200
- a: [
201
- this.parseSpecialReference(SpecialReference.PromiseReject),
202
- parsed,
203
- ],
204
- f: undefined,
205
- b: undefined,
206
- o: undefined,
207
- });
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
+ );
208
215
  }
209
216
  }
210
217
  this.popPendingState();
@@ -233,7 +240,7 @@ export default abstract class BaseStreamParserContext extends BaseSyncParserCont
233
240
  }
234
241
  }
235
242
  }
236
- return undefined;
243
+ return NIL;
237
244
  }
238
245
 
239
246
  protected parseStream(id: number, current: Stream<unknown>): SerovalNode {
@@ -274,12 +281,57 @@ export default abstract class BaseStreamParserContext extends BaseSyncParserCont
274
281
  return result;
275
282
  }
276
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
+
277
329
  private parseWithError<T>(current: T): SerovalNode | undefined {
278
330
  try {
279
331
  return this.parse(current);
280
332
  } catch (err) {
281
333
  this.onError(err);
282
- return undefined;
334
+ return NIL;
283
335
  }
284
336
  }
285
337
 
@@ -19,6 +19,7 @@ import {
19
19
  createTypedArrayNode,
20
20
  } from '../../base-primitives';
21
21
  import { Feature } from '../../compat';
22
+ import { NIL, SerovalNodeType } from '../../constants';
22
23
  import { SerovalParserError, SerovalUnsupportedTypeError } from '../../errors';
23
24
  import {
24
25
  FALSE_NODE,
@@ -26,12 +27,15 @@ import {
26
27
  TRUE_NODE,
27
28
  UNDEFINED_NODE,
28
29
  } from '../../literals';
30
+ import { createSerovalNode } from '../../node';
29
31
  import { OpaqueReference } from '../../opaque-reference';
30
32
  import { SpecialReference } from '../../special-reference';
31
33
  import type { Stream } from '../../stream';
32
34
  import { createStream, isStream } from '../../stream';
33
35
  import { serializeString } from '../../string';
34
36
  import type {
37
+ SerovalAbortSignalConstructorNode,
38
+ SerovalAbortSignalSyncNode,
35
39
  SerovalAggregateErrorNode,
36
40
  SerovalArrayNode,
37
41
  SerovalBigIntTypedArrayNode,
@@ -167,7 +171,7 @@ export default abstract class BaseSyncParserContext extends BaseParserContext {
167
171
  return createErrorNode(
168
172
  id,
169
173
  current,
170
- options ? this.parseProperties(options) : undefined,
174
+ options ? this.parseProperties(options) : NIL,
171
175
  );
172
176
  }
173
177
 
@@ -179,7 +183,7 @@ export default abstract class BaseSyncParserContext extends BaseParserContext {
179
183
  return createAggregateErrorNode(
180
184
  id,
181
185
  current,
182
- options ? this.parseProperties(options) : undefined,
186
+ options ? this.parseProperties(options) : NIL,
183
187
  );
184
188
  }
185
189
 
@@ -241,7 +245,37 @@ export default abstract class BaseSyncParserContext extends BaseParserContext {
241
245
  return this.createPromiseConstructorNode(id);
242
246
  }
243
247
 
244
- // biome-ignore lint/complexity/noExcessiveCognitiveComplexity: <explanation>
248
+ protected parseAbortSignalSync(
249
+ id: number,
250
+ current: AbortSignal,
251
+ ): SerovalAbortSignalSyncNode {
252
+ return createSerovalNode(
253
+ SerovalNodeType.AbortSignalSync,
254
+ id,
255
+ NIL,
256
+ NIL,
257
+ NIL,
258
+ NIL,
259
+ NIL,
260
+ NIL,
261
+ NIL,
262
+ this.parse(current.reason),
263
+ NIL,
264
+ NIL,
265
+ );
266
+ }
267
+
268
+ protected parseAbortSignal(
269
+ id: number,
270
+ current: AbortSignal,
271
+ ): SerovalAbortSignalConstructorNode | SerovalAbortSignalSyncNode {
272
+ if (current.aborted) {
273
+ return this.parseAbortSignalSync(id, current);
274
+ }
275
+ return this.createAbortSignalConstructorNode(id);
276
+ }
277
+
278
+ // biome-ignore lint/complexity/noExcessiveCognitiveComplexity: ehh
245
279
  protected parseObject(id: number, current: object): SerovalNode {
246
280
  if (Array.isArray(current)) {
247
281
  return this.parseArray(id, current);
@@ -315,6 +349,13 @@ export default abstract class BaseSyncParserContext extends BaseParserContext {
315
349
  return this.parsePromise(id, current as unknown as Promise<unknown>);
316
350
  }
317
351
  const currentFeatures = this.features;
352
+ if (
353
+ currentFeatures & Feature.AbortSignal &&
354
+ typeof AbortSignal !== 'undefined' &&
355
+ currentClass === AbortSignal
356
+ ) {
357
+ return this.parseAbortSignal(id, current as unknown as AbortSignal);
358
+ }
318
359
  // BigInt Typed Arrays
319
360
  if (currentFeatures & Feature.BigIntTypedArray) {
320
361
  switch (currentClass) {