serialize-function 1.0.16 → 1.2.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/README.md +4 -4
- package/dist/main.js +1 -1
- package/lib/main.js +1 -1
- package/package.json +9 -10
package/README.md
CHANGED
|
@@ -4,10 +4,10 @@
|
|
|
4
4
|

|
|
5
5
|
](https://github.com/EvanK/npm-serialize-function/actions/workflows/ci.yaml)
|
|
6
6
|
[
|
|
7
|
-

|
|
8
|
+
](https://nodejs.org/en/about/previous-releases)
|
|
9
9
|
[
|
|
10
|
-

|
|
11
11
|
](https://compat-table.github.io/compat-table/es2016plus/)
|
|
12
12
|
|
|
13
13
|
[
|
|
@@ -67,7 +67,7 @@ const tamperedFunc = await deserialize(hashedObj, { hash: true });
|
|
|
67
67
|
// ChecksumError: Checksum failed
|
|
68
68
|
```
|
|
69
69
|
|
|
70
|
-
> Under the hood,
|
|
70
|
+
> Under the hood, hashing uses the [SubtleCrypto](https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto) API.
|
|
71
71
|
|
|
72
72
|
## Whitespace and comments
|
|
73
73
|
|
package/dist/main.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.deserialize=deserialize;exports.serialize=serialize;
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.deserialize=deserialize;exports.serialize=serialize;class JsonError extends Error{};class CryptoError extends Error{};class SerializeError extends Error{}class DeserializeError extends Error{}class ChecksumError extends Error{}class ConstructError extends Error{}const AsyncFunction=async function(){}.constructor;const Generator=function*(){}.constructor;const AsyncGenerator=async function*(){}.constructor;const formatPatterns={"Generator":/^(async\s+)?function\*\s*[^()]*\(([^)]*)\)\s*{([\s\S]*)}$/,"Function":/^(async\s+)?function\s*[^()]*\(([^)]*)\)\s*{([\s\S]*)}$/,"ArrowFunction":/^(async\s+)?(?:\(([^)]*)\)|([^=\s(]+))\s*=>\s*(?:{([\s\S]*)}|([\s\S]+))$/};async function hasher(obj){let json,hashed;try{json=JSON.stringify(obj)}catch(cause){throw new JsonError("Failed to stringify serialized function structure",{cause})}try{const hashBuffer=await(globalThis?.crypto?.subtle??window?.crypto?.subtle).digest("SHA-256",new TextEncoder().encode(json));hashed=Array.from(new Uint8Array(hashBuffer)).map(item=>item.toString(16).padStart(2,"0")).join("")}catch(cause){throw new CryptoError("Failed to generate hash digest",{cause})}return hashed}function getConstructor(type){switch(type){case"Function":case"ArrowFunction":return Function;case"AsyncFunction":case"AsyncArrowFunction":return AsyncFunction;case"Generator":return Generator;case"AsyncGenerator":return AsyncGenerator;default:throw new ConstructError(`Unexpected type ${type}`)}}function removeComments(input){let[...output]=`__${input}__`;const mode={singleQuote:false,doubleQuote:false,regex:false,blockComment:false,lineComment:false,condComp:false};for(let i=0,l=output.length;i<l;i++){if(mode.regex){if(output[i]==="/"&&output[i-1]!=="\\")mode.regex=false;continue}if(mode.singleQuote){if(output[i]==="'"&&output[i-1]!=="\\")mode.singleQuote=false;continue}if(mode.doubleQuote){if(output[i]==="\""&&output[i-1]!=="\\")mode.doubleQuote=false;continue}if(mode.blockComment){if(output[i]==="*"&&output[i+1]==="/"){output[i+1]="";mode.blockComment=false}output[i]="";continue}if(mode.lineComment){if(output[i+1]==="\n"||output[i+1]==="\r")mode.lineComment=false;output[i]="";continue}if(mode.condComp){if(output[i-2]==="@"&&output[i-1]==="*"&&output[i]==="/")mode.condComp=false;continue}mode.doubleQuote=output[i]==="\"";mode.singleQuote=output[i]==="'";if(output[i]==="/"){if(output[i+1]==="*"&&output[i+2]==="@"){mode.condComp=true;continue}if(output[i+1]==="*"){output[i]="";mode.blockComment=true;continue}if(output[i+1]==="/"){output[i]="";mode.lineComment=true;continue}mode.regex=true}}return output.join("").slice(2,-2)}function serialize(func,opts){const def={hash:false,comments:false,whitespace:false};opts=typeof opts==="object"&&null!==opts?Object.assign({},def,opts):Object.assign({},def);const typed=typeof func;if(typed!=="function"){throw new SerializeError("Invalid argument type, must be a function",{cause:{"typeof":typed}})}let stringified=func.toString();if(!opts.comments){stringified=removeComments(stringified)}if(!opts.whitespace){stringified=stringified.split(/[\r\n]+/).map(line=>line.trim()).filter(line=>line!=="").join("\n")}let match,serialized;for(const[type,pattern]of Object.entries(formatPatterns)){try{match=stringified.match(pattern);if(match){let async=match[1]?"Async":"";let params=type==="ArrowFunction"?match[2]??match[3]:match[2];params=params.split(",").map(p=>opts.whitespace?p:p.trim()).filter(Boolean);let body=type==="ArrowFunction"?match[4]??`return (${match[5]});`:match[3];if(!opts.whitespace)body=body.trim();serialized={params,body,type:`${async}${type}`};break}}catch(cause){throw new SerializeError(`Unexpected error serializing ${type}`,{cause})}}if(!serialized){throw new SerializeError("Unsupported function format",{cause:stringified})}if(opts.hash){return hasher(serialized).then(hashed=>{serialized.hash=hashed;return serialized}).catch(cause=>{throw new SerializeError("Failure hashing serialized function",{cause})})}return serialized}function deserialize(struct,opts={hash:false}){if(opts?.hash){if(struct?.hash===undefined){throw new DeserializeError("Deserialized function missing hash")}const test=Object.assign({},struct);delete test.hash;return hasher(test).then(checksum=>{if(checksum!==struct.hash){throw new ChecksumError("Checksum failed",{cause:{a:checksum,b:struct.hash}})}return deserialize(struct,{hash:false})}).catch(cause=>{if(cause instanceof ChecksumError||cause instanceof DeserializeError||cause instanceof ConstructError){throw cause}throw new DeserializeError("Failure generating checksum",{cause})})}try{const constructor=getConstructor(struct.type);return new constructor(...struct.params,struct.body)}catch(cause){if(cause instanceof ConstructError)throw cause;throw new DeserializeError("Failure deserializing",{cause})}}
|
package/lib/main.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
class JsonError extends Error{};class CryptoError extends Error{};class SerializeError extends Error{}class DeserializeError extends Error{}class ChecksumError extends Error{}class ConstructError extends Error{}const AsyncFunction=async function(){}.constructor;const Generator=function*(){}.constructor;const AsyncGenerator=async function*(){}.constructor;const formatPatterns={"Generator":/^(async\s+)?function\*\s*[^()]*\(([^)]*)\)\s*{([\s\S]*)}$/,"Function":/^(async\s+)?function\s*[^()]*\(([^)]*)\)\s*{([\s\S]*)}$/,"ArrowFunction":/^(async\s+)?(?:\(([^)]*)\)|([^=\s(]+))\s*=>\s*(?:{([\s\S]*)}|([\s\S]+))$/};async function hasher(obj){let json,hashed;try{json=JSON.stringify(obj)}catch(cause){throw new JsonError("Failed to stringify serialized function structure",{cause})}try{const hashBuffer=await(globalThis?.crypto?.subtle??window?.crypto?.subtle).digest("SHA-256",new TextEncoder().encode(json));hashed=Array.from(new Uint8Array(hashBuffer)).map(item=>item.toString(16).padStart(2,"0")).join("")}catch(cause){throw new CryptoError("Failed to generate hash digest",{cause})}return hashed}function getConstructor(type){switch(type){case"Function":case"ArrowFunction":return Function;case"AsyncFunction":case"AsyncArrowFunction":return AsyncFunction;case"Generator":return Generator;case"AsyncGenerator":return AsyncGenerator;default:throw new ConstructError(`Unexpected type ${type}`)}}function removeComments(input){let[...output]=`__${input}__`;const mode={singleQuote:false,doubleQuote:false,regex:false,blockComment:false,lineComment:false,condComp:false};for(let i=0,l=output.length;i<l;i++){if(mode.regex){if(output[i]==="/"&&output[i-1]!=="\\")mode.regex=false;continue}if(mode.singleQuote){if(output[i]==="'"&&output[i-1]!=="\\")mode.singleQuote=false;continue}if(mode.doubleQuote){if(output[i]==="\""&&output[i-1]!=="\\")mode.doubleQuote=false;continue}if(mode.blockComment){if(output[i]==="*"&&output[i+1]==="/"){output[i+1]="";mode.blockComment=false}output[i]="";continue}if(mode.lineComment){if(output[i+1]==="\n"||output[i+1]==="\r")mode.lineComment=false;output[i]="";continue}if(mode.condComp){if(output[i-2]==="@"&&output[i-1]==="*"&&output[i]==="/")mode.condComp=false;continue}mode.doubleQuote=output[i]==="\"";mode.singleQuote=output[i]==="'";if(output[i]==="/"){if(output[i+1]==="*"&&output[i+2]==="@"){mode.condComp=true;continue}if(output[i+1]==="*"){output[i]="";mode.blockComment=true;continue}if(output[i+1]==="/"){output[i]="";mode.lineComment=true;continue}mode.regex=true}}return output.join("").slice(2,-2)}function serialize(func,opts){const def={hash:false,comments:false,whitespace:false};opts=typeof opts==="object"&&null!==opts?Object.assign({},def,opts):Object.assign({},def);const typed=typeof func;if(typed!=="function"){throw new SerializeError("Invalid argument type, must be a function",{cause:{"typeof":typed}})}let stringified=func.toString();if(!opts.comments){stringified=removeComments(stringified)}if(!opts.whitespace){stringified=stringified.split(/[\r\n]+/).map(line=>line.trim()).filter(line=>line!=="").join("\n")}let match,serialized;for(const[type,pattern]of Object.entries(formatPatterns)){try{match=stringified.match(pattern);if(match){let async=match[1]?"Async":"";let params=type==="ArrowFunction"?match[2]??match[3]:match[2];params=params.split(",").map(p=>opts.whitespace?p:p.trim()).filter(Boolean);let body=type==="ArrowFunction"?match[4]??`return (${match[5]});`:match[3];if(!opts.whitespace)body=body.trim();serialized={params,body,type:`${async}${type}`};break}}catch(cause){throw new SerializeError(`Unexpected error serializing ${type}`,{cause})}}if(!serialized){throw new SerializeError("Unsupported function format",{cause:stringified})}if(opts.hash){return hasher(serialized).then(hashed=>{serialized.hash=hashed;return serialized}).catch(cause=>{throw new SerializeError("Failure hashing serialized function",{cause})})}return serialized}function deserialize(struct){let opts=arguments.length>1&&arguments[1]!==undefined?arguments[1]:{hash:false};if(opts?.hash){if(struct?.hash===undefined){throw new DeserializeError("Deserialized function missing hash")}const test=Object.assign({},struct);delete test.hash;return hasher(test).then(checksum=>{if(checksum!==struct.hash){throw new ChecksumError("Checksum failed",{cause:{a:checksum,b:struct.hash}})}return deserialize(struct,{hash:false})}).catch(cause=>{if(cause instanceof ChecksumError||cause instanceof DeserializeError||cause instanceof ConstructError){throw cause}throw new DeserializeError("Failure generating checksum",{cause})})}try{const constructor=getConstructor(struct.type);return new constructor(...struct.params,struct.body)}catch(cause){if(cause instanceof ConstructError)throw cause;throw new DeserializeError("Failure deserializing",{cause})}}export{serialize,deserialize};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "serialize-function",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"description": "Serializes javascript functions to a JSON-friendly format",
|
|
5
5
|
"author": "Evan Kaufman <evan@evanskaufman.com>",
|
|
6
6
|
"license": "MIT",
|
|
@@ -27,14 +27,13 @@
|
|
|
27
27
|
},
|
|
28
28
|
"browser": "./lib/main.js",
|
|
29
29
|
"scripts": {
|
|
30
|
-
"
|
|
31
|
-
"
|
|
32
|
-
"transpile-
|
|
33
|
-
"
|
|
34
|
-
"build-
|
|
35
|
-
"build-
|
|
36
|
-
"build-
|
|
37
|
-
"build-node": "npm run concat-node && npm run transpile-node -- --minified --no-comments && cp src/import.mjs dist/",
|
|
30
|
+
"clean": "rm -f ./build-*.*js ./tests/*.ignore.test.cjs ./0*.test.html ./screenshot.png && rm -rf ./lib ./dist",
|
|
31
|
+
"transpile-browser": "babel src/main.mjs --no-babelrc --out-file lib/main.js --config-file ./.babelrc.esm.json",
|
|
32
|
+
"transpile-node": "babel src/main.mjs --no-babelrc --out-file dist/main.js --config-file ./.babelrc.cjs.json",
|
|
33
|
+
"build-browser-dev": "npm run transpile-browser",
|
|
34
|
+
"build-node-dev": "npm run transpile-node && cp src/import.mjs dist/",
|
|
35
|
+
"build-browser": "npm run transpile-browser -- --minified --no-comments",
|
|
36
|
+
"build-node": "npm run transpile-node -- --minified --no-comments && cp src/import.mjs dist/",
|
|
38
37
|
"dist-dev": "npm run build-browser-dev && npm run build-node-dev",
|
|
39
38
|
"dist": "npm run build-browser && npm run build-node",
|
|
40
39
|
"lint": "eslint .",
|
|
@@ -42,7 +41,7 @@
|
|
|
42
41
|
"test-browser": "node ./tests/003-puppeteer.script.mjs",
|
|
43
42
|
"prepare-test-node": "cat tests/001-general.test.template.cjs tests/general.spec.js > tests/001-general.ignore.test.cjs",
|
|
44
43
|
"test-node": "mocha ./tests/**.test.*js",
|
|
45
|
-
"test": "npm run prepare-test-
|
|
44
|
+
"test": "npm run clean && npm run dist && npm run prepare-test-node && npm run test-node && npm run prepare-test-browser && npm run test-browser"
|
|
46
45
|
},
|
|
47
46
|
"devDependencies": {
|
|
48
47
|
"@babel/cli": "^7.28.6",
|