diff --git a/.changeset/bright-pumpkins-speak.md b/.changeset/bright-pumpkins-speak.md
new file mode 100644
index 00000000..73102fa5
--- /dev/null
+++ b/.changeset/bright-pumpkins-speak.md
@@ -0,0 +1,17 @@
+---
+'@stacks/connect-react': major
+'@stacks/connect': major
+---
+
+Switch to new RPC API with SIP-030
+
+BREAKING CHANGE:
+
+Adds the new `request` method to the `@stacks/connect` package.
+Which is the default way of interacting with Bitcoin and Stacks wallets.
+This update needs a slightly breaking change to the `showXyz` and `openXyz` methods.
+
+- ADDED `request` (UI) and `requestRaw` for calling wallet RPC methods
+- UPDATED `SessionData` and `UserSession` to only expose a light `UserData` with `profile.stxAddress`
+- UPDATED `StacksProvider` to only have a `request` method
+- REMOVED `BlockstackProvider`, `StacksProvider`
diff --git a/.changeset/lovely-jobs-fail.md b/.changeset/lovely-jobs-fail.md
new file mode 100644
index 00000000..e84cd6d3
--- /dev/null
+++ b/.changeset/lovely-jobs-fail.md
@@ -0,0 +1,10 @@
+---
+'@stacks/connect-ui': major
+---
+
+Update UI properties
+
+BREAKING CHANGE:
+
+- RENAMED `persistSelection` to `persistWalletSelect`
+- REMOVED `shouldUsePopup` from `@stacks/connect-ui`
diff --git a/.vscode/settings.json b/.vscode/settings.json
index 25fa6215..020e3902 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -1,3 +1,13 @@
{
- "typescript.tsdk": "node_modules/typescript/lib"
+ "typescript.tsdk": "node_modules/typescript/lib",
+
+ // Jest -> Vitest: Run
+ "jestrunner.jestCommand": "npx vitest run",
+
+ // Jest -> Vitest: Debug (Hack)
+ "jestrunner.jestPath": "node_modules/.bin/vitest",
+ "jestrunner.runOptions": ["--"],
+ "jestrunner.debugOptions": {
+ "args": ["run"]
+ }
}
diff --git a/README.md b/README.md
index 91de42a4..48d1562d 100644
--- a/README.md
+++ b/README.md
@@ -20,25 +20,75 @@ This repository includes three packages:
- [`@stacks/connect-react`](./packages/connect-react): A wrapper library for making `@stacks/connect` use in React even easier
- [`@stacks/connect-ui`](./packages/connect-ui): A web-component UI for displaying an intro modal in Stacks web-apps during authentication _(used in the background by `@stacks/connect`)_.
-## 🌎 More Information
-
-The [Stacks documentation website](https://docs.stacks.co/build-apps/overview) includes more examples for building apps using Connect.
-
-It also includes guides for various aspects of Stacks application development:
-
-- [Authentication](https://docs.stacks.co/build-apps/references/authentication)
-- [Transactions](https://docs.stacks.co/understand-stacks/technical-specs#transactions)
-- [Data storage](https://docs.stacks.co/build-apps/references/gaia#understand-data-storage)
-
-## 🐛 Bugs and feature requests
-
-If you encounter a bug or have a feature request, we encourage you to follow the steps below:
-
-1. **Search for existing issues:** Before submitting a new issue, please search [existing and closed issues](https://github.com/hirosystems/connect/issues) to check if a similar problem or feature request has already been reported.
-1. **Open a new issue:** If it hasn't been addressed, please [open a new issue](https://github.com/hirosystems/connect/issues/new/choose). Choose the appropriate issue template and provide as much detail as possible, including steps to reproduce the bug or a clear description of the requested feature.
-1. **Evaluation SLA:** Our team reads and evaluates all the issues and pull requests. We are avaliable Monday to Friday and we make a best effort to respond within 7 business days.
-
-Please **do not** use the issue tracker for personal support requests or to ask for the status of a transaction. You'll find help at the [#stacks-js Discord channel](https://stacks.chat/).
+## 🛠️ Wallet Implementation Guide
+
+Wallets implement a "Provider" interface.
+The latest spec uses a simple JS Object exposing a `.request(method: string, params?: object)` method.
+
+Pseudo-code:
+
+```ts
+window.MyProvider = {
+ async request(method, params) {
+ // Somehow communicate with the wallet (e.g. via events)
+
+ // Recommendation: Create a JSON RPC 2.0 request object
+ // https://www.jsonrpc.org/specification
+
+ return Promise.resolve({
+ // Respond with a JSON RPC 2.0 response object
+ id: crypto.randomUUID(), // required, same as request
+ jsonrpc: '2.0', // required
+
+ // `.result` is required on success
+ result: {
+ // object matching specified RPC methods
+ },
+
+ // `.error` is required on error
+ error: {
+ // Use existing codes from https://www.jsonrpc.org/specification#error_object
+ code: number, // required, integer
+ message: string, // recommended, single sentence
+ data: object, // optional
+ },
+ });
+ },
+ isMyWallet: true, // optional, a way of identifying the wallet for developers
+};
+
+window.wbip_providers = window.wbip_providers || [];
+window.wbip_providers.push({
+ // `WbipProvider` type
+ /** The global "path" of the provider (e.g. `"MyProvider"` if registered at `window.MyProvider`) */
+ id: 'MyProvider',
+ /** The name of the provider, as displayed to the user */
+ name: 'My Wallet';
+ /** The data URL of an image to show (e.g. `data:image/png;base64,iVBORw0...`) @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Data_URLs */
+ icon?: 'data:image/png;base64,iVBORw0...';
+ /** Web URL of the provider */
+ webUrl?: 'https://mywallet.example.com';
+
+ // Addional URLs
+ chromeWebStoreUrl?: string;
+ mozillaAddOnsUrl?: string;
+ googlePlayStoreUrl?: string;
+ iOSAppStoreUrl?: string;
+});
+```
+
+### JSON RPC 2.0
+
+Wallets may add their own unstandardized methods.
+However, the minimum recommended methods are:
+
+- `getAddresses` [WBIP](https://wbips.netlify.app/request_api/getAddresses)
+- `signPsbt` [WBIP](https://wbips.netlify.app/request_api/signPsbt)
+- `stx_getAddresses` [SIP-030](https://github.com/janniks/sips/blob/main/sips/sip-030/sip-030-wallet-interface.md)
+- `stx_transferStx` [SIP-030](https://github.com/janniks/sips/blob/main/sips/sip-030/sip-030-wallet-interface.md)
+- `stx_callContract` [SIP-030](https://github.com/janniks/sips/blob/main/sips/sip-030/sip-030-wallet-interface.md)
+- `stx_signMessage` [SIP-030](https://github.com/janniks/sips/blob/main/sips/sip-030/sip-030-wallet-interface.md)
+- `stx_signStructuredMessage` [SIP-030](https://github.com/janniks/sips/blob/main/sips/sip-030/sip-030-wallet-interface.md)
## 🎁 Contribute
@@ -59,3 +109,14 @@ Join our community and stay connected with the latest updates and discussions:
- [Join our Discord community chat](https://stacks.chat/) to engage with other users, ask questions, and participate in discussions.
- [Visit hiro.so](https://www.hiro.so/) for updates and subcribing to the mailing list.
- Follow [Hiro on Twitter.](https://twitter.com/hirosystems)
+
+### MIGRATION TODO
+
+#### TODO
+
+- Add `PostConditionModeName` to all options (new and old) — This might have been missing since the v7 release.
+- Strip unserializable fields from RawLegacy wrapper just in case.
+- Remove exports from LEGACY_XYZ
+- Make hex to base64 for psbt
+
+Search for the below and replace with inline return.
diff --git a/package-lock.json b/package-lock.json
index b321d469..f8307f0b 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -33,7 +33,8 @@
"react-dom": "^18.3.1",
"tsup": "^8.3.5",
"typedoc": "^0.26.10",
- "typescript": "^5.3.2"
+ "typescript": "^5.3.2",
+ "vitest": "^2.1.8"
}
},
"node_modules/@alloc/quick-lru": {
@@ -7006,30 +7007,14 @@
"license": "MIT"
},
"node_modules/@scure/base": {
- "version": "1.1.9",
- "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.1.9.tgz",
- "integrity": "sha512-8YKhl8GHiNI/pU2VMaofa2Tor7PJRAjwQLBBuilkJ9L5+13yVbC7JO/wS7piioAvPSwR3JKM1IJ/u4xQzbcXKg==",
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.2.4.tgz",
+ "integrity": "sha512-5Yy9czTO47mqz+/J8GM6GIId4umdCk1wc1q8rKERQulIoc8VP9pzDcghv10Tl2E7R96ZUx/PhND3ESYUQX8NuQ==",
"license": "MIT",
"funding": {
"url": "https://paulmillr.com/funding/"
}
},
- "node_modules/@scure/bip39": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.1.0.tgz",
- "integrity": "sha512-pwrPOS16VeTKg98dYXQyIjJEcWfz7/1YJIwxUEPFfQPtc86Ym/1sVgQ2RLoD43AazMk2l/unK4ITySSpW2+82w==",
- "funding": [
- {
- "type": "individual",
- "url": "https://paulmillr.com/funding/"
- }
- ],
- "license": "MIT",
- "dependencies": {
- "@noble/hashes": "~1.1.1",
- "@scure/base": "~1.1.0"
- }
- },
"node_modules/@shikijs/core": {
"version": "1.22.0",
"resolved": "https://registry.npmjs.org/@shikijs/core/-/core-1.22.0.tgz",
@@ -7173,21 +7158,6 @@
"dev": true,
"license": "MIT"
},
- "node_modules/@stacks/auth": {
- "version": "7.0.0",
- "resolved": "https://registry.npmjs.org/@stacks/auth/-/auth-7.0.0.tgz",
- "integrity": "sha512-p0/FJy5jRRgtsVzGpWDgNE0fi/ioPCL3S1tZHRsvdKO7moquHWnTlBV/9yVkGbd+V0kD95Sd0euX5pwWA82MhA==",
- "license": "MIT",
- "dependencies": {
- "@noble/secp256k1": "1.7.1",
- "@stacks/common": "^7.0.0",
- "@stacks/encryption": "^7.0.0",
- "@stacks/network": "^7.0.0",
- "@stacks/profile": "^7.0.0",
- "cross-fetch": "^3.1.5",
- "jsontokens": "^4.0.1"
- }
- },
"node_modules/@stacks/common": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/@stacks/common/-/common-7.0.0.tgz",
@@ -7206,22 +7176,6 @@
"resolved": "packages/connect-ui",
"link": true
},
- "node_modules/@stacks/encryption": {
- "version": "7.0.0",
- "resolved": "https://registry.npmjs.org/@stacks/encryption/-/encryption-7.0.0.tgz",
- "integrity": "sha512-kQhiurn8s0DV6OALqEy2Aqwp2DNJUD41S1bnD/+UESyEDD5XRqL84MEo7mCL8pTPr+FVdhT+diEIPeAfEtbTPQ==",
- "license": "MIT",
- "dependencies": {
- "@noble/hashes": "1.1.5",
- "@noble/secp256k1": "1.7.1",
- "@scure/bip39": "1.1.0",
- "@stacks/common": "^7.0.0",
- "base64-js": "^1.5.1",
- "bs58": "^5.0.0",
- "ripemd160-min": "^0.0.6",
- "varuint-bitcoin": "^1.1.2"
- }
- },
"node_modules/@stacks/eslint-config": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/@stacks/eslint-config/-/eslint-config-2.0.0.tgz",
@@ -12057,6 +12011,92 @@
"node": ">=12"
}
},
+ "node_modules/@vitest/expect": {
+ "version": "2.1.8",
+ "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-2.1.8.tgz",
+ "integrity": "sha512-8ytZ/fFHq2g4PJVAtDX57mayemKgDR6X3Oa2Foro+EygiOJHUXhCqBAAKQYYajZpFoIfvBCF1j6R6IYRSIUFuw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@vitest/spy": "2.1.8",
+ "@vitest/utils": "2.1.8",
+ "chai": "^5.1.2",
+ "tinyrainbow": "^1.2.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/vitest"
+ }
+ },
+ "node_modules/@vitest/pretty-format": {
+ "version": "2.1.8",
+ "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-2.1.8.tgz",
+ "integrity": "sha512-9HiSZ9zpqNLKlbIDRWOnAWqgcA7xu+8YxXSekhr0Ykab7PAYFkhkwoqVArPOtJhPmYeE2YHgKZlj3CP36z2AJQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "tinyrainbow": "^1.2.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/vitest"
+ }
+ },
+ "node_modules/@vitest/runner": {
+ "version": "2.1.8",
+ "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-2.1.8.tgz",
+ "integrity": "sha512-17ub8vQstRnRlIU5k50bG+QOMLHRhYPAna5tw8tYbj+jzjcspnwnwtPtiOlkuKC4+ixDPTuLZiqiWWQ2PSXHVg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@vitest/utils": "2.1.8",
+ "pathe": "^1.1.2"
+ },
+ "funding": {
+ "url": "https://opencollective.com/vitest"
+ }
+ },
+ "node_modules/@vitest/snapshot": {
+ "version": "2.1.8",
+ "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-2.1.8.tgz",
+ "integrity": "sha512-20T7xRFbmnkfcmgVEz+z3AU/3b0cEzZOt/zmnvZEctg64/QZbSDJEVm9fLnnlSi74KibmRsO9/Qabi+t0vCRPg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@vitest/pretty-format": "2.1.8",
+ "magic-string": "^0.30.12",
+ "pathe": "^1.1.2"
+ },
+ "funding": {
+ "url": "https://opencollective.com/vitest"
+ }
+ },
+ "node_modules/@vitest/spy": {
+ "version": "2.1.8",
+ "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-2.1.8.tgz",
+ "integrity": "sha512-5swjf2q95gXeYPevtW0BLk6H8+bPlMb4Vw/9Em4hFxDcaOxS+e0LOX4yqNxoHzMR2akEB2xfpnWUzkZokmgWDg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "tinyspy": "^3.0.2"
+ },
+ "funding": {
+ "url": "https://opencollective.com/vitest"
+ }
+ },
+ "node_modules/@vitest/utils": {
+ "version": "2.1.8",
+ "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-2.1.8.tgz",
+ "integrity": "sha512-dwSoui6djdwbfFmIgbIjX2ZhIoG7Ex/+xpxyiEgIGzjliY8xGkcpITKTlp6B4MgtGkF2ilvm97cPM96XZaAgcA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@vitest/pretty-format": "2.1.8",
+ "loupe": "^3.1.2",
+ "tinyrainbow": "^1.2.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/vitest"
+ }
+ },
"node_modules/@webassemblyjs/ast": {
"version": "1.12.1",
"resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.12.1.tgz",
@@ -12921,6 +12961,16 @@
"util": "^0.12.5"
}
},
+ "node_modules/assertion-error": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz",
+ "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ }
+ },
"node_modules/ast-types": {
"version": "0.16.1",
"resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.16.1.tgz",
@@ -13460,15 +13510,6 @@
"node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
}
},
- "node_modules/bs58": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/bs58/-/bs58-5.0.0.tgz",
- "integrity": "sha512-r+ihvQJvahgYT50JD05dyJNKlmmSlMoOGwn1lCcEzanPglg7TxYjioQUYehQ9mAR/+hOSd2jRc/Z2y5UxBymvQ==",
- "license": "MIT",
- "dependencies": {
- "base-x": "^4.0.0"
- }
- },
"node_modules/bser": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz",
@@ -13905,6 +13946,23 @@
"url": "https://github.com/sponsors/wooorm"
}
},
+ "node_modules/chai": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/chai/-/chai-5.1.2.tgz",
+ "integrity": "sha512-aGtmf24DW6MLHHG5gCx4zaI3uBq3KRtxeVs0DjFH6Z0rDNbsvTxFASFvdj79pxjxZ8/5u3PIiN3IwEIQkiiuPw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "assertion-error": "^2.0.1",
+ "check-error": "^2.1.1",
+ "deep-eql": "^5.0.1",
+ "loupe": "^3.1.0",
+ "pathval": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
"node_modules/chalk": {
"version": "5.3.0",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz",
@@ -13947,6 +14005,16 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/check-error": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/check-error/-/check-error-2.1.1.tgz",
+ "integrity": "sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 16"
+ }
+ },
"node_modules/chokidar": {
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz",
@@ -16168,6 +16236,16 @@
}
}
},
+ "node_modules/deep-eql": {
+ "version": "5.0.2",
+ "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-5.0.2.tgz",
+ "integrity": "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
"node_modules/deep-equal": {
"version": "2.2.3",
"resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-2.2.3.tgz",
@@ -18122,6 +18200,16 @@
"node": ">=6"
}
},
+ "node_modules/expect-type": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.1.0.tgz",
+ "integrity": "sha512-bFi65yM+xZgk+u/KRIpekdSYkTB5W1pEf0Lt8Q8Msh7b+eQ7LXVtIB1Bkm4fvclDEL1b2CZkMhv2mOeF8tMdkA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=12.0.0"
+ }
+ },
"node_modules/exponential-backoff": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/exponential-backoff/-/exponential-backoff-3.1.1.tgz",
@@ -23242,6 +23330,13 @@
"loose-envify": "cli.js"
}
},
+ "node_modules/loupe": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.1.2.tgz",
+ "integrity": "sha512-23I4pFZHmAemUnz8WZXbYRSKYj801VDaNv9ETuMh7IrMc7VuVVSo+Z9iLE3ni30+U48iDWfi30d3twAXBYmnCg==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/lower-case": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz",
@@ -25790,6 +25885,16 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/pathval": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/pathval/-/pathval-2.0.0.tgz",
+ "integrity": "sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 14.16"
+ }
+ },
"node_modules/peek-stream": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/peek-stream/-/peek-stream-1.1.3.tgz",
@@ -28507,14 +28612,6 @@
"node": "*"
}
},
- "node_modules/ripemd160-min": {
- "version": "0.0.6",
- "resolved": "https://registry.npmjs.org/ripemd160-min/-/ripemd160-min-0.0.6.tgz",
- "integrity": "sha512-+GcJgQivhs6S9qvLogusiTcS9kQUfgR75whKuy5jIhuiOfQuJ8fjqxV6EGD5duH1Y/FawFUMtMhyeq3Fbnib8A==",
- "engines": {
- "node": ">=8"
- }
- },
"node_modules/rollup": {
"version": "4.24.0",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.24.0.tgz",
@@ -28625,6 +28722,7 @@
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
+ "dev": true,
"funding": [
{
"type": "github",
@@ -28945,6 +29043,13 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/siginfo": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz",
+ "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==",
+ "dev": true,
+ "license": "ISC"
+ },
"node_modules/signal-exit": {
"version": "3.0.7",
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
@@ -29288,6 +29393,13 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/stackback": {
+ "version": "0.0.2",
+ "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz",
+ "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/stackframe": {
"version": "1.3.4",
"resolved": "https://registry.npmjs.org/stackframe/-/stackframe-1.3.4.tgz",
@@ -29305,6 +29417,13 @@
"node": ">= 0.8"
}
},
+ "node_modules/std-env": {
+ "version": "3.8.0",
+ "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.8.0.tgz",
+ "integrity": "sha512-Bc3YwwCB+OzldMxOXJIIvC6cPRWr/LxOp48CdQTOkPyk/t4JWWJbrilwBd7RJzKV8QW7tJkcgAmeuLLJugl5/w==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/stencil-tailwind-plugin": {
"version": "1.8.0",
"resolved": "https://registry.npmjs.org/stencil-tailwind-plugin/-/stencil-tailwind-plugin-1.8.0.tgz",
@@ -30500,6 +30619,13 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/tinybench": {
+ "version": "2.9.0",
+ "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz",
+ "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/tinyexec": {
"version": "0.3.1",
"resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.1.tgz",
@@ -30549,6 +30675,36 @@
"url": "https://github.com/sponsors/jonschlinkert"
}
},
+ "node_modules/tinypool": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.0.2.tgz",
+ "integrity": "sha512-al6n+QEANGFOMf/dmUMsuS5/r9B06uwlyNjZZql/zv8J7ybHCgoihBNORZCY2mzUuAnomQa2JdhyHKzZxPCrFA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^18.0.0 || >=20.0.0"
+ }
+ },
+ "node_modules/tinyrainbow": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-1.2.0.tgz",
+ "integrity": "sha512-weEDEq7Z5eTHPDh4xjX789+fHfF+P8boiFB+0vbWzpbnbsEr/GRaohi/uMKxg8RZMXnl1ItAi/IUHWMsjDV7kQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/tinyspy": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-3.0.2.tgz",
+ "integrity": "sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
"node_modules/tmp": {
"version": "0.0.33",
"resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz",
@@ -31715,15 +31871,6 @@
"node": "^14.17.0 || ^16.13.0 || >=18.0.0"
}
},
- "node_modules/varuint-bitcoin": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/varuint-bitcoin/-/varuint-bitcoin-1.1.2.tgz",
- "integrity": "sha512-4EVb+w4rx+YfVM32HQX42AbbT7/1f5zwAYhIujKXKk8NQK+JfRVl3pqT3hjNn/L+RstigmGGKVwHA/P0wgITZw==",
- "license": "MIT",
- "dependencies": {
- "safe-buffer": "^5.1.1"
- }
- },
"node_modules/vary": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
@@ -31784,6 +31931,7 @@
"integrity": "sha512-ifW3Lb2sMdX+WU91s3R0FyQlAyLxOzCSCP37ujw0+r5POeHPwe6udWVIElKQq8gk3t7b8rkmvqC6IHBpCff4GQ==",
"dev": true,
"license": "MIT",
+ "peer": true,
"dependencies": {
"esbuild": "^0.18.10",
"postcss": "^8.4.27",
@@ -31834,6 +31982,526 @@
}
}
},
+ "node_modules/vite-node": {
+ "version": "2.1.8",
+ "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-2.1.8.tgz",
+ "integrity": "sha512-uPAwSr57kYjAUux+8E2j0q0Fxpn8M9VoyfGiRI8Kfktz9NcYMCenwY5RnZxnF1WTu3TGiYipirIzacLL3VVGFg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "cac": "^6.7.14",
+ "debug": "^4.3.7",
+ "es-module-lexer": "^1.5.4",
+ "pathe": "^1.1.2",
+ "vite": "^5.0.0"
+ },
+ "bin": {
+ "vite-node": "vite-node.mjs"
+ },
+ "engines": {
+ "node": "^18.0.0 || >=20.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/vitest"
+ }
+ },
+ "node_modules/vite-node/node_modules/@esbuild/aix-ppc64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz",
+ "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "aix"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/vite-node/node_modules/@esbuild/android-arm": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz",
+ "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/vite-node/node_modules/@esbuild/android-arm64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz",
+ "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/vite-node/node_modules/@esbuild/android-x64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz",
+ "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/vite-node/node_modules/@esbuild/darwin-arm64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz",
+ "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/vite-node/node_modules/@esbuild/darwin-x64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz",
+ "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/vite-node/node_modules/@esbuild/freebsd-arm64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz",
+ "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/vite-node/node_modules/@esbuild/freebsd-x64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz",
+ "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/vite-node/node_modules/@esbuild/linux-arm": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz",
+ "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/vite-node/node_modules/@esbuild/linux-arm64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz",
+ "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/vite-node/node_modules/@esbuild/linux-ia32": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz",
+ "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/vite-node/node_modules/@esbuild/linux-loong64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz",
+ "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==",
+ "cpu": [
+ "loong64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/vite-node/node_modules/@esbuild/linux-mips64el": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz",
+ "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==",
+ "cpu": [
+ "mips64el"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/vite-node/node_modules/@esbuild/linux-ppc64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz",
+ "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/vite-node/node_modules/@esbuild/linux-riscv64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz",
+ "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/vite-node/node_modules/@esbuild/linux-s390x": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz",
+ "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==",
+ "cpu": [
+ "s390x"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/vite-node/node_modules/@esbuild/linux-x64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz",
+ "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/vite-node/node_modules/@esbuild/netbsd-x64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz",
+ "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "netbsd"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/vite-node/node_modules/@esbuild/openbsd-x64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz",
+ "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openbsd"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/vite-node/node_modules/@esbuild/sunos-x64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz",
+ "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "sunos"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/vite-node/node_modules/@esbuild/win32-arm64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz",
+ "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/vite-node/node_modules/@esbuild/win32-ia32": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz",
+ "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/vite-node/node_modules/@esbuild/win32-x64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz",
+ "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/vite-node/node_modules/es-module-lexer": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.6.0.tgz",
+ "integrity": "sha512-qqnD1yMU6tk/jnaMosogGySTZP8YtUgAffA9nMN+E/rjxcfRQ6IEk7IiozUjgxKoFHBGjTLnrHB/YC45r/59EQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/vite-node/node_modules/esbuild": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz",
+ "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==",
+ "dev": true,
+ "hasInstallScript": true,
+ "license": "MIT",
+ "bin": {
+ "esbuild": "bin/esbuild"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "optionalDependencies": {
+ "@esbuild/aix-ppc64": "0.21.5",
+ "@esbuild/android-arm": "0.21.5",
+ "@esbuild/android-arm64": "0.21.5",
+ "@esbuild/android-x64": "0.21.5",
+ "@esbuild/darwin-arm64": "0.21.5",
+ "@esbuild/darwin-x64": "0.21.5",
+ "@esbuild/freebsd-arm64": "0.21.5",
+ "@esbuild/freebsd-x64": "0.21.5",
+ "@esbuild/linux-arm": "0.21.5",
+ "@esbuild/linux-arm64": "0.21.5",
+ "@esbuild/linux-ia32": "0.21.5",
+ "@esbuild/linux-loong64": "0.21.5",
+ "@esbuild/linux-mips64el": "0.21.5",
+ "@esbuild/linux-ppc64": "0.21.5",
+ "@esbuild/linux-riscv64": "0.21.5",
+ "@esbuild/linux-s390x": "0.21.5",
+ "@esbuild/linux-x64": "0.21.5",
+ "@esbuild/netbsd-x64": "0.21.5",
+ "@esbuild/openbsd-x64": "0.21.5",
+ "@esbuild/sunos-x64": "0.21.5",
+ "@esbuild/win32-arm64": "0.21.5",
+ "@esbuild/win32-ia32": "0.21.5",
+ "@esbuild/win32-x64": "0.21.5"
+ }
+ },
+ "node_modules/vite-node/node_modules/vite": {
+ "version": "5.4.11",
+ "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.11.tgz",
+ "integrity": "sha512-c7jFQRklXua0mTzneGW9QVyxFjUgwcihC4bXEtujIo2ouWCe1Ajt/amn2PCxYnhYfd5k09JX3SB7OYWFKYqj8Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "esbuild": "^0.21.3",
+ "postcss": "^8.4.43",
+ "rollup": "^4.20.0"
+ },
+ "bin": {
+ "vite": "bin/vite.js"
+ },
+ "engines": {
+ "node": "^18.0.0 || >=20.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/vitejs/vite?sponsor=1"
+ },
+ "optionalDependencies": {
+ "fsevents": "~2.3.3"
+ },
+ "peerDependencies": {
+ "@types/node": "^18.0.0 || >=20.0.0",
+ "less": "*",
+ "lightningcss": "^1.21.0",
+ "sass": "*",
+ "sass-embedded": "*",
+ "stylus": "*",
+ "sugarss": "*",
+ "terser": "^5.4.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/node": {
+ "optional": true
+ },
+ "less": {
+ "optional": true
+ },
+ "lightningcss": {
+ "optional": true
+ },
+ "sass": {
+ "optional": true
+ },
+ "sass-embedded": {
+ "optional": true
+ },
+ "stylus": {
+ "optional": true
+ },
+ "sugarss": {
+ "optional": true
+ },
+ "terser": {
+ "optional": true
+ }
+ }
+ },
"node_modules/vite/node_modules/@esbuild/android-arm": {
"version": "0.18.20",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.18.20.tgz",
@@ -31847,6 +32515,7 @@
"os": [
"android"
],
+ "peer": true,
"engines": {
"node": ">=12"
}
@@ -31864,6 +32533,7 @@
"os": [
"android"
],
+ "peer": true,
"engines": {
"node": ">=12"
}
@@ -31881,6 +32551,7 @@
"os": [
"android"
],
+ "peer": true,
"engines": {
"node": ">=12"
}
@@ -31898,6 +32569,7 @@
"os": [
"darwin"
],
+ "peer": true,
"engines": {
"node": ">=12"
}
@@ -31915,6 +32587,7 @@
"os": [
"darwin"
],
+ "peer": true,
"engines": {
"node": ">=12"
}
@@ -31932,6 +32605,7 @@
"os": [
"freebsd"
],
+ "peer": true,
"engines": {
"node": ">=12"
}
@@ -31949,6 +32623,7 @@
"os": [
"freebsd"
],
+ "peer": true,
"engines": {
"node": ">=12"
}
@@ -31966,6 +32641,7 @@
"os": [
"linux"
],
+ "peer": true,
"engines": {
"node": ">=12"
}
@@ -31983,6 +32659,7 @@
"os": [
"linux"
],
+ "peer": true,
"engines": {
"node": ">=12"
}
@@ -32000,6 +32677,7 @@
"os": [
"linux"
],
+ "peer": true,
"engines": {
"node": ">=12"
}
@@ -32017,6 +32695,7 @@
"os": [
"linux"
],
+ "peer": true,
"engines": {
"node": ">=12"
}
@@ -32034,6 +32713,7 @@
"os": [
"linux"
],
+ "peer": true,
"engines": {
"node": ">=12"
}
@@ -32051,6 +32731,7 @@
"os": [
"linux"
],
+ "peer": true,
"engines": {
"node": ">=12"
}
@@ -32068,6 +32749,7 @@
"os": [
"linux"
],
+ "peer": true,
"engines": {
"node": ">=12"
}
@@ -32085,6 +32767,7 @@
"os": [
"linux"
],
+ "peer": true,
"engines": {
"node": ">=12"
}
@@ -32102,6 +32785,7 @@
"os": [
"linux"
],
+ "peer": true,
"engines": {
"node": ">=12"
}
@@ -32119,6 +32803,7 @@
"os": [
"netbsd"
],
+ "peer": true,
"engines": {
"node": ">=12"
}
@@ -32136,6 +32821,7 @@
"os": [
"openbsd"
],
+ "peer": true,
"engines": {
"node": ">=12"
}
@@ -32153,6 +32839,7 @@
"os": [
"sunos"
],
+ "peer": true,
"engines": {
"node": ">=12"
}
@@ -32170,6 +32857,7 @@
"os": [
"win32"
],
+ "peer": true,
"engines": {
"node": ">=12"
}
@@ -32187,6 +32875,7 @@
"os": [
"win32"
],
+ "peer": true,
"engines": {
"node": ">=12"
}
@@ -32204,6 +32893,7 @@
"os": [
"win32"
],
+ "peer": true,
"engines": {
"node": ">=12"
}
@@ -32215,6 +32905,7 @@
"dev": true,
"hasInstallScript": true,
"license": "MIT",
+ "peer": true,
"bin": {
"esbuild": "bin/esbuild"
},
@@ -32252,6 +32943,7 @@
"integrity": "sha512-GVsDdsbJzzy4S/v3dqWPJ7EfvZJfCHiDqe80IyrF59LYuP+e6U1LJoUqeuqRbwAWoMNoXivMNeNAOf5E22VA1w==",
"dev": true,
"license": "MIT",
+ "peer": true,
"bin": {
"rollup": "dist/bin/rollup"
},
@@ -32263,6 +32955,571 @@
"fsevents": "~2.3.2"
}
},
+ "node_modules/vitest": {
+ "version": "2.1.8",
+ "resolved": "https://registry.npmjs.org/vitest/-/vitest-2.1.8.tgz",
+ "integrity": "sha512-1vBKTZskHw/aosXqQUlVWWlGUxSJR8YtiyZDJAFeW2kPAeX6S3Sool0mjspO+kXLuxVWlEDDowBAeqeAQefqLQ==",
+ "dev": true,
+ "dependencies": {
+ "@vitest/expect": "2.1.8",
+ "@vitest/mocker": "2.1.8",
+ "@vitest/pretty-format": "^2.1.8",
+ "@vitest/runner": "2.1.8",
+ "@vitest/snapshot": "2.1.8",
+ "@vitest/spy": "2.1.8",
+ "@vitest/utils": "2.1.8",
+ "chai": "^5.1.2",
+ "debug": "^4.3.7",
+ "expect-type": "^1.1.0",
+ "magic-string": "^0.30.12",
+ "pathe": "^1.1.2",
+ "std-env": "^3.8.0",
+ "tinybench": "^2.9.0",
+ "tinyexec": "^0.3.1",
+ "tinypool": "^1.0.1",
+ "tinyrainbow": "^1.2.0",
+ "vite": "^5.0.0",
+ "vite-node": "2.1.8",
+ "why-is-node-running": "^2.3.0"
+ },
+ "bin": {
+ "vitest": "vitest.mjs"
+ },
+ "engines": {
+ "node": "^18.0.0 || >=20.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/vitest"
+ },
+ "peerDependencies": {
+ "@edge-runtime/vm": "*",
+ "@types/node": "^18.0.0 || >=20.0.0",
+ "@vitest/browser": "2.1.8",
+ "@vitest/ui": "2.1.8",
+ "happy-dom": "*",
+ "jsdom": "*"
+ },
+ "peerDependenciesMeta": {
+ "@edge-runtime/vm": {
+ "optional": true
+ },
+ "@types/node": {
+ "optional": true
+ },
+ "@vitest/browser": {
+ "optional": true
+ },
+ "@vitest/ui": {
+ "optional": true
+ },
+ "happy-dom": {
+ "optional": true
+ },
+ "jsdom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/vitest/node_modules/@esbuild/aix-ppc64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz",
+ "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "aix"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/vitest/node_modules/@esbuild/android-arm": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz",
+ "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/vitest/node_modules/@esbuild/android-arm64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz",
+ "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/vitest/node_modules/@esbuild/android-x64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz",
+ "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/vitest/node_modules/@esbuild/darwin-arm64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz",
+ "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/vitest/node_modules/@esbuild/darwin-x64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz",
+ "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/vitest/node_modules/@esbuild/freebsd-arm64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz",
+ "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/vitest/node_modules/@esbuild/freebsd-x64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz",
+ "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/vitest/node_modules/@esbuild/linux-arm": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz",
+ "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/vitest/node_modules/@esbuild/linux-arm64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz",
+ "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/vitest/node_modules/@esbuild/linux-ia32": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz",
+ "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/vitest/node_modules/@esbuild/linux-loong64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz",
+ "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==",
+ "cpu": [
+ "loong64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/vitest/node_modules/@esbuild/linux-mips64el": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz",
+ "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==",
+ "cpu": [
+ "mips64el"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/vitest/node_modules/@esbuild/linux-ppc64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz",
+ "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/vitest/node_modules/@esbuild/linux-riscv64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz",
+ "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/vitest/node_modules/@esbuild/linux-s390x": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz",
+ "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==",
+ "cpu": [
+ "s390x"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/vitest/node_modules/@esbuild/linux-x64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz",
+ "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/vitest/node_modules/@esbuild/netbsd-x64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz",
+ "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "netbsd"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/vitest/node_modules/@esbuild/openbsd-x64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz",
+ "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "openbsd"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/vitest/node_modules/@esbuild/sunos-x64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz",
+ "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "sunos"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/vitest/node_modules/@esbuild/win32-arm64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz",
+ "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/vitest/node_modules/@esbuild/win32-ia32": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz",
+ "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/vitest/node_modules/@esbuild/win32-x64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz",
+ "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/vitest/node_modules/@vitest/mocker": {
+ "version": "2.1.8",
+ "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-2.1.8.tgz",
+ "integrity": "sha512-7guJ/47I6uqfttp33mgo6ga5Gr1VnL58rcqYKyShoRK9ebu8T5Rs6HN3s1NABiBeVTdWNrwUMcHH54uXZBN4zA==",
+ "dev": true,
+ "dependencies": {
+ "@vitest/spy": "2.1.8",
+ "estree-walker": "^3.0.3",
+ "magic-string": "^0.30.12"
+ },
+ "funding": {
+ "url": "https://opencollective.com/vitest"
+ },
+ "peerDependencies": {
+ "msw": "^2.4.9",
+ "vite": "^5.0.0"
+ },
+ "peerDependenciesMeta": {
+ "msw": {
+ "optional": true
+ },
+ "vite": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/vitest/node_modules/esbuild": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz",
+ "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==",
+ "dev": true,
+ "hasInstallScript": true,
+ "bin": {
+ "esbuild": "bin/esbuild"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "optionalDependencies": {
+ "@esbuild/aix-ppc64": "0.21.5",
+ "@esbuild/android-arm": "0.21.5",
+ "@esbuild/android-arm64": "0.21.5",
+ "@esbuild/android-x64": "0.21.5",
+ "@esbuild/darwin-arm64": "0.21.5",
+ "@esbuild/darwin-x64": "0.21.5",
+ "@esbuild/freebsd-arm64": "0.21.5",
+ "@esbuild/freebsd-x64": "0.21.5",
+ "@esbuild/linux-arm": "0.21.5",
+ "@esbuild/linux-arm64": "0.21.5",
+ "@esbuild/linux-ia32": "0.21.5",
+ "@esbuild/linux-loong64": "0.21.5",
+ "@esbuild/linux-mips64el": "0.21.5",
+ "@esbuild/linux-ppc64": "0.21.5",
+ "@esbuild/linux-riscv64": "0.21.5",
+ "@esbuild/linux-s390x": "0.21.5",
+ "@esbuild/linux-x64": "0.21.5",
+ "@esbuild/netbsd-x64": "0.21.5",
+ "@esbuild/openbsd-x64": "0.21.5",
+ "@esbuild/sunos-x64": "0.21.5",
+ "@esbuild/win32-arm64": "0.21.5",
+ "@esbuild/win32-ia32": "0.21.5",
+ "@esbuild/win32-x64": "0.21.5"
+ }
+ },
+ "node_modules/vitest/node_modules/estree-walker": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz",
+ "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==",
+ "dev": true,
+ "dependencies": {
+ "@types/estree": "^1.0.0"
+ }
+ },
+ "node_modules/vitest/node_modules/vite": {
+ "version": "5.4.11",
+ "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.11.tgz",
+ "integrity": "sha512-c7jFQRklXua0mTzneGW9QVyxFjUgwcihC4bXEtujIo2ouWCe1Ajt/amn2PCxYnhYfd5k09JX3SB7OYWFKYqj8Q==",
+ "dev": true,
+ "dependencies": {
+ "esbuild": "^0.21.3",
+ "postcss": "^8.4.43",
+ "rollup": "^4.20.0"
+ },
+ "bin": {
+ "vite": "bin/vite.js"
+ },
+ "engines": {
+ "node": "^18.0.0 || >=20.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/vitejs/vite?sponsor=1"
+ },
+ "optionalDependencies": {
+ "fsevents": "~2.3.3"
+ },
+ "peerDependencies": {
+ "@types/node": "^18.0.0 || >=20.0.0",
+ "less": "*",
+ "lightningcss": "^1.21.0",
+ "sass": "*",
+ "sass-embedded": "*",
+ "stylus": "*",
+ "sugarss": "*",
+ "terser": "^5.4.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/node": {
+ "optional": true
+ },
+ "less": {
+ "optional": true
+ },
+ "lightningcss": {
+ "optional": true
+ },
+ "sass": {
+ "optional": true
+ },
+ "sass-embedded": {
+ "optional": true
+ },
+ "stylus": {
+ "optional": true
+ },
+ "sugarss": {
+ "optional": true
+ },
+ "terser": {
+ "optional": true
+ }
+ }
+ },
"node_modules/w3c-xmlserializer": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz",
@@ -32682,6 +33939,23 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/why-is-node-running": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz",
+ "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "siginfo": "^2.0.0",
+ "stackback": "0.0.2"
+ },
+ "bin": {
+ "why-is-node-running": "cli.js"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
"node_modules/wide-align": {
"version": "1.1.5",
"resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz",
@@ -33099,12 +34373,12 @@
},
"packages/connect": {
"name": "@stacks/connect",
- "version": "7.8.0",
+ "version": "7.10.0",
"license": "MIT",
"dependencies": {
- "@stacks/auth": "^7.0.0",
+ "@scure/base": "^1.2.4",
"@stacks/common": "^7.0.0",
- "@stacks/connect-ui": "6.5.0",
+ "@stacks/connect-ui": "6.6.0",
"@stacks/network": "^7.0.0",
"@stacks/network-v6": "npm:@stacks/network@^6.16.0",
"@stacks/profile": "^7.0.0",
@@ -33129,15 +34403,15 @@
"react": "^18.2.0",
"react-dom": "^18.2.0",
"storybook": "^7.6.0-alpha.5",
- "vite": "^4.5.0"
+ "vitest": "^2.1.8"
}
},
"packages/connect-react": {
"name": "@stacks/connect-react",
- "version": "22.5.0",
+ "version": "22.6.1",
"license": "MIT",
"dependencies": {
- "@stacks/connect": "7.8.0",
+ "@stacks/connect": "7.10.0",
"jsontokens": "^4.0.1"
},
"devDependencies": {
@@ -33152,7 +34426,7 @@
},
"packages/connect-ui": {
"name": "@stacks/connect-ui",
- "version": "6.5.0",
+ "version": "6.6.0",
"license": "MIT",
"dependencies": {
"@stencil/core": "^2.17.1"
diff --git a/package.json b/package.json
index 7be01531..a7dc6baf 100644
--- a/package.json
+++ b/package.json
@@ -45,6 +45,7 @@
"react-dom": "^18.3.1",
"tsup": "^8.3.5",
"typedoc": "^0.26.10",
- "typescript": "^5.3.2"
+ "typescript": "^5.3.2",
+ "vitest": "^2.1.8"
}
}
diff --git a/packages/connect-react/src/react/hooks/use-connect.ts b/packages/connect-react/src/react/hooks/use-connect.ts
index 5affc415..76c9d471 100644
--- a/packages/connect-react/src/react/hooks/use-connect.ts
+++ b/packages/connect-react/src/react/hooks/use-connect.ts
@@ -10,23 +10,23 @@ import {
FinishedAuthData,
openContractCall,
openContractDeploy,
- openSignTransaction,
openProfileUpdateRequestPopup,
openPsbtRequestPopup,
openSignatureRequestPopup,
+ openSignTransaction,
openStructuredDataSignatureRequestPopup,
openSTXTransfer,
- PsbtRequestOptions,
ProfileUpdateRequestOptions,
+ PsbtRequestOptions,
showBlockstackConnect,
SignatureRequestOptions,
+ SignTransactionOptions,
+ StacksProvider,
+ StructuredDataSignatureRequestOptions,
STXTransferOptions,
STXTransferRegularOptions,
STXTransferSponsoredOptions,
- StacksProvider,
- SignTransactionOptions,
} from '@stacks/connect';
-import { StructuredDataSignatureRequestOptions } from '@stacks/connect/src/types/structuredDataSignature';
import { useContext } from 'react';
import { ConnectContext, ConnectDispatchContext, States } from '../components/connect/context';
@@ -71,7 +71,7 @@ export const useConnect = () => {
void authenticate(_options, provider);
return;
} else {
- showBlockstackConnect({
+ void showBlockstackConnect({
...authOptions,
sendToSignIn: false,
});
diff --git a/packages/connect-ui/readme.md b/packages/connect-ui/README.md
similarity index 100%
rename from packages/connect-ui/readme.md
rename to packages/connect-ui/README.md
diff --git a/packages/connect-ui/src/components.d.ts b/packages/connect-ui/src/components.d.ts
index 7bedbaee..62c4057f 100644
--- a/packages/connect-ui/src/components.d.ts
+++ b/packages/connect-ui/src/components.d.ts
@@ -5,14 +5,14 @@
* It contains typing information for all components that exist in this project.
*/
import { HTMLStencilElement, JSXBase } from "@stencil/core/internal";
-import { WebBTCProvider } from "./providers";
+import { WbipProvider } from "./providers";
export namespace Components {
interface ConnectModal {
"callback": Function;
"cancelCallback": Function;
- "defaultProviders": WebBTCProvider[];
- "installedProviders": WebBTCProvider[];
- "persistSelection": boolean;
+ "defaultProviders": WbipProvider[];
+ "installedProviders": WbipProvider[];
+ "persistWalletSelect": boolean;
}
}
declare global {
@@ -30,9 +30,9 @@ declare namespace LocalJSX {
interface ConnectModal {
"callback"?: Function;
"cancelCallback"?: Function;
- "defaultProviders"?: WebBTCProvider[];
- "installedProviders"?: WebBTCProvider[];
- "persistSelection"?: boolean;
+ "defaultProviders"?: WbipProvider[];
+ "installedProviders"?: WbipProvider[];
+ "persistWalletSelect"?: boolean;
}
interface IntrinsicElements {
"connect-modal": ConnectModal;
diff --git a/packages/connect-ui/src/components/modal/modal.tsx b/packages/connect-ui/src/components/modal/modal.tsx
index ff6ab0ab..39d01cf3 100644
--- a/packages/connect-ui/src/components/modal/modal.tsx
+++ b/packages/connect-ui/src/components/modal/modal.tsx
@@ -1,5 +1,5 @@
import { Component, Element, Prop, h } from '@stencil/core';
-import { WebBTCProvider, getProviderFromId } from '../../providers';
+import { WbipProvider, getProviderFromId } from '../../providers';
import { setSelectedProviderId } from '../../session';
import CloseIcon from './assets/close-icon.svg';
import { getBrowser, getPlatform } from './utils';
@@ -11,10 +11,10 @@ import { getBrowser, getPlatform } from './utils';
shadow: true,
})
export class Modal {
- @Prop() defaultProviders: WebBTCProvider[];
- @Prop() installedProviders: WebBTCProvider[];
+ @Prop() defaultProviders: WbipProvider[];
+ @Prop() installedProviders: WbipProvider[];
- @Prop() persistSelection: boolean;
+ @Prop() persistWalletSelect: boolean;
@Prop() callback: Function;
@Prop() cancelCallback: Function;
@@ -22,7 +22,7 @@ export class Modal {
@Element() modalEl: HTMLConnectModalElement;
handleSelectProvider(providerId: string) {
- if (this.persistSelection) setSelectedProviderId(providerId);
+ if (this.persistWalletSelect) setSelectedProviderId(providerId);
this.callback(getProviderFromId(providerId));
}
@@ -45,15 +45,15 @@ export class Modal {
// return null;
// }
- getBrowserUrl(provider: WebBTCProvider) {
+ getBrowserUrl(provider: WbipProvider) {
return provider.chromeWebStoreUrl ?? provider.mozillaAddOnsUrl;
}
- getMobileUrl(provider: WebBTCProvider) {
+ getMobileUrl(provider: WbipProvider) {
return provider.iOSAppStoreUrl ?? provider.googlePlayStoreUrl;
}
- getInstallUrl(provider: WebBTCProvider, browser: string, platform: string) {
+ getInstallUrl(provider: WbipProvider, browser: string, platform: string) {
if (platform === 'IOS') {
return provider.iOSAppStoreUrl ?? this.getBrowserUrl(provider) ?? provider.webUrl;
} else if (browser === 'Chrome') {
@@ -115,7 +115,7 @@ export class Modal {
Installed wallets
- {this.installedProviders.map((provider: WebBTCProvider) => (
+ {this.installedProviders.map((provider: WbipProvider) => (
-
@@ -184,7 +184,7 @@ export class Modal {
)}
- {notInstalledProviders.map((provider: WebBTCProvider) => (
+ {notInstalledProviders.map((provider: WbipProvider) => (
-
diff --git a/packages/connect-ui/src/components/modal/readme.md b/packages/connect-ui/src/components/modal/readme.md
index 0edab040..da29bec7 100644
--- a/packages/connect-ui/src/components/modal/readme.md
+++ b/packages/connect-ui/src/components/modal/readme.md
@@ -7,13 +7,13 @@
## Properties
-| Property | Attribute | Description | Type | Default |
-| -------------------- | ------------------- | ----------- | ------------------ | ----------- |
-| `callback` | -- | | `Function` | `undefined` |
-| `cancelCallback` | -- | | `Function` | `undefined` |
-| `defaultProviders` | -- | | `WebBTCProvider[]` | `undefined` |
-| `installedProviders` | -- | | `WebBTCProvider[]` | `undefined` |
-| `persistSelection` | `persist-selection` | | `boolean` | `undefined` |
+| Property | Attribute | Description | Type | Default |
+| --------------------- | ----------------------- | ----------- | ---------------- | ----------- |
+| `callback` | -- | | `Function` | `undefined` |
+| `cancelCallback` | -- | | `Function` | `undefined` |
+| `defaultProviders` | -- | | `WbipProvider[]` | `undefined` |
+| `installedProviders` | -- | | `WbipProvider[]` | `undefined` |
+| `persistWalletSelect` | `persist-wallet-select` | | `boolean` | `undefined` |
----------------------------------------------
diff --git a/packages/connect-ui/src/providers.ts b/packages/connect-ui/src/providers.ts
index f2d9f27e..fc18d805 100644
--- a/packages/connect-ui/src/providers.ts
+++ b/packages/connect-ui/src/providers.ts
@@ -1,6 +1,11 @@
// AUTO REGISTERED PROVIDERS
-export interface WebBTCProvider {
+import { getSelectedProviderId } from './session';
+
+/** Backwards compatible alias for `WbipProvider` */
+export type WebBTCProvider = WbipProvider;
+
+export interface WbipProvider {
/** The global "path" of the provider (e.g. `"MyProvider"` if registered at `window.MyProvider`) */
id: string;
/** The name of the provider, as displayed to the user */
@@ -27,18 +32,23 @@ declare global {
* The provider objects are WBIP004 compliant.
* It may happen that no wallet implements this feature before `@stacks/connect` switches to `webbtc`.
*/
- webbtc_stx_providers?: WebBTCProvider[];
+ webbtc_stx_providers?: WbipProvider[];
+
+ /** @experimental @beta */
+ wbip_providers?: WbipProvider[];
}
}
export const getRegisteredProviders = () => {
if (typeof window === 'undefined') return [];
- if (!window.webbtc_stx_providers) return [];
- return window.webbtc_stx_providers;
+ const legacyProviders = window.webbtc_stx_providers || [];
+ const wbipProviders = window.wbip_providers || [];
+
+ return [...legacyProviders, ...wbipProviders];
};
-export const getInstalledProviders = (defaultProviders: WebBTCProvider[] = []) => {
+export const getInstalledProviders = (defaultProviders: WbipProvider[] = []) => {
if (typeof window === 'undefined') return [];
const registeredProviders = getRegisteredProviders();
@@ -55,6 +65,24 @@ export const getInstalledProviders = (defaultProviders: WebBTCProvider[] = []) =
return registeredProviders.concat(additionalInstalledProviders);
};
+/**
+ * Check if a wallet provider was previously selected via Connect.
+ * @returns `true` if a provider was selected, `false` otherwise.
+ */
+export const isProviderSelected = () => {
+ return !!getSelectedProviderId();
+};
+
+/**
+ * Get the currently selected wallet provider.
+ * Note that this will not return the default `window.StacksProvider` object.
+ * @returns The wallet provider object, or null if no provider is selected.
+ */
+export const getProvider = () => {
+ const providerId = getSelectedProviderId();
+ return getProviderFromId(providerId);
+};
+
export const getProviderFromId = (id: string | undefined) => {
return id?.split('.').reduce((acc, part) => acc?.[part], window);
};
diff --git a/packages/connect/package.json b/packages/connect/package.json
index f506548d..ed49c6c1 100644
--- a/packages/connect/package.json
+++ b/packages/connect/package.json
@@ -4,14 +4,18 @@
"license": "MIT",
"scripts": {
"build": "concurrently 'tsup src/index.ts' 'npm run types'",
+ "dev": "tsc --project tsconfig.json --watch",
"prepublishOnly": "npm run build",
"typecheck": "tsc --project tsconfig.json --noEmit",
"types": "tsc --project tsconfig.json --emitDeclarationOnly",
"storybook": "storybook dev -p 6006",
- "build-storybook": "storybook build"
+ "build-storybook": "storybook build",
+ "test": "vitest run",
+ "test:watch": "vitest",
+ "test:coverage": "vitest run --coverage"
},
"dependencies": {
- "@stacks/auth": "^7.0.0",
+ "@scure/base": "^1.2.4",
"@stacks/common": "^7.0.0",
"@stacks/connect-ui": "6.6.0",
"@stacks/network": "^7.0.0",
@@ -53,6 +57,6 @@
"react": "^18.2.0",
"react-dom": "^18.2.0",
"storybook": "^7.6.0-alpha.5",
- "vite": "^4.5.0"
+ "vitest": "^2.1.8"
}
}
diff --git a/packages/connect/src/asigna.ts b/packages/connect/src/asigna.ts
index 87ef2176..320b7110 100644
--- a/packages/connect/src/asigna.ts
+++ b/packages/connect/src/asigna.ts
@@ -21,9 +21,7 @@ const AsignaIframeProvider = {
return generateCall(payload, 'transactionRequest');
},
};
-const generateAsignaMessage = (payload: string, key: string) => {
- return { source, [key]: payload };
-};
+const generateAsignaMessage = (payload: string, key: string) => ({ source, [key]: payload });
export const initializeAsignaProvider = () => {
const isAsignaIframe = !!window.top && document.referrer.endsWith('.asigna.io/');
diff --git a/packages/connect/src/auth.ts b/packages/connect/src/auth.ts
index a2037dc1..f93fec44 100644
--- a/packages/connect/src/auth.ts
+++ b/packages/connect/src/auth.ts
@@ -1,15 +1,15 @@
-import { AppConfig, UserSession } from '@stacks/auth';
-import { decodeToken } from 'jsontokens';
-import type { AuthOptions, AuthResponsePayload, StacksProvider } from './types';
-
-import { getStacksProvider } from './utils';
+import { NoSessionDataError } from '@stacks/common';
+import { request } from './request';
+import { AuthOptions, StacksProvider } from './types';
+/** @deprecated Not used anymore. */
export const defaultAuthURL = 'https://app.blockstack.org';
if (typeof window !== 'undefined') {
window.__CONNECT_VERSION__ = '__VERSION__'; // replaced via tsup esbuildOptions
}
+/** @deprecated Will be marked as internal going forward. */
export const isMobile = () => {
const ua = navigator.userAgent;
if (/android/i.test(ua)) {
@@ -22,79 +22,309 @@ export const isMobile = () => {
};
/**
- * mobile should not use a 'popup' type of window.
+ * Special `authenticate` legacy request, to store addresses in userSession matching legacy behavior.
+ * @internal Legacy UI request.
*/
-export const shouldUsePopup = () => {
- return !isMobile();
-};
+export const authenticate = async (authOptions: AuthOptions, provider?: StacksProvider) => {
+ const { onFinish, onCancel, userSession: _userSession } = authOptions;
-export const getOrCreateUserSession = (userSession?: UserSession): UserSession => {
- if (!userSession) {
- const appConfig = new AppConfig(['store_write'], document.location.href);
- userSession = new UserSession({ appConfig });
- }
- return userSession;
-};
-
-export const authenticate = async (
- authOptions: AuthOptions,
- provider: StacksProvider = getStacksProvider()
-) => {
- if (!provider) throw new Error('[Connect] No installed Stacks wallet found');
-
- const {
- redirectTo = '/',
- manifestPath,
- onFinish,
- onCancel,
- sendToSignIn = false,
- userSession: _userSession,
- appDetails,
- } = authOptions;
const userSession = getOrCreateUserSession(_userSession);
- if (userSession.isUserSignedIn()) {
- userSession.signUserOut();
- }
- const transitKey = userSession.generateAndStoreTransitKey();
- const authRequest = userSession.makeAuthRequest(
- transitKey,
- `${document.location.origin}${redirectTo}`,
- `${document.location.origin}${manifestPath}`,
- userSession.appConfig.scopes,
- undefined,
- undefined,
- {
- sendToSignIn,
- appDetails,
- connectVersion: '__VERSION__', // replaced via tsup esbuildOptions,
- }
- );
+ if (userSession.isUserSignedIn()) userSession.signUserOut();
try {
- const authResponse = await provider.authenticationRequest(authRequest);
- await userSession.handlePendingSignIn(authResponse);
- const token = decodeToken(authResponse);
- const payload = token?.payload;
- const authResponsePayload = payload as unknown as AuthResponsePayload;
- onFinish?.({
- authResponse,
- authResponsePayload,
- userSession,
+ const method = 'getAddresses';
+ const response = await request({ provider, forceWalletSelect: true }, method);
+
+ const sessionData = userSession.store.getSessionData();
+
+ // Ensure user data structure exists
+ sessionData.userData ??= { profile: {} };
+ sessionData.userData.profile ??= {};
+ sessionData.userData.profile.stxAddress ??= {
+ mainnet: '',
+ testnet: '',
+ };
+
+ // Take first address and use it for legacy connect user session storing.
+ const address = response.addresses
+ .find(a => a?.symbol === 'STX' || a.address.startsWith('S'))
+ .address.toUpperCase();
+ const isMainnet = address[1] === 'P' || address[1] === 'M';
+
+ // Store only returned address
+ Object.assign(sessionData.userData.profile.stxAddress, {
+ [isMainnet ? 'mainnet' : 'testnet']: address,
});
+ userSession.store.setSessionData(sessionData);
+
+ onFinish?.({ userSession });
} catch (error) {
console.error('[Connect] Error during auth request', error);
onCancel?.();
}
};
-// eslint-disable-next-line @typescript-eslint/require-await
-export const getUserData = async (userSession?: UserSession) => {
- userSession = getOrCreateUserSession(userSession);
- if (userSession.isUserSignedIn()) {
- return userSession.loadUserData();
+// Legacy User Session, User Data, etc.
+
+/** @deprecated Update to the latest `request` RPC methods. It's not recommended to use the UserSession. */
+export const LOCALSTORAGE_SESSION_KEY = 'blockstack-session';
+
+/**
+ * Semi-compatible `AppConfig` type for configuring `UserSession`.
+ *
+ * @deprecated Update to the latest `request` RPC methods. It's not recommended to use the UserSession.
+ */
+export class AppConfig {
+ // Copied over from legacy @stacks/auth
+ constructor(
+ _scopes?: any,
+ _appDomain?: any,
+ _redirectPath?: string,
+ _manifestPath?: string,
+ _coreNode?: string,
+ _authenticatorURL?: string
+ ) {}
+}
+
+/**
+ * Semi-compatible `SessionOptions` type for accessing `userData`.
+ *
+ * @deprecated Update to the latest `request` RPC methods. It's not recommended to use the UserSession.
+ */
+export interface SessionOptions {
+ userData?: UserData;
+ localStorageKey?: string;
+ storeOptions?: {
+ localStorageKey?: string;
+ };
+}
+
+/**
+ * Semi-compatible `SessionData` type for accessing `userData`.
+ *
+ * @deprecated Update to the latest `request` RPC methods. It's not recommended to use the UserSession.
+ */
+export interface SessionData {
+ userData?: UserData;
+}
+
+/**
+ * Semi-compatible `UserSession` type for accessing `userData`.
+ *
+ * @deprecated Update to the latest `request` RPC methods. It's not recommended to use the UserSession.
+ */
+export class UserSession {
+ // Copied over from legacy @stacks/auth
+ appConfig: any;
+
+ store: SessionDataStore;
+
+ /**
+ * UserSession might still work for some use cases, but it's not recommended.
+ *
+ * @deprecated Update to the latest `request` RPC methods.
+ */
+ constructor(options?: { appConfig?: any; sessionStore?: any; sessionOptions?: SessionOptions }) {
+ if (options?.appConfig) this.appConfig = options.appConfig;
+
+ if (typeof window === 'undefined' && typeof self === 'undefined') {
+ // not running in browser
+ this.store = new InstanceDataStore();
+ } else {
+ // running in browser
+ this.store = new LocalStorageStore();
+ }
}
- if (userSession.isSignInPending()) {
- return userSession.handlePendingSignIn();
+
+ /** @deprecated No-op. Update to the latest `request` RPC methods. */
+ makeAuthRequestToken() {}
+
+ /** @deprecated No-op. Update to the latest `request` RPC methods. */
+ generateAndStoreTransitKey() {}
+
+ /** @deprecated No-op. Update to the latest `request` RPC methods. */
+ getAuthResponseToken() {}
+
+ /** @deprecated No-op. Update to the latest `request` RPC methods. */
+ isSignInPending() {
+ return false;
}
- return null;
+
+ /**
+ * Check if a user is currently signed in.
+ *
+ * @returns {Boolean} `true` if the user is signed in (aka connected), `false` if not.
+ */
+ isUserSignedIn() {
+ return !!this.store.getSessionData().userData;
+ }
+
+ /**
+ * Try to process any pending sign in request by returning a `Promise` that resolves
+ * to the user data object if the sign in succeeds.
+ *
+ * @returns {Promise} that resolves to the user data object if successful and rejects
+ * if handling the sign in request fails or there was no pending sign in request.
+ */
+ async handlePendingSignIn(): Promise
{
+ return Promise.resolve(this.loadUserData());
+ }
+
+ /**
+ * Retrieves the user data object. The user's profile is stored in the key [[Profile]].
+ *
+ * @returns {Object} User data object.
+ */
+ loadUserData() {
+ const userData = this.store.getSessionData().userData;
+ if (!userData) throw new NoSessionDataError('No user data found. Did the user sign in?');
+ return userData;
+ }
+
+ /** @deprecated No-op. Update to the latest `request` RPC methods. */
+ encryptContent() {}
+
+ /** @deprecated No-op. Update to the latest `request` RPC methods. */
+ decryptContent() {}
+
+ /**
+ * Sign the user out and optionally redirect to given location.
+ * @param redirectURL Location to redirect user to after sign out.
+ */
+ signUserOut(redirectURL?: string) {
+ this.store.deleteSessionData();
+ if (redirectURL && typeof location !== 'undefined' && location.href) {
+ location.href = redirectURL;
+ }
+ }
+}
+
+/** @deprecated Update to the latest `request` RPC methods. It's not recommended to use the UserSession. */
+export interface UserData {
+ /** @deprecated */
+ email?: string;
+ /** @deprecated */
+ decentralizedID?: string;
+ /** @deprecated */
+ identityAddress?: string;
+ /** @deprecated */
+ appPrivateKey?: string;
+ /** @deprecated */
+ hubUrl?: string;
+ /** @deprecated */
+ coreNode?: string;
+ /** @deprecated */
+ authResponseToken?: string;
+ /** @deprecated */
+ coreSessionToken?: string;
+ /** @deprecated */
+ gaiaAssociationToken?: string;
+ /** @deprecated */
+ profile: any;
+ /** @deprecated */
+ gaiaHubConfig?: any;
+ /** @deprecated */
+ appPrivateKeyFromWalletSalt?: string;
+}
+
+/**
+ * @abstract An abstract class representing the SessionDataStore interface.
+ * @deprecated Update to the latest `request` RPC methods. It's not recommended to use the UserSession.
+ */
+export class SessionDataStore {
+ constructor(sessionOptions?: SessionOptions) {
+ if (sessionOptions) this.setSessionData(sessionOptions);
+ }
+
+ getSessionData(): SessionData {
+ throw new Error('Abstract class');
+ }
+
+ setSessionData(_session: SessionData): boolean {
+ throw new Error('Abstract class');
+ }
+
+ deleteSessionData(): boolean {
+ throw new Error('Abstract class');
+ }
+}
+
+/**
+ * Stores session data in the instance of this class.
+ *
+ * @deprecated Update to the latest `request` RPC methods. It's not recommended to use the UserSession.
+ */
+export class InstanceDataStore extends SessionDataStore {
+ sessionData?: SessionData;
+
+ constructor(sessionOptions?: SessionOptions) {
+ super(sessionOptions);
+ if (!this.sessionData) this.setSessionData({});
+ }
+
+ getSessionData(): SessionData {
+ if (!this.sessionData) throw new NoSessionDataError('No session data was found.');
+ return this.sessionData;
+ }
+
+ setSessionData(session: SessionData): boolean {
+ this.sessionData = session;
+ return true;
+ }
+
+ deleteSessionData(): boolean {
+ this.setSessionData({});
+ return true;
+ }
+}
+
+/**
+ * Stores session data in browser a localStorage entry.
+ *
+ * @deprecated Update to the latest `request` RPC methods. It's not recommended to use the UserSession.
+ */
+export class LocalStorageStore extends SessionDataStore {
+ key: string;
+
+ constructor(sessionOptions?: SessionOptions) {
+ super(sessionOptions);
+ this.key =
+ typeof sessionOptions?.storeOptions?.localStorageKey === 'string'
+ ? sessionOptions.storeOptions.localStorageKey
+ : LOCALSTORAGE_SESSION_KEY;
+
+ const data = localStorage.getItem(this.key);
+ if (!data) this.setSessionData({});
+ }
+
+ getSessionData(): SessionData {
+ const data = localStorage.getItem(this.key);
+ if (!data) throw new NoSessionDataError('No session data was found in localStorage');
+
+ return JSON.parse(data);
+ }
+
+ setSessionData(session: SessionData): boolean {
+ localStorage.setItem(this.key, JSON.stringify(session));
+ return true;
+ }
+
+ deleteSessionData(): boolean {
+ localStorage.removeItem(this.key);
+ this.setSessionData({});
+ return true;
+ }
+}
+
+/** @deprecated Update to the latest `request` RPC methods. It's not recommended to use the UserSession. */
+export const getOrCreateUserSession = (userSession?: UserSession): UserSession => {
+ if (userSession) return userSession;
+ return new UserSession();
+};
+
+/** @deprecated Update to the latest `request` RPC methods. It's not recommended to use the UserSession. */
+export const getUserData = async (userSession?: UserSession) => {
+ userSession = getOrCreateUserSession(userSession);
+ if (userSession.isUserSignedIn()) return Promise.resolve(userSession.loadUserData());
+ return Promise.resolve(null);
};
diff --git a/packages/connect/src/bitcoin/psbt.ts b/packages/connect/src/bitcoin/psbt.ts
index 51dee332..f14a9fc8 100644
--- a/packages/connect/src/bitcoin/psbt.ts
+++ b/packages/connect/src/bitcoin/psbt.ts
@@ -1,79 +1,44 @@
-import { createUnsecuredToken, Json, TokenSigner } from 'jsontokens';
-import { getKeys, getUserSession, hasAppPrivateKey } from '../transactions';
+import { base64 } from '@scure/base';
+import { bytesToHex, hexToBytes } from '@stacks/common';
+import { MethodParams, MethodResult, SigHash } from '../methods';
+import { requestRawLegacy } from '../request';
import { StacksProvider } from '../types';
-import { PsbtPayload, PsbtPopup, PsbtRequestOptions } from '../types/bitcoin';
-import { getStacksProvider, legacyNetworkFromConnectNetwork } from '../utils';
+import { PsbtData, PsbtRequestOptions, SignatureHash } from '../types/bitcoin';
+import { getStacksProvider } from '../utils';
-// eslint-disable-next-line @typescript-eslint/require-await
-async function signPayload(payload: PsbtPayload, privateKey: string) {
- const tokenSigner = new TokenSigner('ES256k', privateKey);
- // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
- return tokenSigner.signAsync({ ...payload } as any);
-}
-
-export function getDefaultPsbtRequestOptions(options: PsbtRequestOptions) {
- const network = legacyNetworkFromConnectNetwork(options.network);
- const userSession = getUserSession(options.userSession);
- const defaults: PsbtRequestOptions = {
- ...options,
- network,
- userSession,
- };
- return {
- ...defaults,
- };
-}
-
-async function openPsbtPopup({ token, options }: PsbtPopup, provider: StacksProvider) {
- if (!provider) throw new Error('[Connect] No installed Stacks wallet found');
+/** @deprecated No-op. Tokens are not needed for latest RPC endpoints. */
+export function getDefaultPsbtRequestOptions(_options: PsbtRequestOptions) {}
- try {
- const psbtResponse = await provider.psbtRequest(token);
- options.onFinish?.(psbtResponse);
- } catch (error) {
- console.error('[Connect] Error during psbt request', error);
- options.onCancel?.();
- }
-}
-
-// eslint-disable-next-line @typescript-eslint/require-await
-export const makePsbtToken = async (options: PsbtRequestOptions) => {
- const { allowedSighash, hex, signAtIndex, userSession, ..._options } = options;
- if (hasAppPrivateKey(userSession)) {
- const { privateKey, publicKey } = getKeys(userSession);
+/** @deprecated No-op. Tokens are not needed for latest RPC endpoints. */
+export const makePsbtToken = async (_options: PsbtRequestOptions) => {};
- const payload: PsbtPayload = {
- ..._options,
- allowedSighash,
- hex,
- signAtIndex,
- publicKey,
- };
+const METHOD = 'signPsbt' as const;
- return signPayload(payload, privateKey);
- }
- const payload = { ..._options };
- return createUnsecuredToken(payload as Json);
-};
+/** @internal */
+export const LEGACY_SIGN_PSBT_OPTIONS_MAP = (
+ options: PsbtRequestOptions
+): MethodParams => ({
+ psbt: base64.encode(hexToBytes(options.hex)),
+ signInputs: typeof options.signAtIndex === 'number' ? [options.signAtIndex] : options.signAtIndex,
+ allowedSigHash: options.allowedSighash?.map(hash => SignatureHash[hash] as SigHash),
+});
-async function generateTokenAndOpenPopup(
- options: T,
- makeTokenFn: (options: T) => Promise,
- provider: StacksProvider
-) {
- const token = await makeTokenFn({
- ...getDefaultPsbtRequestOptions(options),
- ...options,
- } as T);
- return openPsbtPopup({ token, options }, provider);
-}
+/** @internal */
+export const LEGACY_SIGN_PSBT_RESPONSE_MAP = (response: MethodResult): PsbtData => ({
+ hex: bytesToHex(base64.decode(response.psbt)),
+});
/**
* @experimental
+ * Compatible interface with previous Connect `openPsbtRequestPopup` version, but using new SIP-030 RPC method.
*/
export function openPsbtRequestPopup(
options: PsbtRequestOptions,
provider: StacksProvider = getStacksProvider()
) {
- return generateTokenAndOpenPopup(options, makePsbtToken, provider);
+ requestRawLegacy(
+ METHOD,
+ LEGACY_SIGN_PSBT_OPTIONS_MAP,
+ LEGACY_SIGN_PSBT_RESPONSE_MAP
+ )(options, provider);
}
diff --git a/packages/connect/src/errors.ts b/packages/connect/src/errors.ts
new file mode 100644
index 00000000..ae97c1ed
--- /dev/null
+++ b/packages/connect/src/errors.ts
@@ -0,0 +1,74 @@
+import { JsonRpcResponseError } from './methods';
+
+export class JsonRpcError extends Error {
+ constructor(
+ public message: string,
+ public code: number,
+ public data?: string,
+
+ public cause?: Error
+ ) {
+ super(message);
+ this.name = 'JsonRpcError';
+ this.message = message;
+ this.code = code;
+ this.data = data;
+
+ this.cause = cause;
+ }
+
+ static fromResponse(error: JsonRpcResponseError) {
+ return new JsonRpcError(error.message, error.code, error.data);
+ }
+
+ toString() {
+ return `${this.name} (${this.code}): ${this.message}${this.data ? `: ${JSON.stringify(this.data)}` : ''}`;
+ }
+}
+
+/**
+ * Numeric error codes for JSON-RPC errors, used for `.code` in {@link JsonRpcError}.
+ * Implementation-defined wallet errors range from `-32099` to `-32000`.
+ */
+export enum JsonRpcErrorCode {
+ // https://www.jsonrpc.org/specification#error_object
+ // > The error codes from and including -32768 to -32000 are reserved for pre-defined errors.
+
+ /** Invalid JSON received by server while parsing */
+ ParseError = -32_700,
+
+ /** Invalid Request object */
+ InvalidRequest = -32_600,
+
+ /** Method not found/available */
+ MethodNotFound = -32_601,
+
+ /** Invalid method params */
+ InvalidParams = -32_602,
+
+ /** Internal JSON-RPC error */
+ InternalError = -32_603,
+
+ // IMPLEMENTATION-DEFINED WALLET ERRORS
+ /** User rejected the request (implementation-defined wallet error) */
+ UserRejection = -32_000,
+
+ /** Address mismatch for the requested method (implementation-defined wallet error) */
+ MethodAddressMismatch = -32_001,
+
+ /** Access denied for the requested method (implementation-defined wallet error) */
+ MethodAccessDenied = -32_002,
+
+ // CUSTOM ERRORS (Custom range, not inside the JSON-RPC error code range)
+ /**
+ * Unknown external error.
+ * Error does not originate from the wallet.
+ */
+ UnknownError = -31_000,
+
+ /**
+ * User canceled the request.
+ * Error may not originate from the wallet.
+ */
+ UserCanceled = -31_001,
+}
diff --git a/packages/connect/src/index.ts b/packages/connect/src/index.ts
index 6edb16e1..dd5dba74 100644
--- a/packages/connect/src/index.ts
+++ b/packages/connect/src/index.ts
@@ -1,19 +1,53 @@
export * from './auth';
-export * from './bitcoin';
-export * from './transactions';
-export * from './signature';
-export * from './signature/structuredData';
-export * from './profile';
+export * from './providers';
export * from './types';
-export * from './utils';
export * from './ui';
-export * from './providers';
+// Manual exports to avoid exporting internals (e.g. `LEGACY_XYZ`)
+export { getDefaultPsbtRequestOptions, makePsbtToken, openPsbtRequestPopup } from './bitcoin';
+export {
+ getDefaultSignatureRequestOptions,
+ SignatureRequestPayload,
+ signMessage,
+ openSignatureRequestPopup,
+} from './signature';
+export {
+ signStructuredMessage,
+ openStructuredDataSignatureRequestPopup,
+} from './signature/structuredData';
+export {
+ getDefaultProfileUpdateRequestOptions,
+ makeProfileUpdateToken,
+ openProfileUpdateRequestPopup,
+} from './profile';
+export {
+ getUserSession,
+ hasAppPrivateKey,
+ getKeys,
+ getStxAddress,
+ makeContractCallToken,
+ makeContractDeployToken,
+ makeSTXTransferToken,
+ makeSignTransaction,
+ openContractCall,
+ openContractDeploy,
+ openSTXTransfer,
+ openSignTransaction,
+} from './transactions';
+export { request, requestRaw } from './request';
+export { getStacksProvider, isStacksWalletInstalled } from './utils';
+
+// TODO: (next)
+// We won't expose these types (TypeBox and Zod) until they are final and stable.
+// TypeBox
+// Only export the outermost typebox schemas
+// export { ClarityValueTypeBoxSchema, PostConditionTypeBoxSchema } from './types/typebox';
-// re-exports
-export * from '@stacks/auth';
+// Re-exports
export {
clearSelectedProviderId,
getSelectedProviderId,
setSelectedProviderId,
+ isProviderSelected,
} from '@stacks/connect-ui';
+export type { WebBTCProvider, WbipProvider } from '@stacks/connect-ui';
diff --git a/packages/connect/src/methods.ts b/packages/connect/src/methods.ts
new file mode 100644
index 00000000..13537889
--- /dev/null
+++ b/packages/connect/src/methods.ts
@@ -0,0 +1,256 @@
+import type {
+ AddressString,
+ ClarityValue,
+ ContractIdString,
+ PostCondition,
+ PostConditionModeName,
+ TupleCV,
+} from '@stacks/transactions';
+
+// Re-export types from Stacks.js
+export type {
+ AddressString,
+ AssetString,
+ ClarityValue,
+ ContractIdString,
+ FungibleComparator,
+ FungiblePostCondition,
+ NonFungibleComparator,
+ NonFungiblePostCondition,
+ PostCondition,
+ PostConditionModeName,
+ StxPostCondition,
+} from '@stacks/transactions';
+
+// TYPES
+
+export type NetworkString = 'mainnet' | 'testnet' | 'regtest' | 'devnet' | string;
+
+export type PrincipalString = AddressString | ContractIdString;
+
+export type Integer = number | bigint | string;
+
+export interface AddressEntry {
+ symbol?: string;
+ address: string;
+ publicKey: string;
+}
+
+export interface AccountEntry extends AddressEntry {
+ gaiaHubUrl: string;
+ gaiaAppKey: string;
+}
+
+// PARAMS
+
+interface CommonTxParams {
+ /**
+ * The recommended address to use for the method.
+ *
+ * ⚠︎ Warning: Wallets may not implement this for privacy reasons.
+ */
+ address?: AddressString;
+
+ network?: NetworkString;
+
+ fee?: Integer;
+ nonce?: Integer;
+
+ sponsored?: boolean;
+
+ postConditions?: (string | PostCondition)[]; // hex-encoded string or JSON PostCondition
+ postConditionMode?: PostConditionModeName;
+}
+
+export interface TransferStxParams
+ extends Omit {
+ recipient: string;
+ amount: Integer;
+ memo?: string;
+}
+
+export interface TransferFungibleParams extends CommonTxParams {
+ recipient: string;
+ asset: string;
+ amount: Integer;
+}
+
+export interface TransferNonFungibleParams extends CommonTxParams {
+ recipient: string;
+ asset: string;
+ assetId: ClarityValue; // todo: add string (hex-encoded), add string (clarity syntax)
+}
+
+export interface CallContractParams extends CommonTxParams {
+ contract: ContractIdString;
+ functionName: string;
+ functionArgs?: ClarityValue[]; // todo: add string (hex-encoded), add string (clarity syntax)
+}
+
+export interface DeployContractParams extends CommonTxParams {
+ name: string;
+ clarityCode: string;
+ clarityVersion?: number;
+}
+
+export interface SignTransactionParams {
+ transaction: string;
+ broadcast?: boolean; // todo: check before merging
+}
+
+export interface SignMessageParams {
+ message: string;
+}
+
+export interface SignStructuredMessageParams {
+ message: ClarityValue;
+ domain: TupleCV;
+}
+
+export interface GetAddressesParams {
+ network?: NetworkString;
+}
+
+export interface GetAccountsParams {
+ network?: NetworkString;
+}
+
+export interface UpdateProfileParams {
+ profile: Record;
+}
+
+// RESULTS
+
+export interface TransactionResult {
+ txid?: string;
+ transaction?: string; // todo: check before merging
+}
+
+export interface SignTransactionResult {
+ txid?: string;
+ transaction: string;
+}
+
+export interface SignMessageResult {
+ signature: string;
+ publicKey: string;
+}
+
+export interface GetAddressesResult {
+ addresses: AddressEntry[];
+}
+
+export interface GetAccountsResult {
+ accounts: AccountEntry[];
+}
+
+export interface UpdateProfileResult {
+ profile: Record;
+}
+
+// ERROR RESPONSES
+
+// todo: add error responses
+
+// JSON RPC METHODS
+
+/// BITCOIN METHODS
+
+export type SigHash = 'ALL' | 'NONE' | 'SINGLE' | 'ANYONECANPAY';
+
+export interface SignInputsByAddress {
+ [address: string]: number[];
+}
+
+export interface SignPsbtParams {
+ psbt: string;
+ signInputs?: number[] | SignInputsByAddress;
+ /** @experimental Might need a rename, when SIPs/WBIPs are finalized. */
+ allowedSigHash?: SigHash[];
+}
+
+export interface SignPsbtResult {
+ txid?: string;
+ psbt: string;
+}
+
+export type JsonRpcResponseError = {
+ code: number;
+ message: string;
+ data?: any;
+};
+
+// todo: double check spec
+export type JsonRpcResponse = {
+ jsonrpc: '2.0';
+ id: string | number | null;
+} & (
+ | {
+ result: Methods[M]['result'];
+ }
+ | {
+ error: JsonRpcResponseError;
+ }
+);
+
+export type Methods = {
+ // BTC
+ signPsbt: {
+ params: SignPsbtParams;
+ result: SignPsbtResult;
+ };
+ getAddresses: {
+ params: GetAddressesParams;
+ result: GetAddressesResult;
+ };
+
+ // STX
+ stx_transferStx: {
+ params: TransferStxParams;
+ result: TransactionResult;
+ };
+ stx_transferSip10Ft: {
+ params: TransferFungibleParams;
+ result: TransactionResult;
+ };
+ stx_transferSip10Nft: {
+ params: TransferNonFungibleParams;
+ result: TransactionResult;
+ };
+ stx_callContract: {
+ params: CallContractParams;
+ result: TransactionResult;
+ };
+ stx_deployContract: {
+ params: DeployContractParams;
+ result: TransactionResult;
+ };
+ stx_signTransaction: {
+ params: SignTransactionParams;
+ result: SignTransactionResult;
+ };
+ stx_signMessage: {
+ params: SignMessageParams;
+ result: SignMessageResult;
+ };
+ stx_signStructuredMessage: {
+ params: SignStructuredMessageParams;
+ result: SignMessageResult;
+ };
+ stx_getAddresses: {
+ params: GetAddressesParams;
+ result: GetAddressesResult;
+ };
+ stx_getAccounts: {
+ params: GetAccountsParams;
+ result: GetAccountsResult;
+ };
+ stx_updateProfile: {
+ params: UpdateProfileParams;
+ result: UpdateProfileResult;
+ };
+};
+
+export type MethodParams = Methods[M]['params'];
+
+export type MethodResult = Methods[M]['result'];
diff --git a/packages/connect/src/profile/index.ts b/packages/connect/src/profile/index.ts
index e013bcf1..bbc5cda1 100644
--- a/packages/connect/src/profile/index.ts
+++ b/packages/connect/src/profile/index.ts
@@ -1,81 +1,35 @@
-import { createUnsecuredToken, Json, TokenSigner } from 'jsontokens';
-import { getKeys, getUserSession, hasAppPrivateKey } from '../transactions';
-import {
- ProfileUpdatePayload,
- ProfileUpdatePopup,
- ProfileUpdateRequestOptions,
- StacksProvider,
-} from '../types';
+import { PublicPersonProfile } from '@stacks/profile';
+import { MethodParams, MethodResult } from '../methods';
+import { requestRawLegacy } from '../request';
+import { ProfileUpdateRequestOptions, StacksProvider } from '../types';
+import { getStacksProvider } from '../utils';
-import { getStacksProvider, legacyNetworkFromConnectNetwork } from '../utils';
+/** @deprecated No-op. Tokens are not needed for latest RPC endpoints. */
+export function getDefaultProfileUpdateRequestOptions(_options: ProfileUpdateRequestOptions) {}
-// eslint-disable-next-line @typescript-eslint/require-await
-async function signPayload(payload: ProfileUpdatePayload, privateKey: string) {
- const tokenSigner = new TokenSigner('ES256k', privateKey);
- // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
- return tokenSigner.signAsync({ ...payload } as any);
-}
-
-export function getDefaultProfileUpdateRequestOptions(options: ProfileUpdateRequestOptions) {
- const network = legacyNetworkFromConnectNetwork(options.network);
- const userSession = getUserSession(options.userSession);
- const defaults: ProfileUpdateRequestOptions = {
- ...options,
- network,
- userSession,
- };
- return {
- ...defaults,
- };
-}
+/** @deprecated No-op. Tokens are not needed for latest RPC endpoints. */
+export const makeProfileUpdateToken = async (_options: ProfileUpdateRequestOptions) => {};
-async function openProfileUpdatePopup(
- { token, options }: ProfileUpdatePopup,
- provider: StacksProvider
-) {
- try {
- const profileUpdateResponse = await provider.profileUpdateRequest(token);
- options.onFinish?.(profileUpdateResponse);
- } catch (error) {
- console.error('[Connect] Error during signature request', error);
- options.onCancel?.();
- }
-}
+const METHOD = 'stx_updateProfile' as const;
-// eslint-disable-next-line @typescript-eslint/require-await
-export const makeProfileUpdateToken = async (options: ProfileUpdateRequestOptions) => {
- const { userSession, profile, ..._options } = options;
- if (hasAppPrivateKey(userSession)) {
- const { privateKey, publicKey } = getKeys(userSession);
+/** @internal */
+export const LEGACY_UPDATE_PROFILE_OPTIONS_MAP = (
+ options: ProfileUpdateRequestOptions
+): MethodParams => options;
- const payload: ProfileUpdatePayload = {
- ..._options,
- profile,
- publicKey,
- };
-
- return signPayload(payload, privateKey);
- }
- const payload = { ..._options };
- return createUnsecuredToken(payload as Json);
-};
-
-async function generateTokenAndOpenPopup(
- options: T,
- makeTokenFn: (options: T) => Promise,
- provider: StacksProvider
-) {
- const token = await makeTokenFn({
- ...getDefaultProfileUpdateRequestOptions(options),
- ...options,
- } as T);
- return openProfileUpdatePopup({ token, options }, provider);
-}
+/** @internal */
+export const LEGACY_UPDATE_PROFILE_RESPONSE_MAP = (
+ response: MethodResult
+): PublicPersonProfile => response.profile as PublicPersonProfile;
+/** Compatible interface with previous Connect `openProfileUpdateRequestPopup` version, but using new SIP-030 RPC method. */
export function openProfileUpdateRequestPopup(
options: ProfileUpdateRequestOptions,
provider: StacksProvider = getStacksProvider()
) {
- if (!provider) throw new Error('[Connect] No installed Stacks wallet found');
- return generateTokenAndOpenPopup(options, makeProfileUpdateToken, provider);
+ requestRawLegacy(
+ METHOD,
+ LEGACY_UPDATE_PROFILE_OPTIONS_MAP,
+ LEGACY_UPDATE_PROFILE_RESPONSE_MAP
+ )(options, provider);
}
diff --git a/packages/connect/src/providers.ts b/packages/connect/src/providers.ts
index 459d1dfb..f448d82c 100644
--- a/packages/connect/src/providers.ts
+++ b/packages/connect/src/providers.ts
@@ -15,7 +15,7 @@ export const DEFAULT_PROVIDERS: WebBTCProvider[] = [
mozillaAddOnsUrl: 'https://leather.io/install-extension',
},
{
- id: 'XverseProviders.StacksProvider',
+ id: 'XverseProviders.BitcoinProvider',
name: 'Xverse Wallet',
icon: 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI2MDAiIGhlaWdodD0iNjAwIj48ZyBmaWxsPSJub25lIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiPjxwYXRoIGZpbGw9IiMxNzE3MTciIGQ9Ik0wIDBoNjAwdjYwMEgweiIvPjxwYXRoIGZpbGw9IiNGRkYiIGZpbGwtcnVsZT0ibm9uemVybyIgZD0iTTQ0MCA0MzUuNHYtNTFjMC0yLS44LTMuOS0yLjItNS4zTDIyMCAxNjIuMmE3LjYgNy42IDAgMCAwLTUuNC0yLjJoLTUxLjFjLTIuNSAwLTQuNiAyLTQuNiA0LjZ2NDcuM2MwIDIgLjggNCAyLjIgNS40bDc4LjIgNzcuOGE0LjYgNC42IDAgMCAxIDAgNi41bC03OSA3OC43Yy0xIC45LTEuNCAyLTEuNCAzLjJ2NTJjMCAyLjQgMiA0LjUgNC42IDQuNUgyNDljMi42IDAgNC42LTIgNC42LTQuNlY0MDVjMC0xLjIuNS0yLjQgMS40LTMuM2w0Mi40LTQyLjJhNC42IDQuNiAwIDAgMSA2LjQgMGw3OC43IDc4LjRhNy42IDcuNiAwIDAgMCA1LjQgMi4yaDQ3LjVjMi41IDAgNC42LTIgNC42LTQuNloiLz48cGF0aCBmaWxsPSIjRUU3QTMwIiBmaWxsLXJ1bGU9Im5vbnplcm8iIGQ9Ik0zMjUuNiAyMjcuMmg0Mi44YzIuNiAwIDQuNiAyLjEgNC42IDQuNnY0Mi42YzAgNCA1IDYuMSA4IDMuMmw1OC43LTU4LjVjLjgtLjggMS4zLTIgMS4zLTMuMnYtNTEuMmMwLTIuNi0yLTQuNi00LjYtNC42TDM4NCAxNjBjLTEuMiAwLTIuNC41LTMuMyAxLjNsLTU4LjQgNTguMWE0LjYgNC42IDAgMCAwIDMuMiA3LjhaIi8+PC9nPjwvc3ZnPg==',
diff --git a/packages/connect/src/request.ts b/packages/connect/src/request.ts
new file mode 100644
index 00000000..9c785edb
--- /dev/null
+++ b/packages/connect/src/request.ts
@@ -0,0 +1,217 @@
+import { getInstalledProviders, getProvider, WbipProvider } from '@stacks/connect-ui';
+import { defineCustomElements } from '@stacks/connect-ui/loader';
+import { JsonRpcError, JsonRpcErrorCode } from './errors';
+import { MethodParams, MethodResult, Methods } from './methods';
+import { DEFAULT_PROVIDERS } from './providers';
+import { StacksProvider } from './types';
+
+export interface ConnectRequestOptions {
+ /**
+ * The provider to use for the request.
+ * If none is provided the UI will be shown.
+ * Defaults to the previously selected provider (unless `forceWalletSelect` is `true`).
+ */
+ provider?: StacksProvider;
+
+ /**
+ * The default wallets to display in the modal.
+ * Defaults to some known popular wallets.
+ */
+ defaultProviders?: WbipProvider[];
+
+ /**
+ * Forces the user to select a wallet.
+ * Defaults to `false`.
+ */
+ forceWalletSelect?: boolean;
+
+ /**
+ * Persist the selected wallet across requests.
+ * Defaults to `true`.
+ */
+ persistWalletSelect?: boolean;
+
+ /**
+ * Adds manual request rewriting to make different providers behave more closely to SIP-030 / WBIPs.
+ * Defaults to `true`.
+ */
+ enableOverrides?: boolean;
+
+ // todo: maybe add callbacks, if set use them instead of throwing errors
+}
+
+export async function requestRaw(
+ provider: StacksProvider,
+ method: M,
+ params?: MethodParams
+): Promise> {
+ try {
+ const response = await provider.request(method, params);
+ if ('error' in response) throw JsonRpcError.fromResponse(response.error);
+
+ return response.result;
+ } catch (error) {
+ const code = error.code ?? JsonRpcErrorCode.UnknownError;
+ throw new JsonRpcError(error.message, code, error.data, error.cause);
+ }
+}
+
+export async function request(
+ method: M,
+ params?: MethodParams
+): Promise>;
+export async function request(
+ options: ConnectRequestOptions,
+ method: M,
+ params?: MethodParams
+): Promise>;
+export async function request(
+ ...args:
+ | [method: M, params?: MethodParams]
+ | [options: ConnectRequestOptions, method: M, params?: MethodParams]
+): Promise> {
+ const { options, method, params } = requestArgs(args);
+
+ // Default options
+ const opts = Object.assign(
+ {
+ provider: getProvider(),
+ defaultProviders: DEFAULT_PROVIDERS,
+
+ forceWalletSelect: false,
+ persistWalletSelect: true,
+ enableOverrides: true,
+ },
+ shallowDefined(options)
+ );
+
+ // WITHOUT UI
+ if (opts.provider && !opts.forceWalletSelect) {
+ return await requestRaw(opts.provider, method, params);
+ }
+
+ // WITH UI
+ if (typeof window === 'undefined') return undefined; // don't throw for SSR contexts
+
+ void defineCustomElements(window);
+
+ return new Promise((resolve, reject) => {
+ const element = document.createElement('connect-modal');
+ element.defaultProviders = opts.defaultProviders;
+ element.installedProviders = getInstalledProviders(opts.defaultProviders);
+ element.persistWalletSelect = opts.persistWalletSelect;
+
+ const originalOverflow = document.body.style.overflow;
+ document.body.style.overflow = 'hidden';
+
+ const closeModal = () => {
+ element.remove();
+ document.body.style.overflow = originalOverflow;
+ };
+
+ element.callback = (selectedProvider: StacksProvider | undefined) => {
+ closeModal();
+
+ // =======================================================================
+ // OVERRIDES
+ // We may need to maintain some overrides to make different providers semi-compatible.
+
+ // Xverse
+ if (
+ opts.enableOverrides &&
+ isXverse(selectedProvider) &&
+ // Permission granting method
+ ['getAddresses', 'stx_getAddresses', 'stx_getAccounts'].includes(method)
+ ) {
+ return resolve(requestRaw(selectedProvider, 'wallet_connect' as any, params)); // Use unknown 'wallet_connect' instead.
+ }
+ // =======================================================================
+
+ resolve(requestRaw(selectedProvider, method, params));
+ };
+
+ element.cancelCallback = () => {
+ closeModal();
+ reject(new JsonRpcError('User canceled the request', JsonRpcErrorCode.UserCanceled));
+ };
+
+ document.body.appendChild(element);
+
+ const handleEsc = (ev: KeyboardEvent) => {
+ if (ev.key !== 'Escape') return;
+ document.removeEventListener('keydown', handleEsc);
+ element.remove();
+ reject(new JsonRpcError('User canceled the request', JsonRpcErrorCode.UserCanceled));
+ };
+ document.addEventListener('keydown', handleEsc);
+ });
+}
+
+/** @internal */
+function requestArgs(
+ args:
+ | [method: M, params?: MethodParams]
+ | [options: ConnectRequestOptions, method: M, params?: MethodParams]
+): {
+ method: M;
+ params?: MethodParams;
+ options?: ConnectRequestOptions;
+} {
+ if (typeof args[0] === 'string') return { method: args[0], params: args[1] as MethodParams };
+ return { options: args[0], method: args[1] as M, params: args[2] };
+}
+
+/**
+ * **Note:** Higher order function!
+ * @internal Legacy non-UI request.
+ */
+export function requestRawLegacy(
+ method: M,
+ mapOptions: (options: O) => MethodParams,
+ mapResponse: (response: MethodResult) => R
+) {
+ return (options: O, provider?: StacksProvider) => {
+ if (!provider) throw new Error('[Connect] No installed Stacks wallet found');
+
+ const params = mapOptions(options);
+
+ // Manual cast, since TypeScipt can't infer generic type of options
+ const o = options as {
+ onCancel?: () => void;
+ onFinish?: (response: R) => void;
+ };
+
+ void requestRaw(provider, method, params)
+ .then(response => {
+ const r = mapResponse(response);
+ o.onFinish?.(r);
+ })
+ .catch(o.onCancel);
+ };
+}
+
+// todo: strip params that might be unserializable or privacy sensitive, e.g.
+// appDetails?: AuthOptions['appDetails'];
+// authOrigin?: string;
+// network?: ConnectNetwork;
+// stxAddress?: string;
+// userSession?: UserSession;
+// onFinish?: ProfileUpdateFinished;
+// onCancel?: ProfileUpdateCanceled;
+
+function isXverse(provider: StacksProvider) {
+ return (
+ // Best effort detection for Xverse
+ 'signMultipleTransactions' in provider &&
+ 'createRepeatInscriptions' in provider &&
+ !provider?.['isLeather']
+ );
+}
+
+function shallowDefined(obj: T): Partial {
+ const result: Partial = {};
+ for (const [key, value] of Object.entries(obj)) {
+ if (value !== undefined) result[key as keyof T] = value;
+ }
+ return result;
+}
diff --git a/packages/connect/src/signature/index.ts b/packages/connect/src/signature/index.ts
index 09c3e6db..5e67058c 100644
--- a/packages/connect/src/signature/index.ts
+++ b/packages/connect/src/signature/index.ts
@@ -1,99 +1,43 @@
-import { ChainId } from '@stacks/network';
-import { createUnsecuredToken, TokenSigner } from 'jsontokens';
-import { getKeys, getUserSession, hasAppPrivateKey } from '../transactions';
+import { MethodParams, MethodResult } from '../methods';
+import { requestRawLegacy } from '../request';
import { StacksProvider } from '../types';
import {
CommonSignatureRequestOptions,
- SignatureOptions,
- SignaturePayload,
- SignaturePopup,
+ SignatureData,
SignatureRequestOptions,
} from '../types/signature';
-import { getStacksProvider, legacyNetworkFromConnectNetwork } from '../utils';
+import { getStacksProvider } from '../utils';
-function getStxAddress(options: CommonSignatureRequestOptions) {
- const { userSession, network: _network } = options;
-
- if (!userSession || !_network) return undefined;
- const stxAddresses = userSession?.loadUserData().profile?.stxAddress;
- const chainIdToKey = {
- [ChainId.Mainnet]: 'mainnet',
- [ChainId.Testnet]: 'testnet',
- };
- const network = legacyNetworkFromConnectNetwork(_network);
- const address: string | undefined = stxAddresses?.[chainIdToKey[network.chainId]];
- return address;
-}
-
-// eslint-disable-next-line @typescript-eslint/require-await
-async function signPayload(payload: SignaturePayload, privateKey: string) {
- const tokenSigner = new TokenSigner('ES256k', privateKey);
- // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
- return tokenSigner.signAsync({ ...payload } as any);
-}
-
-export function getDefaultSignatureRequestOptions(options: CommonSignatureRequestOptions) {
- const network = legacyNetworkFromConnectNetwork(options.network);
- const userSession = getUserSession(options.userSession);
- const defaults: CommonSignatureRequestOptions = {
- ...options,
- network,
- userSession,
- };
- return {
- stxAddress: getStxAddress(defaults),
- ...defaults,
- };
-}
-
-async function openSignaturePopup({ token, options }: SignaturePopup, provider: StacksProvider) {
- try {
- const signatureResponse = await provider.signatureRequest(token);
- options.onFinish?.(signatureResponse);
- } catch (error) {
- console.error('[Connect] Error during signature request', error);
- options.onCancel?.();
- }
-}
+/** @deprecated No-op. Tokens are not needed for latest RPC endpoints. */
+export function getDefaultSignatureRequestOptions(_options: CommonSignatureRequestOptions) {}
export interface SignatureRequestPayload {
message: string;
}
-// eslint-disable-next-line @typescript-eslint/require-await
-export const signMessage = async (options: SignatureRequestOptions) => {
- const { userSession, ..._options } = options;
- if (hasAppPrivateKey(userSession)) {
- const { privateKey, publicKey } = getKeys(userSession);
+/** @deprecated No-op. Tokens are not needed for latest RPC endpoints. */
+export const signMessage = async (_options: SignatureRequestOptions) => {};
- const payload: SignaturePayload = {
- ..._options,
- publicKey,
- };
+const METHOD = 'stx_signMessage' as const;
- return signPayload(payload, privateKey);
- }
- const payload = { ..._options };
- // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
- return createUnsecuredToken(payload as any);
-};
+/** @internal */
+export const LEGACY_SIGN_MESSAGE_OPTIONS_MAP = (
+ options: SignatureRequestOptions
+): MethodParams => options;
-async function generateTokenAndOpenPopup(
- options: T,
- makeTokenFn: (options: T) => Promise,
- provider: StacksProvider
-) {
- const token = await makeTokenFn({
- ...getDefaultSignatureRequestOptions(options),
- ...options,
- } as T);
- return openSignaturePopup({ token, options }, provider);
-}
+/** @internal */
+export const LEGACY_SIGN_MESSAGE_RESPONSE_MAP = (
+ response: MethodResult
+): SignatureData => response;
+/** Compatible interface with previous Connect `openSignatureRequestPopup` version, but using new SIP-030 RPC method. */
export function openSignatureRequestPopup(
options: SignatureRequestOptions,
provider: StacksProvider = getStacksProvider()
) {
- if (!provider) throw new Error('[Connect] No installed Stacks wallet found');
- return generateTokenAndOpenPopup(options, signMessage, provider);
+ requestRawLegacy(
+ METHOD,
+ LEGACY_SIGN_MESSAGE_OPTIONS_MAP,
+ LEGACY_SIGN_MESSAGE_RESPONSE_MAP
+ )(options, provider);
}
diff --git a/packages/connect/src/signature/structuredData.ts b/packages/connect/src/signature/structuredData.ts
index f661cc1d..2553e24a 100644
--- a/packages/connect/src/signature/structuredData.ts
+++ b/packages/connect/src/signature/structuredData.ts
@@ -1,94 +1,42 @@
-import { bytesToHex } from '@stacks/common';
-import {
- serializeCV as legacySerializeCV,
- ClarityValue as LegacyClarityValue,
- TupleCV as LegacyTupleCV,
-} from '@stacks/transactions-v6';
-import { serializeCV } from '@stacks/transactions';
-import { createUnsecuredToken, Json, TokenSigner } from 'jsontokens';
-import { getDefaultSignatureRequestOptions } from '.';
-import { getKeys, hasAppPrivateKey } from '../transactions';
-import {
- StructuredDataSignatureOptions,
- StructuredDataSignaturePayload,
- StructuredDataSignaturePopup,
- StructuredDataSignatureRequestOptions,
-} from '../types/structuredDataSignature';
-import { getStacksProvider } from '../utils';
-import { StacksProvider } from '../types';
-
-async function generateTokenAndOpenPopup(
- options: T,
- makeTokenFn: (options: T) => Promise,
- provider: StacksProvider
-) {
- const token = await makeTokenFn({
- ...getDefaultSignatureRequestOptions(options),
- ...options,
- } as T);
- return openStructuredDataSignaturePopup({ token, options }, provider);
-}
-
-function parseUnserializableBigIntValues(payload: StructuredDataSignaturePayload) {
- const { message, domain } = payload;
-
- if (typeof message.type === 'string' && typeof domain.type === 'string') {
- // new readable types
- return {
- ...payload,
- message: serializeCV(message),
- domain: serializeCV(domain),
- } as Json;
- }
-
- // legacy types
- return {
- ...payload,
- message: bytesToHex(legacySerializeCV(message as LegacyClarityValue)),
- domain: bytesToHex(legacySerializeCV(domain as LegacyTupleCV)),
- } as Json;
-}
-
-// eslint-disable-next-line @typescript-eslint/require-await
-async function signPayload(payload: StructuredDataSignaturePayload, privateKey: string) {
- const tokenSigner = new TokenSigner('ES256k', privateKey);
- return tokenSigner.signAsync(parseUnserializableBigIntValues(payload));
-}
-
-// eslint-disable-next-line @typescript-eslint/require-await
-export async function signStructuredMessage(options: StructuredDataSignatureRequestOptions) {
- const { userSession, ..._options } = options;
- if (hasAppPrivateKey(userSession)) {
- const { privateKey, publicKey } = getKeys(userSession);
- const payload: StructuredDataSignaturePayload = {
- ..._options,
- publicKey,
- };
- return signPayload(payload, privateKey);
- }
- return createUnsecuredToken(
- parseUnserializableBigIntValues(options as StructuredDataSignaturePayload)
- );
-}
-
-async function openStructuredDataSignaturePopup(
- { token, options }: StructuredDataSignaturePopup,
- provider: StacksProvider
-) {
- try {
- const signatureResponse = await provider.structuredDataSignatureRequest(token);
-
- options.onFinish?.(signatureResponse);
- } catch (error) {
- console.error('[Connect] Error during signature request', error);
- options.onCancel?.();
- }
-}
-
+import { TupleCV } from '@stacks/transactions';
+import { ClarityType as LegacyClarityType } from '@stacks/transactions-v6';
+import { MethodParams, MethodResult } from '../methods';
+import { requestRawLegacy } from '../request';
+import { SignatureData, StacksProvider } from '../types';
+import { StructuredDataSignatureRequestOptions } from '../types/structuredDataSignature';
+import { getStacksProvider, legacyCVToCV } from '../utils';
+
+/** @deprecated No-op. Tokens are not needed for latest RPC endpoints. */
+export async function signStructuredMessage(_options: StructuredDataSignatureRequestOptions) {}
+
+const METHOD = 'stx_signStructuredMessage' as const;
+
+/** @internal */
+export const LEGACY_SIGN_STRUCTURED_MESSAGE_OPTIONS_MAP = (
+ options: StructuredDataSignatureRequestOptions
+): MethodParams => ({
+ // todo: also make sure that cvs don't have bigint unserializable values
+ message: legacyCVToCV(options.message),
+ domain: legacyCVToCV(options.domain) as TupleCV, // safe cast, because of below check
+});
+
+/** @internal */
+export const LEGACY_SIGN_STRUCTURED_MESSAGE_RESPONSE_MAP = (
+ response: MethodResult
+): SignatureData => response;
+
+/** Compatible interface with previous Connect `openStructuredDataSignatureRequestPopup` version, but using new SIP-030 RPC method. */
export function openStructuredDataSignatureRequestPopup(
options: StructuredDataSignatureRequestOptions,
provider: StacksProvider = getStacksProvider()
-) {
- if (!provider) throw new Error('[Connect] No installed Stacks wallet found');
- return generateTokenAndOpenPopup(options, signStructuredMessage, provider);
+): void {
+ if (options.domain.type !== LegacyClarityType.Tuple) {
+ throw new Error('Domain must be a tuple'); // check, ensures domain is a tuple
+ }
+
+ requestRawLegacy(
+ METHOD,
+ LEGACY_SIGN_STRUCTURED_MESSAGE_OPTIONS_MAP,
+ LEGACY_SIGN_STRUCTURED_MESSAGE_RESPONSE_MAP
+ )(options, provider);
}
diff --git a/packages/connect/src/stories/Connect.stories.tsx b/packages/connect/src/stories/Connect.stories.tsx
index d0f3a561..2f64377e 100644
--- a/packages/connect/src/stories/Connect.stories.tsx
+++ b/packages/connect/src/stories/Connect.stories.tsx
@@ -1,6 +1,12 @@
import { DEFAULT_PROVIDERS } from '../providers';
import { ConnectPage } from './ConnectPage';
+// set dark mode
+if (typeof document !== 'undefined') {
+ document.body.style.backgroundColor = '#303030';
+ document.body.style.color = '#d0d0d0';
+}
+
export default {
component: ConnectPage,
};
@@ -12,7 +18,7 @@ export const NoMocking = {
// 2. Mocking
const ConnectWithMockedRegister = () => {
- window.webbtc_stx_providers = [DEFAULT_PROVIDERS[0]]; // simulate installed wallet
+ window.wbip_providers = [DEFAULT_PROVIDERS[0]]; // simulate installed wallet
return ;
};
export const WithMockedRegister = {
@@ -26,7 +32,7 @@ declare global {
}
}
const ConnectWithMockedWallet = () => {
- window.webbtc_stx_providers = [
+ window.wbip_providers = [
{
id: 'MockedProvider',
name: 'Mocked Wallet',
diff --git a/packages/connect/src/stories/ConnectPage.tsx b/packages/connect/src/stories/ConnectPage.tsx
index f58bb567..1d13edce 100644
--- a/packages/connect/src/stories/ConnectPage.tsx
+++ b/packages/connect/src/stories/ConnectPage.tsx
@@ -1,12 +1,17 @@
import { getSelectedProviderId } from '@stacks/connect-ui';
import { useReducer } from 'react';
-import { disconnect, showConnect, showSignMessage } from '../ui';
+import { disconnect, showConnect, showSignMessage, showSTXTransfer } from '../ui';
+import { UserSession } from '../auth';
+
+const userSession = new UserSession();
export const ConnectPage = ({ children }: { children?: any }) => {
const refresh = useReducer(x => x + 1, 0)[1];
const selectedProviderId = getSelectedProviderId();
+ const userData = userSession.isUserSignedIn() ? userSession.loadUserData() : 'User not signed in';
+
return (
{
+
+ userData
: {JSON.stringify(userData, null, 2)}