diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 96cb234..7db687b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -32,6 +32,7 @@ jobs: - if: github.event_name == 'pull_request' name: Lint commit messages run: npx commitlint --from ${{ github.event.pull_request.head.sha }}~${{ github.event.pull_request.commits }} --to ${{ github.event.pull_request.head.sha }} --verbose + - run: npm run build - run: npm test publish: name: "Publish" diff --git a/.gitignore b/.gitignore index c32365f..0b94718 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +/lib /.idea # Logs diff --git a/.npmignore b/.npmignore index 20ab70d..967c9f5 100644 --- a/.npmignore +++ b/.npmignore @@ -1,6 +1,7 @@ artifacts/ coverage/ test/ +src/ .gitignore .npmignore .nyc_output diff --git a/index.js b/index.js deleted file mode 100644 index 1a8b4d3..0000000 --- a/index.js +++ /dev/null @@ -1,276 +0,0 @@ -/* -Copyright (c) 2014, Yahoo! Inc. All rights reserved. -Copyrights licensed under the New BSD License. -See the accompanying LICENSE file for terms. -*/ - -'use strict'; - -var crypto = require('crypto'); - -// Generate an internal UID to make the regexp pattern harder to guess. -var UID_LENGTH = 16; -var UID = generateUID(); -var PLACE_HOLDER_REGEXP = new RegExp('(\\\\)?"@__(F|R|D|M|S|A|U|I|B|L)-' + UID + '-(\\d+)__@"', 'g'); - -var IS_NATIVE_CODE_REGEXP = /\{\s*\[native code\]\s*\}/g; -var IS_PURE_FUNCTION = /function.*?\(/; -var IS_ARROW_FUNCTION = /.*?=>.*?/; -var UNSAFE_CHARS_REGEXP = /[<>\/\u2028\u2029]/g; - -var RESERVED_SYMBOLS = ['*', 'async']; - -// Mapping of unsafe HTML and invalid JavaScript line terminator chars to their -// Unicode char counterparts which are safe to use in JavaScript strings. -var ESCAPED_CHARS = { - '<' : '\\u003C', - '>' : '\\u003E', - '/' : '\\u002F', - '\u2028': '\\u2028', - '\u2029': '\\u2029' -}; - -function escapeUnsafeChars(unsafeChar) { - return ESCAPED_CHARS[unsafeChar]; -} - -function generateUID() { - if (crypto.randomUUID) { - const uuid = crypto.randomUUID(); - return uuid.replace(/-/g, ''); - } - - const randomValues = new Uint8Array(UID_LENGTH); - const bytes = crypto.getRandomValues(randomValues); - let result = ''; - - for (let i = 0; i < UID_LENGTH; ++i) { - result += bytes[i].toString(16); - } - - return result; -} - -function deleteFunctions(obj){ - var functionKeys = []; - for (var key in obj) { - if (typeof obj[key] === "function") { - functionKeys.push(key); - } - } - for (var i = 0; i < functionKeys.length; i++) { - delete obj[functionKeys[i]]; - } -} - -module.exports = function serialize(obj, options) { - options || (options = {}); - - // Backwards-compatibility for `space` as the second argument. - if (typeof options === 'number' || typeof options === 'string') { - options = {space: options}; - } - - var functions = []; - var regexps = []; - var dates = []; - var maps = []; - var sets = []; - var arrays = []; - var undefs = []; - var infinities= []; - var bigInts = []; - var urls = []; - - // Returns placeholders for functions and regexps (identified by index) - // which are later replaced by their string representation. - function replacer(key, value) { - - // For nested function - if(options.ignoreFunction){ - deleteFunctions(value); - } - - if (!value && value !== undefined && value !== BigInt(0)) { - return value; - } - - // If the value is an object w/ a toJSON method, toJSON is called before - // the replacer runs, so we use this[key] to get the non-toJSONed value. - var origValue = this[key]; - var type = typeof origValue; - - if (type === 'object') { - if(origValue instanceof RegExp) { - return '@__R-' + UID + '-' + (regexps.push(origValue) - 1) + '__@'; - } - - if(origValue instanceof Date) { - return '@__D-' + UID + '-' + (dates.push(origValue) - 1) + '__@'; - } - - if(origValue instanceof Map) { - return '@__M-' + UID + '-' + (maps.push(origValue) - 1) + '__@'; - } - - if(origValue instanceof Set) { - return '@__S-' + UID + '-' + (sets.push(origValue) - 1) + '__@'; - } - - if(origValue instanceof Array) { - var isSparse = origValue.filter(function(){return true}).length !== origValue.length; - if (isSparse) { - return '@__A-' + UID + '-' + (arrays.push(origValue) - 1) + '__@'; - } - } - - if(origValue instanceof URL) { - return '@__L-' + UID + '-' + (urls.push(origValue) - 1) + '__@'; - } - } - - if (type === 'function') { - return '@__F-' + UID + '-' + (functions.push(origValue) - 1) + '__@'; - } - - if (type === 'undefined') { - return '@__U-' + UID + '-' + (undefs.push(origValue) - 1) + '__@'; - } - - if (type === 'number' && !isNaN(origValue) && !isFinite(origValue)) { - return '@__I-' + UID + '-' + (infinities.push(origValue) - 1) + '__@'; - } - - if (type === 'bigint') { - return '@__B-' + UID + '-' + (bigInts.push(origValue) - 1) + '__@'; - } - - return value; - } - - function serializeFunc(fn) { - var serializedFn = fn.toString(); - if (IS_NATIVE_CODE_REGEXP.test(serializedFn)) { - throw new TypeError('Serializing native function: ' + fn.name); - } - - // pure functions, example: {key: function() {}} - if(IS_PURE_FUNCTION.test(serializedFn)) { - return serializedFn; - } - - // arrow functions, example: arg1 => arg1+5 - if(IS_ARROW_FUNCTION.test(serializedFn)) { - return serializedFn; - } - - var argsStartsAt = serializedFn.indexOf('('); - var def = serializedFn.substr(0, argsStartsAt) - .trim() - .split(' ') - .filter(function(val) { return val.length > 0 }); - - var nonReservedSymbols = def.filter(function(val) { - return RESERVED_SYMBOLS.indexOf(val) === -1 - }); - - // enhanced literal objects, example: {key() {}} - if(nonReservedSymbols.length > 0) { - return (def.indexOf('async') > -1 ? 'async ' : '') + 'function' - + (def.join('').indexOf('*') > -1 ? '*' : '') - + serializedFn.substr(argsStartsAt); - } - - // arrow functions - return serializedFn; - } - - // Check if the parameter is function - if (options.ignoreFunction && typeof obj === "function") { - obj = undefined; - } - // Protects against `JSON.stringify()` returning `undefined`, by serializing - // to the literal string: "undefined". - if (obj === undefined) { - return String(obj); - } - - var str; - - // Creates a JSON string representation of the value. - // NOTE: Node 0.12 goes into slow mode with extra JSON.stringify() args. - if (options.isJSON && !options.space) { - str = JSON.stringify(obj); - } else { - str = JSON.stringify(obj, options.isJSON ? null : replacer, options.space); - } - - // Protects against `JSON.stringify()` returning `undefined`, by serializing - // to the literal string: "undefined". - if (typeof str !== 'string') { - return String(str); - } - - // Replace unsafe HTML and invalid JavaScript line terminator chars with - // their safe Unicode char counterpart. This _must_ happen before the - // regexps and functions are serialized and added back to the string. - if (options.unsafe !== true) { - str = str.replace(UNSAFE_CHARS_REGEXP, escapeUnsafeChars); - } - - if (functions.length === 0 && regexps.length === 0 && dates.length === 0 && maps.length === 0 && sets.length === 0 && arrays.length === 0 && undefs.length === 0 && infinities.length === 0 && bigInts.length === 0 && urls.length === 0) { - return str; - } - - // Replaces all occurrences of function, regexp, date, map and set placeholders in the - // JSON string with their string representations. If the original value can - // not be found, then `undefined` is used. - return str.replace(PLACE_HOLDER_REGEXP, function (match, backSlash, type, valueIndex) { - // The placeholder may not be preceded by a backslash. This is to prevent - // replacing things like `"a\"@__R--0__@"` and thus outputting - // invalid JS. - if (backSlash) { - return match; - } - - if (type === 'D') { - return "new Date(\"" + dates[valueIndex].toISOString() + "\")"; - } - - if (type === 'R') { - return "new RegExp(" + serialize(regexps[valueIndex].source) + ", \"" + regexps[valueIndex].flags + "\")"; - } - - if (type === 'M') { - return "new Map(" + serialize(Array.from(maps[valueIndex].entries()), options) + ")"; - } - - if (type === 'S') { - return "new Set(" + serialize(Array.from(sets[valueIndex].values()), options) + ")"; - } - - if (type === 'A') { - return "Array.prototype.slice.call(" + serialize(Object.assign({ length: arrays[valueIndex].length }, arrays[valueIndex]), options) + ")"; - } - - if (type === 'U') { - return 'undefined' - } - - if (type === 'I') { - return infinities[valueIndex]; - } - - if (type === 'B') { - return "BigInt(\"" + bigInts[valueIndex] + "\")"; - } - - if (type === 'L') { - return "new URL(" + serialize(urls[valueIndex].toString(), options) + ")"; - } - - var fn = functions[valueIndex]; - - return serializeFunc(fn); - }); -} diff --git a/nyc.config.js b/nyc.config.js index db7146e..65f924d 100644 --- a/nyc.config.js +++ b/nyc.config.js @@ -10,11 +10,15 @@ module.exports = { branches: [85, 100], statements: [85, 100], }, - include: ['**/*.js'], - exclude: ['node_modules', 'test-results', 'test/*', '**/*.spec.js', '*.config.js'], + include: ['**/*.ts'], + exclude: ['node_modules', 'test-results', 'test/*', "**/*.d.ts", '**/*.spec.ts', '*.config.js'], reporter: ['text', 'html', 'cobertura'], cache: false, all: true, 'temp-directory': './test-results/coverage/.tmp', 'report-dir': './test-results/coverage', + require: [ + 'ts-node/register', + 'source-map-support/register' + ], }; diff --git a/package-lock.json b/package-lock.json index 62988d4..368976f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,13 +11,20 @@ "devDependencies": { "@commitlint/cli": "^19.5.0", "@commitlint/config-conventional": "^19.5.0", + "@types/mocha": "^10.0.9", + "@types/sinon": "^17.0.3", "benchmark": "^2.1.4", "chai": "^4.1.0", "husky": "^9.1.6", - "mocha": "^10.0.0", + "mocha": "^10.7.3", "mochawesome": "^7.1.3", - "nyc": "^17.0.0", - "semantic-release": "^24.1.3" + "nyc": "^17.1.0", + "semantic-release": "^24.1.3", + "sinon": "^19.0.2", + "source-map-support": "^0.5.21", + "ts-loader": "^9.5.1", + "ts-node": "^10.9.2", + "typescript": "^5.6.3" } }, "node_modules/@ampproject/remapping": { @@ -790,6 +797,30 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "0.3.9" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@cspotcode/source-map-support/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, "node_modules/@istanbuljs/load-nyc-config": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", @@ -884,6 +915,18 @@ "node": ">=6.0.0" } }, + "node_modules/@jridgewell/source-map": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.6.tgz", + "integrity": "sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25" + } + }, "node_modules/@jridgewell/sourcemap-codec": { "version": "1.4.15", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", @@ -1503,6 +1546,83 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/@sinonjs/commons": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", + "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "type-detect": "4.0.8" + } + }, + "node_modules/@sinonjs/fake-timers": { + "version": "13.0.5", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-13.0.5.tgz", + "integrity": "sha512-36/hTbH2uaWuGVERyC6da9YwGWnzUZXuPro/F2LfsdOsLnCojz/iSH8MxUt/FD2S5XBSVPhmArFUXcpCQ2Hkiw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@sinonjs/commons": "^3.0.1" + } + }, + "node_modules/@sinonjs/samsam": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-8.0.2.tgz", + "integrity": "sha512-v46t/fwnhejRSFTGqbpn9u+LQ9xJDse10gNnPgAcxgdoCDMXj/G2asWAC/8Qs+BAZDicX+MNZouXT1A7c83kVw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@sinonjs/commons": "^3.0.1", + "lodash.get": "^4.4.2", + "type-detect": "^4.1.0" + } + }, + "node_modules/@sinonjs/samsam/node_modules/type-detect": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.1.0.tgz", + "integrity": "sha512-Acylog8/luQ8L7il+geoSxhEkazvkslg7PSNKOX59mbB9cOveP5aq9h74Y7YU8yDpJwetzQQrfIwtf4Wp4LKcw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/@sinonjs/text-encoding": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/@sinonjs/text-encoding/-/text-encoding-0.7.3.tgz", + "integrity": "sha512-DE427ROAphMQzU4ENbliGYrBSYPXF+TtLg9S8vzeA+OF4ZKzoDdzfL8sxuMUGS/lgRhM6j1URSk9ghf7Xo1tyA==", + "dev": true, + "license": "(Unlicense OR Apache-2.0)" + }, + "node_modules/@tsconfig/node10": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz", + "integrity": "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node16": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", + "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/color-name": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz", @@ -1519,6 +1639,53 @@ "@types/node": "*" } }, + "node_modules/@types/eslint": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-9.6.1.tgz", + "integrity": "sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@types/estree": "*", + "@types/json-schema": "*" + } + }, + "node_modules/@types/eslint-scope": { + "version": "3.7.7", + "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.7.tgz", + "integrity": "sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@types/eslint": "*", + "@types/estree": "*" + } + }, + "node_modules/@types/estree": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", + "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/@types/mocha": { + "version": "10.0.9", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.9.tgz", + "integrity": "sha512-sicdRoWtYevwxjOHNMPTl3vSfJM6oyW8o1wXeI7uww6b6xHg8eBznQDNSGBCDJmsE8UMxP05JgZRtsKbTqt//Q==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/node": { "version": "22.9.0", "resolved": "https://registry.npmjs.org/@types/node/-/node-22.9.0.tgz", @@ -1543,6 +1710,241 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/sinon": { + "version": "17.0.3", + "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-17.0.3.tgz", + "integrity": "sha512-j3uovdn8ewky9kRBG19bOwaZbexJu/XjtkHyjvUgt4xfPFz18dcORIMqnYh66Fx3Powhcr85NT5+er3+oViapw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/sinonjs__fake-timers": "*" + } + }, + "node_modules/@types/sinonjs__fake-timers": { + "version": "8.1.5", + "resolved": "https://registry.npmjs.org/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-8.1.5.tgz", + "integrity": "sha512-mQkU2jY8jJEF7YHjHvsQO8+3ughTL1mcnn96igfhONmR+fUPSKIkefQYpSe8bsly2Ep7oQbn/6VG5/9/0qcArQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@webassemblyjs/ast": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.14.1.tgz", + "integrity": "sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@webassemblyjs/helper-numbers": "1.13.2", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2" + } + }, + "node_modules/@webassemblyjs/floating-point-hex-parser": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.13.2.tgz", + "integrity": "sha512-6oXyTOzbKxGH4steLbLNOu71Oj+C8Lg34n6CqRvqfS2O71BxY6ByfMDRhBytzknj9yGUPVJ1qIKhRlAwO1AovA==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/@webassemblyjs/helper-api-error": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.13.2.tgz", + "integrity": "sha512-U56GMYxy4ZQCbDZd6JuvvNV/WFildOjsaWD3Tzzvmw/mas3cXzRJPMjP83JqEsgSbyrmaGjBfDtV7KDXV9UzFQ==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/@webassemblyjs/helper-buffer": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.14.1.tgz", + "integrity": "sha512-jyH7wtcHiKssDtFPRB+iQdxlDf96m0E39yb0k5uJVhFGleZFoNw1c4aeIcVUPPbXUVJ94wwnMOAqUHyzoEPVMA==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/@webassemblyjs/helper-numbers": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.13.2.tgz", + "integrity": "sha512-FE8aCmS5Q6eQYcV3gI35O4J789wlQA+7JrqTTpJqn5emA4U2hvwJmvFRC0HODS+3Ye6WioDklgd6scJ3+PLnEA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@webassemblyjs/floating-point-hex-parser": "1.13.2", + "@webassemblyjs/helper-api-error": "1.13.2", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webassemblyjs/helper-wasm-bytecode": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.13.2.tgz", + "integrity": "sha512-3QbLKy93F0EAIXLh0ogEVR6rOubA9AoZ+WRYhNbFyuB70j3dRdwH9g+qXhLAO0kiYGlg3TxDV+I4rQTr/YNXkA==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/@webassemblyjs/helper-wasm-section": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.14.1.tgz", + "integrity": "sha512-ds5mXEqTJ6oxRoqjhWDU83OgzAYjwsCV8Lo/N+oRsNDmx/ZDpqalmrtgOMkHwxsG0iI//3BwWAErYRHtgn0dZw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/wasm-gen": "1.14.1" + } + }, + "node_modules/@webassemblyjs/ieee754": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.13.2.tgz", + "integrity": "sha512-4LtOzh58S/5lX4ITKxnAK2USuNEvpdVV9AlgGQb8rJDHaLeHciwG4zlGr0j/SNWlr7x3vO1lDEsuePvtcDNCkw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@xtuc/ieee754": "^1.2.0" + } + }, + "node_modules/@webassemblyjs/leb128": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.13.2.tgz", + "integrity": "sha512-Lde1oNoIdzVzdkNEAWZ1dZ5orIbff80YPdHx20mrHwHrVNNTjNr8E3xz9BdpcGqRQbAEa+fkrCb+fRFTl/6sQw==", + "dev": true, + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webassemblyjs/utf8": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.13.2.tgz", + "integrity": "sha512-3NQWGjKTASY1xV5m7Hr0iPeXD9+RDobLll3T9d2AO+g3my8xy5peVyjSag4I50mR1bBSN/Ct12lo+R9tJk0NZQ==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/@webassemblyjs/wasm-edit": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.14.1.tgz", + "integrity": "sha512-RNJUIQH/J8iA/1NzlE4N7KtyZNHi3w7at7hDjvRNm5rcUXa00z1vRz3glZoULfJ5mpvYhLybmVcwcjGrC1pRrQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/helper-wasm-section": "1.14.1", + "@webassemblyjs/wasm-gen": "1.14.1", + "@webassemblyjs/wasm-opt": "1.14.1", + "@webassemblyjs/wasm-parser": "1.14.1", + "@webassemblyjs/wast-printer": "1.14.1" + } + }, + "node_modules/@webassemblyjs/wasm-gen": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.14.1.tgz", + "integrity": "sha512-AmomSIjP8ZbfGQhumkNvgC33AY7qtMCXnN6bL2u2Js4gVCg8fp735aEiMSBbDR7UQIj90n4wKAFUSEd0QN2Ukg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/ieee754": "1.13.2", + "@webassemblyjs/leb128": "1.13.2", + "@webassemblyjs/utf8": "1.13.2" + } + }, + "node_modules/@webassemblyjs/wasm-opt": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.14.1.tgz", + "integrity": "sha512-PTcKLUNvBqnY2U6E5bdOQcSM+oVP/PmrDY9NzowJjislEjwP/C4an2303MCVS2Mg9d3AJpIGdUFIQQWbPds0Sw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/wasm-gen": "1.14.1", + "@webassemblyjs/wasm-parser": "1.14.1" + } + }, + "node_modules/@webassemblyjs/wasm-parser": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.14.1.tgz", + "integrity": "sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-api-error": "1.13.2", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/ieee754": "1.13.2", + "@webassemblyjs/leb128": "1.13.2", + "@webassemblyjs/utf8": "1.13.2" + } + }, + "node_modules/@webassemblyjs/wast-printer": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.14.1.tgz", + "integrity": "sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@xtuc/ieee754": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", + "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", + "dev": true, + "license": "BSD-3-Clause", + "peer": true + }, + "node_modules/@xtuc/long": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", + "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", + "dev": true, + "license": "Apache-2.0", + "peer": true + }, + "node_modules/acorn": { + "version": "8.14.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", + "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-walk": { + "version": "8.3.4", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz", + "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "acorn": "^8.11.0" + }, + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/agent-base": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz", @@ -1670,6 +2072,13 @@ "integrity": "sha1-+cjBN1fMHde8N5rHeyxipcKGjEA=", "dev": true }, + "node_modules/arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true, + "license": "MIT" + }, "node_modules/argparse": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", @@ -1770,9 +2179,9 @@ "dev": true }, "node_modules/browserslist": { - "version": "4.23.1", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.1.tgz", - "integrity": "sha512-TUfofFo/KsK/bWZ9TWQ5O26tsWW4Uhmt8IYklbnUa70udB6P2wA7w7o4PY4muaEPBQaAX+CEnmmIA41NVHtPVw==", + "version": "4.24.2", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.2.tgz", + "integrity": "sha512-ZIc+Q62revdMcqC6aChtW4jz3My3klmCO1fEmINZY/8J3EpBg5/A/D0AKmBveUh6pgoeycoMkVMko84tuYS+Gg==", "dev": true, "funding": [ { @@ -1788,11 +2197,12 @@ "url": "https://github.com/sponsors/ai" } ], + "license": "MIT", "dependencies": { - "caniuse-lite": "^1.0.30001629", - "electron-to-chromium": "^1.4.796", - "node-releases": "^2.0.14", - "update-browserslist-db": "^1.0.16" + "caniuse-lite": "^1.0.30001669", + "electron-to-chromium": "^1.5.41", + "node-releases": "^2.0.18", + "update-browserslist-db": "^1.1.1" }, "bin": { "browserslist": "cli.js" @@ -1801,6 +2211,13 @@ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true, + "license": "MIT" + }, "node_modules/caching-transform": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/caching-transform/-/caching-transform-4.0.0.tgz", @@ -1836,9 +2253,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001639", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001639.tgz", - "integrity": "sha512-eFHflNTBIlFwP2AIKaYuBQN/apnUoKNhBdza8ZnW/h2di4LCZ4xFqYlxUxo+LQ76KFI1PGcC1QDxMbxTZpSCAg==", + "version": "1.0.30001680", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001680.tgz", + "integrity": "sha512-rPQy70G6AGUMnbwS1z6Xg+RkHYPAi18ihs47GH0jcxIG7wArmPgY3XbS2sRdBbxJljp3thdT8BIqv9ccCypiPA==", "dev": true, "funding": [ { @@ -1853,7 +2270,8 @@ "type": "github", "url": "https://github.com/sponsors/ai" } - ] + ], + "license": "CC-BY-4.0" }, "node_modules/chai": { "version": "4.3.7", @@ -1945,6 +2363,17 @@ "fsevents": "~2.3.2" } }, + "node_modules/chrome-trace-event": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.4.tgz", + "integrity": "sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=6.0" + } + }, "node_modules/clean-stack": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", @@ -2094,6 +2523,14 @@ "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", "dev": true }, + "node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true, + "license": "MIT", + "peer": true + }, "node_modules/commondir": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", @@ -2314,6 +2751,13 @@ "js-yaml": "bin/js-yaml.js" } }, + "node_modules/create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "dev": true, + "license": "MIT" + }, "node_modules/cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -2507,10 +2951,11 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.4.815", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.815.tgz", - "integrity": "sha512-OvpTT2ItpOXJL7IGcYakRjHCt8L5GrrN/wHCQsRB4PQa1X9fe+X9oen245mIId7s14xvArCGSTIq644yPUKKLg==", - "dev": true + "version": "1.5.57", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.57.tgz", + "integrity": "sha512-xS65H/tqgOwUBa5UmOuNSLuslDo7zho0y/lgQw35pnrqiZh7UOWHCeL/Bt6noJATbA6tpQJGCifsFsIRZj1Fqg==", + "dev": true, + "license": "ISC" }, "node_modules/emoji-regex": { "version": "8.0.0", @@ -2525,6 +2970,20 @@ "dev": true, "license": "MIT" }, + "node_modules/enhanced-resolve": { + "version": "5.17.1", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.17.1.tgz", + "integrity": "sha512-LMHl3dXhTcfv8gM4kEzIUeTQ+7fpdA0l2tUf34BddXPkz2A5xJ5L/Pchd5BL6rdccM9QGvu0sWZzK1Z1t4wwyg==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.2.0" + }, + "engines": { + "node": ">=10.13.0" + } + }, "node_modules/env-ci": { "version": "11.1.0", "resolved": "https://registry.npmjs.org/env-ci/-/env-ci-11.1.0.tgz", @@ -2687,6 +3146,14 @@ "is-arrayish": "^0.2.1" } }, + "node_modules/es-module-lexer": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.5.4.tgz", + "integrity": "sha512-MVNK56NiMrOwitFB7cqDwq0CQutbw+0BvLshJSse0MUNU+y1FC3bUS/AQg7oUng+/wKrrki7JfmwtVHkVfPLlw==", + "dev": true, + "license": "MIT", + "peer": true + }, "node_modules/es6-error": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", @@ -2694,10 +3161,11 @@ "dev": true }, "node_modules/escalade": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz", - "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", "dev": true, + "license": "MIT", "engines": { "node": ">=6" } @@ -2718,6 +3186,21 @@ "node": ">=0.8.0" } }, + "node_modules/eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "license": "BSD-2-Clause", + "peer": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, "node_modules/esprima": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", @@ -2731,24 +3214,71 @@ "node": ">=4" } }, - "node_modules/execa": { - "version": "9.5.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-9.5.1.tgz", - "integrity": "sha512-QY5PPtSonnGwhhHDNI7+3RvY285c7iuJFFB+lU+oEzMY/gEGJ808owqJsrr8Otd1E/x07po1LkUBmdAc5duPAg==", + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", "dev": true, - "license": "MIT", + "license": "BSD-2-Clause", + "peer": true, "dependencies": { - "@sindresorhus/merge-streams": "^4.0.0", - "cross-spawn": "^7.0.3", - "figures": "^6.1.0", - "get-stream": "^9.0.0", - "human-signals": "^8.0.0", - "is-plain-obj": "^4.1.0", - "is-stream": "^4.0.1", - "npm-run-path": "^6.0.0", - "pretty-ms": "^9.0.0", - "signal-exit": "^4.1.0", - "strip-final-newline": "^4.0.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esrecurse/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "license": "BSD-2-Clause", + "peer": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true, + "license": "BSD-2-Clause", + "peer": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=0.8.x" + } + }, + "node_modules/execa": { + "version": "9.5.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-9.5.1.tgz", + "integrity": "sha512-QY5PPtSonnGwhhHDNI7+3RvY285c7iuJFFB+lU+oEzMY/gEGJ808owqJsrr8Otd1E/x07po1LkUBmdAc5duPAg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sindresorhus/merge-streams": "^4.0.0", + "cross-spawn": "^7.0.3", + "figures": "^6.1.0", + "get-stream": "^9.0.0", + "human-signals": "^8.0.0", + "is-plain-obj": "^4.1.0", + "is-stream": "^4.0.1", + "npm-run-path": "^6.0.0", + "pretty-ms": "^9.0.0", + "signal-exit": "^4.1.0", + "strip-final-newline": "^4.0.0", "yoctocolors": "^2.0.0" }, "engines": { @@ -2838,6 +3368,14 @@ "node": ">=8.6.0" } }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT", + "peer": true + }, "node_modules/fast-uri": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.3.tgz", @@ -3174,6 +3712,14 @@ "node": ">= 6" } }, + "node_modules/glob-to-regexp": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", + "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", + "dev": true, + "license": "BSD-2-Clause", + "peer": true + }, "node_modules/glob/node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", @@ -3259,10 +3805,11 @@ } }, "node_modules/graceful-fs": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", - "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", - "dev": true + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true, + "license": "ISC" }, "node_modules/handlebars": { "version": "4.7.8", @@ -3839,6 +4386,22 @@ "node": ">= 0.6.0" } }, + "node_modules/jest-worker": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", + "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": ">= 10.13.0" + } + }, "node_modules/jiti": { "version": "1.21.6", "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.6.tgz", @@ -3960,6 +4523,13 @@ "node": "*" } }, + "node_modules/just-extend": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-6.2.0.tgz", + "integrity": "sha512-cYofQu2Xpom82S6qD778jBDpwvvy39s1l/hrYij2u9AMdQcGRpaBu6kY4mVhuno5kJVi1DAz4aiphA2WI1/OAw==", + "dev": true, + "license": "MIT" + }, "node_modules/lines-and-columns": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", @@ -4007,6 +4577,17 @@ "node": ">=4" } }, + "node_modules/loader-runner": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", + "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=6.11.5" + } + }, "node_modules/locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", @@ -4062,6 +4643,13 @@ "integrity": "sha1-+wMJF/hqMTTlvJvsDWngAT3f7bI=", "dev": true }, + "node_modules/lodash.get": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", + "integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==", + "dev": true, + "license": "MIT" + }, "node_modules/lodash.isempty": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/lodash.isempty/-/lodash.isempty-4.4.0.tgz", @@ -4295,6 +4883,13 @@ "semver": "bin/semver.js" } }, + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true, + "license": "ISC" + }, "node_modules/marked": { "version": "12.0.2", "resolved": "https://registry.npmjs.org/marked/-/marked-12.0.2.tgz", @@ -4416,6 +5011,31 @@ "node": ">=16" } }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/mimic-fn": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", @@ -4843,6 +5463,20 @@ "dev": true, "license": "MIT" }, + "node_modules/nise": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/nise/-/nise-6.1.1.tgz", + "integrity": "sha512-aMSAzLVY7LyeM60gvBS423nBmIPP+Wy7St7hsb+8/fc1HmeoHJfLO8CKse4u3BtOZvQLJghYPI2i/1WZrEj5/g==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@sinonjs/commons": "^3.0.1", + "@sinonjs/fake-timers": "^13.0.1", + "@sinonjs/text-encoding": "^0.7.3", + "just-extend": "^6.2.0", + "path-to-regexp": "^8.1.0" + } + }, "node_modules/node-emoji": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/node-emoji/-/node-emoji-2.1.3.tgz", @@ -4872,10 +5506,11 @@ } }, "node_modules/node-releases": { - "version": "2.0.14", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz", - "integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==", - "dev": true + "version": "2.0.18", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.18.tgz", + "integrity": "sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==", + "dev": true, + "license": "MIT" }, "node_modules/normalize-package-data": { "version": "6.0.2", @@ -8117,6 +8752,7 @@ "resolved": "https://registry.npmjs.org/nyc/-/nyc-17.1.0.tgz", "integrity": "sha512-U42vQ4czpKa0QdI1hu950XuNhYqgoM+ZF1HT+VuUHL9hPfDPVvNQyltmMqdE9bUHMVa+8yNbc3QKTj8zQhlVxQ==", "dev": true, + "license": "ISC", "dependencies": { "@istanbuljs/load-nyc-config": "^1.0.0", "@istanbuljs/schema": "^0.1.2", @@ -8634,6 +9270,16 @@ "node": ">=8" } }, + "node_modules/path-to-regexp": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.2.0.tgz", + "integrity": "sha512-TdrF7fW9Rphjq4RjrW0Kp2AW0Ahwu9sRGTkS6bvDi0SCwZlEZYmcfDbEsTz8RVk0EHIS/Vd1bv3JhG+1xZuAyQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=16" + } + }, "node_modules/path-type": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", @@ -8654,10 +9300,11 @@ } }, "node_modules/picocolors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz", - "integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==", - "dev": true + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" }, "node_modules/picomatch": { "version": "2.3.1", @@ -8877,6 +9524,17 @@ "dev": true, "license": "ISC" }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=6" + } + }, "node_modules/queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", @@ -9172,6 +9830,63 @@ "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", "dev": true }, + "node_modules/schema-utils": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", + "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/schema-utils/node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/schema-utils/node_modules/ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "dev": true, + "license": "MIT", + "peer": true, + "peerDependencies": { + "ajv": "^6.9.1" + } + }, + "node_modules/schema-utils/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT", + "peer": true + }, "node_modules/semantic-release": { "version": "24.2.0", "resolved": "https://registry.npmjs.org/semantic-release/-/semantic-release-24.2.0.tgz", @@ -9430,6 +10145,58 @@ "node": ">=4" } }, + "node_modules/sinon": { + "version": "19.0.2", + "resolved": "https://registry.npmjs.org/sinon/-/sinon-19.0.2.tgz", + "integrity": "sha512-euuToqM+PjO4UgXeLETsfQiuoyPXlqFezr6YZDFwHR3t4qaX0fZUe1MfPMznTL5f8BWrVS89KduLdMUsxFCO6g==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@sinonjs/commons": "^3.0.1", + "@sinonjs/fake-timers": "^13.0.2", + "@sinonjs/samsam": "^8.0.1", + "diff": "^7.0.0", + "nise": "^6.1.1", + "supports-color": "^7.2.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/sinon" + } + }, + "node_modules/sinon/node_modules/diff": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-7.0.0.tgz", + "integrity": "sha512-PJWHUb1RFevKCwaFA9RlG5tCd+FO5iRh9A8HEtkmBH2Li03iJriB6m6JIN4rGz3K3JLawI7/veA1xzRKP6ISBw==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/sinon/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/sinon/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/skin-tone": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/skin-tone/-/skin-tone-2.0.0.tgz", @@ -9466,6 +10233,17 @@ "node": ">=0.10.0" } }, + "node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, "node_modules/spawn-error-forwarder": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/spawn-error-forwarder/-/spawn-error-forwarder-1.0.0.tgz", @@ -9719,6 +10497,16 @@ "node": ">=8" } }, + "node_modules/tapable": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", + "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/tcomb": { "version": "3.2.29", "resolved": "https://registry.npmjs.org/tcomb/-/tcomb-3.2.29.tgz", @@ -9791,6 +10579,62 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/terser": { + "version": "5.36.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.36.0.tgz", + "integrity": "sha512-IYV9eNMuFAV4THUspIRXkLakHnV6XO7FEdtKjf/mDyrnqUg9LnlOn6/RwRvM9SZjR4GUq8Nk8zj67FzVARr74w==", + "dev": true, + "license": "BSD-2-Clause", + "peer": true, + "dependencies": { + "@jridgewell/source-map": "^0.3.3", + "acorn": "^8.8.2", + "commander": "^2.20.0", + "source-map-support": "~0.5.20" + }, + "bin": { + "terser": "bin/terser" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/terser-webpack-plugin": { + "version": "5.3.10", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.10.tgz", + "integrity": "sha512-BKFPWlPDndPs+NGGCr1U59t0XScL5317Y0UReNrHaw9/FwhPENlq6bfgs+4yPfyP51vqC1bQ4rp1EfXW5ZSH9w==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.20", + "jest-worker": "^27.4.5", + "schema-utils": "^3.1.1", + "serialize-javascript": "^6.0.1", + "terser": "^5.26.0" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.1.0" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "uglify-js": { + "optional": true + } + } + }, "node_modules/test-exclude": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", @@ -9948,6 +10792,167 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/ts-loader": { + "version": "9.5.1", + "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-9.5.1.tgz", + "integrity": "sha512-rNH3sK9kGZcH9dYzC7CewQm4NtxJTjSEVRJ2DyBZR7f8/wcta+iV44UPCXc5+nzDzivKtlzV6c9P4e+oFhDLYg==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.1.0", + "enhanced-resolve": "^5.0.0", + "micromatch": "^4.0.0", + "semver": "^7.3.4", + "source-map": "^0.7.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "typescript": "*", + "webpack": "^5.0.0" + } + }, + "node_modules/ts-loader/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/ts-loader/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/ts-loader/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/ts-loader/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/ts-loader/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ts-loader/node_modules/source-map": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", + "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">= 8" + } + }, + "node_modules/ts-loader/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ts-node": { + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", + "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" + }, + "bin": { + "ts-node": "dist/bin.js", + "ts-node-cwd": "dist/bin-cwd.js", + "ts-node-esm": "dist/bin-esm.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js", + "ts-script": "dist/bin-script-deprecated.js" + }, + "peerDependencies": { + "@swc/core": ">=1.2.50", + "@swc/wasm": ">=1.2.50", + "@types/node": "*", + "typescript": ">=2.7" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "@swc/wasm": { + "optional": true + } + } + }, + "node_modules/ts-node/node_modules/diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, "node_modules/type-detect": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", @@ -9981,7 +10986,6 @@ "integrity": "sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==", "dev": true, "license": "Apache-2.0", - "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -10068,9 +11072,9 @@ } }, "node_modules/update-browserslist-db": { - "version": "1.0.16", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.16.tgz", - "integrity": "sha512-KVbTxlBYlckhF5wgfyZXTWnMn7MMZjMu9XG8bPlliUOP9ThaF4QnhP8qrjrH7DRzHfSk0oQv1wToW+iA5GajEQ==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.1.tgz", + "integrity": "sha512-R8UzCaa9Az+38REPiJ1tXlImTJXlVfgHZsglwBD/k6nj76ctsH1E3q4doGrukiLQd3sGQYu56r5+lo5r94l29A==", "dev": true, "funding": [ { @@ -10086,9 +11090,10 @@ "url": "https://github.com/sponsors/ai" } ], + "license": "MIT", "dependencies": { - "escalade": "^3.1.2", - "picocolors": "^1.0.1" + "escalade": "^3.2.0", + "picocolors": "^1.1.0" }, "bin": { "update-browserslist-db": "cli.js" @@ -10097,6 +11102,17 @@ "browserslist": ">= 4.21.0" } }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "license": "BSD-2-Clause", + "peer": true, + "dependencies": { + "punycode": "^2.1.0" + } + }, "node_modules/url-join": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/url-join/-/url-join-5.0.0.tgz", @@ -10124,6 +11140,13 @@ "uuid": "bin/uuid" } }, + "node_modules/v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", + "dev": true, + "license": "MIT" + }, "node_modules/validate-npm-package-license": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", @@ -10145,6 +11168,80 @@ "node": ">= 0.10" } }, + "node_modules/watchpack": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.2.tgz", + "integrity": "sha512-TnbFSbcOCcDgjZ4piURLCbJ3nJhznVh9kw6F6iokjiFPl8ONxe9A6nMDVXDiNbrSfLILs6vB07F7wLBrwPYzJw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.1.2" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/webpack": { + "version": "5.96.1", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.96.1.tgz", + "integrity": "sha512-l2LlBSvVZGhL4ZrPwyr8+37AunkcYj5qh8o6u2/2rzoPc8gxFJkLj1WxNgooi9pnoc06jh0BjuXnamM4qlujZA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@types/eslint-scope": "^3.7.7", + "@types/estree": "^1.0.6", + "@webassemblyjs/ast": "^1.12.1", + "@webassemblyjs/wasm-edit": "^1.12.1", + "@webassemblyjs/wasm-parser": "^1.12.1", + "acorn": "^8.14.0", + "browserslist": "^4.24.0", + "chrome-trace-event": "^1.0.2", + "enhanced-resolve": "^5.17.1", + "es-module-lexer": "^1.2.1", + "eslint-scope": "5.1.1", + "events": "^3.2.0", + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.2.11", + "json-parse-even-better-errors": "^2.3.1", + "loader-runner": "^4.2.0", + "mime-types": "^2.1.27", + "neo-async": "^2.6.2", + "schema-utils": "^3.2.0", + "tapable": "^2.1.1", + "terser-webpack-plugin": "^5.3.10", + "watchpack": "^2.4.1", + "webpack-sources": "^3.2.3" + }, + "bin": { + "webpack": "bin/webpack.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependenciesMeta": { + "webpack-cli": { + "optional": true + } + } + }, + "node_modules/webpack-sources": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz", + "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=10.13.0" + } + }, "node_modules/which-module": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", @@ -10323,6 +11420,16 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", diff --git a/package.json b/package.json index f80dd4a..bc29921 100644 --- a/package.json +++ b/package.json @@ -2,13 +2,16 @@ "name": "@namecheap/serialize-javascript", "version": "0.0.0", "description": "Serialize JavaScript to a superset of JSON that includes regular expressions and functions.", - "main": "index.js", + "main": "./lib/index.js", + "browser": "./lib/browser.js", + "types": "./lib/index.d.ts", "publishConfig": { "access": "public" }, "scripts": { - "benchmark": "node -v && node test/benchmark/serialize.js", - "test": "nyc mocha './{,!(node_modules)/**}/*.spec.js' --forbid-only --forbid-pending --reporter=\"mochawesome\" --reporter-options=\"reportDir=./test-results/mocha/,reportFilename=index.html\"", + "build": "tsc", + "benchmark": "npm run build && node -v && node test/benchmark/serialize.js", + "test": "nyc mocha './{,!(node_modules)/**}/*.spec.ts' --forbid-only --forbid-pending --reporter=\"mochawesome\" --reporter-options=\"reportDir=./test-results/mocha/,reportFilename=index.html\"", "prepare": "husky" }, "repository": { @@ -28,17 +31,23 @@ "url": "https://github.com/namecheap/serialize-javascript/issues" }, "homepage": "https://github.com/namecheap/serialize-javascript", - "dependencies": {}, "devDependencies": { + "@commitlint/cli": "^19.5.0", + "@commitlint/config-conventional": "^19.5.0", + "@types/mocha": "^10.0.9", + "@types/sinon": "^17.0.3", "benchmark": "^2.1.4", "chai": "^4.1.0", + "husky": "^9.1.6", "mocha": "^10.7.3", "mochawesome": "^7.1.3", - "nyc": "^17.0.0", - "@commitlint/cli": "^19.5.0", - "@commitlint/config-conventional": "^19.5.0", - "husky": "^9.1.6", - "semantic-release": "^24.1.3" + "nyc": "^17.1.0", + "semantic-release": "^24.1.3", + "sinon": "^19.0.2", + "source-map-support": "^0.5.21", + "ts-loader": "^9.5.1", + "ts-node": "^10.9.2", + "typescript": "^5.6.3" }, "overrides": { "conventional-changelog-conventionalcommits": ">= 8.0.0" diff --git a/src/Serializer/BidirectionalMap.ts b/src/Serializer/BidirectionalMap.ts new file mode 100644 index 0000000..e91fdbb --- /dev/null +++ b/src/Serializer/BidirectionalMap.ts @@ -0,0 +1,65 @@ +export class BidirectionalMap { + private keyToValue: Map = new Map(); + private valueToKey: Map = new Map(); + + constructor(entries: [K, V][] = []) { + for (const [key, value] of entries) { + this.set(key, value); + } + } + + set(key: K, value: V): void { + if (this.keyToValue.has(key)) { + const oldValue = this.keyToValue.get(key)!; + this.valueToKey.delete(oldValue); + } + + if (this.valueToKey.has(value)) { + const oldKey = this.valueToKey.get(value)!; + this.keyToValue.delete(oldKey); + } + + this.keyToValue.set(key, value); + this.valueToKey.set(value, key); + } + + getByKey(key: K): V | undefined { + return this.keyToValue.get(key); + } + + getByValue(value: V): K | undefined { + return this.valueToKey.get(value); + } + + deleteByKey(key: K): void { + const value = this.keyToValue.get(key); + if (value !== undefined) { + this.keyToValue.delete(key); + this.valueToKey.delete(value); + } + } + + deleteByValue(value: V): void { + const key = this.valueToKey.get(value); + if (key !== undefined) { + this.valueToKey.delete(value); + this.keyToValue.delete(key); + } + } + + hasKey(key: K): boolean { + return this.keyToValue.has(key); + } + + hasValue(value: V): boolean { + return this.valueToKey.has(value); + } + + keys(): MapIterator { + return this.valueToKey.values(); + } + + values(): MapIterator { + return this.keyToValue.values(); + } +} diff --git a/src/Serializer/PlaceholderSerializer.ts b/src/Serializer/PlaceholderSerializer.ts new file mode 100644 index 0000000..35874fc --- /dev/null +++ b/src/Serializer/PlaceholderSerializer.ts @@ -0,0 +1,283 @@ +import { BidirectionalMap } from './BidirectionalMap'; +import { deleteFunctions } from './deleteFunctions'; +import { escapeUnsafeChars } from './escapeUnsafeChars'; +import { SerializerOptions } from './interfaces/SerializerOptions'; + +type PlaceholderedValues = { + url: URL[]; + date: Date[]; + array: any[]; + undefined: any[]; + set: Set[]; + regExp: RegExp[]; + bigint: BigInt[]; + infinity: any[]; + map: Map[]; + function: Function[]; +}; + +type StringifiedWithPlaceholders = { + stringified: string; + placeholdered: PlaceholderedValues; +}; + +type ValueTypes = 'regExp' | 'date' | 'map' | 'set' | 'array' |'url' | 'function' | 'undefined' | 'infinity' | 'bigint'; + +const RESERVED_SYMBOLS = ['*', 'async']; + +const IS_ARROW_FUNCTION = /.*?=>.*?/; +const IS_PURE_FUNCTION = /function.*?\(/; +const UNSAFE_CHARS_REGEXP = /[<>\/\u2028\u2029]/g; +const IS_NATIVE_CODE_REGEXP = /\{\s*\[native code\]\s*\}/g; + +const typesPlaceholderIdentifier: BidirectionalMap = new BidirectionalMap([ + ['map', 'M'], + ['url', 'L'], + ['set', 'S'], + ['date', 'D'], + ['array', 'A'], + ['bigint', 'B'], + ['regExp', 'R'], + ['infinity', 'I'], + ['function', 'F'], + ['undefined', 'U'], +]); + +const objectTypeMap = new Map([ + [Map, 'map'], + [Set, 'set'], + [URL, 'url'], + [Date, 'date'], + [Array, 'array'], + [RegExp, 'regExp'], +]); + +export class PlaceholderSerializer { + private PLACE_HOLDER_REGEXP: RegExp; + + constructor(private UID: string) { + const placeholderIdentifiers = Array.from(typesPlaceholderIdentifier.values()); + this.PLACE_HOLDER_REGEXP = new RegExp(`(\\\\)?"@__(${placeholderIdentifiers.join('|')})-${this.UID}-(\\d+)__@"`, 'g'); + } + + private getValueTypeName(value: any): ValueTypes | string { + const valueType = typeof value; + + if (valueType === 'object' && value !== null) { + for (const [constructor, typeName] of objectTypeMap) { + if (value instanceof constructor) { + return typeName; + } + } + } + + if (valueType === 'number' && !isNaN(value) && !isFinite(value)) { + return 'infinity'; + } + + return valueType; + } + + private generatePlaceholder(placeholderIdentifier, valueIndex): string { + return `@__${placeholderIdentifier}-${this.UID}-${valueIndex}__@`; + } + + private stringifyWithPlaceholders(obj, options: SerializerOptions): StringifiedWithPlaceholders { + const placeholdered: Record> = { + map: [], + set: [], + url: [], + date: [], + array: [], + bigint: [], + regExp: [], + function: [], + infinity: [], + undefined: [], + }; + + const self = this; + + // Returns placeholders for functions and regexps (identified by index) + // which are later replaced by their string representation. + function replacer(key, value) { + // For nested function + if (options?.ignoreFunction) { + deleteFunctions(value); + } + + if (!value && value !== undefined && value !== BigInt(0)) { + return value; + } + + // If the value is an object w/ a toJSON method, toJSON is called before + // the replacer runs, so we use this[key] to get the non-toJSONed value. + // @ts-ignore + const originalValue = this[key]; + const typeName = self.getValueTypeName(originalValue); + + const placeholderedValueDestination: Array | undefined = placeholdered[typeName]; + const typePlaceholderIndentifier: string | undefined = typesPlaceholderIdentifier.getByKey(typeName as ValueTypes); + + if (typeName === 'array') { + var isSparse = originalValue.filter(() => true).length !== originalValue.length; + + if (isSparse) { + return self.generatePlaceholder(typePlaceholderIndentifier, placeholdered.array.push(originalValue) - 1); + } + } else if (typePlaceholderIndentifier && placeholderedValueDestination) { + return self.generatePlaceholder(typePlaceholderIndentifier, (placeholderedValueDestination.push(originalValue) - 1)); + } + + return value; + } + + let stringified; + + // Creates a JSON string representation of the value. + // NOTE: Node 0.12 goes into slow mode with extra JSON.stringify() args. + if (options?.isJSON && !options.space) { + stringified = JSON.stringify(obj); + } else { + stringified = JSON.stringify(obj, options?.isJSON ? undefined : replacer, options?.space); + } + + return { + stringified, + placeholdered, + }; + } + + public serializeFunction(fn: Function): string { + const serializedFn = fn.toString(); + + if (IS_NATIVE_CODE_REGEXP.test(serializedFn)) { + throw new TypeError('Serializing native function: ' + fn.name); + } + + // pure functions, example: {key: function() {}} + if (IS_PURE_FUNCTION.test(serializedFn)) { + return serializedFn; + } + + // arrow functions, example: arg1 => arg1+5 + if (IS_ARROW_FUNCTION.test(serializedFn)) { + return serializedFn; + } + + const argsStartsAt = serializedFn.indexOf('('); + const definition = serializedFn.substr(0, argsStartsAt) + .trim() + .split(' ') + .filter((value) => value.length > 0); + + const nonReservedSymbols = definition.filter((value) => RESERVED_SYMBOLS.indexOf(value) === -1); + + // enhanced literal objects, example: {key() {}} + if (nonReservedSymbols.length > 0) { + return (definition.indexOf('async') > -1 ? 'async ' : '') + 'function' + + (definition.join('').indexOf('*') > -1 ? '*' : '') + + serializedFn.substr(argsStartsAt); + } + + // arrow functions + return serializedFn; + } + + public serialize(obj, opts: SerializerOptions | number | string = {}): string { + let options: SerializerOptions = {}; + + // Backwards-compatibility for `space` as the second argument. + if (typeof opts === 'number' || typeof opts === 'string') { + options = { space: opts }; + } else { + options = opts; + } + + // Check if the parameter is function + if (options?.ignoreFunction && typeof obj === "function") { + obj = undefined; + } + // Protects against `JSON.stringify()` returning `undefined`, by serializing + // to the literal string: "undefined". + if (obj === undefined) { + return String(obj); + } + + const { + placeholdered, + stringified, + } = this.stringifyWithPlaceholders(obj, options); + + // Protects against `JSON.stringify()` returning `undefined`, by serializing + // to the literal string: "undefined". + if (typeof stringified !== 'string') { + return String(stringified); + } + + let str = stringified; + + // Replace unsafe HTML and invalid JavaScript line terminator chars with + // their safe Unicode char counterpart. This _must_ happen before the + // regexps and functions are serialized and added back to the string. + if (options?.unsafe !== true) { + str = str.replace(UNSAFE_CHARS_REGEXP, escapeUnsafeChars); + } + + const hasPlaceholderedValues = Object.keys(placeholdered).some((typeName) => placeholdered[typeName].length > 0); + + if (!hasPlaceholderedValues) { + return str; + } + + // Replaces all occurrences of function, regexp, date, map and set placeholders in the + // JSON string with their string representations. If the original value can + // not be found, then `undefined` is used. + return str.replace(this.PLACE_HOLDER_REGEXP, (match, backSlash, placeholderIdentifier, valueIndex) => { + // The placeholder may not be preceded by a backslash. This is to prevent + // replacing things like `"a\"@__R--0__@"` and thus outputting + // invalid JS. + if (backSlash) { + return match; + } + + const typeName = typesPlaceholderIdentifier.getByValue(placeholderIdentifier); + + switch (typeName) { + case 'date': + return `new Date("${placeholdered.date[valueIndex].toISOString()}")`; + + case 'regExp': + return `new RegExp(${this.serialize(placeholdered.regExp[valueIndex].source)}, "${placeholdered.regExp[valueIndex].flags}")`; + + case 'map': + return `new Map(${this.serialize(Array.from(placeholdered.map[valueIndex].entries()), options)})`; + + case 'set': + return `new Set(${this.serialize(Array.from(placeholdered.set[valueIndex].values()), options)})`; + + case 'array': + return `Array.prototype.slice.call(${this.serialize({ + length: placeholdered.array[valueIndex].length, + ...placeholdered.array[valueIndex] + }, options)})`; + + case 'undefined': + return 'undefined'; + + case 'infinity': + return placeholdered.infinity[valueIndex]; + + case 'bigint': + return `BigInt("${placeholdered.bigint[valueIndex]}")`; + + case 'url': + return `new URL(${this.serialize(placeholdered.url[valueIndex].toString(), options)})`; + + default: + const fn = placeholdered.function[valueIndex]; + return this.serializeFunction(fn); + } + }); + } +} diff --git a/src/Serializer/deleteFunctions.ts b/src/Serializer/deleteFunctions.ts new file mode 100644 index 0000000..bbbffac --- /dev/null +++ b/src/Serializer/deleteFunctions.ts @@ -0,0 +1,7 @@ +export function deleteFunctions(obj: any): void { + for (const key in obj) { + if (typeof obj[key] === 'function') { + delete obj[key]; + } + } +} diff --git a/src/Serializer/escapeUnsafeChars.ts b/src/Serializer/escapeUnsafeChars.ts new file mode 100644 index 0000000..305e36d --- /dev/null +++ b/src/Serializer/escapeUnsafeChars.ts @@ -0,0 +1,11 @@ +const ESCAPED_CHARS: Record = { + '<': '\\u003C', + '>': '\\u003E', + '/': '\\u002F', + '\u2028': '\\u2028', + '\u2029': '\\u2029', +}; + +export function escapeUnsafeChars(unsafeChar: string): string { + return ESCAPED_CHARS[unsafeChar]; +} diff --git a/src/Serializer/index.ts b/src/Serializer/index.ts new file mode 100644 index 0000000..ec57167 --- /dev/null +++ b/src/Serializer/index.ts @@ -0,0 +1,3 @@ +import { PlaceholderSerializer as Serializer } from './PlaceholderSerializer'; + +export { Serializer }; diff --git a/src/Serializer/interfaces/SerializerOptions.ts b/src/Serializer/interfaces/SerializerOptions.ts new file mode 100644 index 0000000..8881883 --- /dev/null +++ b/src/Serializer/interfaces/SerializerOptions.ts @@ -0,0 +1,6 @@ +export type SerializerOptions = { + isJSON?: boolean; + ignoreFunction?: boolean; + space?: number | string; + unsafe?: boolean; +}; diff --git a/src/browser.ts b/src/browser.ts new file mode 100644 index 0000000..31acefb --- /dev/null +++ b/src/browser.ts @@ -0,0 +1,10 @@ +import { Serializer } from './Serializer'; +import { generateUUID } from './generateUUID'; + +const uuid = generateUUID(window.crypto); +const serializer = new Serializer(uuid); + +const serialize = serializer.serialize.bind(serializer); + +export { serialize }; +export default serialize; diff --git a/src/generateUUID.ts b/src/generateUUID.ts new file mode 100644 index 0000000..807326e --- /dev/null +++ b/src/generateUUID.ts @@ -0,0 +1,19 @@ +const UID_LENGTH: number = 16; + +type RandomGenerator = { + randomUUID?: () => string; + getRandomValues: (Uint8Array) => Uint8Array; +}; + +export function generateUUID(randomGenerator: RandomGenerator): string { + if (randomGenerator.randomUUID) { + const uuid = randomGenerator.randomUUID(); + + return uuid.replace(/-/g, ''); + } + + const randomValues = new Uint8Array(UID_LENGTH); + const bytes = randomGenerator.getRandomValues(randomValues); + + return Array.from(bytes).map(byte => byte.toString(16)).join(''); +} diff --git a/src/index.ts b/src/index.ts new file mode 100644 index 0000000..6d78dea --- /dev/null +++ b/src/index.ts @@ -0,0 +1,11 @@ +import crypto from 'crypto'; +import { Serializer } from './Serializer'; +import { generateUUID } from './generateUUID'; + +const uuid = generateUUID(crypto); +const serializer = new Serializer(uuid); + +const serialize = serializer.serialize.bind(serializer); + +export { serialize }; +export default serialize; diff --git a/test/.mocharc.json b/test/.mocharc.json new file mode 100644 index 0000000..785c30c --- /dev/null +++ b/test/.mocharc.json @@ -0,0 +1,8 @@ +{ + "check-leaks": true, + "use_strict": true, + "require": "ts-node/register", + "recursive": true, + "extension": ["ts"], + "spec": "test/**/*.ts" +} diff --git a/test/benchmark/serialize.js b/test/benchmark/serialize.js index 6f6b738..b8a0da9 100644 --- a/test/benchmark/serialize.js +++ b/test/benchmark/serialize.js @@ -1,7 +1,7 @@ 'use strict'; var Benchmark = require('benchmark'); -var serialize = require('../../'); +var { serialize } = require('../../lib/'); var suiteConfig = { onStart: function (e) { diff --git a/test/mocha.opts b/test/mocha.opts deleted file mode 100644 index 4ecb0e6..0000000 --- a/test/mocha.opts +++ /dev/null @@ -1,2 +0,0 @@ ---check-leaks ---use_strict diff --git a/test/unit/Serializer/BidirectionalMap.spec.ts b/test/unit/Serializer/BidirectionalMap.spec.ts new file mode 100644 index 0000000..139bb31 --- /dev/null +++ b/test/unit/Serializer/BidirectionalMap.spec.ts @@ -0,0 +1,94 @@ +import { expect } from 'chai'; +import { BidirectionalMap } from '../../../src/Serializer/BidirectionalMap'; + +describe('BidirectionalMap', () => { + let biMap: BidirectionalMap; + + beforeEach(() => { + biMap = new BidirectionalMap(); + }); + + it('should initialize with empty map when no entries are provided', () => { + expect(biMap.hasKey('key')).to.be.false; + expect(biMap.hasValue(42)).to.be.false; + }); + + it('should initialize with entries when provided', () => { + const entries: [string, number][] = [['a', 1], ['b', 2]]; + biMap = new BidirectionalMap(entries); + + expect(biMap.getByKey('a')).to.equal(1); + expect(biMap.getByValue(2)).to.equal('b'); + }); + + it('should set new key-value pairs correctly', () => { + biMap.set('a', 1); + biMap.set('b', 2); + + expect(biMap.getByKey('a')).to.equal(1); + expect(biMap.getByKey('b')).to.equal(2); + expect(biMap.getByValue(1)).to.equal('a'); + expect(biMap.getByValue(2)).to.equal('b'); + }); + + it('should update existing key-value pairs correctly', () => { + biMap.set('a', 1); + biMap.set('a', 2); + + expect(biMap.getByKey('a')).to.equal(2); + expect(biMap.getByValue(1)).to.be.undefined; + expect(biMap.getByValue(2)).to.equal('a'); + }); + + it('should update existing key-value pairs correctly', () => { + biMap.set('a', 2); + biMap.set('b', 2); + + expect(biMap.getByKey('b')).to.equal(2); + expect(biMap.getByValue(2)).to.equal('b'); + }); + + it('should delete by key', () => { + biMap.set('a', 1); + biMap.deleteByKey('a'); + + expect(biMap.getByKey('a')).to.be.undefined; + expect(biMap.getByValue(1)).to.be.undefined; + }); + + it('should delete by value', () => { + biMap.set('a', 1); + biMap.deleteByValue(1); + + expect(biMap.getByKey('a')).to.be.undefined; + expect(biMap.getByValue(1)).to.be.undefined; + }); + + it('should return true if a key exists', () => { + biMap.set('a', 1); + expect(biMap.hasKey('a')).to.be.true; + expect(biMap.hasKey('b')).to.be.false; + }); + + it('should return true if a value exists', () => { + biMap.set('a', 1); + expect(biMap.hasValue(1)).to.be.true; + expect(biMap.hasValue(2)).to.be.false; + }); + + it('should return the correct iterator for keys', () => { + biMap.set('a', 1); + biMap.set('b', 2); + const keys = Array.from(biMap.keys()); + + expect(keys).to.deep.equal(['a', 'b']); + }); + + it('should return the correct iterator for values', () => { + biMap.set('a', 1); + biMap.set('b', 2); + const values = Array.from(biMap.values()); + + expect(values).to.deep.equal([1, 2]); + }); +}); diff --git a/test/unit/serialize.spec.js b/test/unit/Serializer/PlaceholderSerializer.spec.ts similarity index 93% rename from test/unit/serialize.spec.js rename to test/unit/Serializer/PlaceholderSerializer.spec.ts index a97718b..02a951d 100644 --- a/test/unit/serialize.spec.js +++ b/test/unit/Serializer/PlaceholderSerializer.spec.ts @@ -1,14 +1,25 @@ -/* global describe, it, beforeEach */ -'use strict'; +import { expect } from 'chai'; +import { PlaceholderSerializer } from '../../../src/Serializer/PlaceholderSerializer'; -var serialize = require('../../'), - expect = require('chai').expect; +describe('serialize(obj)', function () { + let serialize; + + before(() => { + const uuid = '00000000000000000000000000000000' + const serializer = new PlaceholderSerializer(uuid); + serialize = serializer.serialize.bind(serializer); + }); -describe('serialize( obj )', function () { it('should be a function', function () { expect(serialize).to.be.a('function'); }); + describe('non-placeholderable types', function () { + it('should serialize to a string', function () { + expect(serialize(new ArrayBuffer(3))).to.be.a('string').equal('{}'); + }); + }); + describe('undefined', function () { it('should serialize `undefined` to a string', function () { expect(serialize()).to.be.a('string').equal('undefined'); @@ -69,7 +80,7 @@ describe('serialize( obj )', function () { describe('functions', function () { it('should serialize annonymous functions', function () { var fn = function () {}; - expect(serialize(fn)).to.be.a('string').equal('function () {}'); + expect(serialize(fn)).to.be.a('string').equal('function () { }'); }); it('should deserialize annonymous functions', function () { @@ -79,7 +90,7 @@ describe('serialize( obj )', function () { it('should serialize named functions', function () { function fn() {} - expect(serialize(fn)).to.be.a('string').equal('function fn() {}'); + expect(serialize(fn)).to.be.a('string').equal('function fn() { }'); }); it('should deserialize named functions', function () { @@ -89,12 +100,12 @@ describe('serialize( obj )', function () { }); it('should serialize functions with arguments', function () { - function fn(arg1, arg2) {} - expect(serialize(fn)).to.equal('function fn(arg1, arg2) {}'); + function fn(arg1, arg2) {}; + expect(serialize(fn)).to.equal('function fn(arg1, arg2) { }'); }); it('should deserialize functions with arguments', function () { - var fn; eval('fn = ' + serialize(function (arg1, arg2) {})); + var fn; eval('fn = ' + serialize(function (arg1, arg2) { })); expect(fn).to.be.a('function'); expect(fn.length).to.equal(2); }); @@ -134,8 +145,8 @@ describe('serialize( obj )', function () { }); it('should serialize functions that contain dates', function () { - function fn(arg1) {return new Date('2016-04-28T22:02:17.156Z')}; - expect(serialize(fn)).to.be.a('string').equal('function fn(arg1) {return new Date(\'2016-04-28T22:02:17.156Z\')}'); + function fn(arg1) { return new Date('2016-04-28T22:02:17.156Z'); }; + expect(serialize(fn)).to.be.a('string').equal('function fn(arg1) { return new Date(\'2016-04-28T22:02:17.156Z\'); }'); }); it('should deserialize functions that contain dates', function () { @@ -145,8 +156,8 @@ describe('serialize( obj )', function () { }); it('should serialize functions that return other functions', function () { - function fn() {return function(arg1) {return arg1 + 5}}; - expect(serialize(fn)).to.be.a('string').equal('function fn() {return function(arg1) {return arg1 + 5}}'); + function fn() { return function(arg1) { return arg1 + 5; }; }; + expect(serialize(fn)).to.be.a('string').equal('function fn() { return function (arg1) { return arg1 + 5; }; }'); }); it('should deserialize functions that return other functions', function () { @@ -157,9 +168,10 @@ describe('serialize( obj )', function () { }); describe('arrow-functions', function () { + it('should serialize arrow functions', function () { var fn = () => {}; - expect(serialize(fn)).to.be.a('string').equal('() => {}'); + expect(serialize(fn)).to.be.a('string').equal('() => { }'); }); it('should deserialize arrow functions', function () { @@ -169,8 +181,8 @@ describe('serialize( obj )', function () { }); it('should serialize arrow functions with one argument', function () { - var fn = arg1 => {} - expect(serialize(fn)).to.be.a('string').equal('arg1 => {}'); + var fn = arg1 => {}; + expect(serialize(fn)).to.be.a('string').equal('arg1 => { }'); }); it('should deserialize arrow functions with one argument', function () { @@ -181,7 +193,7 @@ describe('serialize( obj )', function () { it('should serialize arrow functions with multiple arguments', function () { var fn = (arg1, arg2) => {} - expect(serialize(fn)).to.equal('(arg1, arg2) => {}'); + expect(serialize(fn)).to.equal('(arg1, arg2) => { }'); }); it('should deserialize arrow functions with multiple arguments', function () { @@ -213,7 +225,7 @@ describe('serialize( obj )', function () { it('should deserialize enhanced literal objects', function () { var obj; - eval('obj = ' + serialize({ foo: () => { return true; }, + eval('obj = ' + serialize({ foo: () => { return true; }, bar: arg1 => { return true; }, baz: (arg1, arg2) => { return true; } @@ -226,8 +238,8 @@ describe('serialize( obj )', function () { it('should serialize arrow functions with added properties', function () { var fn = () => {}; - fn.property1 = 'a string' - expect(serialize(fn)).to.be.a('string').equal('() => {}'); + (fn as any).property1 = 'a string' + expect(serialize(fn)).to.be.a('string').equal('() => { }'); }); it('should deserialize arrow functions with added properties', function () { @@ -237,8 +249,8 @@ describe('serialize( obj )', function () { }); it('should serialize arrow functions that return other functions', function () { - var fn = arg1 => { return arg2 => arg1 + arg2 }; - expect(serialize(fn)).to.be.a('string').equal('arg1 => { return arg2 => arg1 + arg2 }'); + var fn = arg1 => { return arg2 => arg1 + arg2; }; + expect(serialize(fn)).to.be.a('string').equal('arg1 => { return arg2 => arg1 + arg2; }'); }); it('should deserialize arrow functions that return other functions', function () { @@ -333,11 +345,12 @@ describe('serialize( obj )', function () { describe('maps', function () { it('should serialize maps', function () { var regexKey = /.*/; + var m = new Map([ ['a', 123], [regexKey, 456], [Infinity, 789] - ]); + ] as any); expect(serialize(m)).to.be.a('string').equal('new Map([["a",123],[new RegExp(".*", ""),456],[Infinity,789]])'); expect(serialize({t: [m]})).to.be.a('string').equal('{"t":[new Map([["a",123],[new RegExp(".*", ""),456],[Infinity,789]])]}'); }); @@ -347,7 +360,7 @@ describe('serialize( obj )', function () { ['a', 123], [null, 456], [Infinity, 789] - ]))); + ] as any))); expect(m).to.be.a('Map'); expect(m.get(null)).to.equal(456); }); @@ -383,7 +396,7 @@ describe('serialize( obj )', function () { var a = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; delete a[0]; a.length = 3; - a[5] = "wat" + (a as any)[5] = "wat" expect(serialize(a)).to.be.a('string').equal('Array.prototype.slice.call({"1":2,"2":3,"5":"wat","length":6})'); expect(serialize({t: [a]})).to.be.a('string').equal('{"t":[Array.prototype.slice.call({"1":2,"2":3,"5":"wat","length":6})]}'); }); @@ -392,7 +405,7 @@ describe('serialize( obj )', function () { var a = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; delete a[0]; a.length = 3; - a[5] = "wat" + (a as any)[5] = "wat" var b = eval(serialize(a)); expect(b).to.be.a('Array').deep.equal([ , 2, 3, , , 'wat' ]); }); @@ -557,7 +570,7 @@ describe('serialize( obj )', function () { describe('placeholders', function() { it('should not be replaced within string literals', function () { // Since we made the UID deterministic this should always be the placeholder - var fakePlaceholder = '"@__R-0000000000000000-0__@'; + var fakePlaceholder = '"@__R-00000000000000000000000000000000-0__@'; var serialized = serialize({bar: /1/i, foo: fakePlaceholder}, {uid: 'foo'}); var obj = eval('(' + serialized + ')'); expect(obj).to.be.a('Object'); diff --git a/test/unit/Serializer/deleteFunctions.spec.ts b/test/unit/Serializer/deleteFunctions.spec.ts new file mode 100644 index 0000000..b985ecb --- /dev/null +++ b/test/unit/Serializer/deleteFunctions.spec.ts @@ -0,0 +1,69 @@ +import { expect } from 'chai'; +import { deleteFunctions } from '../../../src/Serializer/deleteFunctions'; + +describe('deleteFunctions', () => { + it('should delete function properties from the object', () => { + const obj = { + a: 1, + b: () => {}, + c: 'hello', + d: function () {}, + }; + + deleteFunctions(obj); + + expect(obj).to.have.property('a').that.equals(1); + expect(obj).to.have.property('c').that.equals('hello'); + expect(obj).to.not.have.property('b'); + expect(obj).to.not.have.property('d'); + }); + + it('should not modify an object without function properties', () => { + const obj = { + a: 1, + b: 'test', + c: { nested: true }, + d: [1, 2, 3], + }; + + deleteFunctions(obj); + + expect(obj).to.deep.equal({ + a: 1, + b: 'test', + c: { nested: true }, + d: [1, 2, 3], + }); + }); + + it('should handle an empty object without error', () => { + const obj: Record = {}; + + deleteFunctions(obj); + + expect(obj).to.deep.equal({}); + }); + + it('should handle objects with nested functions without deleting nested functions', () => { + const d = function() {}; + + const obj = { + a: 1, + b: () => {}, + c: { + d, + e: 'nested', + }, + }; + + deleteFunctions(obj); + + expect(obj).to.have.property('a').that.equals(1); + expect(obj).to.not.have.property('b'); + + expect(obj.c).to.deep.equal({ + d, + e: 'nested', + }); + }); +}); diff --git a/test/unit/Serializer/escapeUnsafeChars.spec.ts b/test/unit/Serializer/escapeUnsafeChars.spec.ts new file mode 100644 index 0000000..d241ec0 --- /dev/null +++ b/test/unit/Serializer/escapeUnsafeChars.spec.ts @@ -0,0 +1,51 @@ +import { expect } from 'chai'; +import { escapeUnsafeChars } from '../../../src/Serializer/escapeUnsafeChars'; + +describe('escapeUnsafeChars', () => { + it('should return the correct escaped value for <', () => { + const input = '<'; + const expectedOutput = '\\u003C'; + + const result = escapeUnsafeChars(input); + expect(result).to.equal(expectedOutput); + }); + + it('should return the correct escaped value for >', () => { + const input = '>'; + const expectedOutput = '\\u003E'; + + const result = escapeUnsafeChars(input); + expect(result).to.equal(expectedOutput); + }); + + it('should return the correct escaped value for /', () => { + const input = '/'; + const expectedOutput = '\\u002F'; + + const result = escapeUnsafeChars(input); + expect(result).to.equal(expectedOutput); + }); + + it('should return the correct escaped value for \u2028', () => { + const input = '\u2028'; // Unicode Line Separator character + const expectedOutput = '\\u2028'; + + const result = escapeUnsafeChars(input); + expect(result).to.equal(expectedOutput); + }); + + it('should return the correct escaped value for \u2029', () => { + const input = '\u2029'; // Unicode Paragraph Separator character + const expectedOutput = '\\u2029'; + + const result = escapeUnsafeChars(input); + expect(result).to.equal(expectedOutput); + }); + + it('should return undefined for characters not in ESCAPED_CHARS', () => { + const input = 'a'; // This is not in the ESCAPED_CHARS object + const result = escapeUnsafeChars(input); + + expect(result).to.be.undefined; + }); +}); diff --git a/test/unit/browser.spec.ts b/test/unit/browser.spec.ts new file mode 100644 index 0000000..640dd4c --- /dev/null +++ b/test/unit/browser.spec.ts @@ -0,0 +1,64 @@ +import sinon from 'sinon'; +import { expect } from 'chai'; +import * as generateUUIDModule from '../../src/generateUUID'; +import * as placeholderSerializerModule from '../../src/Serializer/PlaceholderSerializer'; + +describe('Browser entrypoint', () => { + let serializeStub: sinon.SinonStub; + let generateUUIDStub: sinon.SinonStub; + let placeholderSerializerSpy: sinon.SinonSpy; + let serializeFn; + let cryptoMock = {}; + + before(() => { + // Mock the `window` and `window.crypto` if they are not defined + if (typeof global.window === 'undefined') { + (global as any).window = {}; + } + + Object.defineProperty(global.window, 'crypto', { + value: cryptoMock, + configurable: true, + writable: true, + }); + + generateUUIDStub = sinon.stub(generateUUIDModule, 'generateUUID'); + generateUUIDStub.returns('00000000000000000000000000000'); + + placeholderSerializerSpy = sinon.spy(placeholderSerializerModule, 'PlaceholderSerializer'); + + serializeStub = sinon.stub(placeholderSerializerModule.PlaceholderSerializer.prototype, 'serialize').returns('serialized-data'); + + const { serialize } = require('../../src/browser'); + serializeFn = serialize; + }); + + after(() => { + Object.defineProperty(global.window, 'crypto', { + value: undefined, + configurable: true, + writable: true, + }); + + sinon.restore(); + }); + + it('should invoke genereUUID function with window.crypto', () => { + expect(generateUUIDStub.calledOnce).to.be.true; + expect(generateUUIDStub.calledWith(cryptoMock)).to.be.true; + }); + + it('should create PlaceholderSerializer instance with UUID returned by generateUUID', () => { + expect(placeholderSerializerSpy.calledOnce).to.be.true; + expect(placeholderSerializerSpy.calledWith('00000000000000000000000000000')).to.be.true; + }); + + it('should invoke PlaceholderSerializer.serialize and return it\'s result', () => { + const toSerialize = { test: 1} + const serialized = serializeFn(toSerialize); + + expect(serializeStub.calledOnce).to.be.true; + expect(serializeStub.calledWith(toSerialize)).to.be.true; + expect(serialized).to.be.equal('serialized-data'); + }); +}); diff --git a/test/unit/generateUUID.spec.ts b/test/unit/generateUUID.spec.ts new file mode 100644 index 0000000..86457ce --- /dev/null +++ b/test/unit/generateUUID.spec.ts @@ -0,0 +1,37 @@ +import { expect } from 'chai'; +import { generateUUID } from '../../src/generateUUID'; + +describe('generateUUID', () => { + const UID_LENGTH = 16; + + it('should generate a UUID using randomUUID when available', () => { + const mockUUID = '123e4567-e89b-12d3-a456-426614174000'; + const randomGeneratorWithUUID = { + randomUUID: () => mockUUID, + getRandomValues: (arr: Uint8Array) => arr, + }; + + const result = generateUUID(randomGeneratorWithUUID); + + expect(result).to.equal(mockUUID.replace(/-/g, '')); + }); + + it('should generate a UUID using getRandomValues when randomUUID is not available', () => { + const randomBytes = new Uint8Array(UID_LENGTH).map(() => Math.floor(Math.random() * 256)); + const randomGeneratorWithoutUUID = { + getRandomValues: (arr: Uint8Array) => { + arr.set(randomBytes); + return arr; + }, + }; + + const result = generateUUID(randomGeneratorWithoutUUID); + + expect(result).to.match(/^[0-9a-f]+$/); + + const expectedHexString = Array.from(randomBytes) + .map(byte => byte.toString(16)) + .join(''); + expect(result).to.equal(expectedHexString); + }); +}); diff --git a/test/unit/index.spec.ts b/test/unit/index.spec.ts new file mode 100644 index 0000000..7298cf2 --- /dev/null +++ b/test/unit/index.spec.ts @@ -0,0 +1,47 @@ +import sinon from 'sinon'; +import { expect } from 'chai'; +import crypto from 'crypto'; +import * as generateUUIDModule from '../../src/generateUUID'; +import * as placeholderSerializerModule from '../../src/Serializer/PlaceholderSerializer'; + +describe('Default entrypoint', () => { + let serializeStub: sinon.SinonStub; + let generateUUIDStub: sinon.SinonStub; + let placeholderSerializerSpy: sinon.SinonSpy; + let serializeFn; + + before(() => { + generateUUIDStub = sinon.stub(generateUUIDModule, 'generateUUID'); + generateUUIDStub.returns('00000000000000000000000000000'); + + placeholderSerializerSpy = sinon.spy(placeholderSerializerModule, 'PlaceholderSerializer'); + + serializeStub = sinon.stub(placeholderSerializerModule.PlaceholderSerializer.prototype, 'serialize').returns('serialized-data'); + + const { serialize } = require('../../src/index'); + serializeFn = serialize; + }); + + after(() => { + sinon.restore(); + }); + + it('should invoke genereUUID function with crypto', () => { + expect(generateUUIDStub.calledOnce).to.be.true; + expect(generateUUIDStub.calledWith(crypto)).to.be.true; + }); + + it('should create PlaceholderSerializer instance with UUID returned by generateUUID', () => { + expect(placeholderSerializerSpy.calledOnce).to.be.true; + expect(placeholderSerializerSpy.calledWith('00000000000000000000000000000')).to.be.true; + }); + + it('should invoke PlaceholderSerializer.serialize and return it\'s result', () => { + const toSerialize = { test: 1} + const serialized = serializeFn(toSerialize); + + expect(serializeStub.calledOnce).to.be.true; + expect(serializeStub.calledWith(toSerialize)).to.be.true; + expect(serialized).to.be.equal('serialized-data'); + }); +}); diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..5a4a2e1 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,17 @@ +{ + "compilerOptions": { + "target": "ES2020", + "module": "CommonJS", + "strict": true, + "esModuleInterop": true, + "declaration": true, + "moduleResolution": "node", + "sourceMap": false, + "outDir": "./lib", + "baseUrl": "./", + "noImplicitAny": false + }, + "include": ["src/index.ts", "src/browser.ts"], + "exclude": ["node_modules"] + } + \ No newline at end of file