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.
- package/.github/workflows/publish.yml +2 -5
- package/.vscode/settings.json +2 -1
- package/README.md +1 -0
- package/package.json +38 -38
- package/resurrect.test.ts +8 -0
- package/resurrect.ts +61 -55
|
@@ -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
|
-
|
|
19
|
+
- run: pnpm install
|
|
23
20
|
- run: pnpm test
|
|
24
21
|
- run: pnpm prepare
|
|
25
22
|
- run: npm publish
|
package/.vscode/settings.json
CHANGED
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
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
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
|
-
|
|
20
|
-
|
|
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,
|
|
247
|
+
private _visit(root: any, transform: (obj: any) => any, replacer?: (k: string, v: any) => any): any {
|
|
245
248
|
if (Resurrect.isAtom(root)) {
|
|
246
|
-
return
|
|
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],
|
|
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
|
-
|
|
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 (
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
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 =
|
|
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.
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
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
|
-
|
|
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
|
-
}
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
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
|
-
|
|
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
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
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
|
-
|
|
391
|
-
|
|
392
|
-
|
|
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
|
-
|
|
402
|
-
|
|
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
|
}
|