seroval 1.0.2 → 1.0.4

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 (34) hide show
  1. package/dist/cjs/development/index.cjs +339 -226
  2. package/dist/cjs/development/index.cjs.map +3 -3
  3. package/dist/cjs/production/index.cjs +17 -3
  4. package/dist/esm/development/index.mjs +339 -226
  5. package/dist/esm/development/index.mjs.map +3 -3
  6. package/dist/esm/production/index.mjs +17 -3
  7. package/dist/types/core/base-primitives.d.ts.map +1 -1
  8. package/dist/types/core/context/deserializer.d.ts.map +1 -1
  9. package/dist/types/core/context/parser/async.d.ts.map +1 -1
  10. package/dist/types/core/context/parser/sync.d.ts.map +1 -1
  11. package/dist/types/core/context/parser.d.ts.map +1 -1
  12. package/dist/types/core/context/serializer.d.ts.map +1 -1
  13. package/dist/types/core/errors.d.ts +38 -0
  14. package/dist/types/core/errors.d.ts.map +1 -0
  15. package/dist/types/core/reference.d.ts.map +1 -1
  16. package/dist/types/core/tree/serializer.d.ts.map +1 -1
  17. package/dist/types/core/utils/typed-array.d.ts.map +1 -1
  18. package/dist/types/index.d.ts +1 -0
  19. package/dist/types/index.d.ts.map +1 -1
  20. package/package.json +2 -2
  21. package/src/core/base-primitives.ts +0 -5
  22. package/src/core/context/deserializer.ts +86 -76
  23. package/src/core/context/parser/async.ts +30 -26
  24. package/src/core/context/parser/sync.ts +30 -26
  25. package/src/core/context/parser.ts +3 -10
  26. package/src/core/context/serializer.ts +113 -80
  27. package/src/core/errors.ts +99 -0
  28. package/src/core/reference.ts +6 -2
  29. package/src/core/tree/serializer.ts +4 -3
  30. package/src/core/utils/typed-array.ts +3 -1
  31. package/src/index.ts +1 -0
  32. package/dist/types/core/UnsupportedTypeError.d.ts +0 -5
  33. package/dist/types/core/UnsupportedTypeError.d.ts.map +0 -1
  34. package/src/core/UnsupportedTypeError.ts +0 -7
@@ -6,6 +6,11 @@ import {
6
6
  SerovalNodeType,
7
7
  SerovalObjectFlags,
8
8
  } from '../constants';
9
+ import {
10
+ SerovalMissingPluginError,
11
+ SerovalSerializationError,
12
+ SerovalUnsupportedNodeError,
13
+ } from '../errors';
9
14
  import { REFERENCES_KEY } from '../keys';
10
15
  import type { Plugin, PluginAccessOptions, SerovalMode } from '../plugin';
11
16
  import { SpecialReference } from '../special-reference';
@@ -446,12 +451,24 @@ export default abstract class BaseSerializerContext
446
451
  ): string {
447
452
  if (typeof key === 'string') {
448
453
  const check = Number(key);
449
- // Test if key is a valid number or JS identifier
450
- // so that we don't have to serialize the key and wrap with brackets
451
- const isIdentifier = check >= 0 || isValidIdentifier(key);
454
+ const isIdentifier =
455
+ // Test if key is a valid positive number or JS identifier
456
+ // so that we don't have to serialize the key and wrap with brackets
457
+ (check >= 0 &&
458
+ // It's also important to consider that if the key is
459
+ // indeed numeric, we need to make sure that when
460
+ // converted back into a string, it's still the same
461
+ // to the original key. This allows us to differentiate
462
+ // keys that has numeric formats but in a different
463
+ // format, which can cause unintentional key declaration
464
+ // Example: { 0x1: 1 } vs { '0x1': 1 }
465
+ check.toString() === key) ||
466
+ isValidIdentifier(key);
452
467
  if (this.isIndexedValueInStack(val)) {
453
468
  const refParam = this.getRefParam((val as SerovalIndexedValueNode).i);
454
469
  this.markRef(source.i);
470
+ // Strict identifier check, make sure
471
+ // that it isn't numeric (except NaN)
455
472
  if (isIdentifier && check !== check) {
456
473
  this.createObjectAssign(source.i, key, refParam);
457
474
  } else {
@@ -516,10 +533,22 @@ export default abstract class BaseSerializerContext
516
533
  ): void {
517
534
  const serialized = this.serialize(value);
518
535
  const check = Number(key);
519
- // Test if key is a valid number or JS identifier
520
- // so that we don't have to serialize the key and wrap with brackets
521
- const isIdentifier = check >= 0 || isValidIdentifier(key);
536
+ const isIdentifier =
537
+ // Test if key is a valid positive number or JS identifier
538
+ // so that we don't have to serialize the key and wrap with brackets
539
+ (check >= 0 &&
540
+ // It's also important to consider that if the key is
541
+ // indeed numeric, we need to make sure that when
542
+ // converted back into a string, it's still the same
543
+ // to the original key. This allows us to differentiate
544
+ // keys that has numeric formats but in a different
545
+ // format, which can cause unintentional key declaration
546
+ // Example: { 0x1: 1 } vs { '0x1': 1 }
547
+ check.toString() === key) ||
548
+ isValidIdentifier(key);
522
549
  if (this.isIndexedValueInStack(value)) {
550
+ // Strict identifier check, make sure
551
+ // that it isn't numeric (except NaN)
523
552
  if (isIdentifier && check !== check) {
524
553
  this.createObjectAssign(source.i, key, serialized);
525
554
  } else {
@@ -532,7 +561,7 @@ export default abstract class BaseSerializerContext
532
561
  } else {
533
562
  const parentAssignment = this.assignments;
534
563
  this.assignments = mainAssignments;
535
- if (isIdentifier) {
564
+ if (isIdentifier && check !== check) {
536
565
  this.createObjectAssign(source.i, key, serialized);
537
566
  } else {
538
567
  this.createArrayAssign(
@@ -875,7 +904,7 @@ export default abstract class BaseSerializerContext
875
904
  }
876
905
  }
877
906
  }
878
- throw new Error('Missing plugin for tag "' + node.c + '".');
907
+ throw new SerovalMissingPluginError(node.c);
879
908
  }
880
909
 
881
910
  private getConstructor(node: SerovalNodeWithID): string {
@@ -1131,78 +1160,82 @@ export default abstract class BaseSerializerContext
1131
1160
  }
1132
1161
 
1133
1162
  serialize(node: SerovalNode): string {
1134
- switch (node.t) {
1135
- case SerovalNodeType.Constant:
1136
- return CONSTANT_STRING[node.s];
1137
- case SerovalNodeType.Number:
1138
- return '' + node.s;
1139
- case SerovalNodeType.String:
1140
- return '"' + node.s + '"';
1141
- case SerovalNodeType.BigInt:
1142
- return node.s + 'n';
1143
- case SerovalNodeType.IndexedValue:
1144
- return this.getRefParam(node.i);
1145
- case SerovalNodeType.Reference:
1146
- return this.serializeReference(node);
1147
- case SerovalNodeType.Array:
1148
- return this.serializeArray(node);
1149
- case SerovalNodeType.Object:
1150
- return this.serializeObject(node);
1151
- case SerovalNodeType.NullConstructor:
1152
- return this.serializeNullConstructor(node);
1153
- case SerovalNodeType.Date:
1154
- return this.serializeDate(node);
1155
- case SerovalNodeType.RegExp:
1156
- return this.serializeRegExp(node);
1157
- case SerovalNodeType.Set:
1158
- return this.serializeSet(node);
1159
- case SerovalNodeType.Map:
1160
- return this.serializeMap(node);
1161
- case SerovalNodeType.ArrayBuffer:
1162
- return this.serializeArrayBuffer(node);
1163
- case SerovalNodeType.BigIntTypedArray:
1164
- case SerovalNodeType.TypedArray:
1165
- return this.serializeTypedArray(node);
1166
- case SerovalNodeType.DataView:
1167
- return this.serializeDataView(node);
1168
- case SerovalNodeType.AggregateError:
1169
- return this.serializeAggregateError(node);
1170
- case SerovalNodeType.Error:
1171
- return this.serializeError(node);
1172
- case SerovalNodeType.Promise:
1173
- return this.serializePromise(node);
1174
- case SerovalNodeType.WKSymbol:
1175
- return this.serializeWellKnownSymbol(node);
1176
- case SerovalNodeType.Boxed:
1177
- return this.serializeBoxed(node);
1178
- case SerovalNodeType.PromiseConstructor:
1179
- return this.serializePromiseConstructor(node);
1180
- case SerovalNodeType.PromiseResolve:
1181
- return this.serializePromiseResolve(node);
1182
- case SerovalNodeType.PromiseReject:
1183
- return this.serializePromiseReject(node);
1184
- case SerovalNodeType.Plugin:
1185
- return this.serializePlugin(node);
1186
- case SerovalNodeType.SpecialReference:
1187
- return this.serializeSpecialReference(node);
1188
- case SerovalNodeType.IteratorFactory:
1189
- return this.serializeIteratorFactory(node);
1190
- case SerovalNodeType.IteratorFactoryInstance:
1191
- return this.serializeIteratorFactoryInstance(node);
1192
- case SerovalNodeType.AsyncIteratorFactory:
1193
- return this.serializeAsyncIteratorFactory(node);
1194
- case SerovalNodeType.AsyncIteratorFactoryInstance:
1195
- return this.serializeAsyncIteratorFactoryInstance(node);
1196
- case SerovalNodeType.StreamConstructor:
1197
- return this.serializeStreamConstructor(node);
1198
- case SerovalNodeType.StreamNext:
1199
- return this.serializeStreamNext(node);
1200
- case SerovalNodeType.StreamThrow:
1201
- return this.serializeStreamThrow(node);
1202
- case SerovalNodeType.StreamReturn:
1203
- return this.serializeStreamReturn(node);
1204
- default:
1205
- throw new Error('invariant');
1163
+ try {
1164
+ switch (node.t) {
1165
+ case SerovalNodeType.Constant:
1166
+ return CONSTANT_STRING[node.s];
1167
+ case SerovalNodeType.Number:
1168
+ return '' + node.s;
1169
+ case SerovalNodeType.String:
1170
+ return '"' + node.s + '"';
1171
+ case SerovalNodeType.BigInt:
1172
+ return node.s + 'n';
1173
+ case SerovalNodeType.IndexedValue:
1174
+ return this.getRefParam(node.i);
1175
+ case SerovalNodeType.Reference:
1176
+ return this.serializeReference(node);
1177
+ case SerovalNodeType.Array:
1178
+ return this.serializeArray(node);
1179
+ case SerovalNodeType.Object:
1180
+ return this.serializeObject(node);
1181
+ case SerovalNodeType.NullConstructor:
1182
+ return this.serializeNullConstructor(node);
1183
+ case SerovalNodeType.Date:
1184
+ return this.serializeDate(node);
1185
+ case SerovalNodeType.RegExp:
1186
+ return this.serializeRegExp(node);
1187
+ case SerovalNodeType.Set:
1188
+ return this.serializeSet(node);
1189
+ case SerovalNodeType.Map:
1190
+ return this.serializeMap(node);
1191
+ case SerovalNodeType.ArrayBuffer:
1192
+ return this.serializeArrayBuffer(node);
1193
+ case SerovalNodeType.BigIntTypedArray:
1194
+ case SerovalNodeType.TypedArray:
1195
+ return this.serializeTypedArray(node);
1196
+ case SerovalNodeType.DataView:
1197
+ return this.serializeDataView(node);
1198
+ case SerovalNodeType.AggregateError:
1199
+ return this.serializeAggregateError(node);
1200
+ case SerovalNodeType.Error:
1201
+ return this.serializeError(node);
1202
+ case SerovalNodeType.Promise:
1203
+ return this.serializePromise(node);
1204
+ case SerovalNodeType.WKSymbol:
1205
+ return this.serializeWellKnownSymbol(node);
1206
+ case SerovalNodeType.Boxed:
1207
+ return this.serializeBoxed(node);
1208
+ case SerovalNodeType.PromiseConstructor:
1209
+ return this.serializePromiseConstructor(node);
1210
+ case SerovalNodeType.PromiseResolve:
1211
+ return this.serializePromiseResolve(node);
1212
+ case SerovalNodeType.PromiseReject:
1213
+ return this.serializePromiseReject(node);
1214
+ case SerovalNodeType.Plugin:
1215
+ return this.serializePlugin(node);
1216
+ case SerovalNodeType.SpecialReference:
1217
+ return this.serializeSpecialReference(node);
1218
+ case SerovalNodeType.IteratorFactory:
1219
+ return this.serializeIteratorFactory(node);
1220
+ case SerovalNodeType.IteratorFactoryInstance:
1221
+ return this.serializeIteratorFactoryInstance(node);
1222
+ case SerovalNodeType.AsyncIteratorFactory:
1223
+ return this.serializeAsyncIteratorFactory(node);
1224
+ case SerovalNodeType.AsyncIteratorFactoryInstance:
1225
+ return this.serializeAsyncIteratorFactoryInstance(node);
1226
+ case SerovalNodeType.StreamConstructor:
1227
+ return this.serializeStreamConstructor(node);
1228
+ case SerovalNodeType.StreamNext:
1229
+ return this.serializeStreamNext(node);
1230
+ case SerovalNodeType.StreamThrow:
1231
+ return this.serializeStreamThrow(node);
1232
+ case SerovalNodeType.StreamReturn:
1233
+ return this.serializeStreamReturn(node);
1234
+ default:
1235
+ throw new SerovalUnsupportedNodeError(node);
1236
+ }
1237
+ } catch (error) {
1238
+ throw new SerovalSerializationError(error);
1206
1239
  }
1207
1240
  }
1208
1241
  }
@@ -0,0 +1,99 @@
1
+ import { serializeString } from './string';
2
+ import { SerovalNode } from './types';
3
+
4
+ const { toString: objectToString } = /* @__PURE__ */ Object.prototype;
5
+
6
+ function getErrorMessage(type: string, cause: any): string {
7
+ if (cause instanceof Error) {
8
+ return `Seroval caught an error during the ${type} process.
9
+
10
+ ${cause.name}
11
+ ${cause.message}
12
+
13
+ - For more information, please check the "cause" property of this error.
14
+ - If you believe this is an error in Seroval, please submit an issue at https://github.com/lxsmnsyc/seroval/issues/new`;
15
+ }
16
+ return `Seroval caught an error during the ${type} process.
17
+
18
+ "${objectToString.call(cause)}"
19
+
20
+ For more information, please check the "cause" property of this error.`;
21
+ }
22
+
23
+ export class SerovalError extends Error {
24
+ constructor(type: string, public cause: any) {
25
+ super(getErrorMessage(type, cause));
26
+ }
27
+ }
28
+
29
+ export class SerovalParserError extends SerovalError {
30
+ constructor(cause: any) {
31
+ super('parsing', cause);
32
+ }
33
+ }
34
+
35
+ export class SerovalSerializationError extends SerovalError {
36
+ constructor(cause: any) {
37
+ super('serialization', cause);
38
+ }
39
+ }
40
+
41
+ export class SerovalDeserializationError extends SerovalError {
42
+ constructor(cause: any) {
43
+ super('deserialization', cause);
44
+ }
45
+ }
46
+
47
+ export class SerovalUnsupportedTypeError extends Error {
48
+ constructor(public value: unknown) {
49
+ super(
50
+ `The value ${objectToString.call(value)} of type "${typeof value}" cannot be parsed/serialized.
51
+
52
+ There are few workarounds for this problem:
53
+ - Transform the value in a way that it can be serialized.
54
+ - If the reference is present on multiple runtimes (isomorphic), you can use the Reference API to map the references.`,
55
+ );
56
+ }
57
+ }
58
+
59
+ export class SerovalUnsupportedNodeError extends Error {
60
+ constructor(node: SerovalNode) {
61
+ super('Unsupported node type "' + node.t + '".');
62
+ }
63
+ }
64
+
65
+ export class SerovalMissingPluginError extends Error {
66
+ constructor(tag: string) {
67
+ super('Missing plugin for tag "' + tag + '".');
68
+ }
69
+ }
70
+
71
+ export class SerovalMissingInstanceError extends Error {
72
+ constructor(tag: string) {
73
+ super('Missing "' + tag + '" instance.');
74
+ }
75
+ }
76
+
77
+ export class SerovalMissingReferenceError extends Error {
78
+ constructor(public value: unknown) {
79
+ super(
80
+ 'Missing reference for the value "' +
81
+ objectToString.call(value) +
82
+ '" of type "' +
83
+ typeof value +
84
+ '"',
85
+ );
86
+ }
87
+ }
88
+
89
+ export class SerovalMissingReferenceForIdError extends Error {
90
+ constructor(id: string) {
91
+ super('Missing reference for id "' + serializeString(id) + '"');
92
+ }
93
+ }
94
+
95
+ export class SerovalUnknownTypedArrayError extends Error {
96
+ constructor(name: string) {
97
+ super('Unknown TypedArray "' + name + '"');
98
+ }
99
+ }
@@ -1,5 +1,9 @@
1
1
  import assert from './utils/assert';
2
2
  import { REFERENCES_KEY } from './keys';
3
+ import {
4
+ SerovalMissingReferenceError,
5
+ SerovalMissingReferenceForIdError,
6
+ } from '..';
3
7
 
4
8
  const REFERENCE = new Map<unknown, string>();
5
9
  const INV_REFERENCE = new Map<string, unknown>();
@@ -19,12 +23,12 @@ export function hasReference(id: string): boolean {
19
23
  }
20
24
 
21
25
  export function getReferenceID<T>(value: T): string {
22
- assert(hasReferenceID(value), new Error('Missing reference id'));
26
+ assert(hasReferenceID(value), new SerovalMissingReferenceError(value));
23
27
  return REFERENCE.get(value)!;
24
28
  }
25
29
 
26
30
  export function getReference<T>(id: string): T {
27
- assert(hasReference(id), new Error('Missing reference for id:' + id));
31
+ assert(hasReference(id), new SerovalMissingReferenceForIdError(id));
28
32
  return INV_REFERENCE.get(id) as T;
29
33
  }
30
34
 
@@ -9,6 +9,7 @@ import BaseSerializerContext from '../context/serializer';
9
9
  import getIdentifier from '../utils/get-identifier';
10
10
  import type { SerovalMode } from '../plugin';
11
11
  import { SerovalNodeType } from '../constants';
12
+ import { SerovalUnsupportedNodeError } from '../errors';
12
13
 
13
14
  export type VanillaSerializerContextOptions = BaseSerializerContextOptions;
14
15
 
@@ -61,15 +62,15 @@ export default class VanillaSerializerContext extends BaseSerializerContext {
61
62
  protected serializePromiseConstructor(
62
63
  node: SerovalPromiseConstructorNode,
63
64
  ): string {
64
- throw new Error('Unsupported node type "' + node.t + '".');
65
+ throw new SerovalUnsupportedNodeError(node);
65
66
  }
66
67
 
67
68
  protected serializePromiseResolve(node: SerovalPromiseResolveNode): string {
68
- throw new Error('Unsupported node type "' + node.t + '".');
69
+ throw new SerovalUnsupportedNodeError(node);
69
70
  }
70
71
 
71
72
  protected serializePromiseReject(node: SerovalPromiseRejectNode): string {
72
- throw new Error('Unsupported node type "' + node.t + '".');
73
+ throw new SerovalUnsupportedNodeError(node);
73
74
  }
74
75
 
75
76
  serializeTop(tree: SerovalNode): string {
@@ -1,3 +1,5 @@
1
+ import { SerovalUnknownTypedArrayError } from '../errors';
2
+
1
3
  type TypedArrayConstructor =
2
4
  | Int8ArrayConstructor
3
5
  | Int16ArrayConstructor
@@ -49,6 +51,6 @@ export function getTypedArrayConstructor(name: string): TypedArrayConstructor {
49
51
  case 'BigUint64Array':
50
52
  return BigUint64Array;
51
53
  default:
52
- throw new Error(`Unknown TypedArray "${name}"`);
54
+ throw new SerovalUnknownTypedArrayError(name);
53
55
  }
54
56
  }
package/src/index.ts CHANGED
@@ -13,3 +13,4 @@ export type { Stream } from './core/stream';
13
13
  export { createStream } from './core/stream';
14
14
 
15
15
  export type { SerovalNode } from './core/types';
16
+ export * from './core/errors';
@@ -1,5 +0,0 @@
1
- export default class UnsupportedTypeError extends Error {
2
- value: unknown;
3
- constructor(value: unknown);
4
- }
5
- //# sourceMappingURL=UnsupportedTypeError.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"UnsupportedTypeError.d.ts","sourceRoot":"","sources":["../../../src/core/UnsupportedTypeError.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,OAAO,OAAO,oBAAqB,SAAQ,KAAK;IAClC,KAAK,EAAE,OAAO;gBAAd,KAAK,EAAE,OAAO;CAGlC"}
@@ -1,7 +0,0 @@
1
- const { toString: objectToString } = /* @__PURE__ */ Object.prototype;
2
-
3
- export default class UnsupportedTypeError extends Error {
4
- constructor(public value: unknown) {
5
- super('Unsupported type "' + objectToString.call(value) + '"');
6
- }
7
- }