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.mjs CHANGED
@@ -1,7 +1,9 @@
1
1
  // src/const/const.ts
2
- var SIGNATURE_KEY = "$";
3
- var DATA_KEY = "d";
2
+ var SIGNATURE_INDEX = 0;
3
+ var DATA_INDEX = 1;
4
4
  var NO_TRANSFORM_DATA = void 0;
5
+ var PROTOCOL_VERSION = 1;
6
+ var LIB = "seriall";
5
7
  var PRIORITY = {
6
8
  CUSTOM_CLASS: 0,
7
9
  NATIVE_CLASS: 1,
@@ -14,13 +16,13 @@ var isSpecialNumber = (value) => {
14
16
  return [Infinity, -Infinity, NaN].includes(value) || Object.is(value, -0);
15
17
  };
16
18
  var encodeSpecialNumber = (value) => {
17
- if (Number.isNaN(value)) return 0;
18
- if (value === Infinity) return 1;
19
- if (value === -Infinity) return 2;
20
- if (Object.is(value, -0)) return 3;
19
+ if (Number.isNaN(value)) return [0];
20
+ if (value === Infinity) return [1];
21
+ if (value === -Infinity) return [2];
22
+ if (Object.is(value, -0)) return [3];
21
23
  throw new Error(value + " is not a special number");
22
24
  };
23
- var decodeSpecialNumber = (value) => {
25
+ var decodeSpecialNumber = ([value]) => {
24
26
  switch (value) {
25
27
  case 0:
26
28
  return NaN;
@@ -40,7 +42,7 @@ var isJsonPrimitive = (value) => {
40
42
  if (typeof value === "number") {
41
43
  return !isSpecialNumber(value);
42
44
  }
43
- return typeof value === "string" || typeof value === "boolean";
45
+ return typeof value === "string" || typeof value === "boolean" || value === null;
44
46
  };
45
47
 
46
48
  // src/utils/object.utils.ts
@@ -68,27 +70,30 @@ var serialize = (data, transformers) => {
68
70
  const graph = [];
69
71
  const seen = /* @__PURE__ */ new Map();
70
72
  const addNodeToGraph = (node) => {
71
- if (isJsonPrimitive(node)) {
72
- const id2 = graph.length;
73
- graph.push(node);
74
- return id2;
75
- }
76
73
  let id = seen.get(node);
77
- if (id !== void 0) {
74
+ if (id !== void 0 && !Object.is(node, -0)) {
78
75
  return id;
79
76
  }
80
77
  id = graph.length;
81
78
  seen.set(node, id);
79
+ if (isJsonPrimitive(node)) {
80
+ const id2 = graph.length;
81
+ graph.push(node);
82
+ return id2;
83
+ }
82
84
  const transformer = transformers.find(
83
85
  (transformer2) => transformer2.match(node)
84
86
  );
85
87
  if (transformer) {
86
- graph.push({ $: transformer.id });
88
+ graph.push([]);
87
89
  const revivable = graph[id];
90
+ revivable[SIGNATURE_INDEX] = transformer.id;
88
91
  const encoded = transformer.encode(node);
89
92
  if (encoded !== NO_TRANSFORM_DATA) {
90
- const dataId = addNodeToGraph(encoded);
91
- revivable.d = dataId;
93
+ encoded.forEach((el, index) => {
94
+ const dataId = addNodeToGraph(el);
95
+ revivable[DATA_INDEX + index] = dataId;
96
+ });
92
97
  }
93
98
  return id;
94
99
  }
@@ -101,21 +106,49 @@ var serialize = (data, transformers) => {
101
106
  }
102
107
  return id;
103
108
  }
109
+ if (typeof node === "object") {
110
+ graph.push({});
111
+ const obj = graph[id];
112
+ for (const key in node) {
113
+ const elementId = addNodeToGraph(node[key]);
114
+ obj[key] = elementId;
115
+ }
116
+ return id;
117
+ }
104
118
  throw new Error(
105
119
  `No transformer found for the current value "${node?.toString()}", please add one`
106
120
  );
107
121
  };
108
122
  addNodeToGraph(data);
109
- return JSON.stringify(graph);
123
+ const serialized = {
124
+ lib: LIB,
125
+ v: PROTOCOL_VERSION,
126
+ d: graph
127
+ };
128
+ return JSON.stringify(serialized);
110
129
  };
111
130
 
112
131
  // src/serialization/deserialize.ts
113
132
  var deserialize = (data, transformers) => {
114
- const graph = JSON.parse(data);
115
- const revived = /* @__PURE__ */ new Map();
133
+ const { lib, v, d } = JSON.parse(data);
134
+ if (lib !== LIB) {
135
+ throw new Error(
136
+ `Can't deserialize data, invalid lib metadata. Expected "${LIB}", got "${lib}"`
137
+ );
138
+ }
139
+ if (v !== PROTOCOL_VERSION) {
140
+ throw new Error(
141
+ `Can't deserialize data, invalid protocol version. Expected ${PROTOCOL_VERSION}, got ${v}`
142
+ );
143
+ }
144
+ if (!Array.isArray(d)) {
145
+ throw new Error("Invalid seriall payload: 'd' must be an array");
146
+ }
147
+ const graph = d;
148
+ const revived = [];
116
149
  const reviveNode = (index) => {
117
- if (revived.has(index)) {
118
- return revived.get(index);
150
+ if (index in revived) {
151
+ return revived[index];
119
152
  }
120
153
  if (graph.length <= index || index < 0) {
121
154
  throw new Error(`Invalid serialized graph reference: ${index}`);
@@ -124,37 +157,54 @@ var deserialize = (data, transformers) => {
124
157
  if (isJsonPrimitive(node)) {
125
158
  return node;
126
159
  }
127
- if (Array.isArray(node)) {
160
+ if (!Array.isArray(node) && typeof node === "object") {
161
+ const obj = {};
162
+ revived[index] = obj;
163
+ for (const key of Object.keys(node)) {
164
+ obj[key] = reviveNode(node[key]);
165
+ }
166
+ return obj;
167
+ }
168
+ if (typeof node[SIGNATURE_INDEX] !== "string") {
169
+ const typedNode2 = node;
128
170
  const arr = [];
129
- revived.set(index, arr);
130
- for (const element of node) {
171
+ revived[index] = arr;
172
+ for (const element of typedNode2) {
131
173
  arr.push(reviveNode(element));
132
174
  }
133
175
  return arr;
134
176
  }
135
- const transformerId = node[SIGNATURE_KEY];
136
- const transformer = transformers.get(transformerId);
177
+ const typedNode = node;
178
+ const transformerId = typedNode[SIGNATURE_INDEX];
179
+ const transformer = transformers[transformerId];
137
180
  if (!transformerId) {
138
- throw new Error(`No transformer found for node "${node}"`);
181
+ throw new Error(`No transformer found for node "${typedNode}"`);
139
182
  }
140
183
  if (transformer === void 0) {
141
184
  throw new Error(`No transformer found with id "${transformerId}"`);
142
185
  }
143
186
  const nonRecursiveTransformer = transformer;
144
- const dataId = node[DATA_KEY];
145
- if (dataId === void 0) {
187
+ if (!transformer.recursive && typedNode[DATA_INDEX] === void 0) {
146
188
  const decoded = nonRecursiveTransformer.decode(NO_TRANSFORM_DATA);
147
- revived.set(index, decoded);
189
+ revived[index] = decoded;
148
190
  return decoded;
149
191
  }
150
192
  if (!transformer.recursive) {
151
- const decoded = nonRecursiveTransformer.decode(reviveNode(dataId));
152
- revived.set(index, decoded);
193
+ const revivedValues = new Array(typedNode.length - 1);
194
+ for (let i = 1; i < typedNode.length; i++) {
195
+ revivedValues[i - 1] = reviveNode(typedNode[i]);
196
+ }
197
+ const decoded = nonRecursiveTransformer.decode(revivedValues);
198
+ revived[index] = decoded;
153
199
  return decoded;
154
200
  }
155
201
  const registerNode = (node2) => {
156
- revived.set(index, node2);
157
- return reviveNode(dataId);
202
+ revived[index] = node2;
203
+ const revivedValues = new Array(typedNode.length - 1);
204
+ for (let i = 1; i < typedNode.length; i++) {
205
+ revivedValues[i - 1] = reviveNode(typedNode[i]);
206
+ }
207
+ return revivedValues;
158
208
  };
159
209
  return transformer.decode(registerNode);
160
210
  };
@@ -193,15 +243,15 @@ var primitivesTransformers = [
193
243
  id: "sym",
194
244
  priority: Transformer.PRIORITY.PRIMITIVE,
195
245
  match: (node) => typeof node === "symbol",
196
- encode: (node) => ({
197
- d: node.description,
198
- f: Symbol.keyFor(node) === void 0 ? 0 : 1
199
- }),
246
+ encode: (node) => [
247
+ node.description,
248
+ Symbol.keyFor(node) === void 0 ? 0 : 1
249
+ ],
200
250
  decode: (encoded) => {
201
- if (encoded.f === 1 && encoded.d) {
251
+ if (encoded[1] === 1) {
202
252
  return Symbol.for(encoded.d);
203
253
  }
204
- return Symbol(encoded.d);
254
+ return Symbol(encoded[0]);
205
255
  }
206
256
  }),
207
257
  new Transformer({
@@ -218,49 +268,45 @@ var primitivesTransformers = [
218
268
  encode: () => NO_TRANSFORM_DATA,
219
269
  decode: () => void 0
220
270
  }),
221
- new Transformer({
222
- id: "nul",
223
- priority: Transformer.PRIORITY.PRIMITIVE,
224
- match: (node) => node === null,
225
- encode: () => NO_TRANSFORM_DATA,
226
- decode: () => null
227
- }),
228
271
  new Transformer({
229
272
  id: "big",
230
273
  priority: Transformer.PRIORITY.PRIMITIVE,
231
274
  match: (node) => typeof node === "bigint",
232
- encode: (node) => node.toString(),
233
- decode: (node) => BigInt(node)
234
- }),
235
- new Transformer({
236
- id: "obj",
237
- priority: Transformer.PRIORITY.PRIMITIVE,
238
- recursive: true,
239
- match: (node) => !Array.isArray(node) && typeof node === "object",
240
- encode: (node) => {
241
- const obj = [];
242
- for (const key of Reflect.ownKeys(node)) {
275
+ encode: (node) => [node.toString()],
276
+ decode: ([node]) => BigInt(node)
277
+ })
278
+ ];
279
+ var objectSymbolIndexingTransformer = new Transformer({
280
+ id: "obj",
281
+ priority: Transformer.PRIORITY.PRIMITIVE,
282
+ recursive: true,
283
+ match: (node) => !Array.isArray(node) && typeof node === "object",
284
+ encode: (node) => {
285
+ const obj = [];
286
+ for (const key of Reflect.ownKeys(node)) {
287
+ const value = node[key];
288
+ if (typeof value !== "function") {
243
289
  obj.push([key, node[key]]);
244
290
  }
245
- return obj;
246
- },
247
- decode: (registerNode) => {
248
- const obj = {};
249
- const encoded = registerNode(obj);
250
- for (const [key, value] of encoded) {
251
- obj[key] = value;
252
- }
253
- return obj;
254
291
  }
255
- })
256
- ];
292
+ return obj;
293
+ },
294
+ decode: (registerNode) => {
295
+ const obj = {};
296
+ const encoded = registerNode(obj);
297
+ for (const [key, value] of encoded) {
298
+ obj[key] = value;
299
+ }
300
+ return obj;
301
+ }
302
+ });
257
303
  var nativeClassesTransformers = [
258
304
  new Transformer({
259
305
  id: "Dte",
260
306
  priority: Transformer.PRIORITY.NATIVE_CLASS,
261
307
  match: (node) => node instanceof Date,
262
- encode: (node) => node.getTime(),
263
- decode: (node) => new Date(node)
308
+ encode: (node) => [node.getTime()],
309
+ decode: ([node]) => new Date(node)
264
310
  }),
265
311
  new Transformer({
266
312
  id: "Set",
@@ -296,29 +342,29 @@ var nativeClassesTransformers = [
296
342
  id: "Rgx",
297
343
  priority: Transformer.PRIORITY.NATIVE_CLASS,
298
344
  match: (node) => node instanceof RegExp,
299
- encode: (node) => ({ s: node.source, f: node.flags }),
300
- decode: (node) => new RegExp(node.s, node.f)
345
+ encode: (node) => [node.source, node.flags],
346
+ decode: (node) => new RegExp(node[0], node[1])
301
347
  }),
302
348
  new Transformer({
303
349
  id: "Str",
304
350
  priority: Transformer.PRIORITY.NATIVE_CLASS,
305
351
  match: (node) => node instanceof String,
306
- encode: (node) => node.valueOf(),
307
- decode: (node) => new String(node)
352
+ encode: (node) => [node.valueOf()],
353
+ decode: ([node]) => new String(node)
308
354
  }),
309
355
  new Transformer({
310
356
  id: "Num",
311
357
  priority: Transformer.PRIORITY.NATIVE_CLASS,
312
358
  match: (node) => node instanceof Number,
313
- encode: (node) => node.valueOf(),
314
- decode: (node) => new Number(node)
359
+ encode: (node) => [node.valueOf()],
360
+ decode: ([node]) => new Number(node)
315
361
  }),
316
362
  new Transformer({
317
363
  id: "Bol",
318
364
  priority: Transformer.PRIORITY.NATIVE_CLASS,
319
365
  match: (node) => node instanceof Boolean,
320
- encode: (node) => node.valueOf(),
321
- decode: (node) => new Boolean(node)
366
+ encode: (node) => [node.valueOf()],
367
+ decode: ([node]) => new Boolean(node)
322
368
  })
323
369
  ];
324
370
 
@@ -329,20 +375,21 @@ var SYMBOLS = { ENCODE, DECODE };
329
375
  var SerializableClass = class {
330
376
  constructor(..._) {
331
377
  }
332
- [ENCODE] = () => {
333
- const encoded = {};
334
- for (const key of Reflect.ownKeys(this)) {
378
+ [ENCODE]() {
379
+ const encoded = [];
380
+ for (const key of Object.keys(this)) {
335
381
  const value = this[key];
336
382
  if (typeof value !== "function") {
337
- encoded[key] = this[key];
383
+ encoded.push(this[key]);
338
384
  }
339
385
  }
340
386
  return encoded;
341
- };
387
+ }
342
388
  static [DECODE] = function(registerNode) {
343
389
  let revivedInstance = {};
344
390
  const args = registerNode(revivedInstance);
345
- Object.assign(revivedInstance, args);
391
+ const keys = Reflect.ownKeys(new this({}));
392
+ keys.forEach((key, index) => revivedInstance[key] = args[index]);
346
393
  Object.setPrototypeOf(revivedInstance, this.prototype);
347
394
  return revivedInstance;
348
395
  };
@@ -361,23 +408,30 @@ var defaultOptions = {
361
408
  enable: {
362
409
  builtinPrimitiveTransformers: true,
363
410
  builtinNativeClasses: true,
364
- builtinClassTransformer: true
365
- }
411
+ objectSymbolIndexing: false
412
+ },
413
+ classes: {}
366
414
  };
367
415
  var Serializer = class {
368
416
  transformers = [];
369
- transformersMap = /* @__PURE__ */ new Map();
417
+ transformersRecord = {};
370
418
  constructor(options = {}) {
371
419
  const opt = deepMerge(defaultOptions, options);
372
420
  if (opt.enable.builtinPrimitiveTransformers)
373
421
  primitivesTransformers.forEach(this.registerTransformer);
374
422
  if (opt.enable.builtinNativeClasses)
375
423
  nativeClassesTransformers.forEach(this.registerTransformer);
424
+ if (opt.enable.objectSymbolIndexing) {
425
+ this.registerTransformer(objectSymbolIndexingTransformer);
426
+ }
427
+ for (const className in opt.classes) {
428
+ this.registerClass(className, opt.classes[className]);
429
+ }
376
430
  }
377
431
  serialize = (data) => serialize(data, this.transformers);
378
- deserialize = (data) => deserialize(data, this.transformersMap);
432
+ deserialize = (data) => deserialize(data, this.transformersRecord);
379
433
  registerTransformer = (transformer) => {
380
- if (this.transformersMap.has(transformer.id)) {
434
+ if (transformer.id in this.transformersRecord) {
381
435
  throw new Error(
382
436
  `A transformer with id "${transformer.id}" is already registered, chose another one`
383
437
  );
@@ -386,11 +440,11 @@ var Serializer = class {
386
440
  this.transformers = this.transformers.sort(
387
441
  (a, b) => (a.priority ?? Infinity) - (b.priority ?? Infinity)
388
442
  );
389
- this.transformersMap.set(transformer.id, transformer);
443
+ this.transformersRecord[transformer.id] = transformer;
390
444
  };
391
445
  deregisterTransformer = (transformer) => {
392
446
  const id = typeof transformer === "object" ? transformer.id : transformer;
393
- if (!this.transformersMap.has(id)) {
447
+ if (!(id in this.transformersRecord)) {
394
448
  throw new Error(
395
449
  `No transformer found for id "${id}", cannot deregister it`
396
450
  );
@@ -398,7 +452,7 @@ var Serializer = class {
398
452
  this.transformers = this.transformers.filter(
399
453
  (transformer2) => transformer2.id !== id
400
454
  );
401
- this.transformersMap.delete(id);
455
+ delete this.transformersRecord[id];
402
456
  };
403
457
  registerClass = (name, clazz) => {
404
458
  this.registerTransformer(createClassTransformer("$" + name, clazz));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "seriall",
3
- "version": "1.0.0",
3
+ "version": "1.1.0",
4
4
  "description": "A flexible js serializer, capable of serializing/deserializing any js data, while preserving referential integrity, non-json-primitive values, and even custom class instances",
5
5
  "author": {
6
6
  "email": "loic.duvail.pro@gmail.com",
@@ -25,7 +25,9 @@
25
25
  "build:js": "node ./esbuild.js",
26
26
  "build:types": "rollup -c",
27
27
  "build:test": "node ./esbuild.test.js",
28
- "test": "npm run build:test && npx jest"
28
+ "build:benchmark": "node ./esbuild.benchmark.js",
29
+ "test": "npm run build:test && npx jest",
30
+ "bench": "npm run build:benchmark && node benchmark/benchmark.js /"
29
31
  },
30
32
  "keywords": [
31
33
  "serializer",
@@ -43,10 +45,13 @@
43
45
  "devDependencies": {
44
46
  "@types/jest": "^30.0.0",
45
47
  "@types/node": "^26.5.1",
48
+ "devalue": "^6.0.2",
46
49
  "esbuild": "^0.28.2",
50
+ "flatted": "^3.4.4",
47
51
  "jest": "^30.5.1",
48
52
  "rollup": "^4.63.6",
49
53
  "rollup-plugin-dts": "^6.5.1",
54
+ "superjson": "^2.2.6",
50
55
  "typescript": "^6.0.3"
51
56
  }
52
57
  }