resurrect-esm 2.0.2 → 2.0.3

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.
@@ -1,8 +1,5 @@
1
1
  name: Publish Package
2
- on:
3
- push:
4
- tags:
5
- - 'v*'
2
+ on: workflow_dispatch
6
3
  permissions:
7
4
  id-token: write # Required for OIDC
8
5
  contents: read
@@ -19,7 +16,7 @@ jobs:
19
16
  with:
20
17
  version: 10
21
18
  cache: on
22
- install: yes
19
+ - run: pnpm install
23
20
  - run: pnpm test
24
21
  - run: pnpm prepare
25
22
  - run: npm publish
@@ -1,6 +1,7 @@
1
1
  {
2
2
  "cSpell.words": [
3
3
  "kybernetikos",
4
- "registrator"
4
+ "registrator",
5
+ "skeeto"
5
6
  ]
6
7
  }
package/README.md CHANGED
@@ -9,6 +9,7 @@ An ES6 module port of ResurrectTS.
9
9
  > * ResurrectError is now a top-level export, instead of living inside a Resurrect object.
10
10
  > * NamespaceResolver is now a top-level export, instead of living inside the Resurrect static constructor namespace.
11
11
  > * NamespaceResolver now has a new method, `getConstructor(name: string)`, which should return the constructor function to generate the object (it will not be called directly, so the parameters are irrelevant).
12
+ > The bug found in skeeto/resurrect-js#11 has been fixed.
12
13
 
13
14
  ResurrectJS preserves object behavior (prototypes) and reference
14
15
  circularity with a special JSON encoding. Unlike flat JSON, it can
package/package.json CHANGED
@@ -1,42 +1,42 @@
1
1
  {
2
- "name": "resurrect-esm",
3
- "version": "2.0.2",
4
- "type": "module",
5
- "description": "ResurrectJS preserves object behavior (prototypes) and reference circularity with a special JSON encoding. Unlike flat JSON, it can also properly resurrect self-referential objects, objects with shared structure, Dates, RegExps, HTMLElements, NaN, Infinity, undefined (which won't get turned into null), and any other custom object type given the proper resolver.",
6
- "repository": {
7
- "type": "git",
8
- "url": "git+https://github.com/dragoncoder047/resurrect-esm.git"
9
- },
10
- "keywords": [],
11
- "author": "skeeto",
12
- "contributors": [
13
- {
14
- "name": "Andrew Bradley",
15
- "email": "cspotcode@gmail.com",
16
- "url": "https://github.com/cspotcode"
2
+ "name": "resurrect-esm",
3
+ "version": "2.0.3",
4
+ "type": "module",
5
+ "description": "ResurrectJS preserves object behavior (prototypes) and reference circularity with a special JSON encoding. Unlike flat JSON, it can also properly resurrect self-referential objects, objects with shared structure, Dates, RegExps, HTMLElements, NaN, Infinity, undefined (which won't get turned into null), and any other custom object type given the proper resolver.",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git+https://github.com/dragoncoder047/resurrect-esm.git"
17
9
  },
18
- {
19
- "name": "dragoncoder047",
20
- "url": "https://github.com/dragoncoder047"
10
+ "keywords": [],
11
+ "author": "skeeto",
12
+ "contributors": [
13
+ {
14
+ "name": "Andrew Bradley",
15
+ "email": "cspotcode@gmail.com",
16
+ "url": "https://github.com/cspotcode"
17
+ },
18
+ {
19
+ "name": "dragoncoder047",
20
+ "url": "https://github.com/dragoncoder047"
21
+ }
22
+ ],
23
+ "license": "UNLICENSE",
24
+ "bugs": {
25
+ "url": "https://github.com/dragoncoder047/resurrect-esm/issues"
26
+ },
27
+ "homepage": "https://github.com/dragoncoder047/resurrect-esm#readme",
28
+ "devDependencies": {
29
+ "@happy-dom/global-registrator": "^20.6.1",
30
+ "@types/bun": "^1.3.9",
31
+ "bun": "^1.3.9",
32
+ "esbuild": "^0.25.0",
33
+ "typescript": "^5.6.2"
34
+ },
35
+ "main": "resurrect.ts",
36
+ "scripts": {
37
+ "build": "pnpm esbuild --sourcemap --platform=browser --target=esnext --format=esm resurrect.ts --outfile=resurrect.js",
38
+ "test": "pnpm bun test",
39
+ "test:watch": "pnpm test --watch",
40
+ "prepare": "pnpm build --minify --mangle-props=^_"
21
41
  }
22
- ],
23
- "license": "UNLICENSE",
24
- "bugs": {
25
- "url": "https://github.com/dragoncoder047/resurrect-esm/issues"
26
- },
27
- "homepage": "https://github.com/dragoncoder047/resurrect-esm#readme",
28
- "devDependencies": {
29
- "@happy-dom/global-registrator": "^20.6.1",
30
- "@types/bun": "^1.3.9",
31
- "bun": "^1.3.9",
32
- "esbuild": "^0.25.0",
33
- "typescript": "^5.6.2"
34
- },
35
- "main": "resurrect.ts",
36
- "scripts": {
37
- "build": "pnpm esbuild --sourcemap --platform=browser --target=esnext --format=esm resurrect.ts --outfile=resurrect.js",
38
- "test": "pnpm bun test",
39
- "test:watch": "pnpm test --watch",
40
- "prepare": "pnpm build --minify --mangle-props=^_"
41
- }
42
42
  }
package/resurrect.test.ts CHANGED
@@ -107,3 +107,11 @@ function suite(opt?: ResurrectOptions) {
107
107
  describe("default options", () => suite());
108
108
  describe("custom prefix", () => suite({ prefix: "qwerty" }));
109
109
  describe("no revive", () => suite({ revive: false }));
110
+
111
+ test("malformed state bug", () => {
112
+ // Test for the bug found in skeeto/resurrect-js#11
113
+ const r = new Resurrect();
114
+ const O = { test() { } };
115
+ expect(() => r.stringify(O)).toThrow(new ResurrectError("Can't serialize functions."));
116
+ expect(() => r.stringify(O)).toThrow(new ResurrectError("Can't serialize functions."));
117
+ });
package/resurrect.ts CHANGED
@@ -83,6 +83,7 @@
83
83
 
84
84
  export class Resurrect {
85
85
  private _table: any[] | null;
86
+ private _cleanups: (() => void)[] = [];
86
87
  prefix: string;
87
88
  cleanup: boolean;
88
89
  revive: boolean;
@@ -180,10 +181,12 @@ export class Resurrect {
180
181
  throw new ResurrectError("Constructor mismatch!");
181
182
  } else {
182
183
  object[this._protocode] = constructor;
184
+ this._cleanups.push(() => { delete object[this._protocode as any]; })
183
185
  }
184
186
  }
185
187
  }
186
188
  object[this._refcode] = this._table!.length;
189
+ this._cleanups.push(() => { delete object[this._refcode as any]; })
187
190
  this._table!.push(object);
188
191
  return object[this._refcode];
189
192
  }
@@ -241,32 +244,30 @@ export class Resurrect {
241
244
  * Visit root and all its ancestors, visiting atoms with f.
242
245
  * @returns A fresh copy of root to be serialized.
243
246
  */
244
- private _visit(root: any, f: (obj: any) => any, replacer?: (k: string, v: any) => any): any {
247
+ private _visit(root: any, transform: (obj: any) => any, replacer?: (k: string, v: any) => any): any {
245
248
  if (Resurrect.isAtom(root)) {
246
- return f(root);
249
+ return transform(root);
247
250
  } else if (!this._isTagged(root)) {
248
251
  let copy: any = null;
249
252
  if (Resurrect._isArray(root)) {
250
253
  copy = [];
251
254
  root[this._refcode as any] = this._tag(copy);
255
+ this._cleanups.push(() => { delete root[this._refcode as any]; })
252
256
  for (let i = 0; i < root.length; i++) {
253
- copy.push(this._visit(root[i], f, replacer));
257
+ copy.push(this._visit(root[i], transform, replacer));
254
258
  }
255
259
  } else { /* Object */
256
260
  copy = Object.create(Object.getPrototypeOf(root));
257
261
  root[this._refcode as any] = this._tag(copy);
258
- for (const key in root) {
262
+ this._cleanups.push(() => { delete root[this._refcode as any]; })
263
+ for (const key of Object.getOwnPropertyNames(root)) {
259
264
  let value = root[key];
260
- if (root.hasOwnProperty(key)) {
261
- if (replacer && value !== undefined) {
262
- // Call replacer like JSON.stringify's replacer
263
- value = replacer.call(root, key, root[key]);
264
- if (value === undefined) {
265
- continue; // Omit from result
266
- }
267
- }
268
- copy[key] = this._visit(value, f, replacer);
265
+ if (replacer && value !== undefined) {
266
+ // Call replacer like JSON.stringify's replacer
267
+ value = replacer.call(root, key, root[key]);
268
+ if (value === undefined) continue; // Omit from result
269
269
  }
270
+ copy[key] = this._visit(value, transform, replacer);
270
271
  }
271
272
  }
272
273
  copy[this._origcode] = root;
@@ -321,26 +322,31 @@ export class Resurrect {
321
322
  replacer = this._replacerWrapper(replacer);
322
323
  } else if (Resurrect._isArray(replacer)) {
323
324
  const acceptKeys = replacer;
324
- replacer = function (k, v) {
325
- return acceptKeys.indexOf(k) >= 0 ? v : undefined;
326
- };
325
+ replacer = (k, v) => acceptKeys.includes(k) ? v : undefined;
327
326
  }
328
327
  if (Resurrect.isAtom(object)) {
329
328
  return JSON.stringify(this._handleAtom(object), replacer, space);
330
329
  } else {
331
- this._table = [];
332
- this._visit(object, this._handleAtom.bind(this), replacer);
333
- for (let i = 0; i < this._table.length; i++) {
334
- if (this.cleanup) {
335
- delete this._table[i][this._origcode][this._refcode];
336
- } else {
337
- this._table[i][this._origcode][this._refcode] = null;
330
+ this._cleanups = [];
331
+ const table = this._table = [] as any[];
332
+ try {
333
+ this._visit(object, this._handleAtom.bind(this), replacer);
334
+ } catch (e) {
335
+ this._cleanups.forEach(e => e());
336
+ throw e;
337
+ } finally {
338
+ for (let i = 0; i < table.length; i++) {
339
+ if (this.cleanup) {
340
+ delete table[i][this._origcode][this._refcode];
341
+ } else {
342
+ const obj = table[i][this._origcode];
343
+ if (obj) obj[this._refcode] = null;
344
+ }
345
+ delete table[i][this._refcode];
346
+ delete table[i][this._origcode];
338
347
  }
339
- delete this._table[i][this._refcode];
340
- delete this._table[i][this._origcode];
348
+ this._table = null;
341
349
  }
342
- const table = this._table;
343
- this._table = null;
344
350
  return JSON.stringify(table, null, space);
345
351
  }
346
352
  }
@@ -359,18 +365,17 @@ export class Resurrect {
359
365
  delete (object as any)[this._protocode];
360
366
  }
361
367
  return object;
362
- } else { // IE
363
- const copy = Object.create(prototype);
364
- for (const key in object) {
365
- if (object.hasOwnProperty(key) && key !== this.prefix) {
366
- copy[key] = object[key];
367
- }
368
+ }
369
+ // IE
370
+ const copy = Object.create(prototype);
371
+ for (const key of Object.getOwnPropertyNames(object)) {
372
+ if (key !== this._protocode) {
373
+ copy[key] = (object as any)[key];
368
374
  }
369
- return copy;
370
375
  }
371
- } else {
372
- return object;
376
+ return copy;
373
377
  }
378
+ return object;
374
379
  }
375
380
 
376
381
  /**
@@ -379,33 +384,34 @@ export class Resurrect {
379
384
  resurrect(string: string): any {
380
385
  let result = null;
381
386
  const data = JSON.parse(string);
382
- if (Resurrect._isArray(data)) {
383
- this._table = data;
384
- /* Restore __proto__. */
385
- if (this.revive) {
386
- for (let i = 0; i < this._table.length; i++) {
387
- this._table[i] = this._fixPrototype(this._table[i]);
387
+ try {
388
+ if (Resurrect._isArray(data)) {
389
+ this._table = data;
390
+ /* Restore __proto__. */
391
+ if (this.revive) {
392
+ for (let i = 0; i < this._table.length; i++) {
393
+ this._table[i] = this._fixPrototype(this._table[i]);
394
+ }
388
395
  }
389
- }
390
- /* Re-establish object references and construct atoms. */
391
- for (let i = 0; i < this._table.length; i++) {
392
- const object = this._table[i];
393
- for (const key in object) {
394
- if (object.hasOwnProperty(key)) {
396
+ /* Re-establish object references and construct atoms. */
397
+ for (let i = 0; i < this._table.length; i++) {
398
+ const object = this._table[i];
399
+ for (const key of Object.getOwnPropertyNames(object)) {
395
400
  if (!(Resurrect.isAtom(object[key]))) {
396
401
  object[key] = this._decode(object[key]);
397
402
  }
398
403
  }
399
404
  }
405
+ result = this._table[0];
406
+ } else if (Resurrect._isObject(data)) {
407
+ this._table = [];
408
+ result = this._decode(data);
409
+ } else {
410
+ result = data;
400
411
  }
401
- result = this._table[0];
402
- } else if (Resurrect._isObject(data)) {
403
- this._table = [];
404
- result = this._decode(data);
405
- } else {
406
- result = data;
412
+ } finally {
413
+ this._table = null;
407
414
  }
408
- this._table = null;
409
415
  return result;
410
416
  }
411
417
  }