serialize-function 2.0.1 → 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/README.md +1 -1
- package/lib/main.js +1 -1
- package/package.json +13 -13
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|

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

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

|
package/lib/main.js
CHANGED
|
@@ -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":/^(?<isAsync>async\s+)?function\*\s*[^()]*\((?<params>[^)]*)\)\s*{(?<body>[\s\S]*)}$/,"Function":/^(?<isAsync>async\s+)?function\s*[^()]*\((?<params>[^)]*)\)\s*{(?<body>[\s\S]*)}$/,"ArrowFunction":/^(?<isAsync>async\s+)?(?:\((?<params>[^)]*)\)|(?<singleParam>[^=\s(]+))\s*=>\s*(?:{(?<bracedBody>[\s\S]*)}|(?<bodyExpr>[\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};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}mode.doubleQuote=output[i]==="\"";mode.singleQuote=output[i]==="'";if(output[i]==="/"){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)}async 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.groups.isAsync?"Async":"";let params=type==="ArrowFunction"?match.groups.params??match.groups.singleParam:match.groups.params;params=params.split(",").map(p=>opts.whitespace?p:p.trim()).filter(Boolean);let body=type==="ArrowFunction"?match.groups.bracedBody??`return (${match.groups.bodyExpr});`:match.groups.body;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){try{const hashed=await hasher(serialized);serialized.hash=hashed}catch(cause){throw new SerializeError("Failure hashing serialized function",{cause})}}return serialized}async function deserialize(struct
|
|
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":/^(?<isAsync>async\s+)?function\*\s*[^()]*\((?<params>[^)]*)\)\s*{(?<body>[\s\S]*)}$/,"Function":/^(?<isAsync>async\s+)?function\s*[^()]*\((?<params>[^)]*)\)\s*{(?<body>[\s\S]*)}$/,"ArrowFunction":/^(?<isAsync>async\s+)?(?:\((?<params>[^)]*)\)|(?<singleParam>[^=\s(]+))\s*=>\s*(?:{(?<bracedBody>[\s\S]*)}|(?<bodyExpr>[\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};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}mode.doubleQuote=output[i]==="\"";mode.singleQuote=output[i]==="'";if(output[i]==="/"){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)}async 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.groups.isAsync?"Async":"";let params=type==="ArrowFunction"?match.groups.params??match.groups.singleParam:match.groups.params;params=params.split(",").map(p=>opts.whitespace?p:p.trim()).filter(Boolean);let body=type==="ArrowFunction"?match.groups.bracedBody??`return (${match.groups.bodyExpr});`:match.groups.body;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){try{const hashed=await hasher(serialized);serialized.hash=hashed}catch(cause){throw new SerializeError("Failure hashing serialized function",{cause})}}return serialized}async 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;try{const checksum=await hasher(test);if(checksum!==struct.hash){throw new ChecksumError("Checksum failed",{cause:{a:checksum,b:struct.hash}})}}catch(cause){if(cause instanceof ChecksumError)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})}}async function traverse(input,tester,converter){if(tester(input)){return await converter(input)}if(input===null||typeof input!=="object"){return input}const nonTraversables=[String,Boolean,Number,Date,RegExp];for(const constructor of nonTraversables){if(input instanceof constructor){return new constructor(input.valueOf())}}if(input instanceof Array){const cloned=[];for(let i=0;i<input.length;i++){if(tester(input[i]))cloned[i]=await converter(input[i]);else cloned[i]=await traverse(input[i],tester,converter)}return cloned}if(input instanceof Set){const cloned=new Set;for(const value of input){if(tester(value))cloned.add(await converter(value));else cloned.add(await traverse(value,tester,converter))}return cloned}if(input instanceof Map){const cloned=new Map;for(const[key,value]of input){if(tester(value))cloned.set(key,await converter(value));else cloned.set(key,await traverse(value,tester,converter))}return cloned}if(input instanceof Object){const cloned=Object.create(Object.getPrototypeOf(input));for(const key in input){if(Object.hasOwn(input,key)){if(tester(input[key]))cloned[key]=await converter(input[key]);else cloned[key]=await traverse(input[key],tester,converter)}}return cloned}return input}async function deepSerialize(value,options){try{return await traverse(value,input=>typeof input==="function",input=>serialize(input,options))}catch(cause){throw new SerializeError("Failure traversing and serializing",{cause})}}async function deepDeserialize(value,options){try{return await traverse(value,input=>typeof input==="object"&&Object.hasOwn(input,"params")&&Object.hasOwn(input,"body")&&Object.hasOwn(input,"type"),input=>deserialize(input,Object.assign({hash:Object.hasOwn(input,"hash")},options)))}catch(cause){throw new DeserializeError("Failure traversing and deserializing",{cause})}}export{serialize,deserialize,deepSerialize,deepDeserialize,JsonError,CryptoError,SerializeError,DeserializeError,ChecksumError,ConstructError};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "serialize-function",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.3",
|
|
4
4
|
"description": "Serializes javascript functions to a JSON-friendly format",
|
|
5
5
|
"author": "Evan Kaufman <evan@evanskaufman.com>",
|
|
6
6
|
"license": "MIT",
|
|
@@ -45,23 +45,23 @@
|
|
|
45
45
|
"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
46
|
},
|
|
47
47
|
"devDependencies": {
|
|
48
|
-
"@babel/cli": "^
|
|
49
|
-
"@babel/core": "^
|
|
50
|
-
"@babel/plugin-transform-nullish-coalescing-operator": "^
|
|
51
|
-
"@babel/preset-env": "^
|
|
48
|
+
"@babel/cli": "^8.0.4",
|
|
49
|
+
"@babel/core": "^8.0.1",
|
|
50
|
+
"@babel/plugin-transform-nullish-coalescing-operator": "^8.0.1",
|
|
51
|
+
"@babel/preset-env": "^8.0.2",
|
|
52
52
|
"@eslint/js": "^10.0.1",
|
|
53
|
-
"@fastify/static": "^
|
|
53
|
+
"@fastify/static": "^10.1.3",
|
|
54
54
|
"@stylistic/eslint-plugin": "^5.10.0",
|
|
55
55
|
"chai": "^6.2.2",
|
|
56
56
|
"classy-template": "^1.5.4",
|
|
57
|
-
"eslint": "^10.
|
|
58
|
-
"eslint-plugin-mocha": "^
|
|
59
|
-
"fastify": "^5.
|
|
60
|
-
"globals": "^17.
|
|
57
|
+
"eslint": "^10.9.1",
|
|
58
|
+
"eslint-plugin-mocha": "^12.0.2",
|
|
59
|
+
"fastify": "^5.12.1",
|
|
60
|
+
"globals": "^17.11.0",
|
|
61
61
|
"jsdoc": "^4.0.5",
|
|
62
|
-
"mocha": "^
|
|
62
|
+
"mocha": "^12.0.0",
|
|
63
63
|
"proxyquire": "^2.1.3",
|
|
64
|
-
"puppeteer": "^
|
|
65
|
-
"sinon": "^
|
|
64
|
+
"puppeteer": "^25.9.0",
|
|
65
|
+
"sinon": "^22.1.0"
|
|
66
66
|
}
|
|
67
67
|
}
|