From ffb778d716844d77342d3a176c6cd54a4078d8b2 Mon Sep 17 00:00:00 2001 From: thephez Date: Fri, 13 Mar 2026 16:12:22 -0400 Subject: [PATCH 1/9] test: add setup dash client test cases and related dependencies --- package-lock.json | 1327 ++++++++++++++++++++++++++++++--- package.json | 9 +- test/setupDashClient.test.mjs | 699 +++++++++++++++++ 3 files changed, 1935 insertions(+), 100 deletions(-) create mode 100644 test/setupDashClient.test.mjs diff --git a/package-lock.json b/package-lock.json index 3d596c6..3dc0643 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,8 +12,10 @@ "@dashevo/evo-sdk": "3.1.0-dev.1" }, "devDependencies": { - "dotenv": "^16.0.0", - "eslint": "^8.45.0" + "chai": "6.2.2", + "dotenv": "17.3.1", + "eslint": "^8.45.0", + "mocha": "^11.1.0" } }, "node_modules/@aashutoshrathi/word-wrap": { @@ -107,24 +109,6 @@ "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/@eslint/eslintrc/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 - }, - "node_modules/@eslint/eslintrc/node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, "node_modules/@eslint/eslintrc/node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", @@ -173,6 +157,109 @@ "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", "dev": true }, + "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.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", + "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.2.2" + }, + "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/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -208,6 +295,17 @@ "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==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=14" + } + }, "node_modules/acorn": { "version": "8.10.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz", @@ -253,6 +351,13 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "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, + "license": "Python-2.0" + }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -269,6 +374,13 @@ "concat-map": "0.0.1" } }, + "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, + "license": "ISC" + }, "node_modules/callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", @@ -278,6 +390,29 @@ "node": ">=6" } }, + "node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/chai": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/chai/-/chai-6.2.2.tgz", + "integrity": "sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==", + "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", @@ -294,6 +429,37 @@ "url": "https://github.com/chalk/chalk?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/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", @@ -319,10 +485,11 @@ "dev": true }, "node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "dev": true, + "license": "MIT", "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", @@ -333,12 +500,13 @@ } }, "node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", "dev": true, + "license": "MIT", "dependencies": { - "ms": "2.1.2" + "ms": "^2.1.3" }, "engines": { "node": ">=6.0" @@ -349,12 +517,35 @@ } } }, + "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, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "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/diff": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-7.0.0.tgz", + "integrity": "sha512-PJWHUb1RFevKCwaFA9RlG5tCd+FO5iRh9A8HEtkmBH2Li03iJriB6m6JIN4rGz3K3JLawI7/veA1xzRKP6ISBw==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, "node_modules/doctrine": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", @@ -368,15 +559,40 @@ } }, "node_modules/dotenv": { - "version": "16.3.1", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.3.1.tgz", - "integrity": "sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ==", + "version": "17.3.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.3.1.tgz", + "integrity": "sha512-IO8C/dzEb6O3F9/twg6ZLXz164a2fhTnEWb95H23Dm4OuN+92NmEAlTrupP9VW6Jm3sO26tQlqyvyi4CsnY9GA==", "dev": true, + "license": "BSD-2-Clause", "engines": { "node": ">=12" }, "funding": { - "url": "https://github.com/motdotla/dotenv?sponsor=1" + "url": "https://dotenvx.com" + } + }, + "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/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, + "license": "MIT" + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" } }, "node_modules/escape-string-regexp": { @@ -489,24 +705,6 @@ "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/eslint/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 - }, - "node_modules/eslint/node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, "node_modules/eslint/node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", @@ -627,6 +825,16 @@ "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, + "license": "BSD-3-Clause", + "bin": { + "flat": "cli.js" + } + }, "node_modules/flat-cache": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", @@ -646,12 +854,39 @@ "integrity": "sha512-WIWGi2L3DyTUvUrwRKgGi9TwxQMUEqPOPQBVi71R96jZXJdFskXEmf54BoZaS1kknGODoIGASGEzBUYdyMCBJg==", "dev": true }, + "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/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", "dev": true }, + "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, + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, "node_modules/glob": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", @@ -714,6 +949,16 @@ "node": ">=8" } }, + "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, + "license": "MIT", + "bin": { + "he": "bin/he" + } + }, "node_modules/ignore": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", @@ -773,6 +1018,16 @@ "node": ">=0.10.0" } }, + "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, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/is-glob": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", @@ -794,12 +1049,64 @@ "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, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "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, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", "dev": true }, + "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-yaml": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, "node_modules/json-stable-stringify-without-jsonify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", @@ -840,6 +1147,30 @@ "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "dev": true }, + "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, + "license": "MIT", + "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": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true, + "license": "ISC" + }, "node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", @@ -852,11 +1183,123 @@ "node": "*" } }, + "node_modules/minipass": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", + "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "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/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/mocha/node_modules/glob": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "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" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/mocha/node_modules/minimatch": { + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", + "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.2" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "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==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, "node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" }, "node_modules/natural-compare": { "version": "1.4.0", @@ -920,6 +1363,13 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "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", @@ -959,6 +1409,30 @@ "node": ">=8" } }, + "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/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -997,6 +1471,40 @@ } ] }, + "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/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/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, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/resolve-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", @@ -1054,6 +1562,37 @@ "queue-microtask": "^1.2.2" } }, + "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" + } + ], + "license": "MIT" + }, + "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/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -1075,11 +1614,69 @@ "node": ">=8" } }, - "node_modules/strip-ansi": { + "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/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-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/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" }, @@ -1165,12 +1762,111 @@ "node": ">= 8" } }, + "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": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", "dev": true }, + "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/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, + "license": "MIT", + "dependencies": { + "camelcase": "^6.0.0", + "decamelize": "^4.0.0", + "flat": "^5.0.2", + "is-plain-obj": "^2.1.0" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", @@ -1248,21 +1944,6 @@ "uri-js": "^4.2.2" } }, - "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 - }, - "js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, - "requires": { - "argparse": "^2.0.1" - } - }, "json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", @@ -1300,6 +1981,71 @@ "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", "dev": true }, + "@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, + "requires": { + "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" + }, + "dependencies": { + "ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "dev": true + }, + "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 + }, + "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 + }, + "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, + "requires": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + } + }, + "strip-ansi": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", + "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", + "dev": true, + "requires": { + "ansi-regex": "^6.2.2" + } + }, + "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, + "requires": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + } + } + } + }, "@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -1326,6 +2072,13 @@ "fastq": "^1.6.0" } }, + "@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "optional": true + }, "acorn": { "version": "8.10.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz", @@ -1354,6 +2107,12 @@ "color-convert": "^2.0.1" } }, + "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 + }, "balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -1370,12 +2129,30 @@ "concat-map": "0.0.1" } }, + "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 + }, "callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", "dev": true }, + "camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true + }, + "chai": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/chai/-/chai-6.2.2.tgz", + "integrity": "sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==", + "dev": true + }, "chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -1386,6 +2163,26 @@ "supports-color": "^7.1.0" } }, + "chokidar": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "dev": true, + "requires": { + "readdirp": "^4.0.1" + } + }, + "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, + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + } + }, "color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -1408,9 +2205,9 @@ "dev": true }, "cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "dev": true, "requires": { "path-key": "^3.1.0", @@ -1419,20 +2216,32 @@ } }, "debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", "dev": true, "requires": { - "ms": "2.1.2" + "ms": "^2.1.3" } }, + "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 + }, "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 }, + "diff": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-7.0.0.tgz", + "integrity": "sha512-PJWHUb1RFevKCwaFA9RlG5tCd+FO5iRh9A8HEtkmBH2Li03iJriB6m6JIN4rGz3K3JLawI7/veA1xzRKP6ISBw==", + "dev": true + }, "doctrine": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", @@ -1443,9 +2252,27 @@ } }, "dotenv": { - "version": "16.3.1", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.3.1.tgz", - "integrity": "sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ==", + "version": "17.3.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.3.1.tgz", + "integrity": "sha512-IO8C/dzEb6O3F9/twg6ZLXz164a2fhTnEWb95H23Dm4OuN+92NmEAlTrupP9VW6Jm3sO26tQlqyvyi4CsnY9GA==", + "dev": true + }, + "eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true + }, + "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 + }, + "escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", "dev": true }, "escape-string-regexp": { @@ -1511,21 +2338,6 @@ "uri-js": "^4.2.2" } }, - "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 - }, - "js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, - "requires": { - "argparse": "^2.0.1" - } - }, "json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", @@ -1637,6 +2449,12 @@ "path-exists": "^4.0.0" } }, + "flat": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", + "dev": true + }, "flat-cache": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", @@ -1653,12 +2471,28 @@ "integrity": "sha512-WIWGi2L3DyTUvUrwRKgGi9TwxQMUEqPOPQBVi71R96jZXJdFskXEmf54BoZaS1kknGODoIGASGEzBUYdyMCBJg==", "dev": true }, + "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, + "requires": { + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" + } + }, "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", "dev": true }, + "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 + }, "glob": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", @@ -1703,6 +2537,12 @@ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true }, + "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 + }, "ignore": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", @@ -1747,6 +2587,12 @@ "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", "dev": true }, + "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 + }, "is-glob": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", @@ -1762,12 +2608,43 @@ "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", "dev": true }, + "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 + }, + "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 + }, "isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", "dev": true }, + "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, + "requires": { + "@isaacs/cliui": "^8.0.2", + "@pkgjs/parseargs": "^0.11.0" + } + }, + "js-yaml": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", + "dev": true, + "requires": { + "argparse": "^2.0.1" + } + }, "json-stable-stringify-without-jsonify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", @@ -1799,6 +2676,22 @@ "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "dev": true }, + "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, + "requires": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + } + }, + "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 + }, "minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", @@ -1808,10 +2701,88 @@ "brace-expansion": "^1.1.7" } }, + "minipass": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", + "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", + "dev": true + }, + "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, + "requires": { + "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" + }, + "dependencies": { + "brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0" + } + }, + "glob": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", + "dev": true, + "requires": { + "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" + } + }, + "minimatch": { + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", + "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", + "dev": true, + "requires": { + "brace-expansion": "^2.0.2" + } + }, + "supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "dev": true }, "natural-compare": { @@ -1861,6 +2832,12 @@ "p-limit": "^3.0.2" } }, + "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 + }, "parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -1888,6 +2865,22 @@ "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", "dev": true }, + "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, + "requires": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + } + }, + "picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true + }, "prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -1906,6 +2899,27 @@ "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", "dev": true }, + "randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "requires": { + "safe-buffer": "^5.1.0" + } + }, + "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 + }, + "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 + }, "resolve-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", @@ -1936,6 +2950,21 @@ "queue-microtask": "^1.2.2" } }, + "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 + }, + "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, + "requires": { + "randombytes": "^2.1.0" + } + }, "shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -1951,6 +2980,34 @@ "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", "dev": true }, + "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 + }, + "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, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } + }, + "string-width-cjs": { + "version": "npm:string-width@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, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } + }, "strip-ansi": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", @@ -1960,6 +3017,15 @@ "ansi-regex": "^5.0.1" } }, + "strip-ansi-cjs": { + "version": "npm:strip-ansi@6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.1" + } + }, "strip-json-comments": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", @@ -2014,12 +3080,79 @@ "isexe": "^2.0.0" } }, + "workerpool": { + "version": "9.3.4", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-9.3.4.tgz", + "integrity": "sha512-TmPRQYYSAnnDiEB0P/Ytip7bFGvqnSU6I2BcuSw7Hx+JSg/DsUi5ebYfc8GYaSdpuvOcEs6dXxPurOYpe9QFwg==", + "dev": true + }, + "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, + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + } + }, + "wrap-ansi-cjs": { + "version": "npm:wrap-ansi@7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + } + }, "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", "dev": true }, + "y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true + }, + "yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "requires": { + "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" + } + }, + "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 + }, + "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, + "requires": { + "camelcase": "^6.0.0", + "decamelize": "^4.0.0", + "flat": "^5.0.2", + "is-plain-obj": "^2.1.0" + } + }, "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 be042d6..37abdd9 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,8 @@ "test": "node --test --test-timeout=120000 test/read-only.test.mjs", "test:read-only": "node --test --test-timeout=120000 test/read-only.test.mjs", "test:read-write": "node --test --test-timeout=300000 --test-concurrency=1 test/read-write.test.mjs", - "test:all": "node --test --test-timeout=300000 --test-concurrency=1 test/read-only.test.mjs test/read-write.test.mjs" + "test:all": "node --test --test-timeout=300000 --test-concurrency=1 test/read-only.test.mjs test/read-write.test.mjs && npm run test:setup", + "test:setup": "mocha --slow 500 test/setupDashClient.test.mjs --exit" }, "repository": { "type": "git", @@ -25,7 +26,9 @@ "@dashevo/evo-sdk": "3.1.0-dev.1" }, "devDependencies": { - "dotenv": "^16.0.0", - "eslint": "^8.45.0" + "chai": "6.2.2", + "dotenv": "17.3.1", + "eslint": "^8.45.0", + "mocha": "^11.1.0" } } diff --git a/test/setupDashClient.test.mjs b/test/setupDashClient.test.mjs new file mode 100644 index 0000000..c465e85 --- /dev/null +++ b/test/setupDashClient.test.mjs @@ -0,0 +1,699 @@ +import { expect } from 'chai'; +import dotenv from 'dotenv'; +import { + Identity, + IdentityPublicKeyInCreation, + IdentitySigner, + PlatformAddressSigner, + PrivateKey, + wallet, +} from '@dashevo/evo-sdk'; +import { + IdentityKeyManager, + AddressKeyManager, + createClient, + setupDashClient, + clientConfig, +} from '../setupDashClient.mjs'; + +dotenv.config(); +const network = process.env.NETWORK || 'testnet'; + +// Testnet identity used by the tutorials (known to exist on-chain) +const IDENTITY_ID = 'GgZekwh38XcWQTyWWWvmw6CEYFnLU7yiZFPWZEjqKHit'; + +const TEST_MNEMONIC = + 'abandon abandon abandon abandon abandon abandon ' + + 'abandon abandon abandon abandon abandon about'; + +/** + * Derive identity authentication keys from a BIP39 mnemonic using DIP-9 paths. + * Inlined here since setupDashClient.mjs doesn't export this as a standalone function. + */ +async function deriveKeysFromMnemonic( + mnemonic, + net = 'testnet', + identityIndex = 0, + keyCount = 5, +) { + const coin = net === 'testnet' ? 1 : 5; + const keys = []; + + for (let keyIndex = 0; keyIndex < keyCount; keyIndex++) { + const path = `m/9'/${coin}'/5'/0'/0'/${identityIndex}'/${keyIndex}'`; + const keyInfo = await wallet.deriveKeyFromSeedWithPath({ + mnemonic, + path, + network: net, + }); + keys.push({ keyIndex, path, ...keyInfo.toObject() }); + } + + return keys; +} + +describe('IdentityKeyManager', function suite() { + this.timeout(30000); + + let sdk; + + before(async function () { + sdk = await createClient(network); + }); + + describe('create()', function () { + it('should derive deterministic keys from mnemonic', async function () { + const km = await IdentityKeyManager.create({ + sdk, + identityId: IDENTITY_ID, + mnemonic: TEST_MNEMONIC, + network: 'testnet', + identityIndex: 0, + }); + + expect(km.identityId).to.equal(IDENTITY_ID); + expect(km.keys.master).to.have.property('keyId', 0); + expect(km.keys.master) + .to.have.property('privateKeyWif') + .that.is.a('string'); + expect(km.keys.authHigh).to.have.property('keyId', 1); + expect(km.keys.authHigh) + .to.have.property('privateKeyWif') + .that.is.a('string'); + expect(km.keys.auth).to.have.property('keyId', 2); + expect(km.keys.auth) + .to.have.property('privateKeyWif') + .that.is.a('string'); + expect(km.keys.transfer).to.have.property('keyId', 3); + expect(km.keys.transfer) + .to.have.property('privateKeyWif') + .that.is.a('string'); + expect(km.keys.encryption).to.have.property('keyId', 4); + expect(km.keys.encryption) + .to.have.property('privateKeyWif') + .that.is.a('string'); + }); + + it('should produce same keys as deriveKeysFromMnemonic', async function () { + const km = await IdentityKeyManager.create({ + sdk, + identityId: IDENTITY_ID, + mnemonic: TEST_MNEMONIC, + network: 'testnet', + identityIndex: 0, + }); + const rawKeys = await deriveKeysFromMnemonic( + TEST_MNEMONIC, + 'testnet', + 0, + 5, + ); + + expect(km.keys.master.privateKeyWif).to.equal(rawKeys[0].privateKeyWif); + expect(km.keys.authHigh.privateKeyWif).to.equal(rawKeys[1].privateKeyWif); + expect(km.keys.auth.privateKeyWif).to.equal(rawKeys[2].privateKeyWif); + expect(km.keys.transfer.privateKeyWif).to.equal(rawKeys[3].privateKeyWif); + expect(km.keys.encryption.privateKeyWif).to.equal( + rawKeys[4].privateKeyWif, + ); + }); + + it('should auto-resolve identityId from mnemonic when not provided', async function () { + if (!process.env.PLATFORM_MNEMONIC) { + this.skip('PLATFORM_MNEMONIC not set'); + } + const km = await IdentityKeyManager.create({ + sdk, + mnemonic: process.env.PLATFORM_MNEMONIC, + network, + }); + expect(km.identityId).to.be.a('string').with.length.greaterThan(0); + this.test.title += ` (${km.identityId})`; + + const { identity, identityKey, signer } = await km.getAuth(); + expect(identity).to.be.an.instanceOf(Identity); + expect(identityKey).to.be.an('object'); + expect(signer).to.be.an.instanceOf(IdentitySigner); + }); + + it('should match explicit identityId when auto-resolved', async function () { + if (!process.env.PLATFORM_MNEMONIC) { + this.skip('PLATFORM_MNEMONIC not set'); + } + const auto = await IdentityKeyManager.create({ + sdk, + mnemonic: process.env.PLATFORM_MNEMONIC, + network, + }); + const explicit = await IdentityKeyManager.create({ + sdk, + identityId: auto.identityId, + mnemonic: process.env.PLATFORM_MNEMONIC, + network, + }); + expect(auto.identityId).to.equal(explicit.identityId); + expect(auto.keys.master.privateKeyWif).to.equal( + explicit.keys.master.privateKeyWif, + ); + expect(auto.keys.auth.privateKeyWif).to.equal( + explicit.keys.auth.privateKeyWif, + ); + }); + + it('should be deterministic (same inputs = same keys)', async function () { + const km1 = await IdentityKeyManager.create({ + sdk, + identityId: IDENTITY_ID, + mnemonic: TEST_MNEMONIC, + }); + const km2 = await IdentityKeyManager.create({ + sdk, + identityId: IDENTITY_ID, + mnemonic: TEST_MNEMONIC, + }); + + expect(km1.keys.auth.privateKeyWif).to.equal(km2.keys.auth.privateKeyWif); + expect(km1.keys.master.privateKeyWif).to.equal( + km2.keys.master.privateKeyWif, + ); + expect(km1.keys.authHigh.privateKeyWif).to.equal( + km2.keys.authHigh.privateKeyWif, + ); + expect(km1.keys.transfer.privateKeyWif).to.equal( + km2.keys.transfer.privateKeyWif, + ); + expect(km1.keys.encryption.privateKeyWif).to.equal( + km2.keys.encryption.privateKeyWif, + ); + }); + }); + + describe('getAuth()', function () { + it('should return identity, identityKey, and signer', async function () { + const km = await IdentityKeyManager.create({ + sdk, + identityId: IDENTITY_ID, + mnemonic: TEST_MNEMONIC, + }); + const result = await km.getAuth(); + + expect(result) + .to.have.property('identity') + .that.is.an.instanceOf(Identity); + expect(result).to.have.property('identityKey').that.is.an('object'); + expect(result) + .to.have.property('signer') + .that.is.an.instanceOf(IdentitySigner); + }); + }); + + describe('getAuthHigh()', function () { + it('should return identity, identityKey, and signer', async function () { + const km = await IdentityKeyManager.create({ + sdk, + identityId: IDENTITY_ID, + mnemonic: TEST_MNEMONIC, + }); + const result = await km.getAuthHigh(); + + expect(result) + .to.have.property('identity') + .that.is.an.instanceOf(Identity); + expect(result).to.have.property('identityKey').that.is.an('object'); + expect(result) + .to.have.property('signer') + .that.is.an.instanceOf(IdentitySigner); + }); + }); + + describe('getTransfer()', function () { + it('should return identity, identityKey, and signer', async function () { + const km = await IdentityKeyManager.create({ + sdk, + identityId: IDENTITY_ID, + mnemonic: TEST_MNEMONIC, + }); + const result = await km.getTransfer(); + + expect(result) + .to.have.property('identity') + .that.is.an.instanceOf(Identity); + expect(result).to.have.property('identityKey').that.is.an('object'); + expect(result) + .to.have.property('signer') + .that.is.an.instanceOf(IdentitySigner); + }); + }); + + describe('getEncryption()', function () { + it('should return identity, identityKey, and signer', async function () { + const km = await IdentityKeyManager.create({ + sdk, + identityId: IDENTITY_ID, + mnemonic: TEST_MNEMONIC, + }); + const result = await km.getEncryption(); + + expect(result) + .to.have.property('identity') + .that.is.an.instanceOf(Identity); + expect(result).to.have.property('identityKey').that.is.an('object'); + expect(result) + .to.have.property('signer') + .that.is.an.instanceOf(IdentitySigner); + }); + }); + + describe('getMaster()', function () { + it('should return identity, identityKey, and signer', async function () { + const km = await IdentityKeyManager.create({ + sdk, + identityId: IDENTITY_ID, + mnemonic: TEST_MNEMONIC, + }); + const result = await km.getMaster(); + + expect(result) + .to.have.property('identity') + .that.is.an.instanceOf(Identity); + expect(result).to.have.property('identityKey').that.is.an('object'); + expect(result) + .to.have.property('signer') + .that.is.an.instanceOf(IdentitySigner); + }); + + it('should add additional key WIFs to the signer', async function () { + const km = await IdentityKeyManager.create({ + sdk, + identityId: IDENTITY_ID, + mnemonic: TEST_MNEMONIC, + }); + // Derive an extra key to use as the additional WIF + const extraKeys = await deriveKeysFromMnemonic( + TEST_MNEMONIC, + 'testnet', + 1, + 1, + ); + const extraWif = extraKeys[0].privateKeyWif; + + const result = await km.getMaster([extraWif]); + expect(result.signer).to.be.an.instanceOf(IdentitySigner); + // Signer should accept the extra WIF without error — verify by + // checking it's still a valid signer (no throw on construction) + expect(result) + .to.have.property('identity') + .that.is.an.instanceOf(Identity); + }); + }); + + describe('identityIndex', function () { + it('should store the provided identityIndex', async function () { + const km = await IdentityKeyManager.create({ + sdk, + identityId: IDENTITY_ID, + mnemonic: TEST_MNEMONIC, + identityIndex: 3, + }); + expect(km.identityIndex).to.equal(3); + }); + + it('should default identityIndex to 0', async function () { + const km = await IdentityKeyManager.create({ + sdk, + identityId: IDENTITY_ID, + mnemonic: TEST_MNEMONIC, + }); + expect(km.identityIndex).to.equal(0); + }); + }); + + describe('getFullSigner()', function () { + it('should return an IdentitySigner with all keys', async function () { + const km = await IdentityKeyManager.create({ + sdk, + identityId: IDENTITY_ID, + mnemonic: TEST_MNEMONIC, + }); + const signer = km.getFullSigner(); + expect(signer).to.be.an.instanceOf(IdentitySigner); + }); + }); + + describe('getKeysInCreation()', function () { + it('should throw when public keys are not available', async function () { + const km = await IdentityKeyManager.create({ + sdk, + identityId: IDENTITY_ID, + mnemonic: TEST_MNEMONIC, + }); + expect(() => km.getKeysInCreation()).to.throw( + 'Public key data not available', + ); + }); + + it('should return 5 IdentityPublicKeyInCreation when public keys present', async function () { + // Construct a manager with publicKey fields (as createForNewIdentity does) + const base = await IdentityKeyManager.create({ + sdk, + identityId: IDENTITY_ID, + mnemonic: TEST_MNEMONIC, + }); + const withPub = (entry) => { + const pk = PrivateKey.fromWIF(entry.privateKeyWif); + const publicKey = Buffer.from(pk.getPublicKey().toBytes()).toString( + 'hex', + ); + return { ...entry, publicKey }; + }; + const km = new IdentityKeyManager( + sdk, + null, + { + master: withPub(base.keys.master), + authHigh: withPub(base.keys.authHigh), + auth: withPub(base.keys.auth), + transfer: withPub(base.keys.transfer), + encryption: withPub(base.keys.encryption), + }, + 0, + ); + + const keys = km.getKeysInCreation(); + expect(keys).to.be.an('array').with.length(5); + keys.forEach((k) => { + expect(k).to.be.an.instanceOf(IdentityPublicKeyInCreation); + }); + }); + }); + + describe('getSigner() guard', function () { + it('should throw when identity ID is not set', async function () { + const km = new IdentityKeyManager( + sdk, + null, + { + auth: { keyId: 2, privateKeyWif: 'placeholder' }, + }, + 0, + ); + try { + await km.getAuth(); + expect.fail('should have thrown'); + } catch (err) { + expect(err.message).to.include('Identity ID is not set'); + } + }); + + it('should throw for invalid key name', async function () { + const km = await IdentityKeyManager.create({ + sdk, + identityId: IDENTITY_ID, + mnemonic: TEST_MNEMONIC, + }); + try { + await km.getSigner('bogus'); + expect.fail('should have thrown'); + } catch (err) { + expect(err.message).to.include('Unknown key "bogus"'); + } + }); + }); + + describe('create() error paths', function () { + it('should throw when mnemonic has no on-chain identity', async function () { + try { + await IdentityKeyManager.create({ + sdk, + mnemonic: TEST_MNEMONIC, + // no identityId — forces auto-resolve, which will fail + }); + expect.fail('should have thrown'); + } catch (err) { + expect(err.message).to.include('No identity found'); + } + }); + + it('should produce different keys for different identityIndex', async function () { + const km0 = await IdentityKeyManager.create({ + sdk, + identityId: IDENTITY_ID, + mnemonic: TEST_MNEMONIC, + identityIndex: 0, + }); + const km1 = await IdentityKeyManager.create({ + sdk, + identityId: IDENTITY_ID, + mnemonic: TEST_MNEMONIC, + identityIndex: 1, + }); + expect(km0.keys.auth.privateKeyWif).to.not.equal( + km1.keys.auth.privateKeyWif, + ); + }); + }); + + describe('createForNewIdentity()', function () { + it('should return manager with null id and publicKey fields', async function () { + const km = await IdentityKeyManager.createForNewIdentity({ + sdk, + mnemonic: TEST_MNEMONIC, + identityIndex: 99, // high index to avoid collision + }); + expect(km.identityId).to.be.null; + expect(km.identityIndex).to.equal(99); + // All keys should have publicKey fields + Object.values(km.keys).forEach((key) => { + expect(key).to.have.property('publicKey').that.is.a('string'); + expect(key).to.have.property('privateKeyWif').that.is.a('string'); + }); + }); + }); + + describe('findNextIndex()', function () { + it('should return 0 for mnemonic with no on-chain identity', async function () { + const idx = await IdentityKeyManager.findNextIndex(sdk, TEST_MNEMONIC); + expect(idx).to.equal(0); + }); + }); +}); + +describe('AddressKeyManager', function suite() { + this.timeout(30000); + + let sdk; + let akm; + + before(async function () { + sdk = await createClient(network); + akm = await AddressKeyManager.create({ + sdk, + mnemonic: TEST_MNEMONIC, + network: 'testnet', + count: 2, + }); + }); + + describe('create()', function () { + it('should derive addresses from mnemonic', function () { + expect(akm.network).to.equal('testnet'); + expect(akm.addresses).to.have.length(2); + akm.addresses.forEach((addr) => { + expect(addr).to.have.property('address'); + expect(addr).to.have.property('bech32m').that.is.a('string'); + expect(addr.bech32m).to.match(/^tdash1/); + expect(addr).to.have.property('privateKeyWif').that.is.a('string'); + expect(addr).to.have.property('path').that.is.a('string'); + }); + }); + + it('should be deterministic (same inputs = same addresses)', async function () { + const akm2 = await AddressKeyManager.create({ + sdk, + mnemonic: TEST_MNEMONIC, + network: 'testnet', + count: 2, + }); + expect(akm.addresses[0].bech32m).to.equal(akm2.addresses[0].bech32m); + expect(akm.addresses[1].bech32m).to.equal(akm2.addresses[1].bech32m); + }); + + it('should default to count=1', async function () { + const single = await AddressKeyManager.create({ + sdk, + mnemonic: TEST_MNEMONIC, + network: 'testnet', + }); + expect(single.addresses).to.have.length(1); + }); + }); + + describe('primaryAddress', function () { + it('should return the first derived address', function () { + expect(akm.primaryAddress).to.equal(akm.addresses[0]); + expect(akm.primaryAddress) + .to.have.property('bech32m') + .that.is.a('string'); + }); + }); + + describe('getSigner()', function () { + it('should return a PlatformAddressSigner', function () { + const signer = akm.getSigner(); + expect(signer).to.be.an.instanceOf(PlatformAddressSigner); + }); + }); + + describe('getFullSigner()', function () { + it('should return a PlatformAddressSigner with all keys', function () { + const signer = akm.getFullSigner(); + expect(signer).to.be.an.instanceOf(PlatformAddressSigner); + }); + }); + + describe('getInfo()', function () { + it('should fetch primary address info', async function () { + if (!process.env.PLATFORM_MNEMONIC) { + this.skip('PLATFORM_MNEMONIC not set (address may not be funded)'); + } + const funded = await AddressKeyManager.create({ + sdk, + mnemonic: process.env.PLATFORM_MNEMONIC, + network, + }); + const info = await funded.getInfo(); + // Address may or may not be funded — just verify no crash + // If funded, info is an object; if not, undefined + if (info) { + expect(info).to.be.an('object'); + } + }); + }); + + describe('getInfoAt()', function () { + it('should throw for out-of-range index', async function () { + const empty = new AddressKeyManager(null, [], 'testnet'); + try { + await empty.getInfoAt(0); + expect.fail('should have thrown'); + } catch (err) { + expect(err.message).to.include('No derived address at index 0'); + } + }); + }); +}); + +describe('createClient()', function () { + this.timeout(30000); + + it('should throw for unknown network', async function () { + try { + await createClient('bogus'); + expect.fail('should have thrown'); + } catch (err) { + expect(err.message).to.include('Unknown network "bogus"'); + } + }); +}); + +describe('setupDashClient()', function () { + this.timeout(30000); + + it('should return sdk, keyManager, and addressKeyManager', async function () { + if (!process.env.PLATFORM_MNEMONIC) { + this.skip('PLATFORM_MNEMONIC not set'); + } + const result = await setupDashClient(); + expect(result).to.have.property('sdk'); + expect(result) + .to.have.property('keyManager') + .that.is.an.instanceOf(IdentityKeyManager); + expect(result) + .to.have.property('addressKeyManager') + .that.is.an.instanceOf(AddressKeyManager); + expect(result.keyManager.identityId) + .to.be.a('string') + .with.length.greaterThan(0); + expect(result.addressKeyManager.network).to.equal(clientConfig.network); + }); + + it('should reject an invalid mnemonic before attempting key derivation', async function () { + const saved = clientConfig.mnemonic; + try { + clientConfig.mnemonic = 'not a valid mnemonic phrase at all'; + try { + await setupDashClient(); + expect.fail('should have thrown'); + } catch (err) { + // Without early validation, an invalid mnemonic reaches the WASM SDK + // which throws a raw WasmSdkError (no .message, not an Error instance). + // The fix catches it up front with a standard Error and helpful message. + expect(err).to.be.an.instanceOf(Error); + expect(err.message).to.be.a('string'); + expect(err.message).to.include('mnemonic'); + } + } finally { + clientConfig.mnemonic = saved; + } + }); + + it('should return undefined managers when no mnemonic configured', async function () { + const saved = clientConfig.mnemonic; + try { + clientConfig.mnemonic = null; + const result = await setupDashClient(); + expect(result).to.have.property('sdk'); + expect(result.keyManager).to.be.undefined; + expect(result.addressKeyManager).to.be.undefined; + } finally { + clientConfig.mnemonic = saved; + } + }); + + it('should not throw for a fresh mnemonic with requireIdentity: false', async function () { + const saved = clientConfig.mnemonic; + try { + clientConfig.mnemonic = TEST_MNEMONIC; + const result = await setupDashClient({ requireIdentity: false }); + expect(result).to.have.property('sdk'); + expect(result) + .to.have.property('keyManager') + .that.is.an.instanceOf(IdentityKeyManager); + expect(result.keyManager.identityId).to.be.null; + expect(result) + .to.have.property('addressKeyManager') + .that.is.an.instanceOf(AddressKeyManager); + } finally { + clientConfig.mnemonic = saved; + } + }); + + it('requireIdentity: false should auto-scan to an unused identity index', async function () { + if (!process.env.PLATFORM_MNEMONIC) { + this.skip('PLATFORM_MNEMONIC not set'); + } + const { keyManager } = await setupDashClient({ requireIdentity: false }); + // Must pick an index beyond all registered identities (not 0) + expect(keyManager.identityIndex).to.be.a('number').greaterThan(0); + expect(keyManager.identityId).to.be.null; + this.test.title += ` (index ${keyManager.identityIndex})`; + }); +}); + +describe('IdentityKeyManager.createForNewIdentity() auto-index', function () { + this.timeout(30000); + + it('should auto-select index 0 for unfunded mnemonic', async function () { + const sdk = await createClient(network); + const km = await IdentityKeyManager.createForNewIdentity({ + sdk, + mnemonic: TEST_MNEMONIC, + // no identityIndex — triggers findNextIndex + }); + expect(km.identityIndex).to.equal(0); + expect(km.identityId).to.be.null; + Object.values(km.keys).forEach((key) => { + expect(key).to.have.property('publicKey').that.is.a('string'); + }); + }); +}); From 6c767c2cfbb43cec73b39f1a560d36fd0b490ec9 Mon Sep 17 00:00:00 2001 From: thephez Date: Sat, 14 Mar 2026 11:24:42 -0400 Subject: [PATCH 2/9] test: use dip13KeyPath() in test helper and add setupDashClient coverage Replace hardcoded DIP-13 path construction in deriveKeysFromMnemonic with the exported dip13KeyPath() helper, ensuring a single source of truth for path derivation. Add test cases for missing setupDashClient() permutations including undefined manager guards, requireIdentity flag, and identityIndex forwarding. Co-Authored-By: Claude Opus 4.6 --- test/setupDashClient.test.mjs | 109 +++++++++++++++++++++++++++++++++- 1 file changed, 107 insertions(+), 2 deletions(-) diff --git a/test/setupDashClient.test.mjs b/test/setupDashClient.test.mjs index c465e85..726d489 100644 --- a/test/setupDashClient.test.mjs +++ b/test/setupDashClient.test.mjs @@ -14,6 +14,7 @@ import { createClient, setupDashClient, clientConfig, + dip13KeyPath, } from '../setupDashClient.mjs'; dotenv.config(); @@ -36,11 +37,10 @@ async function deriveKeysFromMnemonic( identityIndex = 0, keyCount = 5, ) { - const coin = net === 'testnet' ? 1 : 5; const keys = []; for (let keyIndex = 0; keyIndex < keyCount; keyIndex++) { - const path = `m/9'/${coin}'/5'/0'/0'/${identityIndex}'/${keyIndex}'`; + const path = await dip13KeyPath(net, identityIndex, keyIndex); const keyInfo = await wallet.deriveKeyFromSeedWithPath({ mnemonic, path, @@ -650,6 +650,111 @@ describe('setupDashClient()', function () { } }); + it('should not produce a TypeError when write tutorials use keyManager without a mnemonic', async function () { + // Write tutorials do: const { keyManager } = await setupDashClient(); + // const { signer } = await keyManager.getAuth(); + // Without a mnemonic, keyManager is undefined → TypeError on .getAuth(). + // This test verifies the failure is a clear Error, not a raw TypeError. + const saved = clientConfig.mnemonic; + try { + clientConfig.mnemonic = null; + const { keyManager, addressKeyManager } = await setupDashClient(); + + try { + await keyManager.getAuth(); + expect.fail('should have thrown'); + } catch (err) { + expect(err).to.be.an.instanceOf(Error); + expect(err.message).to.not.include('Cannot read properties of undefined'); + } + + try { + addressKeyManager.getSigner(); + expect.fail('should have thrown'); + } catch (err) { + expect(err).to.be.an.instanceOf(Error); + expect(err.message).to.not.include('Cannot read properties of undefined'); + } + } finally { + clientConfig.mnemonic = saved; + } + }); + + it('should return undefined managers with requireIdentity: false and no mnemonic', async function () { + // #2: requireIdentity: false still skips key derivation when no mnemonic is set + const saved = clientConfig.mnemonic; + try { + clientConfig.mnemonic = null; + const result = await setupDashClient({ requireIdentity: false }); + expect(result).to.have.property('sdk'); + expect(result.keyManager).to.be.undefined; + expect(result.addressKeyManager).to.be.undefined; + } finally { + clientConfig.mnemonic = saved; + } + }); + + it('should throw when mnemonic has no registered identity and requireIdentity is true', async function () { + // #7: Valid mnemonic but no identity registered on-chain — the default + // requireIdentity: true path tries to auto-resolve and fails. + const saved = clientConfig.mnemonic; + try { + clientConfig.mnemonic = TEST_MNEMONIC; + try { + await setupDashClient(); // requireIdentity defaults to true + expect.fail('should have thrown'); + } catch (err) { + expect(err).to.be.an.instanceOf(Error); + expect(err.message).to.include('No identity found'); + } + } finally { + clientConfig.mnemonic = saved; + } + }); + + it('should pass identityIndex through to IdentityKeyManager.create()', async function () { + // #6: requireIdentity: true with explicit identityIndex — verifies the + // parameter is forwarded (different index = different derived keys). + const saved = clientConfig.mnemonic; + try { + clientConfig.mnemonic = TEST_MNEMONIC; + // Both will fail with "No identity found" since TEST_MNEMONIC has no + // on-chain identity, but they fail at different derivation paths, + // proving identityIndex is forwarded. We just need it not to crash + // before reaching the identity lookup. + // Use requireIdentity: false to avoid the lookup and verify the index is stored. + const r0 = await setupDashClient({ requireIdentity: false, identityIndex: 0 }); + const r1 = await setupDashClient({ requireIdentity: false, identityIndex: 1 }); + expect(r0.keyManager.identityIndex).to.equal(0); + expect(r1.keyManager.identityIndex).to.equal(1); + // Different indices must produce different keys + expect(r0.keyManager.keys.master.privateKeyWif).to.not.equal( + r1.keyManager.keys.master.privateKeyWif, + ); + } finally { + clientConfig.mnemonic = saved; + } + }); + + it('should pass explicit identityIndex through with requireIdentity: false', async function () { + // #10: createForNewIdentity with explicit identityIndex — skips findNextIndex scan + const saved = clientConfig.mnemonic; + try { + clientConfig.mnemonic = TEST_MNEMONIC; + const result = await setupDashClient({ requireIdentity: false, identityIndex: 42 }); + expect(result.keyManager).to.be.an.instanceOf(IdentityKeyManager); + expect(result.keyManager.identityIndex).to.equal(42); + expect(result.keyManager.identityId).to.be.null; + // All keys should have publicKey fields (createForNewIdentity path) + Object.values(result.keyManager.keys).forEach((key) => { + expect(key).to.have.property('publicKey').that.is.a('string'); + expect(key).to.have.property('privateKeyWif').that.is.a('string'); + }); + } finally { + clientConfig.mnemonic = saved; + } + }); + it('should not throw for a fresh mnemonic with requireIdentity: false', async function () { const saved = clientConfig.mnemonic; try { From b6ca0af3e36fee281ef259d94ffb717714d0c65a Mon Sep 17 00:00:00 2001 From: thephez Date: Sat, 14 Mar 2026 11:26:33 -0400 Subject: [PATCH 3/9] fix: validate mnemonic in setupDashClient before key derivation Catch invalid BIP39 mnemonics early with a clear error message instead of letting them propagate to the WASM SDK as opaque WasmSdkError objects. Co-Authored-By: Claude Opus 4.6 --- setupDashClient.mjs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/setupDashClient.mjs b/setupDashClient.mjs index a23ee61..be07600 100644 --- a/setupDashClient.mjs +++ b/setupDashClient.mjs @@ -519,6 +519,14 @@ export async function setupDashClient({ identityIndex, } = {}) { const { network, mnemonic } = clientConfig; + + if (mnemonic && !(await wallet.validateMnemonic(mnemonic))) { + throw new Error( + 'PLATFORM_MNEMONIC is not a valid BIP39 mnemonic. ' + + 'Run `node create-wallet.mjs` to generate one.', + ); + } + const sdk = await createClient(network); let keyManager; From 2943bbd68fafccc26dda7a734585afb7cd359ad4 Mon Sep 17 00:00:00 2001 From: thephez Date: Sat, 14 Mar 2026 11:47:59 -0400 Subject: [PATCH 4/9] test: add dip13KeyPath unit tests for path derivation Co-Authored-By: Claude Opus 4.6 --- test/setupDashClient.test.mjs | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/test/setupDashClient.test.mjs b/test/setupDashClient.test.mjs index 726d489..c248f7d 100644 --- a/test/setupDashClient.test.mjs +++ b/test/setupDashClient.test.mjs @@ -802,3 +802,27 @@ describe('IdentityKeyManager.createForNewIdentity() auto-index', function () { }); }); }); + +describe('dip13KeyPath()', function () { + it('should build a valid DIP-13 testnet path', async function () { + const path = await dip13KeyPath('testnet', 0, 0); + // m/9'/{coin}'/5'/0'/0'/{identityIndex}'/{keyIndex}' + expect(path).to.equal("m/9'/1'/5'/0'/0'/0'/0'"); + }); + + it('should vary by identityIndex and keyIndex', async function () { + const p00 = await dip13KeyPath('testnet', 0, 0); + const p01 = await dip13KeyPath('testnet', 0, 1); + const p10 = await dip13KeyPath('testnet', 1, 0); + expect(p00).to.not.equal(p01); + expect(p00).to.not.equal(p10); + expect(p01).to.equal("m/9'/1'/5'/0'/0'/0'/1'"); + expect(p10).to.equal("m/9'/1'/5'/0'/0'/1'/0'"); + }); + + it('should use coin type 5 for mainnet', async function () { + const path = await dip13KeyPath('mainnet', 0, 0); + expect(path).to.equal("m/9'/5'/5'/0'/0'/0'/0'"); + }); +}); + From 18dbcf4a643d4fec59ec0e1ce978668b7fb3eaa0 Mon Sep 17 00:00:00 2001 From: thephez Date: Sat, 14 Mar 2026 12:33:31 -0400 Subject: [PATCH 5/9] test: deepen assertions and add stubbed tests for untested paths Verify key-role correctness (keyId per getSigner method), signer depth (keyCount for getMaster/getFullSigner), createClient success branches (testnet/mainnet isConnected), findNextIndex loop logic with stubbed occupied indices, createForNewIdentity nonzero auto-index, and AddressKeyManager getInfo/getInfoAt with stubbed SDK. Fix stale comment and remove weak clientConfig direct-usage tests. Co-Authored-By: Claude Opus 4.6 --- test/setupDashClient.test.mjs | 107 +++++++++++++++++++++++++++++++--- 1 file changed, 99 insertions(+), 8 deletions(-) diff --git a/test/setupDashClient.test.mjs b/test/setupDashClient.test.mjs index c248f7d..ac0be42 100644 --- a/test/setupDashClient.test.mjs +++ b/test/setupDashClient.test.mjs @@ -52,6 +52,28 @@ async function deriveKeysFromMnemonic( return keys; } +/** + * Build a fake SDK whose identities.byPublicKeyHash() returns truthy for + * the given occupied identity indices and null for everything else. + * Used by findNextIndex() and createForNewIdentity() stubbed tests. + */ +async function fakeSdkWithOccupiedIndices(occupiedIndices) { + const occupiedHashes = new Set(); + for (const i of occupiedIndices) { + const keys = await deriveKeysFromMnemonic(TEST_MNEMONIC, 'testnet', i, 1); + const pk = PrivateKey.fromWIF(keys[0].privateKeyWif); + occupiedHashes.add(Buffer.from(pk.getPublicKeyHash()).toString('hex')); + } + return { + identities: { + byPublicKeyHash: async (hash) => { + const hex = Buffer.from(hash).toString('hex'); + return occupiedHashes.has(hex) ? { id: 'fake' } : null; + }, + }, + }; +} + describe('IdentityKeyManager', function suite() { this.timeout(30000); @@ -204,6 +226,7 @@ describe('IdentityKeyManager', function suite() { expect(result) .to.have.property('signer') .that.is.an.instanceOf(IdentitySigner); + expect(result.identityKey.keyId).to.equal(2); }); }); @@ -223,6 +246,7 @@ describe('IdentityKeyManager', function suite() { expect(result) .to.have.property('signer') .that.is.an.instanceOf(IdentitySigner); + expect(result.identityKey.keyId).to.equal(1); }); }); @@ -242,6 +266,7 @@ describe('IdentityKeyManager', function suite() { expect(result) .to.have.property('signer') .that.is.an.instanceOf(IdentitySigner); + expect(result.identityKey.keyId).to.equal(3); }); }); @@ -261,6 +286,7 @@ describe('IdentityKeyManager', function suite() { expect(result) .to.have.property('signer') .that.is.an.instanceOf(IdentitySigner); + expect(result.identityKey.keyId).to.equal(4); }); }); @@ -280,6 +306,8 @@ describe('IdentityKeyManager', function suite() { expect(result) .to.have.property('signer') .that.is.an.instanceOf(IdentitySigner); + expect(result.signer.keyCount).to.equal(1); + expect(result.identityKey.keyId).to.equal(0); }); it('should add additional key WIFs to the signer', async function () { @@ -299,11 +327,8 @@ describe('IdentityKeyManager', function suite() { const result = await km.getMaster([extraWif]); expect(result.signer).to.be.an.instanceOf(IdentitySigner); - // Signer should accept the extra WIF without error — verify by - // checking it's still a valid signer (no throw on construction) - expect(result) - .to.have.property('identity') - .that.is.an.instanceOf(Identity); + expect(result.signer.keyCount).to.equal(2); + expect(result.identityKey.keyId).to.equal(0); }); }); @@ -337,6 +362,7 @@ describe('IdentityKeyManager', function suite() { }); const signer = km.getFullSigner(); expect(signer).to.be.an.instanceOf(IdentitySigner); + expect(signer.keyCount).to.equal(5); }); }); @@ -468,6 +494,19 @@ describe('IdentityKeyManager', function suite() { expect(key).to.have.property('privateKeyWif').that.is.a('string'); }); }); + + it('should auto-scan to a nonzero index when earlier indices are occupied', async function () { + const fakeSdk = await fakeSdkWithOccupiedIndices([0, 1]); + const km = await IdentityKeyManager.createForNewIdentity({ + sdk: fakeSdk, + mnemonic: TEST_MNEMONIC, + }); + expect(km.identityIndex).to.equal(2); + expect(km.identityId).to.be.null; + Object.values(km.keys).forEach((key) => { + expect(key).to.have.property('publicKey').that.is.a('string'); + }); + }); }); describe('findNextIndex()', function () { @@ -475,6 +514,12 @@ describe('IdentityKeyManager', function suite() { const idx = await IdentityKeyManager.findNextIndex(sdk, TEST_MNEMONIC); expect(idx).to.equal(0); }); + + it('should skip occupied indices and return first unused', async function () { + const fakeSdk = await fakeSdkWithOccupiedIndices([0, 1]); + const idx = await IdentityKeyManager.findNextIndex(fakeSdk, TEST_MNEMONIC); + expect(idx).to.equal(2); + }); }); }); @@ -568,6 +613,24 @@ describe('AddressKeyManager', function suite() { expect(info).to.be.an('object'); } }); + + it('should query the primary address bech32m', async function () { + const fakeInfo = { balance: 500, nonce: 0 }; + let queriedAddress; + const fakeSdk = { + addresses: { + get: async (addr) => { queriedAddress = addr; return fakeInfo; }, + }, + }; + const mgr = new AddressKeyManager( + fakeSdk, + [{ bech32m: 'tdash1primary' }], + 'testnet', + ); + const info = await mgr.getInfo(); + expect(queriedAddress).to.equal('tdash1primary'); + expect(info).to.deep.equal(fakeInfo); + }); }); describe('getInfoAt()', function () { @@ -580,6 +643,24 @@ describe('AddressKeyManager', function suite() { expect(err.message).to.include('No derived address at index 0'); } }); + + it('should query the correct address for a valid index', async function () { + const fakeInfo = { balance: 1000, nonce: 1 }; + let queriedAddress; + const fakeSdk = { + addresses: { + get: async (addr) => { queriedAddress = addr; return fakeInfo; }, + }, + }; + const mgr = new AddressKeyManager( + fakeSdk, + [{ bech32m: 'tdash1aaa' }, { bech32m: 'tdash1bbb' }], + 'testnet', + ); + const info = await mgr.getInfoAt(1); + expect(queriedAddress).to.equal('tdash1bbb'); + expect(info).to.deep.equal(fakeInfo); + }); }); }); @@ -594,6 +675,16 @@ describe('createClient()', function () { expect(err.message).to.include('Unknown network "bogus"'); } }); + + it('should connect to testnet', async function () { + const sdk = await createClient('testnet'); + expect(sdk.isConnected).to.be.true; + }); + + it('should connect to mainnet', async function () { + const sdk = await createClient('mainnet'); + expect(sdk.isConnected).to.be.true; + }); }); describe('setupDashClient()', function () { @@ -650,7 +741,7 @@ describe('setupDashClient()', function () { } }); - it('should not produce a TypeError when write tutorials use keyManager without a mnemonic', async function () { + it.skip('should not produce a TypeError when write tutorials use keyManager without a mnemonic', async function () { // Write tutorials do: const { keyManager } = await setupDashClient(); // const { signer } = await keyManager.getAuth(); // Without a mnemonic, keyManager is undefined → TypeError on .getAuth(). @@ -713,8 +804,8 @@ describe('setupDashClient()', function () { }); it('should pass identityIndex through to IdentityKeyManager.create()', async function () { - // #6: requireIdentity: true with explicit identityIndex — verifies the - // parameter is forwarded (different index = different derived keys). + // Verifies identityIndex is forwarded through setupDashClient + // (uses requireIdentity: false to avoid identity lookup). const saved = clientConfig.mnemonic; try { clientConfig.mnemonic = TEST_MNEMONIC; From 83f42f17d2f535d42a1c2c884009688d275ff16c Mon Sep 17 00:00:00 2001 From: thephez Date: Sat, 14 Mar 2026 12:37:55 -0400 Subject: [PATCH 6/9] chore: update test scripts Don't run setup in test:all --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 37abdd9..52565e5 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,7 @@ "test": "node --test --test-timeout=120000 test/read-only.test.mjs", "test:read-only": "node --test --test-timeout=120000 test/read-only.test.mjs", "test:read-write": "node --test --test-timeout=300000 --test-concurrency=1 test/read-write.test.mjs", - "test:all": "node --test --test-timeout=300000 --test-concurrency=1 test/read-only.test.mjs test/read-write.test.mjs && npm run test:setup", + "test:all": "node --test --test-timeout=300000 --test-concurrency=1 test/read-only.test.mjs test/read-write.test.mjs", "test:setup": "mocha --slow 500 test/setupDashClient.test.mjs --exit" }, "repository": { From 68cae9f231db42db86b424ec3e4d24227cd060b0 Mon Sep 17 00:00:00 2001 From: thephez Date: Sat, 14 Mar 2026 12:41:05 -0400 Subject: [PATCH 7/9] chore: update mocha to latest and pin --- package-lock.json | 4 ++-- package.json | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index 3dc0643..1aebe31 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,8 +14,8 @@ "devDependencies": { "chai": "6.2.2", "dotenv": "17.3.1", - "eslint": "^8.45.0", - "mocha": "^11.1.0" + "eslint": "8.45.0", + "mocha": "11.7.5" } }, "node_modules/@aashutoshrathi/word-wrap": { diff --git a/package.json b/package.json index 52565e5..139e8e7 100644 --- a/package.json +++ b/package.json @@ -28,7 +28,7 @@ "devDependencies": { "chai": "6.2.2", "dotenv": "17.3.1", - "eslint": "^8.45.0", - "mocha": "^11.1.0" + "eslint": "8.45.0", + "mocha": "11.7.5" } } From 8cef1de8335b49ad93dd723e210e8c1a36c14779 Mon Sep 17 00:00:00 2001 From: thephez Date: Sat, 14 Mar 2026 13:09:01 -0400 Subject: [PATCH 8/9] build: move dotenv from devDependencies to dependencies Tutorials depend on dotenv to load .env files, so it should be a regular dependency to ensure it is available after `npm install`. Co-Authored-By: Claude Opus 4.6 --- package-lock.json | 8 +++----- package.json | 4 ++-- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/package-lock.json b/package-lock.json index 1aebe31..9f2de92 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,11 +9,11 @@ "version": "3.1-dev", "license": "MIT", "dependencies": { - "@dashevo/evo-sdk": "3.1.0-dev.1" + "@dashevo/evo-sdk": "3.1.0-dev.1", + "dotenv": "17.3.1" }, "devDependencies": { "chai": "6.2.2", - "dotenv": "17.3.1", "eslint": "8.45.0", "mocha": "11.7.5" } @@ -562,7 +562,6 @@ "version": "17.3.1", "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.3.1.tgz", "integrity": "sha512-IO8C/dzEb6O3F9/twg6ZLXz164a2fhTnEWb95H23Dm4OuN+92NmEAlTrupP9VW6Jm3sO26tQlqyvyi4CsnY9GA==", - "dev": true, "license": "BSD-2-Clause", "engines": { "node": ">=12" @@ -2254,8 +2253,7 @@ "dotenv": { "version": "17.3.1", "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.3.1.tgz", - "integrity": "sha512-IO8C/dzEb6O3F9/twg6ZLXz164a2fhTnEWb95H23Dm4OuN+92NmEAlTrupP9VW6Jm3sO26tQlqyvyi4CsnY9GA==", - "dev": true + "integrity": "sha512-IO8C/dzEb6O3F9/twg6ZLXz164a2fhTnEWb95H23Dm4OuN+92NmEAlTrupP9VW6Jm3sO26tQlqyvyi4CsnY9GA==" }, "eastasianwidth": { "version": "0.2.0", diff --git a/package.json b/package.json index 139e8e7..d0021e2 100644 --- a/package.json +++ b/package.json @@ -23,11 +23,11 @@ }, "homepage": "https://github.com/dashpay/platform-tutorials#readme", "dependencies": { - "@dashevo/evo-sdk": "3.1.0-dev.1" + "@dashevo/evo-sdk": "3.1.0-dev.1", + "dotenv": "17.3.1" }, "devDependencies": { "chai": "6.2.2", - "dotenv": "17.3.1", "eslint": "8.45.0", "mocha": "11.7.5" } From 394f94f9c71806e61fb637ef0b2aa3df1981ad54 Mon Sep 17 00:00:00 2001 From: thephez Date: Sat, 14 Mar 2026 13:15:55 -0400 Subject: [PATCH 9/9] test: skip mnemonic-dependent tests when PLATFORM_MNEMONIC is unset Fork PRs don't have access to repository secrets, so identity-retrieve fails in CI. Skip tests marked with requiresMnemonic when the env var is absent. Co-Authored-By: Claude Opus 4.6 --- test/read-only.test.mjs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/test/read-only.test.mjs b/test/read-only.test.mjs index 83a3fcb..e9b7944 100644 --- a/test/read-only.test.mjs +++ b/test/read-only.test.mjs @@ -1,7 +1,10 @@ import { describe, it } from 'node:test'; +import dotenv from 'dotenv'; import { runTutorial } from './run-tutorial.mjs'; import { assertTutorialSuccess } from './assertions.mjs'; +dotenv.config(); + const tutorials = [ { path: 'connect.mjs', @@ -22,6 +25,7 @@ const tutorials = [ name: 'identity-retrieve', expectedPatterns: ['Identity retrieved:'], errorPatterns: ['Something went wrong'], + requiresMnemonic: true, }, { path: '1-Identities-and-Names/name-resolve-by-name.mjs', @@ -61,9 +65,12 @@ const tutorials = [ }, ]; +const hasMnemonic = !!process.env.PLATFORM_MNEMONIC; + describe('Read-only tutorials', () => { for (const entry of tutorials) { - it(entry.name, { timeout: entry.timeoutMs ?? 120_000 }, async () => { + const testFn = entry.requiresMnemonic && !hasMnemonic ? it.skip : it; + testFn(entry.name, { timeout: entry.timeoutMs ?? 120_000 }, async () => { const result = await runTutorial(entry.path, { env: entry.env, timeoutMs: entry.timeoutMs,