From 0e4ec1e2618cba4641f01aadbf852b6f9d8183cd Mon Sep 17 00:00:00 2001 From: Hugo C <911307+hugocaillard@users.noreply.github.com> Date: Thu, 21 Sep 2023 18:28:05 +0200 Subject: [PATCH] feat: clarinet-sdk: vitest config and custom matchers (#1151) * feat: vitest config and helpers for clarinet projects * feat: implement clarity value formatter * feat: improve and actually use cv formatter * chore: preparing for the release * feat: improve sdk tx handling for transfer stx and deploy contract * refactor: remove dead code * chore: upgrade dependencies * fix: parse json clarity args * refactor: self review * refactor: improve types * fix: remove pub function * chore: prep release * docs: typo Co-authored-by: Micaiah Reid * docs: typo Co-authored-by: Micaiah Reid * refactor: type * chore: upgrade dependencies * docs: update readme * chore: restore deleted deployment plans * docs: update sdk readme * fix: use the right packaged name * docs: update components/clarinet-sdk/README.md --------- Co-authored-by: Micaiah Reid --- Cargo.lock | 39 +- .../deployments/default.devnet-plan.yaml | 11 + .../deployments/increment.devnet-plan.yaml | 2 +- components/clarinet-sdk/Cargo.toml | 8 +- components/clarinet-sdk/README.md | 177 ++- .../clarinet-sdk/clarinet-sdk.code-workspace | 2 - components/clarinet-sdk/package-lock.json | 754 +++++++--- components/clarinet-sdk/package.json | 34 +- components/clarinet-sdk/src-ts/index.ts | 147 +- .../clarinet-sdk/src-ts/vitest/index.ts | 23 + components/clarinet-sdk/src/core.rs | 305 ++-- components/clarinet-sdk/tsconfig.cjs.json | 2 +- .../{tsconfig.esm.json => tsconfig.json} | 0 .../clarinet-sdk/vitest-helpers/README.md | 11 + .../vitest-helpers/package-lock.json | 1264 +++++++++++++++++ .../clarinet-sdk/vitest-helpers/package.json | 22 + .../src/clarityValuesMatchers.ts | 377 +++++ .../vitest-helpers/src/formatCV.ts | 130 ++ .../vitest-helpers/src/global.d.ts | 13 + .../vitest-helpers/src/vitest.d.ts | 28 + .../vitest-helpers/src/vitest.setup.ts | 29 + .../tests/clarityValueMatchers.test.ts | 381 +++++ .../vitest-helpers/tests/formatCV.test.ts | 138 ++ .../clarinet-sdk/vitest-helpers/tsconfig.json | 26 + components/clarinet-sdk/webpack.config.dev.js | 23 +- components/clarinet-sdk/webpack.config.js | 18 +- 26 files changed, 3559 insertions(+), 405 deletions(-) create mode 100644 components/clarinet-sdk/src-ts/vitest/index.ts rename components/clarinet-sdk/{tsconfig.esm.json => tsconfig.json} (100%) create mode 100644 components/clarinet-sdk/vitest-helpers/README.md create mode 100644 components/clarinet-sdk/vitest-helpers/package-lock.json create mode 100644 components/clarinet-sdk/vitest-helpers/package.json create mode 100644 components/clarinet-sdk/vitest-helpers/src/clarityValuesMatchers.ts create mode 100644 components/clarinet-sdk/vitest-helpers/src/formatCV.ts create mode 100644 components/clarinet-sdk/vitest-helpers/src/global.d.ts create mode 100644 components/clarinet-sdk/vitest-helpers/src/vitest.d.ts create mode 100644 components/clarinet-sdk/vitest-helpers/src/vitest.setup.ts create mode 100644 components/clarinet-sdk/vitest-helpers/tests/clarityValueMatchers.test.ts create mode 100644 components/clarinet-sdk/vitest-helpers/tests/formatCV.test.ts create mode 100644 components/clarinet-sdk/vitest-helpers/tsconfig.json diff --git a/Cargo.lock b/Cargo.lock index 1747102be..2420a49aa 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1015,7 +1015,7 @@ dependencies = [ "js-sys", "libsecp256k1 0.7.1", "serde", - "serde-wasm-bindgen", + "serde-wasm-bindgen 0.5.0", "serde_derive", "serde_json", "tiny-hderive", @@ -1033,9 +1033,10 @@ dependencies = [ "clarinet-files", "clarity-repl 1.8.0", "console_error_panic_hook", + "gloo-utils", "js-sys", "serde", - "serde-wasm-bindgen", + "serde-wasm-bindgen 0.6.0", "serde_json", "wasm-bindgen", "wasm-bindgen-futures", @@ -1110,7 +1111,7 @@ dependencies = [ "lsp-types", "regex", "serde", - "serde-wasm-bindgen", + "serde-wasm-bindgen 0.5.0", "serde_json", "wasm-bindgen", "wasm-bindgen-futures", @@ -2721,6 +2722,19 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" +[[package]] +name = "gloo-utils" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b5555354113b18c547c1d3a98fbf7fb32a9ff4f6fa112ce823a21641a0ba3aa" +dependencies = [ + "js-sys", + "serde", + "serde_json", + "wasm-bindgen", + "web-sys", +] + [[package]] name = "group" version = "0.12.1" @@ -5276,9 +5290,9 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.183" +version = "1.0.188" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32ac8da02677876d532745a130fc9d8e6edfa81a269b107c5b00829b91d8eb3c" +checksum = "cf9e0fcba69a370eed61bcf2b728575f726b50b55cba78064753d708ddc7549e" dependencies = [ "serde_derive", ] @@ -5305,6 +5319,17 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "serde-wasm-bindgen" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30c9933e5689bd420dc6c87b7a1835701810cbc10cd86a26e4da45b73e6b1d78" +dependencies = [ + "js-sys", + "serde", + "wasm-bindgen", +] + [[package]] name = "serde_bytes" version = "0.11.9" @@ -5326,9 +5351,9 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.183" +version = "1.0.188" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aafe972d60b0b9bee71a91b92fee2d4fb3c9d7e8f6b179aa99f27203d99a4816" +checksum = "4eca7ac642d82aa35b60049a6eccb4be6be75e599bd2e9adb5f875a737654af2" dependencies = [ "proc-macro2 1.0.66", "quote 1.0.28", diff --git a/components/clarinet-cli/examples/counter/deployments/default.devnet-plan.yaml b/components/clarinet-cli/examples/counter/deployments/default.devnet-plan.yaml index 95ea47e93..53689a630 100644 --- a/components/clarinet-cli/examples/counter/deployments/default.devnet-plan.yaml +++ b/components/clarinet-cli/examples/counter/deployments/default.devnet-plan.yaml @@ -15,3 +15,14 @@ plan: path: contracts/counter.clar anchor-block-only: true clarity-version: 1 + epoch: "2.0" + - id: 1 + transactions: + - contract-publish: + contract-name: counter-2 + expected-sender: ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM + cost: 6940 + path: contracts/counter-v2.clar + anchor-block-only: true + clarity-version: 2 + epoch: "2.4" diff --git a/components/clarinet-cli/examples/counter/deployments/increment.devnet-plan.yaml b/components/clarinet-cli/examples/counter/deployments/increment.devnet-plan.yaml index 96975d7cb..ab17f30a1 100644 --- a/components/clarinet-cli/examples/counter/deployments/increment.devnet-plan.yaml +++ b/components/clarinet-cli/examples/counter/deployments/increment.devnet-plan.yaml @@ -14,4 +14,4 @@ plan: method: increment parameters: - u1 - cost: 5960 \ No newline at end of file + cost: 5960 diff --git a/components/clarinet-sdk/Cargo.toml b/components/clarinet-sdk/Cargo.toml index 169599996..4693c5126 100644 --- a/components/clarinet-sdk/Cargo.toml +++ b/components/clarinet-sdk/Cargo.toml @@ -9,15 +9,15 @@ name = "clarinet_sdk" path = "src/lib.rs" [dependencies] -serde = { version = "1.0.136", features = ["derive"] } -serde_json = "1.0" clarinet-files = { path = "../clarinet-files", default-features = false } clarity-repl = { path = "../clarity-repl", default-features = false, optional = true } clarinet-deployments = { path = "../clarinet-deployments", default-features = false } -# WASM console_error_panic_hook = { version = "0.1", optional = true } +gloo-utils = { version = "0.2", features = ["serde"] } js-sys = { version = "0.3", optional = true } -serde-wasm-bindgen = { version = "0.5", optional = true } +serde = { version = "1.0.136", features = ["derive"] } +serde_json = "1.0" +serde-wasm-bindgen = { version = "0.6.0", optional = true } wasm-bindgen = { version = "0.2", optional = true } wasm-bindgen-futures = { version = "0.4", optional = true } web-sys = { version = "0.3", features = ["console"], optional = true } diff --git a/components/clarinet-sdk/README.md b/components/clarinet-sdk/README.md index 12eabeab3..41d5e391e 100644 --- a/components/clarinet-sdk/README.md +++ b/components/clarinet-sdk/README.md @@ -1,20 +1,33 @@ # Clarinet SDK +The Clarinet SDK can be used to interact with the simnet from Node.js. + +You can use this SDK to: +- call public and read-only functions from smart contracts +- get clarity maps or data-var values +- deploy contracts +- get contract ABI +- write unit tests for Clarity smart contracts + ## Core +``` +npm install @hirosystems/clarinet-sdk +``` + ### Usage ```ts +import { initVM } from "@hirosystems/clarinet-sdk"; import { Cl } from "@stacks/transactions"; -import { initVM } from "obscurity-sdk"; async function main() { const vm = await initVM(); + const accounts = vm.getAccounts(); - const w1 = accounts.get("wallet_1"); - if (!w1) return; + const w1 = accounts.get("wallet_1")!; - const call = vm.callPublicFn("counter", "increment", [Cl.uint(1)], w1); + const call = vm.callPublicFn("counter", "add", [Cl.uint(1)], w1); console.log(call.result); // Cl.int(Cl.ok(true)) const counter = vm.getDataVar("counter", "counter"); @@ -24,20 +37,168 @@ async function main() { main(); ``` -## Contributing +By default, the SDK will look for a Clarinet.toml file in the current working directory. +It's also possible to provide the path to the manifest like so: +```ts + const vm = await initVM("./path/to/Clarinet.toml"); +``` + +## Tests + +> Note: A bit of boilerplate is needed to setup the testing environment. Soon it will be handled by the clarinet-cli. + +The SDK can be used to write unit-tests for Clarinet projects. +Make sure you are in directory with a Clarinet.toml file and the associated Clarity smart contracts: + +```sh +cd ./my-project +ls # here you should see the Clarinet.toml file +``` -Clone the clarinet repo adn switch to the sdk component +Let's initialize the Node.js project: +```sh +npm init -y # the -y option sets default properties +npm install @hirosystems/clarinet-sdk @stacks/transactions vite vitest vitest-environment-clarinet ``` + +Update the package.json file scripts to handle tests: +```json + "scripts": { + "test": "vitest run", + "test:coverage": "vitest run -- --coverage true" + }, +``` + +The `./.gitignore` file also needs to be updated, add the following lines at the end. It is especially important to ignore `node_modules`. +``` +logs +*.log +npm-debug.log* +coverage +*.info +node_modules +``` + +A config file is needed for Vitest to use the clarinet-environment. +Create the file `vitest.config.js` with the following content: +```js +/// + +import { defineConfig } from "vite"; +import { vitestSetupFilePath, getClarinetVitestsArgv } from "@hirosystems/clarinet-sdk/vitest"; + +export default defineConfig({ + test: { + environment: "clarinet", + singleThread: true, + setupFiles: [vitestSetupFilePath], + environmentOptions: { + clarinet: getClarinetVitestsArgv(), + }, + }, +}); +``` + +The set up is ready, let's write the first test. Create a test file in the `unit-tests` directory: + +```sh +mkdir unit-tests +touch unit-tests/my-contract.test.js +``` + +```js +// unit-tests/my-contract.test.js +import { describe, it, expect } from "vitest"; +import { Cl } from "@stacks/transactions"; + +describe("test counter ONE", () => { + const accounts = vm.getAccounts(); + const w1 = accounts.get("wallet_1"); + if (!w1) throw new Error("wallet_1 does not exist"); + + it("adds two numbers", () => { + const callAdd = vm.callPublicFn("my-contract", "add", [Cl.uint(21), Cl.uint(21)], w1); + expect(callAdd.result).toBeOk(Cl.uint(42)); + }); +}); + +``` + +### Notes: + +- This code assumes that you have a contract called `my-contract` with a method `add`. +```clar +;; contracts/my-contract.clar +(define-public (add (n1 uint) (n2 uint)) + (ok (+ n1 n2)) +) +``` + +- You may need to disable the deno extension if it's activated in `.vscode/settings.json`. + + +### Type checking + +You can use TypeScript to write test by installing it and setting up the `tsconfig.json`. + +```sh +npm install typescript +touch tsconfig.json +``` + +```json +{ + "compilerOptions": { + "target": "ESNext", + "useDefineForClassFields": true, + "module": "ESNext", + "lib": ["ESNext"], + "skipLibCheck": true, + + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + + "strict": true, + "noImplicitAny": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true + }, + "include": ["node_modules/@hirosystems/clarinet-sdk/vitest-helpers/src", "unit-tests"] +} + +``` + +If you want to write your test in JavaScript but still have a certain level of type safety and autocompletion, VSCode can help you with that. You can create a basic `jsconfig.json` file: + +```json +{ + "compilerOptions": { + "checkJs": true, + "strict": true + }, + "include": ["node_modules/@hirosystems/clarinet-sdk/vitest-helpers/src", "unit-tests"] +} +``` + +## Contributing + +Clone the clarinet repo and go to the clarinet-sdk component directory: +```sh git clone git@github.com:hirosystems/clarinet.git cd clarinet/components/clarinet-sdk ``` Open the SDK workspace in VSCode: -``` +```sh code ./clarinet-sdk.code-workspace ``` Compile the project (both WASM and JS): -``` +```sh +npm install npm run build ``` diff --git a/components/clarinet-sdk/clarinet-sdk.code-workspace b/components/clarinet-sdk/clarinet-sdk.code-workspace index f3508125d..842af5a32 100644 --- a/components/clarinet-sdk/clarinet-sdk.code-workspace +++ b/components/clarinet-sdk/clarinet-sdk.code-workspace @@ -1,8 +1,6 @@ { "folders": [{ "path": "../../" }], "settings": { - // "rust-analyzer.checkOnSave.command": "clippy -p clarinet-sdk -- --no-deps", - // "rust-analyzer.check.command": "check", "rust-analyzer.check.overrideCommand": [ "cargo", "clippy", diff --git a/components/clarinet-sdk/package-lock.json b/components/clarinet-sdk/package-lock.json index 02584958a..b390a5e74 100644 --- a/components/clarinet-sdk/package-lock.json +++ b/components/clarinet-sdk/package-lock.json @@ -1,25 +1,31 @@ { - "name": "obscurity-sdk", - "version": "0.3.5", + "name": "@hirosystems/clarinet-sdk", + "version": "0.1.0", "lockfileVersion": 3, "requires": true, "packages": { "": { - "name": "obscurity-sdk", - "version": "0.3.5", + "name": "@hirosystems/clarinet-sdk", + "version": "0.1.0", "license": "GPL-3.0", "dependencies": { - "@stacks/transactions": "^6.5.4" + "@stacks/transactions": "^6.5.4", + "yargs": "^17.7.2" }, "devDependencies": { "@types/node": "^20.4.5", + "@types/yargs": "^17.0.24", "@wasm-tool/wasm-pack-plugin": "^1.7.0", "copy-webpack-plugin": "^11.0.0", "rimraf": "^5.0.1", "ts-loader": "^9.4.4", + "typedoc": "^0.25.1", "typescript": "^5.1.6", "webpack": "^5.88.2", "webpack-cli": "^5.1.4" + }, + "engines": { + "node": ">=18.0.0" } }, "node_modules/@discoveryjs/json-ext": { @@ -174,45 +180,45 @@ } }, "node_modules/@stacks/common": { - "version": "6.5.5", - "resolved": "https://registry.npmjs.org/@stacks/common/-/common-6.5.5.tgz", - "integrity": "sha512-6v2AVHTTryvl1Govu5rmBXLywAyen2fU3doMCx/7Lk/tFLc4OjMEx4uf1wzpPx1zw/fwJnvoz74OrT/RSALDYw==", + "version": "6.8.0", + "resolved": "https://registry.npmjs.org/@stacks/common/-/common-6.8.0.tgz", + "integrity": "sha512-HWmHXc8ycEfiDQ+m9itSAra1dVQCLLbCSP436B+Lz1ipAdllqe81g1IBV8q0e5DGsuXZdcjd7zNq732auqgzdg==", "dependencies": { "@types/bn.js": "^5.1.0", "@types/node": "^18.0.4" } }, "node_modules/@stacks/common/node_modules/@types/node": { - "version": "18.17.2", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.17.2.tgz", - "integrity": "sha512-wBo3KqP/PBqje5TI9UTiuL3yWfP6sdPtjtygSOqcYZWT232dfDeDOnkDps5wqZBP9NgGgYrNejinl0faAuE+HQ==" + "version": "18.17.17", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.17.17.tgz", + "integrity": "sha512-cOxcXsQ2sxiwkykdJqvyFS+MLQPLvIdwh5l6gNg8qF6s+C7XSkEWOZjK+XhUZd+mYvHV/180g2cnCcIl4l06Pw==" }, "node_modules/@stacks/network": { - "version": "6.5.5", - "resolved": "https://registry.npmjs.org/@stacks/network/-/network-6.5.5.tgz", - "integrity": "sha512-lw7+g6UhOpvWasMeRYMb2OGRKm9ptYkGt27Usg3Eo0z/pu20jZxvHXLBMdDQqxNQOOmwiG4FadICnwTlmnHaqw==", + "version": "6.8.0", + "resolved": "https://registry.npmjs.org/@stacks/network/-/network-6.8.0.tgz", + "integrity": "sha512-QMH+nNZinzrMAJ+kiADlU95O+4Y7wjrrBZzbZ8u0lprfgswLtZ7vHdbUw6MzcbXyHM6Dg0Y2ep746bn8zfaULw==", "dependencies": { - "@stacks/common": "^6.5.5", + "@stacks/common": "^6.8.0", "cross-fetch": "^3.1.5" } }, "node_modules/@stacks/transactions": { - "version": "6.7.0", - "resolved": "https://registry.npmjs.org/@stacks/transactions/-/transactions-6.7.0.tgz", - "integrity": "sha512-M/SxSkKk78cTa7hLxq36YcN+NnnSanvKgINfXnQGTL31dzvH3UfQbRzPZHEkjPRRN82E9XC0scu6Kpwq+qVbtA==", + "version": "6.8.0", + "resolved": "https://registry.npmjs.org/@stacks/transactions/-/transactions-6.8.0.tgz", + "integrity": "sha512-8vLhVK3KQ8q/tYZchPO5WKnEHeW+M+MfPQ4xu6FOYK3Yi7CfmKiIDTe3XpgeeW0B+pmjuQv20LtS/QxNUD4zmA==", "dependencies": { "@noble/hashes": "1.1.5", "@noble/secp256k1": "1.7.1", - "@stacks/common": "^6.5.5", - "@stacks/network": "^6.5.5", + "@stacks/common": "^6.8.0", + "@stacks/network": "^6.8.0", "c32check": "^2.0.0", "lodash.clonedeep": "^4.5.0" } }, "node_modules/@types/bn.js": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-5.1.1.tgz", - "integrity": "sha512-qNrYbZqMx0uJAfKnKclPh+dTwK33KfLHYqtyODwd5HnXOjnkhc4qgn3BrK6RWyGZm5+sIFE7Q7Vz6QQtJB7w7g==", + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-5.1.2.tgz", + "integrity": "sha512-dkpZu0szUtn9UXTmw+e0AJFd4D2XAxDnsCLdc05SfqpqzPEBft8eQr8uaFitfo/dUUOZERaLec2hHMG87A4Dxg==", "dependencies": { "@types/node": "*" } @@ -244,15 +250,30 @@ "dev": true }, "node_modules/@types/json-schema": { - "version": "7.0.12", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.12.tgz", - "integrity": "sha512-Hr5Jfhc9eYOQNPYO5WLDq/n4jqijdHNlDXjuAQkkt+mWdQR+XJToOHrsD4cPaMXpn6KO7y2+wM8AZEs8VpBLVA==", + "version": "7.0.13", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.13.tgz", + "integrity": "sha512-RbSSoHliUbnXj3ny0CNFOoxrIDV6SUGyStHsvDqosw6CkdPV8TtWGlfecuK4ToyMEAql6pzNxgCFKanovUzlgQ==", "dev": true }, "node_modules/@types/node": { - "version": "20.4.7", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.4.7.tgz", - "integrity": "sha512-bUBrPjEry2QUTsnuEjzjbS7voGWCc30W0qzgMf90GPeDGFRakvrz47ju+oqDAKCXLUCe39u57/ORMl/O/04/9g==" + "version": "20.6.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.6.2.tgz", + "integrity": "sha512-Y+/1vGBHV/cYk6OI1Na/LHzwnlNCAfU3ZNGrc1LdRe/LAIbdDPTTv/HU3M7yXN448aTVDq3eKRm2cg7iKLb8gw==" + }, + "node_modules/@types/yargs": { + "version": "17.0.24", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.24.tgz", + "integrity": "sha512-6i0aC7jV6QzQB8ne1joVZ0eSFIstHsCrobmOtghM11yGlH0j43FKL2UhWdELkyps0zuf7qVTUVCCR+tgSlyLLw==", + "dev": true, + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/@types/yargs-parser": { + "version": "21.0.0", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.0.tgz", + "integrity": "sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==", + "dev": true }, "node_modules/@wasm-tool/wasm-pack-plugin": { "version": "1.7.0", @@ -490,14 +511,14 @@ } }, "node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", "dev": true, "dependencies": { "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", "uri-js": "^4.2.2" }, "funding": { @@ -522,35 +543,16 @@ } } }, - "node_modules/ajv-formats/node_modules/ajv": { - "version": "8.12.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", - "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "node_modules/ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", "dev": true, "dependencies": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" + "fast-deep-equal": "^3.1.3" }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/ajv-formats/node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true - }, - "node_modules/ajv-keywords": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", - "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", - "dev": true, "peerDependencies": { - "ajv": "^6.9.1" + "ajv": "^8.8.2" } }, "node_modules/ansi-regex": { @@ -565,6 +567,12 @@ "url": "https://github.com/chalk/ansi-regex?sponsor=1" } }, + "node_modules/ansi-sequence-parser": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ansi-sequence-parser/-/ansi-sequence-parser-1.1.1.tgz", + "integrity": "sha512-vJXt3yiaUL4UU546s3rPXlsry/RnM730G1+HkpKE012AN0sx1eOrxSu95oKDIonskeLTijMgqWZ3uDEe3NFvyg==", + "dev": true + }, "node_modules/ansi-styles": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", @@ -660,9 +668,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001519", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001519.tgz", - "integrity": "sha512-0QHgqR+Jv4bxHMp8kZ1Kn8CH55OikjKJ6JmKkZYP1F3D7w+lnFXF70nG5eNfsZS89jadi5Ywy5UCSKLAglIRkg==", + "version": "1.0.30001535", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001535.tgz", + "integrity": "sha512-48jLyUkiWFfhm/afF7cQPqPjaUmSraEhK4j+FCTJpgnGGEZHqyLe3hmWH7lIooZdSzXL0ReMvHz0vKDoTBsrwg==", "dev": true, "funding": [ { @@ -693,34 +701,109 @@ "node": ">=4" } }, - "node_modules/chalk/node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "node_modules/chrome-trace-event": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz", + "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==", "dev": true, "engines": { - "node": ">=4" + "node": ">=6.0" } }, - "node_modules/chalk/node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", "dependencies": { - "has-flag": "^3.0.0" + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" }, "engines": { - "node": ">=4" + "node": ">=12" } }, - "node_modules/chrome-trace-event": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz", - "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==", - "dev": true, + "node_modules/cliui/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "engines": { - "node": ">=6.0" + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/cliui/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/cliui/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/cliui/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + }, + "node_modules/cliui/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, "node_modules/clone-deep": { @@ -737,18 +820,6 @@ "node": ">=6" } }, - "node_modules/clone-deep/node_modules/is-plain-object": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", - "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", - "dev": true, - "dependencies": { - "isobject": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/color-convert": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", @@ -764,6 +835,12 @@ "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", "dev": true }, + "node_modules/colorette": { + "version": "2.0.20", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", + "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", + "dev": true + }, "node_modules/command-exists": { "version": "1.2.9", "resolved": "https://registry.npmjs.org/command-exists/-/command-exists-1.2.9.tgz", @@ -800,59 +877,6 @@ "webpack": "^5.1.0" } }, - "node_modules/copy-webpack-plugin/node_modules/ajv": { - "version": "8.12.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", - "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", - "dev": true, - "dependencies": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/copy-webpack-plugin/node_modules/ajv-keywords": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", - "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", - "dev": true, - "dependencies": { - "fast-deep-equal": "^3.1.3" - }, - "peerDependencies": { - "ajv": "^8.8.2" - } - }, - "node_modules/copy-webpack-plugin/node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true - }, - "node_modules/copy-webpack-plugin/node_modules/schema-utils": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.2.0.tgz", - "integrity": "sha512-L0jRsrPpjdckP3oPug3/VxNKt2trR8TcabrM6FOAAlvC/9Phcmm+cuAgTlxBqdBR1WJx7Naj9WHw+aOmheSVbw==", - "dev": true, - "dependencies": { - "@types/json-schema": "^7.0.9", - "ajv": "^8.9.0", - "ajv-formats": "^2.1.1", - "ajv-keywords": "^5.1.0" - }, - "engines": { - "node": ">= 12.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, "node_modules/cross-fetch": { "version": "3.1.8", "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.8.tgz", @@ -894,9 +918,9 @@ "dev": true }, "node_modules/electron-to-chromium": { - "version": "1.4.488", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.488.tgz", - "integrity": "sha512-Dv4sTjiW7t/UWGL+H8ZkgIjtUAVZDgb/PwGWvMsCT7jipzUV/u5skbLXPFKb6iV0tiddVi/bcS2/kUrczeWgIQ==", + "version": "1.4.523", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.523.tgz", + "integrity": "sha512-9AreocSUWnzNtvLcbpng6N+GkXnCcBR80IQkxRC9Dfdyg4gaWNUPBujAHUpKkiUkoSoR9UlhA4zD/IgBklmhzg==", "dev": true }, "node_modules/emoji-regex": { @@ -931,16 +955,15 @@ } }, "node_modules/es-module-lexer": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.3.0.tgz", - "integrity": "sha512-vZK7T0N2CBmBOixhmjdqx2gWVbFZ4DXZ/NyRMZVlJXPa7CyFS+/a4QQsDGDQy9ZfEzxFuNEsMLeQJnKP2p5/JA==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.3.1.tgz", + "integrity": "sha512-JUFAyicQV9mXc3YRxPnDlrfBKpqt6hUYzz9/boprUJHs4e4KVr3XwOF70doO6gwXUor6EWZJAyWAfKki84t20Q==", "dev": true }, "node_modules/escalade": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", - "dev": true, "engines": { "node": ">=6" } @@ -1111,10 +1134,18 @@ "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", "dev": true }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, "node_modules/glob": { - "version": "10.3.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.3.tgz", - "integrity": "sha512-92vPiMb/iqpmEgsOoIDvTjc50wf9CCCvMzsi6W0JLPeUKE8TWP1a73PgqSrqy7iAZxaSD1YdzU7QZR5LF51MJw==", + "version": "10.3.4", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.4.tgz", + "integrity": "sha512-6LFElP3A+i/Q8XQKEvZjkEWEOTgAIALR9AO2rwT8bgPhDd1anmqDJDZ6lLddI4ehxxxR1S5RIqKe1uapMQfYaQ==", "dev": true, "dependencies": { "foreground-child": "^3.1.0", @@ -1189,12 +1220,12 @@ } }, "node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", "dev": true, "engines": { - "node": ">=8" + "node": ">=4" } }, "node_modules/ignore": { @@ -1235,9 +1266,9 @@ } }, "node_modules/is-core-module": { - "version": "2.12.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.12.1.tgz", - "integrity": "sha512-Q4ZuBAe2FUsKtyQJoQHlvP8OvBERxO3jEmy1I7hcRXcJBGGHFh/aJBswbXuS9sgrDH2QUO8ilkwNPHvHMd8clg==", + "version": "2.13.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.0.tgz", + "integrity": "sha512-Z7dk6Qo8pOCp3l4tsX2C5ZVas4V+UxwQodwZhLopL91TX8UyyHEXafPcyoeeWuLrwzHcr3igO78wNLwHJHsMCQ==", "dev": true, "dependencies": { "has": "^1.0.3" @@ -1259,7 +1290,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, "engines": { "node": ">=8" } @@ -1285,6 +1315,18 @@ "node": ">=0.12.0" } }, + "node_modules/is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "dev": true, + "dependencies": { + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", @@ -1301,9 +1343,9 @@ } }, "node_modules/jackspeak": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.2.2.tgz", - "integrity": "sha512-mgNtVv4vUuaKA97yxUHoA3+FkuhtxkjdXEWOyB/N76fjy0FjezEt34oy3epBtvCvS+7DyKwqCFWx/oJLV5+kCg==", + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.3.3.tgz", + "integrity": "sha512-R2bUw+kVZFS/h1AZqBKrSgDmdmjApzgY0AlCPumopFiAlbUxE2gf+SCuBzQ0cP5hHmUmFYF5yw55T97Th5Kstg==", "dev": true, "dependencies": { "@isaacs/cliui": "^8.0.2" @@ -1332,6 +1374,30 @@ "node": ">= 10.13.0" } }, + "node_modules/jest-worker/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-worker/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, "node_modules/json-parse-even-better-errors": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", @@ -1339,9 +1405,15 @@ "dev": true }, "node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + }, + "node_modules/jsonc-parser": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.0.tgz", + "integrity": "sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==", "dev": true }, "node_modules/kind-of": { @@ -1380,15 +1452,30 @@ "integrity": "sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==" }, "node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.0.1.tgz", + "integrity": "sha512-IJ4uwUTi2qCccrioU6g9g/5rvvVl13bsdczUUcqbciD9iLr095yj8DQKdObriEvuNSx325N1rV1O0sJFszx75g==", "dev": true, - "dependencies": { - "yallist": "^4.0.0" + "engines": { + "node": "14 || >=16.14" + } + }, + "node_modules/lunr": { + "version": "2.3.9", + "resolved": "https://registry.npmjs.org/lunr/-/lunr-2.3.9.tgz", + "integrity": "sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==", + "dev": true + }, + "node_modules/marked": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/marked/-/marked-4.3.0.tgz", + "integrity": "sha512-PRsaiG84bK+AMvxziE/lCFss8juXjNaWzVbN5tXAm4XjeaS9NAHhop+PjQxz2A9h8Q4M/xGmzP8vqNwy6JeK0A==", + "dev": true, + "bin": { + "marked": "bin/marked.js" }, "engines": { - "node": ">=10" + "node": ">= 12" } }, "node_modules/merge-stream": { @@ -1456,9 +1543,9 @@ } }, "node_modules/minipass": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.0.2.tgz", - "integrity": "sha512-eL79dXrE1q9dBbDCLg7xfn/vl7MS4F1gvJAgjJrQli/jbQWdUttuVawphqpffoIYfRdq78LHx6GP4bU/EQ2ATA==", + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.0.3.tgz", + "integrity": "sha512-LhbbwCfz3vsb12j/WkWQPZfKTsgqIe1Nf/ti1pKjYESGLHIVjWU96G9/ljLH4F9mWNVhlQOm0VySdAWzf05dpg==", "dev": true, "engines": { "node": ">=16 || 14 >=14.17" @@ -1471,9 +1558,9 @@ "dev": true }, "node_modules/node-fetch": { - "version": "2.6.12", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.12.tgz", - "integrity": "sha512-C/fGU2E8ToujUivIO0H+tpQ6HWo4eEmchoPIoXtxCrVghxdKq+QOHqEZW7tuP3KlV3bC8FRMO5nMCC7Zm1VP6g==", + "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" }, @@ -1580,15 +1667,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/path-scurry/node_modules/lru-cache": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.0.0.tgz", - "integrity": "sha512-svTf/fzsKHffP42sujkO/Rjs37BCIsQVRCeNYIm9WN8rgT7ffoUnRtZCqU+6BqcSBdv8gwJeTz8knJpgACeQMw==", - "dev": true, - "engines": { - "node": "14 || >=16.14" - } - }, "node_modules/path-type": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", @@ -1678,6 +1756,14 @@ "node": ">= 10.13.0" } }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/require-from-string": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", @@ -1688,12 +1774,12 @@ } }, "node_modules/resolve": { - "version": "1.22.2", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.2.tgz", - "integrity": "sha512-Sb+mjNHOULsBv818T40qSPeRiuWLyaGMa5ewydRLFimneixmVy2zdivRl+AF6jaYPC8ERxGDmFSiqui6SfPd+g==", + "version": "1.22.6", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.6.tgz", + "integrity": "sha512-njhxM7mV12JfufShqGy3Rz8j11RPdLy4xi15UurGJeoHLfJpVXKdh3ueuOqbYUcDZnffr6X739JBo5LzyahEsw==", "dev": true, "dependencies": { - "is-core-module": "^2.11.0", + "is-core-module": "^2.13.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, @@ -1797,17 +1883,18 @@ ] }, "node_modules/schema-utils": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", - "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.2.0.tgz", + "integrity": "sha512-L0jRsrPpjdckP3oPug3/VxNKt2trR8TcabrM6FOAAlvC/9Phcmm+cuAgTlxBqdBR1WJx7Naj9WHw+aOmheSVbw==", "dev": true, "dependencies": { - "@types/json-schema": "^7.0.8", - "ajv": "^6.12.5", - "ajv-keywords": "^3.5.2" + "@types/json-schema": "^7.0.9", + "ajv": "^8.9.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.1.0" }, "engines": { - "node": ">= 10.13.0" + "node": ">= 12.13.0" }, "funding": { "type": "opencollective", @@ -1829,6 +1916,18 @@ "node": ">=10" } }, + "node_modules/semver/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/serialize-javascript": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.1.tgz", @@ -1871,6 +1970,18 @@ "node": ">=8" } }, + "node_modules/shiki": { + "version": "0.14.4", + "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.14.4.tgz", + "integrity": "sha512-IXCRip2IQzKwxArNNq1S+On4KPML3Yyn8Zzs/xRgcgOWIr8ntIK3IKzjFPfjy/7kt9ZMjc+FItfqHRBg8b6tNQ==", + "dev": true, + "dependencies": { + "ansi-sequence-parser": "^1.1.0", + "jsonc-parser": "^3.2.0", + "vscode-oniguruma": "^1.7.0", + "vscode-textmate": "^8.0.0" + } + }, "node_modules/signal-exit": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", @@ -2011,18 +2122,15 @@ } }, "node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", "dev": true, "dependencies": { - "has-flag": "^4.0.0" + "has-flag": "^3.0.0" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" + "node": ">=4" } }, "node_modules/supports-preserve-symlinks-flag": { @@ -2047,9 +2155,9 @@ } }, "node_modules/terser": { - "version": "5.19.2", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.19.2.tgz", - "integrity": "sha512-qC5+dmecKJA4cpYxRa5aVkKehYsQKc+AHeKl0Oe62aYjBL8ZA33tTljktDHJSaxxMnbI5ZYw+o/S2DxxLu8OfA==", + "version": "5.19.4", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.19.4.tgz", + "integrity": "sha512-6p1DjHeuluwxDXcuT9VR8p64klWJKo1ILiy19s6C9+0Bh2+NWTX6nD9EPppiER4ICkHDVB1RkVpin/YW2nQn/g==", "dev": true, "dependencies": { "@jridgewell/source-map": "^0.3.3", @@ -2098,6 +2206,55 @@ } } }, + "node_modules/terser-webpack-plugin/node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/terser-webpack-plugin/node_modules/ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "dev": true, + "peerDependencies": { + "ajv": "^6.9.1" + } + }, + "node_modules/terser-webpack-plugin/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "node_modules/terser-webpack-plugin/node_modules/schema-utils": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", + "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -2183,6 +2340,15 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, + "node_modules/ts-loader/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/ts-loader/node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -2195,10 +2361,31 @@ "node": ">=8" } }, + "node_modules/typedoc": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.25.1.tgz", + "integrity": "sha512-c2ye3YUtGIadxN2O6YwPEXgrZcvhlZ6HlhWZ8jQRNzwLPn2ylhdGqdR8HbyDRyALP8J6lmSANILCkkIdNPFxqA==", + "dev": true, + "dependencies": { + "lunr": "^2.3.9", + "marked": "^4.3.0", + "minimatch": "^9.0.3", + "shiki": "^0.14.1" + }, + "bin": { + "typedoc": "bin/typedoc" + }, + "engines": { + "node": ">= 16" + }, + "peerDependencies": { + "typescript": "4.6.x || 4.7.x || 4.8.x || 4.9.x || 5.0.x || 5.1.x || 5.2.x" + } + }, "node_modules/typescript": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.1.6.tgz", - "integrity": "sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA==", + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz", + "integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==", "dev": true, "bin": { "tsc": "bin/tsc", @@ -2247,6 +2434,18 @@ "punycode": "^2.1.0" } }, + "node_modules/vscode-oniguruma": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/vscode-oniguruma/-/vscode-oniguruma-1.7.0.tgz", + "integrity": "sha512-L9WMGRfrjOhgHSdOYgCt/yRMsXzLDJSL7BPrOZt73gU0iWO4mpqzqQzOz5srxqTvMBaR0XZTSrVWo4j55Rc6cA==", + "dev": true + }, + "node_modules/vscode-textmate": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/vscode-textmate/-/vscode-textmate-8.0.0.tgz", + "integrity": "sha512-AFbieoL7a5LMqcnOF04ji+rpXadgOXnZsxQr//r83kLPr7biP7am3g9zbaZIaBGwBRWeSvoMD4mgPdX3e4NWBg==", + "dev": true + }, "node_modules/watchpack": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz", @@ -2357,12 +2556,6 @@ } } }, - "node_modules/webpack-cli/node_modules/colorette": { - "version": "2.0.20", - "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", - "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", - "dev": true - }, "node_modules/webpack-cli/node_modules/commander": { "version": "10.0.1", "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", @@ -2394,6 +2587,55 @@ "node": ">=10.13.0" } }, + "node_modules/webpack/node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/webpack/node_modules/ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "dev": true, + "peerDependencies": { + "ajv": "^6.9.1" + } + }, + "node_modules/webpack/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "node_modules/webpack/node_modules/schema-utils": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", + "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, "node_modules/whatwg-url": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", @@ -2545,11 +2787,81 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "engines": { + "node": ">=10" + } + }, "node_modules/yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", "dev": true + }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + }, + "node_modules/yargs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } } } } diff --git a/components/clarinet-sdk/package.json b/components/clarinet-sdk/package.json index 5505f7f53..3289f7ed4 100644 --- a/components/clarinet-sdk/package.json +++ b/components/clarinet-sdk/package.json @@ -1,13 +1,17 @@ { - "name": "obscurity-sdk", - "version": "0.3.5", - "description": "clarity testing lib", + "name": "@hirosystems/clarinet-sdk", + "version": "0.1.2", + "description": "A SDK to interact with Clarity Smart Contracts", "repository": { "type": "git", "url": "https://github.com/hirosystems/clarinet" }, + "engines": { + "node": ">=18.0.0" + }, "files": [ - "dist" + "dist", + "vitest-helpers/src" ], "main": "dist/cjs/index.js", "module": "dist/esm/index.mjs", @@ -22,6 +26,16 @@ "types": "./dist/cjs/index.d.ts", "default": "./dist/cjs/index.js" } + }, + "./vitest": { + "import": { + "types": "./dist/esm/vitest/index.d.ts", + "default": "./dist/esm/vitest/index.mjs" + }, + "require": { + "types": "./dist/cjs/vitest/index.d.ts", + "default": "./dist/cjs/vitest/index.js" + } } }, "scripts": { @@ -30,19 +44,27 @@ "build": "npm run clean; webpack -c webpack.config.js", "prepare": "npm run build" }, - "keywords": [], + "keywords": [ + "stacks", + "clarity", + "clarinet", + "tests" + ], "author": "hirosystems", "license": "GPL-3.0", "readme": "./README.md", "dependencies": { - "@stacks/transactions": "^6.5.4" + "@stacks/transactions": "^6.5.4", + "yargs": "^17.7.2" }, "devDependencies": { "@types/node": "^20.4.5", + "@types/yargs": "^17.0.24", "@wasm-tool/wasm-pack-plugin": "^1.7.0", "copy-webpack-plugin": "^11.0.0", "rimraf": "^5.0.1", "ts-loader": "^9.4.4", + "typedoc": "^0.25.1", "typescript": "^5.1.6", "webpack": "^5.88.2", "webpack-cli": "^5.1.4" diff --git a/components/clarinet-sdk/src-ts/index.ts b/components/clarinet-sdk/src-ts/index.ts index 4d2175815..a09c69d4f 100644 --- a/components/clarinet-sdk/src-ts/index.ts +++ b/components/clarinet-sdk/src-ts/index.ts @@ -2,43 +2,114 @@ import { Cl, ClarityValue } from "@stacks/transactions"; import { vfs } from "./vfs"; import type { ContractInterface } from "./contractInterface"; +import { SDK, TransactionRes, CallContractArgs, DeployContractArgs, TransferSTXArgs } from "./sdk"; -import type { SDK } from "./sdk"; type WASMModule = typeof import("./sdk"); const wasmModule = import("./sdk"); +// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt#use_within_json +// @ts-ignore +BigInt.prototype.toJSON = function () { + return this.toString(); +}; + +type ClarityEvent = { event: string; data: { [key: string]: any } }; +export type ParsedTransactionRes = { + result: ClarityValue; + events: ClarityEvent[]; +}; + type CallFn = ( contract: string, method: string, args: ClarityValue[], sender: string -) => { - result: ClarityValue; - events: { event: string; data: { [key: string]: any } }[]; +) => ParsedTransactionRes; + +type DeployContract = (name: string, content: string, sender: string) => ParsedTransactionRes; + +type TransferSTX = ( + amount: number | bigint, + content: string, + sender: string +) => ParsedTransactionRes; + +type Tx = + | { + callPublicFn: { contract: string; method: string; args: ClarityValue[]; sender: string }; + deployContract?: never; + transferSTX?: never; + } + | { + callPublicFn?: never; + deployContract: { name: string; content: string; sender: string }; + transferSTX?: never; + } + | { + callPublicFn?: never; + deployContradct?: never; + transferSTX: { amount: number; recipient: string; sender: string }; + }; + +export const tx = { + callPublicFn: (contract: string, method: string, args: ClarityValue[], sender: string): Tx => ({ + callPublicFn: { contract, method, args, sender }, + }), + deployContract: (name: string, content: string, sender: string): Tx => ({ + deployContract: { name, content, sender }, + }), + transferSTX: (amount: number, recipient: string, sender: string): Tx => ({ + transferSTX: { amount, recipient, sender }, + }), }; + +type MineBlock = (txs: Array) => ParsedTransactionRes[]; type GetDataVar = (contract: string, dataVar: string) => ClarityValue; type GetMapEntry = (contract: string, mapName: string, mapKey: ClarityValue) => ClarityValue; -type GetAssetsMap = () => Map>; -type GetAccounts = () => Map; // because the session is wrapped in a proxy the types need to be hardcoded export type ClarityVM = { [K in keyof SDK]: K extends "callReadOnlyFn" | "callPublicFn" ? CallFn + : K extends "deployContract" + ? DeployContract + : K extends "transferSTX" + ? TransferSTX + : K extends "mineBlock" + ? MineBlock : K extends "getDataVar" ? GetDataVar : K extends "getMapEntry" ? GetMapEntry - : K extends "getAccounts" - ? GetAccounts - : K extends "getAssetsMap" - ? GetAssetsMap : K extends "getContractsInterfaces" ? () => Map : SDK[K]; }; -const getSessionProxy = (wasm: WASMModule) => ({ +function parseEvents(events: string): ClarityEvent[] { + try { + // @todo: improve type safety + return JSON.parse(events).map((e: string) => { + const { event, data } = JSON.parse(e); + return { + event: event, + data: data, + }; + }); + } catch (e) { + console.error(`Fail to parse events: ${e}`); + return []; + } +} + +function parseTxResult(response: TransactionRes): ParsedTransactionRes { + return { + result: Cl.deserialize(response.result), + events: parseEvents(response.events), + }; +} + +const getSessionProxy = () => ({ get(session: SDK, prop: keyof SDK, receiver: any) { // some of the WASM methods are proxied here to: // - serialize clarity values input argument @@ -47,26 +118,52 @@ const getSessionProxy = (wasm: WASMModule) => ({ if (prop === "callReadOnlyFn" || prop === "callPublicFn") { const callFn: CallFn = (contract, method, args, sender) => { const response = session[prop]( - new wasm.CallContractArgs( + new CallContractArgs( contract, method, - args.map((a) => Cl.serialize(a)) - ), - sender + args.map((a) => Cl.serialize(a)), + sender + ) ); - const result = Cl.deserialize(response.result); + return parseTxResult(response); + }; + return callFn; + } - const events = response.events.map((e: { event: string; data: Map }) => { - return { - event: e.event, - data: Object.fromEntries(e.data.entries()), - }; - }); + if (prop === "deployContract") { + const callDeployContract: DeployContract = (...args) => { + const response = session.deployContract(new DeployContractArgs(...args)); + return parseTxResult(response); + }; + return callDeployContract; + } - return { result, events }; + if (prop === "transferSTX") { + const callTransferSTX: TransferSTX = (amount, ...args) => { + const response = session.transferSTX(new TransferSTXArgs(BigInt(amount), ...args)); + return parseTxResult(response); }; + return callTransferSTX; + } - return callFn; + if (prop === "mineBlock") { + const callMineBlock: MineBlock = (txs) => { + const serializedTxs = txs.map((tx) => { + if (tx.callPublicFn) { + return { + callPublicFn: { + ...tx.callPublicFn, + args_maps: tx.callPublicFn.args.map((a) => Cl.serialize(a)), + }, + }; + } + return tx; + }); + + const responses: TransactionRes[] = session.mineBlock(serializedTxs); + return responses.map(parseTxResult); + }; + return callMineBlock; } if (prop === "getDataVar") { @@ -108,7 +205,7 @@ function memoizedInit() { if (!vm) { console.log("init clarity vm"); - vm = new Proxy(new wasm.SDK(vfs), getSessionProxy(wasm)) as unknown as ClarityVM; + vm = new Proxy(new wasm.SDK(vfs), getSessionProxy()) as unknown as ClarityVM; } // start a new session await vm.initSession(process.cwd(), manifestPath); diff --git a/components/clarinet-sdk/src-ts/vitest/index.ts b/components/clarinet-sdk/src-ts/vitest/index.ts new file mode 100644 index 000000000..bf52881a5 --- /dev/null +++ b/components/clarinet-sdk/src-ts/vitest/index.ts @@ -0,0 +1,23 @@ +import yargs from "yargs"; +import { hideBin } from "yargs/helpers"; + +export function getClarinetVitestsArgv() { + const argv = hideBin(process.argv); + const topLevel = yargs(argv).argv; + + // @ts-ignore + return yargs(topLevel._) + .option("coverage", { + alias: "cov", + type: "boolean", + default: false, + }) + .option("coverage-filename", { + alias: "cov-file", + type: "string", + default: "lcov.info", + }).argv; +} + +export const vitestHelpersPath = "node_modules/@hirosystems/clarinet-sdk/vitest-helpers/src/"; +export const vitestSetupFilePath = `${vitestHelpersPath}vitest.setup.ts`; diff --git a/components/clarinet-sdk/src/core.rs b/components/clarinet-sdk/src/core.rs index 5b544978a..39c837f4d 100644 --- a/components/clarinet-sdk/src/core.rs +++ b/components/clarinet-sdk/src/core.rs @@ -17,8 +17,10 @@ use clarity_repl::repl::{ ClarityCodeSource, ClarityContract, ContractDeployer, Session, DEFAULT_CLARITY_VERSION, DEFAULT_EPOCH, }; +use gloo_utils::format::JsValueSerdeExt; use js_sys::Function as JsFunction; -use serde::Deserialize; +use serde::{Deserialize, Serialize}; +use serde_json::json; use serde_wasm_bindgen::to_value as encode_to_js; use std::collections::HashMap; use std::{panic, path::PathBuf}; @@ -27,22 +29,74 @@ use wasm_bindgen::JsValue; use crate::utils::{self, serialize_event, uint8_to_string, uint8_to_value}; +#[wasm_bindgen] +extern "C" { + #[wasm_bindgen(typescript_type = "Map>")] + pub type AssetsMap; + #[wasm_bindgen(typescript_type = "Map")] + pub type Accounts; +} + +#[derive(Debug, Deserialize)] +struct CallContractArgsJSON { + contract: String, + method: String, + args_maps: Vec>, + sender: String, +} + #[derive(Debug, Deserialize)] #[wasm_bindgen] pub struct CallContractArgs { contract: String, method: String, args: Vec>, + sender: String, } #[wasm_bindgen] impl CallContractArgs { #[wasm_bindgen(constructor)] - pub fn new(contract: String, method: String, args: Vec) -> Self { + pub fn new( + contract: String, + method: String, + args: Vec, + sender: String, + ) -> Self { Self { contract, method, args: args.iter().map(|a| a.to_vec()).collect::>>(), + sender, + } + } + + /* + The mineBlock method receives an JSON Array of Txs, including ContractCalls. + Because it's JSON, the Uint8Array arguments are passed as Map instead of Vec. + This method transform the Map back into a Vec. + */ + fn from_json_args( + CallContractArgsJSON { + contract, + method, + args_maps, + sender, + }: CallContractArgsJSON, + ) -> Self { + let mut args: Vec> = vec![]; + for arg in args_maps { + let mut parsed_arg: Vec = vec![0; arg.len()]; + for (i, v) in arg.iter() { + parsed_arg[*i] = *v; + } + args.push(parsed_arg); + } + Self { + contract, + method, + args, + sender, } } } @@ -52,28 +106,38 @@ impl CallContractArgs { pub struct DeployContractArgs { name: String, content: String, + sender: String, } #[wasm_bindgen] impl DeployContractArgs { #[wasm_bindgen(constructor)] - pub fn new(name: String, content: String) -> Self { - Self { name, content } + pub fn new(name: String, content: String, sender: String) -> Self { + Self { + name, + content, + sender, + } } } -#[derive(Debug, Deserialize)] +#[derive(Debug, Serialize, Deserialize)] #[wasm_bindgen] pub struct TransferSTXArgs { amount: u64, recipient: String, + sender: String, } #[wasm_bindgen] impl TransferSTXArgs { #[wasm_bindgen(constructor)] - pub fn new(amount: u64, recipient: String) -> Self { - Self { amount, recipient } + pub fn new(amount: u64, recipient: String, sender: String) -> Self { + Self { + amount, + recipient, + sender, + } } } @@ -81,10 +145,10 @@ impl TransferSTXArgs { #[serde(rename_all = "camelCase")] #[wasm_bindgen] pub struct TxArgs { - call_contract: Option, + call_public_fn: Option, deploy_contract: Option, + #[serde(rename(serialize = "transfer_stx", deserialize = "transferSTX"))] transfer_stx: Option, - sender: String, } macro_rules! log { @@ -94,9 +158,16 @@ macro_rules! log { } #[wasm_bindgen(getter_with_clone)] +#[derive(Debug, Serialize, Deserialize)] pub struct TransactionRes { pub result: String, - pub events: js_sys::Array, + pub events: String, +} + +#[derive(Serialize, Deserialize)] +pub struct TransactionResRaw { + pub result: String, + pub events: Vec, } #[wasm_bindgen(getter_with_clone)] @@ -107,14 +178,24 @@ pub struct SessionReport { pub fn execution_result_to_transaction_res(execution: &ExecutionResult) -> TransactionRes { let result = match &execution.result { EvaluationResult::Snippet(result) => utils::to_raw_value(&result.result), - _ => unreachable!("Contract value from snippet"), + EvaluationResult::Contract(ref contract) => { + // contract.; + match contract.result { + Some(ref result) => utils::to_raw_value(result), + _ => "0x03".into(), + } + } }; - let events = js_sys::Array::new_with_length(execution.events.len() as u32); - for (i, event) in execution.events.iter().enumerate() { - events.set(i as u32, encode_to_js(&serialize_event(event)).unwrap()) + let events_as_strings = execution + .events + .iter() + .map(|e| json!(serialize_event(e)).to_string()) + .collect::>(); + + TransactionRes { + result, + events: json!(events_as_strings).to_string(), } - - TransactionRes { result, events } } #[wasm_bindgen(getter_with_clone)] @@ -126,6 +207,7 @@ pub struct SDK { contracts_locations: HashMap, contracts_interfaces: HashMap, cache: Option<(DeploymentSpecification, DeploymentGenerationArtifacts)>, + current_test_name: String, } #[wasm_bindgen] @@ -143,9 +225,20 @@ impl SDK { contracts_locations: HashMap::new(), contracts_interfaces: HashMap::new(), cache: None, + current_test_name: String::new(), } } + fn desugar_contract_id(&self, contract: &str) -> Result { + let contract_id = if contract.starts_with('S') { + contract.to_string() + } else { + format!("{}.{}", self.deployer, contract,) + }; + + QualifiedContractIdentifier::parse(&contract_id).map_err(|e| e.to_string()) + } + #[wasm_bindgen(js_name=initSession)] pub async fn init_session( &mut self, @@ -156,7 +249,7 @@ impl SDK { root_path.push(root); let project_root = FileLocation::FileSystem { path: root_path }; let manifest_location = FileLocation::try_parse(&manifest_path, Some(&project_root)) - .ok_or("failed to parse manifest location")?; + .ok_or("Failed to parse manifest location")?; let manifest = ProjectManifest::from_file_accessor(&manifest_location, &self.file_accessor).await?; @@ -241,32 +334,25 @@ impl SDK { } #[wasm_bindgen(js_name=getContractsInterfaces)] - pub fn get_contracts_interfaces(&self) -> Result { + pub fn get_contracts_interfaces(&self) -> Result { Ok(encode_to_js(&self.contracts_interfaces)?) } #[wasm_bindgen(js_name=getAssetsMap)] - pub fn get_assets_maps(&self) -> Result { + pub fn get_assets_maps(&self) -> Result { let session = &self.get_session(); let assets_maps = session.get_assets_maps(); - Ok(encode_to_js(&assets_maps)?) + Ok(encode_to_js(&assets_maps)?.unchecked_into::()) } #[wasm_bindgen(js_name=getAccounts)] - pub fn get_accounts(&mut self) -> Result { - Ok(encode_to_js(&self.accounts)?) + pub fn get_accounts(&mut self) -> Result { + Ok(encode_to_js(&self.accounts)?.unchecked_into::()) } #[wasm_bindgen(js_name=getDataVar)] pub fn get_data_var(&mut self, contract: &str, var_name: &str) -> Result { - let contract_id = if contract.starts_with('S') { - contract.to_string() - } else { - format!("{}.{}", self.deployer, contract,) - }; - let contract_id = - QualifiedContractIdentifier::parse(&contract_id).map_err(|e| e.to_string())?; - + let contract_id = self.desugar_contract_id(contract)?; let session = self.get_session_mut(); session .interpreter @@ -281,14 +367,7 @@ impl SDK { map_name: &str, map_key: Vec, ) -> Result { - let contract_id = if contract.starts_with('S') { - contract.to_string() - } else { - format!("{}.{}", self.deployer, contract,) - }; - let contract_id = - QualifiedContractIdentifier::parse(&contract_id).map_err(|e| e.to_string())?; - + let contract_id = self.desugar_contract_id(contract)?; let session = self.get_session_mut(); session .interpreter @@ -301,14 +380,11 @@ impl SDK { contract: &str, method: &str, ) -> Result<&ContractInterfaceFunction, String> { - let contract_id = - QualifiedContractIdentifier::parse(&format!("{}.{}", self.deployer, contract)) - .map_err(|e| e.to_string())?; + let contract_id = self.desugar_contract_id(contract)?; let contract_interface = self .contracts_interfaces .get(&contract_id) - .ok_or("unable to get contract interface")?; - + .ok_or(format!("unable to get contract interface for {contract}"))?; contract_interface .functions .iter() @@ -319,13 +395,13 @@ impl SDK { fn invoke_contract_call( &mut self, call_contract_args: &CallContractArgs, - sender: &str, test_name: &str, ) -> Result { let CallContractArgs { contract, method, args, + sender, } = call_contract_args; let clarity_args: Vec = args.iter().map(|a| uint8_to_string(a)).collect(); @@ -350,7 +426,6 @@ impl SDK { if let Some(diag) = diagnostics.last() { message = format!("{} -> {}", message, diag.message); } - log!("message: {}", message); return Err(message); } }; @@ -359,23 +434,18 @@ impl SDK { } #[wasm_bindgen(js_name=callReadOnlyFn)] - pub fn call_read_only_fn( - &mut self, - args: &CallContractArgs, - sender: &str, - ) -> Result { + pub fn call_read_only_fn(&mut self, args: &CallContractArgs) -> Result { let interface = self.get_function_interface(&args.contract, &args.method)?; if interface.access != ContractInterfaceFunctionAccess::read_only { return Err(format!("{} is not a read-only function", &args.method)); } - self.invoke_contract_call(args, sender, "read-only call") + self.invoke_contract_call(args, &self.current_test_name.clone()) } fn call_public_fn_private( &mut self, args: &CallContractArgs, - sender: &str, advance_chain_tip: bool, ) -> Result { let interface = self.get_function_interface(&args.contract, &args.method)?; @@ -388,23 +458,22 @@ impl SDK { session.advance_chain_tip(1); } - self.invoke_contract_call(args, sender, "public call") + self.invoke_contract_call(args, &self.current_test_name.clone()) } fn transfer_stx_private( &mut self, args: &TransferSTXArgs, - sender: &str, advance_chain_tip: bool, ) -> Result { let session = self.get_session_mut(); let initial_tx_sender = session.get_tx_sender(); - session.set_tx_sender(sender.to_string()); + session.set_tx_sender(args.sender.to_string()); let execution = match session.stx_transfer(args.amount, &args.recipient) { Ok(res) => res, Err(diagnostics) => { - let mut message = format!("{}: {}", "STX transfer error", sender); + let mut message = format!("{}: {}", "STX transfer error", args.sender); if let Some(diag) = diagnostics.last() { message = format!("{} -> {}", message, diag.message); } @@ -422,90 +491,96 @@ impl SDK { fn deploy_contract_private( &mut self, args: &DeployContractArgs, - sender: &str, advance_chain_tip: bool, ) -> Result { - let session = self.get_session_mut(); let contract = ClarityContract { code_source: ClarityCodeSource::ContractInMemory(args.content.clone()), name: args.name.clone(), - deployer: ContractDeployer::Address(sender.to_string()), + deployer: ContractDeployer::Address(args.sender.to_string()), clarity_version: DEFAULT_CLARITY_VERSION, epoch: DEFAULT_EPOCH, }; - let execution = match session.deploy_contract( - &contract, - None, - false, - Some(args.name.clone()), - &mut None, - ) { - Ok(res) => res, - Err(diagnostics) => { - let mut message = format!( - "{}: {}.{}", - "Contract deployment runtime error", sender, args.name - ); - if let Some(diag) = diagnostics.last() { - message = format!("{} -> {}", message, diag.message); + let execution = { + let session = self.get_session_mut(); + let execution = match session.deploy_contract( + &contract, + None, + false, + Some(args.name.clone()), + &mut None, + ) { + Ok(res) => res, + Err(diagnostics) => { + let mut message = format!( + "Contract deployment runtime error: {}.{}", + args.sender, args.name + ); + if let Some(diag) = diagnostics.last() { + message = format!("{} -> {}", message, diag.message); + } + return Err(message); } - return Err(message); + }; + + if advance_chain_tip { + session.advance_chain_tip(1); + } + execution + }; + + if let EvaluationResult::Contract(ref result) = &execution.result { + if let Some(contract_interface) = &result.contract.analysis.contract_interface { + self.contracts_interfaces.insert( + result.contract.analysis.contract_identifier.clone(), + contract_interface.clone(), + ); } }; - if advance_chain_tip { - session.advance_chain_tip(1); - } Ok(execution_result_to_transaction_res(&execution)) } #[wasm_bindgen(js_name=deployContract)] - pub fn deploy_contract( - &mut self, - args: &DeployContractArgs, - sender: &str, - ) -> Result { - self.deploy_contract_private(args, sender, true) + pub fn deploy_contract(&mut self, args: &DeployContractArgs) -> Result { + self.deploy_contract_private(args, true) } #[wasm_bindgen(js_name = "transferSTX")] - pub fn transfer_stx( - &mut self, - args: &TransferSTXArgs, - sender: &str, - ) -> Result { - self.transfer_stx_private(args, sender, true) + pub fn transfer_stx(&mut self, args: &TransferSTXArgs) -> Result { + self.transfer_stx_private(args, true) } #[wasm_bindgen(js_name = "callPublicFn")] - pub fn call_public_fn( - &mut self, - args: &CallContractArgs, - sender: &str, - ) -> Result { - self.call_public_fn_private(args, sender, true) + pub fn call_public_fn(&mut self, args: &CallContractArgs) -> Result { + self.call_public_fn_private(args, true) } #[wasm_bindgen(js_name=mineBlock)] - pub fn mine_block_js(&mut self, txs: js_sys::Array) -> Result<(), String> { - for js_tx in txs.to_vec() { - let tx: TxArgs = match serde_wasm_bindgen::from_value(js_tx) { - Ok(tx) => tx, - Err(err) => return Err(format!("error: {}", err)), - }; - if let Some(contract_call_args) = tx.call_contract { - let _ = self.call_public_fn_private(&contract_call_args, &tx.sender, false); - } else if let Some(transfer_stx_args) = tx.transfer_stx { - let _ = self.transfer_stx_private(&transfer_stx_args, &tx.sender, false); - } else if let Some(deploy_contract_args) = tx.deploy_contract { - let _ = self.deploy_contract_private(&deploy_contract_args, &tx.sender, false); - } + pub fn mine_block_js(&mut self, js_txs: js_sys::Array) -> Result { + let mut results: Vec = vec![]; + + let txs: Vec = js_txs + .into_serde() + .map_err(|e| format!("Failed to parse js txs: {:}", e))?; + + for tx in txs { + let result = if let Some(args) = tx.call_public_fn { + self.call_public_fn_private(&CallContractArgs::from_json_args(args), false) + } else if let Some(transfer_stx) = tx.transfer_stx { + self.transfer_stx_private(&transfer_stx, false) + } else if let Some(deploy_contract) = tx.deploy_contract { + self.deploy_contract_private(&deploy_contract, false) + } else { + return Err("Invalid tx arguments".into()); + }?; + results.push(result); } let session = self.get_session_mut(); session.advance_chain_tip(1); - Ok(()) + + encode_to_js(&results).map_err(|e| format!("error: {}", e)) } #[wasm_bindgen(js_name=mineEmptyBlock)] @@ -521,15 +596,19 @@ impl SDK { } #[wasm_bindgen(js_name=runSnippet)] - pub fn run_snippet(&mut self, snippet: String) -> JsValue { + pub fn run_snippet(&mut self, snippet: String) -> js_sys::Array { let session = self.get_session_mut(); let (_, output) = session.handle_command(&snippet); let output_as_array = js_sys::Array::new_with_length(output.len() as u32); for string in output { output_as_array.push(&JsValue::from_str(&string)); } - // @todo: can actually return raw value like contract calls - output_as_array.into() + output_as_array + } + + #[wasm_bindgen(js_name=setCurrentTestName)] + pub fn set_current_test_name(&mut self, test_name: String) { + self.current_test_name = test_name; } #[wasm_bindgen(js_name=getReport)] diff --git a/components/clarinet-sdk/tsconfig.cjs.json b/components/clarinet-sdk/tsconfig.cjs.json index ca83f5bdd..1c180f199 100644 --- a/components/clarinet-sdk/tsconfig.cjs.json +++ b/components/clarinet-sdk/tsconfig.cjs.json @@ -3,6 +3,6 @@ "compilerOptions": { "module": "commonjs", "outDir": "dist/cjs", - "target": "es2015" + "target": "ESNext" } } diff --git a/components/clarinet-sdk/tsconfig.esm.json b/components/clarinet-sdk/tsconfig.json similarity index 100% rename from components/clarinet-sdk/tsconfig.esm.json rename to components/clarinet-sdk/tsconfig.json diff --git a/components/clarinet-sdk/vitest-helpers/README.md b/components/clarinet-sdk/vitest-helpers/README.md new file mode 100644 index 000000000..ca6f6eff2 --- /dev/null +++ b/components/clarinet-sdk/vitest-helpers/README.md @@ -0,0 +1,11 @@ +# vitest-helpers + +This directory contains the setup files used in the `vitest.config.js` of a Clarinet project. + +This mean that these file are not directly part of the clarinet-sdk code and not compiled. +Instead, they are directly loaded by `vitest.config.js`, following the path exposed in `@hirosystems/clarinet-sdk/vitest` + + +### Contributing + +This directory is self contained and can be tested by running `npm test` at the root. diff --git a/components/clarinet-sdk/vitest-helpers/package-lock.json b/components/clarinet-sdk/vitest-helpers/package-lock.json new file mode 100644 index 000000000..32cc8ddbe --- /dev/null +++ b/components/clarinet-sdk/vitest-helpers/package-lock.json @@ -0,0 +1,1264 @@ +{ + "name": "clarity-matcher-unit-tests", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "clarity-matcher-unit-tests", + "version": "1.0.0", + "license": "GPL-3.0", + "devDependencies": { + "@stacks/transactions": "^6.5.4", + "@vitest/expect": "^0.34.4", + "typescript": "^5.1.6", + "vite": "^4.4.9", + "vitest": "^0.34.4" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.18.20.tgz", + "integrity": "sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.18.20.tgz", + "integrity": "sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.18.20.tgz", + "integrity": "sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "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/@esbuild/darwin-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.18.20.tgz", + "integrity": "sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.18.20.tgz", + "integrity": "sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.18.20.tgz", + "integrity": "sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.18.20.tgz", + "integrity": "sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.18.20.tgz", + "integrity": "sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.18.20.tgz", + "integrity": "sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.18.20.tgz", + "integrity": "sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==", + "cpu": [ + "loong64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.18.20.tgz", + "integrity": "sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==", + "cpu": [ + "mips64el" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.18.20.tgz", + "integrity": "sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.18.20.tgz", + "integrity": "sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.18.20.tgz", + "integrity": "sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.18.20.tgz", + "integrity": "sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.18.20.tgz", + "integrity": "sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.18.20.tgz", + "integrity": "sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.18.20.tgz", + "integrity": "sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.18.20.tgz", + "integrity": "sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.18.20.tgz", + "integrity": "sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.18.20.tgz", + "integrity": "sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@jest/schemas": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", + "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", + "dev": true, + "dependencies": { + "@sinclair/typebox": "^0.27.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "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/@noble/hashes": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.1.5.tgz", + "integrity": "sha512-LTMZiiLc+V4v1Yi16TD6aX2gmtKszNye0pQgbaLqkvhIqP7nVsSaJsWloGQjJfJ8offaoP5GtX3yY5swbcJxxQ==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ] + }, + "node_modules/@noble/secp256k1": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/@noble/secp256k1/-/secp256k1-1.7.1.tgz", + "integrity": "sha512-hOUk6AyBFmqVrv7k5WAw/LpszxVbj9gGN4JRkIX52fdFAj1UA61KXmZDvqVEm+pOyec3+fIeZB02LYa/pWOArw==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ] + }, + "node_modules/@sinclair/typebox": { + "version": "0.27.8", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", + "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", + "dev": true + }, + "node_modules/@stacks/common": { + "version": "6.8.0", + "resolved": "https://registry.npmjs.org/@stacks/common/-/common-6.8.0.tgz", + "integrity": "sha512-HWmHXc8ycEfiDQ+m9itSAra1dVQCLLbCSP436B+Lz1ipAdllqe81g1IBV8q0e5DGsuXZdcjd7zNq732auqgzdg==", + "dev": true, + "dependencies": { + "@types/bn.js": "^5.1.0", + "@types/node": "^18.0.4" + } + }, + "node_modules/@stacks/network": { + "version": "6.8.0", + "resolved": "https://registry.npmjs.org/@stacks/network/-/network-6.8.0.tgz", + "integrity": "sha512-QMH+nNZinzrMAJ+kiADlU95O+4Y7wjrrBZzbZ8u0lprfgswLtZ7vHdbUw6MzcbXyHM6Dg0Y2ep746bn8zfaULw==", + "dev": true, + "dependencies": { + "@stacks/common": "^6.8.0", + "cross-fetch": "^3.1.5" + } + }, + "node_modules/@stacks/transactions": { + "version": "6.8.0", + "resolved": "https://registry.npmjs.org/@stacks/transactions/-/transactions-6.8.0.tgz", + "integrity": "sha512-8vLhVK3KQ8q/tYZchPO5WKnEHeW+M+MfPQ4xu6FOYK3Yi7CfmKiIDTe3XpgeeW0B+pmjuQv20LtS/QxNUD4zmA==", + "dev": true, + "dependencies": { + "@noble/hashes": "1.1.5", + "@noble/secp256k1": "1.7.1", + "@stacks/common": "^6.8.0", + "@stacks/network": "^6.8.0", + "c32check": "^2.0.0", + "lodash.clonedeep": "^4.5.0" + } + }, + "node_modules/@types/bn.js": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-5.1.2.tgz", + "integrity": "sha512-dkpZu0szUtn9UXTmw+e0AJFd4D2XAxDnsCLdc05SfqpqzPEBft8eQr8uaFitfo/dUUOZERaLec2hHMG87A4Dxg==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/chai": { + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.6.tgz", + "integrity": "sha512-VOVRLM1mBxIRxydiViqPcKn6MIxZytrbMpd6RJLIWKxUNr3zux8no0Oc7kJx0WAPIitgZ0gkrDS+btlqQpubpw==", + "dev": true + }, + "node_modules/@types/chai-subset": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@types/chai-subset/-/chai-subset-1.3.3.tgz", + "integrity": "sha512-frBecisrNGz+F4T6bcc+NLeolfiojh5FxW2klu669+8BARtyQv2C/GkNW6FUodVe4BroGMP/wER/YDGc7rEllw==", + "dev": true, + "dependencies": { + "@types/chai": "*" + } + }, + "node_modules/@types/node": { + "version": "18.17.17", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.17.17.tgz", + "integrity": "sha512-cOxcXsQ2sxiwkykdJqvyFS+MLQPLvIdwh5l6gNg8qF6s+C7XSkEWOZjK+XhUZd+mYvHV/180g2cnCcIl4l06Pw==", + "dev": true + }, + "node_modules/@vitest/expect": { + "version": "0.34.4", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-0.34.4.tgz", + "integrity": "sha512-XlMKX8HyYUqB8dsY8Xxrc64J2Qs9pKMt2Z8vFTL4mBWXJsg4yoALHzJfDWi8h5nkO4Zua4zjqtapQ/IluVkSnA==", + "dev": true, + "dependencies": { + "@vitest/spy": "0.34.4", + "@vitest/utils": "0.34.4", + "chai": "^4.3.7" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/runner": { + "version": "0.34.4", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-0.34.4.tgz", + "integrity": "sha512-hwwdB1StERqUls8oV8YcpmTIpVeJMe4WgYuDongVzixl5hlYLT2G8afhcdADeDeqCaAmZcSgLTLtqkjPQF7x+w==", + "dev": true, + "dependencies": { + "@vitest/utils": "0.34.4", + "p-limit": "^4.0.0", + "pathe": "^1.1.1" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/snapshot": { + "version": "0.34.4", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-0.34.4.tgz", + "integrity": "sha512-GCsh4coc3YUSL/o+BPUo7lHQbzpdttTxL6f4q0jRx2qVGoYz/cyTRDJHbnwks6TILi6560bVWoBpYC10PuTLHw==", + "dev": true, + "dependencies": { + "magic-string": "^0.30.1", + "pathe": "^1.1.1", + "pretty-format": "^29.5.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/spy": { + "version": "0.34.4", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-0.34.4.tgz", + "integrity": "sha512-PNU+fd7DUPgA3Ya924b1qKuQkonAW6hL7YUjkON3wmBwSTIlhOSpy04SJ0NrRsEbrXgMMj6Morh04BMf8k+w0g==", + "dev": true, + "dependencies": { + "tinyspy": "^2.1.1" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/utils": { + "version": "0.34.4", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-0.34.4.tgz", + "integrity": "sha512-yR2+5CHhp/K4ySY0Qtd+CAL9f5Yh1aXrKfAT42bq6CtlGPh92jIDDDSg7ydlRow1CP+dys4TrOrbELOyNInHSg==", + "dev": true, + "dependencies": { + "diff-sequences": "^29.4.3", + "loupe": "^2.3.6", + "pretty-format": "^29.5.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/acorn": { + "version": "8.10.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz", + "integrity": "sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-walk": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", + "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/assertion-error": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", + "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/base-x": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/base-x/-/base-x-4.0.0.tgz", + "integrity": "sha512-FuwxlW4H5kh37X/oW59pwTzzTKRzfrrQwhmyspRM7swOEZcHtDZSCt45U6oKgtuFE+WYPblePMVIPR4RZrh/hw==", + "dev": true + }, + "node_modules/c32check": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/c32check/-/c32check-2.0.0.tgz", + "integrity": "sha512-rpwfAcS/CMqo0oCqDf3r9eeLgScRE3l/xHDCXhM3UyrfvIn7PrLq63uHh7yYbv8NzaZn5MVsVhIRpQ+5GZ5HyA==", + "dev": true, + "dependencies": { + "@noble/hashes": "^1.1.2", + "base-x": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "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/chai": { + "version": "4.3.8", + "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.8.tgz", + "integrity": "sha512-vX4YvVVtxlfSZ2VecZgFUTU5qPCYsobVI2O9FmwEXBhDigYGQA6jRXCycIs1yJnnWbZ6/+a2zNIF5DfVCcJBFQ==", + "dev": true, + "dependencies": { + "assertion-error": "^1.1.0", + "check-error": "^1.0.2", + "deep-eql": "^4.1.2", + "get-func-name": "^2.0.0", + "loupe": "^2.3.1", + "pathval": "^1.1.1", + "type-detect": "^4.0.5" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/check-error": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", + "integrity": "sha512-BrgHpW9NURQgzoNyjfq0Wu6VFO6D7IZEmJNdtgNqpzGG8RuNFHt2jQxWlAs4HMe119chBnv+34syEZtc6IhLtA==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/cross-fetch": { + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.8.tgz", + "integrity": "sha512-cvA+JwZoU0Xq+h6WkMvAUqPEYy92Obet6UdKLfW60qn99ftItKjB5T+BkyWOFWe2pUyfQ+IJHmpOTznqk1M6Kg==", + "dev": true, + "dependencies": { + "node-fetch": "^2.6.12" + } + }, + "node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/deep-eql": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.3.tgz", + "integrity": "sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw==", + "dev": true, + "dependencies": { + "type-detect": "^4.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/diff-sequences": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", + "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", + "dev": true, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "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/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-func-name": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", + "integrity": "sha512-Hm0ixYtaSZ/V7C8FJrtZIuBBI+iSgL+1Aq82zSu8VQNB4S3Gk8e7Qs3VwBDJAhmRZcFqkl3tQu36g/Foh5I5ig==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/jsonc-parser": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.0.tgz", + "integrity": "sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==", + "dev": true + }, + "node_modules/local-pkg": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/local-pkg/-/local-pkg-0.4.3.tgz", + "integrity": "sha512-SFppqq5p42fe2qcZQqqEOiVRXl+WCP1MdT6k7BDEW1j++sp5fIY+/fdRQitvKgB5BrBcmrs5m/L0v2FrU5MY1g==", + "dev": true, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/lodash.clonedeep": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", + "integrity": "sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==", + "dev": true + }, + "node_modules/loupe": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.6.tgz", + "integrity": "sha512-RaPMZKiMy8/JruncMU5Bt6na1eftNoo++R4Y+N2FrxkDVTrGvcyzFTsaGif4QTeKESheMGegbhw6iUAq+5A8zA==", + "dev": true, + "dependencies": { + "get-func-name": "^2.0.0" + } + }, + "node_modules/magic-string": { + "version": "0.30.3", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.3.tgz", + "integrity": "sha512-B7xGbll2fG/VjP+SWg4sX3JynwIU0mjoTc6MPpKNuIvftk6u6vqhDnk1R80b8C2GBR6ywqy+1DcKBrevBg+bmw==", + "dev": true, + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.4.15" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/mlly": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.4.2.tgz", + "integrity": "sha512-i/Ykufi2t1EZ6NaPLdfnZk2AX8cs0d+mTzVKuPfqPKPatxLApaBoxJQ9x1/uckXtrS/U5oisPMDkNs0yQTaBRg==", + "dev": true, + "dependencies": { + "acorn": "^8.10.0", + "pathe": "^1.1.1", + "pkg-types": "^1.0.3", + "ufo": "^1.3.0" + } + }, + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "node_modules/nanoid": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz", + "integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "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==", + "dev": true, + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/p-limit": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-4.0.0.tgz", + "integrity": "sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==", + "dev": true, + "dependencies": { + "yocto-queue": "^1.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pathe": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.1.tgz", + "integrity": "sha512-d+RQGp0MAYTIaDBIMmOfMwz3E+LOZnxx1HZd5R18mmCZY0QBlK0LDZfPc8FW8Ed2DlvsuE6PRjroDY+wg4+j/Q==", + "dev": true + }, + "node_modules/pathval": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", + "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", + "dev": true + }, + "node_modules/pkg-types": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.0.3.tgz", + "integrity": "sha512-nN7pYi0AQqJnoLPC9eHFQ8AcyaixBUOwvqc5TDnIKCMEE6I0y8P7OKA7fPexsXGCGxQDl/cmrLAp26LhcwxZ4A==", + "dev": true, + "dependencies": { + "jsonc-parser": "^3.2.0", + "mlly": "^1.2.0", + "pathe": "^1.1.0" + } + }, + "node_modules/postcss": { + "version": "8.4.29", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.29.tgz", + "integrity": "sha512-cbI+jaqIeu/VGqXEarWkRCCffhjgXc0qjBtXpqJhTBohMUjUQnbBr0xqX3vEKudc4iviTewcJo5ajcec5+wdJw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "nanoid": "^3.3.6", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/react-is": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", + "dev": true + }, + "node_modules/rollup": { + "version": "3.29.2", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.29.2.tgz", + "integrity": "sha512-CJouHoZ27v6siztc21eEQGo0kIcE5D1gVPA571ez0mMYb25LGYGKnVNXpEj5MGlepmDWGXNjDB5q7uNiPHC11A==", + "dev": true, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=14.18.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/siginfo": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", + "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", + "dev": true + }, + "node_modules/source-map-js": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", + "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/stackback": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", + "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", + "dev": true + }, + "node_modules/std-env": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.4.3.tgz", + "integrity": "sha512-f9aPhy8fYBuMN+sNfakZV18U39PbalgjXG3lLB9WkaYTxijru61wb57V9wxxNthXM5Sd88ETBWi29qLAsHO52Q==", + "dev": true + }, + "node_modules/strip-literal": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/strip-literal/-/strip-literal-1.3.0.tgz", + "integrity": "sha512-PugKzOsyXpArk0yWmUwqOZecSO0GH0bPoctLcqNDH9J04pVW3lflYE0ujElBGTloevcxF5MofAOZ7C5l2b+wLg==", + "dev": true, + "dependencies": { + "acorn": "^8.10.0" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/tinybench": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.5.1.tgz", + "integrity": "sha512-65NKvSuAVDP/n4CqH+a9w2kTlLReS9vhsAP06MWx+/89nMinJyB2icyl58RIcqCmIggpojIGeuJGhjU1aGMBSg==", + "dev": true + }, + "node_modules/tinypool": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-0.7.0.tgz", + "integrity": "sha512-zSYNUlYSMhJ6Zdou4cJwo/p7w5nmAH17GRfU/ui3ctvjXFErXXkruT4MWW6poDeXgCaIBlGLrfU6TbTXxyGMww==", + "dev": true, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/tinyspy": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-2.1.1.tgz", + "integrity": "sha512-XPJL2uSzcOyBMky6OFrusqWlzfFrXtE0hPuMgW8A2HmaqrPo4ZQHRN/V0QXN3FSjKxpsbRrFc5LI7KOwBsT1/w==", + "dev": true, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "dev": true + }, + "node_modules/type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/typescript": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz", + "integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/ufo": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.3.0.tgz", + "integrity": "sha512-bRn3CsoojyNStCZe0BG0Mt4Nr/4KF+rhFlnNXybgqt5pXHNFRlqinSoQaTrGyzE4X8aHplSb+TorH+COin9Yxw==", + "dev": true + }, + "node_modules/vite": { + "version": "4.4.9", + "resolved": "https://registry.npmjs.org/vite/-/vite-4.4.9.tgz", + "integrity": "sha512-2mbUn2LlUmNASWwSCNSJ/EG2HuSRTnVNaydp6vMCm5VIqJsjMfbIWtbH2kDuwUVW5mMUKKZvGPX/rqeqVvv1XA==", + "dev": true, + "dependencies": { + "esbuild": "^0.18.10", + "postcss": "^8.4.27", + "rollup": "^3.27.1" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + }, + "peerDependencies": { + "@types/node": ">= 14", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.4.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + } + } + }, + "node_modules/vite-node": { + "version": "0.34.4", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-0.34.4.tgz", + "integrity": "sha512-ho8HtiLc+nsmbwZMw8SlghESEE3KxJNp04F/jPUCLVvaURwt0d+r9LxEqCX5hvrrOQ0GSyxbYr5ZfRYhQ0yVKQ==", + "dev": true, + "dependencies": { + "cac": "^6.7.14", + "debug": "^4.3.4", + "mlly": "^1.4.0", + "pathe": "^1.1.1", + "picocolors": "^1.0.0", + "vite": "^3.0.0 || ^4.0.0" + }, + "bin": { + "vite-node": "vite-node.mjs" + }, + "engines": { + "node": ">=v14.18.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/vitest": { + "version": "0.34.4", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-0.34.4.tgz", + "integrity": "sha512-SE/laOsB6995QlbSE6BtkpXDeVNLJc1u2LHRG/OpnN4RsRzM3GQm4nm3PQCK5OBtrsUqnhzLdnT7se3aeNGdlw==", + "dev": true, + "dependencies": { + "@types/chai": "^4.3.5", + "@types/chai-subset": "^1.3.3", + "@types/node": "*", + "@vitest/expect": "0.34.4", + "@vitest/runner": "0.34.4", + "@vitest/snapshot": "0.34.4", + "@vitest/spy": "0.34.4", + "@vitest/utils": "0.34.4", + "acorn": "^8.9.0", + "acorn-walk": "^8.2.0", + "cac": "^6.7.14", + "chai": "^4.3.7", + "debug": "^4.3.4", + "local-pkg": "^0.4.3", + "magic-string": "^0.30.1", + "pathe": "^1.1.1", + "picocolors": "^1.0.0", + "std-env": "^3.3.3", + "strip-literal": "^1.0.1", + "tinybench": "^2.5.0", + "tinypool": "^0.7.0", + "vite": "^3.1.0 || ^4.0.0 || ^5.0.0-0", + "vite-node": "0.34.4", + "why-is-node-running": "^2.2.2" + }, + "bin": { + "vitest": "vitest.mjs" + }, + "engines": { + "node": ">=v14.18.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@edge-runtime/vm": "*", + "@vitest/browser": "*", + "@vitest/ui": "*", + "happy-dom": "*", + "jsdom": "*", + "playwright": "*", + "safaridriver": "*", + "webdriverio": "*" + }, + "peerDependenciesMeta": { + "@edge-runtime/vm": { + "optional": true + }, + "@vitest/browser": { + "optional": true + }, + "@vitest/ui": { + "optional": true + }, + "happy-dom": { + "optional": true + }, + "jsdom": { + "optional": true + }, + "playwright": { + "optional": true + }, + "safaridriver": { + "optional": true + }, + "webdriverio": { + "optional": true + } + } + }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "dev": true + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dev": true, + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "node_modules/why-is-node-running": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.2.2.tgz", + "integrity": "sha512-6tSwToZxTOcotxHeA+qGCq1mVzKR3CwcJGmVcY+QE8SHy6TnpFnh8PAvPNHYr7EcuVeG0QSMxtYCuO1ta/G/oA==", + "dev": true, + "dependencies": { + "siginfo": "^2.0.0", + "stackback": "0.0.2" + }, + "bin": { + "why-is-node-running": "cli.js" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yocto-queue": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.0.0.tgz", + "integrity": "sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==", + "dev": true, + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} diff --git a/components/clarinet-sdk/vitest-helpers/package.json b/components/clarinet-sdk/vitest-helpers/package.json new file mode 100644 index 000000000..aeb9cec1e --- /dev/null +++ b/components/clarinet-sdk/vitest-helpers/package.json @@ -0,0 +1,22 @@ +{ + "name": "clarity-matcher-unit-tests", + "version": "1.0.0", + "description": "This package is only used to test the vitest-helpers", + "private": true, + "directories": { + "tests": "tests" + }, + "scripts": { + "test": "vitest run" + }, + "keywords": [], + "author": "hirosystems", + "license": "GPL-3.0", + "devDependencies": { + "@stacks/transactions": "^6.5.4", + "@vitest/expect": "^0.34.4", + "typescript": "^5.1.6", + "vite": "^4.4.9", + "vitest": "^0.34.4" + } +} diff --git a/components/clarinet-sdk/vitest-helpers/src/clarityValuesMatchers.ts b/components/clarinet-sdk/vitest-helpers/src/clarityValuesMatchers.ts new file mode 100644 index 000000000..5db21dc94 --- /dev/null +++ b/components/clarinet-sdk/vitest-helpers/src/clarityValuesMatchers.ts @@ -0,0 +1,377 @@ +import { expect, ExpectStatic, assert } from "vitest"; +import { + Cl, + ClarityValue, + ClarityType, + ResponseOkCV, + NoneCV, + SomeCV, + ResponseErrorCV, + IntCV, + UIntCV, + StringAsciiCV, + StringUtf8CV, + ContractPrincipalCV, + StandardPrincipalCV, + ListCV, + TupleCV, + BufferCV, + principalToString, + TrueCV, + FalseCV, + BooleanCV, +} from "@stacks/transactions"; + +import { MatcherState } from "@vitest/expect"; +import { formatCV } from "./formatCV"; + +// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt#use_within_json +// @ts-ignore +BigInt.prototype.toJSON = function () { + return this.toString(); +}; + +function notStr(isNot: boolean) { + return isNot ? "not " : ""; +} + +function formatMessage(this: MatcherState, received: string, expected: string) { + return `expected ${received} ${notStr(this.isNot)}to be ${expected}`; +} + +export class ClarityTypeError extends Error { + actual?: any; + expected?: any; + + constructor({ message, actual, expected }: { message: string; actual?: any; expected?: any }) { + super(message); + this.actual = actual; + this.expected = expected; + + Object.setPrototypeOf(this, ClarityTypeError.prototype); + } +} + +type ClarityTypetoValue = { + [ClarityType.OptionalNone]: NoneCV; + [ClarityType.OptionalSome]: SomeCV; + [ClarityType.ResponseOk]: ResponseOkCV; + [ClarityType.ResponseErr]: ResponseErrorCV; + [ClarityType.BoolTrue]: TrueCV; + [ClarityType.BoolFalse]: FalseCV; + [ClarityType.Int]: IntCV; + [ClarityType.UInt]: UIntCV; + [ClarityType.StringASCII]: StringAsciiCV; + [ClarityType.StringUTF8]: StringUtf8CV; + [ClarityType.PrincipalStandard]: StandardPrincipalCV; + [ClarityType.PrincipalContract]: ContractPrincipalCV; + [ClarityType.List]: ListCV; + [ClarityType.Tuple]: TupleCV; + [ClarityType.Buffer]: BufferCV; +}; + +// the "simple clarity values" are CVs that can't be nested and have `value` property +type SimpleCV = BooleanCV | IntCV | UIntCV | StringAsciiCV | StringUtf8CV; +type SimpleCVTypes = + | ClarityType.BoolFalse + | ClarityType.BoolTrue + | ClarityType.Int + | ClarityType.UInt + | ClarityType.StringASCII + | ClarityType.StringUTF8; + +const validClarityTypes = Object.values(ClarityType).filter((t) => typeof t === "number"); + +function isClarityValue(input: unknown): input is ClarityValue { + if (!input || typeof input !== "object") return false; + if (!("type" in input) || typeof input.type !== "number") return false; + if (!validClarityTypes.includes(input.type)) return false; + + return true; +} + +function isClarityValueWithType( + input: unknown, + withType: T +): input is ClarityTypetoValue[T] { + if (!isClarityValue(input)) return false; + if (input.type !== withType) return false; + + return true; +} + +function checkCVType( + actual: unknown, + expectedType: T, + isNot: boolean +): actual is ClarityTypetoValue[T] { + const isCV = isClarityValue(actual); + + if (!isCV) { + throw new ClarityTypeError({ + message: `actual value must ${notStr(isNot)}be a Clarity "${ + ClarityType[expectedType] + }", received "${typeof actual}"`, + }); + } + + const isCVWithType = isClarityValueWithType(actual, expectedType); + + if (!isCVWithType) { + throw new ClarityTypeError({ + message: `actual value must ${notStr(isNot)}be a Clarity "${ + ClarityType[expectedType] + }", received "${ClarityType[actual.type]}"`, + actual: ClarityType[actual.type], + expected: ClarityType[expectedType], + }); + } + + return true; +} + +function errorToAssertionResult(this: MatcherState, err: any) { + return { + pass: false, + message: () => err.message, + actual: err.actual, + expected: err.expected, + }; +} + +function simpleAssertion( + this: MatcherState, + cvType: SimpleCVTypes, + actualRaw: unknown, + expectedRaw: SimpleCV +) { + try { + const isCV = checkCVType(actualRaw, cvType, this.isNot); + assert(isCV); + } catch (e: any) { + return errorToAssertionResult.call(this, e); + } + + return { + pass: this.equals(actualRaw, expectedRaw, undefined, true), + message: () => + `expected ${formatCV(actualRaw)} ${notStr(this.isNot)}to be ${formatCV(expectedRaw)}`, + actual: formatCV(actualRaw, 2), + expected: formatCV(expectedRaw, 2), + }; +} + +const typeToCvMethod = { + [ClarityType.ResponseOk]: Cl.ok, + [ClarityType.ResponseErr]: Cl.error, + [ClarityType.OptionalSome]: Cl.some, +}; + +// simple composite types are `ok`, `err`, `some` +function simpleCompositeAssertion( + this: MatcherState, + expectedType: ClarityType.ResponseOk | ClarityType.ResponseErr | ClarityType.OptionalSome, + actualRaw: unknown, + expectedValue: ClarityValue | ExpectStatic +) { + try { + const isCV = checkCVType(actualRaw, expectedType, this.isNot); + assert(isCV); + } catch (e: any) { + return errorToAssertionResult.call(this, e); + } + + const clMethod = typeToCvMethod[expectedType]; + + const expectedIsCV = isClarityValue(expectedValue); + const expectedOneLine = expectedIsCV + ? formatCV(clMethod(expectedValue)) + : JSON.stringify(expectedValue); + const expected = expectedIsCV + ? formatCV(clMethod(expectedValue), 2) + : JSON.stringify(expectedValue); + + return { + pass: this.equals(actualRaw.value, expectedValue, undefined, true), + message: () => formatMessage.call(this, formatCV(actualRaw), expectedOneLine), + actual: formatCV(actualRaw, 2), + expected, + }; +} + +expect.extend({ + toHaveClarityType(actual: unknown, expectedType: ClarityType) { + try { + const isCV = checkCVType(actual, expectedType, this.isNot); + assert(isCV); + } catch (e: any) { + return errorToAssertionResult.call(this, e); + } + + return { + pass: true, + message: () => + `actual value must ${notStr(this.isNot)}be a Clarity "${ClarityType[expectedType]}"`, + }; + }, + + toBeBool(actual: unknown, expected: boolean) { + const expectedType = expected ? ClarityType.BoolTrue : ClarityType.BoolFalse; + return simpleAssertion.call(this, expectedType, actual, Cl.bool(expected)); + }, + + toBeInt(actual: unknown, expected: number | bigint) { + return simpleAssertion.call(this, ClarityType.Int, actual, Cl.int(expected)); + }, + + toBeUint(actual: unknown, expected: number | bigint) { + return simpleAssertion.call(this, ClarityType.UInt, actual, Cl.uint(expected)); + }, + + toBeAscii(actual: unknown, expected: string) { + return simpleAssertion.call(this, ClarityType.StringASCII, actual, Cl.stringAscii(expected)); + }, + + toBeUtf8(actual: unknown, expected: string) { + return simpleAssertion.call(this, ClarityType.StringUTF8, actual, Cl.stringUtf8(expected)); + }, + + toBeOk(actual: unknown, expectedValue: ExpectStatic | ClarityValue) { + return simpleCompositeAssertion.call(this, ClarityType.ResponseOk, actual, expectedValue); + }, + + toBeErr(actual: unknown, expectedValue: ExpectStatic | ClarityValue) { + return simpleCompositeAssertion.call(this, ClarityType.ResponseErr, actual, expectedValue); + }, + + toBeSome(actual: unknown, expectedValue: ExpectStatic | ClarityValue) { + return simpleCompositeAssertion.call(this, ClarityType.OptionalSome, actual, expectedValue); + }, + + toBeNone(actual: unknown) { + const expectedType = ClarityType.OptionalNone; + try { + const isCV = checkCVType(actual, expectedType, this.isNot); + assert(isCV); + } catch (e: any) { + return errorToAssertionResult.call(this, e); + } + + const expected = Cl.none(); + return { + pass: this.equals(actual, expected, undefined, true), + message: () => formatMessage.call(this, formatCV(actual), formatCV(actual)), + actual: formatCV(actual, 2), + expected: formatCV(actual, 2), + }; + }, + + toBePrincipal(actual: unknown, expectedString: string) { + const isStandard = !expectedString.includes("."); + let expected: StandardPrincipalCV | ContractPrincipalCV; + + const expectedType = isStandard ? ClarityType.PrincipalStandard : ClarityType.PrincipalContract; + try { + const isCV = checkCVType(actual, expectedType, this.isNot); + assert(isCV); + } catch (e: any) { + return errorToAssertionResult.call(this, e); + } + + const actualString = principalToString(actual); + + try { + expected = isStandard + ? Cl.standardPrincipal(expectedString) + : Cl.contractPrincipal(...(expectedString.split(".") as [string, string])); + } catch { + return { + pass: false, + message: () => `expected ${expectedString} ${notStr(this.isNot)}to be a principal`, + actual: actualString, + expected: expectedString, + }; + } + + return { + pass: this.equals(actual, expected, undefined, true), + message: () => formatMessage.call(this, actualString, expectedString), + actual: formatCV(actual, 2), + expected: formatCV(expected, 2), + }; + }, + + toBeBuff(actual: unknown, expectedRaw: Uint8Array) { + const expectedType = ClarityType.Buffer; + try { + const isCV = checkCVType(actual, expectedType, this.isNot); + assert(isCV); + } catch (e: any) { + return errorToAssertionResult.call(this, e); + } + + const expected = Cl.buffer(expectedRaw); + return { + pass: this.equals(actual, expected, undefined, true), + // note: throw a simple message and rely on `actual` and `expected` to display the diff + message: () => `the received Buffer does ${this.isNot ? "" : "not "}match the expected one`, + actual: formatCV(actual, 2), + expected: formatCV(expected, 2), + }; + }, + + toBeList(actual: unknown, expectedItems: ExpectStatic[] | ClarityValue[]) { + const expectedType = ClarityType.List; + try { + const isCV = checkCVType(actual, expectedType, this.isNot); + assert(isCV); + } catch (e: any) { + return errorToAssertionResult.call(this, e); + } + + const isListArray = checkIsListArray(expectedItems); + const expected = isListArray ? formatCV(Cl.list(expectedItems), 2) : expectedItems; + + return { + pass: this.equals(actual.list, expectedItems, undefined, true), + // note: throw a simple message and rely on `actual` and `expected` to display the diff + message: () => `the received List does ${this.isNot ? "" : "not "}match the expected one`, + actual: formatCV(actual, 2), + expected, + }; + }, + + toBeTuple(actual: unknown, expectedData: Record) { + const expectedType = ClarityType.Tuple; + try { + const isCV = checkCVType(actual, expectedType, this.isNot); + assert(isCV); + } catch (e: any) { + return errorToAssertionResult.call(this, e); + } + + const isTupleData = checkIsTupleData(expectedData); + const expected = isTupleData ? formatCV(Cl.tuple(expectedData), 2) : expectedData; + + return { + pass: this.equals(actual.data, expectedData, undefined, true), + // note: throw a simple message and rely on `actual` and `expected` to display the diff + message: () => `the received Tuple does ${this.isNot ? "" : "not "}match the expected one`, + actual: formatCV(actual, 2), + expected, + }; + }, +}); + +// for composite types, matchers need to narrow the type of the expected value +// to know if it contains AsymmetricMatchers or if it's only ClarityValues + +function checkIsTupleData( + expected: Record +): expected is Record { + return Object.values(expected).every((v) => isClarityValue(v)); +} + +function checkIsListArray(expected: ExpectStatic[] | ClarityValue[]): expected is ClarityValue[] { + return expected.every((v) => isClarityValue(v)); +} diff --git a/components/clarinet-sdk/vitest-helpers/src/formatCV.ts b/components/clarinet-sdk/vitest-helpers/src/formatCV.ts new file mode 100644 index 000000000..cab7071ba --- /dev/null +++ b/components/clarinet-sdk/vitest-helpers/src/formatCV.ts @@ -0,0 +1,130 @@ +/* + This file could be implement in stacks.js + Format stacks.js Clarity Value into Clarity style readable string + eg: + `Cl.uint(1)` => u1 + `Cl.list(Cl.uint(1))` => (list u1) +*/ + +import { + BufferCV, + ClarityType, + ClarityValue, + ListCV, + TupleCV, + principalToString, +} from "@stacks/transactions"; + +let byteToHex: string[] = []; +for (let n = 0; n <= 0xff; ++n) { + const hexOctet = n.toString(16).padStart(2, "0"); + byteToHex.push(hexOctet); +} + +function formatSpace(space: number, depth: number, end = false) { + if (!space) return " "; + return `\n${" ".repeat(space * (depth - (end ? 1 : 0)))}`; +} + +function formatBuffer(cv: BufferCV): string { + const hex = Array.from(cv.buffer).map((n) => byteToHex[n]); + return `0x${hex.join("")}`; +} + +/** + * formatList + * @description format List clarity values in clarity style strings + * with the ability to prettify the result with line break end space indentation + * @exemple + * ``` + * formatList(Cl.list([Cl.uint(1)])) + * // (list u1) + * + * formatList(Cl.list([Cl.uint(1)]), 2) + * // (list + * // u1 + * // ) + * ``` + */ +function formatList(cv: ListCV, space: number, depth = 1): string { + if (cv.list.length === 0) return "(list)"; + + const spaceBefore = formatSpace(space, depth, false); + const endSpace = space ? formatSpace(space, depth, true) : ""; + + const items = cv.list.map((v) => formatCVPrivate(v, space, depth)).join(spaceBefore); + + return `(list${spaceBefore}${items}${endSpace})`; +} + +/** + * formatTuple + * @description format Tuple clarity values in clarity style strings + * with the ability to prettify the result with line break end space indentation + * @exemple + * ``` + * formatTuple(Cl.tuple({ id: Cl.uint(1) })) + * // { id: u1 } + * + * formatTuple(Cl.tuple({ id: Cl.uint(1) }, 2)) + * // { + * // id: u1 + * // } + * ``` + */ +function formatTuple(cv: TupleCV, space: number, depth = 1): string { + if (Object.keys(cv.data).length === 0) return "{}"; + + const items: Array = []; + for (const [key, value] of Object.entries(cv.data)) { + items.push(`${key}: ${formatCVPrivate(value, space, depth)}`); + } + + const spaceBefore = formatSpace(space, depth, false); + const endSpace = formatSpace(space, depth, true); + + return `{${spaceBefore}${items.join(`,${spaceBefore}`)}${endSpace}}`; +} + +function exhaustiveCheck(param: never): never { + throw new Error(`invalid clarity value type: ${param}`); +} + +// we don't want the exported function to have a `depth` argument +function formatCVPrivate(cv: ClarityValue, space = 0, depth: number): string { + if (cv.type === ClarityType.BoolFalse) return "false"; + if (cv.type === ClarityType.BoolTrue) return "true"; + + if (cv.type === ClarityType.Int) return cv.value.toString(); + if (cv.type === ClarityType.UInt) return `u${cv.value.toString()}`; + + if (cv.type === ClarityType.StringASCII) return `"${cv.data}"`; + if (cv.type === ClarityType.StringUTF8) return `u"${cv.data}"`; + + if (cv.type === ClarityType.PrincipalContract) return `'${principalToString(cv)}`; + if (cv.type === ClarityType.PrincipalStandard) return `'${principalToString(cv)}`; + + if (cv.type === ClarityType.Buffer) return formatBuffer(cv); + + if (cv.type === ClarityType.OptionalNone) return "none"; + if (cv.type === ClarityType.OptionalSome) + return `(some ${formatCVPrivate(cv.value, space, depth)})`; + + if (cv.type === ClarityType.ResponseOk) return `(ok ${formatCVPrivate(cv.value, space, depth)})`; + if (cv.type === ClarityType.ResponseErr) + return `(err ${formatCVPrivate(cv.value, space, depth)})`; + + if (cv.type === ClarityType.List) { + return formatList(cv, space, depth + 1); + } + if (cv.type === ClarityType.Tuple) { + return formatTuple(cv, space, depth + 1); + } + + // make sure that we exhausted all ClarityTypes + exhaustiveCheck(cv); +} + +export function formatCV(cv: ClarityValue, space = 0): string { + return formatCVPrivate(cv, space, 0); +} diff --git a/components/clarinet-sdk/vitest-helpers/src/global.d.ts b/components/clarinet-sdk/vitest-helpers/src/global.d.ts new file mode 100644 index 000000000..989309966 --- /dev/null +++ b/components/clarinet-sdk/vitest-helpers/src/global.d.ts @@ -0,0 +1,13 @@ +import type { ClarityVM } from "../../dist/esm"; + +declare global { + var vm: ClarityVM; + var testEnvironment: string; + var coverageReports: string[]; + var options: { + clarinet: { + coverage: boolean; + coverageFilename: string; + }; + }; +} diff --git a/components/clarinet-sdk/vitest-helpers/src/vitest.d.ts b/components/clarinet-sdk/vitest-helpers/src/vitest.d.ts new file mode 100644 index 000000000..299cb2dc1 --- /dev/null +++ b/components/clarinet-sdk/vitest-helpers/src/vitest.d.ts @@ -0,0 +1,28 @@ +import type { Assertion, AsymmetricMatchersContaining, ExpectStatic } from "vitest"; +import type { ClarityType, ClarityValue } from "@stacks/transactions"; + +interface ClarityValuesMatchers { + toHaveClarityType(expectedType: ClarityType): R; + + toBeOk(expected: ExpectStatic | ClarityValue): R; + toBeErr(expected: ExpectStatic | ClarityValue): R; + + toBeSome(expected: ExpectStatic | ClarityValue): R; + toBeNone(): R; + + toBeBool(expected: boolean): R; + toBeInt(rexpected: number | bigint): R; + toBeUint(expected: number | bigint): R; + toBeAscii(expected: string): R; + toBeUtf8(expected: string): R; + toBePrincipal(expected: string): R; + toBeBuff(expected: Uint8Array): R; + + toBeList(expected: ExpectStatic[] | ClarityValue[]): R; + toBeTuple(expected: Record): R; +} + +declare module "vitest" { + interface Assertion extends ClarityValuesMatchers {} + interface AsymmetricMatchersContaining extends ClarityValuesMatchers {} +} diff --git a/components/clarinet-sdk/vitest-helpers/src/vitest.setup.ts b/components/clarinet-sdk/vitest-helpers/src/vitest.setup.ts new file mode 100644 index 000000000..1f8ea732d --- /dev/null +++ b/components/clarinet-sdk/vitest-helpers/src/vitest.setup.ts @@ -0,0 +1,29 @@ +import { Task, afterAll, beforeAll, beforeEach } from "vitest"; + +import "./clarityValuesMatchers"; + +function getFullTestName(task: Task, names: string[]) { + const fullNames = [task.name, ...names]; + if (task.suite?.name) { + return getFullTestName(task.suite, fullNames); + } + return fullNames; +} + +beforeAll(async () => { + await vm.initSession(process.cwd(), "./Clarinet.toml"); +}); + +beforeEach(async (ctx) => { + if (global.options.clarinet.coverage) { + const suiteTestNames = getFullTestName(ctx.task, []); + const fullName = [ctx.task.file?.name || "", ...suiteTestNames].join("__"); + vm.setCurrentTestName(fullName); + } +}); + +afterAll(() => { + if (global.options.clarinet.coverage) { + coverageReports.push(vm.getReport().coverage); + } +}); diff --git a/components/clarinet-sdk/vitest-helpers/tests/clarityValueMatchers.test.ts b/components/clarinet-sdk/vitest-helpers/tests/clarityValueMatchers.test.ts new file mode 100644 index 000000000..97d84b666 --- /dev/null +++ b/components/clarinet-sdk/vitest-helpers/tests/clarityValueMatchers.test.ts @@ -0,0 +1,381 @@ +import { describe, it, expect } from "vitest"; +import { Cl, ClarityType } from "@stacks/transactions"; + +import "../src/clarityValuesMatchers"; + +describe("tests the Clarity Type of a CV", () => { + it("tests any CV type", () => { + expect(Cl.ok(Cl.int(1))).toHaveClarityType(ClarityType.ResponseOk); + expect(Cl.error(Cl.int(1))).toHaveClarityType(ClarityType.ResponseErr); + expect(Cl.some(Cl.int(1))).toHaveClarityType(ClarityType.OptionalSome); + expect(Cl.none()).toHaveClarityType(ClarityType.OptionalNone); + + expect(Cl.int(1)).toHaveClarityType(ClarityType.Int); + expect(Cl.uint(1n)).toHaveClarityType(ClarityType.UInt); + expect(Cl.stringAscii("hello")).toHaveClarityType(ClarityType.StringASCII); + expect(Cl.stringUtf8("hello")).toHaveClarityType(ClarityType.StringUTF8); + + try { + expect(1).toHaveClarityType(ClarityType.ResponseOk); + } catch (e: any) { + expect(e.message).toBe('actual value must be a Clarity "ResponseOk", received "number"'); + } + try { + expect(Cl.uint(1)).toHaveClarityType(ClarityType.ResponseOk); + } catch (e: any) { + expect(e.message).toBe('actual value must be a Clarity "ResponseOk", received "UInt"'); + } + + try { + expect(Cl.ok(Cl.uint(1))).not.toHaveClarityType(ClarityType.ResponseOk); + } catch (e: any) { + expect(e.message).toBe('actual value must not be a Clarity "ResponseOk"'); + } + }); +}); + +describe("test simple clarity values assertions", () => { + it("tests ok", () => { + const okRes = Cl.ok(Cl.uint(1)); + expect(okRes).toEqual(Cl.ok(Cl.uint(1))); + + expect(Cl.ok(Cl.uint(1))).toBeOk(Cl.uint(1)); + + expect(Cl.ok(Cl.uint(1))).toBeOk(expect.toBeUint(1)); + expect(Cl.ok(Cl.uint(1))).toBeOk(expect.not.toBeUint(2)); + expect(Cl.error(Cl.uint(1))).not.toBeOk(expect.toBeUint(1)); + + const nestedOks = Cl.ok(Cl.ok(Cl.uint(1))); + expect(nestedOks).toBeOk(expect.toBeOk(expect.toBeUint(1))); + + expect(() => expect(Cl.uint(1)).toBeOk(Cl.uint(1))).toThrow( + 'actual value must be a Clarity "ResponseOk", received "UInt"' + ); + + try { + const failingTest = () => expect(Cl.ok(Cl.uint(1))).toBeOk(Cl.uint(2)); + expect(failingTest).toThrow("expected (ok u1) to be (ok u2)"); + } catch (e: any) { + expect(e.actual).toBe("(ok u1)"); + expect(e.expected).toBe("(ok u2)"); + } + }); + + it("tests error", () => { + expect(Cl.error(Cl.uint(1))).toBeErr(expect.toBeUint(1)); + + try { + const failingTest = () => expect(Cl.error(Cl.uint(1))).toBeErr(Cl.uint(2)); + expect(failingTest).toThrow("expected (err u1) to be (err u2)"); + } catch (e: any) { + expect(e.actual).toBe("(err u1)"); + expect(e.expected).toBe("(err u2)"); + } + }); + + it("tests some", () => { + expect(Cl.some(Cl.uint(1))).toBeSome(expect.toBeUint(1)); + expect(Cl.some(Cl.uint(1))).toBeSome(Cl.uint(1)); + + expect(() => expect(Cl.some(Cl.uint(1))).toBeSome(Cl.uint(2))).toThrow( + "expected (some u1) to be (some u2)" + ); + }); + + it("tests none", () => { + expect(Cl.none()).toBeNone(); + expect(Cl.uint(1)).not.toBeNone(); + + try { + expect(Cl.uint(1)).toBeNone(); + } catch (e: any) { + expect(e.message).toBe('actual value must be a Clarity "OptionalNone", received "UInt"'); + } + }); + + it("tests bool", () => { + expect(Cl.bool(true)).toBeBool(true); + expect(Cl.bool(false)).toBeBool(false); + + expect(() => expect(Cl.uint(1)).toBeBool(false)).toThrow( + 'actual value must be a Clarity "BoolFalse", received "UInt"' + ); + expect(() => expect(false).toBeBool(false)).toThrow( + 'actual value must be a Clarity "BoolFalse", received "boolean"' + ); + }); + + it("tests int", () => { + expect(Cl.int(1)).toBeInt(1); + expect(Cl.int(1)).toBeInt(1n); + + expect(() => expect(Cl.uint(1)).toBeInt(1)).toThrow( + 'actual value must be a Clarity "Int", received "UInt"' + ); + + try { + const failingTest = () => expect(Cl.int(2)).toBeInt(1); + expect(failingTest).toThrow("expected 2 to be 1"); + failingTest(); + } catch (e: any) { + expect(e.actual).toBe("2"); + expect(e.expected).toBe("1"); + } + }); + + it("tests uint", () => { + expect(Cl.uint(1)).toBeUint(1); + expect(Cl.uint(1)).toBeUint(1n); + + expect(() => expect(Cl.int(1)).toBeUint(1)).toThrow( + 'actual value must be a Clarity "UInt", received "Int"' + ); + + try { + const failingTest = () => expect(Cl.uint(2)).toBeUint(1); + expect(failingTest).toThrow("expected u2 to be u1"); + failingTest(); + } catch (e: any) { + expect(e.actual).toBe("u2"); + expect(e.expected).toBe("u1"); + } + }); + + it("tests string-ascii", () => { + expect(Cl.stringAscii("hello world")).toBeAscii("hello world"); + + expect(() => expect(Cl.int(1)).toBeAscii("hello world")).toThrow( + 'actual value must be a Clarity "StringASCII", received "Int"' + ); + + try { + const failingTest = () => expect(Cl.stringAscii("hello")).toBeAscii("hello world"); + expect(failingTest).toThrow('expected "hello" to be "hello world"'); + failingTest(); + } catch (e: any) { + expect(e.actual).toBe('"hello"'); + expect(e.expected).toBe('"hello world"'); + } + }); + + it("tests string-utf8", () => { + expect(Cl.stringUtf8("hello world")).toBeUtf8("hello world"); + + expect(() => expect(Cl.int(1)).toBeUtf8("hello world")).toThrow( + 'actual value must be a Clarity "StringUTF8", received "Int"' + ); + + try { + const failingTest = () => expect(Cl.stringUtf8("hello")).toBeUtf8("hello world"); + expect(failingTest).toThrow('expected u"hello" to be u"hello world"'); + failingTest(); + } catch (e: any) { + expect(e.actual).toBe('u"hello"'); + expect(e.expected).toBe('u"hello world"'); + } + }); + + it("tests standard principal", () => { + const addr = "ST2CY5V39NHDPWSXMW9QDT3HC3GD6Q6XX4CFRK9AG"; + expect(Cl.standardPrincipal(addr)).toBePrincipal(addr); + + const addr2 = "STNHKEPYEPJ8ET55ZZ0M5A34J0R3N5FM2CMMMAZ6"; + try { + const failingTest = () => expect(Cl.standardPrincipal(addr)).toBePrincipal(addr2); + expect(failingTest).toThrow(`expected ${addr} to be ${addr2}`); + failingTest(); + } catch (e: any) { + expect(e.actual).toBe("'ST2CY5V39NHDPWSXMW9QDT3HC3GD6Q6XX4CFRK9AG"); + expect(e.expected).toBe("'STNHKEPYEPJ8ET55ZZ0M5A34J0R3N5FM2CMMMAZ6"); + } + + expect(() => expect(Cl.standardPrincipal(addr)).toBePrincipal("INVALID")).toThrow( + "expected INVALID to be a principal" + ); + + const contractAddress = "ST2CY5V39NHDPWSXMW9QDT3HC3GD6Q6XX4CFRK9AG.contract"; + expect( + Cl.contractPrincipal("ST2CY5V39NHDPWSXMW9QDT3HC3GD6Q6XX4CFRK9AG", "contract") + ).toBePrincipal(contractAddress); + }); + + it("tests contract principal", () => { + const addr = "ST2CY5V39NHDPWSXMW9QDT3HC3GD6Q6XX4CFRK9AG"; + expect(Cl.contractPrincipal(addr, "counter")).toBePrincipal(`${addr}.counter`); + }); + + it("tests buffer", () => { + const val = [98, 116, 99]; + expect(Cl.buffer(Uint8Array.from(val))).toBeBuff(Uint8Array.from(val)); + expect(Cl.buffer(Uint8Array.from(val))).toStrictEqual(Cl.bufferFromUtf8("btc")); + + expect(() => expect(Cl.int(1)).toBeBuff(Uint8Array.from(val))).toThrow( + 'actual value must be a Clarity "Buffer", received "Int"' + ); + + try { + const failingTest = () => + expect(Cl.buffer(Uint8Array.from([99, 117, 100]))).toBeBuff(Uint8Array.from(val)); + // make sure that it actually throws and therefore that the `catch` branch below is executed + expect(failingTest).toThrow("the received Buffer does not match the expected one"); + failingTest(); + } catch (e: any) { + expect(e.actual).toBe("0x637564"); + expect(e.expected).toBe("0x627463"); + } + + expect(() => + expect(Cl.buffer(Uint8Array.from(val))).not.toBeBuff(Uint8Array.from(val)) + ).toThrow("the received Buffer does match the expected one"); + }); +}); + +describe("tests lists", () => { + it("test simple list", () => { + expect(Cl.list([Cl.uint(1), Cl.uint(2)])).toBeList([Cl.uint(1), Cl.uint(2)]); + expect(Cl.list([Cl.uint(1), Cl.uint(2)])).toBeList([expect.toBeUint(1), expect.toBeUint(2)]); + + expect(() => + expect(Cl.list([Cl.uint(1), Cl.uint(2)])).toBeList([Cl.uint(1), Cl.uint(3)]) + ).toThrow("the received List does not match the expected one"); + + expect(() => + expect(Cl.list([Cl.uint(1), Cl.uint(2)])).not.toBeList([Cl.uint(1), Cl.uint(2)]) + ).toThrow("the received List does match the expected one"); + }); + + it("tests failing list with pretty diff format", () => { + const list = Cl.list([Cl.uint(1), Cl.uint(1)]); + + expect(list).toHaveClarityType(ClarityType.List); + + try { + const failingTest = () => expect(list).toBeList([Cl.uint(1), Cl.uint(2)]); + expect(failingTest).toThrow(); + failingTest(); + } catch (e: any) { + expect(e.message).toStrictEqual("the received List does not match the expected one"); + expect(e.actual).toBe("(list\n u1\n u1\n)"); + expect(e.expected).toBe("(list\n u1\n u2\n)"); + } + }); +}); + +describe("tests tuple", () => { + it("tests simple tuple", () => { + const addr = "ST2CY5V39NHDPWSXMW9QDT3HC3GD6Q6XX4CFRK9AG"; + const tuple = Cl.tuple({ + count: Cl.int(1), + owner: Cl.standardPrincipal(addr), + }); + + expect(tuple).toHaveClarityType(ClarityType.Tuple); + expect(tuple).toBeTuple({ + count: Cl.int(1), + owner: Cl.standardPrincipal(addr), + }); + + expect(tuple).toBeTuple({ + count: expect.toBeInt(1), + owner: expect.toBePrincipal(addr), + }); + + expect(() => + expect(tuple).toBeTuple({ + count: Cl.int(2), + owner: Cl.standardPrincipal(addr), + }) + ).toThrow("the received Tuple does not match the expected one"); + + expect(() => + expect(tuple).not.toBeTuple({ + count: Cl.int(1), + owner: Cl.standardPrincipal(addr), + }) + ).toThrow("the received Tuple does match the expected one"); + }); + + it("tests failing tuple with pretty diff format", () => { + const tuple = Cl.tuple({ + id: Cl.uint(1), + message: Cl.stringAscii("hello world"), + }); + + expect(tuple).toHaveClarityType(ClarityType.Tuple); + + try { + const failingTest = () => + expect(tuple).toBeTuple({ + id: Cl.uint(2), + message: Cl.stringAscii("hello world"), + }); + expect(failingTest).toThrow(); + failingTest(); + } catch (e: any) { + expect(e.message).toStrictEqual("the received Tuple does not match the expected one"); + expect(e.actual).toBe('{\n id: u1,\n message: "hello world"\n}'); + expect(e.expected).toBe('{\n id: u2,\n message: "hello world"\n}'); + } + }); +}); + +describe("test nested types", () => { + it("test tuple with complex types", () => { + const addr = "ST2CY5V39NHDPWSXMW9QDT3HC3GD6Q6XX4CFRK9AG"; + const complexTuple = Cl.tuple({ + id: Cl.uint(1), + items: Cl.list([ + Cl.tuple({ + id: Cl.uint(1), + owner: Cl.some(Cl.standardPrincipal(addr)), + valid: Cl.ok(Cl.uint(2)), + }), + Cl.tuple({ + id: Cl.uint(2), + owner: Cl.none(), + valid: Cl.error(Cl.uint(1000)), + }), + ]), + }); + + // show multiple ways of testing + + // toEqual + expect(complexTuple).toEqual(complexTuple); + + // toBeTuple with a Cl.tuple + expect(complexTuple).toBeTuple(complexTuple.data); + + // toBeTuple asymmetric matched + expect(complexTuple).toBeTuple({ + id: expect.toBeUint(1), + items: expect.toBeList([ + expect.toBeTuple({ + id: expect.toBeUint(1), + owner: expect.toBeSome(expect.toBePrincipal(addr)), + // test for ok and value + valid: expect.toBeOk(expect.toBeUint(2)), + }), + expect.toBeTuple({ + id: expect.toBeUint(2), + owner: expect.toBeNone(), + // test for error but not the value within + valid: expect.toHaveClarityType(ClarityType.ResponseErr), + }), + ]), + }); + }); + + it("display values on one line in message", () => { + const failingTest = () => + expect(Cl.ok(Cl.tuple({ counter: Cl.uint(1) }))).toBeOk(Cl.tuple({ counter: Cl.uint(2) })); + + expect(failingTest).toThrow("expected (ok { counter: u1 }) to be (ok { counter: u2 })"); + try { + failingTest(); + } catch (e: any) { + expect(e.actual).toBe("(ok {\n counter: u1\n})"); + expect(e.expected).toBe("(ok {\n counter: u2\n})"); + } + }); +}); diff --git a/components/clarinet-sdk/vitest-helpers/tests/formatCV.test.ts b/components/clarinet-sdk/vitest-helpers/tests/formatCV.test.ts new file mode 100644 index 000000000..09dc3a6bf --- /dev/null +++ b/components/clarinet-sdk/vitest-helpers/tests/formatCV.test.ts @@ -0,0 +1,138 @@ +import { describe, expect, it } from "vitest"; +import { formatCV } from "../src/formatCV"; +import { Cl } from "@stacks/transactions"; + +describe("test format of Stacks.js clarity values into clarity style strings", () => { + it("formats basic types", () => { + expect(formatCV(Cl.bool(true))).toStrictEqual("true"); + expect(formatCV(Cl.bool(false))).toStrictEqual("false"); + expect(formatCV(Cl.none())).toStrictEqual("none"); + + expect(formatCV(Cl.int(1))).toStrictEqual("1"); + expect(formatCV(Cl.int(10n))).toStrictEqual("10"); + + expect(formatCV(Cl.stringAscii("hello world!"))).toStrictEqual('"hello world!"'); + expect(formatCV(Cl.stringUtf8("hello world!"))).toStrictEqual('u"hello world!"'); + }); + + it("formats principal", () => { + const addr = "ST2CY5V39NHDPWSXMW9QDT3HC3GD6Q6XX4CFRK9AG"; + + expect(formatCV(Cl.standardPrincipal(addr))).toStrictEqual( + "'ST2CY5V39NHDPWSXMW9QDT3HC3GD6Q6XX4CFRK9AG" + ); + expect(formatCV(Cl.contractPrincipal(addr, "contract"))).toStrictEqual( + "'ST2CY5V39NHDPWSXMW9QDT3HC3GD6Q6XX4CFRK9AG.contract" + ); + }); + + it("formats optional some", () => { + expect(formatCV(Cl.some(Cl.uint(1)))).toStrictEqual("(some u1)"); + expect(formatCV(Cl.some(Cl.stringAscii("btc")))).toStrictEqual('(some "btc")'); + expect(formatCV(Cl.some(Cl.stringUtf8("stx 🚀")))).toStrictEqual('(some u"stx 🚀")'); + }); + + it("formats reponse", () => { + expect(formatCV(Cl.ok(Cl.uint(1)))).toStrictEqual("(ok u1)"); + expect(formatCV(Cl.error(Cl.uint(1)))).toStrictEqual("(err u1)"); + expect(formatCV(Cl.ok(Cl.some(Cl.uint(1))))).toStrictEqual("(ok (some u1))"); + expect(formatCV(Cl.ok(Cl.none()))).toStrictEqual("(ok none)"); + }); + + it("formats buffer", () => { + expect(formatCV(Cl.buffer(Uint8Array.from([98, 116, 99])))).toStrictEqual("0x627463"); + expect(formatCV(Cl.bufferFromAscii("stx"))).toStrictEqual("0x737478"); + }); + + it("formats lists", () => { + expect(formatCV(Cl.list([1, 2, 3].map(Cl.int)))).toStrictEqual("(list 1 2 3)"); + expect(formatCV(Cl.list([1, 2, 3].map(Cl.uint)))).toStrictEqual("(list u1 u2 u3)"); + expect(formatCV(Cl.list(["a", "b", "c"].map(Cl.stringUtf8)))).toStrictEqual( + '(list u"a" u"b" u"c")' + ); + + expect(formatCV(Cl.list([]))).toStrictEqual("(list)"); + }); + + it("can prettify lists on multiple lines", () => { + const list = Cl.list([1, 2, 3].map(Cl.int)); + expect(formatCV(list)).toStrictEqual("(list 1 2 3)"); + expect(formatCV(list, 2)).toStrictEqual("(list\n 1\n 2\n 3\n)"); + + expect(formatCV(Cl.list([]), 2)).toStrictEqual("(list)"); + }); + + it("formats tuples", () => { + expect(formatCV(Cl.tuple({ counter: Cl.uint(10) }))).toStrictEqual("{ counter: u10 }"); + expect( + formatCV(Cl.tuple({ counter: Cl.uint(10), state: Cl.ok(Cl.stringUtf8("valid")) })) + ).toStrictEqual('{ counter: u10, state: (ok u"valid") }'); + + expect(formatCV(Cl.tuple({}))).toStrictEqual("{}"); + }); + + it("can prettify tuples on multiple lines", () => { + const tuple = Cl.tuple({ counter: Cl.uint(10) }); + + expect(formatCV(tuple)).toStrictEqual("{ counter: u10 }"); + expect(formatCV(tuple, 2)).toStrictEqual("{\n counter: u10\n}"); + + expect(formatCV(Cl.tuple({}), 2)).toStrictEqual("{}"); + }); + + it("prettifies nested list and tuples", () => { + // test that the right indentation level is applied for nested composite types + const addr = "ST2CY5V39NHDPWSXMW9QDT3HC3GD6Q6XX4CFRK9AG"; + const value = Cl.tuple({ + id: Cl.uint(1), + messageAscii: Cl.stringAscii("hello world"), + someMessageUtf8: Cl.some(Cl.stringUtf8("hello world")), + items: Cl.some( + Cl.list([ + Cl.ok( + Cl.tuple({ + id: Cl.uint(1), + owner: Cl.some(Cl.standardPrincipal(addr)), + valid: Cl.ok(Cl.uint(2)), + history: Cl.some(Cl.list([Cl.uint(1), Cl.uint(2)])), + }) + ), + Cl.ok( + Cl.tuple({ + id: Cl.uint(2), + owner: Cl.none(), + valid: Cl.error(Cl.uint(1000)), + history: Cl.none(), + }) + ), + ]) + ), + }); + + const expected = `{ + id: u1, + messageAscii: "hello world", + someMessageUtf8: (some u"hello world"), + items: (some (list + (ok { + id: u1, + owner: (some 'ST2CY5V39NHDPWSXMW9QDT3HC3GD6Q6XX4CFRK9AG), + valid: (ok u2), + history: (some (list + u1 + u2 + )) + }) + (ok { + id: u2, + owner: none, + valid: (err u1000), + history: none + }) + )) +}`; + + const result = formatCV(value, 2); + expect(result).toStrictEqual(expected); + }); +}); diff --git a/components/clarinet-sdk/vitest-helpers/tsconfig.json b/components/clarinet-sdk/vitest-helpers/tsconfig.json new file mode 100644 index 000000000..299111e3e --- /dev/null +++ b/components/clarinet-sdk/vitest-helpers/tsconfig.json @@ -0,0 +1,26 @@ +{ + "compilerOptions": { + "target": "ESNext", + "useDefineForClassFields": true, + "module": "ESNext", + "lib": ["ESNext"], + "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + + /* Linting */ + "strict": true, + "noImplicitAny": true, + "noUncheckedIndexedAccess": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true + }, + "include": ["tests/", "src/"], + "exclude": ["node_modules"] +} diff --git a/components/clarinet-sdk/webpack.config.dev.js b/components/clarinet-sdk/webpack.config.dev.js index 7ce6f2d87..885a0dd94 100644 --- a/components/clarinet-sdk/webpack.config.dev.js +++ b/components/clarinet-sdk/webpack.config.dev.js @@ -1,24 +1,25 @@ const path = require("path"); +/** @typedef {import('webpack').Configuration} WebpackConfig **/ + +const target = "node18"; +const entry = { + index: "./src-ts/index.ts", + "vitest/index": "./src-ts/vitest/index.ts", +}; + // watch and build src-ts only // run npm run build to re-build rust sources -/** @typedef {import('webpack').Configuration} WebpackConfig **/ - /** @type WebpackConfig */ -const configBase = { +const configESM = { mode: "production", - entry: "./src-ts/index.ts", + entry, resolve: { extensions: [".ts", ".js"] }, optimization: { minimize: false, }, -}; - -/** @type WebpackConfig */ -const configESM = { - ...configBase, - target: "node20", + target, watch: true, output: { filename: "index.mjs", @@ -33,7 +34,7 @@ const configESM = { test: /\.ts$/, loader: "ts-loader", exclude: /node_modules/, - options: { configFile: "tsconfig.esm.json" }, + options: { configFile: "tsconfig.json" }, }, ], }, diff --git a/components/clarinet-sdk/webpack.config.js b/components/clarinet-sdk/webpack.config.js index 3ee351439..f9938dfb9 100644 --- a/components/clarinet-sdk/webpack.config.js +++ b/components/clarinet-sdk/webpack.config.js @@ -4,10 +4,16 @@ const CopyPlugin = require("copy-webpack-plugin"); /** @typedef {import('webpack').Configuration} WebpackConfig **/ +const target = "node18"; +const entry = { + index: "./src-ts/index.ts", + "vitest/index": "./src-ts/vitest/index.ts", +}; + /** @type WebpackConfig */ const configBase = { mode: "production", - entry: "./src-ts/index.ts", + entry, resolve: { extensions: [".ts", ".js"] }, optimization: { minimize: false, @@ -17,9 +23,9 @@ const configBase = { /** @type WebpackConfig */ const configESM = { ...configBase, - target: "node20", + target, output: { - filename: "index.mjs", + filename: "[name].mjs", path: path.resolve(__dirname, "dist/esm"), library: { type: "module", @@ -31,7 +37,7 @@ const configESM = { test: /\.ts$/, loader: "ts-loader", exclude: /node_modules/, - options: { configFile: "tsconfig.esm.json" }, + options: { configFile: "tsconfig.json" }, }, ], }, @@ -54,9 +60,9 @@ const configESM = { /** @type WebpackConfig */ const configCJS = { ...configBase, - target: "node20", + target, output: { - filename: "index.js", + filename: "[name].js", path: path.resolve(__dirname, "dist/cjs"), library: { type: "commonjs",