From c7f995b1440613e30786b78d66da35f1a2d3de7b Mon Sep 17 00:00:00 2001 From: James Ross Date: Sun, 14 Dec 2025 20:27:40 +0000 Subject: [PATCH] refactor: tests to vitest --- .eslintignore | 1 + .github/workflows/test.yml | 6 +- .mocharc.json | 6 - lib/spawnpoint.js | 23 +- package-lock.json | 3960 +++++++++--------------- package.json | 13 +- test/.eslintrc.json | 15 +- test/code.js | 39 - test/code.mjs | 51 + test/config.js | 110 - test/config.mjs | 111 + test/config/blocklistPatterns.json | 20 + test/config/pluginSideload.json | 13 + test/errorCode.js | 41 - test/errorCode.mjs | 53 + test/errorsRegistration.js | 105 - test/errorsRegistration.mjs | 142 + test/failcode.js | 47 - test/failcode.mjs | 59 + test/{getAndLock.js => getAndLock.mjs} | 79 +- test/helpers.js | 159 - test/helpers.mjs | 252 ++ test/init.js | 323 -- test/init.mjs | 425 +++ test/{isRoot.js => isRoot.mjs} | 5 +- test/{isSecure.js => isSecure.mjs} | 6 +- test/jsonHandler.js | 15 - test/jsonHandler.mjs | 19 + test/loadCodes.mjs | 79 + test/loadPlugins.js | 34 - test/loadPlugins.mjs | 77 + test/output.js | 185 -- test/output.mjs | 117 + test/parseEnvValue.mjs | 88 + test/plugin.js | 31 - test/plugin.mjs | 64 + test/process-void/void.js | 1 + test/properties.mjs | 119 + test/random.js | 28 - test/random.mjs | 40 + test/recursiveList.js | 75 - test/recursiveList.mjs | 117 + test/registerConfig.mjs | 84 + test/registerLimit.js | 118 - test/registerLimit.mjs | 202 ++ test/require.js | 14 - test/require.mjs | 17 + test/{roundrobin.js => roundrobin.mjs} | 37 +- test/sample.js | 43 - test/sample.mjs | 52 + test/spawnpointMethods.mjs | 176 ++ vitest.config.js | 33 + 52 files changed, 4028 insertions(+), 3901 deletions(-) delete mode 100644 .mocharc.json delete mode 100644 test/code.js create mode 100644 test/code.mjs delete mode 100644 test/config.js create mode 100644 test/config.mjs create mode 100644 test/config/blocklistPatterns.json create mode 100644 test/config/pluginSideload.json delete mode 100644 test/errorCode.js create mode 100644 test/errorCode.mjs delete mode 100644 test/errorsRegistration.js create mode 100644 test/errorsRegistration.mjs delete mode 100644 test/failcode.js create mode 100644 test/failcode.mjs rename test/{getAndLock.js => getAndLock.mjs} (50%) delete mode 100644 test/helpers.js create mode 100644 test/helpers.mjs delete mode 100644 test/init.js create mode 100644 test/init.mjs rename test/{isRoot.js => isRoot.mjs} (68%) rename test/{isSecure.js => isSecure.mjs} (77%) delete mode 100644 test/jsonHandler.js create mode 100644 test/jsonHandler.mjs create mode 100644 test/loadCodes.mjs delete mode 100644 test/loadPlugins.js create mode 100644 test/loadPlugins.mjs delete mode 100644 test/output.js create mode 100644 test/output.mjs create mode 100644 test/parseEnvValue.mjs delete mode 100644 test/plugin.js create mode 100644 test/plugin.mjs create mode 100644 test/properties.mjs delete mode 100644 test/random.js create mode 100644 test/random.mjs delete mode 100644 test/recursiveList.js create mode 100644 test/recursiveList.mjs create mode 100644 test/registerConfig.mjs delete mode 100644 test/registerLimit.js create mode 100644 test/registerLimit.mjs delete mode 100644 test/require.js create mode 100644 test/require.mjs rename test/{roundrobin.js => roundrobin.mjs} (60%) delete mode 100644 test/sample.js create mode 100644 test/sample.mjs create mode 100644 test/spawnpointMethods.mjs create mode 100644 vitest.config.js diff --git a/.eslintignore b/.eslintignore index 1e23b04..2699150 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1,3 +1,4 @@ node_modules/** build/** +coverage/** test/json/** diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 0404c33..7514535 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -7,12 +7,12 @@ jobs: strategy: matrix: os: [ubuntu-latest, windows-latest, macOS-latest] - node: [20, 22] + node: [22, 24] name: Node ${{ matrix.node }} ${{ matrix.os }} Test steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - name: Setup node - uses: actions/setup-node@v4 + uses: actions/setup-node@v5 with: node-version: ${{ matrix.node }} cache: "npm" diff --git a/.mocharc.json b/.mocharc.json deleted file mode 100644 index 6fa3870..0000000 --- a/.mocharc.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "check-leaks": true, - "throw-deprecations": true, - "exit": true, - "sort": true -} \ No newline at end of file diff --git a/lib/spawnpoint.js b/lib/spawnpoint.js index 1d85e81..86500ed 100644 --- a/lib/spawnpoint.js +++ b/lib/spawnpoint.js @@ -26,14 +26,23 @@ const helpers = require('./helpers.js'); class spawnpoint extends EventEmitter { /** * Creates new instance of spawnpoint - * @param {string} [configFile] Sets the JSON file spawnpoint uses to setup the framework. + * @param {string|Object} [configFile] Sets the JSON file spawnpoint uses to setup the framework, or an options object. + * @param {string} [configFile.configFile='/config/app.json'] Path to the config file when using options object. + * @param {string} [configFile.cwd=process.cwd()] Working directory for the applicationndash avoids needing process.chdir(). * @return {this} */ constructor(configFile = '/config/app.json') { // init EventEmitter super(); - if (typeof(configFile) !== 'string') { + // Support options object for more flexibility + let options = {}; + if (typeof configFile === 'object' && configFile !== null && !Array.isArray(configFile)) { + options = configFile; + configFile = options.configFile || '/config/app.json'; + } + + if (typeof configFile !== 'string') { throw new TypeError('`configFile` must be a path string to a Spawnpoint config file.'); } if (!configFile.endsWith('.json') && !configFile.endsWith('.js')) { @@ -51,8 +60,8 @@ class spawnpoint extends EventEmitter { stopAttempts: 0, // how many attempts to stop have been triggered }; - // app CWD - this.cwd = process.cwd(); + // app CWD - can be passed via options to avoid process.chdir() + this.cwd = options.cwd || process.cwd(); // detect if we are in a container (lazy-loaded on first access) this._containerized = null; @@ -283,6 +292,8 @@ class spawnpoint extends EventEmitter { if (typeof(exts) === 'string') { exts = [exts]; } + // normalize path separators for cross-platform support + dir = dir.replaceAll('\\', '/'); const list = []; let stat; try { @@ -293,8 +304,8 @@ class spawnpoint extends EventEmitter { if (!stat || !stat.isDirectory()) { return list; } - // ensure proper trailing slash and normalize path separators - dir = String(dir + '/').replaceAll('\\', '/'); + // ensure proper trailing slash + dir = String(dir + '/'); // Use withFileTypes to avoid a stat for every entry (significantly faster) const stack = [dir]; diff --git a/package-lock.json b/package-lock.json index 73102eb..77c8b56 100644 --- a/package-lock.json +++ b/package-lock.json @@ -20,16 +20,13 @@ }, "devDependencies": { "@nodecraft/eslint-config": "^46.0.0", - "coveralls": "^3.1.1", + "@vitest/coverage-v8": "^4.0.15", "eslint": "^8.57.1", "eslint-plugin-json": "^4.0.1", "eslint-plugin-n": "^17.23.1", "eslint-plugin-unicorn": "^56.0.1", "jsdoc-to-markdown": "^9.1.3", - "mocha": "^11.7.5", - "nyc": "^17.1.0", - "unexpected": "^13.2.1", - "unexpected-eventemitter": "^2.4.0" + "vitest": "^4.0.15" }, "engines": { "node": ">=22", @@ -51,257 +48,506 @@ "node": ">=6.9.0" } }, - "node_modules/@babel/compat-data": { - "version": "7.28.4", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.4.tgz", - "integrity": "sha512-YsmSKC29MJwf0gF8Rjjrg5LQCmyh+j/nD8/eP7f+BeoQTKYqs9RoWbjGOdy0+1Ekr68RJZMUOPVQaQisnIo4Rw==", + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", "dev": true, "license": "MIT", "engines": { "node": ">=6.9.0" } }, - "node_modules/@babel/core": { - "version": "7.28.4", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.4.tgz", - "integrity": "sha512-2BCOP7TN8M+gVDj7/ht3hsaO/B/n5oDbiAyyvnRlNOs+u1o+JWNYTQrmpuNp1/Wq2gcFrI01JAW+paEKDMx/CA==", + "node_modules/@babel/helper-validator-identifier": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", "dev": true, "license": "MIT", - "peer": true, - "dependencies": { - "@babel/code-frame": "^7.27.1", - "@babel/generator": "^7.28.3", - "@babel/helper-compilation-targets": "^7.27.2", - "@babel/helper-module-transforms": "^7.28.3", - "@babel/helpers": "^7.28.4", - "@babel/parser": "^7.28.4", - "@babel/template": "^7.27.2", - "@babel/traverse": "^7.28.4", - "@babel/types": "^7.28.4", - "@jridgewell/remapping": "^2.3.5", - "convert-source-map": "^2.0.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.2.3", - "semver": "^6.3.1" - }, "engines": { "node": ">=6.9.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/babel" } }, - "node_modules/@babel/core/node_modules/convert-source-map": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", - "dev": true, - "license": "MIT" - }, - "node_modules/@babel/core/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "node_modules/@babel/parser": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.5.tgz", + "integrity": "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==", "dev": true, - "license": "ISC", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.5" + }, "bin": { - "semver": "bin/semver.js" + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" } }, - "node_modules/@babel/generator": { - "version": "7.28.3", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.3.tgz", - "integrity": "sha512-3lSpxGgvnmZznmBkCRnVREPUFJv2wrv9iAoFDvADJc0ypmdOxdUtcLeBgBJ6zE0PMeTKnxeQzyk0xTBq4Ep7zw==", + "node_modules/@babel/types": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.5.tgz", + "integrity": "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/parser": "^7.28.3", - "@babel/types": "^7.28.2", - "@jridgewell/gen-mapping": "^0.3.12", - "@jridgewell/trace-mapping": "^0.3.28", - "jsesc": "^3.0.2" + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5" }, "engines": { "node": ">=6.9.0" } }, - "node_modules/@babel/helper-compilation-targets": { - "version": "7.27.2", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz", - "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==", + "node_modules/@bcoe/v8-coverage": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-1.0.2.tgz", + "integrity": "sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA==", "dev": true, "license": "MIT", - "dependencies": { - "@babel/compat-data": "^7.27.2", - "@babel/helper-validator-option": "^7.27.1", - "browserslist": "^4.24.0", - "lru-cache": "^5.1.1", - "semver": "^6.3.1" - }, "engines": { - "node": ">=6.9.0" + "node": ">=18" } }, - "node_modules/@babel/helper-compilation-targets/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "node_modules/@esbuild/aix-ppc64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz", + "integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==", + "cpu": [ + "ppc64" + ], "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" } }, - "node_modules/@babel/helper-globals": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", - "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", + "node_modules/@esbuild/android-arm": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.12.tgz", + "integrity": "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==", + "cpu": [ + "arm" + ], "dev": true, "license": "MIT", + "optional": true, + "os": [ + "android" + ], "engines": { - "node": ">=6.9.0" + "node": ">=18" } }, - "node_modules/@babel/helper-module-imports": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz", - "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==", + "node_modules/@esbuild/android-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz", + "integrity": "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==", + "cpu": [ + "arm64" + ], "dev": true, "license": "MIT", - "dependencies": { - "@babel/traverse": "^7.27.1", - "@babel/types": "^7.27.1" - }, + "optional": true, + "os": [ + "android" + ], "engines": { - "node": ">=6.9.0" + "node": ">=18" } }, - "node_modules/@babel/helper-module-transforms": { - "version": "7.28.3", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.3.tgz", - "integrity": "sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==", + "node_modules/@esbuild/android-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.12.tgz", + "integrity": "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==", + "cpu": [ + "x64" + ], "dev": true, "license": "MIT", - "dependencies": { - "@babel/helper-module-imports": "^7.27.1", - "@babel/helper-validator-identifier": "^7.27.1", - "@babel/traverse": "^7.28.3" - }, + "optional": true, + "os": [ + "android" + ], "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" + "node": ">=18" } }, - "node_modules/@babel/helper-string-parser": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", - "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "node_modules/@esbuild/darwin-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz", + "integrity": "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==", + "cpu": [ + "arm64" + ], "dev": true, "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], "engines": { - "node": ">=6.9.0" + "node": ">=18" } }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz", - "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==", + "node_modules/@esbuild/darwin-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz", + "integrity": "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==", + "cpu": [ + "x64" + ], "dev": true, "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], "engines": { - "node": ">=6.9.0" + "node": ">=18" } }, - "node_modules/@babel/helper-validator-option": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", - "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz", + "integrity": "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==", + "cpu": [ + "arm64" + ], "dev": true, "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], "engines": { - "node": ">=6.9.0" + "node": ">=18" } }, - "node_modules/@babel/helpers": { - "version": "7.28.4", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.4.tgz", - "integrity": "sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==", + "node_modules/@esbuild/freebsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz", + "integrity": "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==", + "cpu": [ + "x64" + ], "dev": true, "license": "MIT", - "dependencies": { - "@babel/template": "^7.27.2", - "@babel/types": "^7.28.4" - }, + "optional": true, + "os": [ + "freebsd" + ], "engines": { - "node": ">=6.9.0" + "node": ">=18" } }, - "node_modules/@babel/parser": { - "version": "7.28.4", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.4.tgz", - "integrity": "sha512-yZbBqeM6TkpP9du/I2pUZnJsRMGGvOuIrhjzC1AwHwW+6he4mni6Bp/m8ijn0iOuZuPI2BfkCoSRunpyjnrQKg==", + "node_modules/@esbuild/linux-arm": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz", + "integrity": "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==", + "cpu": [ + "arm" + ], "dev": true, "license": "MIT", - "dependencies": { - "@babel/types": "^7.28.4" - }, - "bin": { - "parser": "bin/babel-parser.js" - }, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=6.0.0" + "node": ">=18" } }, - "node_modules/@babel/template": { - "version": "7.27.2", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz", - "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==", + "node_modules/@esbuild/linux-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz", + "integrity": "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==", + "cpu": [ + "arm64" + ], "dev": true, "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.27.1", - "@babel/parser": "^7.27.2", - "@babel/types": "^7.27.1" - }, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=6.9.0" + "node": ">=18" } }, - "node_modules/@babel/traverse": { - "version": "7.28.4", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.4.tgz", - "integrity": "sha512-YEzuboP2qvQavAcjgQNVgsvHIDv6ZpwXvcvjmyySP2DIMuByS/6ioU5G9pYrWHM6T2YDfc7xga9iNzYOs12CFQ==", + "node_modules/@esbuild/linux-ia32": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz", + "integrity": "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==", + "cpu": [ + "ia32" + ], "dev": true, "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.27.1", - "@babel/generator": "^7.28.3", - "@babel/helper-globals": "^7.28.0", - "@babel/parser": "^7.28.4", - "@babel/template": "^7.27.2", - "@babel/types": "^7.28.4", - "debug": "^4.3.1" - }, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=6.9.0" + "node": ">=18" } }, - "node_modules/@babel/types": { - "version": "7.28.4", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.4.tgz", - "integrity": "sha512-bkFqkLhh3pMBUQQkpVgWDWq/lqzc2678eUyDlTBhRqhCHFguYYGM0Efga7tYk4TogG/3x0EEl66/OQ+WGbWB/Q==", + "node_modules/@esbuild/linux-loong64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz", + "integrity": "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==", + "cpu": [ + "loong64" + ], "dev": true, "license": "MIT", - "dependencies": { - "@babel/helper-string-parser": "^7.27.1", - "@babel/helper-validator-identifier": "^7.27.1" - }, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=6.9.0" + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz", + "integrity": "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz", + "integrity": "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz", + "integrity": "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz", + "integrity": "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz", + "integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz", + "integrity": "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz", + "integrity": "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz", + "integrity": "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz", + "integrity": "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz", + "integrity": "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz", + "integrity": "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz", + "integrity": "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz", + "integrity": "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz", + "integrity": "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" } }, "node_modules/@eslint-community/eslint-utils": { @@ -480,218 +726,7 @@ "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", "deprecated": "Use @eslint/object-schema instead", "dev": true, - "license": "BSD-3-Clause" - }, - "node_modules/@isaacs/cliui": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", - "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", - "dev": true, - "license": "ISC", - "dependencies": { - "string-width": "^5.1.2", - "string-width-cjs": "npm:string-width@^4.2.0", - "strip-ansi": "^7.0.1", - "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", - "wrap-ansi": "^8.1.0", - "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@isaacs/cliui/node_modules/ansi-regex": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", - "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/@isaacs/cliui/node_modules/ansi-styles": { - "version": "6.2.3", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", - "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@isaacs/cliui/node_modules/emoji-regex": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", - "dev": true, - "license": "MIT" - }, - "node_modules/@isaacs/cliui/node_modules/string-width": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", - "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", - "dev": true, - "license": "MIT", - "dependencies": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^9.2.2", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@isaacs/cliui/node_modules/strip-ansi": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz", - "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, - "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", - "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^6.1.0", - "string-width": "^5.0.1", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "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", - "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", - "dev": true, - "dependencies": { - "camelcase": "^5.3.1", - "find-up": "^4.1.0", - "get-package-type": "^0.1.0", - "js-yaml": "^3.13.1", - "resolve-from": "^5.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "dependencies": { - "p-locate": "^4.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "dependencies": { - "p-limit": "^2.2.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/@istanbuljs/schema": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", - "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.13", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", - "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.0", - "@jridgewell/trace-mapping": "^0.3.24" - } - }, - "node_modules/@jridgewell/remapping": { - "version": "2.3.5", - "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", - "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.24" - } + "license": "BSD-3-Clause" }, "node_modules/@jridgewell/resolve-uri": { "version": "3.1.2", @@ -810,16 +845,313 @@ "node": ">= 8" } }, - "node_modules/@pkgjs/parseargs": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", - "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.53.3.tgz", + "integrity": "sha512-mRSi+4cBjrRLoaal2PnqH82Wqyb+d3HsPUN/W+WslCXsZsyHa9ZeQQX/pQsZaVIWDkPcpV6jJ+3KLbTbgnwv8w==", + "cpu": [ + "arm" + ], "dev": true, "license": "MIT", "optional": true, - "engines": { - "node": ">=14" - } + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.53.3.tgz", + "integrity": "sha512-CbDGaMpdE9sh7sCmTrTUyllhrg65t6SwhjlMJsLr+J8YjFuPmCEjbBSx4Z/e4SmDyH3aB5hGaJUP2ltV/vcs4w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.53.3.tgz", + "integrity": "sha512-Nr7SlQeqIBpOV6BHHGZgYBuSdanCXuw09hon14MGOLGmXAFYjx1wNvquVPmpZnl0tLjg25dEdr4IQ6GgyToCUA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.53.3.tgz", + "integrity": "sha512-DZ8N4CSNfl965CmPktJ8oBnfYr3F8dTTNBQkRlffnUarJ2ohudQD17sZBa097J8xhQ26AwhHJ5mvUyQW8ddTsQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.53.3.tgz", + "integrity": "sha512-yMTrCrK92aGyi7GuDNtGn2sNW+Gdb4vErx4t3Gv/Tr+1zRb8ax4z8GWVRfr3Jw8zJWvpGHNpss3vVlbF58DZ4w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.53.3.tgz", + "integrity": "sha512-lMfF8X7QhdQzseM6XaX0vbno2m3hlyZFhwcndRMw8fbAGUGL3WFMBdK0hbUBIUYcEcMhVLr1SIamDeuLBnXS+Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.53.3.tgz", + "integrity": "sha512-k9oD15soC/Ln6d2Wv/JOFPzZXIAIFLp6B+i14KhxAfnq76ajt0EhYc5YPeX6W1xJkAdItcVT+JhKl1QZh44/qw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.53.3.tgz", + "integrity": "sha512-vTNlKq+N6CK/8UktsrFuc+/7NlEYVxgaEgRXVUVK258Z5ymho29skzW1sutgYjqNnquGwVUObAaxae8rZ6YMhg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.53.3.tgz", + "integrity": "sha512-RGrFLWgMhSxRs/EWJMIFM1O5Mzuz3Xy3/mnxJp/5cVhZ2XoCAxJnmNsEyeMJtpK+wu0FJFWz+QF4mjCA7AUQ3w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.53.3.tgz", + "integrity": "sha512-kASyvfBEWYPEwe0Qv4nfu6pNkITLTb32p4yTgzFCocHnJLAHs+9LjUu9ONIhvfT/5lv4YS5muBHyuV84epBo/A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.53.3.tgz", + "integrity": "sha512-JiuKcp2teLJwQ7vkJ95EwESWkNRFJD7TQgYmCnrPtlu50b4XvT5MOmurWNrCj3IFdyjBQ5p9vnrX4JM6I8OE7g==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.53.3.tgz", + "integrity": "sha512-EoGSa8nd6d3T7zLuqdojxC20oBfNT8nexBbB/rkxgKj5T5vhpAQKKnD+h3UkoMuTyXkP5jTjK/ccNRmQrPNDuw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.53.3.tgz", + "integrity": "sha512-4s+Wped2IHXHPnAEbIB0YWBv7SDohqxobiiPA1FIWZpX+w9o2i4LezzH/NkFUl8LRci/8udci6cLq+jJQlh+0g==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.53.3.tgz", + "integrity": "sha512-68k2g7+0vs2u9CxDt5ktXTngsxOQkSEV/xBbwlqYcUrAVh6P9EgMZvFsnHy4SEiUl46Xf0IObWVbMvPrr2gw8A==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.53.3.tgz", + "integrity": "sha512-VYsFMpULAz87ZW6BVYw3I6sWesGpsP9OPcyKe8ofdg9LHxSbRMd7zrVrr5xi/3kMZtpWL/wC+UIJWJYVX5uTKg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.53.3.tgz", + "integrity": "sha512-3EhFi1FU6YL8HTUJZ51imGJWEX//ajQPfqWLI3BQq4TlvHy4X0MOr5q3D2Zof/ka0d5FNdPwZXm3Yyib/UEd+w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.53.3.tgz", + "integrity": "sha512-eoROhjcc6HbZCJr+tvVT8X4fW3/5g/WkGvvmwz/88sDtSJzO7r/blvoBDgISDiCjDRZmHpwud7h+6Q9JxFwq1Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.53.3.tgz", + "integrity": "sha512-OueLAWgrNSPGAdUdIjSWXw+u/02BRTcnfw9PN41D2vq/JSEPnJnVuBgw18VkN8wcd4fjUs+jFHVM4t9+kBSNLw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.53.3.tgz", + "integrity": "sha512-GOFuKpsxR/whszbF/bzydebLiXIHSgsEUp6M0JI8dWvi+fFa1TD6YQa4aSZHtpmh2/uAlj/Dy+nmby3TJ3pkTw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.53.3.tgz", + "integrity": "sha512-iah+THLcBJdpfZ1TstDFbKNznlzoxa8fmnFYK4V67HvmuNYkVdAywJSoteUszvBQ9/HqN2+9AZghbajMsFT+oA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.53.3.tgz", + "integrity": "sha512-J9QDiOIZlZLdcot5NXEepDkstocktoVjkaKUtqzgzpt2yWjGlbYiKyp05rWwk4nypbYUNoFAztEgixoLaSETkg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.53.3.tgz", + "integrity": "sha512-UhTd8u31dXadv0MopwGgNOBpUVROFKWVQgAg5N1ESyCz8AuBcMqm4AuTjrwgQKGDfoFuz02EuMRHQIw/frmYKQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] }, "node_modules/@rtsao/scc": { "version": "1.1.0", @@ -828,6 +1160,13 @@ "dev": true, "license": "MIT" }, + "node_modules/@standard-schema/spec": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.0.0.tgz", + "integrity": "sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA==", + "dev": true, + "license": "MIT" + }, "node_modules/@stylistic/eslint-plugin": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin/-/eslint-plugin-3.1.0.tgz", @@ -879,6 +1218,31 @@ "url": "https://opencollective.com/eslint" } }, + "node_modules/@types/chai": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.3.tgz", + "integrity": "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/deep-eql": "*", + "assertion-error": "^2.0.1" + } + }, + "node_modules/@types/deep-eql": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz", + "integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/json5": { "version": "0.0.29", "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", @@ -1026,59 +1390,217 @@ "dev": true, "license": "MIT", "dependencies": { - "@eslint-community/eslint-utils": "^4.7.0", - "@typescript-eslint/scope-manager": "8.46.1", - "@typescript-eslint/types": "8.46.1", - "@typescript-eslint/typescript-estree": "8.46.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "@eslint-community/eslint-utils": "^4.7.0", + "@typescript-eslint/scope-manager": "8.46.1", + "@typescript-eslint/types": "8.46.1", + "@typescript-eslint/typescript-estree": "8.46.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "8.46.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.46.1.tgz", + "integrity": "sha512-ptkmIf2iDkNUjdeu2bQqhFPV1m6qTnFFjg7PPDjxKWaMaP0Z6I9l30Jr3g5QqbZGdw8YdYvLp+XnqnWWZOg/NA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.46.1", + "eslint-visitor-keys": "^4.2.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@ungap/structured-clone": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", + "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", + "dev": true + }, + "node_modules/@vitest/coverage-v8": { + "version": "4.0.15", + "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-4.0.15.tgz", + "integrity": "sha512-FUJ+1RkpTFW7rQITdgTi93qOCWJobWhBirEPCeXh2SW2wsTlFxy51apDz5gzG+ZEYt/THvWeNmhdAoS9DTwpCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@bcoe/v8-coverage": "^1.0.2", + "@vitest/utils": "4.0.15", + "ast-v8-to-istanbul": "^0.3.8", + "istanbul-lib-coverage": "^3.2.2", + "istanbul-lib-report": "^3.0.1", + "istanbul-lib-source-maps": "^5.0.6", + "istanbul-reports": "^3.2.0", + "magicast": "^0.5.1", + "obug": "^2.1.1", + "std-env": "^3.10.0", + "tinyrainbow": "^3.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@vitest/browser": "4.0.15", + "vitest": "4.0.15" + }, + "peerDependenciesMeta": { + "@vitest/browser": { + "optional": true + } + } + }, + "node_modules/@vitest/coverage-v8/node_modules/istanbul-lib-source-maps": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-5.0.6.tgz", + "integrity": "sha512-yg2d+Em4KizZC5niWhQaIomgf5WlL4vOOjZ5xGCmF8SnPE/mDWWXgvRExdcpCgh9lLRRa1/fSYp2ymmbJ1pI+A==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.23", + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@vitest/expect": { + "version": "4.0.15", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.0.15.tgz", + "integrity": "sha512-Gfyva9/GxPAWXIWjyGDli9O+waHDC0Q0jaLdFP1qPAUUfo1FEXPXUfUkp3eZA0sSq340vPycSyOlYUeM15Ft1w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@standard-schema/spec": "^1.0.0", + "@types/chai": "^5.2.2", + "@vitest/spy": "4.0.15", + "@vitest/utils": "4.0.15", + "chai": "^6.2.1", + "tinyrainbow": "^3.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/mocker": { + "version": "4.0.15", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.0.15.tgz", + "integrity": "sha512-CZ28GLfOEIFkvCFngN8Sfx5h+Se0zN+h4B7yOsPVCcgtiO7t5jt9xQh2E1UkFep+eb9fjyMfuC5gBypwb07fvQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/spy": "4.0.15", + "estree-walker": "^3.0.3", + "magic-string": "^0.30.21" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "msw": "^2.4.9", + "vite": "^6.0.0 || ^7.0.0-0" + }, + "peerDependenciesMeta": { + "msw": { + "optional": true + }, + "vite": { + "optional": true + } + } + }, + "node_modules/@vitest/pretty-format": { + "version": "4.0.15", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.0.15.tgz", + "integrity": "sha512-SWdqR8vEv83WtZcrfLNqlqeQXlQLh2iilO1Wk1gv4eiHKjEzvgHb2OVc3mIPyhZE6F+CtfYjNlDJwP5MN6Km7A==", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyrainbow": "^3.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/runner": { + "version": "4.0.15", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.0.15.tgz", + "integrity": "sha512-+A+yMY8dGixUhHmNdPUxOh0la6uVzun86vAbuMT3hIDxMrAOmn5ILBHm8ajrqHE0t8R9T1dGnde1A5DTnmi3qw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/utils": "4.0.15", + "pathe": "^2.0.3" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <6.0.0" + "url": "https://opencollective.com/vitest" } }, - "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.46.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.46.1.tgz", - "integrity": "sha512-ptkmIf2iDkNUjdeu2bQqhFPV1m6qTnFFjg7PPDjxKWaMaP0Z6I9l30Jr3g5QqbZGdw8YdYvLp+XnqnWWZOg/NA==", + "node_modules/@vitest/snapshot": { + "version": "4.0.15", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.0.15.tgz", + "integrity": "sha512-A7Ob8EdFZJIBjLjeO0DZF4lqR6U7Ydi5/5LIZ0xcI+23lYlsYJAfGn8PrIWTYdZQRNnSRlzhg0zyGu37mVdy5g==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.46.1", - "eslint-visitor-keys": "^4.2.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "@vitest/pretty-format": "4.0.15", + "magic-string": "^0.30.21", + "pathe": "^2.0.3" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "url": "https://opencollective.com/vitest" } }, - "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", - "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "node_modules/@vitest/spy": { + "version": "4.0.15", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.0.15.tgz", + "integrity": "sha512-+EIjOJmnY6mIfdXtE/bnozKEvTC4Uczg19yeZ2vtCz5Yyb0QQ31QWVQ8hswJ3Ysx/K2EqaNsVanjr//2+P3FHw==", "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, + "license": "MIT", "funding": { - "url": "https://opencollective.com/eslint" + "url": "https://opencollective.com/vitest" } }, - "node_modules/@ungap/structured-clone": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", - "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", - "dev": true + "node_modules/@vitest/utils": { + "version": "4.0.15", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.0.15.tgz", + "integrity": "sha512-HXjPW2w5dxhTD0dLwtYHDnelK3j8sR8cWIaLxr22evTyY6q8pRCjZSmhRWVjBaOVXChQd6AwMzi9pucorXCPZA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "4.0.15", + "tinyrainbow": "^3.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } }, "node_modules/acorn": { "version": "8.15.0", @@ -1103,19 +1625,6 @@ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, - "node_modules/aggregate-error": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", - "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", - "dev": true, - "dependencies": { - "clean-stack": "^2.0.0", - "indent-string": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/ajv": { "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", @@ -1156,33 +1665,6 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/append-transform": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/append-transform/-/append-transform-2.0.0.tgz", - "integrity": "sha512-7yeyCEurROLQJFv5Xj4lEGTy0borxepjFv1g22oAdqFu//SrAlDl1O1Nxx15SH1RoliUml6p8dwJW9jvZughhg==", - "dev": true, - "dependencies": { - "default-require-extensions": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/archy": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz", - "integrity": "sha512-Xg+9RwCg/0p32teKdGMPTPnVXKD0w3DfHnFTficozsAgsvq2XenPJq/MYpzzQ/v8zrOyJn6Ds39VA4JIDwFfqw==", - "dev": true - }, - "node_modules/argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "dependencies": { - "sprintf-js": "~1.0.2" - } - }, "node_modules/array-back": { "version": "6.2.2", "resolved": "https://registry.npmjs.org/array-back/-/array-back-6.2.2.tgz", @@ -1210,24 +1692,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/array-changes": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/array-changes/-/array-changes-3.0.1.tgz", - "integrity": "sha512-UYXV+qUaTKJO3GUBVfD6b9Mu7wUzDvpfovZKtbxNJApwRUifgrJMidvE+/rbqV3wCffly5HXcbOW3/7shmmEag==", - "dev": true, - "dependencies": { - "arraydiff-papandreou": "0.1.1-patch1" - } - }, - "node_modules/array-changes-async": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/array-changes-async/-/array-changes-async-3.0.1.tgz", - "integrity": "sha512-WNHLhMOTzntixkBxNm/MiWCNKuC4FMYXk6DKuzZUbkWXAe0Xomwv40SEUicfOuHHtW7Ue661Mc5AJA0AOfqApg==", - "dev": true, - "dependencies": { - "arraydiff-async": "0.2.0" - } - }, "node_modules/array-includes": { "version": "3.1.9", "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.9.tgz", @@ -1333,35 +1797,34 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/arraydiff-async": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/arraydiff-async/-/arraydiff-async-0.2.0.tgz", - "integrity": "sha512-i5QgybCLzbTyGlbdOd630AFwpradPgcbsdJ2XoXmgwaQ05lUC44Jn8Gs3EHklHVFoA6grV7ssJ9ExdHBu1C/nw==", - "dev": true - }, - "node_modules/arraydiff-papandreou": { - "version": "0.1.1-patch1", - "resolved": "https://registry.npmjs.org/arraydiff-papandreou/-/arraydiff-papandreou-0.1.1-patch1.tgz", - "integrity": "sha512-QPi68m5STvfROKohFfZb/yWH60UVdmbvCB2SJqcEiitriXRlrAU8Rhxc2PiU/x+htvdPW+jYlN1bhwhEOut9qg==", - "dev": true + "node_modules/assertion-error": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", + "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + } }, - "node_modules/asn1": { - "version": "0.2.6", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz", - "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==", + "node_modules/ast-v8-to-istanbul": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/ast-v8-to-istanbul/-/ast-v8-to-istanbul-0.3.8.tgz", + "integrity": "sha512-szgSZqUxI5T8mLKvS7WTjF9is+MVbOeLADU73IseOcrqhxr/VAvy6wfoVE39KnKzA7JRhjF5eUagNlHwvZPlKQ==", "dev": true, + "license": "MIT", "dependencies": { - "safer-buffer": "~2.1.0" + "@jridgewell/trace-mapping": "^0.3.31", + "estree-walker": "^3.0.3", + "js-tokens": "^9.0.1" } }, - "node_modules/assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==", + "node_modules/ast-v8-to-istanbul/node_modules/js-tokens": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-9.0.1.tgz", + "integrity": "sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==", "dev": true, - "engines": { - "node": ">=0.8" - } + "license": "MIT" }, "node_modules/async": { "version": "3.2.6", @@ -1379,12 +1842,6 @@ "node": ">= 0.4" } }, - "node_modules/asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", - "dev": true - }, "node_modules/available-typed-arrays": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", @@ -1401,21 +1858,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/aws-sign2": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", - "integrity": "sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA==", - "dev": true, - "engines": { - "node": "*" - } - }, - "node_modules/aws4": { - "version": "1.12.0", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.12.0.tgz", - "integrity": "sha512-NmWvPnx0F1SfrQbYwOi7OeaNGokp9XhzNioJ/CSBs8Qa4vxug81mhJEAVZwxXuBmYB5KDRfMq/F3RR0BIU7sWg==", - "dev": true - }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -1432,15 +1874,6 @@ "baseline-browser-mapping": "dist/cli.js" } }, - "node_modules/bcrypt-pbkdf": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", - "integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==", - "dev": true, - "dependencies": { - "tweetnacl": "^0.14.3" - } - }, "node_modules/bluebird": { "version": "3.7.2", "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", @@ -1471,12 +1904,6 @@ "node": ">=8" } }, - "node_modules/browser-stdout": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", - "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", - "dev": true - }, "node_modules/browserslist": { "version": "4.26.3", "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.26.3.tgz", @@ -1546,21 +1973,6 @@ } } }, - "node_modules/caching-transform": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/caching-transform/-/caching-transform-4.0.0.tgz", - "integrity": "sha512-kpqOvwXnjjN44D89K5ccQC+RUrsy7jB/XLlRrx0D7/2HNcTPqzsb6XgYoErwko6QsV184CA2YgS1fxDiiDZMWA==", - "dev": true, - "dependencies": { - "hasha": "^5.0.0", - "make-dir": "^3.0.0", - "package-hash": "^4.0.0", - "write-file-atomic": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/call-bind": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", @@ -1620,15 +2032,6 @@ "node": ">=6" } }, - "node_modules/camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true, - "engines": { - "node": ">=6" - } - }, "node_modules/caniuse-lite": { "version": "1.0.30001750", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001750.tgz", @@ -1650,12 +2053,6 @@ ], "license": "CC-BY-4.0" }, - "node_modules/caseless": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", - "integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==", - "dev": true - }, "node_modules/catharsis": { "version": "0.9.0", "resolved": "https://registry.npmjs.org/catharsis/-/catharsis-0.9.0.tgz", @@ -1669,6 +2066,16 @@ "node": ">= 10" } }, + "node_modules/chai": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/chai/-/chai-6.2.1.tgz", + "integrity": "sha512-p4Z49OGG5W/WBCPSS/dH3jQ73kD6tiMmUM+bckNK6Jr5JHMG3k9bg/BvKR8lKmtVBKmOiuVaV2ws8s9oSbwysg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -1701,22 +2108,6 @@ "url": "https://github.com/chalk/chalk-template?sponsor=1" } }, - "node_modules/chokidar": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", - "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", - "dev": true, - "license": "MIT", - "dependencies": { - "readdirp": "^4.0.1" - }, - "engines": { - "node": ">= 14.16.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, "node_modules/ci-info": { "version": "4.3.1", "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.3.1.tgz", @@ -1756,30 +2147,6 @@ "node": ">=0.8.0" } }, - "node_modules/clean-stack": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", - "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "dev": true, - "license": "ISC", - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -1792,30 +2159,12 @@ "node": ">=7.0.0" } }, - "node_modules/color-diff": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/color-diff/-/color-diff-0.1.7.tgz", - "integrity": "sha512-Tuh3W2d3LdK3E8BhKltCuESgUva+oluFYqvzHg8a3tu5XzO/a4PF4W8islodUcqtiPgPdkg42PzL2bwtOUaJeQ==", - "dev": true - }, "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 }, - "node_modules/combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "dev": true, - "dependencies": { - "delayed-stream": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, "node_modules/command-line-args": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/command-line-args/-/command-line-args-6.0.1.tgz", @@ -1866,12 +2215,6 @@ "node": ">=12.17" } }, - "node_modules/commondir": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", - "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==", - "dev": true - }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -1896,12 +2239,6 @@ "node": ">=0.10.0" } }, - "node_modules/convert-source-map": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", - "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", - "dev": true - }, "node_modules/core-js-compat": { "version": "3.46.0", "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.46.0.tgz", @@ -1916,31 +2253,6 @@ "url": "https://opencollective.com/core-js" } }, - "node_modules/core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==", - "dev": true - }, - "node_modules/coveralls": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/coveralls/-/coveralls-3.1.1.tgz", - "integrity": "sha512-+dxnG2NHncSD1NrqbSM3dn/lE57O6Qf/koe9+I7c+wzkqRmEvcp0kgJdxKInzYzkICKkFMZsX3Vct3++tsF9ww==", - "dev": true, - "dependencies": { - "js-yaml": "^3.13.1", - "lcov-parse": "^1.0.0", - "log-driver": "^1.2.7", - "minimist": "^1.2.5", - "request": "^2.88.2" - }, - "bin": { - "coveralls": "bin/coveralls.js" - }, - "engines": { - "node": ">=6" - } - }, "node_modules/cross-spawn": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", @@ -1966,18 +2278,6 @@ "node": ">=12.17" } }, - "node_modules/dashdash": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", - "integrity": "sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==", - "dev": true, - "dependencies": { - "assert-plus": "^1.0.0" - }, - "engines": { - "node": ">=0.10" - } - }, "node_modules/data-view-buffer": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.2.tgz", @@ -2056,36 +2356,12 @@ } } }, - "node_modules/decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/deep-is": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", "dev": true }, - "node_modules/default-require-extensions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-3.0.1.tgz", - "integrity": "sha512-eXTJmRbm2TIt9MgWTsOH1wEuhew6XGZcMeGKCtLedIg/NCsg1iBePXkceTdK4Fii7pzmN9tGsZhKzZ4h7O/fxw==", - "dev": true, - "dependencies": { - "strip-bom": "^4.0.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/define-data-property": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", @@ -2122,41 +2398,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", - "dev": true, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/detect-indent": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-3.0.1.tgz", - "integrity": "sha512-xo3WP66SNbr1Eim85s/qyH0ZL8PQUwp86HWm0S1l8WnJ/zjT6T3w1nwNA0yOZeuvOemupEYvpvF6BIdYRuERJQ==", - "dev": true, - "dependencies": { - "get-stdin": "^4.0.1", - "minimist": "^1.1.0", - "repeating": "^1.1.0" - }, - "bin": { - "detect-indent": "cli.js" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/diff": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", - "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", - "dev": true, - "engines": { - "node": ">=0.3.1" - } - }, "node_modules/dmd": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/dmd/-/dmd-7.1.1.tgz", @@ -2211,23 +2452,6 @@ "node": ">= 0.4" } }, - "node_modules/eastasianwidth": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", - "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", - "dev": true, - "license": "MIT" - }, - "node_modules/ecc-jsbn": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", - "integrity": "sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==", - "dev": true, - "dependencies": { - "jsbn": "~0.1.0", - "safer-buffer": "^2.1.0" - } - }, "node_modules/electron-to-chromium": { "version": "1.5.235", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.235.tgz", @@ -2235,12 +2459,6 @@ "dev": true, "license": "ISC" }, - "node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, "node_modules/enhanced-resolve": { "version": "5.18.3", "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.3.tgz", @@ -2367,6 +2585,13 @@ "node": ">= 0.4" } }, + "node_modules/es-module-lexer": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz", + "integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==", + "dev": true, + "license": "MIT" + }, "node_modules/es-object-atoms": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", @@ -2427,12 +2652,48 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/es6-error": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", - "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==", - "dev": true - }, + "node_modules/esbuild": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz", + "integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.12", + "@esbuild/android-arm": "0.25.12", + "@esbuild/android-arm64": "0.25.12", + "@esbuild/android-x64": "0.25.12", + "@esbuild/darwin-arm64": "0.25.12", + "@esbuild/darwin-x64": "0.25.12", + "@esbuild/freebsd-arm64": "0.25.12", + "@esbuild/freebsd-x64": "0.25.12", + "@esbuild/linux-arm": "0.25.12", + "@esbuild/linux-arm64": "0.25.12", + "@esbuild/linux-ia32": "0.25.12", + "@esbuild/linux-loong64": "0.25.12", + "@esbuild/linux-mips64el": "0.25.12", + "@esbuild/linux-ppc64": "0.25.12", + "@esbuild/linux-riscv64": "0.25.12", + "@esbuild/linux-s390x": "0.25.12", + "@esbuild/linux-x64": "0.25.12", + "@esbuild/netbsd-arm64": "0.25.12", + "@esbuild/netbsd-x64": "0.25.12", + "@esbuild/openbsd-arm64": "0.25.12", + "@esbuild/openbsd-x64": "0.25.12", + "@esbuild/openharmony-arm64": "0.25.12", + "@esbuild/sunos-x64": "0.25.12", + "@esbuild/win32-arm64": "0.25.12", + "@esbuild/win32-ia32": "0.25.12", + "@esbuild/win32-x64": "0.25.12" + } + }, "node_modules/escalade": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", @@ -2899,19 +3160,6 @@ "url": "https://opencollective.com/eslint" } }, - "node_modules/esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true, - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/esquery": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", @@ -2946,6 +3194,16 @@ "node": ">=4.0" } }, + "node_modules/estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0" + } + }, "node_modules/esutils": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", @@ -2955,20 +3213,15 @@ "node": ">=0.10.0" } }, - "node_modules/extend": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", - "dev": true - }, - "node_modules/extsprintf": { + "node_modules/expect-type": { "version": "1.3.0", - "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", - "integrity": "sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==", + "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.3.0.tgz", + "integrity": "sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==", "dev": true, - "engines": [ - "node >=0.6.0" - ] + "license": "Apache-2.0", + "engines": { + "node": ">=12.0.0" + } }, "node_modules/fast-deep-equal": { "version": "3.1.3", @@ -3027,6 +3280,24 @@ "reusify": "^1.0.4" } }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, "node_modules/file-entry-cache": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", @@ -3074,23 +3345,6 @@ "node": ">=8" } }, - "node_modules/find-cache-dir": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz", - "integrity": "sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==", - "dev": true, - "dependencies": { - "commondir": "^1.0.1", - "make-dir": "^3.0.2", - "pkg-dir": "^4.1.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/avajs/find-cache-dir?sponsor=1" - } - }, "node_modules/find-replace": { "version": "5.0.2", "resolved": "https://registry.npmjs.org/find-replace/-/find-replace-5.0.2.tgz", @@ -3125,15 +3379,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/flat": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", - "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", - "dev": true, - "bin": { - "flat": "cli.js" - } - }, "node_modules/flat-cache": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", @@ -3170,68 +3415,27 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/foreground-child": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-2.0.0.tgz", - "integrity": "sha512-dCIq9FpEcyQyXKCkyzmlPTFNgrCzPudOe+mhvJU5zAtlBnGVy2yKxtfsxK2tQBThwq225jcvBjpw1Gr40uzZCA==", - "dev": true, - "dependencies": { - "cross-spawn": "^7.0.0", - "signal-exit": "^3.0.2" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/forever-agent": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", - "integrity": "sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==", - "dev": true, - "engines": { - "node": "*" - } - }, - "node_modules/form-data": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", - "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", - "dev": true, - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.6", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 0.12" - } - }, - "node_modules/fromentries": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/fromentries/-/fromentries-1.3.2.tgz", - "integrity": "sha512-cHEpEQHUg0f8XdtZCc2ZAhrHzKzT0MrFUTcvx+hfxYu7rGMDc5SKoXFh+n4YigxsHXRzc6OrCshdR1bWH6HHyg==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", "dev": true }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, "node_modules/function-bind": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", @@ -3283,25 +3487,6 @@ "node": ">= 0.4" } }, - "node_modules/gensync": { - "version": "1.0.0-beta.2", - "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", - "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true, - "engines": { - "node": "6.* || 8.* || >= 10.*" - } - }, "node_modules/get-intrinsic": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", @@ -3327,15 +3512,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/get-package-type": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", - "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", - "dev": true, - "engines": { - "node": ">=8.0.0" - } - }, "node_modules/get-proto": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", @@ -3350,15 +3526,6 @@ "node": ">= 0.4" } }, - "node_modules/get-stdin": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz", - "integrity": "sha512-F5aQMywwJ2n85s4hJPTT9RPxGmubonuB10MNYo17/xph174n2MIR33HRguhzVag10O/npM7SPk73LMZNP+FaWw==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/get-symbol-description": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.1.0.tgz", @@ -3390,15 +3557,6 @@ "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" } }, - "node_modules/getpass": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", - "integrity": "sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==", - "dev": true, - "dependencies": { - "assert-plus": "^1.0.0" - } - }, "node_modules/glob": { "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", @@ -3518,12 +3676,6 @@ "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", "dev": true }, - "node_modules/greedy-interval-packer": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/greedy-interval-packer/-/greedy-interval-packer-1.2.0.tgz", - "integrity": "sha512-4ap45COKmRa2BdeVTY9FXIlR5UIkQX/a0pGtEvk+DnZ7THF3n1UkUKB17AFo+5TMaXnwJkHDn9VH5ATXt/YzHA==", - "dev": true - }, "node_modules/handlebars": { "version": "4.7.8", "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz", @@ -3546,29 +3698,6 @@ "uglify-js": "^3.1.4" } }, - "node_modules/har-schema": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", - "integrity": "sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/har-validator": { - "version": "5.1.5", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", - "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", - "deprecated": "this library is no longer supported", - "dev": true, - "dependencies": { - "ajv": "^6.12.3", - "har-schema": "^2.0.0" - }, - "engines": { - "node": ">=6" - } - }, "node_modules/has-bigints": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz", @@ -3649,31 +3778,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/hasha": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/hasha/-/hasha-5.2.2.tgz", - "integrity": "sha512-Hrp5vIK/xr5SkeN2onO32H0MgNZ0f17HRNH39WfL0SYUNOTZ5Lz1TJ8Pajo/87dYGEFlLMm7mIc/k/s6Bvz9HQ==", - "dev": true, - "dependencies": { - "is-stream": "^2.0.0", - "type-fest": "^0.8.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/hasha/node_modules/type-fest": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", - "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/hasown": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", @@ -3687,15 +3791,6 @@ "node": ">= 0.4" } }, - "node_modules/he": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", - "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", - "dev": true, - "bin": { - "he": "bin/he" - } - }, "node_modules/hosted-git-info": { "version": "2.8.9", "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", @@ -3707,22 +3802,8 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", - "dev": true - }, - "node_modules/http-signature": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", - "integrity": "sha512-CAbnr6Rz4CYQkLYUtSNXxQPUH2gK8f3iWexVlsnMeD+GjlsQ0Xsy1cOX+mN3dtxYomRy21CiOzU8Uhw6OwncEQ==", "dev": true, - "dependencies": { - "assert-plus": "^1.0.0", - "jsprim": "^1.2.2", - "sshpk": "^1.7.0" - }, - "engines": { - "node": ">=0.8", - "npm": ">=1.3.7" - } + "license": "MIT" }, "node_modules/ignore": { "version": "5.3.2", @@ -3982,27 +4063,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-finite": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.1.0.tgz", - "integrity": "sha512-cdyMtqX/BOqqNBBiKlIVkytNHm49MtMlYyn1zxzvJKWmFMlGzm+ry5BBfYyeY9YmNKbRSo/o7OX9w9ale0wg3w==", - "dev": true, - "engines": { - "node": ">=0.10.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/is-generator-function": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.2.tgz", @@ -4097,15 +4157,6 @@ "node": ">=8" } }, - "node_modules/is-plain-obj": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", - "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/is-regex": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", @@ -4154,18 +4205,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-stream": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", - "dev": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/is-string": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.1.1.tgz", @@ -4217,24 +4256,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==", - "dev": true - }, - "node_modules/is-unicode-supported": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", - "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/is-weakmap": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz", @@ -4281,85 +4302,24 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-windows": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", - "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/isarray": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", - "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", - "dev": true, - "license": "MIT" - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true - }, - "node_modules/isstream": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", - "integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==", - "dev": true - }, - "node_modules/istanbul-lib-coverage": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", - "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-lib-hook": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-hook/-/istanbul-lib-hook-3.0.0.tgz", - "integrity": "sha512-Pt/uge1Q9s+5VAZ+pCo16TYMWPBIl+oaNIjgLQxcX0itS6ueeaA+pEfThZpH8WxhFgCiEb8sAJY6MdUKgiIWaQ==", - "dev": true, - "dependencies": { - "append-transform": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-lib-instrument": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.3.tgz", - "integrity": "sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "@babel/core": "^7.23.9", - "@babel/parser": "^7.23.9", - "@istanbuljs/schema": "^0.1.3", - "istanbul-lib-coverage": "^3.2.0", - "semver": "^7.5.4" - }, - "engines": { - "node": ">=10" - } + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true, + "license": "MIT" }, - "node_modules/istanbul-lib-processinfo": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/istanbul-lib-processinfo/-/istanbul-lib-processinfo-2.0.3.tgz", - "integrity": "sha512-NkwHbo3E00oybX6NGJi6ar0B29vxyvNwoC7eJ4G4Yq28UfY758Hgn/heV8VRFhevPED4LXfFz0DQ8z/0kw9zMg==", + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "node_modules/istanbul-lib-coverage": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", + "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", "dev": true, - "dependencies": { - "archy": "^1.0.0", - "cross-spawn": "^7.0.3", - "istanbul-lib-coverage": "^3.2.0", - "p-map": "^3.0.0", - "rimraf": "^3.0.0", - "uuid": "^8.3.2" - }, "engines": { "node": ">=8" } @@ -4369,6 +4329,7 @@ "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { "istanbul-lib-coverage": "^3.0.0", "make-dir": "^4.0.0", @@ -4378,40 +4339,12 @@ "node": ">=10" } }, - "node_modules/istanbul-lib-report/node_modules/make-dir": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", - "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", - "dev": true, - "dependencies": { - "semver": "^7.5.3" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/istanbul-lib-source-maps": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", - "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", - "dev": true, - "dependencies": { - "debug": "^4.1.1", - "istanbul-lib-coverage": "^3.0.0", - "source-map": "^0.6.1" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/istanbul-reports": { - "version": "3.1.7", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.7.tgz", - "integrity": "sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.2.0.tgz", + "integrity": "sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { "html-escaper": "^2.0.0", "istanbul-lib-report": "^3.0.0" @@ -4420,22 +4353,6 @@ "node": ">=8" } }, - "node_modules/jackspeak": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", - "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", - "dev": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "@isaacs/cliui": "^8.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - }, - "optionalDependencies": { - "@pkgjs/parseargs": "^0.11.0" - } - }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -4443,20 +4360,6 @@ "dev": true, "license": "MIT" }, - "node_modules/js-yaml": { - "version": "3.14.2", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.2.tgz", - "integrity": "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==", - "dev": true, - "license": "MIT", - "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, "node_modules/js2xmlparser": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/js2xmlparser/-/js2xmlparser-4.0.2.tgz", @@ -4467,12 +4370,6 @@ "xmlcreate": "^2.0.4" } }, - "node_modules/jsbn": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", - "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==", - "dev": true - }, "node_modules/jsdoc": { "version": "4.0.5", "resolved": "https://registry.npmjs.org/jsdoc/-/jsdoc-4.0.5.tgz", @@ -4625,12 +4522,6 @@ "dev": true, "license": "MIT" }, - "node_modules/json-schema": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", - "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==", - "dev": true - }, "node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", @@ -4643,25 +4534,6 @@ "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", "dev": true }, - "node_modules/json-stringify-safe": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==", - "dev": true - }, - "node_modules/json5": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", - "dev": true, - "license": "MIT", - "bin": { - "json5": "lib/cli.js" - }, - "engines": { - "node": ">=6" - } - }, "node_modules/jsonc-parser": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.3.1.tgz", @@ -4669,21 +4541,6 @@ "dev": true, "license": "MIT" }, - "node_modules/jsprim": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.2.tgz", - "integrity": "sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==", - "dev": true, - "dependencies": { - "assert-plus": "1.0.0", - "extsprintf": "1.3.0", - "json-schema": "0.4.0", - "verror": "1.10.0" - }, - "engines": { - "node": ">=0.6.0" - } - }, "node_modules/keyv": { "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", @@ -4711,15 +4568,6 @@ "node": ">=6" } }, - "node_modules/lcov-parse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/lcov-parse/-/lcov-parse-1.0.0.tgz", - "integrity": "sha512-aprLII/vPzuQvYZnDRU78Fns9I2Ag3gi4Ipga/hxnVMCZC8DnR2nI7XBqrPoywGfxqIx/DgarGvDJZAD3YBTgQ==", - "dev": true, - "bin": { - "lcov-parse": "bin/cli.js" - } - }, "node_modules/levn": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", @@ -4777,96 +4625,50 @@ "dev": true, "license": "MIT" }, - "node_modules/lodash.flattendeep": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz", - "integrity": "sha512-uHaJFihxmJcEX3kT4I23ABqKKalJ/zDrDg0lsFtc1h+3uw49SIJ5beyhx5ExVRti3AvKoOJngIj7xz3oylPdWQ==", - "dev": true - }, "node_modules/lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "dev": true }, - "node_modules/log-driver": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/log-driver/-/log-driver-1.2.7.tgz", - "integrity": "sha512-U7KCmLdqsGHBLeWqYlFA0V0Sl6P08EE1ZrmA9cxjUE0WVqT9qnyVDPz1kzpFEP0jdJuFnasWIfSd7fsaNXkpbg==", - "dev": true, - "engines": { - "node": ">=0.8.6" - } - }, - "node_modules/log-symbols": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", - "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", - "dev": true, - "dependencies": { - "chalk": "^4.1.0", - "is-unicode-supported": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", "dev": true, - "license": "ISC", + "license": "MIT", "dependencies": { - "yallist": "^3.0.2" + "@jridgewell/sourcemap-codec": "^1.5.5" } }, - "node_modules/magicpen": { - "version": "6.2.4", - "resolved": "https://registry.npmjs.org/magicpen/-/magicpen-6.2.4.tgz", - "integrity": "sha512-rT4JcgakSrmR9/qPY/EsDSvKH4+nQuFfSQ34Djnj0Zx9jJ+c3REOz+K3CITvRZcmAcCFM6jJO7wSiHlMEXYy3A==", + "node_modules/magicast": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/magicast/-/magicast-0.5.1.tgz", + "integrity": "sha512-xrHS24IxaLrvuo613F719wvOIv9xPHFWQHuvGUBmPnCA/3MQxKI3b+r7n1jAoDHmsbC5bRhTZYR77invLAxVnw==", "dev": true, + "license": "MIT", "dependencies": { - "ansi-styles": "2.0.0", - "color-diff": "0.1.7" - } - }, - "node_modules/magicpen/node_modules/ansi-styles": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.0.0.tgz", - "integrity": "sha512-0kjBHdIQSa1iuh2rs8Md1GQNHAKrefcRSp2W5OKQU1oBZgCSqQ5aG4o+r69irBlhIPwA8wUaPdN/FWZVIHW7rA==", - "dev": true, - "engines": { - "node": ">=0.10.0" + "@babel/parser": "^7.28.5", + "@babel/types": "^7.28.5", + "source-map-js": "^1.2.1" } }, "node_modules/make-dir": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", - "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", + "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", "dev": true, + "license": "MIT", "dependencies": { - "semver": "^6.0.0" + "semver": "^7.5.3" }, "engines": { - "node": ">=8" + "node": ">=10" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/make-dir/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, "node_modules/markdown-it": { "version": "14.1.0", "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.0.tgz", @@ -4930,267 +4732,91 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-2.0.0.tgz", "integrity": "sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==", - "dev": true, - "license": "MIT" - }, - "node_modules/merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 8" - } - }, - "node_modules/micromatch": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", - "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", - "dev": true, - "license": "MIT", - "dependencies": { - "braces": "^3.0.3", - "picomatch": "^2.3.1" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/micromatch/node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "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, - "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, - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/min-indent": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", - "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/minimist": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/minipass": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", - "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, - "node_modules/mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "dev": true, - "license": "MIT", - "bin": { - "mkdirp": "bin/cmd.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/mocha": { - "version": "11.7.5", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-11.7.5.tgz", - "integrity": "sha512-mTT6RgopEYABzXWFx+GcJ+ZQ32kp4fMf0xvpZIIfSq9Z8lC/++MtcCnQ9t5FP2veYEP95FIYSvW+U9fV4xrlig==", - "dev": true, - "license": "MIT", - "dependencies": { - "browser-stdout": "^1.3.1", - "chokidar": "^4.0.1", - "debug": "^4.3.5", - "diff": "^7.0.0", - "escape-string-regexp": "^4.0.0", - "find-up": "^5.0.0", - "glob": "^10.4.5", - "he": "^1.2.0", - "is-path-inside": "^3.0.3", - "js-yaml": "^4.1.0", - "log-symbols": "^4.1.0", - "minimatch": "^9.0.5", - "ms": "^2.1.3", - "picocolors": "^1.1.1", - "serialize-javascript": "^6.0.2", - "strip-json-comments": "^3.1.1", - "supports-color": "^8.1.1", - "workerpool": "^9.2.0", - "yargs": "^17.7.2", - "yargs-parser": "^21.1.1", - "yargs-unparser": "^2.0.0" - }, - "bin": { - "_mocha": "bin/_mocha", - "mocha": "bin/mocha.js" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/mocha/node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true + "dev": true, + "license": "MIT" }, - "node_modules/mocha/node_modules/diff": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-7.0.0.tgz", - "integrity": "sha512-PJWHUb1RFevKCwaFA9RlG5tCd+FO5iRh9A8HEtkmBH2Li03iJriB6m6JIN4rGz3K3JLawI7/veA1xzRKP6ISBw==", + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", "dev": true, - "license": "BSD-3-Clause", + "license": "MIT", "engines": { - "node": ">=0.3.1" + "node": ">= 8" } }, - "node_modules/mocha/node_modules/foreground-child": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", - "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", "dev": true, - "license": "ISC", + "license": "MIT", "dependencies": { - "cross-spawn": "^7.0.6", - "signal-exit": "^4.0.1" + "braces": "^3.0.3", + "picomatch": "^2.3.1" }, "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": ">=8.6" } }, - "node_modules/mocha/node_modules/glob": { - "version": "10.5.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", - "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", + "node_modules/micromatch/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "dev": true, - "license": "ISC", - "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^3.1.2", - "minimatch": "^9.0.4", - "minipass": "^7.1.2", - "package-json-from-dist": "^1.0.0", - "path-scurry": "^1.11.1" - }, - "bin": { - "glob": "dist/esm/bin.mjs" + "license": "MIT", + "engines": { + "node": ">=8.6" }, "funding": { - "url": "https://github.com/sponsors/isaacs" + "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/mocha/node_modules/js-yaml": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", - "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", + "node_modules/min-indent": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", + "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==", "dev": true, "license": "MIT", - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" + "engines": { + "node": ">=4" } }, - "node_modules/mocha/node_modules/signal-exit": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", - "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", "dev": true, "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, "engines": { - "node": ">=14" + "node": ">=16 || 14 >=14.17" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/mocha/node_modules/strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/mocha/node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", "dev": true, - "dependencies": { - "has-flag": "^4.0.0" + "license": "MIT", + "bin": { + "mkdirp": "bin/cmd.js" }, "engines": { "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" } }, "node_modules/ms": { @@ -5231,18 +4857,6 @@ "dev": true, "license": "MIT" }, - "node_modules/node-preload": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/node-preload/-/node-preload-0.2.1.tgz", - "integrity": "sha512-RM5oyBy45cLEoHqCeh+MNuFAxO0vTFBLskvQbOKnEE7YTTSN4tbN8QWDIPQ6L+WvKsB/qLEGpYe2ZZ9d4W9OIQ==", - "dev": true, - "dependencies": { - "process-on-spawn": "^1.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/node-releases": { "version": "2.0.23", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.23.tgz", @@ -5273,214 +4887,6 @@ "semver": "bin/semver" } }, - "node_modules/nyc": { - "version": "17.1.0", - "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", - "caching-transform": "^4.0.0", - "convert-source-map": "^1.7.0", - "decamelize": "^1.2.0", - "find-cache-dir": "^3.2.0", - "find-up": "^4.1.0", - "foreground-child": "^3.3.0", - "get-package-type": "^0.1.0", - "glob": "^7.1.6", - "istanbul-lib-coverage": "^3.0.0", - "istanbul-lib-hook": "^3.0.0", - "istanbul-lib-instrument": "^6.0.2", - "istanbul-lib-processinfo": "^2.0.2", - "istanbul-lib-report": "^3.0.0", - "istanbul-lib-source-maps": "^4.0.0", - "istanbul-reports": "^3.0.2", - "make-dir": "^3.0.0", - "node-preload": "^0.2.1", - "p-map": "^3.0.0", - "process-on-spawn": "^1.0.0", - "resolve-from": "^5.0.0", - "rimraf": "^3.0.0", - "signal-exit": "^3.0.2", - "spawn-wrap": "^2.0.0", - "test-exclude": "^6.0.0", - "yargs": "^15.0.2" - }, - "bin": { - "nyc": "bin/nyc.js" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/nyc/node_modules/cliui": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", - "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", - "dev": true, - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^6.2.0" - } - }, - "node_modules/nyc/node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/nyc/node_modules/foreground-child": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", - "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", - "dev": true, - "license": "ISC", - "dependencies": { - "cross-spawn": "^7.0.6", - "signal-exit": "^4.0.1" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/nyc/node_modules/foreground-child/node_modules/signal-exit": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", - "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/nyc/node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "dependencies": { - "p-locate": "^4.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/nyc/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/nyc/node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "dependencies": { - "p-limit": "^2.2.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/nyc/node_modules/resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/nyc/node_modules/wrap-ansi": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", - "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/nyc/node_modules/y18n": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", - "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", - "dev": true - }, - "node_modules/nyc/node_modules/yargs": { - "version": "15.4.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", - "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", - "dev": true, - "dependencies": { - "cliui": "^6.0.0", - "decamelize": "^1.2.0", - "find-up": "^4.1.0", - "get-caller-file": "^2.0.1", - "require-directory": "^2.1.1", - "require-main-filename": "^2.0.0", - "set-blocking": "^2.0.0", - "string-width": "^4.2.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", - "yargs-parser": "^18.1.2" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/nyc/node_modules/yargs-parser": { - "version": "18.1.3", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", - "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", - "dev": true, - "dependencies": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/oauth-sign": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", - "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", - "dev": true, - "engines": { - "node": "*" - } - }, "node_modules/object-inspect": { "version": "1.13.4", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", @@ -5588,6 +4994,17 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/obug": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/obug/-/obug-2.1.1.tgz", + "integrity": "sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==", + "dev": true, + "funding": [ + "https://github.com/sponsors/sxzz", + "https://opencollective.com/debug" + ], + "license": "MIT" + }, "node_modules/once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -5662,18 +5079,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/p-map": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-3.0.0.tgz", - "integrity": "sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ==", - "dev": true, - "dependencies": { - "aggregate-error": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/p-try": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", @@ -5683,28 +5088,6 @@ "node": ">=6" } }, - "node_modules/package-hash": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/package-hash/-/package-hash-4.0.0.tgz", - "integrity": "sha512-whdkPIooSu/bASggZ96BWVvZTRMOFxnyUG5PnTSGKoJE2gd5mbVNmR2Nj20QFzxYYgAXpoqC+AiXzl+UMRh7zQ==", - "dev": true, - "dependencies": { - "graceful-fs": "^4.1.15", - "hasha": "^5.0.0", - "lodash.flattendeep": "^4.4.0", - "release-zalgo": "^1.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/package-json-from-dist": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", - "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", - "dev": true, - "license": "BlueOak-1.0.0" - }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -5768,37 +5151,14 @@ "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", "dev": true, - "license": "MIT" - }, - "node_modules/path-scurry": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", - "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", - "dev": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "lru-cache": "^10.2.0", - "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" - }, - "engines": { - "node": ">=16 || 14 >=14.18" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/path-scurry/node_modules/lru-cache": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", - "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", - "dev": true, - "license": "ISC" + "license": "MIT" }, - "node_modules/performance-now": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", - "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==", - "dev": true + "node_modules/pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "dev": true, + "license": "MIT" }, "node_modules/picocolors": { "version": "1.1.1", @@ -5820,70 +5180,6 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/pkg-dir": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", - "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", - "dev": true, - "dependencies": { - "find-up": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/pkg-dir/node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/pkg-dir/node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "dependencies": { - "p-locate": "^4.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/pkg-dir/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/pkg-dir/node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "dependencies": { - "p-limit": "^2.2.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/pluralize": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-8.0.0.tgz", @@ -5904,32 +5200,62 @@ "node": ">= 0.4" } }, - "node_modules/prelude-ls": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "node_modules/postcss": { + "version": "8.5.6", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", + "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, "engines": { - "node": ">= 0.8.0" + "node": "^10 || ^12 || >=14" } }, - "node_modules/process-on-spawn": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/process-on-spawn/-/process-on-spawn-1.0.0.tgz", - "integrity": "sha512-1WsPDsUSMmZH5LeMLegqkPDrsGgsWwk1Exipy2hvB0o/F0ASzbpIctSCcZIK1ykJvtTJULEH+20WOFjMvGnCTg==", + "node_modules/postcss/node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", "dev": true, - "dependencies": { - "fromentries": "^1.2.0" + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" }, "engines": { - "node": ">=8" + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, - "node_modules/psl": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", - "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==", - "dev": true + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } }, "node_modules/punycode": { "version": "2.3.1", @@ -5950,15 +5276,6 @@ "node": ">=6" } }, - "node_modules/qs": { - "version": "6.5.3", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.3.tgz", - "integrity": "sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==", - "dev": true, - "engines": { - "node": ">=0.6" - } - }, "node_modules/queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", @@ -5979,16 +5296,6 @@ } ] }, - "node_modules/randombytes": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", - "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "safe-buffer": "^5.1.0" - } - }, "node_modules/read-pkg": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", @@ -6099,20 +5406,6 @@ "node": ">=8" } }, - "node_modules/readdirp": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", - "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 14.18.0" - }, - "funding": { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - }, "node_modules/reflect.getprototypeof": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz", @@ -6189,90 +5482,6 @@ "jsesc": "bin/jsesc" } }, - "node_modules/release-zalgo": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/release-zalgo/-/release-zalgo-1.0.0.tgz", - "integrity": "sha512-gUAyHVHPPC5wdqX/LG4LWtRYtgjxyX78oanFNTMMyFEfOqdC54s3eE82imuWKbOeqYht2CrNf64Qb8vgmmtZGA==", - "dev": true, - "dependencies": { - "es6-error": "^4.0.1" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/repeating": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/repeating/-/repeating-1.1.3.tgz", - "integrity": "sha512-Nh30JLeMHdoI+AsQ5eblhZ7YlTsM9wiJQe/AHIunlK3KWzvXhXb36IJ7K1IOeRjIOtzMjdUHjwXUFxKJoPTSOg==", - "dev": true, - "dependencies": { - "is-finite": "^1.0.0" - }, - "bin": { - "repeating": "cli.js" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/request": { - "version": "2.88.2", - "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", - "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", - "deprecated": "request has been deprecated, see https://github.com/request/request/issues/3142", - "dev": true, - "dependencies": { - "aws-sign2": "~0.7.0", - "aws4": "^1.8.0", - "caseless": "~0.12.0", - "combined-stream": "~1.0.6", - "extend": "~3.0.2", - "forever-agent": "~0.6.1", - "form-data": "~2.3.2", - "har-validator": "~5.1.3", - "http-signature": "~1.2.0", - "is-typedarray": "~1.0.0", - "isstream": "~0.1.2", - "json-stringify-safe": "~5.0.1", - "mime-types": "~2.1.19", - "oauth-sign": "~0.9.0", - "performance-now": "^2.1.0", - "qs": "~6.5.2", - "safe-buffer": "^5.1.2", - "tough-cookie": "~2.5.0", - "tunnel-agent": "^0.6.0", - "uuid": "^3.3.2" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/request/node_modules/uuid": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", - "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", - "deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.", - "dev": true, - "bin": { - "uuid": "bin/uuid" - } - }, - "node_modules/require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/require-main-filename": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", - "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", - "dev": true - }, "node_modules/requizzle": { "version": "0.2.4", "resolved": "https://registry.npmjs.org/requizzle/-/requizzle-0.2.4.tgz", @@ -6348,6 +5557,48 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/rollup": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.53.3.tgz", + "integrity": "sha512-w8GmOxZfBmKknvdXU1sdM9NHcoQejwF/4mNgj2JuEEdRaHwwF12K7e9eXn1nLZ07ad+du76mkVsyeb2rKGllsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.8" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.53.3", + "@rollup/rollup-android-arm64": "4.53.3", + "@rollup/rollup-darwin-arm64": "4.53.3", + "@rollup/rollup-darwin-x64": "4.53.3", + "@rollup/rollup-freebsd-arm64": "4.53.3", + "@rollup/rollup-freebsd-x64": "4.53.3", + "@rollup/rollup-linux-arm-gnueabihf": "4.53.3", + "@rollup/rollup-linux-arm-musleabihf": "4.53.3", + "@rollup/rollup-linux-arm64-gnu": "4.53.3", + "@rollup/rollup-linux-arm64-musl": "4.53.3", + "@rollup/rollup-linux-loong64-gnu": "4.53.3", + "@rollup/rollup-linux-ppc64-gnu": "4.53.3", + "@rollup/rollup-linux-riscv64-gnu": "4.53.3", + "@rollup/rollup-linux-riscv64-musl": "4.53.3", + "@rollup/rollup-linux-s390x-gnu": "4.53.3", + "@rollup/rollup-linux-x64-gnu": "4.53.3", + "@rollup/rollup-linux-x64-musl": "4.53.3", + "@rollup/rollup-openharmony-arm64": "4.53.3", + "@rollup/rollup-win32-arm64-msvc": "4.53.3", + "@rollup/rollup-win32-ia32-msvc": "4.53.3", + "@rollup/rollup-win32-x64-gnu": "4.53.3", + "@rollup/rollup-win32-x64-msvc": "4.53.3", + "fsevents": "~2.3.2" + } + }, "node_modules/run-parallel": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", @@ -6391,26 +5642,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, "node_modules/safe-push-apply": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/safe-push-apply/-/safe-push-apply-1.0.0.tgz", @@ -6446,12 +5677,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "dev": true - }, "node_modules/semver": { "version": "7.7.3", "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", @@ -6465,22 +5690,6 @@ "node": ">=10" } }, - "node_modules/serialize-javascript": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", - "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "randombytes": "^2.1.0" - } - }, - "node_modules/set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", - "dev": true - }, "node_modules/set-function-length": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", @@ -6627,11 +5836,12 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "dev": true + "node_modules/siginfo": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", + "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", + "dev": true, + "license": "ISC" }, "node_modules/sort-array": { "version": "5.1.1", @@ -6664,21 +5874,14 @@ "node": ">=0.10.0" } }, - "node_modules/spawn-wrap": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/spawn-wrap/-/spawn-wrap-2.0.0.tgz", - "integrity": "sha512-EeajNjfN9zMnULLwhZZQU3GWBoFNkbngTUPfaawT4RkMiviTxcX0qfhVbGey39mfctfDHkWtuecgQ8NJcyQWHg==", + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", "dev": true, - "dependencies": { - "foreground-child": "^2.0.0", - "is-windows": "^1.0.2", - "make-dir": "^3.0.0", - "rimraf": "^3.0.0", - "signal-exit": "^3.0.2", - "which": "^2.0.1" - }, + "license": "BSD-3-Clause", "engines": { - "node": ">=8" + "node": ">=0.10.0" } }, "node_modules/spdx-correct": { @@ -6717,36 +5920,19 @@ "dev": true, "license": "CC0-1.0" }, - "node_modules/sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", - "dev": true + "node_modules/stackback": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", + "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", + "dev": true, + "license": "MIT" }, - "node_modules/sshpk": { - "version": "1.18.0", - "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.18.0.tgz", - "integrity": "sha512-2p2KJZTSqQ/I3+HX42EpYOa2l3f8Erv8MWKsy2I9uf4wA7yFIkXRffYdsx86y6z4vHtV8u7g+pPlr8/4ouAxsQ==", + "node_modules/std-env": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.10.0.tgz", + "integrity": "sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==", "dev": true, - "dependencies": { - "asn1": "~0.2.3", - "assert-plus": "^1.0.0", - "bcrypt-pbkdf": "^1.0.0", - "dashdash": "^1.12.0", - "ecc-jsbn": "~0.1.1", - "getpass": "^0.1.1", - "jsbn": "~0.1.0", - "safer-buffer": "^2.0.2", - "tweetnacl": "~0.14.0" - }, - "bin": { - "sshpk-conv": "bin/sshpk-conv", - "sshpk-sign": "bin/sshpk-sign", - "sshpk-verify": "bin/sshpk-verify" - }, - "engines": { - "node": ">=0.10.0" - } + "license": "MIT" }, "node_modules/stop-iteration-iterator": { "version": "1.1.0", @@ -6767,36 +5953,6 @@ "resolved": "https://registry.npmjs.org/string-template/-/string-template-1.0.0.tgz", "integrity": "sha512-SLqR3GBUXuoPP5MmYtD7ompvXiG87QjT6lzOszyXjTM86Uu7At7vNnt2xgyTLq5o9T4IxTYFyGxcULqpsmsfdg==" }, - "node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/string-width-cjs": { - "name": "string-width", - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/string.prototype.trim": { "version": "1.2.10", "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.10.tgz", @@ -6859,34 +6015,11 @@ "node_modules/strip-ansi": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-ansi-cjs": { - "name": "strip-ansi", - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-bom": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", - "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, "engines": { "node": ">=8" } @@ -6969,48 +6102,55 @@ "url": "https://opencollective.com/webpack" } }, - "node_modules/test-exclude": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", - "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true + }, + "node_modules/tinybench": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", + "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", "dev": true, - "dependencies": { - "@istanbuljs/schema": "^0.1.2", - "glob": "^7.1.4", - "minimatch": "^3.0.4" - }, - "engines": { - "node": ">=8" - } + "license": "MIT" }, - "node_modules/test-exclude/node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "node_modules/tinyexec": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.0.2.tgz", + "integrity": "sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg==", "dev": true, "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "engines": { + "node": ">=18" } }, - "node_modules/test-exclude/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "node_modules/tinyglobby": { + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", "dev": true, + "license": "MIT", "dependencies": { - "brace-expansion": "^1.1.7" + "fdir": "^6.5.0", + "picomatch": "^4.0.3" }, "engines": { - "node": "*" + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" } }, - "node_modules/text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", - "dev": true + "node_modules/tinyrainbow": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-3.0.3.tgz", + "integrity": "sha512-PSkbLUoxOFRzJYjjxHJt9xro7D+iilgMX/C9lawzVuYiIdcihh9DXmVibBe8lmcFrRi/VzlPjBxbN7rH24q8/Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } }, "node_modules/to-regex-range": { "version": "5.0.1", @@ -7025,19 +6165,6 @@ "node": ">=8.0" } }, - "node_modules/tough-cookie": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", - "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", - "dev": true, - "dependencies": { - "psl": "^1.1.28", - "punycode": "^2.1.1" - }, - "engines": { - "node": ">=0.8" - } - }, "node_modules/ts-api-utils": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz", @@ -7110,24 +6237,6 @@ "node": ">=4" } }, - "node_modules/tunnel-agent": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", - "dev": true, - "dependencies": { - "safe-buffer": "^5.0.1" - }, - "engines": { - "node": "*" - } - }, - "node_modules/tweetnacl": { - "version": "0.14.5", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", - "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==", - "dev": true - }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -7230,15 +6339,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/typedarray-to-buffer": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", - "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", - "dev": true, - "dependencies": { - "is-typedarray": "^1.0.0" - } - }, "node_modules/typescript": { "version": "5.9.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", @@ -7285,15 +6385,6 @@ "node": ">=0.8.0" } }, - "node_modules/ukkonen": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/ukkonen/-/ukkonen-2.1.0.tgz", - "integrity": "sha512-unACtiJBMpL5Q+JKEBYtB88DVClP4Ch42NFkuj7Ck7jcJ4UKkkfvvfGQ2WeaMeuq7OIGLkbm0X7YN+TGP9C5bw==", - "dev": true, - "engines": { - "node": ">=12" - } - }, "node_modules/unbox-primitive": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz", @@ -7320,41 +6411,6 @@ "dev": true, "license": "MIT" }, - "node_modules/unexpected": { - "version": "13.2.1", - "resolved": "https://registry.npmjs.org/unexpected/-/unexpected-13.2.1.tgz", - "integrity": "sha512-6rWjexbMV4DICp8xeKHKql5JTC5No7Mz55wei2YcXGkqBAtAgfzIhFLzbDohRzN/lNtwG6+NuIvMI/0giQXOvA==", - "dev": true, - "peer": true, - "dependencies": { - "array-changes": "3.0.1", - "array-changes-async": "3.0.1", - "detect-indent": "3.0.1", - "diff": "^5.0.0", - "greedy-interval-packer": "1.2.0", - "magicpen": "^6.2.4", - "ukkonen": "^2.1.0", - "unexpected-bluebird": "2.9.34-longstack2" - } - }, - "node_modules/unexpected-bluebird": { - "version": "2.9.34-longstack2", - "resolved": "https://registry.npmjs.org/unexpected-bluebird/-/unexpected-bluebird-2.9.34-longstack2.tgz", - "integrity": "sha512-lAgr5q+ToN4cO+mCus6h9VLcnl27fCiWiCuDyx7Pcvf9IoFOaTRv0bauvikXRkg9+78c/1nDBbQxP+Wk9+uOCA==", - "dev": true - }, - "node_modules/unexpected-eventemitter": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/unexpected-eventemitter/-/unexpected-eventemitter-2.4.0.tgz", - "integrity": "sha512-RhqPmdrD9T62HVzIdild1gGL6bkwk6QNh52FJxEmPaHTLOZBXmBASRpjzpCkRlP+E137qduV7ePSBCF5pUyLnw==", - "dev": true, - "engines": { - "node": ">=8" - }, - "peerDependencies": { - "unexpected": "^10 || ^11 || ^12 || ^13" - } - }, "node_modules/update-browserslist-db": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", @@ -7395,15 +6451,6 @@ "punycode": "^2.1.0" } }, - "node_modules/uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", - "dev": true, - "bin": { - "uuid": "dist/bin/uuid" - } - }, "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", @@ -7415,18 +6462,159 @@ "spdx-expression-parse": "^3.0.0" } }, - "node_modules/verror": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", - "integrity": "sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==", + "node_modules/vite": { + "version": "7.2.7", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.2.7.tgz", + "integrity": "sha512-ITcnkFeR3+fI8P1wMgItjGrR10170d8auB4EpMLPqmx6uxElH3a/hHGQabSHKdqd4FXWO1nFIp9rRn7JQ34ACQ==", "dev": true, - "engines": [ - "node >=0.6.0" - ], + "license": "MIT", + "peer": true, + "dependencies": { + "esbuild": "^0.25.0", + "fdir": "^6.5.0", + "picomatch": "^4.0.3", + "postcss": "^8.5.6", + "rollup": "^4.43.0", + "tinyglobby": "^0.2.15" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "lightningcss": "^1.21.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/vitest": { + "version": "4.0.15", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.0.15.tgz", + "integrity": "sha512-n1RxDp8UJm6N0IbJLQo+yzLZ2sQCDyl1o0LeugbPWf8+8Fttp29GghsQBjYJVmWq3gBFfe9Hs1spR44vovn2wA==", + "dev": true, + "license": "MIT", + "peer": true, "dependencies": { - "assert-plus": "^1.0.0", - "core-util-is": "1.0.2", - "extsprintf": "^1.2.0" + "@vitest/expect": "4.0.15", + "@vitest/mocker": "4.0.15", + "@vitest/pretty-format": "4.0.15", + "@vitest/runner": "4.0.15", + "@vitest/snapshot": "4.0.15", + "@vitest/spy": "4.0.15", + "@vitest/utils": "4.0.15", + "es-module-lexer": "^1.7.0", + "expect-type": "^1.2.2", + "magic-string": "^0.30.21", + "obug": "^2.1.1", + "pathe": "^2.0.3", + "picomatch": "^4.0.3", + "std-env": "^3.10.0", + "tinybench": "^2.9.0", + "tinyexec": "^1.0.2", + "tinyglobby": "^0.2.15", + "tinyrainbow": "^3.0.3", + "vite": "^6.0.0 || ^7.0.0", + "why-is-node-running": "^2.3.0" + }, + "bin": { + "vitest": "vitest.mjs" + }, + "engines": { + "node": "^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@edge-runtime/vm": "*", + "@opentelemetry/api": "^1.9.0", + "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0", + "@vitest/browser-playwright": "4.0.15", + "@vitest/browser-preview": "4.0.15", + "@vitest/browser-webdriverio": "4.0.15", + "@vitest/ui": "4.0.15", + "happy-dom": "*", + "jsdom": "*" + }, + "peerDependenciesMeta": { + "@edge-runtime/vm": { + "optional": true + }, + "@opentelemetry/api": { + "optional": true + }, + "@types/node": { + "optional": true + }, + "@vitest/browser-playwright": { + "optional": true + }, + "@vitest/browser-preview": { + "optional": true + }, + "@vitest/browser-webdriverio": { + "optional": true + }, + "@vitest/ui": { + "optional": true + }, + "happy-dom": { + "optional": true + }, + "jsdom": { + "optional": true + } } }, "node_modules/vscode-json-languageservice": { @@ -7563,12 +6751,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/which-module": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.1.tgz", - "integrity": "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==", - "dev": true - }, "node_modules/which-typed-array": { "version": "1.1.19", "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.19.tgz", @@ -7591,6 +6773,23 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/why-is-node-running": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", + "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", + "dev": true, + "license": "MIT", + "dependencies": { + "siginfo": "^2.0.0", + "stackback": "0.0.2" + }, + "bin": { + "why-is-node-running": "cli.js" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/word-wrap": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", @@ -7617,68 +6816,12 @@ "node": ">=12.17" } }, - "node_modules/workerpool": { - "version": "9.3.4", - "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-9.3.4.tgz", - "integrity": "sha512-TmPRQYYSAnnDiEB0P/Ytip7bFGvqnSU6I2BcuSw7Hx+JSg/DsUi5ebYfc8GYaSdpuvOcEs6dXxPurOYpe9QFwg==", - "dev": true, - "license": "Apache-2.0" - }, - "node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/wrap-ansi-cjs": { - "name": "wrap-ansi", - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", "dev": true }, - "node_modules/write-file-atomic": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", - "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", - "dev": true, - "dependencies": { - "imurmurhash": "^0.1.4", - "is-typedarray": "^1.0.0", - "signal-exit": "^3.0.2", - "typedarray-to-buffer": "^3.1.5" - } - }, "node_modules/xmlcreate": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/xmlcreate/-/xmlcreate-2.0.4.tgz", @@ -7686,91 +6829,6 @@ "dev": true, "license": "Apache-2.0" }, - "node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=10" - } - }, - "node_modules/yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true, - "license": "ISC" - }, - "node_modules/yargs": { - "version": "17.7.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", - "dev": true, - "license": "MIT", - "dependencies": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=12" - } - }, - "node_modules/yargs-unparser": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", - "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", - "dev": true, - "dependencies": { - "camelcase": "^6.0.0", - "decamelize": "^4.0.0", - "flat": "^5.0.2", - "is-plain-obj": "^2.1.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/yargs-unparser/node_modules/camelcase": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/yargs-unparser/node_modules/decamelize": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", - "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "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 e294983..4f67c65 100644 --- a/package.json +++ b/package.json @@ -33,14 +33,12 @@ "author": "Nodecraft, Inc.", "main": "index.js", "scripts": { - "coverage": "nyc report --reporter=lcov", + "coverage": "vitest run --coverage", "docs": "jsdoc2md lib/spawnpoint.js > docs.md", "lint": "eslint .", "lint:fix": "eslint . --fix", - "mocha": "mocha", - "nyc": "nyc mocha", "precommit": "lint", - "test": "npm run lint && npm run nyc" + "test": "npm run lint && vitest run" }, "dependencies": { "async": "^3.2.6", @@ -54,16 +52,13 @@ }, "devDependencies": { "@nodecraft/eslint-config": "^46.0.0", - "coveralls": "^3.1.1", + "@vitest/coverage-v8": "^4.0.15", "eslint": "^8.57.1", "eslint-plugin-json": "^4.0.1", "eslint-plugin-n": "^17.23.1", "eslint-plugin-unicorn": "^56.0.1", "jsdoc-to-markdown": "^9.1.3", - "mocha": "^11.7.5", - "nyc": "^17.1.0", - "unexpected": "^13.2.1", - "unexpected-eventemitter": "^2.4.0" + "vitest": "^4.0.15" }, "engines": { "node": ">=22", diff --git a/test/.eslintrc.json b/test/.eslintrc.json index 3803f98..cc29735 100644 --- a/test/.eslintrc.json +++ b/test/.eslintrc.json @@ -3,7 +3,14 @@ "../.eslintrc.json" ], "env": { - "node": true, - "mocha": true - } -} \ No newline at end of file + "node": true + }, + "overrides": [ + { + "files": ["*.mjs"], + "parserOptions": { + "sourceType": "module" + } + } + ] +} diff --git a/test/code.js b/test/code.js deleted file mode 100644 index fff32b2..0000000 --- a/test/code.js +++ /dev/null @@ -1,39 +0,0 @@ -'use strict'; -const assert = require('node:assert'); - -const spawnpoint = require('..'); - -process.chdir(__dirname); -describe('spawnpoint.code', () => { - const app = new spawnpoint(); - app.setup(); - - const tests = { - system: { - code: 'spawnpoint.register_plugin_on_runtime', - message: 'App plugin registration has already occurred.', - }, - custom: { - code: 'test.code', - message: 'This is a test code.', - }, - }; - - it('should print a system code', () => { - assert.deepStrictEqual(tests.system, app.code('spawnpoint.register_plugin_on_runtime')); - }); - it('should print a app code', () => { - assert.deepStrictEqual(tests.custom, app.code('test.code')); - }); - it('Throws on an unset code', () => { - assert.throws(() => app.code('invalid.unset.code'), Error); - }); - it('Throws on an invalid input', () => { - assert.throws(() => app.code(), Error); - assert.throws(() => app.code(null), Error); - assert.throws(() => app.code(true), Error); - assert.throws(() => app.code({ foo: 'bar' }), Error); - assert.throws(() => app.code(['foo', 'bar']), Error); - }); - // TODO: print a plugin code -}); diff --git a/test/code.mjs b/test/code.mjs new file mode 100644 index 0000000..27271c8 --- /dev/null +++ b/test/code.mjs @@ -0,0 +1,51 @@ +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; + +import { + beforeAll, + describe, + expect, + it, +} from 'vitest'; + +import spawnpoint from '../index.js'; + +const __dirname = path.dirname(fileURLToPath(import.meta.url)); + +describe('spawnpoint.code', () => { + let app; + + const tests = { + system: { + code: 'spawnpoint.register_plugin_on_runtime', + message: 'App plugin registration has already occurred.', + }, + custom: { + code: 'test.code', + message: 'This is a test code.', + }, + }; + + beforeAll(() => new Promise((resolve) => { + app = new spawnpoint({ cwd: __dirname }); + app.setup(resolve); + })); + + it('should print a system code', () => { + expect(app.code('spawnpoint.register_plugin_on_runtime')).toEqual(tests.system); + }); + it('should print a app code', () => { + expect(app.code('test.code')).toEqual(tests.custom); + }); + it('Throws on an unset code', () => { + expect(() => app.code('invalid.unset.code')).toThrow(Error); + }); + it('Throws on an invalid input', () => { + expect(() => app.code()).toThrow(Error); + expect(() => app.code(null)).toThrow(Error); + expect(() => app.code(true)).toThrow(Error); + expect(() => app.code({ foo: 'bar' })).toThrow(Error); + expect(() => app.code(['foo', 'bar'])).toThrow(Error); + }); + // TODO: print a plugin code +}); diff --git a/test/config.js b/test/config.js deleted file mode 100644 index f85a6ae..0000000 --- a/test/config.js +++ /dev/null @@ -1,110 +0,0 @@ -'use strict'; - -const async = require('async'); -const _ = require('lodash'); -const expect = require('unexpected'); - -const spawnpoint = require('..'); - -describe('spawnpoint.initConfig', () => { - it('successfully takes a configFile in the constructor', () => { - const app = new spawnpoint('config/app'); - app.initConfig(); - expect(app.config, 'to satisfy', { - name: expect.it('to be', 'Simple, no extras'), - log: expect.it('to be null'), - signals: expect.it('to be null'), - catchExceptions: expect.it('to be false'), - }); - }); - - it('should reassign a configFile if passed', () => { - const app = new spawnpoint('config/app'); - app.initConfig('config/limitErrors.json'); - expect(app.config, 'to satisfy', { - name: expect.it('to be', 'Simple, tracking errors.'), - log: expect.it('to be null'), - signals: expect.it('to be null'), - catchExceptions: expect.it('to be false'), - trackErrors: expect.it('to be true'), - }); - }); - - it('automatically sets a configOverride if needed', () => { - const app = new spawnpoint('config/debugEnabled'); - app.initConfig(); - expect(app.config, 'to satisfy', { - name: expect.it('to be', 'Simple, debug mode enabled.'), - debug: expect.it('to be true'), - configOverride: expect.it('to be', 'dev-config.json'), - }); - }); - - it('resets config blocklist options', () => { - const app = new spawnpoint('config/resetConfigBlocklist'); - app.initConfig(); - expect(app.config, 'to satisfy', { - name: expect.it('to be', 'Simple, resetting the config blocklist.'), - resetConfigBlockListDefaults: expect.it('to be true'), - }); - expect(app.configBlocklist, 'to have values satisfying', 'to have values satisfying', 'to be empty'); - }); - - it('is able to add a blocklist option', () => { - const app = new spawnpoint('config/configBlocklisting.json'); - app.initConfig(); - expect(app.config, 'to satisfy', { - name: expect.it('to be', 'Simple, with a config blocklist.'), - }); - expect(app.configBlocklist.env.list, 'to have an item satisfying', 'to equal', 'PATH'); - }); - - it('successfully registers helper methods', (done) => { - const app = new spawnpoint(); - app.initConfig(); - expect(app.config.get, 'when called with', ['codes'], 'to equal', '/config/codes'); - expect(app.config.has, 'when called with', ['log.format'], 'to be false'); - app.initCodes(); - expect(() => { - app.config.getRandom('log.format'); - }, 'to throw'); - app.config.numArray = [1, 2, 3, 4, 5]; - expect(app.config.getRandom, 'when called with', ['numArray'], 'to be one of', app.config.numArray); - let used = []; - _.times(app.config.numArray.length, () => { - const item = app.config.getRoundRobin('numArray'); - expect(item, 'to be one of', app.config.numArray).and('not to be one of', used); - used.push(item); - }); - - used = {}; - async.times(app.config.numArray.length * 15, (i, cb) => { - app.config.getAndLock('numArray', (err, results, clear) => { - if (err) { return cb(err); } - if (used[results]) { - clear(); - return cb(new Error('Returned another result that is already in use.')); - } - used[results] = true; - setTimeout(() => { - used[results] = false; - clear(); - return cb(); - }, _.random(10, 75)); - }); - }, done); - }); -}); - -describe('spawnpoint.loadConfig', () => { - it('loads a plugin\'s configs', () => { - const app = new spawnpoint('config/loadPlugins'); - app.initConfig(); - app.loadPlugins(); - app.loadConfig(); - expect(app.config.test, 'to satisfy', { - test: false, - name: 'TestThing', - }); - }); -}); diff --git a/test/config.mjs b/test/config.mjs new file mode 100644 index 0000000..360b88b --- /dev/null +++ b/test/config.mjs @@ -0,0 +1,111 @@ +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; + +import async from 'async'; +import _ from 'lodash'; +import { describe, expect, it } from 'vitest'; + +import spawnpoint from '../index.js'; + +const __dirname = path.dirname(fileURLToPath(import.meta.url)); + +describe('spawnpoint.initConfig', () => { + it('successfully takes a configFile in the constructor', () => { + const app = new spawnpoint({ cwd: __dirname, configFile: 'config/app' }); + app.initConfig(); + expect(app.config.name).toBe('Simple, no extras'); + expect(app.config.log).toBeNull(); + expect(app.config.signals).toBeNull(); + expect(app.config.catchExceptions).toBe(false); + }); + + it('should reassign a configFile if passed', () => { + const app = new spawnpoint({ cwd: __dirname, configFile: 'config/app' }); + app.initConfig('config/limitErrors.json'); + expect(app.config.name).toBe('Simple, tracking errors.'); + expect(app.config.log).toBeNull(); + expect(app.config.signals).toBeNull(); + expect(app.config.catchExceptions).toBe(false); + expect(app.config.trackErrors).toBe(true); + }); + + it('automatically sets a configOverride if needed', () => { + const app = new spawnpoint({ cwd: __dirname, configFile: 'config/debugEnabled' }); + app.initConfig(); + expect(app.config.name).toBe('Simple, debug mode enabled.'); + expect(app.config.debug).toBe(true); + expect(app.config.configOverride).toBe('dev-config.json'); + }); + + it('resets config blocklist options', () => { + const app = new spawnpoint({ cwd: __dirname, configFile: 'config/resetConfigBlocklist' }); + app.initConfig(); + expect(app.config.name).toBe('Simple, resetting the config blocklist.'); + expect(app.config.resetConfigBlockListDefaults).toBe(true); + for (const blocklist of Object.values(app.configBlocklist)) { + for (const list of Object.values(blocklist)) { + expect(list).toHaveLength(0); + } + } + }); + + it('is able to add a blocklist option', () => { + const app = new spawnpoint({ cwd: __dirname, configFile: 'config/configBlocklisting.json' }); + app.initConfig(); + expect(app.config.name).toBe('Simple, with a config blocklist.'); + expect(app.configBlocklist.env.list).toContain('PATH'); + }); + + it('successfully registers helper methods', () => new Promise((resolve, reject) => { + const app = new spawnpoint({ cwd: __dirname }); + app.initConfig(); + expect(app.config.get('codes')).toBe('/config/codes'); + expect(app.config.has('log.format')).toBe(false); + app.initCodes(); + expect(() => { + app.config.getRandom('log.format'); + }).toThrow(); + app.config.numArray = [1, 2, 3, 4, 5]; + expect(app.config.numArray).toContain(app.config.getRandom('numArray')); + let used = []; + _.times(app.config.numArray.length, () => { + const item = app.config.getRoundRobin('numArray'); + expect(app.config.numArray).toContain(item); + expect(used).not.toContain(item); + used.push(item); + }); + + used = {}; + async.times(app.config.numArray.length * 15, (i, cb) => { + app.config.getAndLock('numArray', (err, results, clear) => { + if (err) { return cb(err); } + if (used[results]) { + clear(); + return cb(new Error('Returned another result that is already in use.')); + } + used[results] = true; + setTimeout(() => { + used[results] = false; + clear(); + return cb(); + }, _.random(10, 75)); + }); + }, (err) => { + if (err) { return reject(err); } + resolve(); + }); + })); +}); + +describe('spawnpoint.loadConfig', () => { + it('loads a plugin\'s configs', () => { + const app = new spawnpoint({ cwd: __dirname, configFile: 'config/loadPlugins' }); + app.initConfig(); + app.loadPlugins(); + app.loadConfig(); + expect(app.config.test).toMatchObject({ + test: false, + name: 'TestThing', + }); + }); +}); diff --git a/test/config/blocklistPatterns.json b/test/config/blocklistPatterns.json new file mode 100644 index 0000000..a520660 --- /dev/null +++ b/test/config/blocklistPatterns.json @@ -0,0 +1,20 @@ +{ + "name": "Simple, with blocklist patterns.", + "log": null, + "signals": null, + "catchExceptions": false, + "configBlocklist": { + "env": { + "list": ["BLOCKED_VAR"], + "patterns": ["^SECRET_", "^PRIVATE_"] + }, + "secrets": { + "list": [], + "patterns": ["^api_key"] + }, + "args": { + "list": ["blocked-arg"], + "patterns": ["^secret-"] + } + } +} diff --git a/test/config/pluginSideload.json b/test/config/pluginSideload.json new file mode 100644 index 0000000..8a0db8e --- /dev/null +++ b/test/config/pluginSideload.json @@ -0,0 +1,13 @@ +{ + "name": "Simple, with sideloaded plugin.", + "log": null, + "signals": null, + "catchExceptions": false, + "plugins": [ + { + "plugin": "../test/spawnpoint-test", + "name": "SideloadedPlugin", + "namespace": "customNamespace" + } + ] +} diff --git a/test/errorCode.js b/test/errorCode.js deleted file mode 100644 index 0a431f6..0000000 --- a/test/errorCode.js +++ /dev/null @@ -1,41 +0,0 @@ -'use strict'; -const assert = require('node:assert'); - -const spawnpoint = require('..'); - -process.chdir(__dirname); -describe('spawnpoint.errorCode', () => { - const app = new spawnpoint(); - app.setup(); - - it('Is an errorCode instance', () => { - assert(app.errorCode('test.code') instanceof app._errorCode); - }); - - it('Returns proper json serialized result without data', () => { - const code = app.errorCode('test.code'); - const json = JSON.stringify(code); - assert.strictEqual(json, '{"name":"errorCode","message":"This is a test code.","code":"test.code","data":{}}'); - }); - - it('Returns proper json serialized result with data', () => { - const code = app.errorCode('test.code', { - foo: 'bar', - }); - const json = JSON.stringify(code); - assert.strictEqual(json, '{"name":"errorCode","message":"This is a test code.","code":"test.code","data":{"foo":"bar"}}'); - }); - - it('Throws on an unset code', () => { - assert.throws(() => app.errorCode('invalid.unset.code'), Error); - }); - - it('Throws on an invalid input', () => { - assert.throws(() => app.errorCode(), Error); - assert.throws(() => app.errorCode(null), Error); - assert.throws(() => app.errorCode(true), Error); - assert.throws(() => app.errorCode({ foo: 'bar' }), Error); - assert.throws(() => app.errorCode(['foo', 'bar']), Error); - }); - // TODO: print a plugin code -}); diff --git a/test/errorCode.mjs b/test/errorCode.mjs new file mode 100644 index 0000000..438f7df --- /dev/null +++ b/test/errorCode.mjs @@ -0,0 +1,53 @@ +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; + +import { + beforeAll, + describe, + expect, + it, +} from 'vitest'; + +import spawnpoint from '../index.js'; + +const __dirname = path.dirname(fileURLToPath(import.meta.url)); + +describe('spawnpoint.errorCode', () => { + let app; + + beforeAll(() => new Promise((resolve) => { + app = new spawnpoint({ cwd: __dirname }); + app.setup(resolve); + })); + + it('Is an errorCode instance', () => { + expect(app.errorCode('test.code')).toBeInstanceOf(app._errorCode); + }); + + it('Returns proper json serialized result without data', () => { + const code = app.errorCode('test.code'); + const json = JSON.stringify(code); + expect(json).toBe('{"name":"errorCode","message":"This is a test code.","code":"test.code","data":{}}'); + }); + + it('Returns proper json serialized result with data', () => { + const code = app.errorCode('test.code', { + foo: 'bar', + }); + const json = JSON.stringify(code); + expect(json).toBe('{"name":"errorCode","message":"This is a test code.","code":"test.code","data":{"foo":"bar"}}'); + }); + + it('Throws on an unset code', () => { + expect(() => app.errorCode('invalid.unset.code')).toThrow(Error); + }); + + it('Throws on an invalid input', () => { + expect(() => app.errorCode()).toThrow(Error); + expect(() => app.errorCode(null)).toThrow(Error); + expect(() => app.errorCode(true)).toThrow(Error); + expect(() => app.errorCode({ foo: 'bar' })).toThrow(Error); + expect(() => app.errorCode(['foo', 'bar'])).toThrow(Error); + }); + // TODO: print a plugin code +}); diff --git a/test/errorsRegistration.js b/test/errorsRegistration.js deleted file mode 100644 index 81b6a19..0000000 --- a/test/errorsRegistration.js +++ /dev/null @@ -1,105 +0,0 @@ -/* eslint-disable unicorn/custom-error-definition */ -'use strict'; -const assert = require('node:assert'); - -const spawnpoint = require('..'); - -// resources for creating tests: -// https://sinonjs.org/ -// https://github.com/elliotf/mocha-sinon -// https://github.com/mochajs/mocha/issues/1582 - -process.chdir(__dirname); - -// define custom error for testing -class customError extends Error { - constructor(err) { - super(err); - this.name = 'customError'; - } -} - -class anotherCustomError extends Error { - constructor(err) { - super(err); - this.name = 'customErrorB'; - } -} - -describe('spawnpoint.registerError', () => { - let app; - beforeEach((done) => { - app = new spawnpoint(); - app.setup(done); - }); - - it('can create a customError for testing', () => { - assert(new customError('test') instanceof customError, 'customError failed to create test'); - }); - - it('can register an error', () => { - app.registerError('test.code', customError); - assert(app.errorMaps['test.code']); - }); -}); - -describe('spawnpoint.maskErrorToCode', () => { - let app; - beforeEach((done) => { - app = new spawnpoint(); - app.registerError('test.code', customError); - app.registerError('test.code.b', anotherCustomError); - app.setup(done); - }); - - it('can register an error', () => { - assert(app.maskErrorToCode(new customError('test'))); - }); - - it('throws when invalid type is passed', () => { - assert.throws(() => app.maskErrorToCode(new customError('test'), 'invalidError')); - }); - - it('is errorCode', () => { - assert(app.maskErrorToCode(new customError('test'), 'errorCode') instanceof app._errorCode); - }); - - it('is failCode', () => { - assert(app.maskErrorToCode(new customError('test'), 'failCode') instanceof app._failCode); - }); -}); - -describe('spawnpoint.loadErrorMap', () => { - let app; - beforeEach(() => { - app = new spawnpoint(); - }); - - it('Runs correctly, without plugins', (done) => { - app.setup(() => { - app.on('app.setup.loadErrorMap', done); - app.loadErrorMap(); - }); - }); - - it('Runs correctly, with plugins', (done) => { - app.setup(() => { - app.plugins = [ - { - name: 'fake plugin`', - errors: { - 'test.code': customError, - }, - }, - { - name: 'Another fake plugin', - }, - ]; - app.on('app.setup.loadErrorMap', () => { - assert(app.errorMaps['test.code']); - done(); - }); - app.loadErrorMap(); - }); - }); -}); diff --git a/test/errorsRegistration.mjs b/test/errorsRegistration.mjs new file mode 100644 index 0000000..9d34787 --- /dev/null +++ b/test/errorsRegistration.mjs @@ -0,0 +1,142 @@ +/* eslint-disable unicorn/custom-error-definition */ +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; + +import { + beforeEach, + describe, + expect, + it, +} from 'vitest'; + +import spawnpoint from '../index.js'; + +const __dirname = path.dirname(fileURLToPath(import.meta.url)); + +// resources for creating tests: +// https://sinonjs.org/ +// https://github.com/elliotf/mocha-sinon +// https://github.com/mochajs/mocha/issues/1582 + +// define custom error for testing +class customError extends Error { + constructor(err) { + super(err); + this.name = 'customError'; + } +} + +class anotherCustomError extends Error { + constructor(err) { + super(err); + this.name = 'customErrorB'; + } +} + +describe('spawnpoint.registerError', () => { + let app; + beforeEach(() => new Promise((resolve) => { + app = new spawnpoint({ cwd: __dirname }); + app.setup(resolve); + })); + + it('can create a customError for testing', () => { + expect(new customError('test')).toBeInstanceOf(customError); + }); + + it('can register an error', () => { + app.registerError('test.code', customError); + expect(app.errorMaps['test.code']).toBeTruthy(); + }); +}); + +describe('spawnpoint.maskErrorToCode', () => { + let app; + beforeEach(() => new Promise((resolve) => { + app = new spawnpoint({ cwd: __dirname }); + app.registerError('test.code', customError); + app.registerError('test.code.b', anotherCustomError); + app.setup(resolve); + })); + + it('can register an error', () => { + expect(app.maskErrorToCode(new customError('test'))).toBeTruthy(); + }); + + it('throws when invalid type is passed', () => { + expect(() => app.maskErrorToCode(new customError('test'), 'invalidError')).toThrow(); + }); + + it('is errorCode', () => { + expect(app.maskErrorToCode(new customError('test'), 'errorCode')).toBeInstanceOf(app._errorCode); + }); + + it('is failCode', () => { + expect(app.maskErrorToCode(new customError('test'), 'failCode')).toBeInstanceOf(app._failCode); + }); +}); + +describe('spawnpoint.loadErrorMap', () => { + let app; + beforeEach(() => { + app = new spawnpoint({ cwd: __dirname }); + }); + + it('Runs correctly, without plugins', () => new Promise((resolve) => { + app.setup(() => { + app.on('app.setup.loadErrorMap', resolve); + app.loadErrorMap(); + }); + })); + + it('Runs correctly, with plugins', () => new Promise((resolve) => { + app.setup(() => { + app.plugins = [ + { + name: 'fake plugin`', + errors: { + 'test.code': customError, + }, + }, + { + name: 'Another fake plugin', + }, + ]; + app.on('app.setup.loadErrorMap', () => { + expect(app.errorMaps['test.code']).toBeTruthy(); + resolve(); + }); + app.loadErrorMap(); + }); + })); +}); + +describe('spawnpoint.maskErrorToCode edge cases', () => { + let app; + beforeEach(() => new Promise((resolve) => { + app = new spawnpoint({ cwd: __dirname }); + app.registerError('test.code', customError); + app.setup(resolve); + })); + + it('returns false when error does not match any registered error', () => { + const result = app.maskErrorToCode(new Error('generic error')); + expect(result).toBe(false); + }); + + it('returns false for non-Error objects', () => { + const result = app.maskErrorToCode('not an error'); + expect(result).toBe(false); + }); + + it('returns false for null', () => { + const result = app.maskErrorToCode(null); + expect(result).toBe(false); + }); + + it('correctly masks error to code type', () => { + const result = app.maskErrorToCode(new customError('test'), 'code'); + expect(result).toBeTruthy(); + expect(result.code).toBe('test.code'); + }); +}); diff --git a/test/failcode.js b/test/failcode.js deleted file mode 100644 index 791e6cf..0000000 --- a/test/failcode.js +++ /dev/null @@ -1,47 +0,0 @@ -'use strict'; -const assert = require('node:assert'); - -const spawnpoint = require('..'); - -process.chdir(__dirname); -describe('spawnpoint.failCode', () => { - const app = new spawnpoint(); - app.setup(); - - it('Is an failCode instance', () => { - assert(app.failCode('test.code') instanceof app._failCode); - }); - - it('Returns proper json serialized result without data', () => { - const code = app.failCode('test.code'); - const json = JSON.stringify(code); - assert.strictEqual(json, '{"name":"failCode","message":"This is a test code.","code":"test.code","data":{}}'); - }); - - it('Returns proper json serialized result with data', () => { - const code = app.failCode('test.code', { - foo: 'bar', - }); - const json = JSON.stringify(code); - assert.strictEqual(json, '{"name":"failCode","message":"This is a test code.","code":"test.code","data":{"foo":"bar"}}'); - }); - - it('Throws on an unset code', () => { - assert.throws(() => app.failCode('invalid.unset.code'), Error); - }); - - it('Throws on an invalid input', () => { - assert.throws(() => app.failCode(), Error); - assert.throws(() => app.failCode(null), Error); - assert.throws(() => app.failCode(true), Error); - assert.throws(() => app.failCode({ foo: 'bar' }), Error); - assert.throws(() => app.failCode(['foo', 'bar']), Error); - }); - - it('Ensures data object does not have duplicate code or message', () => { - const failCode = app.failCode('UNKNOWN', { foo: 'bar' }); - assert.notStrictEqual(failCode.message, failCode.data.message); - assert.notStrictEqual(failCode.code, failCode.data.code); - }); - // TODO: print a plugin code -}); diff --git a/test/failcode.mjs b/test/failcode.mjs new file mode 100644 index 0000000..cb02e93 --- /dev/null +++ b/test/failcode.mjs @@ -0,0 +1,59 @@ +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; + +import { + beforeAll, + describe, + expect, + it, +} from 'vitest'; + +import spawnpoint from '../index.js'; + +const __dirname = path.dirname(fileURLToPath(import.meta.url)); + +describe('spawnpoint.failCode', () => { + let app; + + beforeAll(() => new Promise((resolve) => { + app = new spawnpoint({ cwd: __dirname }); + app.setup(resolve); + })); + + it('Is an failCode instance', () => { + expect(app.failCode('test.code')).toBeInstanceOf(app._failCode); + }); + + it('Returns proper json serialized result without data', () => { + const code = app.failCode('test.code'); + const json = JSON.stringify(code); + expect(json).toBe('{"name":"failCode","message":"This is a test code.","code":"test.code","data":{}}'); + }); + + it('Returns proper json serialized result with data', () => { + const code = app.failCode('test.code', { + foo: 'bar', + }); + const json = JSON.stringify(code); + expect(json).toBe('{"name":"failCode","message":"This is a test code.","code":"test.code","data":{"foo":"bar"}}'); + }); + + it('Throws on an unset code', () => { + expect(() => app.failCode('invalid.unset.code')).toThrow(Error); + }); + + it('Throws on an invalid input', () => { + expect(() => app.failCode()).toThrow(Error); + expect(() => app.failCode(null)).toThrow(Error); + expect(() => app.failCode(true)).toThrow(Error); + expect(() => app.failCode({ foo: 'bar' })).toThrow(Error); + expect(() => app.failCode(['foo', 'bar'])).toThrow(Error); + }); + + it('Ensures data object does not have duplicate code or message', () => { + const failCode = app.failCode('UNKNOWN', { foo: 'bar' }); + expect(failCode.message).not.toBe(failCode.data.message); + expect(failCode.code).not.toBe(failCode.data.code); + }); + // TODO: print a plugin code +}); diff --git a/test/getAndLock.js b/test/getAndLock.mjs similarity index 50% rename from test/getAndLock.js rename to test/getAndLock.mjs index 95376ef..fc9d4f9 100644 --- a/test/getAndLock.js +++ b/test/getAndLock.mjs @@ -1,25 +1,28 @@ -'use strict'; -const assert = require('node:assert'); +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; -const async = require('async'); -const _ = require('lodash'); +import async from 'async'; +import _ from 'lodash'; +import { describe, expect, it } from 'vitest'; -const spawnpoint = require('..'); +import spawnpoint from '../index.js'; + +const __dirname = path.dirname(fileURLToPath(import.meta.url)); describe('spawnpoint.getAndLock', () => { - const app = new spawnpoint(); + const app = new spawnpoint({ cwd: __dirname }); const test = ['one', 'two', 'three', 'four', 'five']; it('fails with bad/invalid options', () => { - assert.throws(() => app.getAndLock(''), Error); - assert.throws(() => app.getAndLock(10), Error); - assert.throws(() => app.getAndLock(false), Error); - assert.throws(() => app.getAndLock(true), Error); - assert.throws(() => app.getAndLock({ foo: 'bar' }), Error); - assert.throws(() => app.getAndLock('five'), Error); + expect(() => app.getAndLock('')).toThrow(Error); + expect(() => app.getAndLock(10)).toThrow(Error); + expect(() => app.getAndLock(false)).toThrow(Error); + expect(() => app.getAndLock(true)).toThrow(Error); + expect(() => app.getAndLock({ foo: 'bar' })).toThrow(Error); + expect(() => app.getAndLock('five')).toThrow(Error); }); - it('never calls the same locked value async', (done) => { + it('never calls the same locked value async', () => new Promise((resolve, reject) => { const lock = app.getAndLock(test); const used = {}; @@ -37,11 +40,14 @@ describe('spawnpoint.getAndLock', () => { return cb(); }, _.random(10, 75)); }); - }, done); - }); + }, (err) => { + if (err) { return reject(err); } + resolve(); + }); + })); - it('Still works even when spawnpoint is initialized', (done) => { - const newApp = new spawnpoint(); + it('Still works even when spawnpoint is initialized', () => new Promise((resolve, reject) => { + const newApp = new spawnpoint({ cwd: __dirname }); newApp.setup(); const lock = newApp.getAndLock(test); @@ -60,24 +66,28 @@ describe('spawnpoint.getAndLock', () => { return cb(); }, _.random(10, 75)); }); - }, done); - }); + }, (err) => { + if (err) { return reject(err); } + resolve(); + }); + })); - it('Correctly does not trigger timeout', (done) => { + it('Correctly does not trigger timeout', () => new Promise((resolve, reject) => { const lock = app.getAndLock(test); for (let i = 0; i < test.length; i++) { lock.next((err, results, clear) => { - if (err) { return done(err); } + if (err) { return reject(err); } setTimeout(clear, 1); }); } lock.next(50, (err, results, clear) => { clear(); - done(err); + if (err) { return reject(err); } + resolve(); }); - }); + })); - it('Correctly triggers timeout', (done) => { + it('Correctly triggers timeout', () => new Promise((resolve) => { const lock = app.getAndLock(test); for (let i = 0; i < test.length; i++) { lock.next((err, results, clear) => { @@ -85,26 +95,29 @@ describe('spawnpoint.getAndLock', () => { }); } lock.next(100, (err, results, clear) => { - assert(!results, 'Should not have returned results.'); - assert(err && err.code === 'getAndLock.locked_timeout', 'Wrong error returned: ' + err.code); + expect(results).toBeFalsy(); + expect(err).toBeTruthy(); + expect(err.code).toBe('getAndLock.locked_timeout'); clear(); - setTimeout(done, 750); + setTimeout(resolve, 750); }); - }); + })); - it('Timeout doesn\'t hold up entire queue', (done) => { + it('Timeout doesn\'t hold up entire queue', () => new Promise((resolve, reject) => { const lock = app.getAndLock(['singleItem']); lock.next((err, results, clear) => { setTimeout(clear, 100); }); lock.next(50, (err, results, clear) => { - assert(!results, 'Should not have returned results.'); - assert(err && err.code === 'getAndLock.locked_timeout', 'Wrong error returned: ' + err.code); + expect(results).toBeFalsy(); + expect(err).toBeTruthy(); + expect(err.code).toBe('getAndLock.locked_timeout'); setTimeout(clear, 500); }); lock.next(1000, (err, results, clear) => { clear(); - done(err); + if (err) { return reject(err); } + resolve(); }); - }); + })); }); diff --git a/test/helpers.js b/test/helpers.js deleted file mode 100644 index 7895294..0000000 --- a/test/helpers.js +++ /dev/null @@ -1,159 +0,0 @@ -'use strict'; -const assert = require('node:assert'); - - -const dayjs = require('dayjs'); -const kleur = require('kleur'); -const _ = require('lodash'); -const expect = require('unexpected'); -//const processVoid = require('process-void'); -//const spawnpoint = require.resolve('..'); - -const helpers = require('../lib/helpers'); - -describe('helpers.tag', () => { - it('outputs correctly', () => { - const string = 'output'; - const output = helpers.tag(string); - expect(output, 'to equal', kleur.gray('[') + kleur.gray(string) + kleur.gray(']') + kleur.reset('')); - }); - - it('changes tag color', () => { - const string = 'output'; - helpers.tag('output', kleur.red); - expect(helpers.tag(string, kleur.red), 'to equal', kleur.gray('[') + kleur.red(string) + kleur.gray(']') + kleur.reset('')); - }); - - it('does not output without a tag', () => { - const output = helpers.tag(''); - expect(output, 'to equal', ''); - }); - - it('throws with invalid color', () => { - assert.throws(() => helpers.tag('output', 'red'), Error); - }); - - it('throws with invalid input', () => { - assert.throws(() => helpers.tag(true), Error); - assert.throws(() => helpers.tag({ foo: 'bar' }), Error); - assert.throws(() => helpers.tag(['foo', 'bar']), Error); - assert.throws(() => helpers.tag(1), Error); - }); -}); - -describe('helpers.camelCase', () => { - it('throws with invalid input', () => { - assert.throws(() => helpers.camelCase(), Error); - assert.throws(() => helpers.camelCase(false), Error); - assert.throws(() => helpers.camelCase({ foo: 'bar' }), Error); - assert.throws(() => helpers.camelCase(['foo', 'bar']), Error); - }); - - it('correctly outputs camelCase', () => { - const tests = { - 'UPPERCASE': 'uppercase', - 'basic camel': 'basicCamel', - 'CamelCase': 'camelCase', - 'with 1 number': 'with1Number', - 'with multiple spaces': 'withMultipleSpaces', - 'tab\tTest': 'tabTest', - ' leading space': 'leadingSpace', - 'trailing space ': 'trailingSpace', - }; - _.each(tests, (result, test) => { - assert(helpers.camelCase(test) === result); - }); - }); -}); - -describe('helpers.log', () => { - it('does not log when passed no config', () => { - helpers.log({ - config: null, - }); - // TODO: detect no logging! - }); - it('does not log when passed no config format', () => { - helpers.log({ - config: { - format: '', - }, - }); - // TODO: detect no logging! - }); - it('announces new timestamp day when none is set', () => { - helpers.log({ - config: { - format: '', - }, - logs: {}, - }); - // TODO: detect logging - }); - it('announces new timestamp day when another date is set', () => { - const dateFormat = 'dddd, MMMM DD YYYY'; - const day = dayjs().format(dateFormat); - helpers.log({ - config: { - format: '', - }, - logs: { - date: day, - }, - }); - // TODO: detect logging - }); - it('announces new timestamp day when another day is set', () => { - const dateFormat = 'dddd, MMMM DD YYYY'; - const day = dayjs().format(dateFormat); - helpers.log({ - config: { - format: '', - }, - logs: { - date: 'not the same date', - day: day, - }, - }); - // TODO: detect logging - }); - it('correctly logs with format', () => { - const dateFormat = 'dddd, MMMM DD YYYY'; - const day = dayjs().format(dateFormat); - helpers.log({ - config: { - format: '{date} {type}: {line}', - time: 'HH:mm:ss', - date: dateFormat, - }, - logs: { - day: day, - date: day, - }, - }); - // TODO: detect logging - }); -}); - -describe('helpers.isContainerized', () => { - it('correctly detects docker cgroups', () => { - assert(helpers.isContainerized('./store/cgroups-docker')); - }); - - it('Does not detects docker cgroups', () => { - assert(!helpers.isContainerized('./store/cgroups-no-container')); - }); -}); - -describe('helpers.omit', () => { - it('correctly omits keys', () => { - const input = { - foo: 'bar', - bar: 'foo', - }; - const output = helpers.omit(input, ['foo']); - assert(output.foo === undefined); - assert(output.bar === 'foo'); - assert(output, { bar: 'foo' }); - }); -}); diff --git a/test/helpers.mjs b/test/helpers.mjs new file mode 100644 index 0000000..77d29a6 --- /dev/null +++ b/test/helpers.mjs @@ -0,0 +1,252 @@ +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; + +import dayjs from 'dayjs'; +import kleur from 'kleur'; +import _ from 'lodash'; +import { describe, expect, it } from 'vitest'; + +import helpers from '../lib/helpers.js'; + +const __dirname = path.dirname(fileURLToPath(import.meta.url)); + +describe('helpers.tag', () => { + it('outputs correctly', () => { + const string = 'output'; + const output = helpers.tag(string); + expect(output).toBe(kleur.gray('[') + kleur.gray(string) + kleur.gray(']') + kleur.reset('')); + }); + + it('changes tag color', () => { + const string = 'output'; + helpers.tag('output', kleur.red); + expect(helpers.tag(string, kleur.red)).toBe(kleur.gray('[') + kleur.red(string) + kleur.gray(']') + kleur.reset('')); + }); + + it('does not output without a tag', () => { + const output = helpers.tag(''); + expect(output).toBe(''); + }); + + it('throws with invalid color', () => { + expect(() => helpers.tag('output', 'red')).toThrow(Error); + }); + + it('throws with invalid input', () => { + expect(() => helpers.tag(true)).toThrow(Error); + expect(() => helpers.tag({ foo: 'bar' })).toThrow(Error); + expect(() => helpers.tag(['foo', 'bar'])).toThrow(Error); + expect(() => helpers.tag(1)).toThrow(Error); + }); +}); + +describe('helpers.camelCase', () => { + it('throws with invalid input', () => { + expect(() => helpers.camelCase()).toThrow(Error); + expect(() => helpers.camelCase(false)).toThrow(Error); + expect(() => helpers.camelCase({ foo: 'bar' })).toThrow(Error); + expect(() => helpers.camelCase(['foo', 'bar'])).toThrow(Error); + }); + + it('correctly outputs camelCase', () => { + const tests = { + 'UPPERCASE': 'uppercase', + 'basic camel': 'basicCamel', + 'CamelCase': 'camelCase', + 'with 1 number': 'with1Number', + 'with multiple spaces': 'withMultipleSpaces', + 'tab\tTest': 'tabTest', + ' leading space': 'leadingSpace', + 'trailing space ': 'trailingSpace', + }; + _.each(tests, (result, test) => { + expect(helpers.camelCase(test)).toBe(result); + }); + }); +}); + +describe('helpers.log', () => { + it('does not log when passed no config', () => { + helpers.log({ + config: null, + }); + // TODO: detect no logging! + }); + it('does not log when passed no config format', () => { + helpers.log({ + config: { + format: '', + }, + }); + // TODO: detect no logging! + }); + it('announces new timestamp day when none is set', () => { + const logs = {}; + helpers.log({ + config: { + format: '{date}', + date: 'dddd, MMMM DD YYYY', + time: 'HH:mm', + }, + logs, + }); + // The date should now be set in logs + expect(logs.date).toBe(dayjs().format('dddd, MMMM DD YYYY')); + }); + it('does not announce date when already set to same day', () => { + const dateFormat = 'dddd, MMMM DD YYYY'; + const day = dayjs().format(dateFormat); + const logs = { date: day }; + helpers.log({ + config: { + format: '{date}', + date: dateFormat, + time: 'HH:mm', + }, + logs, + }); + // Date should remain the same + expect(logs.date).toBe(day); + }); + it('announces new timestamp day when date changes', () => { + const dateFormat = 'dddd, MMMM DD YYYY'; + const logs = { date: 'not the same date' }; + helpers.log({ + config: { + format: '{date}', + date: dateFormat, + time: 'HH:mm', + }, + logs, + }); + // Date should be updated to today + expect(logs.date).toBe(dayjs().format(dateFormat)); + }); + it('correctly logs with format', () => { + const dateFormat = 'dddd, MMMM DD YYYY'; + const day = dayjs().format(dateFormat); + helpers.log({ + config: { + format: '{date} {type}: {line}', + time: 'HH:mm:ss', + date: dateFormat, + }, + logs: { + day: day, + date: day, + }, + }); + // TODO: detect logging + }); +}); + +describe('helpers.isContainerized', () => { + it('correctly detects docker cgroups', () => { + expect(helpers.isContainerized(path.join(__dirname, 'store/cgroups-docker'))).toBe(true); + }); + + it('Does not detects docker cgroups', () => { + expect(helpers.isContainerized(path.join(__dirname, 'store/cgroups-no-container'))).toBe(false); + }); + + it('returns false when cgroups file does not exist', () => { + expect(helpers.isContainerized(path.join(__dirname, 'non-existent-file-path'))).toBe(false); + }); +}); + +describe('helpers.omit', () => { + it('correctly omits keys', () => { + const input = { + foo: 'bar', + bar: 'foo', + }; + const output = helpers.omit(input, ['foo']); + expect(output.foo).toBeUndefined(); + expect(output.bar).toBe('foo'); + expect(output).toEqual({ bar: 'foo' }); + }); + + it('returns empty object when all keys are omitted', () => { + const input = { foo: 'bar' }; + const output = helpers.omit(input, ['foo']); + expect(output).toEqual({}); + }); + + it('returns copy of object when no keys are omitted', () => { + const input = { foo: 'bar', baz: 'qux' }; + const output = helpers.omit(input, []); + expect(output).toEqual(input); + expect(output).not.toBe(input); + }); + + it('handles non-existent keys gracefully', () => { + const input = { foo: 'bar' }; + const output = helpers.omit(input, ['nonexistent']); + expect(output).toEqual({ foo: 'bar' }); + }); + + it('uses default empty array when keysToOmit is not provided', () => { + const input = { foo: 'bar' }; + const output = helpers.omit(input); + expect(output).toEqual({ foo: 'bar' }); + }); +}); + +describe('helpers.camelCase with dot notation', () => { + it('preserves dots in path-like strings', () => { + expect(helpers.camelCase('foo.bar')).toBe('foo.bar'); + expect(helpers.camelCase('my config.nested value')).toBe('myConfig.nestedValue'); + }); + + it('handles multiple dots', () => { + expect(helpers.camelCase('a.b.c')).toBe('a.b.c'); + expect(helpers.camelCase('hello world.foo bar.test case')).toBe('helloWorld.fooBar.testCase'); + }); +}); + +describe('helpers.log with stderr output', () => { + it('uses console.error for type=error', () => { + // This tests the type parameter path + const logs = { date: dayjs().format('dddd, MMMM DD YYYY') }; + helpers.log({ + config: { + format: '{date} {type}: {line}', + date: 'dddd, MMMM DD YYYY', + time: 'HH:mm', + }, + logs, + type: 'ERROR', + line: 'Test error message', + }, 'error'); + // Test passes if no error thrown - actual stderr output is captured by vitest + }); + + it('uses default console.log when type not specified', () => { + const logs = { date: dayjs().format('dddd, MMMM DD YYYY') }; + helpers.log({ + config: { + format: '{line}', + date: 'dddd, MMMM DD YYYY', + time: 'HH:mm', + }, + logs, + line: 'Test log message', + }); + // Test passes if no error thrown + }); + + it('sets date from opts when provided', () => { + const logs = { date: dayjs().format('dddd, MMMM DD YYYY') }; + const customDate = '[Custom Date]'; + helpers.log({ + config: { + format: '{date}', + date: 'dddd, MMMM DD YYYY', + time: 'HH:mm', + }, + logs, + date: customDate, + }); + // When date is already set in opts, it should be used + }); +}); diff --git a/test/init.js b/test/init.js deleted file mode 100644 index 8483c00..0000000 --- a/test/init.js +++ /dev/null @@ -1,323 +0,0 @@ -'use strict'; -const assert = require('node:assert'); - -const _ = require('lodash'); -const expectRaw = require('unexpected'); - -const spawnpoint = require('..'); -const processVoid = require('./process-void/void.js'); - -const expect = expectRaw.clone().use(require('unexpected-eventemitter')); - -process.chdir(__dirname); -describe('spawnpoint initialization', () => { - it('Successfully initializes', () => { - assert.doesNotThrow(() => new spawnpoint()); - assert.doesNotThrow(() => new spawnpoint('config/app.json')); - }); - - it('fails with bad configFile', () => { - assert.throws(() => new spawnpoint({ invalid: 'object' }), Error); - assert.throws(() => new spawnpoint(['invalid', 'array']), Error); - assert.throws(() => new spawnpoint(null), Error); - assert.throws(() => new spawnpoint(true), Error); - }); -}); - -describe('spawnpoint setup', () => { - it('Basic startup', (done) => { - const app = new spawnpoint(); - app.setup(done); - }); - it('Basic startup without .json extension', (done) => { - const app = new spawnpoint('config/app'); - app.setup(done); - }); - it('Basic startup with path', (done) => { - const app = new spawnpoint('config/app.json'); - app.setup(done); - }); - it('Basic startup with /path', (done) => { - const app = new spawnpoint('/config/app.json'); - app.setup(done); - }); - it('Basic startup with plugins', (done) => { - const app = new spawnpoint('config/loadPlugins'); - app.setup(done); - }); - - it('Throws when setup is run more than once', (done) => { - const app = new spawnpoint(); - app.setup(); - app.setup((err) => { - assert(err && err.code === 'spawnpoint.already_setup'); - done(); - }); - }); - - it('sync autoloading', (done) => { - const app = new spawnpoint('config/autoloading-sync.json'); - app.setup((err) => { - if (err) { return done(err); } - assert(app.customHoistedVarFromAutoload); - done(); - }); - }); - - it('async autoloading', (done) => { - const app = new spawnpoint('config/autoloading-async.json'); - app.setup((err) => { - if (err) { return done(err); } - assert(app.customHoistedVarFromAutoload); - done(); - }); - }); - - it('autoloading with folder only', (done) => { - const app = new spawnpoint('config/autoloading-noName.json'); - app.setup((err) => { - if (err) { return done(err); } - expect(app.customHoistedVarFromAutoload, 'to be true'); - done(); - }); - }); - - it('sync autoloading error handles correctly', (done) => { - //const app = fork('./autoload-void', ['config/autoloading-error.json'], { 'silent': true }); - const app = new processVoid(done, require.resolve('..'), { construct: true }, 'config/autoloading-error.json'); - app.stderr.once('data', (data) => { - expect(data, 'when decoded as', 'utf8', 'to contain', 'TypeError'); - void app.done(); - }); - //app.send({"command": 'setup'}); - app.setup(); - }); - - it('async autoloading error handles correctly', (done) => { - const app = new spawnpoint('config/autoloading-error-async.json'); - app.on('app.setup.initRegistry', () => { - app.removeAllListeners('app.exit'); - }); - app.setup((err) => { - expect(err, 'to be an', 'Error'); - done(); - }); - }); -}); - -describe('spawnpoint registry', () => { - let app; - beforeEach('initialization of app', () => { - app = new spawnpoint(); - app.initRegistry(); - }); - describe('app.register', () => { - it('registers a plugin if it isn\'t already on the list', (done) => { - app.on('app.register', (data) => { - expect(app.register, 'to contain', data); - }); - app.emit('app.register', 'spawnpoint-redis'); - done(); - }); - - it('does not duplicate a plugin already on the list', (done) => { - app.on('app.register', () => { - expect(app.register, 'to have length', 1); - }); - app.emit('app.register', 'spawnpoint-redis'); - app.emit('app.register', 'spawnpoint-redis'); - done(); - }); - }); - - describe('app.deregister', () => { - beforeEach(() => { - app.emit('app.register', 'spawnpoint-redis'); - app.emit('app.register', 'lodash'); - }); - it('app.deregister removes a plugin if it is on the list', (done) => { - app.on('app.deregister', (data) => { - expect(app.register, 'not to contain', data); - }); - app.emit('app.deregister', 'spawnpoint-redis'); - done(); - }); - it('does not remove an item if no match is found', (done) => { - app.on('app.deregister', () => { - expect(app.register, 'to have length', 1); - }); - app.emit('app.deregister', 'spawnpoint-redis'); - app.emit('app.deregister', 'spawnpoint-redis'); - done(); - }); - it('gracefully exits if no items in registry and app is not running', (done) => { - app.removeAllListeners('app.exit'); - expect(() => app.emit('app.deregister', 'spawnpoint-redis'), 'not to emit from', app, 'app.exit'); - expect(() => app.emit('app.deregister', 'lodash'), 'to emit from', app, 'app.exit', expect.it('to be true')); - done(); - }); - it('does not exit if no items in registry and app is running', (done) => { - app.removeAllListeners('app.exit'); - app.emit('app.deregister', 'spawnpoint-redis'); - app.status.running = true; - expect(() => app.emit('app.deregister', 'lodash'), 'not to emit from', app, 'app.exit'); - done(); - }); - }); - - describe('app.ready', () => { - // I would add this to the previous definition, if I could find it. - it('sets status.running to true', (done) => { - app.on('app.ready', () => { - expect(app.status.running, 'to be true'); - }); - app.emit('app.ready'); - done(); - }); - it('does not add a listener to process\'s uncaughtException event if catchExceptions is false', (done) => { - app.config.catchExceptions = false; - expect(() => app.emit('app.ready'), 'not to emit from', process, 'newListener'); - done(); - }); - it('adds a listener to process\'s uncaughtException event if catchExceptions is true', (done) => { - app.config.catchExceptions = true; - expect(() => app.emit('app.ready'), 'to emit from', process, 'newListener', 'uncaughtException'); - done(); - }); - it('after app.ready is called with catchExceptions being true, stops the app if an uncaughtException is emitted on process and app.status.running is false', (done) => { - app.config.catchExceptions = true; - app.removeAllListeners('app.stop'); - const originalListeners = process.listeners('uncaughtException'); - process.removeAllListeners('uncaughtException'); // prevents the exception from throwing. This is dangerous. - app.on('app.ready', () => { - app.status.running = false; - expect(() => process.emit('uncaughtException', new Error('Test error')), 'to emit from', app, 'app.stop', expect.it('to be true')); - }); - app.emit('app.ready'); - _.eachRight(originalListeners, item => process.prependListener('uncaughtException', item)); // Adding the original listeners back to process's uncaughtException. Hopefully makes this less dangerous. - done(); - }); - }); - - describe('app.stop', () => { - beforeEach(() => { - // prevent timing out due to process.exit() being called before testing finishes. - app.removeAllListeners('app.exit'); - }); - it('sets values correctly and calls app.close', (done) => { - app.status.running = true; - app.register.push('lodash'); - app.info = function(message, argument) { - // mock this to see if it gets the correct values. - expect(message, 'to equal', 'Stopping %s gracefully'); - expect(argument, 'to equal', app.config.name); - }; - app.on('app.stop', () => { - expect(app.status.running, 'to be false'); - expect(app.status.stopping, 'to be true'); - }); - expect(() => app.emit('app.stop'), 'to emit from', app, 'app.close'); - done(); - }); - it('calls app.exit if nothing is in the registry', (done) => { - expect(() => app.emit('app.stop'), 'to emit from', app, 'app.exit'); - done(); - }); - it('does not call app.exit if something is in the registry', (done) => { - app.register.push('lodash'); - expect(() => app.emit('app.stop'), 'not to emit from', app, 'app.exit'); - done(); - }); - _.times(6, (inde) => { - let index = inde + 1; - let runs = index % 3; - runs += 2; - index %= 2; - index += 2; - if (runs >= index) { - it('with ' + index + ' stopAttempts forcefully stops once app.stop is called ' + index + ' times', (done) => { - app.register.push('lodash'); // make sure the other way app.exit can be called doesn't happen. - app.config.stopAttempts = index; - _.times(index, () => expect(() => app.emit('app.stop'), 'not to emit from', app, 'app.exit')); - expect(() => app.emit('app.stop'), 'to emit from', app, 'app.exit'); - done(); - }); - } else if (runs < index) { - it('with ' + index + ' stopAttempts never stops with app.stop being called ' + runs + ' times', (done) => { - app.register.push('lodash'); // make sure the other way app.exit can be called doesn't happen. - app.config.stopAttempts = index; - _.times(runs, () => expect(() => app.emit('app.stop'), 'not to emit from', app, 'app.exit')); - done(); - }); - } - }); - }); - - describe('app.exit', () => { - it('allows the process to exit gracefully', function(done) { - this.timeout(5000); - let message; - const testApp = new processVoid(() => { - expect(message, 'when decoded as', 'utf8', 'to equal', 'Test gracefully closed.\n'); - expect(testApp.exited, 'to have property', 'code', 0); - done(); - }, require.resolve('..'), { construct: true }); - testApp.config.name = 'Test'; - testApp.config.log = { format: '{line}' }; - testApp.initRegistry(); - const date = /^\[\d{4}-[01]\d-[0-3]\dT[0-2](?:\d:[0-6]){2}\d[+-][01]\d:\d{2}]\n$/; - testApp.stdout.once('data', (data) => { - if (date.test(data)) { - testApp.stdout.once('data', (data) => { - message = data; - }); - } else { - message = data; - } - }); - testApp.emit('app.exit', true); - }); - - it('allows the process to exit unsafely', function(done) { - this.timeout(5000); - const testApp = new processVoid(() => { - expect(testApp.exited, 'to have property', 'code', 1); - done(); - }, require.resolve('..'), { construct: true }); - testApp.initRegistry(); - testApp.emit('app.exit', false); - }); - }); - - describe('initialization', () => { - beforeEach(() => { - app = new spawnpoint(); - app.config.signals = {}; - }); - it('emits a signal once done', (done) => { - expect(() => app.initRegistry(), 'to emit from', app, 'app.setup.initRegistry'); - done(); - }); - - it('accepts configuration options for events that close the app', (done) => { - // Testing with SIGINT caused bad things. - app.config.signals.close = ['SIGUSR1']; - app.config.signals.debug = []; - expect(() => app.initRegistry(), 'to emit from', process, 'newListener', 'SIGUSR1'); - app.removeAllListeners('app.stop'); - expect(() => process.emit('SIGUSR1'), 'to emit from', app, 'app.stop'); - done(); - }); - - it('accepts configuration options for events that toggle debug mode', (done) => { - app.config.signals.close = []; - app.config.signals.debug = ['SIGUSR1']; - app.config.debug = false; - expect(() => app.initRegistry(), 'to emit from', process, 'newListener', 'SIGUSR1'); - expect(() => process.emit('SIGUSR1'), 'when called').then((result) => { - expect(result, 'to be true').and('to equal', app.config.debug); - done(); - }); - }); - }); -}); diff --git a/test/init.mjs b/test/init.mjs new file mode 100644 index 0000000..2050f02 --- /dev/null +++ b/test/init.mjs @@ -0,0 +1,425 @@ +import { createRequire } from 'node:module'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; + +import _ from 'lodash'; +import { + beforeEach, + describe, + expect, + it, +} from 'vitest'; + +import spawnpoint from '../index.js'; +import processVoid from './process-void/void.js'; + +const __dirname = path.dirname(fileURLToPath(import.meta.url)); +const require = createRequire(import.meta.url); + +describe('spawnpoint initialization', () => { + it('Successfully initializes', () => { + expect(() => new spawnpoint()).not.toThrow(); + expect(() => new spawnpoint('config/app.json')).not.toThrow(); + }); + + it('fails with bad configFile', () => { + expect(() => new spawnpoint(['invalid', 'array'])).toThrow(Error); + expect(() => new spawnpoint(null)).toThrow(Error); + expect(() => new spawnpoint(true)).toThrow(Error); + // Note: objects are now valid as options (e.g., { cwd: '/path' }) + }); + + it('accepts options object with cwd', () => { + const app = new spawnpoint({ cwd: __dirname }); + expect(app.cwd).toBe(__dirname); + }); + + it('accepts options object with configFile', () => { + const app = new spawnpoint({ configFile: '/custom/config.json' }); + expect(app.configFile).toBe('/custom/config.json'); + }); + + it('defaults cwd to process.cwd() when not specified', () => { + const app = new spawnpoint(); + expect(app.cwd).toBe(process.cwd()); + }); + + it('uses cwd from options object for config file resolution', () => new Promise((resolve) => { + // This test verifies that passing cwd allows loading configs from that directory + // without needing process.chdir() + const app = new spawnpoint({ cwd: __dirname, configFile: 'config/app.json' }); + app.setup((err) => { + expect(err).toBeFalsy(); + expect(app.config.name).toBe('Simple, no extras'); + resolve(); + }); + })); + + it('combines cwd and configFile options correctly', () => { + const app = new spawnpoint({ cwd: __dirname, configFile: 'config/debugEnabled.json' }); + expect(app.cwd).toBe(__dirname); + expect(app.configFile).toBe('config/debugEnabled.json'); + }); + + it('supports legacy string configFile parameter', () => { + const app = new spawnpoint('/config/app.json'); + expect(app.configFile).toBe('/config/app.json'); + expect(app.cwd).toBe(process.cwd()); + }); +}); + +describe('spawnpoint setup', () => { + it('Basic startup', () => new Promise((resolve) => { + const app = new spawnpoint({ cwd: __dirname }); + app.setup(resolve); + })); + it('Basic startup without .json extension', () => new Promise((resolve) => { + const app = new spawnpoint({ cwd: __dirname, configFile: 'config/app' }); + app.setup(resolve); + })); + it('Basic startup with path', () => new Promise((resolve) => { + const app = new spawnpoint({ cwd: __dirname, configFile: 'config/app.json' }); + app.setup(resolve); + })); + it('Basic startup with /path', () => new Promise((resolve) => { + const app = new spawnpoint({ cwd: __dirname, configFile: '/config/app.json' }); + app.setup(resolve); + })); + it('Basic startup with plugins', () => new Promise((resolve) => { + const app = new spawnpoint({ cwd: __dirname, configFile: 'config/loadPlugins' }); + app.setup(resolve); + })); + + it('Throws when setup is run more than once', () => new Promise((resolve) => { + const app = new spawnpoint({ cwd: __dirname }); + app.setup(); + app.setup((err) => { + expect(err).toBeTruthy(); + expect(err.code).toBe('spawnpoint.already_setup'); + resolve(); + }); + })); + + it('sync autoloading', () => new Promise((resolve, reject) => { + const app = new spawnpoint({ cwd: __dirname, configFile: 'config/autoloading-sync.json' }); + app.setup((err) => { + if (err) { return reject(err); } + expect(app.customHoistedVarFromAutoload).toBeTruthy(); + resolve(); + }); + })); + + it('async autoloading', () => new Promise((resolve, reject) => { + const app = new spawnpoint({ cwd: __dirname, configFile: 'config/autoloading-async.json' }); + app.setup((err) => { + if (err) { return reject(err); } + expect(app.customHoistedVarFromAutoload).toBeTruthy(); + resolve(); + }); + })); + + it('autoloading with folder only', () => new Promise((resolve, reject) => { + const app = new spawnpoint({ cwd: __dirname, configFile: 'config/autoloading-noName.json' }); + app.setup((err) => { + if (err) { return reject(err); } + expect(app.customHoistedVarFromAutoload).toBe(true); + resolve(); + }); + })); + + it('sync autoloading error handles correctly', () => new Promise((resolve) => { + const app = new processVoid(resolve, require.resolve('..'), { construct: true, cwd: __dirname }, 'config/autoloading-error.json'); + app.stderr.once('data', (data) => { + expect(data.toString('utf8')).toContain('TypeError'); + void app.done(); + }); + app.setup(); + })); + + it('async autoloading error handles correctly', () => new Promise((resolve) => { + const app = new spawnpoint({ cwd: __dirname, configFile: 'config/autoloading-error-async.json' }); + app.on('app.setup.initRegistry', () => { + app.removeAllListeners('app.exit'); + }); + app.setup((err) => { + expect(err).toBeInstanceOf(Error); + resolve(); + }); + })); +}); + +describe('spawnpoint registry', () => { + let app; + beforeEach(() => { + app = new spawnpoint(); + app.initRegistry(); + }); + describe('app.register', () => { + it('registers a plugin if it isn\'t already on the list', () => new Promise((resolve) => { + app.on('app.register', (data) => { + expect(app.register).toContain(data); + }); + app.emit('app.register', 'spawnpoint-redis'); + resolve(); + })); + + it('does not duplicate a plugin already on the list', () => new Promise((resolve) => { + app.on('app.register', () => { + expect(app.register).toHaveLength(1); + }); + app.emit('app.register', 'spawnpoint-redis'); + app.emit('app.register', 'spawnpoint-redis'); + resolve(); + })); + }); + + describe('app.deregister', () => { + beforeEach(() => { + app.emit('app.register', 'spawnpoint-redis'); + app.emit('app.register', 'lodash'); + }); + it('app.deregister removes a plugin if it is on the list', () => new Promise((resolve) => { + app.on('app.deregister', (data) => { + expect(app.register).not.toContain(data); + }); + app.emit('app.deregister', 'spawnpoint-redis'); + resolve(); + })); + it('does not remove an item if no match is found', () => new Promise((resolve) => { + app.on('app.deregister', () => { + expect(app.register).toHaveLength(1); + }); + app.emit('app.deregister', 'spawnpoint-redis'); + app.emit('app.deregister', 'spawnpoint-redis'); + resolve(); + })); + it('gracefully exits if no items in registry and app is not running', () => new Promise((resolve) => { + app.removeAllListeners('app.exit'); + let exitCalled = false; + app.on('app.exit', () => { + exitCalled = true; + }); + app.emit('app.deregister', 'spawnpoint-redis'); + expect(exitCalled).toBe(false); + app.emit('app.deregister', 'lodash'); + expect(exitCalled).toBe(true); + resolve(); + })); + it('does not exit if no items in registry and app is running', () => new Promise((resolve) => { + app.removeAllListeners('app.exit'); + let exitCalled = false; + app.on('app.exit', () => { + exitCalled = true; + }); + app.emit('app.deregister', 'spawnpoint-redis'); + app.status.running = true; + app.emit('app.deregister', 'lodash'); + expect(exitCalled).toBe(false); + resolve(); + })); + }); + + describe('app.ready', () => { + it('sets status.running to true', () => new Promise((resolve) => { + app.on('app.ready', () => { + expect(app.status.running).toBe(true); + }); + app.emit('app.ready'); + resolve(); + })); + it('does not add a listener to process\'s uncaughtException event if catchExceptions is false', () => new Promise((resolve) => { + app.config.catchExceptions = false; + const listenerCountBefore = process.listenerCount('uncaughtException'); + app.emit('app.ready'); + const listenerCountAfter = process.listenerCount('uncaughtException'); + expect(listenerCountAfter).toBe(listenerCountBefore); + resolve(); + })); + it('adds a listener to process\'s uncaughtException event if catchExceptions is true', () => new Promise((resolve) => { + app.config.catchExceptions = true; + const listenerCountBefore = process.listenerCount('uncaughtException'); + app.emit('app.ready'); + const listenerCountAfter = process.listenerCount('uncaughtException'); + expect(listenerCountAfter).toBe(listenerCountBefore + 1); + resolve(); + })); + it('after app.ready is called with catchExceptions being true, stops the app if an uncaughtException is emitted on process and app.status.running is false', () => new Promise((resolve) => { + app.config.catchExceptions = true; + app.removeAllListeners('app.stop'); + const originalListeners = process.listeners('uncaughtException'); + process.removeAllListeners('uncaughtException'); + let stopCalled = false; + app.on('app.stop', () => { + stopCalled = true; + }); + app.on('app.ready', () => { + app.status.running = false; + process.emit('uncaughtException', new Error('Test error')); + expect(stopCalled).toBe(true); + }); + app.emit('app.ready'); + _.eachRight(originalListeners, item => process.prependListener('uncaughtException', item)); + resolve(); + })); + }); + + describe('app.stop', () => { + beforeEach(() => { + app.removeAllListeners('app.exit'); + }); + it('sets values correctly and calls app.close', () => new Promise((resolve) => { + app.status.running = true; + app.register.push('lodash'); + app.info = function(message, argument) { + expect(message).toBe('Stopping %s gracefully'); + expect(argument).toBe(app.config.name); + }; + let closeCalled = false; + app.on('app.close', () => { + closeCalled = true; + }); + app.on('app.stop', () => { + expect(app.status.running).toBe(false); + expect(app.status.stopping).toBe(true); + }); + app.emit('app.stop'); + expect(closeCalled).toBe(true); + resolve(); + })); + it('calls app.exit if nothing is in the registry', () => new Promise((resolve) => { + let exitCalled = false; + app.on('app.exit', () => { + exitCalled = true; + }); + app.emit('app.stop'); + expect(exitCalled).toBe(true); + resolve(); + })); + it('does not call app.exit if something is in the registry', () => new Promise((resolve) => { + app.register.push('lodash'); + let exitCalled = false; + app.on('app.exit', () => { + exitCalled = true; + }); + app.emit('app.stop'); + expect(exitCalled).toBe(false); + resolve(); + })); + _.times(6, (inde) => { + let index = inde + 1; + let runs = index % 3; + runs += 2; + index %= 2; + index += 2; + if (runs >= index) { + it('with ' + index + ' stopAttempts forcefully stops once app.stop is called ' + index + ' times', () => new Promise((resolve) => { + app.register.push('lodash'); + app.config.stopAttempts = index; + let exitCalled = false; + app.on('app.exit', () => { + exitCalled = true; + }); + _.times(index, () => { + app.emit('app.stop'); + expect(exitCalled).toBe(false); + }); + app.emit('app.stop'); + expect(exitCalled).toBe(true); + resolve(); + })); + } else if (runs < index) { + it('with ' + index + ' stopAttempts never stops with app.stop being called ' + runs + ' times', () => new Promise((resolve) => { + app.register.push('lodash'); + app.config.stopAttempts = index; + let exitCalled = false; + app.on('app.exit', () => { + exitCalled = true; + }); + _.times(runs, () => { + app.emit('app.stop'); + expect(exitCalled).toBe(false); + }); + resolve(); + })); + } + }); + }); + + describe('app.exit', () => { + it('allows the process to exit gracefully', { timeout: 5000 }, () => new Promise((resolve) => { + let message; + const testApp = new processVoid(() => { + expect(message.toString('utf8')).toBe('Test gracefully closed.\n'); + expect(testApp.exited).toHaveProperty('code', 0); + resolve(); + }, require.resolve('..'), { construct: true }); + testApp.config.name = 'Test'; + testApp.config.log = { format: '{line}' }; + testApp.initRegistry(); + const date = /^\[\d{4}-[01]\d-[0-3]\dT[0-2](?:\d:[0-6]){2}\d[+-][01]\d:\d{2}]\n$/; + testApp.stdout.once('data', (data) => { + if (date.test(data)) { + testApp.stdout.once('data', (data) => { + message = data; + }); + } else { + message = data; + } + }); + testApp.emit('app.exit', true); + })); + + it('allows the process to exit unsafely', { timeout: 5000 }, () => new Promise((resolve) => { + const testApp = new processVoid(() => { + expect(testApp.exited).toHaveProperty('code', 1); + resolve(); + }, require.resolve('..'), { construct: true }); + testApp.initRegistry(); + testApp.emit('app.exit', false); + })); + }); + + describe('initialization', () => { + beforeEach(() => { + app = new spawnpoint(); + app.config.signals = {}; + }); + it('emits a signal once done', () => new Promise((resolve) => { + app.on('app.setup.initRegistry', () => { + resolve(); + }); + app.initRegistry(); + })); + + it('accepts configuration options for events that close the app', () => new Promise((resolve) => { + app.config.signals.close = ['SIGUSR1']; + app.config.signals.debug = []; + let sigusr1ListenerAdded = false; + process.once('newListener', (event) => { + if (event === 'SIGUSR1') { + sigusr1ListenerAdded = true; + } + }); + app.initRegistry(); + expect(sigusr1ListenerAdded).toBe(true); + app.removeAllListeners('app.stop'); + let stopCalled = false; + app.on('app.stop', () => { + stopCalled = true; + }); + process.emit('SIGUSR1'); + expect(stopCalled).toBe(true); + resolve(); + })); + + it('accepts configuration options for events that toggle debug mode', async () => { + app.config.signals.close = []; + app.config.signals.debug = ['SIGUSR1']; + app.config.debug = false; + app.initRegistry(); + const result = process.emit('SIGUSR1'); + expect(result).toBe(true); + expect(app.config.debug).toBe(true); + }); + }); +}); diff --git a/test/isRoot.js b/test/isRoot.mjs similarity index 68% rename from test/isRoot.js rename to test/isRoot.mjs index b0cfa15..d89f040 100644 --- a/test/isRoot.js +++ b/test/isRoot.mjs @@ -1,5 +1,6 @@ -'use strict'; -const spawnpoint = require('..'); +import { describe, it } from 'vitest'; + +import spawnpoint from '../index.js'; describe('spawnpoint.isRoot', () => { it('Should run successfully', function() { diff --git a/test/isSecure.js b/test/isSecure.mjs similarity index 77% rename from test/isSecure.js rename to test/isSecure.mjs index e92d9cf..d61e060 100644 --- a/test/isSecure.js +++ b/test/isSecure.mjs @@ -1,6 +1,6 @@ -'use strict'; -const spawnpoint = require('..'); -//const expect = require('unexpected'); +import { describe, it } from 'vitest'; + +import spawnpoint from '../index.js'; describe('spawnpoint.isSecure', () => { if (typeof(process.getuid) === 'function' && process.getuid() === 0) { diff --git a/test/jsonHandler.js b/test/jsonHandler.js deleted file mode 100644 index 84c0f48..0000000 --- a/test/jsonHandler.js +++ /dev/null @@ -1,15 +0,0 @@ -'use strict'; -require('../lib/json-handler.js'); -const expect = require('unexpected'); - -describe('JSON-handler', () => { - it('should not error with good files', () => { - expect(() => require('./json/good.json'), 'not to error'); - expect(() => require('./json/commented.json'), 'not to error'); - }); - - it('should throw a syntax error on a bad file', () => { - expect(() => require('./json/badLint.json'), 'to throw a', SyntaxError); - expect(() => require('./json/bad.json'), 'to throw a', SyntaxError); - }); -}); diff --git a/test/jsonHandler.mjs b/test/jsonHandler.mjs new file mode 100644 index 0000000..badf01e --- /dev/null +++ b/test/jsonHandler.mjs @@ -0,0 +1,19 @@ +import { createRequire } from 'node:module'; + +import { describe, expect, it } from 'vitest'; + +import '../lib/json-handler.js'; + +const require = createRequire(import.meta.url); + +describe('JSON-handler', () => { + it('should not error with good files', () => { + expect(() => require('./json/good.json')).not.toThrow(); + expect(() => require('./json/commented.json')).not.toThrow(); + }); + + it('should throw a syntax error on a bad file', () => { + expect(() => require('./json/badLint.json')).toThrow(SyntaxError); + expect(() => require('./json/bad.json')).toThrow(SyntaxError); + }); +}); diff --git a/test/loadCodes.mjs b/test/loadCodes.mjs new file mode 100644 index 0000000..98b1483 --- /dev/null +++ b/test/loadCodes.mjs @@ -0,0 +1,79 @@ +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; + +import { + beforeEach, + describe, + expect, + it, +} from 'vitest'; + +import spawnpoint from '../index.js'; + +const __dirname = path.dirname(fileURLToPath(import.meta.url)); + +describe('spawnpoint.loadCodes', () => { + let app; + beforeEach(() => { + app = new spawnpoint({ cwd: __dirname }); + app.initConfig(); + app.initCodes(); + }); + + it('loads codes from plugin with codes property', () => { + app.plugins = { + test: { + codes: { + 'plugin.custom_code': 'Custom code from plugin', + }, + dir: __dirname, + }, + }; + app.loadCodes(); + expect(app.codes['plugin.custom_code']).toBe('Custom code from plugin'); + }); + + it('handles missing codes folder gracefully', () => { + app.config.codes = '/non-existent-codes-folder'; + // Should not throw + app.loadCodes(); + }); + + it('emits app.setup.loadCodes event', () => new Promise((resolve) => { + app.on('app.setup.loadCodes', () => { + resolve(); + }); + app.loadCodes(); + })); + + it('loads codes from a specified directory', () => { + // Load codes from a specific path + const codesDir = path.join(__dirname, 'config/codes'); + app.loadCodes(codesDir); + // Codes should include test codes from config/codes + expect(app.codes['test.code']).toBeDefined(); + }); +}); + +describe('spawnpoint.registerCodes', () => { + let app; + beforeEach(() => { + app = new spawnpoint({ cwd: __dirname }); + app.initConfig(); + app.initCodes(); + }); + + it('registers new codes', () => { + app.registerCodes({ + 'new.code': 'New code message', + 'another.code': 'Another code message', + }); + expect(app.codes['new.code']).toBe('New code message'); + expect(app.codes['another.code']).toBe('Another code message'); + }); + + it('returns this for chaining', () => { + const result = app.registerCodes({ 'chain.code': 'Chain test' }); + expect(result).toBe(app); + }); +}); diff --git a/test/loadPlugins.js b/test/loadPlugins.js deleted file mode 100644 index 946fe0f..0000000 --- a/test/loadPlugins.js +++ /dev/null @@ -1,34 +0,0 @@ -'use strict'; -const expect = require('unexpected'); - -const spawnpoint = require('..'); - -describe('spawnpoint.loadPlugins', () => { - let app; - const config = './config/loadPlugins.json'; - beforeEach(() => { - app = new spawnpoint(config); - }); - - it('should create an array of plugins', function(done) { - app.setup(() => { - app.on('app.setup.loadPlugins', () => { - expect(app.config.plugins, 'to be an', 'array'); - expect(app.config.plugins, 'to have an item satisfying', 'to satisfy', { - plugin: expect.it('to contain', 'spawnpoint-test-cb'), - name: expect.it('to be', 'TestWCallback'), - namespace: expect.it('to be', 'test'), - original_namespace: expect.it('to be', 'test'), - }); - expect(app.config.plugins, 'to have an item satisfying', 'to satisfy', { - plugin: expect.it('to contain', 'spawnpoint-test'), - name: expect.it('to be', 'test'), - namespace: expect.it('to be', 'testB'), - original_namespace: expect.it('to be', 'testB'), - }); - done(); - }); - app.loadPlugins(); - }); - }); -}); diff --git a/test/loadPlugins.mjs b/test/loadPlugins.mjs new file mode 100644 index 0000000..7875992 --- /dev/null +++ b/test/loadPlugins.mjs @@ -0,0 +1,77 @@ +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; + +import { + beforeEach, + describe, + expect, + it, +} from 'vitest'; + +import spawnpoint from '../index.js'; + +const __dirname = path.dirname(fileURLToPath(import.meta.url)); + +describe('spawnpoint.loadPlugins', () => { + let app; + beforeEach(() => { + app = new spawnpoint({ cwd: __dirname, configFile: 'config/loadPlugins.json' }); + }); + + it('should create an array of plugins', function() { + return new Promise((resolve) => { + app.setup(() => { + app.on('app.setup.loadPlugins', () => { + expect(app.config.plugins).toBeInstanceOf(Array); + expect(app.config.plugins).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + name: 'TestWCallback', + namespace: 'test', + original_namespace: 'test', + }), + ]), + ); + expect(app.config.plugins).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + name: 'test', + namespace: 'testB', + original_namespace: 'testB', + }), + ]), + ); + resolve(); + }); + app.loadPlugins(); + }); + }); + }); +}); + +describe('spawnpoint.loadPlugins sideloading', () => { + it('should handle plugin sideloading with custom namespace', () => new Promise((resolve) => { + const app = new spawnpoint({ cwd: __dirname, configFile: 'config/pluginSideload.json' }); + app.initConfig(); + app.initCodes(); + app.initRegistry(); + app.loadPlugins(); + + // Check that plugin was sideloaded with custom namespace + expect(app.plugins.customNamespace).toBeDefined(); + expect(app.plugins.customNamespace.original_namespace).toBe('testB'); + resolve(); + })); + + it('handles string plugin format', () => new Promise((resolve) => { + const app = new spawnpoint({ cwd: __dirname, configFile: 'config/loadPlugins.json' }); + app.initConfig(); + app.initCodes(); + app.initRegistry(); + app.loadPlugins(); + + // String format plugin should be loaded + expect(app.plugins.testB).toBeDefined(); + resolve(); + })); +}); diff --git a/test/output.js b/test/output.js deleted file mode 100644 index e385781..0000000 --- a/test/output.js +++ /dev/null @@ -1,185 +0,0 @@ -'use strict'; -const dayjs = require('dayjs'); -const expect = require('unexpected'); - -const processVoid = require('./process-void/void.js'); -const spawnpoint = require.resolve('..'); - -const timeFormat = { - format: '{date} {type}: {line}', - time: 'HH:mm', - date: 'dddd, MMMM DD YYYY', -}; - -const datePattern = /\[(Mon|Tues|Wednes|Thurs|Fri|Satur|Sun)day, (January ([0-2]\d|3[01])|February [0-2]\d|Ma(rch|y) ([0-2]\d|3[01])|April ([0-2]\d|30)|June ([0-2]\d|30)|July ([0-2]\d|3[01])|August ([0-2]\d|3[01])|(Sept|Nov)ember ([0-2]\d|30)|(Octo|Decem)ber ([0-2]\d|3[01])) \d{4}]/; - -// resources for creating tests: -// https://sinonjs.org/ -// https://github.com/elliotf/mocha-sinon -// https://github.com/mochajs/mocha/issues/1582 - - -/** - * Adds 1 minute to a buffered string (e.g. [13:37]) converted to Array - * @param {Array} time buffered string (e.g. [13:37]) converted to Array - */ -const subtractOneMinute = (time) => { - const tCopy = time; - console.log('t: ' + tCopy); - tCopy[5]--; - /** - * The two digit numbers here are hex number references to a Buffer - * hexes 48-57 correspond to 0-9 (as string characters) - * The first if loop should be read as: - * if(time[seconds] < 0){ - * time[seconds] = 9; - * time[minutes]--; - * ... - */ - if (tCopy[5] < 48) { - tCopy[5] = 57; - tCopy[4]--; - if (tCopy[4] < 48) { - tCopy[4] = 53; - tCopy[2]--; - if (tCopy[2] < 48) { - tCopy[2] = 57; - tCopy[1]--; - if (tCopy[1] < 48) { - tCopy[1] = 50; - tCopy[2] = 51; - } - } - } - } - - return tCopy; -}; - -/** - * Checks time from test to currentTime generated by test - * @param {Buffer} data recieved from 'app.stdout.data' - * @param {String} time recieved from 'time = currentTime.format(timeFormat.time)' - */ -const reformTimeData = (data, time) => { - time = '[' + time + ']'; - const dataTime = [...data].slice(0, 7); - const remains = [...data].slice(7, [...data].length); - - if (Buffer.from(dataTime).toString() !== time) { - const newTime = subtractOneMinute(dataTime); - const newTimeString = Buffer.from(newTime).toString(); - if (newTimeString !== time) { - return Buffer.from('Check reformTimeData(), result was: ' + Buffer.from([...newTime, ...remains]).toString()); - } - return Buffer.from([...newTime, ...remains]); - } - return data; -}; - -describe('spawnpoint.debug', () => { - it('should output Test', (done) => { - const app = new processVoid(done, spawnpoint, { construct: true }); - void app.stdout.once('data', (data) => { - expect(data, 'when decoded as', 'utf8', 'to equal', 'Test\n'); - void app.done(); - }); - app.config.debug = true; - void app.debug('Test'); - }); -}); - -describe('spawnpoint.log', () => { - it('should output Test', (done) => { - const app = new processVoid(done, spawnpoint, { construct: true }); - app.stdout.once('data', (data) => { - const currentTime = dayjs(); - if (datePattern.test(data)) { - const date = currentTime.format(timeFormat.date); - expect(data, 'when decoded as', 'utf8', 'to equal', `[${date}]\n`); - app.stdout.once('data', (data) => { - const time = currentTime.format(timeFormat.time); - data = reformTimeData(data, time); - expect(data, 'when decoded as', 'utf8', 'to equal', `[${time}] [LOG]: Test\n`); - void app.done(); - }); - } else { - const time = currentTime.format(timeFormat.time); - data = reformTimeData(data, time); - expect(data, 'when decoded as', 'utf8', 'to equal', `[${time}] [LOG]: Test\n`); - void app.done(); - } - }); - //app.send({"set": {'key': 'config.log', 'value': timeFormat}}); - app.config.log = timeFormat; - //app.send({'command': 'log', args: ["Test"]}); - app.log('Test'); - }); -}); - -describe('spawnpoint.info', () => { - it('should output Test', (done) => { - //const app = fork('./autoload-void', [''], { 'silent': true }); - const app = new processVoid(done, spawnpoint, { construct: true }); - app.stdout.once('data', (data) => { - const currentTime = dayjs(); - if (datePattern.test(data)) { - const date = currentTime.format(timeFormat.date); - expect(data, 'when decoded as', 'utf8', 'to equal', `[${date}]\n`); - app.stdout.once('data', (data) => { - const time = currentTime.format(timeFormat.time); - data = reformTimeData(data, time); - expect(data, 'when decoded as', 'utf8', 'to equal', `[${time}] [INFO]: Test\n`); - void app.done(); - }); - } else { - const time = currentTime.format(timeFormat.time); - data = reformTimeData(data, time); - expect(data, 'when decoded as', 'utf8', 'to equal', `[${time}] [INFO]: Test\n`); - void app.done(); - } - }); - //app.send({"set": {'key': 'config.log', 'value': timeFormat}}); - app.config.log = timeFormat; - app.info('Test'); - //app.send({'command': 'info', args: ["Test"]}); - }); -}); - -describe('spawnpoint.warn', () => { - it('should output Test', (done) => { - const app = new processVoid(done, spawnpoint, { construct: true }); - const currentTime = dayjs(); - app.stdout.once('data', (data) => { - const date = currentTime.format(timeFormat.date); - expect(data, 'when decoded as', 'utf8', 'to equal', `[${date}]\n`); - }); - app.stderr.once('data', (data) => { - const time = currentTime.format(timeFormat.time); - data = reformTimeData(data, time); - expect(data, 'when decoded as', 'utf8', 'to equal', `[${time}] [WARN]: Test\n`); - void app.done(); - }); - app.config.log = timeFormat; - app.warn('Test'); - }); -}); - -describe('spawnpoint.error', () => { - it('should output Test', (done) => { - const app = new processVoid(done, spawnpoint, { construct: true }); - const currentTime = dayjs(); - app.stdout.once('data', (data) => { - const date = currentTime.format(timeFormat.date); - expect(data, 'when decoded as', 'utf8', 'to equal', `[${date}]\n`); - }); - app.stderr.once('data', (data) => { - const time = currentTime.format(timeFormat.time); - data = reformTimeData(data, time); - expect(data, 'when decoded as', 'utf8', 'to equal', `[${time}] [ERROR]: Test\n`); - void app.done(); - }); - app.config.log = timeFormat; - app.error('Test'); - }); -}); diff --git a/test/output.mjs b/test/output.mjs new file mode 100644 index 0000000..d2933b8 --- /dev/null +++ b/test/output.mjs @@ -0,0 +1,117 @@ +import { createRequire } from 'node:module'; + +import { describe, expect, it } from 'vitest'; + +import processVoid from './process-void/void.js'; + +const require = createRequire(import.meta.url); +const spawnpoint = require.resolve('..'); + +const timeFormat = { + format: '{date} {type}: {line}', + time: 'HH:mm', + date: 'dddd, MMMM DD YYYY', +}; + +// Pattern to match date line like "[Sunday, December 14 2025]" +const dateLinePattern = /^\[(Mon|Tues|Wednes|Thurs|Fri|Satur|Sun)day, (January|February|March|April|May|June|July|August|September|October|November|December) ([0-2]?\d|3[01]) \d{4}]\n$/; + +// Pattern to match time + log type + message like "[16:30] [LOG]: Test" +const timeLogPattern = /^\[\d{2}:\d{2}] \[LOG]: Test\n$/; +const timeInfoPattern = /^\[\d{2}:\d{2}] \[INFO]: Test\n$/; +const timeWarnPattern = /^\[\d{2}:\d{2}] \[WARN]: Test\n$/; +const timeErrorPattern = /^\[\d{2}:\d{2}] \[ERROR]: Test\n$/; + +describe('spawnpoint.debug', () => { + it('should output Test', () => new Promise((resolve) => { + const app = new processVoid(resolve, spawnpoint, { construct: true }); + void app.stdout.once('data', (data) => { + expect(data.toString('utf8')).toBe('Test\n'); + void app.done(); + }); + app.config.debug = true; + void app.debug('Test'); + })); +}); + +describe('spawnpoint.log', () => { + it('should output Test', () => new Promise((resolve) => { + const app = new processVoid(resolve, spawnpoint, { construct: true }); + app.stdout.once('data', (data) => { + const output = data.toString('utf8'); + if (dateLinePattern.test(output)) { + // First output is just the date line, wait for the log line + app.stdout.once('data', (data) => { + expect(data.toString('utf8')).toMatch(timeLogPattern); + void app.done(); + }); + } else { + // Output may contain date line + log line together due to buffering + // Extract and verify the log line + const lines = output.split('\n').filter(Boolean); + const logLine = lines.find(line => line.includes('[LOG]:')); + expect(logLine).toBeTruthy(); + expect(logLine + '\n').toMatch(timeLogPattern); + void app.done(); + } + }); + app.config.log = timeFormat; + app.log('Test'); + })); +}); + +describe('spawnpoint.info', () => { + it('should output Test', () => new Promise((resolve) => { + const app = new processVoid(resolve, spawnpoint, { construct: true }); + app.stdout.once('data', (data) => { + const output = data.toString('utf8'); + if (dateLinePattern.test(output)) { + // First output is just the date line, wait for the info line + app.stdout.once('data', (data) => { + expect(data.toString('utf8')).toMatch(timeInfoPattern); + void app.done(); + }); + } else { + // Output may contain date line + info line together due to buffering + // Extract and verify the info line + const lines = output.split('\n').filter(Boolean); + const infoLine = lines.find(line => line.includes('[INFO]:')); + expect(infoLine).toBeTruthy(); + expect(infoLine + '\n').toMatch(timeInfoPattern); + void app.done(); + } + }); + app.config.log = timeFormat; + app.info('Test'); + })); +}); + +describe('spawnpoint.warn', () => { + it('should output Test', () => new Promise((resolve) => { + const app = new processVoid(resolve, spawnpoint, { construct: true }); + app.stdout.once('data', (data) => { + expect(data.toString('utf8')).toMatch(dateLinePattern); + }); + app.stderr.once('data', (data) => { + expect(data.toString('utf8')).toMatch(timeWarnPattern); + void app.done(); + }); + app.config.log = timeFormat; + app.warn('Test'); + })); +}); + +describe('spawnpoint.error', () => { + it('should output Test', () => new Promise((resolve) => { + const app = new processVoid(resolve, spawnpoint, { construct: true }); + app.stdout.once('data', (data) => { + expect(data.toString('utf8')).toMatch(dateLinePattern); + }); + app.stderr.once('data', (data) => { + expect(data.toString('utf8')).toMatch(timeErrorPattern); + void app.done(); + }); + app.config.log = timeFormat; + app.error('Test'); + })); +}); diff --git a/test/parseEnvValue.mjs b/test/parseEnvValue.mjs new file mode 100644 index 0000000..d5690c0 --- /dev/null +++ b/test/parseEnvValue.mjs @@ -0,0 +1,88 @@ +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; + +import { + beforeEach, + describe, + expect, + it, +} from 'vitest'; + +import spawnpoint from '../index.js'; + +const __dirname = path.dirname(fileURLToPath(import.meta.url)); + +describe('spawnpoint._parseEnvValue', () => { + let app; + beforeEach(() => { + app = new spawnpoint({ cwd: __dirname }); + }); + + it('returns non-string values unchanged', () => { + expect(app._parseEnvValue(123)).toBe(123); + expect(app._parseEnvValue(null)).toBe(null); + expect(app._parseEnvValue(undefined)).toBe(undefined); + expect(app._parseEnvValue({ foo: 'bar' })).toEqual({ foo: 'bar' }); + }); + + it('parses "true" to boolean true (case-insensitive)', () => { + expect(app._parseEnvValue('true')).toBe(true); + expect(app._parseEnvValue('TRUE')).toBe(true); + expect(app._parseEnvValue('True')).toBe(true); + }); + + it('parses "false" to boolean false (case-insensitive)', () => { + expect(app._parseEnvValue('false')).toBe(false); + expect(app._parseEnvValue('FALSE')).toBe(false); + expect(app._parseEnvValue('False')).toBe(false); + }); + + it('parses "null" to null (case-insensitive)', () => { + expect(app._parseEnvValue('null')).toBe(null); + expect(app._parseEnvValue('NULL')).toBe(null); + expect(app._parseEnvValue('Null')).toBe(null); + }); + + it('parses integer strings to numbers', () => { + expect(app._parseEnvValue('42')).toBe(42); + expect(app._parseEnvValue('0')).toBe(0); + expect(app._parseEnvValue('-100')).toBe(-100); + }); + + it('parses float strings to numbers', () => { + expect(app._parseEnvValue('3.14')).toBe(3.14); + expect(app._parseEnvValue('-2.5')).toBe(-2.5); + expect(app._parseEnvValue('0.001')).toBe(0.001); + }); + + it('parses JSON object strings', () => { + expect(app._parseEnvValue('{"foo":"bar"}')).toEqual({ foo: 'bar' }); + expect(app._parseEnvValue('{"nested":{"value":123}}')).toEqual({ nested: { value: 123 } }); + }); + + it('parses JSON array strings', () => { + expect(app._parseEnvValue('[1,2,3]')).toEqual([1, 2, 3]); + expect(app._parseEnvValue('["a","b","c"]')).toEqual(['a', 'b', 'c']); + }); + + it('returns original string for invalid JSON', () => { + expect(app._parseEnvValue('{invalid}')).toBe('{invalid}'); + expect(app._parseEnvValue('[unclosed')).toBe('[unclosed'); + }); + + it('returns original string for non-parseable values', () => { + expect(app._parseEnvValue('hello world')).toBe('hello world'); + expect(app._parseEnvValue('not-a-number')).toBe('not-a-number'); + expect(app._parseEnvValue('')).toBe(''); + }); + + it('does not parse JSON when allowJson is false', () => { + expect(app._parseEnvValue('{"foo":"bar"}', false)).toBe('{"foo":"bar"}'); + expect(app._parseEnvValue('[1,2,3]', false)).toBe('[1,2,3]'); + }); + + it('still parses primitives when allowJson is false', () => { + expect(app._parseEnvValue('true', false)).toBe(true); + expect(app._parseEnvValue('42', false)).toBe(42); + }); +}); diff --git a/test/plugin.js b/test/plugin.js deleted file mode 100644 index e771004..0000000 --- a/test/plugin.js +++ /dev/null @@ -1,31 +0,0 @@ -'use strict'; -const assert = require('node:assert'); - -const spawnpoint = require('..'); - -describe('plugin registration', () => { - it('registers sync plugin', () => { - const pluginFn = function(app) { - app.config[this.namespace] = true; - }; - - const results = { - dir: __dirname, - name: 'Text Plugin', - namespace: 'testPlugin', - exports: pluginFn, - codes: null, - config: null, - }; - - assert.deepStrictEqual(results, spawnpoint.registerPlugin({ - dir: __dirname, - name: 'Text Plugin', - namespace: 'testPlugin', - exports: pluginFn, - })); - }); -}); - - -// TODO mock require and testing a plugin loading via sync/async diff --git a/test/plugin.mjs b/test/plugin.mjs new file mode 100644 index 0000000..0e7a6a7 --- /dev/null +++ b/test/plugin.mjs @@ -0,0 +1,64 @@ +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; + +import { describe, expect, it } from 'vitest'; + +import spawnpoint from '../index.js'; + +const __dirname = path.dirname(fileURLToPath(import.meta.url)); + +describe('plugin registration', () => { + it('registers sync plugin', () => { + const pluginFn = function(app) { + app.config[this.namespace] = true; + }; + + const results = { + dir: __dirname, + name: 'Text Plugin', + namespace: 'testPlugin', + exports: pluginFn, + codes: null, + config: null, + }; + + expect(spawnpoint.registerPlugin({ + dir: __dirname, + name: 'Text Plugin', + namespace: 'testPlugin', + exports: pluginFn, + })).toEqual(results); + }); + + it('throws without required name option', () => { + const pluginFn = function(app) { + app.config[this.namespace] = true; + }; + + expect(() => spawnpoint.registerPlugin({ + dir: __dirname, + namespace: 'testPlugin', + exports: pluginFn, + })).toThrow('Plugin is missing required `name` option.'); + }); + + it('throws without required namespace option', () => { + const pluginFn = function(app) { + app.config[this.namespace] = true; + }; + + expect(() => spawnpoint.registerPlugin({ + dir: __dirname, + name: 'Test Plugin', + exports: pluginFn, + })).toThrow('Plugin is missing required `namespace` option.'); + }); + + it('throws without required exports option', () => { + expect(() => spawnpoint.registerPlugin({ + dir: __dirname, + name: 'Test Plugin', + namespace: 'testPlugin', + })).toThrow('Plugin is missing required `exports` function.'); + }); +}); diff --git a/test/process-void/void.js b/test/process-void/void.js index 968ab1b..9ea9d41 100644 --- a/test/process-void/void.js +++ b/test/process-void/void.js @@ -21,6 +21,7 @@ class ProcessVoid { const self = {}; const forkOptions = { silent: true, + cwd: options.cwd || process.cwd(), }; if (typeof(callback) === 'function') { self.callback = callback; diff --git a/test/properties.mjs b/test/properties.mjs new file mode 100644 index 0000000..56bb92f --- /dev/null +++ b/test/properties.mjs @@ -0,0 +1,119 @@ +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; + +import { + beforeEach, + describe, + expect, + it, +} from 'vitest'; + +import spawnpoint from '../index.js'; + +const __dirname = path.dirname(fileURLToPath(import.meta.url)); + +describe('spawnpoint.containerized property', () => { + let app; + beforeEach(() => { + app = new spawnpoint({ cwd: __dirname }); + }); + + it('returns cached value on subsequent access', () => { + // First access triggers lazy load + const firstAccess = app.containerized; + // Second access returns cached value + const secondAccess = app.containerized; + expect(firstAccess).toBe(secondAccess); + }); + + it('allows setting containerized value', () => { + app.containerized = true; + expect(app.containerized).toBe(true); + expect(app._containerized).toBe(true); + + app.containerized = false; + expect(app.containerized).toBe(false); + }); + + it('is enumerable', () => { + const keys = Object.keys(app); + expect(keys).toContain('containerized'); + }); +}); + +describe('spawnpoint.configBlocklist property', () => { + let app; + beforeEach(() => { + app = new spawnpoint({ cwd: __dirname }); + }); + + it('lazy loads config blocklist on first access', () => { + const blocklist = app.configBlocklist; + expect(blocklist).toBeDefined(); + expect(blocklist.env).toBeDefined(); + expect(blocklist.secrets).toBeDefined(); + expect(blocklist.args).toBeDefined(); + }); + + it('returns cached blocklist on subsequent access', () => { + const first = app.configBlocklist; + const second = app.configBlocklist; + expect(first).toBe(second); + }); + + it('allows setting configBlocklist value', () => { + const customBlocklist = { + env: { list: ['CUSTOM'], patterns: [] }, + secrets: { list: [], patterns: [] }, + args: { list: [], patterns: [] }, + }; + app.configBlocklist = customBlocklist; + expect(app.configBlocklist).toBe(customBlocklist); + }); + + it('is enumerable', () => { + const keys = Object.keys(app); + expect(keys).toContain('configBlocklist'); + }); +}); + +describe('spawnpoint lazy-loaded properties', () => { + let app; + beforeEach(() => { + app = new spawnpoint({ cwd: __dirname }); + app.initConfig(); + app.initCodes(); + }); + + it('_errorCode is lazy loaded and cached', () => { + const errorCode = app._errorCode; + expect(errorCode).toBeDefined(); + expect(typeof errorCode).toBe('function'); + // Second access returns same reference + expect(app._errorCode).toBe(errorCode); + }); + + it('_failCode is lazy loaded and cached', () => { + const failCode = app._failCode; + expect(failCode).toBeDefined(); + expect(typeof failCode).toBe('function'); + // Second access returns same reference + expect(app._failCode).toBe(failCode); + }); + + it('_roundRobin is lazy loaded and cached', () => { + const roundRobin = app._roundRobin; + expect(roundRobin).toBeDefined(); + expect(typeof roundRobin).toBe('function'); + // Second access returns same reference + expect(app._roundRobin).toBe(roundRobin); + }); + + it('_getAndLock is lazy loaded and cached', () => { + const getAndLock = app._getAndLock; + expect(getAndLock).toBeDefined(); + expect(typeof getAndLock).toBe('function'); + // Second access returns same reference + expect(app._getAndLock).toBe(getAndLock); + }); +}); diff --git a/test/random.js b/test/random.js deleted file mode 100644 index 224ea5f..0000000 --- a/test/random.js +++ /dev/null @@ -1,28 +0,0 @@ -'use strict'; -const assert = require('node:assert'); - -const spawnpoint = require('..'); -describe('spawnpoint.random', () => { - const app = new spawnpoint(); - it('fails with bad/invalid options', () => { - assert.throws(() => app.random(''), Error); - assert.throws(() => app.random(false), Error); - assert.throws(() => app.random(true), Error); - assert.throws(() => app.random({ foo: 'bar' }), Error); - assert.throws(() => app.random(['foo', 'bar']), Error); - assert.throws(() => app.random('five'), Error); - }); - - it('successfully creates 10000 random strings', () => { - const randomStrings = []; - for (let i = 0; i < 10000; i++) { - randomStrings.push(app.random()); - } - assert(!randomStrings.every(item => item === randomStrings[0])); - }); - - it('defaults to 16 if length is less then 1', () => { - assert(app.random(0).length === 16); - assert(app.random(-12).length === 16); - }); -}); diff --git a/test/random.mjs b/test/random.mjs new file mode 100644 index 0000000..2827174 --- /dev/null +++ b/test/random.mjs @@ -0,0 +1,40 @@ +import { describe, expect, it } from 'vitest'; + +import spawnpoint from '../index.js'; + +describe('spawnpoint.random', () => { + const app = new spawnpoint(); + it('fails with bad/invalid options', () => { + expect(() => app.random('')).toThrow(Error); + expect(() => app.random(false)).toThrow(Error); + expect(() => app.random(true)).toThrow(Error); + expect(() => app.random({ foo: 'bar' })).toThrow(Error); + expect(() => app.random(['foo', 'bar'])).toThrow(Error); + expect(() => app.random('five')).toThrow(Error); + }); + + it('successfully creates 10000 random strings', () => { + const randomStrings = []; + for (let i = 0; i < 10000; i++) { + randomStrings.push(app.random()); + } + expect(randomStrings.every(item => item === randomStrings[0])).toBe(false); + }); + + it('defaults to 16 if length is less then 1', () => { + expect(app.random(0).length).toBe(16); + expect(app.random(-12).length).toBe(16); + }); + + it('generates string of specified length', () => { + expect(app.random(8).length).toBe(8); + expect(app.random(32).length).toBe(32); + expect(app.random(64).length).toBe(64); + }); + + it('generates unique strings on each call', () => { + const str1 = app.random(32); + const str2 = app.random(32); + expect(str1).not.toBe(str2); + }); +}); diff --git a/test/recursiveList.js b/test/recursiveList.js deleted file mode 100644 index b9da189..0000000 --- a/test/recursiveList.js +++ /dev/null @@ -1,75 +0,0 @@ -'use strict'; -const assert = require('node:assert'); - -const _ = require('lodash'); -const expect = require('unexpected'); - -const spawnpoint = require('..'); - -describe('spawnpoint.recursiveList', () => { - const app = new spawnpoint(); - it('fails with bad/invalid path', () => { - assert.throws(() => app.recursiveList(), Error); - assert.throws(() => app.recursiveList(false), Error); - assert.throws(() => app.recursiveList(true), Error); - assert.throws(() => app.recursiveList({ foo: 'bar' }), Error); - assert.throws(() => app.recursiveList(['foo', 'bar']), Error); - - // REVIEW: these tests due to the way recursiveList suppresses empty results to an empty array. - // This might be worth reviewing when an error is expected, rather than a silent result. - // The reasoning currently is that it's internal for a positive result of "find what you can" - - // assert.throws(() => app.recursiveList(''), Error); - // assert.throws(() => app.recursiveList('invalid/path'), Error); - }); - - it('lists files', () => { - const tests = { - default: [], - js: [], - txt: [ - 'store/list/1.txt', - 'store/list/2.txt', - 'store/list/3.txt', - 'store/list/recursive/4.txt', - ], - }; - - const results = { - default: app.recursiveList('store/list'), - js: app.recursiveList('store/list', '.js'), - txt: app.recursiveList('store/list', '.txt'), - }; - - assert(_.xor(tests.default, results.default).length === 0, `default list failed to match expected [${tests.default.join(',')}]. Provided [${results.default.join(',')}]`); - assert(_.xor(tests.js, results.js).length === 0, `js list failed to match expected [${tests.js.join(',')}]. Provided [${results.js.join(',')}]`); - assert(_.xor(tests.txt, results.txt).length === 0, `txt list failed to match expected [${tests.txt.join(',')}]. Provided [${results.txt.join(',')}]`); - }); - - it('lists files a single directory away', () => { - const tests = { - json: [ - 'json/bad.json', - 'json/badLint.json', - 'json/commented.json', - 'json/good.json', - ], - }; - const results = { - default: app.recursiveList('json'), - json: app.recursiveList('json', '.json'), - }; - expect(results.default, 'to equal', []); - expect(results.json, 'to equal', tests.json); - }); - - it('lists directories', () => { - const test = [ - './config', - './json', - './store', - ]; - const results = app.recursiveList('.', '/'); - expect(results, 'to contain', ...test); - }); -}); diff --git a/test/recursiveList.mjs b/test/recursiveList.mjs new file mode 100644 index 0000000..24bb419 --- /dev/null +++ b/test/recursiveList.mjs @@ -0,0 +1,117 @@ +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; + +import _ from 'lodash'; +import { describe, expect, it } from 'vitest'; + +import spawnpoint from '../index.js'; + +const __dirname = path.dirname(fileURLToPath(import.meta.url)); + +// recursiveList uses fs.statSync with relative paths, so we need process.chdir +// to ensure paths like 'store/list' resolve correctly +process.chdir(__dirname); + +describe('spawnpoint.recursiveList', () => { + const app = new spawnpoint({ cwd: __dirname }); + it('fails with bad/invalid path', () => { + expect(() => app.recursiveList()).toThrow(Error); + expect(() => app.recursiveList(false)).toThrow(Error); + expect(() => app.recursiveList(true)).toThrow(Error); + expect(() => app.recursiveList({ foo: 'bar' })).toThrow(Error); + expect(() => app.recursiveList(['foo', 'bar'])).toThrow(Error); + + // REVIEW: these tests due to the way recursiveList suppresses empty results to an empty array. + // This might be worth reviewing when an error is expected, rather than a silent result. + // The reasoning currently is that it's internal for a positive result of "find what you can" + + // expect(() => app.recursiveList('')).toThrow(Error); + // expect(() => app.recursiveList('invalid/path')).toThrow(Error); + }); + + it('lists files', () => { + const tests = { + default: [], + js: [], + txt: [ + 'store/list/1.txt', + 'store/list/2.txt', + 'store/list/3.txt', + 'store/list/recursive/4.txt', + ], + }; + + const results = { + default: app.recursiveList('store/list'), + js: app.recursiveList('store/list', '.js'), + txt: app.recursiveList('store/list', '.txt'), + }; + + expect(_.xor(tests.default, results.default).length, `default list failed to match expected [${tests.default.join(',')}]. Provided [${results.default.join(',')}]`).toBe(0); + expect(_.xor(tests.js, results.js).length, `js list failed to match expected [${tests.js.join(',')}]. Provided [${results.js.join(',')}]`).toBe(0); + expect(_.xor(tests.txt, results.txt).length, `txt list failed to match expected [${tests.txt.join(',')}]. Provided [${results.txt.join(',')}]`).toBe(0); + }); + + it('lists files a single directory away', () => { + const tests = { + json: [ + 'json/bad.json', + 'json/badLint.json', + 'json/commented.json', + 'json/good.json', + ], + }; + const results = { + default: app.recursiveList('json'), + json: app.recursiveList('json', '.json'), + }; + expect(results.default).toEqual([]); + expect(results.json).toEqual(tests.json); + }); + + it('lists directories', () => { + const test = [ + './config', + './json', + './store', + ]; + const results = app.recursiveList('.', '/'); + for (const dir of test) { + expect(results).toContain(dir); + } + }); + + it('returns empty array for non-existent directory', () => { + const results = app.recursiveList('non-existent-directory'); + expect(results).toEqual([]); + }); + + it('returns empty array when path is a file, not directory', () => { + const results = app.recursiveList('store/list/1.txt'); + expect(results).toEqual([]); + }); + + it('accepts string extension and converts to array', () => { + const results = app.recursiveList('store/list', '.txt'); + expect(results.length).toBeGreaterThan(0); + expect(results.every(file => file.endsWith('.txt'))).toBe(true); + }); + + it('returns all files when exts is falsy', () => { + const results = app.recursiveList('store/list', false); + expect(results.length).toBeGreaterThan(0); + }); + + it('handles backslash path normalization', () => { + // This tests that paths with backslashes are normalized to forward slashes + const results = app.recursiveList('.\\store\\list', '.txt'); + expect(results.length).toBeGreaterThan(0); + }); + + it('handles trailing slash correctly', () => { + const withSlash = app.recursiveList('store/list/', '.txt'); + const withoutSlash = app.recursiveList('store/list', '.txt'); + // Both should return the same number of files + expect(withSlash.length).toEqual(withoutSlash.length); + }); +}); diff --git a/test/registerConfig.mjs b/test/registerConfig.mjs new file mode 100644 index 0000000..be2bce7 --- /dev/null +++ b/test/registerConfig.mjs @@ -0,0 +1,84 @@ +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; + +import { + beforeEach, + describe, + expect, + it, +} from 'vitest'; + +import spawnpoint from '../index.js'; + +const __dirname = path.dirname(fileURLToPath(import.meta.url)); + +describe('spawnpoint.registerConfig', () => { + let app; + beforeEach(() => { + app = new spawnpoint({ cwd: __dirname, configFile: 'config/blocklistPatterns.json' }); + app.initConfig(); + }); + + it('registers config without blocklist check', () => { + app.registerConfig('myKey', 'myValue'); + expect(app.config.myKey).toBe('myValue'); + }); + + it('blocks config in env blocklist by name', () => { + const result = app.registerConfig('BLOCKED_VAR', 'should-be-blocked', 'env'); + expect(app.config.BLOCKED_VAR).toBeUndefined(); + expect(result).toBe(app); // returns this from debug() + }); + + it('blocks config matching env blocklist pattern', () => { + app.registerConfig('SECRET_KEY', 'should-be-blocked', 'env'); + expect(app.config.SECRET_KEY).toBeUndefined(); + + app.registerConfig('PRIVATE_TOKEN', 'should-be-blocked', 'env'); + expect(app.config.PRIVATE_TOKEN).toBeUndefined(); + }); + + it('allows config not in blocklist', () => { + app.registerConfig('ALLOWED_VAR', 'allowed-value', 'env'); + expect(app.config.ALLOWED_VAR).toBe('allowed-value'); + }); + + it('handles args blocklist', () => { + app.registerConfig('blocked-arg', 'blocked', 'args'); + expect(app.config['blocked-arg']).toBeUndefined(); + + app.registerConfig('secret-password', 'blocked', 'args'); + expect(app.config['secret-password']).toBeUndefined(); + }); + + it('handles secrets blocklist', () => { + app.registerConfig('api_key_main', 'blocked', 'secrets'); + expect(app.config.api_key_main).toBeUndefined(); + }); + + it('uses _.set for env/secrets/config-hoist', () => { + app.registerConfig('nested.deep.value', 'test-value', 'env'); + expect(app.config.nested.deep.value).toBe('test-value'); + + app.registerConfig('another.nested', 'secret-value', 'secrets'); + expect(app.config.another.nested).toBe('secret-value'); + + app.registerConfig('hoist.value', 'hoisted', 'config-hoist'); + expect(app.config.hoist.value).toBe('hoisted'); + }); + + it('merges config when name is object and config is falsy', () => { + app.registerConfig({ mergedKey: 'mergedValue' }); + expect(app.config.mergedKey).toBe('mergedValue'); + }); +}); + +describe('spawnpoint.registerConfig with debug mode', () => { + it('logs when setting env variable in debug mode', () => { + const app = new spawnpoint({ cwd: __dirname, configFile: 'config/debugEnabled.json' }); + app.initConfig(); + // In debug mode, setting an allowed env var should log + app.registerConfig('DEBUG_TEST_VAR', 'debug-value', 'env'); + expect(app.config.DEBUG_TEST_VAR).toBe('debug-value'); + }); +}); diff --git a/test/registerLimit.js b/test/registerLimit.js deleted file mode 100644 index ae3b950..0000000 --- a/test/registerLimit.js +++ /dev/null @@ -1,118 +0,0 @@ -'use strict'; -const expect = require('unexpected').clone().use(require('unexpected-eventemitter')); - -const spawnpoint = require('..'); - -// eslint-disable-next-line unicorn/custom-error-definition -class customError extends Error { - constructor(err) { - super(err); - this.name = 'customError'; - } -} - -process.chdir(__dirname); -describe('spawnpoint.registerLimit', () => { - let app; - // TODO - - beforeEach(() => { - app = new spawnpoint(); - }); - it('should trigger the callback', (done) => { - app.config.trackErrors = true; - app.registerError('test.code', customError); - app.setup(); - app.registerLimit('test.code', 2, () => { - app.emit('testing.success'); - }); - app.emit('test.code'); - //expect(() => app.emit('test.code'), 'to emit from', app, 'testing.success'); - done(); - }); -}); - -describe('spawnpoint.initLimitListeners', () => { - let app; - - it('should register errors correctly', (done) => { - app = new spawnpoint('config/limitErrors.json'); - const listeners = []; - app.on('newListener', (value) => { - listeners.push(value); - }); - app.setup(() => { - expect(listeners, 'to contain', 'errorCode', 'failCode'); - done(); - }); - }); -}); - -describe('spawnpoint.initLimitListeners.limitToErrors', () => { - let app; - beforeEach(() => { - app = new spawnpoint('config/limitErrors.json'); - }); - - it('should run without errors', (done) => { - app.setup(); - app.errorCode('test.code'); - done(); - }); - - it('should correctly handle tracked errors', (done) => { - app.registerError('test.code', customError); - app.registerLimit('test.code', 1, { reset: -1 }, (data) => { - expect(data.occurrences, 'to equal', 1); - app.config.done = true; - }); - app.setup(); - app.config.done = false; - app.errorCode('test.code'); - expect(app.config.done, 'to be true'); - done(); - }); - - it('should reset a tracked error', (done) => { - app.registerError('test.code', customError); - app.registerLimit('test.code', 2, { reset: 1 }, (data) => { - expect(data.balance, 'to equal', 2); - app.config.done++; - }); - app.setup(); - app.config.done = 0; - app.errorCode('test.code'); - app.errorCode('test.code'); - expect(app.config.done, 'to equal', 1); - app.errorCode('test.code'); - app.errorCode('test.code'); - expect(app.config.done, 'to equal', 2); - done(); - }); - - it('should gradually timeout the error', (done) => { - app.registerError('test.code', customError); - app.registerLimit('test.code', 2, { time: 100 }, (data) => { - expect(data.balance, 'to equal', 2); - expect(data.occurrences, 'to be one of', [2, 5]); - app.config.done++; - }); - app.setup(); - app.config.done = 0; - setTimeout(function() { - app.errorCode('test.code'); - }, 50); - setTimeout(function() { - app.errorCode('test.code'); - }, 100); - setTimeout(function() { - app.errorCode('test.code'); - }, 300); - setTimeout(function() { - app.errorCode('test.code'); - app.errorCode('test.code'); - expect(app.config.done, 'to equal', 2); - done(); - }, 500); - }); -}); diff --git a/test/registerLimit.mjs b/test/registerLimit.mjs new file mode 100644 index 0000000..e4178b5 --- /dev/null +++ b/test/registerLimit.mjs @@ -0,0 +1,202 @@ +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; + +import { + beforeEach, + describe, + expect, + it, +} from 'vitest'; + +import spawnpoint from '../index.js'; + +const __dirname = path.dirname(fileURLToPath(import.meta.url)); + +// eslint-disable-next-line unicorn/custom-error-definition +class customError extends Error { + constructor(err) { + super(err); + this.name = 'customError'; + } +} + +describe('spawnpoint.registerLimit', () => { + let app; + // TODO + + beforeEach(() => { + app = new spawnpoint({ cwd: __dirname }); + }); + it('should trigger the callback', () => new Promise((resolve) => { + app.config.trackErrors = true; + app.registerError('test.code', customError); + app.setup(); + app.registerLimit('test.code', 2, () => { + app.emit('testing.success'); + }); + app.emit('test.code'); + resolve(); + })); +}); + +describe('spawnpoint.initLimitListeners', () => { + let app; + + it('should register errors correctly', () => new Promise((resolve) => { + app = new spawnpoint({ cwd: __dirname, configFile: 'config/limitErrors.json' }); + const listeners = []; + app.on('newListener', (value) => { + listeners.push(value); + }); + app.setup(() => { + expect(listeners).toContain('errorCode'); + expect(listeners).toContain('failCode'); + resolve(); + }); + })); +}); + +describe('spawnpoint.initLimitListeners.limitToErrors', () => { + let app; + beforeEach(() => { + app = new spawnpoint({ cwd: __dirname, configFile: 'config/limitErrors.json' }); + }); + + it('should run without errors', () => new Promise((resolve) => { + app.setup(); + app.errorCode('test.code'); + resolve(); + })); + + it('should correctly handle tracked errors', () => new Promise((resolve) => { + app.registerError('test.code', customError); + app.registerLimit('test.code', 1, { reset: -1 }, (data) => { + expect(data.occurrences).toBe(1); + app.config.done = true; + }); + app.setup(); + app.config.done = false; + app.errorCode('test.code'); + expect(app.config.done).toBe(true); + resolve(); + })); + + it('should reset a tracked error', () => new Promise((resolve) => { + app.registerError('test.code', customError); + app.registerLimit('test.code', 2, { reset: 1 }, (data) => { + expect(data.balance).toBe(2); + app.config.done++; + }); + app.setup(); + app.config.done = 0; + app.errorCode('test.code'); + app.errorCode('test.code'); + expect(app.config.done).toBe(1); + app.errorCode('test.code'); + app.errorCode('test.code'); + expect(app.config.done).toBe(2); + resolve(); + })); + + it('should gradually timeout the error', () => new Promise((resolve) => { + app.registerError('test.code', customError); + app.registerLimit('test.code', 2, { time: 100 }, (data) => { + expect(data.balance).toBe(2); + expect([2, 5]).toContain(data.occurrences); + app.config.done++; + }); + app.setup(); + app.config.done = 0; + setTimeout(function() { + app.errorCode('test.code'); + }, 50); + setTimeout(function() { + app.errorCode('test.code'); + }, 100); + setTimeout(function() { + app.errorCode('test.code'); + }, 300); + setTimeout(function() { + app.errorCode('test.code'); + app.errorCode('test.code'); + expect(app.config.done).toBe(2); + resolve(); + }, 500); + })); +}); + +describe('spawnpoint.initLimitListeners with failCode', () => { + let app; + beforeEach(() => { + app = new spawnpoint({ cwd: __dirname, configFile: 'config/limitErrors.json' }); + }); + + it('should track failCode events', () => new Promise((resolve) => { + app.registerLimit('test.code', 1, { error: 'failCode', reset: -1 }, (data) => { + expect(data.occurrences).toBe(1); + app.config.done = true; + }); + app.setup(); + app.config.done = false; + app.failCode('test.code'); + expect(app.config.done).toBe(true); + resolve(); + })); + + it('should handle failCode with time-based reset', () => new Promise((resolve) => { + app.registerLimit('test.code', 2, { error: 'failCode', time: 100 }, (data) => { + expect(data.balance).toBe(2); + app.config.done++; + }); + app.setup(); + app.config.done = 0; + app.failCode('test.code'); + app.failCode('test.code'); + expect(app.config.done).toBe(1); + setTimeout(() => { + // After timeout, balance should reset + app.failCode('test.code'); + app.failCode('test.code'); + expect(app.config.done).toBe(2); + resolve(); + }, 250); + })); + + it('ignores failCode when no limit is registered', () => new Promise((resolve) => { + app.setup(); + // This should not throw - it's just ignored + app.failCode('test.code'); + resolve(); + })); +}); + +describe('spawnpoint.registerLimit options handling', () => { + let app; + beforeEach(() => { + app = new spawnpoint({ cwd: __dirname, configFile: 'config/limitErrors.json' }); + }); + + it('handles callback as third parameter (no options)', () => new Promise((resolve) => { + let callbackCalled = false; + app.registerLimit('test.code', 1, () => { + callbackCalled = true; + }); + app.setup(); + app.errorCode('test.code'); + expect(callbackCalled).toBe(true); + resolve(); + })); + + it('tracks by index when specified', () => new Promise((resolve) => { + const results = []; + app.registerLimit('test.code', 1, { index: 'data.id', reset: -1 }, (data) => { + results.push(data); + }); + app.setup(); + // These would normally be tracked separately by index + app.errorCode('test.code', { data: { id: 'a' } }); + app.errorCode('test.code', { data: { id: 'b' } }); + expect(results.length).toBeGreaterThan(0); + resolve(); + })); +}); diff --git a/test/require.js b/test/require.js deleted file mode 100644 index a0f0ddf..0000000 --- a/test/require.js +++ /dev/null @@ -1,14 +0,0 @@ -'use strict'; -const assert = require('node:assert'); - -const spawnpoint = require('..'); - -process.chdir(__dirname); -describe('spawnpoint.require', () => { - const app = new spawnpoint(); - it('Should require the test autoload-sync file', () => { - app.require('autoload-sync/sync.js'); - assert(app.customHoistedVarFromAutoload, 'Failed to autoload the file'); - }); - // TODO: handle user testing -}); diff --git a/test/require.mjs b/test/require.mjs new file mode 100644 index 0000000..af16a07 --- /dev/null +++ b/test/require.mjs @@ -0,0 +1,17 @@ +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; + +import { describe, expect, it } from 'vitest'; + +import spawnpoint from '../index.js'; + +const __dirname = path.dirname(fileURLToPath(import.meta.url)); + +describe('spawnpoint.require', () => { + const app = new spawnpoint({ cwd: __dirname }); + it('Should require the test autoload-sync file', () => { + app.require('autoload-sync/sync.js'); + expect(app.customHoistedVarFromAutoload).toBeTruthy(); + }); + // TODO: handle user testing +}); diff --git a/test/roundrobin.js b/test/roundrobin.mjs similarity index 60% rename from test/roundrobin.js rename to test/roundrobin.mjs index 15fa936..fe7aa55 100644 --- a/test/roundrobin.js +++ b/test/roundrobin.mjs @@ -1,21 +1,24 @@ -'use strict'; -const assert = require('node:assert'); +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; -const _ = require('lodash'); +import _ from 'lodash'; +import { describe, expect, it } from 'vitest'; -const spawnpoint = require('..'); +import spawnpoint from '../index.js'; + +const __dirname = path.dirname(fileURLToPath(import.meta.url)); describe('spawnpoint.roundRobin', () => { - const app = new spawnpoint(); + const app = new spawnpoint({ cwd: __dirname }); const test = ['one', 'two', 'three', 'four', 'five']; it('fails with bad/invalid options', () => { - assert.throws(() => app.roundRobin(''), Error); - assert.throws(() => app.roundRobin(10), Error); - assert.throws(() => app.roundRobin(false), Error); - assert.throws(() => app.roundRobin(true), Error); - assert.throws(() => app.roundRobin({ foo: 'bar' }), Error); - assert.throws(() => app.roundRobin('five'), Error); + expect(() => app.roundRobin('')).toThrow(Error); + expect(() => app.roundRobin(10)).toThrow(Error); + expect(() => app.roundRobin(false)).toThrow(Error); + expect(() => app.roundRobin(true)).toThrow(Error); + expect(() => app.roundRobin({ foo: 'bar' })).toThrow(Error); + expect(() => app.roundRobin('five')).toThrow(Error); }); it('next(): never calls the same value', () => { @@ -27,7 +30,7 @@ describe('spawnpoint.roundRobin', () => { while (i < (test.length * 15)) { i++; const results = rr.next(); - assert(!used.includes(results), 'roundRobin failed, item was reused unevenly'); + expect(used).not.toContain(results); used.push(results); // reset when full @@ -46,7 +49,7 @@ describe('spawnpoint.roundRobin', () => { while (i < (test.length * 15)) { i++; const results = rr.item; - assert(!used.includes(results), 'roundRobin failed, item was reused unevenly'); + expect(used).not.toContain(results); used.push(results); // reset when full @@ -57,7 +60,7 @@ describe('spawnpoint.roundRobin', () => { }); it('Still works when Spawnpoint has been initialized', () => { - const newApp = new spawnpoint(); + const newApp = new spawnpoint({ cwd: __dirname }); newApp.setup(); const rr = newApp.roundRobin(test); @@ -67,7 +70,7 @@ describe('spawnpoint.roundRobin', () => { while (i < (test.length * 15)) { i++; const results = rr.item; - assert(!used.includes(results), 'roundRobin failed, item was reused unevenly'); + expect(used).not.toContain(results); used.push(results); // reset when full @@ -82,12 +85,12 @@ describe('spawnpoint.roundRobin', () => { rr.next(); rr.next(); rr.clear(); - assert(rr.rrKeys.length === 0, 'rrKeys is not empty'); + expect(rr.rrKeys.length).toBe(0); }); it('trigger error when list is tampered', () => { const rr = app.roundRobin(test); rr.rrKeys = _.keys(test); - assert.throws(() => rr.next(), app._errorCode); + expect(() => rr.next()).toThrow(app._errorCode); }); }); diff --git a/test/sample.js b/test/sample.js deleted file mode 100644 index 80028b6..0000000 --- a/test/sample.js +++ /dev/null @@ -1,43 +0,0 @@ -'use strict'; -const assert = require('node:assert'); - -const _ = require('lodash'); - -const spawnpoint = require('..'); -describe('spawnpoint.sample', () => { - const app = new spawnpoint(); - it('fails with bad/invalid options', () => { - assert(!app.sample('')); - assert(!app.sample(false)); - assert(!app.sample(true)); - }); - - it('successfully picks items', () => { - const tests = { - obj: { - foo: 'bar', - bar: 'foo', - }, - deepObj: { - foo: { - bar: { - one: 'two', - }, - }, - a: { - foo: { - three: 'four', - }, - }, - }, - arr: [ - 'foo', 'bar', - ], - }; - - - assert(_.keys(tests.obj).includes(app.sample(tests.obj)), 'tests.obj failed sample'); - assert(_.findKey(tests.deepObj, app.sample(tests.deepObj)), 'tests.deepObj failed sample'); - assert(tests.arr.includes(app.sample(tests.arr)), 'tests.arr failed sample'); - }); -}); diff --git a/test/sample.mjs b/test/sample.mjs new file mode 100644 index 0000000..44fbb9c --- /dev/null +++ b/test/sample.mjs @@ -0,0 +1,52 @@ +import _ from 'lodash'; +import { describe, expect, it } from 'vitest'; + +import spawnpoint from '../index.js'; + +describe('spawnpoint.sample', () => { + const app = new spawnpoint(); + it('fails with bad/invalid options', () => { + expect(app.sample('')).toBeFalsy(); + expect(app.sample(false)).toBeFalsy(); + expect(app.sample(true)).toBeFalsy(); + }); + + it('successfully picks items', () => { + const tests = { + obj: { + foo: 'bar', + bar: 'foo', + }, + deepObj: { + foo: { + bar: { + one: 'two', + }, + }, + a: { + foo: { + three: 'four', + }, + }, + }, + arr: [ + 'foo', 'bar', + ], + }; + + + expect(_.keys(tests.obj)).toContain(app.sample(tests.obj)); + expect(_.findKey(tests.deepObj, app.sample(tests.deepObj))).toBeTruthy(); + expect(tests.arr).toContain(app.sample(tests.arr)); + }); + + it('returns undefined for empty collections', () => { + expect(app.sample({})).toBeUndefined(); + expect(app.sample([])).toBeUndefined(); + }); + + it('returns the only item from single-item collections', () => { + expect(app.sample({ only: 'value' })).toBe('value'); + expect(app.sample(['single'])).toBe('single'); + }); +}); diff --git a/test/spawnpointMethods.mjs b/test/spawnpointMethods.mjs new file mode 100644 index 0000000..b195774 --- /dev/null +++ b/test/spawnpointMethods.mjs @@ -0,0 +1,176 @@ +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; + +import { + beforeEach, + describe, + expect, + it, +} from 'vitest'; + +import spawnpoint from '../index.js'; + +const __dirname = path.dirname(fileURLToPath(import.meta.url)); + +describe('spawnpoint.omit', () => { + let app; + beforeEach(() => { + app = new spawnpoint({ cwd: __dirname }); + }); + + it('omits specified keys from object', () => { + const input = { foo: 'bar', baz: 'qux', keep: 'this' }; + const result = app.omit(input, ['foo', 'baz']); + expect(result).toEqual({ keep: 'this' }); + }); + + it('returns copy when no keys to omit', () => { + const input = { foo: 'bar' }; + const result = app.omit(input, []); + expect(result).toEqual(input); + expect(result).not.toBe(input); + }); + + it('handles missing keys gracefully', () => { + const input = { foo: 'bar' }; + const result = app.omit(input, ['nonexistent']); + expect(result).toEqual({ foo: 'bar' }); + }); +}); + +describe('spawnpoint.require', () => { + let app; + beforeEach(() => { + app = new spawnpoint({ cwd: __dirname }); + }); + + it('requires file with relative path', () => { + app.require('autoload-sync/sync.js'); + expect(app.customHoistedVarFromAutoload).toBe(true); + }); + + it('requires file with absolute path', () => { + const absolutePath = path.join(__dirname, 'autoload-sync/sync.js'); + // Reset to test absolute path handling + app.customHoistedVarFromAutoload = false; + app.require(absolutePath); + expect(app.customHoistedVarFromAutoload).toBe(true); + }); +}); + +describe('spawnpoint.debug', () => { + let app; + beforeEach(() => { + app = new spawnpoint({ cwd: __dirname }); + app.initConfig(); + }); + + it('returns this for chaining when debug is disabled', () => { + app.config.debug = false; + const result = app.debug('test message'); + expect(result).toBe(app); + }); + + it('logs and returns this when debug is enabled', () => { + app.config.debug = true; + const result = app.debug('debug message', { extra: 'data' }); + expect(result).toBe(app); + }); +}); + +describe('spawnpoint.setupJSONHandler', () => { + let app; + beforeEach(() => { + app = new spawnpoint({ cwd: __dirname }); + }); + + it('returns this for chaining', () => { + const result = app.setupJSONHandler(); + expect(result).toBe(app); + }); + + it('allows requiring JSON with comments', () => { + app.setupJSONHandler(); + // The JSON handler should now be set up + // This is tested more thoroughly in jsonHandler.mjs + }); +}); + +describe('spawnpoint.initCodes', () => { + let app; + beforeEach(() => { + app = new spawnpoint({ cwd: __dirname }); + }); + + it('initializes codes from internal codes folder', () => { + app.initCodes(); + // Should have system codes like spawnpoint.already_setup + expect(app.codes['spawnpoint.already_setup']).toBeDefined(); + }); + + it('emits app.setup.initCodes event', () => new Promise((resolve) => { + app.on('app.setup.initCodes', () => { + resolve(); + }); + app.initCodes(); + })); + + it('returns this for chaining', () => { + const result = app.initCodes(); + expect(result).toBe(app); + }); +}); + +describe('spawnpoint event methods return this', () => { + it('log returns this', () => { + const app = new spawnpoint({ cwd: __dirname }); + app.initConfig(); + const result = app.log('test'); + expect(result).toBe(app); + }); + + it('info returns this', () => { + const app = new spawnpoint({ cwd: __dirname }); + app.initConfig(); + const result = app.info('test'); + expect(result).toBe(app); + }); + + it('warn returns this', () => { + const app = new spawnpoint({ cwd: __dirname }); + app.initConfig(); + const result = app.warn('test'); + expect(result).toBe(app); + }); + + it('error returns this', () => { + const app = new spawnpoint({ cwd: __dirname }); + app.initConfig(); + const result = app.error('test'); + expect(result).toBe(app); + }); + + it('registerError returns this', () => { + const app = new spawnpoint({ cwd: __dirname }); + app.initConfig(); + app.initCodes(); + const result = app.registerError('test.code', Error); + expect(result).toBe(app); + }); + + it('registerErrors returns this', () => { + const app = new spawnpoint({ cwd: __dirname }); + app.initConfig(); + app.initCodes(); + const result = app.registerErrors({ 'test.code2': Error }); + expect(result).toBe(app); + }); + + it('registerLimit returns this', () => { + const app = new spawnpoint({ cwd: __dirname }); + app.initConfig(); + app.initCodes(); + const result = app.registerLimit('test.code', 1, () => {}); + expect(result).toBe(app); + }); +}); diff --git a/vitest.config.js b/vitest.config.js new file mode 100644 index 0000000..90fa2eb --- /dev/null +++ b/vitest.config.js @@ -0,0 +1,33 @@ +'use strict'; +const { defineConfig } = require('vitest/config'); + +module.exports = defineConfig({ + test: { + globals: false, + testTimeout: 10000, + hookTimeout: 10000, + pool: 'forks', + poolOptions: { + forks: { + singleFork: true, + }, + }, + fileParallelism: false, + include: ['test/**/*.mjs'], + exclude: [ + 'test/process-void/**', + 'test/autoload-*/**', + 'test/config/**', + 'test/store/**', + 'test/json/**', + 'test/spawnpoint-test/**', + 'test/spawnpoint-test-cb/**', + ], + coverage: { + provider: 'v8', + reporter: ['text', 'lcov'], + reportsDirectory: './coverage', + include: ['lib/**/*.js', 'index.js'], + }, + }, +});