seriall 1.0.0 → 1.1.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/dist/index.cjs CHANGED
@@ -27,9 +27,11 @@ __export(index_exports, {
27
27
  module.exports = __toCommonJS(index_exports);
28
28
 
29
29
  // src/const/const.ts
30
- var SIGNATURE_KEY = "$";
31
- var DATA_KEY = "d";
30
+ var SIGNATURE_INDEX = 0;
31
+ var DATA_INDEX = 1;
32
32
  var NO_TRANSFORM_DATA = void 0;
33
+ var PROTOCOL_VERSION = 1;
34
+ var LIB = "seriall";
33
35
  var PRIORITY = {
34
36
  CUSTOM_CLASS: 0,
35
37
  NATIVE_CLASS: 1,
@@ -42,13 +44,13 @@ var isSpecialNumber = (value) => {
42
44
  return [Infinity, -Infinity, NaN].includes(value) || Object.is(value, -0);
43
45
  };
44
46
  var encodeSpecialNumber = (value) => {
45
- if (Number.isNaN(value)) return 0;
46
- if (value === Infinity) return 1;
47
- if (value === -Infinity) return 2;
48
- if (Object.is(value, -0)) return 3;
47
+ if (Number.isNaN(value)) return [0];
48
+ if (value === Infinity) return [1];
49
+ if (value === -Infinity) return [2];
50
+ if (Object.is(value, -0)) return [3];
49
51
  throw new Error(value + " is not a special number");
50
52
  };
51
- var decodeSpecialNumber = (value) => {
53
+ var decodeSpecialNumber = ([value]) => {
52
54
  switch (value) {
53
55
  case 0:
54
56
  return NaN;
@@ -68,7 +70,7 @@ var isJsonPrimitive = (value) => {
68
70
  if (typeof value === "number") {
69
71
  return !isSpecialNumber(value);
70
72
  }
71
- return typeof value === "string" || typeof value === "boolean";
73
+ return typeof value === "string" || typeof value === "boolean" || value === null;
72
74
  };
73
75
 
74
76
  // src/utils/object.utils.ts
@@ -96,27 +98,30 @@ var serialize = (data, transformers) => {
96
98
  const graph = [];
97
99
  const seen = /* @__PURE__ */ new Map();
98
100
  const addNodeToGraph = (node) => {
99
- if (isJsonPrimitive(node)) {
100
- const id2 = graph.length;
101
- graph.push(node);
102
- return id2;
103
- }
104
101
  let id = seen.get(node);
105
- if (id !== void 0) {
102
+ if (id !== void 0 && !Object.is(node, -0)) {
106
103
  return id;
107
104
  }
108
105
  id = graph.length;
109
106
  seen.set(node, id);
107
+ if (isJsonPrimitive(node)) {
108
+ const id2 = graph.length;
109
+ graph.push(node);
110
+ return id2;
111
+ }
110
112
  const transformer = transformers.find(
111
113
  (transformer2) => transformer2.match(node)
112
114
  );
113
115
  if (transformer) {
114
- graph.push({ $: transformer.id });
116
+ graph.push([]);
115
117
  const revivable = graph[id];
118
+ revivable[SIGNATURE_INDEX] = transformer.id;
116
119
  const encoded = transformer.encode(node);
117
120
  if (encoded !== NO_TRANSFORM_DATA) {
118
- const dataId = addNodeToGraph(encoded);
119
- revivable.d = dataId;
121
+ encoded.forEach((el, index) => {
122
+ const dataId = addNodeToGraph(el);
123
+ revivable[DATA_INDEX + index] = dataId;
124
+ });
120
125
  }
121
126
  return id;
122
127
  }
@@ -129,21 +134,49 @@ var serialize = (data, transformers) => {
129
134
  }
130
135
  return id;
131
136
  }
137
+ if (typeof node === "object") {
138
+ graph.push({});
139
+ const obj = graph[id];
140
+ for (const key in node) {
141
+ const elementId = addNodeToGraph(node[key]);
142
+ obj[key] = elementId;
143
+ }
144
+ return id;
145
+ }
132
146
  throw new Error(
133
147
  `No transformer found for the current value "${node?.toString()}", please add one`
134
148
  );
135
149
  };
136
150
  addNodeToGraph(data);
137
- return JSON.stringify(graph);
151
+ const serialized = {
152
+ lib: LIB,
153
+ v: PROTOCOL_VERSION,
154
+ d: graph
155
+ };
156
+ return JSON.stringify(serialized);
138
157
  };
139
158
 
140
159
  // src/serialization/deserialize.ts
141
160
  var deserialize = (data, transformers) => {
142
- const graph = JSON.parse(data);
143
- const revived = /* @__PURE__ */ new Map();
161
+ const { lib, v, d } = JSON.parse(data);
162
+ if (lib !== LIB) {
163
+ throw new Error(
164
+ `Can't deserialize data, invalid lib metadata. Expected "${LIB}", got "${lib}"`
165
+ );
166
+ }
167
+ if (v !== PROTOCOL_VERSION) {
168
+ throw new Error(
169
+ `Can't deserialize data, invalid protocol version. Expected ${PROTOCOL_VERSION}, got ${v}`
170
+ );
171
+ }
172
+ if (!Array.isArray(d)) {
173
+ throw new Error("Invalid seriall payload: 'd' must be an array");
174
+ }
175
+ const graph = d;
176
+ const revived = [];
144
177
  const reviveNode = (index) => {
145
- if (revived.has(index)) {
146
- return revived.get(index);
178
+ if (index in revived) {
179
+ return revived[index];
147
180
  }
148
181
  if (graph.length <= index || index < 0) {
149
182
  throw new Error(`Invalid serialized graph reference: ${index}`);
@@ -152,37 +185,54 @@ var deserialize = (data, transformers) => {
152
185
  if (isJsonPrimitive(node)) {
153
186
  return node;
154
187
  }
155
- if (Array.isArray(node)) {
188
+ if (!Array.isArray(node) && typeof node === "object") {
189
+ const obj = {};
190
+ revived[index] = obj;
191
+ for (const key of Object.keys(node)) {
192
+ obj[key] = reviveNode(node[key]);
193
+ }
194
+ return obj;
195
+ }
196
+ if (typeof node[SIGNATURE_INDEX] !== "string") {
197
+ const typedNode2 = node;
156
198
  const arr = [];
157
- revived.set(index, arr);
158
- for (const element of node) {
199
+ revived[index] = arr;
200
+ for (const element of typedNode2) {
159
201
  arr.push(reviveNode(element));
160
202
  }
161
203
  return arr;
162
204
  }
163
- const transformerId = node[SIGNATURE_KEY];
164
- const transformer = transformers.get(transformerId);
205
+ const typedNode = node;
206
+ const transformerId = typedNode[SIGNATURE_INDEX];
207
+ const transformer = transformers[transformerId];
165
208
  if (!transformerId) {
166
- throw new Error(`No transformer found for node "${node}"`);
209
+ throw new Error(`No transformer found for node "${typedNode}"`);
167
210
  }
168
211
  if (transformer === void 0) {
169
212
  throw new Error(`No transformer found with id "${transformerId}"`);
170
213
  }
171
214
  const nonRecursiveTransformer = transformer;
172
- const dataId = node[DATA_KEY];
173
- if (dataId === void 0) {
215
+ if (!transformer.recursive && typedNode[DATA_INDEX] === void 0) {
174
216
  const decoded = nonRecursiveTransformer.decode(NO_TRANSFORM_DATA);
175
- revived.set(index, decoded);
217
+ revived[index] = decoded;
176
218
  return decoded;
177
219
  }
178
220
  if (!transformer.recursive) {
179
- const decoded = nonRecursiveTransformer.decode(reviveNode(dataId));
180
- revived.set(index, decoded);
221
+ const revivedValues = new Array(typedNode.length - 1);
222
+ for (let i = 1; i < typedNode.length; i++) {
223
+ revivedValues[i - 1] = reviveNode(typedNode[i]);
224
+ }
225
+ const decoded = nonRecursiveTransformer.decode(revivedValues);
226
+ revived[index] = decoded;
181
227
  return decoded;
182
228
  }
183
229
  const registerNode = (node2) => {
184
- revived.set(index, node2);
185
- return reviveNode(dataId);
230
+ revived[index] = node2;
231
+ const revivedValues = new Array(typedNode.length - 1);
232
+ for (let i = 1; i < typedNode.length; i++) {
233
+ revivedValues[i - 1] = reviveNode(typedNode[i]);
234
+ }
235
+ return revivedValues;
186
236
  };
187
237
  return transformer.decode(registerNode);
188
238
  };
@@ -221,15 +271,15 @@ var primitivesTransformers = [
221
271
  id: "sym",
222
272
  priority: Transformer.PRIORITY.PRIMITIVE,
223
273
  match: (node) => typeof node === "symbol",
224
- encode: (node) => ({
225
- d: node.description,
226
- f: Symbol.keyFor(node) === void 0 ? 0 : 1
227
- }),
274
+ encode: (node) => [
275
+ node.description,
276
+ Symbol.keyFor(node) === void 0 ? 0 : 1
277
+ ],
228
278
  decode: (encoded) => {
229
- if (encoded.f === 1 && encoded.d) {
279
+ if (encoded[1] === 1) {
230
280
  return Symbol.for(encoded.d);
231
281
  }
232
- return Symbol(encoded.d);
282
+ return Symbol(encoded[0]);
233
283
  }
234
284
  }),
235
285
  new Transformer({
@@ -246,49 +296,45 @@ var primitivesTransformers = [
246
296
  encode: () => NO_TRANSFORM_DATA,
247
297
  decode: () => void 0
248
298
  }),
249
- new Transformer({
250
- id: "nul",
251
- priority: Transformer.PRIORITY.PRIMITIVE,
252
- match: (node) => node === null,
253
- encode: () => NO_TRANSFORM_DATA,
254
- decode: () => null
255
- }),
256
299
  new Transformer({
257
300
  id: "big",
258
301
  priority: Transformer.PRIORITY.PRIMITIVE,
259
302
  match: (node) => typeof node === "bigint",
260
- encode: (node) => node.toString(),
261
- decode: (node) => BigInt(node)
262
- }),
263
- new Transformer({
264
- id: "obj",
265
- priority: Transformer.PRIORITY.PRIMITIVE,
266
- recursive: true,
267
- match: (node) => !Array.isArray(node) && typeof node === "object",
268
- encode: (node) => {
269
- const obj = [];
270
- for (const key of Reflect.ownKeys(node)) {
303
+ encode: (node) => [node.toString()],
304
+ decode: ([node]) => BigInt(node)
305
+ })
306
+ ];
307
+ var objectSymbolIndexingTransformer = new Transformer({
308
+ id: "obj",
309
+ priority: Transformer.PRIORITY.PRIMITIVE,
310
+ recursive: true,
311
+ match: (node) => !Array.isArray(node) && typeof node === "object",
312
+ encode: (node) => {
313
+ const obj = [];
314
+ for (const key of Reflect.ownKeys(node)) {
315
+ const value = node[key];
316
+ if (typeof value !== "function") {
271
317
  obj.push([key, node[key]]);
272
318
  }
273
- return obj;
274
- },
275
- decode: (registerNode) => {
276
- const obj = {};
277
- const encoded = registerNode(obj);
278
- for (const [key, value] of encoded) {
279
- obj[key] = value;
280
- }
281
- return obj;
282
319
  }
283
- })
284
- ];
320
+ return obj;
321
+ },
322
+ decode: (registerNode) => {
323
+ const obj = {};
324
+ const encoded = registerNode(obj);
325
+ for (const [key, value] of encoded) {
326
+ obj[key] = value;
327
+ }
328
+ return obj;
329
+ }
330
+ });
285
331
  var nativeClassesTransformers = [
286
332
  new Transformer({
287
333
  id: "Dte",
288
334
  priority: Transformer.PRIORITY.NATIVE_CLASS,
289
335
  match: (node) => node instanceof Date,
290
- encode: (node) => node.getTime(),
291
- decode: (node) => new Date(node)
336
+ encode: (node) => [node.getTime()],
337
+ decode: ([node]) => new Date(node)
292
338
  }),
293
339
  new Transformer({
294
340
  id: "Set",
@@ -324,29 +370,29 @@ var nativeClassesTransformers = [
324
370
  id: "Rgx",
325
371
  priority: Transformer.PRIORITY.NATIVE_CLASS,
326
372
  match: (node) => node instanceof RegExp,
327
- encode: (node) => ({ s: node.source, f: node.flags }),
328
- decode: (node) => new RegExp(node.s, node.f)
373
+ encode: (node) => [node.source, node.flags],
374
+ decode: (node) => new RegExp(node[0], node[1])
329
375
  }),
330
376
  new Transformer({
331
377
  id: "Str",
332
378
  priority: Transformer.PRIORITY.NATIVE_CLASS,
333
379
  match: (node) => node instanceof String,
334
- encode: (node) => node.valueOf(),
335
- decode: (node) => new String(node)
380
+ encode: (node) => [node.valueOf()],
381
+ decode: ([node]) => new String(node)
336
382
  }),
337
383
  new Transformer({
338
384
  id: "Num",
339
385
  priority: Transformer.PRIORITY.NATIVE_CLASS,
340
386
  match: (node) => node instanceof Number,
341
- encode: (node) => node.valueOf(),
342
- decode: (node) => new Number(node)
387
+ encode: (node) => [node.valueOf()],
388
+ decode: ([node]) => new Number(node)
343
389
  }),
344
390
  new Transformer({
345
391
  id: "Bol",
346
392
  priority: Transformer.PRIORITY.NATIVE_CLASS,
347
393
  match: (node) => node instanceof Boolean,
348
- encode: (node) => node.valueOf(),
349
- decode: (node) => new Boolean(node)
394
+ encode: (node) => [node.valueOf()],
395
+ decode: ([node]) => new Boolean(node)
350
396
  })
351
397
  ];
352
398
 
@@ -357,20 +403,21 @@ var SYMBOLS = { ENCODE, DECODE };
357
403
  var SerializableClass = class {
358
404
  constructor(..._) {
359
405
  }
360
- [ENCODE] = () => {
361
- const encoded = {};
362
- for (const key of Reflect.ownKeys(this)) {
406
+ [ENCODE]() {
407
+ const encoded = [];
408
+ for (const key of Object.keys(this)) {
363
409
  const value = this[key];
364
410
  if (typeof value !== "function") {
365
- encoded[key] = this[key];
411
+ encoded.push(this[key]);
366
412
  }
367
413
  }
368
414
  return encoded;
369
- };
415
+ }
370
416
  static [DECODE] = function(registerNode) {
371
417
  let revivedInstance = {};
372
418
  const args = registerNode(revivedInstance);
373
- Object.assign(revivedInstance, args);
419
+ const keys = Reflect.ownKeys(new this({}));
420
+ keys.forEach((key, index) => revivedInstance[key] = args[index]);
374
421
  Object.setPrototypeOf(revivedInstance, this.prototype);
375
422
  return revivedInstance;
376
423
  };
@@ -389,23 +436,30 @@ var defaultOptions = {
389
436
  enable: {
390
437
  builtinPrimitiveTransformers: true,
391
438
  builtinNativeClasses: true,
392
- builtinClassTransformer: true
393
- }
439
+ objectSymbolIndexing: false
440
+ },
441
+ classes: {}
394
442
  };
395
443
  var Serializer = class {
396
444
  transformers = [];
397
- transformersMap = /* @__PURE__ */ new Map();
445
+ transformersRecord = {};
398
446
  constructor(options = {}) {
399
447
  const opt = deepMerge(defaultOptions, options);
400
448
  if (opt.enable.builtinPrimitiveTransformers)
401
449
  primitivesTransformers.forEach(this.registerTransformer);
402
450
  if (opt.enable.builtinNativeClasses)
403
451
  nativeClassesTransformers.forEach(this.registerTransformer);
452
+ if (opt.enable.objectSymbolIndexing) {
453
+ this.registerTransformer(objectSymbolIndexingTransformer);
454
+ }
455
+ for (const className in opt.classes) {
456
+ this.registerClass(className, opt.classes[className]);
457
+ }
404
458
  }
405
459
  serialize = (data) => serialize(data, this.transformers);
406
- deserialize = (data) => deserialize(data, this.transformersMap);
460
+ deserialize = (data) => deserialize(data, this.transformersRecord);
407
461
  registerTransformer = (transformer) => {
408
- if (this.transformersMap.has(transformer.id)) {
462
+ if (transformer.id in this.transformersRecord) {
409
463
  throw new Error(
410
464
  `A transformer with id "${transformer.id}" is already registered, chose another one`
411
465
  );
@@ -414,11 +468,11 @@ var Serializer = class {
414
468
  this.transformers = this.transformers.sort(
415
469
  (a, b) => (a.priority ?? Infinity) - (b.priority ?? Infinity)
416
470
  );
417
- this.transformersMap.set(transformer.id, transformer);
471
+ this.transformersRecord[transformer.id] = transformer;
418
472
  };
419
473
  deregisterTransformer = (transformer) => {
420
474
  const id = typeof transformer === "object" ? transformer.id : transformer;
421
- if (!this.transformersMap.has(id)) {
475
+ if (!(id in this.transformersRecord)) {
422
476
  throw new Error(
423
477
  `No transformer found for id "${id}", cannot deregister it`
424
478
  );
@@ -426,7 +480,7 @@ var Serializer = class {
426
480
  this.transformers = this.transformers.filter(
427
481
  (transformer2) => transformer2.id !== id
428
482
  );
429
- this.transformersMap.delete(id);
483
+ delete this.transformersRecord[id];
430
484
  };
431
485
  registerClass = (name, clazz) => {
432
486
  this.registerTransformer(createClassTransformer("$" + name, clazz));
package/dist/index.d.cts CHANGED
@@ -8,22 +8,34 @@ declare namespace Seriall {
8
8
  | object
9
9
  | Serializable[];
10
10
 
11
- export type JsonPrimitive = string | number | boolean;
11
+ export type JsonPrimitive = string | number | boolean | null;
12
+
13
+ export type MetaData = {
14
+ lib: "seriall";
15
+ v: 1;
16
+ };
12
17
 
13
18
  export namespace Serialized {
14
- export type RevivableNode = { $: Transformer.Id; d?: number };
15
- export type Node = JsonPrimitive | number[] | RevivableNode;
19
+ export type NodeId = number;
20
+ export type TransformedNode = [Transformer.Id, NodeId];
21
+ export type Node =
22
+ | JsonPrimitive
23
+ | NodeId[]
24
+ | TransformedNode
25
+ | Record<string | number, NodeId>;
16
26
  export type Graph = Node[];
17
27
  }
18
28
 
29
+ export type Serialized = MetaData & { d: Seriall.Serialized.Graph };
30
+
19
31
  namespace Transformer {
20
32
  /** `NoData` is used to signify that no value should be passed to the Revivable node,
21
33
  * which is then only described by its tag ($)*/
22
34
  type NoData = undefined;
23
35
 
24
36
  type Encoded<Recursive extends boolean> = Recursive extends false
25
- ? JsonPrimitive | object | Encoded<false>[] | NoData
26
- : (JsonPrimitive | Encoded<true>)[] | object;
37
+ ? (JsonPrimitive | object | Encoded<false>)[] | NoData
38
+ : (JsonPrimitive | object | Encoded<true>)[];
27
39
 
28
40
  type Id = string | number;
29
41
 
@@ -91,7 +103,7 @@ declare const SYMBOLS: {
91
103
  };
92
104
  declare abstract class SerializableClass {
93
105
  constructor(..._: any);
94
- [ENCODE]: () => {};
106
+ [ENCODE](): any;
95
107
  static [DECODE]: <T extends typeof SerializableClass>(this: T, registerNode: Parameters<Seriall.Transformer.Decoder<any, any, true>>[0]) => InstanceType<T>;
96
108
  }
97
109
 
@@ -99,12 +111,13 @@ type SerializerOptions = {
99
111
  enable: {
100
112
  builtinPrimitiveTransformers: boolean;
101
113
  builtinNativeClasses: boolean;
102
- builtinClassTransformer: boolean;
114
+ objectSymbolIndexing: boolean;
103
115
  };
116
+ classes: Record<Seriall.Transformer.Id, typeof SerializableClass>;
104
117
  };
105
118
  declare class Serializer {
106
119
  private transformers;
107
- private transformersMap;
120
+ private transformersRecord;
108
121
  constructor(options?: DeepOptional<SerializerOptions>);
109
122
  serialize: (data: Seriall.Serializable) => string;
110
123
  deserialize: (data: string) => any;
package/dist/index.d.mts CHANGED
@@ -8,22 +8,34 @@ declare namespace Seriall {
8
8
  | object
9
9
  | Serializable[];
10
10
 
11
- export type JsonPrimitive = string | number | boolean;
11
+ export type JsonPrimitive = string | number | boolean | null;
12
+
13
+ export type MetaData = {
14
+ lib: "seriall";
15
+ v: 1;
16
+ };
12
17
 
13
18
  export namespace Serialized {
14
- export type RevivableNode = { $: Transformer.Id; d?: number };
15
- export type Node = JsonPrimitive | number[] | RevivableNode;
19
+ export type NodeId = number;
20
+ export type TransformedNode = [Transformer.Id, NodeId];
21
+ export type Node =
22
+ | JsonPrimitive
23
+ | NodeId[]
24
+ | TransformedNode
25
+ | Record<string | number, NodeId>;
16
26
  export type Graph = Node[];
17
27
  }
18
28
 
29
+ export type Serialized = MetaData & { d: Seriall.Serialized.Graph };
30
+
19
31
  namespace Transformer {
20
32
  /** `NoData` is used to signify that no value should be passed to the Revivable node,
21
33
  * which is then only described by its tag ($)*/
22
34
  type NoData = undefined;
23
35
 
24
36
  type Encoded<Recursive extends boolean> = Recursive extends false
25
- ? JsonPrimitive | object | Encoded<false>[] | NoData
26
- : (JsonPrimitive | Encoded<true>)[] | object;
37
+ ? (JsonPrimitive | object | Encoded<false>)[] | NoData
38
+ : (JsonPrimitive | object | Encoded<true>)[];
27
39
 
28
40
  type Id = string | number;
29
41
 
@@ -91,7 +103,7 @@ declare const SYMBOLS: {
91
103
  };
92
104
  declare abstract class SerializableClass {
93
105
  constructor(..._: any);
94
- [ENCODE]: () => {};
106
+ [ENCODE](): any;
95
107
  static [DECODE]: <T extends typeof SerializableClass>(this: T, registerNode: Parameters<Seriall.Transformer.Decoder<any, any, true>>[0]) => InstanceType<T>;
96
108
  }
97
109
 
@@ -99,12 +111,13 @@ type SerializerOptions = {
99
111
  enable: {
100
112
  builtinPrimitiveTransformers: boolean;
101
113
  builtinNativeClasses: boolean;
102
- builtinClassTransformer: boolean;
114
+ objectSymbolIndexing: boolean;
103
115
  };
116
+ classes: Record<Seriall.Transformer.Id, typeof SerializableClass>;
104
117
  };
105
118
  declare class Serializer {
106
119
  private transformers;
107
- private transformersMap;
120
+ private transformersRecord;
108
121
  constructor(options?: DeepOptional<SerializerOptions>);
109
122
  serialize: (data: Seriall.Serializable) => string;
110
123
  deserialize: (data: string) => any;