diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index 47ac343..fc5d116 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -18,6 +18,11 @@ jobs: # Install Solana CLI (beta, required to fix 'ahash' issue) solana-cli-version: "1.18.5" + - name: Setup Node.js version + uses: actions/setup-node@v4 + with: + node-version: 21.x + - name: Run Solana validator (and background it) run: solana-test-validator & @@ -28,4 +33,4 @@ jobs: run: echo $PATH; which solana-keygen - name: Run tests - run: npm test + run: npm run test:ci diff --git a/.gitignore b/.gitignore index c509a27..87ed9a1 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ -src/temp +tests/temp dist node_modules test-ledger -.env* \ No newline at end of file +.env* +coverage \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 3437846..e66f8e2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,12 +16,11 @@ "dotenv": "^16.4.5" }, "devDependencies": { - "@digitak/esrun": "^3.2.24", - "@types/node": "^20.5.7", - "esrun": "^3.2.26", + "@types/node": "^20.16.1", + "esbuild": "^0.23.1", + "esbuild-register": "^3.6.0", "prettier": "^3.0.3", - "tsup": "^7.2.0", - "typescript": "^5.2.2" + "typescript": "^5.5.4" } }, "node_modules/@babel/runtime": { @@ -36,91 +35,412 @@ "node": ">=6.9.0" } }, - "node_modules/@digitak/esrun": { - "version": "3.2.24", - "resolved": "https://registry.npmjs.org/@digitak/esrun/-/esrun-3.2.24.tgz", - "integrity": "sha512-HvD1eSuZVBaFZpKU/kl2rzDELCAbAnrFO2in855IrX15Zji4sdrekiEQph+eq5W5xjCyc254zx/Bh8RM2216mg==", + "node_modules/@esbuild/aix-ppc64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.23.1.tgz", + "integrity": "sha512-6VhYk1diRqrhBAqpJEdjASR/+WVRtfjpqKuNw11cLiaWpAT/Uu+nokB+UJnevzy/P9C/ty6AOe0dwueMrGh/iQ==", + "cpu": [ + "ppc64" + ], "dev": true, - "dependencies": { - "@digitak/grubber": "^3.1.4", - "chokidar": "^3.5.1", - "esbuild": "^0.17.4" - }, - "bin": { - "esrun": "bin.js" - }, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], "engines": { - "node": ">=14.0" + "node": ">=18" } }, - "node_modules/@digitak/grubber": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/@digitak/grubber/-/grubber-3.1.4.tgz", - "integrity": "sha512-pqsnp2BUYlDoTXWG34HWgEJse/Eo1okRgNex8IG84wHrJp8h3SakeR5WhB4VxSA2+/D+frNYJ0ch3yXzsfNDoA==", - "dev": true + "node_modules/@esbuild/android-arm": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.23.1.tgz", + "integrity": "sha512-uz6/tEy2IFm9RYOyvKl88zdzZfwEfKZmnX9Cj1BHjeSGNuGLuMD1kR8y5bteYmwqKm1tj8m4cb/aKEorr6fHWQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.23.1.tgz", + "integrity": "sha512-xw50ipykXcLstLeWH7WRdQuysJqejuAGPd30vd1i5zSyKK3WE+ijzHmLKxdiCMtH1pHz78rOg0BKSYOSB/2Khw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.23.1.tgz", + "integrity": "sha512-nlN9B69St9BwUoB+jkyU090bru8L0NA3yFvAd7k8dNsVH8bi9a8cUAUSEcEEgTp2z3dbEDGJGfP6VUnkQnlReg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } }, "node_modules/@esbuild/darwin-arm64": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.17.19.tgz", - "integrity": "sha512-80wEoCfF/hFKM6WE1FyBHc9SfUblloAWx6FJkFWTWiCoht9Mc0ARGEM47e67W9rI09YoUxJL68WHfDRYEAvOhg==", + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.23.1.tgz", + "integrity": "sha512-YsS2e3Wtgnw7Wq53XXBLcV6JhRsEq8hkfg91ESVadIrzr9wO6jJDMZnCQbHm1Guc5t/CdDiFSSfWP58FNuvT3Q==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "darwin" ], "engines": { - "node": ">=12" + "node": ">=18" } }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", - "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", + "node_modules/@esbuild/darwin-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.23.1.tgz", + "integrity": "sha512-aClqdgTDVPSEGgoCS8QDG37Gu8yc9lTHNAQlsztQ6ENetKEO//b8y31MMu2ZaPbn4kVsIABzVLXYLhCGekGDqw==", + "cpu": [ + "x64" + ], "dev": true, - "dependencies": { - "@jridgewell/set-array": "^1.0.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.9" - }, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], "engines": { - "node": ">=6.0.0" + "node": ">=18" } }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz", - "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==", + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.23.1.tgz", + "integrity": "sha512-h1k6yS8/pN/NHlMl5+v4XPfikhJulk4G+tKGFIOwURBSFzE8bixw1ebjluLOjfwtLqY0kewfjLSrO6tN2MgIhA==", + "cpu": [ + "arm64" + ], "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], "engines": { - "node": ">=6.0.0" + "node": ">=18" } }, - "node_modules/@jridgewell/set-array": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", - "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", + "node_modules/@esbuild/freebsd-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.23.1.tgz", + "integrity": "sha512-lK1eJeyk1ZX8UklqFd/3A60UuZ/6UVfGT2LuGo3Wp4/z7eRTRYY+0xOu2kpClP+vMTi9wKOfXi2vjUpO1Ro76g==", + "cpu": [ + "x64" + ], "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], "engines": { - "node": ">=6.0.0" + "node": ">=18" } }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.4.15", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", - "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", - "dev": true + "node_modules/@esbuild/linux-arm": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.23.1.tgz", + "integrity": "sha512-CXXkzgn+dXAPs3WBwE+Kvnrf4WECwBdfjfeYHpMeVxWE0EceB6vhWGShs6wi0IYEqMSIzdOF1XjQ/Mkm5d7ZdQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.19", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.19.tgz", - "integrity": "sha512-kf37QtfW+Hwx/buWGMPcR60iF9ziHa6r/CZJIHbmcm4+0qrXiVdxegAH0F6yddEVQ7zdkjcGCgCzUu+BcbhQxw==", + "node_modules/@esbuild/linux-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.23.1.tgz", + "integrity": "sha512-/93bf2yxencYDnItMYV/v116zff6UyTjo4EtEQjUBeGiVpMmffDNUyD9UN2zV+V3LRV3/on4xdZ26NKzn6754g==", + "cpu": [ + "arm64" + ], "dev": true, - "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.23.1.tgz", + "integrity": "sha512-VTN4EuOHwXEkXzX5nTvVY4s7E/Krz7COC8xkftbbKRYAl96vPiUssGkeMELQMOnLOJ8k3BY1+ZY52tttZnHcXQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.23.1.tgz", + "integrity": "sha512-Vx09LzEoBa5zDnieH8LSMRToj7ir/Jeq0Gu6qJ/1GcBq9GkfoEAoXvLiW1U9J1qE/Y/Oyaq33w5p2ZWrNNHNEw==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.23.1.tgz", + "integrity": "sha512-nrFzzMQ7W4WRLNUOU5dlWAqa6yVeI0P78WKGUo7lg2HShq/yx+UYkeNSE0SSfSure0SqgnsxPvmAUu/vu0E+3Q==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.23.1.tgz", + "integrity": "sha512-dKN8fgVqd0vUIjxuJI6P/9SSSe/mB9rvA98CSH2sJnlZ/OCZWO1DJvxj8jvKTfYUdGfcq2dDxoKaC6bHuTlgcw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.23.1.tgz", + "integrity": "sha512-5AV4Pzp80fhHL83JM6LoA6pTQVWgB1HovMBsLQ9OZWLDqVY8MVobBXNSmAJi//Csh6tcY7e7Lny2Hg1tElMjIA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.23.1.tgz", + "integrity": "sha512-9ygs73tuFCe6f6m/Tb+9LtYxWR4c9yg7zjt2cYkjDbDpV/xVn+68cQxMXCjUpYwEkze2RcU/rMnfIXNRFmSoDw==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.23.1.tgz", + "integrity": "sha512-EV6+ovTsEXCPAp58g2dD68LxoP/wK5pRvgy0J/HxPGB009omFPv3Yet0HiaqvrIrgPTBuC6wCH1LTOY91EO5hQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.23.1.tgz", + "integrity": "sha512-aevEkCNu7KlPRpYLjwmdcuNz6bDFiE7Z8XC4CPqExjTvrHugh28QzUXVOZtiYghciKUacNktqxdpymplil1beA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.23.1.tgz", + "integrity": "sha512-3x37szhLexNA4bXhLrCC/LImN/YtWis6WXr1VESlfVtVeoFJBRINPJ3f0a/6LV8zpikqoUg4hyXw0sFBt5Cr+Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.23.1.tgz", + "integrity": "sha512-aY2gMmKmPhxfU+0EdnN+XNtGbjfQgwZj43k8G3fyrDM/UdZww6xrWxmDkuz2eCZchqVeABjV5BpildOrUbBTqA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.23.1.tgz", + "integrity": "sha512-RBRT2gqEl0IKQABT4XTj78tpk9v7ehp+mazn2HbUeZl1YMdaGAQqhapjGTCe7uw7y0frDi4gS0uHzhvpFuI1sA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.23.1.tgz", + "integrity": "sha512-4O+gPR5rEBe2FpKOVyiJ7wNDPA8nGzDuJ6gN4okSA1gEOYZ67N8JPk58tkWtdtPeLz7lBnY6I5L3jdsr3S+A6A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.23.1.tgz", + "integrity": "sha512-BcaL0Vn6QwCwre3Y717nVHZbAa4UBEigzFm6VdsVdT/MbZ38xoj1X9HPkZhbmaBGUD1W8vxAfffbDe8bA6AKnQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.23.1.tgz", + "integrity": "sha512-BHpFFeslkWrXWyUPnbKm+xYYVYruCinGcftSBaa8zoF9hZO4BcSCFUvHVTtzpIY6YzUnYtuEhZ+C9iEXjxnasg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" } }, "node_modules/@noble/curves": { @@ -147,41 +467,6 @@ "url": "https://paulmillr.com/funding/" } }, - "node_modules/@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "dev": true, - "dependencies": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "dev": true, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "dev": true, - "dependencies": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - }, - "engines": { - "node": ">= 8" - } - }, "node_modules/@solana/buffer-layout": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/@solana/buffer-layout/-/buffer-layout-4.0.1.tgz", @@ -529,9 +814,13 @@ } }, "node_modules/@types/node": { - "version": "20.5.7", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.5.7.tgz", - "integrity": "sha512-dP7f3LdZIysZnmvP3ANJYTSwg+wLLl8p7RqniVlV7j+oXSXAbt9h0WIBFmJy5inWZoX9wZN6eXx+YXd9Rh3RBA==" + "version": "20.16.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.16.2.tgz", + "integrity": "sha512-91s/n4qUPV/wg8eE9KHYW1kouTfDk2FPGjXbBMfRWP/2vg1rCXNQL1OCabwGs0XSdukuK+MwCDXE30QpSeMUhQ==", + "license": "MIT", + "dependencies": { + "undici-types": "~6.19.2" + } }, "node_modules/@types/uuid": { "version": "8.3.4", @@ -559,40 +848,6 @@ "node": ">= 8.0.0" } }, - "node_modules/any-promise": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", - "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", - "dev": true - }, - "node_modules/anymatch": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", - "dev": true, - "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true - }, "node_modules/base-x": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/base-x/-/base-x-5.0.0.tgz", @@ -639,15 +894,6 @@ "node": "*" } }, - "node_modules/binary-extensions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", - "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/bindings": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", @@ -687,29 +933,6 @@ "base-x": "^3.0.2" } }, - "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/braces": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", - "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", - "dev": true, - "license": "MIT", - "dependencies": { - "fill-range": "^7.1.1" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/bs58": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/bs58/-/bs58-6.0.0.tgz", @@ -755,30 +978,6 @@ "node": ">=6.14.2" } }, - "node_modules/bundle-require": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/bundle-require/-/bundle-require-4.0.1.tgz", - "integrity": "sha512-9NQkRHlNdNpDBGmLpngF3EFDcwodhMUuLz9PaWYciVcQF9SE4LFjM2DB/xV1Li5JiuDMv7ZUWuC3rGbqR0MAXQ==", - "dev": true, - "dependencies": { - "load-tsconfig": "^0.2.3" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "peerDependencies": { - "esbuild": ">=0.17" - } - }, - "node_modules/cac": { - "version": "6.7.14", - "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", - "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/chalk": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", @@ -788,34 +987,7 @@ "node": "^12.17.0 || ^14.13 || >=16.0.0" }, "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/chokidar": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", - "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - ], - "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - }, - "engines": { - "node": ">= 8.10.0" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" + "url": "https://github.com/chalk/chalk?sponsor=1" } }, "node_modules/commander": { @@ -824,31 +996,12 @@ "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", "license": "MIT" }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "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==", - "dev": true, - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, "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.3.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.6.tgz", + "integrity": "sha512-O/09Bd4Z1fBrU4VzkhFqVgpPzaGbw6Sm9FEkBT1A/YBXQFGuuSxa1dN2nxgxS34JmKXqYx8CZAwEVoJFImUXIg==", "dev": true, + "license": "MIT", "dependencies": { "ms": "2.1.2" }, @@ -865,7 +1018,8 @@ "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/delay": { "version": "5.0.0", @@ -879,18 +1033,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/dir-glob": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", - "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", - "dev": true, - "dependencies": { - "path-type": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/dotenv": { "version": "16.4.5", "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz", @@ -919,57 +1061,56 @@ } }, "node_modules/esbuild": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.17.19.tgz", - "integrity": "sha512-XQ0jAPFkK/u3LcVRcvVHQcTIqD6E2H1fvZMA5dQPSOWb3suUbWbfbRf94pjc0bNzRYLfIrDRQXr7X+LHIm5oHw==", + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.23.1.tgz", + "integrity": "sha512-VVNz/9Sa0bs5SELtn3f7qhJCDPCF5oMEl5cO9/SSinpE9hbPVvxbd572HH5AKiP7WD8INO53GgfDDhRjkylHEg==", "dev": true, "hasInstallScript": true, + "license": "MIT", "bin": { "esbuild": "bin/esbuild" }, "engines": { - "node": ">=12" + "node": ">=18" }, "optionalDependencies": { - "@esbuild/android-arm": "0.17.19", - "@esbuild/android-arm64": "0.17.19", - "@esbuild/android-x64": "0.17.19", - "@esbuild/darwin-arm64": "0.17.19", - "@esbuild/darwin-x64": "0.17.19", - "@esbuild/freebsd-arm64": "0.17.19", - "@esbuild/freebsd-x64": "0.17.19", - "@esbuild/linux-arm": "0.17.19", - "@esbuild/linux-arm64": "0.17.19", - "@esbuild/linux-ia32": "0.17.19", - "@esbuild/linux-loong64": "0.17.19", - "@esbuild/linux-mips64el": "0.17.19", - "@esbuild/linux-ppc64": "0.17.19", - "@esbuild/linux-riscv64": "0.17.19", - "@esbuild/linux-s390x": "0.17.19", - "@esbuild/linux-x64": "0.17.19", - "@esbuild/netbsd-x64": "0.17.19", - "@esbuild/openbsd-x64": "0.17.19", - "@esbuild/sunos-x64": "0.17.19", - "@esbuild/win32-arm64": "0.17.19", - "@esbuild/win32-ia32": "0.17.19", - "@esbuild/win32-x64": "0.17.19" - } - }, - "node_modules/esrun": { - "version": "3.2.26", - "resolved": "https://registry.npmjs.org/esrun/-/esrun-3.2.26.tgz", - "integrity": "sha512-gDjP87qj4RW0BryZXPY3/L161hPo9uG6luBTjLsuHG3cKnhSMrzB7eNzSzvDyBLg7OgugyvzSgB2ov7mZ/oa7Q==", + "@esbuild/aix-ppc64": "0.23.1", + "@esbuild/android-arm": "0.23.1", + "@esbuild/android-arm64": "0.23.1", + "@esbuild/android-x64": "0.23.1", + "@esbuild/darwin-arm64": "0.23.1", + "@esbuild/darwin-x64": "0.23.1", + "@esbuild/freebsd-arm64": "0.23.1", + "@esbuild/freebsd-x64": "0.23.1", + "@esbuild/linux-arm": "0.23.1", + "@esbuild/linux-arm64": "0.23.1", + "@esbuild/linux-ia32": "0.23.1", + "@esbuild/linux-loong64": "0.23.1", + "@esbuild/linux-mips64el": "0.23.1", + "@esbuild/linux-ppc64": "0.23.1", + "@esbuild/linux-riscv64": "0.23.1", + "@esbuild/linux-s390x": "0.23.1", + "@esbuild/linux-x64": "0.23.1", + "@esbuild/netbsd-x64": "0.23.1", + "@esbuild/openbsd-arm64": "0.23.1", + "@esbuild/openbsd-x64": "0.23.1", + "@esbuild/sunos-x64": "0.23.1", + "@esbuild/win32-arm64": "0.23.1", + "@esbuild/win32-ia32": "0.23.1", + "@esbuild/win32-x64": "0.23.1" + } + }, + "node_modules/esbuild-register": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/esbuild-register/-/esbuild-register-3.6.0.tgz", + "integrity": "sha512-H2/S7Pm8a9CL1uhp9OvjwrBh5Pvx0H8qVOxNu8Wed9Y7qv56MPtq+GGM8RJpq6glYJn9Wspr8uw7l55uyinNeg==", "dev": true, + "license": "MIT", "dependencies": { - "@digitak/grubber": "^3.1.4", - "chokidar": "^3.5.1", - "esbuild": "^0.17.4" + "debug": "^4.3.4" }, - "bin": { - "esrun": "bin.js" - }, - "engines": { - "node": ">=14.0" + "peerDependencies": { + "esbuild": ">=0.12 <1" } }, "node_modules/eventemitter3": { @@ -978,29 +1119,6 @@ "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==", "license": "MIT" }, - "node_modules/execa": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", - "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", - "dev": true, - "dependencies": { - "cross-spawn": "^7.0.3", - "get-stream": "^6.0.0", - "human-signals": "^2.1.0", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.1", - "onetime": "^5.1.2", - "signal-exit": "^3.0.3", - "strip-final-newline": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" - } - }, "node_modules/eyes": { "version": "0.1.8", "resolved": "https://registry.npmjs.org/eyes/-/eyes-0.1.8.tgz", @@ -1009,22 +1127,6 @@ "node": "> 0.1.90" } }, - "node_modules/fast-glob": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.1.tgz", - "integrity": "sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==", - "dev": true, - "dependencies": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.4" - }, - "engines": { - "node": ">=8.6.0" - } - }, "node_modules/fast-stable-stringify": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fast-stable-stringify/-/fast-stable-stringify-1.0.0.tgz", @@ -1037,126 +1139,11 @@ "license": "CC0-1.0", "peer": true }, - "node_modules/fastq": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", - "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", - "dev": true, - "dependencies": { - "reusify": "^1.0.4" - } - }, "node_modules/file-uri-to-path": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==" }, - "node_modules/fill-range": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", - "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", - "dev": true, - "license": "MIT", - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true - }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, - "hasInstallScript": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/get-stream": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", - "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", - "dev": true, - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/globby": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", - "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", - "dev": true, - "dependencies": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.2.9", - "ignore": "^5.2.0", - "merge2": "^1.4.1", - "slash": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/human-signals": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", - "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", - "dev": true, - "engines": { - "node": ">=10.17.0" - } - }, "node_modules/humanize-ms": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", @@ -1184,92 +1171,6 @@ } ] }, - "node_modules/ignore": { - "version": "5.2.4", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", - "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", - "dev": true, - "engines": { - "node": ">= 4" - } - }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "dev": true, - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true - }, - "node_modules/is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, - "dependencies": { - "binary-extensions": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, - "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/is-stream": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", - "dev": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true - }, "node_modules/isomorphic-ws": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/isomorphic-ws/-/isomorphic-ws-4.0.1.tgz", @@ -1311,15 +1212,6 @@ "integrity": "sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==", "license": "MIT" }, - "node_modules/joycon": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/joycon/-/joycon-3.1.1.tgz", - "integrity": "sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==", - "dev": true, - "engines": { - "node": ">=10" - } - }, "node_modules/json-stringify-safe": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", @@ -1339,275 +1231,53 @@ "version": "1.3.5", "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz", "integrity": "sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==", - "license": "(MIT OR Apache-2.0)", - "dependencies": { - "jsonparse": "^1.2.0", - "through": ">=2.2.7 <3" - }, - "bin": { - "JSONStream": "bin.js" - }, - "engines": { - "node": "*" - } - }, - "node_modules/lilconfig": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz", - "integrity": "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/lines-and-columns": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", - "dev": true - }, - "node_modules/load-tsconfig": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/load-tsconfig/-/load-tsconfig-0.2.5.tgz", - "integrity": "sha512-IXO6OCs9yg8tMKzfPZ1YmheJbZCiEsnBdcB03l0OcfK9prKnJb96siuHCr5Fl37/yo9DnKU+TLpxzTUspw9shg==", - "dev": true, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - } - }, - "node_modules/lodash.sortby": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", - "integrity": "sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==", - "dev": true - }, - "node_modules/merge-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", - "dev": true - }, - "node_modules/merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "dev": true, - "engines": { - "node": ">= 8" - } - }, - "node_modules/micromatch": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", - "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", - "dev": true, - "license": "MIT", - "dependencies": { - "braces": "^3.0.3", - "picomatch": "^2.3.1" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" - }, - "node_modules/mz": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", - "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", - "dev": true, - "dependencies": { - "any-promise": "^1.0.0", - "object-assign": "^4.0.1", - "thenify-all": "^1.0.0" - } - }, - "node_modules/node-fetch": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", - "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", - "dependencies": { - "whatwg-url": "^5.0.0" - }, - "engines": { - "node": "4.x || >=6.0.0" - }, - "peerDependencies": { - "encoding": "^0.1.0" - }, - "peerDependenciesMeta": { - "encoding": { - "optional": true - } - } - }, - "node_modules/node-gyp-build": { - "version": "4.6.1", - "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.6.1.tgz", - "integrity": "sha512-24vnklJmyRS8ViBNI8KbtK/r/DmXQMRiOMXTNz2nrTnAYUwjmEEbnnpB/+kt+yWRv73bPsSPRFddrcIbAxSiMQ==", - "optional": true, - "bin": { - "node-gyp-build": "bin.js", - "node-gyp-build-optional": "optional.js", - "node-gyp-build-test": "build-test.js" - } - }, - "node_modules/normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/npm-run-path": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", - "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", - "dev": true, - "dependencies": { - "path-key": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dev": true, - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", - "dev": true, + "license": "(MIT OR Apache-2.0)", "dependencies": { - "mimic-fn": "^2.1.0" + "jsonparse": "^1.2.0", + "through": ">=2.2.7 <3" }, - "engines": { - "node": ">=6" + "bin": { + "JSONStream": "bin.js" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true, "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" + "node": "*" } }, - "node_modules/pirates": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz", - "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==", - "dev": true, - "engines": { - "node": ">= 6" - } + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" }, - "node_modules/postcss-load-config": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-4.0.1.tgz", - "integrity": "sha512-vEJIc8RdiBRu3oRAI0ymerOn+7rPuMvRXslTvZUKZonDHFIczxztIyJ1urxM1x9JXEikvpWWTUUqal5j/8QgvA==", - "dev": true, + "node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", "dependencies": { - "lilconfig": "^2.0.5", - "yaml": "^2.1.1" + "whatwg-url": "^5.0.0" }, "engines": { - "node": ">= 14" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" + "node": "4.x || >=6.0.0" }, "peerDependencies": { - "postcss": ">=8.0.9", - "ts-node": ">=9.0.0" + "encoding": "^0.1.0" }, "peerDependenciesMeta": { - "postcss": { - "optional": true - }, - "ts-node": { + "encoding": { "optional": true } } }, + "node_modules/node-gyp-build": { + "version": "4.6.1", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.6.1.tgz", + "integrity": "sha512-24vnklJmyRS8ViBNI8KbtK/r/DmXQMRiOMXTNz2nrTnAYUwjmEEbnnpB/+kt+yWRv73bPsSPRFddrcIbAxSiMQ==", + "optional": true, + "bin": { + "node-gyp-build": "bin.js", + "node-gyp-build-optional": "optional.js", + "node-gyp-build-test": "build-test.js" + } + }, "node_modules/prettier": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.0.3.tgz", @@ -1623,88 +1293,12 @@ "url": "https://github.com/prettier/prettier?sponsor=1" } }, - "node_modules/punycode": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", - "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "dev": true, - "dependencies": { - "picomatch": "^2.2.1" - }, - "engines": { - "node": ">=8.10.0" - } - }, "node_modules/regenerator-runtime": { "version": "0.14.1", "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==", "license": "MIT" }, - "node_modules/resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/reusify": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", - "dev": true, - "engines": { - "iojs": ">=1.0.0", - "node": ">=0.10.0" - } - }, - "node_modules/rollup": { - "version": "3.29.1", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.29.1.tgz", - "integrity": "sha512-c+ebvQz0VIH4KhhCpDsI+Bik0eT8ZFEVZEYw0cGMVqIP8zc+gnwl7iXCamTw7vzv2MeuZFZfdx5JJIq+ehzDlg==", - "dev": true, - "bin": { - "rollup": "dist/bin/rollup" - }, - "engines": { - "node": ">=14.18.0", - "npm": ">=8.0.0" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" - } - }, "node_modules/rpc-websockets": { "version": "9.0.2", "resolved": "https://registry.npmjs.org/rpc-websockets/-/rpc-websockets-9.0.2.tgz", @@ -1758,29 +1352,6 @@ } } }, - "node_modules/run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "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" - } - ], - "dependencies": { - "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", @@ -1800,120 +1371,6 @@ } ] }, - "node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "dev": true - }, - "node_modules/slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/source-map": { - "version": "0.8.0-beta.0", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.8.0-beta.0.tgz", - "integrity": "sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA==", - "dev": true, - "dependencies": { - "whatwg-url": "^7.0.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/source-map/node_modules/tr46": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-1.0.1.tgz", - "integrity": "sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA==", - "dev": true, - "dependencies": { - "punycode": "^2.1.0" - } - }, - "node_modules/source-map/node_modules/webidl-conversions": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz", - "integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==", - "dev": true - }, - "node_modules/source-map/node_modules/whatwg-url": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.1.0.tgz", - "integrity": "sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==", - "dev": true, - "dependencies": { - "lodash.sortby": "^4.7.0", - "tr46": "^1.0.1", - "webidl-conversions": "^4.0.2" - } - }, - "node_modules/strip-final-newline": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", - "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/sucrase": { - "version": "3.34.0", - "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.34.0.tgz", - "integrity": "sha512-70/LQEZ07TEcxiU2dz51FKaE6hCTWC6vr7FOk3Gr0U60C3shtAN+H+BFr9XlYe5xqf3RA8nrc+VIwzCfnxuXJw==", - "dev": true, - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.2", - "commander": "^4.0.0", - "glob": "7.1.6", - "lines-and-columns": "^1.1.6", - "mz": "^2.7.0", - "pirates": "^4.0.1", - "ts-interface-checker": "^0.1.9" - }, - "bin": { - "sucrase": "bin/sucrase", - "sucrase-node": "bin/sucrase-node" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/sucrase/node_modules/commander": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", - "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", - "dev": true, - "engines": { - "node": ">= 6" - } - }, "node_modules/superstruct": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/superstruct/-/superstruct-2.0.2.tgz", @@ -1928,174 +1385,28 @@ "resolved": "https://registry.npmjs.org/text-encoding-utf-8/-/text-encoding-utf-8-1.0.2.tgz", "integrity": "sha512-8bw4MY9WjdsD2aMtO0OzOCY3pXGYNx2d2FfHRVUKkiCPDWjKuOlhLVASS+pD7VkLTVjW268LYJHwsnPFlBpbAg==" }, - "node_modules/thenify": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", - "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", - "dev": true, - "dependencies": { - "any-promise": "^1.0.0" - } - }, - "node_modules/thenify-all": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", - "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", - "dev": true, - "dependencies": { - "thenify": ">= 3.1.0 < 4" - }, - "engines": { - "node": ">=0.8" - } - }, "node_modules/through": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", "license": "MIT" }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, "node_modules/tr46": { "version": "0.0.3", "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" }, - "node_modules/tree-kill": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", - "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", - "dev": true, - "bin": { - "tree-kill": "cli.js" - } - }, - "node_modules/ts-interface-checker": { - "version": "0.1.13", - "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", - "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", - "dev": true - }, "node_modules/tslib": { "version": "2.6.3", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==", "license": "0BSD" }, - "node_modules/tsup": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/tsup/-/tsup-7.2.0.tgz", - "integrity": "sha512-vDHlczXbgUvY3rWvqFEbSqmC1L7woozbzngMqTtL2PGBODTtWlRwGDDawhvWzr5c1QjKe4OAKqJGfE1xeXUvtQ==", - "dev": true, - "dependencies": { - "bundle-require": "^4.0.0", - "cac": "^6.7.12", - "chokidar": "^3.5.1", - "debug": "^4.3.1", - "esbuild": "^0.18.2", - "execa": "^5.0.0", - "globby": "^11.0.3", - "joycon": "^3.0.1", - "postcss-load-config": "^4.0.1", - "resolve-from": "^5.0.0", - "rollup": "^3.2.5", - "source-map": "0.8.0-beta.0", - "sucrase": "^3.20.3", - "tree-kill": "^1.2.2" - }, - "bin": { - "tsup": "dist/cli-default.js", - "tsup-node": "dist/cli-node.js" - }, - "engines": { - "node": ">=16.14" - }, - "peerDependencies": { - "@swc/core": "^1", - "postcss": "^8.4.12", - "typescript": ">=4.1.0" - }, - "peerDependenciesMeta": { - "@swc/core": { - "optional": true - }, - "postcss": { - "optional": true - }, - "typescript": { - "optional": true - } - } - }, - "node_modules/tsup/node_modules/@esbuild/darwin-arm64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.18.20.tgz", - "integrity": "sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/tsup/node_modules/esbuild": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.18.20.tgz", - "integrity": "sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==", - "dev": true, - "hasInstallScript": true, - "bin": { - "esbuild": "bin/esbuild" - }, - "engines": { - "node": ">=12" - }, - "optionalDependencies": { - "@esbuild/android-arm": "0.18.20", - "@esbuild/android-arm64": "0.18.20", - "@esbuild/android-x64": "0.18.20", - "@esbuild/darwin-arm64": "0.18.20", - "@esbuild/darwin-x64": "0.18.20", - "@esbuild/freebsd-arm64": "0.18.20", - "@esbuild/freebsd-x64": "0.18.20", - "@esbuild/linux-arm": "0.18.20", - "@esbuild/linux-arm64": "0.18.20", - "@esbuild/linux-ia32": "0.18.20", - "@esbuild/linux-loong64": "0.18.20", - "@esbuild/linux-mips64el": "0.18.20", - "@esbuild/linux-ppc64": "0.18.20", - "@esbuild/linux-riscv64": "0.18.20", - "@esbuild/linux-s390x": "0.18.20", - "@esbuild/linux-x64": "0.18.20", - "@esbuild/netbsd-x64": "0.18.20", - "@esbuild/openbsd-x64": "0.18.20", - "@esbuild/sunos-x64": "0.18.20", - "@esbuild/win32-arm64": "0.18.20", - "@esbuild/win32-ia32": "0.18.20", - "@esbuild/win32-x64": "0.18.20" - } - }, "node_modules/typescript": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz", - "integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==", + "version": "5.5.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.4.tgz", + "integrity": "sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q==", + "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -2104,6 +1415,12 @@ "node": ">=14.17" } }, + "node_modules/undici-types": { + "version": "6.19.8", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", + "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", + "license": "MIT" + }, "node_modules/utf-8-validate": { "version": "5.0.10", "resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-5.0.10.tgz", @@ -2139,27 +1456,6 @@ "webidl-conversions": "^3.0.0" } }, - "node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "dev": true - }, "node_modules/ws": { "version": "7.5.10", "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz", @@ -2180,15 +1476,6 @@ "optional": true } } - }, - "node_modules/yaml": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.3.2.tgz", - "integrity": "sha512-N/lyzTPaJasoDmfV7YTrYCI0G/3ivm/9wdG0aHuheKowWQwGTsK0Eoiw6utmzAnI6pkJa0DUVygvp3spqqEKXg==", - "dev": true, - "engines": { - "node": ">= 14" - } } } } diff --git a/package.json b/package.json index e6f6691..93a284b 100644 --- a/package.json +++ b/package.json @@ -23,11 +23,11 @@ "sideEffects": false, "scripts": { "build": "npm run build:esm && npm run build:cjs", - "build:esm": "tsc", + "build:esm": "tsc -p tsconfig.json", "build:cjs": "tsc -p tsconfig.cjs.json", "clean": "rm -rf dist", - "test": "esrun src/index.test.ts", - "typecheck": "tsc --noEmit" + "test": "cd tests && npm run test", + "test:ci": "cd tests && npm run test:ci" }, "keywords": [ "solana", @@ -55,12 +55,11 @@ "dotenv": "^16.4.5" }, "devDependencies": { - "@digitak/esrun": "^3.2.24", - "@types/node": "^20.5.7", - "esrun": "^3.2.26", + "@types/node": "^20.16.1", + "esbuild": "^0.23.1", + "esbuild-register": "^3.6.0", "prettier": "^3.0.3", - "tsup": "^7.2.0", - "typescript": "^5.2.2" + "typescript": "^5.5.4" }, "publishConfig": { "access": "public", diff --git a/src/index.test.ts b/src/index.test.ts deleted file mode 100644 index d16705f..0000000 --- a/src/index.test.ts +++ /dev/null @@ -1,617 +0,0 @@ -import { before, describe, test } from "node:test"; -import { - getKeypairFromEnvironment, - getKeypairFromFile, - addKeypairToEnvFile, - getCustomErrorMessage, - airdropIfRequired, - getExplorerLink, - confirmTransaction, - makeKeypairs, - InitializeKeypairOptions, - initializeKeypair, - getLogs, - getSimulationComputeUnits, - createAccountsMintsAndTokenAccounts, - makeTokenMint, -} from "./index"; -import { - Connection, - Keypair, - LAMPORTS_PER_SOL, - PublicKey, - SystemProgram, - Transaction, - TransactionInstruction, -} from "@solana/web3.js"; -import assert from "node:assert/strict"; -import base58 from "bs58"; -// See https://m.media-amazon.com/images/I/51TJeGHxyTL._SY445_SX342_.jpg -import { exec as execNoPromises } from "child_process"; -import { promisify } from "util"; -import { writeFile, unlink as deleteFile } from "node:fs/promises"; -import { TokenMetadata, unpack } from "@solana/spl-token-metadata"; -import dotenv from "dotenv"; -import { getTokenMetadata } from "@solana/spl-token"; - -const exec = promisify(execNoPromises); - -const LOCALHOST = "http://127.0.0.1:8899"; -const MEMO_PROGRAM_ID = new PublicKey( - "MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr", -); -const TEMP_DIR = "src/temp"; - -describe(`getCustomErrorMessage`, () => { - test(`we turn error messages with hex codes into error messages for the program`, () => { - // This example set of error is from the token program - // https://github.com/solana-labs/solana-program-library/blob/master/token/program/src/error.rs - const programErrors = [ - "Lamport balance below rent-exempt threshold", - "Insufficient funds", - "Invalid Mint", - "Account not associated with this Mint", - "Owner does not match", - "Fixed supply", - "Already in use", - "Invalid number of provided signers", - "Invalid number of required signers", - "State is unititialized", - "Instruction does not support native tokens", - "Non-native account can only be closed if its balance is zero", - "Invalid instruction", - "State is invalid for requested operation", - "Operation overflowed", - "Account does not support specified authority type", - "This token mint cannot freeze accounts", - "Account is frozen", - "The provided decimals value different from the Mint decimals", - "Instruction does not support non-native tokens", - ]; - const errorMessage = getCustomErrorMessage( - programErrors, - "failed to send transaction: Transaction simulation failed: Error processing Instruction 0: custom program error: 0x10", - ); - assert.equal(errorMessage, "This token mint cannot freeze accounts"); - }); -}); - -describe("getKeypairFromFile", () => { - let TEST_FILE_NAME = `${TEMP_DIR}/test-keyfile-do-not-use.json`; - let MISSING_FILE_NAME = "THIS FILE DOES NOT EXIST"; - let CORRUPT_TEST_FILE_NAME = `${TEMP_DIR}/corrupt-keyfile-do-not-use.json`; - before(async () => { - const { stdout } = await exec( - `solana-keygen new --force --no-bip39-passphrase -o ${TEST_FILE_NAME}`, - ); - assert(stdout.includes("Wrote new keypair")); - - await writeFile(CORRUPT_TEST_FILE_NAME, "I AM CORRUPT"); - }); - - test("getting a keypair from a file", async () => { - await getKeypairFromFile(TEST_FILE_NAME); - }); - - test("throws a nice error if the file is missing", async () => { - assert.rejects(async () => await getKeypairFromFile(MISSING_FILE_NAME), { - message: `Could not read keypair from file at '${MISSING_FILE_NAME}'`, - }); - }); - - test("throws a nice error if the file is corrupt", async () => { - assert.rejects(() => getKeypairFromFile(CORRUPT_TEST_FILE_NAME), { - message: `Invalid secret key file at '${CORRUPT_TEST_FILE_NAME}'!`, - }); - }); -}); - -describe("getKeypairFromEnvironment", () => { - let TEST_ENV_VAR_ARRAY_OF_NUMBERS = "TEST_ENV_VAR_ARRAY_OF_NUMBERS"; - let TEST_ENV_VAR_BASE58 = "TEST_ENV_VAR_BASE58"; - let TEST_ENV_VAR_WITH_BAD_VALUE = "TEST_ENV_VAR_WITH_BAD_VALUE"; - - before(async () => { - const randomKeypair = Keypair.generate(); - - process.env[TEST_ENV_VAR_ARRAY_OF_NUMBERS] = JSON.stringify( - Object.values(randomKeypair.secretKey), - ); - - process.env[TEST_ENV_VAR_BASE58] = base58.encode(randomKeypair.secretKey); - - process.env[TEST_ENV_VAR_WITH_BAD_VALUE] = - "this isn't a valid value for a secret key"; - }); - - test("getting a keypair from an environment variable (array of numbers format)", async () => { - await getKeypairFromEnvironment(TEST_ENV_VAR_ARRAY_OF_NUMBERS); - }); - - test("getting a keypair from an environment variable (base58 format)", async () => { - await getKeypairFromEnvironment(TEST_ENV_VAR_BASE58); - }); - - test("throws a nice error if the env var doesn't exist", () => { - assert.throws(() => getKeypairFromEnvironment("MISSING_ENV_VAR"), { - message: `Please set 'MISSING_ENV_VAR' in environment.`, - }); - }); - - test("throws a nice error if the value of the env var isn't valid", () => { - assert.throws( - () => getKeypairFromEnvironment("TEST_ENV_VAR_WITH_BAD_VALUE"), - { - message: `Invalid secret key in environment variable 'TEST_ENV_VAR_WITH_BAD_VALUE'!`, - }, - ); - }); -}); - -describe("addKeypairToEnvFile", () => { - let TEST_ENV_VAR_ARRAY_OF_NUMBERS = "TEST_ENV_VAR_ARRAY_OF_NUMBERS"; - let testKeypair: Keypair; - - before(async () => { - testKeypair = Keypair.generate(); - - process.env[TEST_ENV_VAR_ARRAY_OF_NUMBERS] = JSON.stringify( - Object.values(testKeypair.secretKey), - ); - }); - - test("generates new keypair and writes to env if variable doesn't exist", async () => { - // We need to use a specific file name to avoid conflicts with other tests - const envFileName = ".env-unittest-addkeypairtoenvfile"; - await addKeypairToEnvFile(testKeypair, "TEMP_KEYPAIR", envFileName); - - // Now reload the environment and check it matches our test keypair - dotenv.config({ path: envFileName }); - - // Get the secret from the .env file - const secretKeyString = process.env["TEMP_KEYPAIR"]; - - if (!secretKeyString) { - throw new Error("TEMP_KEYPAIR not found in environment"); - } - const decodedSecretKey = Uint8Array.from(JSON.parse(secretKeyString)); - const envKeypair = Keypair.fromSecretKey(decodedSecretKey); - - assert.ok(envKeypair.secretKey); - - await deleteFile(envFileName); - }); - - test("throws a nice error if the env var already exists", async () => { - assert.rejects( - async () => - addKeypairToEnvFile(testKeypair, TEST_ENV_VAR_ARRAY_OF_NUMBERS), - { - message: `'TEST_ENV_VAR_ARRAY_OF_NUMBERS' already exists in env file.`, - }, - ); - }); -}); - -describe("initializeKeypair", () => { - const connection = new Connection(LOCALHOST); - const keypairVariableName = "INITIALIZE_KEYPAIR_TEST"; - - test("generates a new keypair and airdrops needed amount", async () => { - // We need to use a specific file name to avoid conflicts with other tests - const envFileName = ".env-unittest-initkeypair"; - const options: InitializeKeypairOptions = { - envFileName, - envVariableName: keypairVariableName, - }; - - const userBefore = await initializeKeypair(connection, options); - - // Check balance - const balanceBefore = await connection.getBalance(userBefore.publicKey); - assert.ok(balanceBefore > 0); - - // Check that the environment variable was created - dotenv.config({ path: envFileName }); - const secretKeyString = process.env[keypairVariableName]; - if (!secretKeyString) { - throw new Error(`${secretKeyString} not found in environment`); - } - - // Now reload the environment and check it matches our test keypair - const userAfter = await initializeKeypair(connection, options); - - // Check the keypair is the same - assert.ok(userBefore.publicKey.equals(userAfter.publicKey)); - - // Check balance has not changed - const balanceAfter = await connection.getBalance(userAfter.publicKey); - assert.equal(balanceBefore, balanceAfter); - - // Check there is a secret key - assert.ok(userAfter.secretKey); - - await deleteFile(envFileName); - }); -}); - -describe("airdropIfRequired", () => { - test("Checking the balance after airdropIfRequired", async () => { - const keypair = Keypair.generate(); - const connection = new Connection(LOCALHOST); - const originalBalance = await connection.getBalance(keypair.publicKey); - assert.equal(originalBalance, 0); - const lamportsToAirdrop = 1 * LAMPORTS_PER_SOL; - - const newBalance = await airdropIfRequired( - connection, - keypair.publicKey, - lamportsToAirdrop, - 1 * LAMPORTS_PER_SOL, - ); - - assert.equal(newBalance, lamportsToAirdrop); - - const recipient = Keypair.generate(); - - // Spend our SOL now to ensure we can use the airdrop immediately - await connection.sendTransaction( - new Transaction().add( - SystemProgram.transfer({ - fromPubkey: keypair.publicKey, - toPubkey: recipient.publicKey, - lamports: 500_000_000, - }), - ), - [keypair], - ); - }); - - test("doesn't request unnecessary airdrops", async () => { - const keypair = Keypair.generate(); - const connection = new Connection(LOCALHOST); - const originalBalance = await connection.getBalance(keypair.publicKey); - assert.equal(originalBalance, 0); - const lamportsToAirdrop = 1 * LAMPORTS_PER_SOL; - - await airdropIfRequired( - connection, - keypair.publicKey, - lamportsToAirdrop, - 500_000, - ); - const finalBalance = await airdropIfRequired( - connection, - keypair.publicKey, - lamportsToAirdrop, - 1 * LAMPORTS_PER_SOL, - ); - // Check second airdrop didn't happen (since we only had 1 sol) - assert.equal(finalBalance, 1 * lamportsToAirdrop); - }); - - test("airdropIfRequired does airdrop when necessary", async () => { - const keypair = Keypair.generate(); - const connection = new Connection(LOCALHOST); - const originalBalance = await connection.getBalance(keypair.publicKey); - assert.equal(originalBalance, 0); - // Get 999_999_999 lamports if we have less than 500_000 lamports - const lamportsToAirdrop = 1 * LAMPORTS_PER_SOL - 1; - await airdropIfRequired( - connection, - keypair.publicKey, - lamportsToAirdrop, - 500_000, - ); - // We only have 999_999_999 lamports, so we should need another airdrop - const finalBalance = await airdropIfRequired( - connection, - keypair.publicKey, - 1 * LAMPORTS_PER_SOL, - 1 * LAMPORTS_PER_SOL, - ); - // Check second airdrop happened - assert.equal(finalBalance, 2 * LAMPORTS_PER_SOL - 1); - }); -}); - -describe("getExplorerLink", () => { - test("getExplorerLink works for a block on mainnet", () => { - const link = getExplorerLink("block", "242233124", "mainnet-beta"); - assert.equal(link, "https://explorer.solana.com/block/242233124"); - }); - - test("getExplorerLink works for a block on mainnet when no network is supplied", () => { - const link = getExplorerLink("block", "242233124"); - assert.equal(link, "https://explorer.solana.com/block/242233124"); - }); - - test("getExplorerLink works for an address on mainnet", () => { - const link = getExplorerLink( - "address", - "dDCQNnDmNbFVi8cQhKAgXhyhXeJ625tvwsunRyRc7c8", - "mainnet-beta", - ); - assert.equal( - link, - "https://explorer.solana.com/address/dDCQNnDmNbFVi8cQhKAgXhyhXeJ625tvwsunRyRc7c8", - ); - }); - - test("getExplorerLink works for an address on devnet", () => { - const link = getExplorerLink( - "address", - "dDCQNnDmNbFVi8cQhKAgXhyhXeJ625tvwsunRyRc7c8", - "devnet", - ); - assert.equal( - link, - "https://explorer.solana.com/address/dDCQNnDmNbFVi8cQhKAgXhyhXeJ625tvwsunRyRc7c8?cluster=devnet", - ); - }); - - test("getExplorerLink works for a transaction on mainnet", () => { - const link = getExplorerLink( - "transaction", - "4nzNU7YxPtPsVzeg16oaZvLz4jMPtbAzavDfEFmemHNv93iYXKKYAaqBJzFCwEVxiULqTYYrbjPwQnA1d9ZCTELg", - "mainnet-beta", - ); - assert.equal( - link, - "https://explorer.solana.com/tx/4nzNU7YxPtPsVzeg16oaZvLz4jMPtbAzavDfEFmemHNv93iYXKKYAaqBJzFCwEVxiULqTYYrbjPwQnA1d9ZCTELg", - ); - }); - - test("getExplorerLink works for a block on mainnet", () => { - const link = getExplorerLink("block", "241889720", "mainnet-beta"); - assert.equal(link, "https://explorer.solana.com/block/241889720"); - }); - - test("getExplorerLink provides a localnet URL", () => { - const link = getExplorerLink( - "tx", - "2QC8BkDVZgaPHUXG9HuPw7aE5d6kN5DTRXLe2inT1NzurkYTCFhraSEo883CPNe18BZ2peJC1x1nojZ5Jmhs94pL", - "localnet", - ); - assert.equal( - link, - "https://explorer.solana.com/tx/2QC8BkDVZgaPHUXG9HuPw7aE5d6kN5DTRXLe2inT1NzurkYTCFhraSEo883CPNe18BZ2peJC1x1nojZ5Jmhs94pL?cluster=custom&customUrl=http%3A%2F%2Flocalhost%3A8899", - ); - }); -}); - -describe("makeKeypairs", () => { - test("makeKeypairs makes exactly the amount of keypairs requested", () => { - // We could test more, but keypair generation takes time and slows down tests - const KEYPAIRS_TO_MAKE = 3; - const keypairs = makeKeypairs(KEYPAIRS_TO_MAKE); - assert.equal(keypairs.length, KEYPAIRS_TO_MAKE); - assert.ok(keypairs[KEYPAIRS_TO_MAKE - 1].secretKey); - }); - - test("makeKeypairs() creates the correct number of keypairs", () => { - const keypairs = makeKeypairs(3); - assert.equal(keypairs.length, 3); - }); -}); - -describe("confirmTransaction", () => { - test("confirmTransaction works for a successful transaction", async () => { - const connection = new Connection(LOCALHOST); - const [sender, recipient] = [Keypair.generate(), Keypair.generate()]; - const lamportsToAirdrop = 2 * LAMPORTS_PER_SOL; - await airdropIfRequired( - connection, - sender.publicKey, - lamportsToAirdrop, - 1 * LAMPORTS_PER_SOL, - ); - - const transaction = await connection.sendTransaction( - new Transaction().add( - SystemProgram.transfer({ - fromPubkey: sender.publicKey, - toPubkey: recipient.publicKey, - lamports: 1_000_000, - }), - ), - [sender], - ); - - await confirmTransaction(connection, transaction); - }); -}); - -describe(`getLogs`, () => { - test(`getLogs works`, async () => { - const connection = new Connection(LOCALHOST); - const [sender, recipient] = [Keypair.generate(), Keypair.generate()]; - const lamportsToAirdrop = 2 * LAMPORTS_PER_SOL; - await airdropIfRequired( - connection, - sender.publicKey, - lamportsToAirdrop, - 1 * LAMPORTS_PER_SOL, - ); - - const transaction = await connection.sendTransaction( - new Transaction().add( - SystemProgram.transfer({ - fromPubkey: sender.publicKey, - toPubkey: recipient.publicKey, - lamports: 1_000_000, - }), - ), - [sender], - ); - - const logs = await getLogs(connection, transaction); - assert.deepEqual(logs, [ - "Program 11111111111111111111111111111111 invoke [1]", - "Program 11111111111111111111111111111111 success", - ]); - }); -}); - -describe("getSimulationComputeUnits", () => { - test("getSimulationComputeUnits returns 300 CUs for a SOL transfer, and 3888 for a SOL transfer with a memo", async () => { - const connection = new Connection(LOCALHOST); - const sender = Keypair.generate(); - await airdropIfRequired( - connection, - sender.publicKey, - 1 * LAMPORTS_PER_SOL, - 1 * LAMPORTS_PER_SOL, - ); - const recipient = Keypair.generate().publicKey; - - const sendSol = SystemProgram.transfer({ - fromPubkey: sender.publicKey, - toPubkey: recipient, - lamports: 1_000_000, - }); - - const sayThanks = new TransactionInstruction({ - keys: [], - programId: MEMO_PROGRAM_ID, - data: Buffer.from("thanks"), - }); - - const computeUnitsSendSol = await getSimulationComputeUnits( - connection, - [sendSol], - sender.publicKey, - [], - ); - - // TODO: it would be useful to have a breakdown of exactly how 300 CUs is calculated - assert.equal(computeUnitsSendSol, 300); - - const computeUnitsSendSolAndSayThanks = await getSimulationComputeUnits( - connection, - [sendSol, sayThanks], - sender.publicKey, - [], - ); - - // TODO: it would be useful to have a breakdown of exactly how 3888 CUs is calculated - // also worth reviewing why memo program seems to use so many CUs. - assert.equal(computeUnitsSendSolAndSayThanks, 3888); - }); -}); - -describe("makeTokenMint", () => { - test("makeTokenMint makes a new mint with the specified metadata", async () => { - const mintAuthority = Keypair.generate(); - const connection = new Connection(LOCALHOST); - await airdropIfRequired( - connection, - mintAuthority.publicKey, - 100 * LAMPORTS_PER_SOL, - 1 * LAMPORTS_PER_SOL, - ); - - const name = "Unit test token"; - const symbol = "TEST"; - const decimals = 9; - const uri = "https://example.com"; - const additionalMetadata = { - shlerm: "frobular", - glerp: "flerpy", - gurperderp: "erpy", - nurmagerd: "flerpy", - zurp: "flerpy", - eruper: "flerpy", - zerperurperserp: "flerpy", - zherp: "flerpy", - }; - - const mintAddress = await makeTokenMint( - connection, - mintAuthority, - name, - symbol, - decimals, - uri, - additionalMetadata, - ); - - assert.ok(mintAddress); - - const tokenMetadata = await getTokenMetadata(connection, mintAddress); - - if (!tokenMetadata) { - throw new Error( - `Token metadata not found for mint address ${mintAddress}`, - ); - } - - assert.equal(tokenMetadata.mint.toBase58(), mintAddress.toBase58()); - assert.equal( - tokenMetadata.updateAuthority?.toBase58(), - mintAuthority.publicKey.toBase58(), - ); - assert.equal(tokenMetadata.name, name); - assert.equal(tokenMetadata.symbol, symbol); - assert.equal(tokenMetadata.uri, uri); - assert.deepEqual( - tokenMetadata.additionalMetadata, - Object.entries(additionalMetadata), - ); - }); -}); - -describe("createAccountsMintsAndTokenAccounts", () => { - test("createAccountsMintsAndTokenAccounts works", async () => { - const payer = Keypair.generate(); - const connection = new Connection(LOCALHOST); - await airdropIfRequired( - connection, - payer.publicKey, - 100 * LAMPORTS_PER_SOL, - 1 * LAMPORTS_PER_SOL, - ); - - const SOL_BALANCE = 10 * LAMPORTS_PER_SOL; - - const usersMintsAndTokenAccounts = - await createAccountsMintsAndTokenAccounts( - [ - [1_000_000_000, 0], // User 0 has 1_000_000_000 of token A and 0 of token B - [0, 1_000_000_000], // User 1 has 0 of token A and 1_000_000_000 of token B - ], - SOL_BALANCE, - connection, - payer, - ); - - // Check all users have been created and have some SOL - const users = usersMintsAndTokenAccounts.users; - assert.equal(users.length, 2); - await Promise.all( - users.map(async (user) => { - const balance = await connection.getBalance(user.publicKey); - assert(balance === SOL_BALANCE); - }), - ); - - // Check the mints - assert.equal(usersMintsAndTokenAccounts.mints.length, 2); - - // Check the token accounts - const tokenAccounts = usersMintsAndTokenAccounts.tokenAccounts; - - // Get the balances of the token accounts for the first user - // (note there is no tokenAccountB balance yet) - const firstUserFirstTokenBalance = await connection.getTokenAccountBalance( - tokenAccounts[0][0], // First user, first token mint - ); - assert(Number(firstUserFirstTokenBalance.value.amount) === 1_000_000_000); - - // // Get the balances of the token accounts for the second user - // // (note there is no tokenAccountA account yet) - const secondUserSecondTokenBalance = - await connection.getTokenAccountBalance(tokenAccounts[1][1]); // Second user, second token mint - assert(Number(secondUserSecondTokenBalance.value.amount) === 1_000_000_000); - }); -}); diff --git a/src/lib/airdrop.ts b/src/lib/airdrop.ts index 9dea5c7..0ca5555 100644 --- a/src/lib/airdrop.ts +++ b/src/lib/airdrop.ts @@ -1,5 +1,5 @@ -import { Connection, Keypair, LAMPORTS_PER_SOL, PublicKey } from "@solana/web3.js"; -import { InitializeKeypairOptions } from "../types"; +import { type Connection, Keypair, LAMPORTS_PER_SOL, type PublicKey } from "@solana/web3.js"; +import type { InitializeKeypairOptions } from "../types"; import { addKeypairToEnvFile, getKeypairFromEnvironment, getKeypairFromFile } from "./keypair"; const DEFAULT_AIRDROP_AMOUNT = 1 * LAMPORTS_PER_SOL; @@ -13,7 +13,7 @@ export const initializeKeypair = async ( connection: Connection, options?: InitializeKeypairOptions, ): Promise => { - let { + const { keypairPath, envFileName, envVariableName = DEFAULT_ENV_KEYPAIR_VARIABLE_NAME, diff --git a/src/lib/keypair.ts b/src/lib/keypair.ts index 33be0f0..73fa27e 100644 --- a/src/lib/keypair.ts +++ b/src/lib/keypair.ts @@ -9,7 +9,7 @@ export const keypairToSecretKeyJSON = (keypair: Keypair): string => { }; export const getKeypairFromFile = async (filepath?: string) => { - const path = await import("path"); + const path = await import("node:path"); // Work out correct file name if (!filepath) { filepath = DEFAULT_FILEPATH; @@ -24,7 +24,7 @@ export const getKeypairFromFile = async (filepath?: string) => { // Get contents of file let fileContents: string; try { - const { readFile } = await import("fs/promises"); + const { readFile } = await import("node:fs/promises"); const fileContentsBuffer = await readFile(filepath); fileContents = fileContentsBuffer.toString(); } catch (error) { @@ -81,7 +81,7 @@ export const addKeypairToEnvFile = async ( variableName: string, envFileName?: string, ) => { - const { appendFile } = await import("fs/promises"); + const { appendFile } = await import("node:fs/promises"); if (!envFileName) { envFileName = ".env"; } diff --git a/tests/package.json b/tests/package.json new file mode 100644 index 0000000..f939223 --- /dev/null +++ b/tests/package.json @@ -0,0 +1,10 @@ +{ + "scripts": { + "clean": "rm -rf dist", + "test": "npm run test:esm", + "test:ci": "npm run test:cjs && npm run test:esm", + "pretest:cjs": "npm run clean && tsc -p tsconfig.test.json", + "test:cjs": "node --test dist/tests/src/**/*.test.js", + "test:esm": "node --test --require esbuild-register src/**" + } +} diff --git a/tests/src/airdrop.test.ts b/tests/src/airdrop.test.ts new file mode 100644 index 0000000..08cef61 --- /dev/null +++ b/tests/src/airdrop.test.ts @@ -0,0 +1,135 @@ +import { describe, test } from "node:test"; +import { Connection } from "@solana/web3.js"; +import { airdropIfRequired, initializeKeypair, type InitializeKeypairOptions } from "../../src"; +import assert from "node:assert"; +import dotenv from "dotenv"; +import { unlink as deleteFile } from "node:fs/promises" +import { LAMPORTS_PER_SOL } from "@solana/web3.js"; +import { Keypair } from "@solana/web3.js"; +import { SystemProgram } from "@solana/web3.js"; +import { Transaction } from "@solana/web3.js"; +import { sendAndConfirmTransaction } from "@solana/web3.js"; + +const LOCALHOST = "http://127.0.0.1:8899"; + +describe("initializeKeypair", () => { + const connection = new Connection(LOCALHOST); + const keypairVariableName = "INITIALIZE_KEYPAIR_TEST"; + + test("generates a new keypair and airdrops needed amount", async () => { + // We need to use a specific file name to avoid conflicts with other tests + const envFileName = "../.env-unittest-initkeypair"; + const options: InitializeKeypairOptions = { + envFileName, + envVariableName: keypairVariableName, + }; + + const userBefore = await initializeKeypair(connection, options); + + // Check balance + const balanceBefore = await connection.getBalance(userBefore.publicKey); + assert.ok(balanceBefore > 0); + + // Check that the environment variable was created + dotenv.config({ path: envFileName }); + const secretKeyString = process.env[keypairVariableName]; + if (!secretKeyString) { + throw new Error(`${secretKeyString} not found in environment`); + } + + // Now reload the environment and check it matches our test keypair + const userAfter = await initializeKeypair(connection, options); + + // Check the keypair is the same + assert.ok(userBefore.publicKey.equals(userAfter.publicKey)); + + // Check balance has not changed + const balanceAfter = await connection.getBalance(userAfter.publicKey); + assert.equal(balanceBefore, balanceAfter); + + // Check there is a secret key + assert.ok(userAfter.secretKey); + + await deleteFile(envFileName); + }); +}); + +describe("airdropIfRequired", () => { + test("Checking the balance after airdropIfRequired", async () => { + const keypair = Keypair.generate(); + const connection = new Connection(LOCALHOST); + const originalBalance = await connection.getBalance(keypair.publicKey); + assert.equal(originalBalance, 0); + const lamportsToAirdrop = 1 * LAMPORTS_PER_SOL; + + const newBalance = await airdropIfRequired( + connection, + keypair.publicKey, + lamportsToAirdrop, + 1 * LAMPORTS_PER_SOL, + ); + + assert.equal(newBalance, lamportsToAirdrop); + + const recipient = Keypair.generate(); + + // Spend our SOL now to ensure we can use the airdrop immediately + await sendAndConfirmTransaction(connection, + new Transaction().add( + SystemProgram.transfer({ + fromPubkey: keypair.publicKey, + toPubkey: recipient.publicKey, + lamports: 500_000_000, + }), + ), + [keypair], + ) + }); + + test("doesn't request unnecessary airdrops", async () => { + const keypair = Keypair.generate(); + const connection = new Connection(LOCALHOST); + const originalBalance = await connection.getBalance(keypair.publicKey); + assert.equal(originalBalance, 0); + const lamportsToAirdrop = 1 * LAMPORTS_PER_SOL; + + await airdropIfRequired( + connection, + keypair.publicKey, + lamportsToAirdrop, + 500_000, + ); + const finalBalance = await airdropIfRequired( + connection, + keypair.publicKey, + lamportsToAirdrop, + 1 * LAMPORTS_PER_SOL, + ); + // Check second airdrop didn't happen (since we only had 1 sol) + assert.equal(finalBalance, 1 * lamportsToAirdrop); + }); + + test("airdropIfRequired does airdrop when necessary", async () => { + const keypair = Keypair.generate(); + const connection = new Connection(LOCALHOST); + const originalBalance = await connection.getBalance(keypair.publicKey); + assert.equal(originalBalance, 0); + // Get 999_999_999 lamports if we have less than 500_000 lamports + const lamportsToAirdrop = 1 * LAMPORTS_PER_SOL - 1; + await airdropIfRequired( + connection, + keypair.publicKey, + lamportsToAirdrop, + 500_000, + ); + // We only have 999_999_999 lamports, so we should need another airdrop + const finalBalance = await airdropIfRequired( + connection, + keypair.publicKey, + 1 * LAMPORTS_PER_SOL, + 1 * LAMPORTS_PER_SOL, + ); + // Check second airdrop happened + assert.equal(finalBalance, 2 * LAMPORTS_PER_SOL - 1); + }); +}); diff --git a/tests/src/explorer.test.ts b/tests/src/explorer.test.ts new file mode 100644 index 0000000..503b6ed --- /dev/null +++ b/tests/src/explorer.test.ts @@ -0,0 +1,68 @@ +import { getExplorerLink } from "../../src/index"; +import { describe, test } from "node:test"; +import assert from "node:assert"; + +describe("getExplorerLink", () => { + test("getExplorerLink works for a block on mainnet", () => { + const link = getExplorerLink("block", "242233124", "mainnet-beta"); + assert.equal(link, "https://explorer.solana.com/block/242233124"); + }); + + test("getExplorerLink works for a block on mainnet when no network is supplied", () => { + const link = getExplorerLink("block", "242233124"); + assert.equal(link, "https://explorer.solana.com/block/242233124"); + }); + + test("getExplorerLink works for an address on mainnet", () => { + const link = getExplorerLink( + "address", + "dDCQNnDmNbFVi8cQhKAgXhyhXeJ625tvwsunRyRc7c8", + "mainnet-beta", + ); + assert.equal( + link, + "https://explorer.solana.com/address/dDCQNnDmNbFVi8cQhKAgXhyhXeJ625tvwsunRyRc7c8", + ); + }); + + test("getExplorerLink works for an address on devnet", () => { + const link = getExplorerLink( + "address", + "dDCQNnDmNbFVi8cQhKAgXhyhXeJ625tvwsunRyRc7c8", + "devnet", + ); + assert.equal( + link, + "https://explorer.solana.com/address/dDCQNnDmNbFVi8cQhKAgXhyhXeJ625tvwsunRyRc7c8?cluster=devnet", + ); + }); + + test("getExplorerLink works for a transaction on mainnet", () => { + const link = getExplorerLink( + "transaction", + "4nzNU7YxPtPsVzeg16oaZvLz4jMPtbAzavDfEFmemHNv93iYXKKYAaqBJzFCwEVxiULqTYYrbjPwQnA1d9ZCTELg", + "mainnet-beta", + ); + assert.equal( + link, + "https://explorer.solana.com/tx/4nzNU7YxPtPsVzeg16oaZvLz4jMPtbAzavDfEFmemHNv93iYXKKYAaqBJzFCwEVxiULqTYYrbjPwQnA1d9ZCTELg", + ); + }); + + test("getExplorerLink works for a block on mainnet", () => { + const link = getExplorerLink("block", "241889720", "mainnet-beta"); + assert.equal(link, "https://explorer.solana.com/block/241889720"); + }); + + test("getExplorerLink provides a localnet URL", () => { + const link = getExplorerLink( + "tx", + "2QC8BkDVZgaPHUXG9HuPw7aE5d6kN5DTRXLe2inT1NzurkYTCFhraSEo883CPNe18BZ2peJC1x1nojZ5Jmhs94pL", + "localnet", + ); + assert.equal( + link, + "https://explorer.solana.com/tx/2QC8BkDVZgaPHUXG9HuPw7aE5d6kN5DTRXLe2inT1NzurkYTCFhraSEo883CPNe18BZ2peJC1x1nojZ5Jmhs94pL?cluster=custom&customUrl=http%3A%2F%2Flocalhost%3A8899", + ); + }); +}); diff --git a/tests/src/keypair.test.ts b/tests/src/keypair.test.ts new file mode 100644 index 0000000..e8d69b6 --- /dev/null +++ b/tests/src/keypair.test.ts @@ -0,0 +1,145 @@ +import { describe, test, before } from "node:test"; +import assert from "node:assert"; +import { addKeypairToEnvFile, getKeypairFromEnvironment, getKeypairFromFile, makeKeypairs } from "../../src"; +import { Keypair } from "@solana/web3.js"; +import base58 from "bs58"; +// See https://m.media-amazon.com/images/I/51TJeGHxyTL._SY445_SX342_.jpg +import { exec as execNoPromises } from "node:child_process"; +import { promisify } from "node:util"; +import { writeFile, unlink as deleteFile } from "node:fs/promises"; +import dotenv from "dotenv"; + +const exec = promisify(execNoPromises); +const TEMP_DIR = "temp"; + +describe("addKeypairToEnvFile", () => { + const TEST_ENV_VAR_ARRAY_OF_NUMBERS = "TEST_ENV_VAR_ARRAY_OF_NUMBERS"; + let testKeypair: Keypair; + + before(async () => { + testKeypair = Keypair.generate(); + + process.env[TEST_ENV_VAR_ARRAY_OF_NUMBERS] = JSON.stringify( + Object.values(testKeypair.secretKey), + ); + }); + + test("generates new keypair and writes to env if variable doesn't exist", async () => { + // We need to use a specific file name to avoid conflicts with other tests + const envFileName = "../.env-unittest-addkeypairtoenvfile"; + await addKeypairToEnvFile(testKeypair, "TEMP_KEYPAIR", envFileName); + + // Now reload the environment and check it matches our test keypair + dotenv.config({ path: envFileName }); + + // Get the secret from the .env file + const secretKeyString = process.env.TEMP_KEYPAIR; + + if (!secretKeyString) { + throw new Error("TEMP_KEYPAIR not found in environment"); + } + const decodedSecretKey = Uint8Array.from(JSON.parse(secretKeyString)); + const envKeypair = Keypair.fromSecretKey(decodedSecretKey); + + assert.ok(envKeypair.secretKey); + + await deleteFile(envFileName); + }); + + test("throws a nice error if the env var already exists", async () => { + assert.rejects( + async () => + addKeypairToEnvFile(testKeypair, TEST_ENV_VAR_ARRAY_OF_NUMBERS), + { + message: `'TEST_ENV_VAR_ARRAY_OF_NUMBERS' already exists in env file.`, + }, + ); + }); +}); + +describe("makeKeypairs", () => { + test("makeKeypairs makes exactly the amount of keypairs requested", () => { + // We could test more, but keypair generation takes time and slows down tests + const KEYPAIRS_TO_MAKE = 3; + const keypairs = makeKeypairs(KEYPAIRS_TO_MAKE); + assert.equal(keypairs.length, KEYPAIRS_TO_MAKE); + assert.ok(keypairs[KEYPAIRS_TO_MAKE - 1].secretKey); + }); + + test("makeKeypairs() creates the correct number of keypairs", () => { + const keypairs = makeKeypairs(3); + assert.equal(keypairs.length, 3); + }); +}); + +describe("getKeypairFromFile", () => { + const TEST_FILE_NAME = `${TEMP_DIR}/test-keyfile-do-not-use.json`; + const MISSING_FILE_NAME = "THIS FILE DOES NOT EXIST"; + const CORRUPT_TEST_FILE_NAME = `${TEMP_DIR}/corrupt-keyfile-do-not-use.json`; + before(async () => { + const { stdout } = await exec( + `solana-keygen new --force --no-bip39-passphrase -o ${TEST_FILE_NAME}`, + ); + assert(stdout.includes("Wrote new keypair")); + + await writeFile(CORRUPT_TEST_FILE_NAME, "I AM CORRUPT"); + }); + + test("getting a keypair from a file", async () => { + await getKeypairFromFile(TEST_FILE_NAME); + }); + + test("throws a nice error if the file is missing", async () => { + assert.rejects(async () => await getKeypairFromFile(MISSING_FILE_NAME), { + message: `Could not read keypair from file at '${MISSING_FILE_NAME}'`, + }); + }); + + test("throws a nice error if the file is corrupt", async () => { + assert.rejects(() => getKeypairFromFile(CORRUPT_TEST_FILE_NAME), { + message: `Invalid secret key file at '${CORRUPT_TEST_FILE_NAME}'!`, + }); + }); +}); + +describe("getKeypairFromEnvironment", () => { + const TEST_ENV_VAR_ARRAY_OF_NUMBERS = "TEST_ENV_VAR_ARRAY_OF_NUMBERS"; + const TEST_ENV_VAR_BASE58 = "TEST_ENV_VAR_BASE58"; + const TEST_ENV_VAR_WITH_BAD_VALUE = "TEST_ENV_VAR_WITH_BAD_VALUE"; + + before(async () => { + const randomKeypair = Keypair.generate(); + + process.env[TEST_ENV_VAR_ARRAY_OF_NUMBERS] = JSON.stringify( + Object.values(randomKeypair.secretKey), + ); + + process.env[TEST_ENV_VAR_BASE58] = base58.encode(randomKeypair.secretKey); + + process.env[TEST_ENV_VAR_WITH_BAD_VALUE] = + "this isn't a valid value for a secret key"; + }); + + test("getting a keypair from an environment variable (array of numbers format)", async () => { + await getKeypairFromEnvironment(TEST_ENV_VAR_ARRAY_OF_NUMBERS); + }); + + test("getting a keypair from an environment variable (base58 format)", async () => { + await getKeypairFromEnvironment(TEST_ENV_VAR_BASE58); + }); + + test("throws a nice error if the env var doesn't exist", () => { + assert.throws(() => getKeypairFromEnvironment("MISSING_ENV_VAR"), { + message: `Please set 'MISSING_ENV_VAR' in environment.`, + }); + }); + + test("throws a nice error if the value of the env var isn't valid", () => { + assert.throws( + () => getKeypairFromEnvironment("TEST_ENV_VAR_WITH_BAD_VALUE"), + { + message: `Invalid secret key in environment variable 'TEST_ENV_VAR_WITH_BAD_VALUE'!`, + }, + ); + }); +}); \ No newline at end of file diff --git a/tests/src/logs.test.ts b/tests/src/logs.test.ts new file mode 100644 index 0000000..444696b --- /dev/null +++ b/tests/src/logs.test.ts @@ -0,0 +1,76 @@ +import { describe, test } from "node:test"; +import { Connection } from "@solana/web3.js"; +import { airdropIfRequired, getCustomErrorMessage, getLogs } from "../../src"; +import { LAMPORTS_PER_SOL } from "@solana/web3.js"; +import { Keypair } from "@solana/web3.js"; +import { Transaction } from "@solana/web3.js"; +import { SystemProgram } from "@solana/web3.js"; +import assert from "node:assert"; + +const LOCALHOST = "http://127.0.0.1:8899"; + +describe("getLogs", () => { + test("getLogs works", async () => { + const connection = new Connection(LOCALHOST); + const [sender, recipient] = [Keypair.generate(), Keypair.generate()]; + const lamportsToAirdrop = 2 * LAMPORTS_PER_SOL; + await airdropIfRequired( + connection, + sender.publicKey, + lamportsToAirdrop, + 1 * LAMPORTS_PER_SOL, + ); + + const transaction = await connection.sendTransaction( + new Transaction().add( + SystemProgram.transfer({ + fromPubkey: sender.publicKey, + toPubkey: recipient.publicKey, + lamports: 1_000_000, + }), + ), + [sender], + ); + + const logs = await getLogs(connection, transaction); + assert.deepEqual(logs, [ + "Program 11111111111111111111111111111111 invoke [1]", + "Program 11111111111111111111111111111111 success", + ]); + }); +}); + + +describe("getCustomErrorMessage", () => { + test("we turn error messages with hex codes into error messages for the program", () => { + // This example set of error is from the token program + // https://github.com/solana-labs/solana-program-library/blob/master/token/program/src/error.rs + const programErrors = [ + "Lamport balance below rent-exempt threshold", + "Insufficient funds", + "Invalid Mint", + "Account not associated with this Mint", + "Owner does not match", + "Fixed supply", + "Already in use", + "Invalid number of provided signers", + "Invalid number of required signers", + "State is unititialized", + "Instruction does not support native tokens", + "Non-native account can only be closed if its balance is zero", + "Invalid instruction", + "State is invalid for requested operation", + "Operation overflowed", + "Account does not support specified authority type", + "This token mint cannot freeze accounts", + "Account is frozen", + "The provided decimals value different from the Mint decimals", + "Instruction does not support non-native tokens", + ]; + const errorMessage = getCustomErrorMessage( + programErrors, + "failed to send transaction: Transaction simulation failed: Error processing Instruction 0: custom program error: 0x10", + ); + assert.equal(errorMessage, "This token mint cannot freeze accounts"); + }); +}); \ No newline at end of file diff --git a/tests/src/token.test.ts b/tests/src/token.test.ts new file mode 100644 index 0000000..6e3dfe6 --- /dev/null +++ b/tests/src/token.test.ts @@ -0,0 +1,125 @@ +import { describe, test } from "node:test"; +import { LAMPORTS_PER_SOL } from "@solana/web3.js"; +import { airdropIfRequired, createAccountsMintsAndTokenAccounts, makeTokenMint } from "../../src"; +import { Connection } from "@solana/web3.js"; +import { Keypair } from "@solana/web3.js"; +import { getTokenMetadata } from "@solana/spl-token"; +import assert from 'node:assert'; + +const LOCALHOST = "http://127.0.0.1:8899"; + +describe("makeTokenMint", () => { + test("makeTokenMint makes a new mint with the specified metadata", async () => { + const mintAuthority = Keypair.generate(); + const connection = new Connection(LOCALHOST); + await airdropIfRequired( + connection, + mintAuthority.publicKey, + 100 * LAMPORTS_PER_SOL, + 1 * LAMPORTS_PER_SOL, + ); + + const name = "Unit test token"; + const symbol = "TEST"; + const decimals = 9; + const uri = "https://example.com"; + const additionalMetadata = { + shlerm: "frobular", + glerp: "flerpy", + gurperderp: "erpy", + nurmagerd: "flerpy", + zurp: "flerpy", + eruper: "flerpy", + zerperurperserp: "flerpy", + zherp: "flerpy", + }; + + const mintAddress = await makeTokenMint( + connection, + mintAuthority, + name, + symbol, + decimals, + uri, + additionalMetadata, + ); + + assert.ok(mintAddress); + + const tokenMetadata = await getTokenMetadata(connection, mintAddress); + + if (!tokenMetadata) { + throw new Error( + `Token metadata not found for mint address ${mintAddress}`, + ); + } + + assert.equal(tokenMetadata.mint.toBase58(), mintAddress.toBase58()); + assert.equal( + tokenMetadata.updateAuthority?.toBase58(), + mintAuthority.publicKey.toBase58(), + ); + assert.equal(tokenMetadata.name, name); + assert.equal(tokenMetadata.symbol, symbol); + assert.equal(tokenMetadata.uri, uri); + assert.deepEqual( + tokenMetadata.additionalMetadata, + Object.entries(additionalMetadata), + ); + }); +}); + +describe("createAccountsMintsAndTokenAccounts", () => { + test("createAccountsMintsAndTokenAccounts works", async () => { + const payer = Keypair.generate(); + const connection = new Connection(LOCALHOST); + await airdropIfRequired( + connection, + payer.publicKey, + 100 * LAMPORTS_PER_SOL, + 1 * LAMPORTS_PER_SOL, + ); + + const SOL_BALANCE = 10 * LAMPORTS_PER_SOL; + + const usersMintsAndTokenAccounts = + await createAccountsMintsAndTokenAccounts( + [ + [1_000_000_000, 0], // User 0 has 1_000_000_000 of token A and 0 of token B + [0, 1_000_000_000], // User 1 has 0 of token A and 1_000_000_000 of token B + ], + SOL_BALANCE, + connection, + payer, + ); + + // Check all users have been created and have some SOL + const users = usersMintsAndTokenAccounts.users; + assert.equal(users.length, 2); + await Promise.all( + users.map(async (user) => { + const balance = await connection.getBalance(user.publicKey); + assert(balance === SOL_BALANCE); + }), + ); + + // Check the mints + assert.equal(usersMintsAndTokenAccounts.mints.length, 2); + + // Check the token accounts + const tokenAccounts = usersMintsAndTokenAccounts.tokenAccounts; + + // Get the balances of the token accounts for the first user + // (note there is no tokenAccountB balance yet) + const firstUserFirstTokenBalance = await connection.getTokenAccountBalance( + tokenAccounts[0][0], // First user, first token mint + ); + assert(Number(firstUserFirstTokenBalance.value.amount) === 1_000_000_000); + + // // Get the balances of the token accounts for the second user + // // (note there is no tokenAccountA account yet) + const secondUserSecondTokenBalance = + await connection.getTokenAccountBalance(tokenAccounts[1][1]); // Second user, second token mint + assert(Number(secondUserSecondTokenBalance.value.amount) === 1_000_000_000); + }); +}); \ No newline at end of file diff --git a/tests/src/transaction.test.ts b/tests/src/transaction.test.ts new file mode 100644 index 0000000..3bf0481 --- /dev/null +++ b/tests/src/transaction.test.ts @@ -0,0 +1,90 @@ +import { describe, test } from "node:test"; +import { Keypair } from "@solana/web3.js"; +import { LAMPORTS_PER_SOL } from "@solana/web3.js"; +import { Connection } from "@solana/web3.js"; +import { airdropIfRequired, confirmTransaction, getSimulationComputeUnits } from "../../src"; +import { sendAndConfirmTransaction } from "@solana/web3.js"; +import { Transaction } from "@solana/web3.js"; +import { SystemProgram } from "@solana/web3.js"; +import assert from "node:assert"; +import { TransactionInstruction } from "@solana/web3.js"; +import { PublicKey } from "@solana/web3.js"; + +const LOCALHOST = "http://127.0.0.1:8899"; +const MEMO_PROGRAM_ID = new PublicKey( + "MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr", +); + +describe("confirmTransaction", () => { + test("confirmTransaction works for a successful transaction", async () => { + const connection = new Connection(LOCALHOST); + const [sender, recipient] = [Keypair.generate(), Keypair.generate()]; + const lamportsToAirdrop = 2 * LAMPORTS_PER_SOL; + await airdropIfRequired( + connection, + sender.publicKey, + lamportsToAirdrop, + 1 * LAMPORTS_PER_SOL, + ); + + const signature = await sendAndConfirmTransaction(connection, + new Transaction().add( + SystemProgram.transfer({ + fromPubkey: sender.publicKey, + toPubkey: recipient.publicKey, + lamports: 1_000_000, + }), + ), + [sender], + ); + + await confirmTransaction(connection, signature); + }); +}); + +describe("getSimulationComputeUnits", () => { + test("getSimulationComputeUnits returns 300 CUs for a SOL transfer, and 3888 for a SOL transfer with a memo", async () => { + const connection = new Connection(LOCALHOST); + const sender = Keypair.generate(); + await airdropIfRequired( + connection, + sender.publicKey, + 1 * LAMPORTS_PER_SOL, + 1 * LAMPORTS_PER_SOL, + ); + const recipient = Keypair.generate().publicKey; + + const sendSol = SystemProgram.transfer({ + fromPubkey: sender.publicKey, + toPubkey: recipient, + lamports: 1_000_000, + }); + + const sayThanks = new TransactionInstruction({ + keys: [], + programId: MEMO_PROGRAM_ID, + data: Buffer.from("thanks"), + }); + + const computeUnitsSendSol = await getSimulationComputeUnits( + connection, + [sendSol], + sender.publicKey, + [], + ); + + // TODO: it would be useful to have a breakdown of exactly how 300 CUs is calculated + assert.equal(computeUnitsSendSol, 300); + + const computeUnitsSendSolAndSayThanks = await getSimulationComputeUnits( + connection, + [sendSol, sayThanks], + sender.publicKey, + [], + ); + + // TODO: it would be useful to have a breakdown of exactly how 3888 CUs is calculated + // also worth reviewing why memo program seems to use so many CUs. + assert.equal(computeUnitsSendSolAndSayThanks, 3888); + }); +}); \ No newline at end of file diff --git a/tests/tsconfig.test.json b/tests/tsconfig.test.json new file mode 100644 index 0000000..3626865 --- /dev/null +++ b/tests/tsconfig.test.json @@ -0,0 +1,20 @@ +{ + "extends": "../tsconfig.json", + "compilerOptions": { + "module": "CommonJS", + "target": "ES2022", + "moduleResolution": "node", + "rootDir": "../", + "outDir": "../tests/dist", + "types": ["node"], + "esModuleInterop": true + }, + "include": ["src/**/*", "tests/src/*"], + "exclude": [ + "node_modules", + "dist", + "tests/bankrun_test", + "coverage/**", + "**/*.d.ts" + ] +} \ No newline at end of file