@vue-solana/vue 3.0.1 → 4.0.0-rc.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (104) hide show
  1. package/README.md +8 -4
  2. package/dist/index.d.ts +12 -0
  3. package/dist/index.mjs +79 -47
  4. package/dist/shared/{vue.CjSw0Hh1.mjs → vue.4ne5EBD9.mjs} +4 -4
  5. package/dist/shared/{vue.C9OOmvkD.mjs → vue.BBN9aU6D.mjs} +2 -2
  6. package/dist/shared/{vue.CsFY1mfz.mjs → vue.BXNjyJ2c.mjs} +1 -1
  7. package/dist/shared/{vue.DlihC5GG.mjs → vue.BiKE9Xuv.mjs} +6 -4
  8. package/dist/shared/{vue.CXB4NEMA.mjs → vue.Bu2cQ690.mjs} +47 -17
  9. package/dist/shared/{vue.CpkwC8Th.mjs → vue.C6OkOK2W.mjs} +1 -1
  10. package/dist/shared/{vue.B9uRpSDJ.mjs → vue.CAltY57B.mjs} +6 -3
  11. package/dist/shared/{vue.DDQxNX5q.mjs → vue.CELGI7de.mjs} +9 -9
  12. package/dist/shared/{vue.BxtOHzY_.mjs → vue.CLAqNcLp.mjs} +1 -1
  13. package/dist/shared/{vue.hG_FieA3.mjs → vue.CW-1AuSO.mjs} +2 -2
  14. package/dist/shared/{vue.Mxc8w6Qk.mjs → vue.CZApWQAY.mjs} +1 -1
  15. package/dist/shared/vue.CbmzveKF.mjs +44 -0
  16. package/dist/shared/{vue.CQh3AUE8.mjs → vue.CcOZ9858.mjs} +3 -5
  17. package/dist/shared/{vue.B-xtuLtD.mjs → vue.D2J7_NpQ.mjs} +2 -2
  18. package/dist/shared/{vue.BsGwt78R.mjs → vue.D8zNWw2t.mjs} +1 -1
  19. package/dist/shared/{vue.CaAgH2Xt.mjs → vue.DPwdqRO_.mjs} +3 -3
  20. package/dist/shared/{vue.DNKD6jqM.mjs → vue.DTpvqDyY.mjs} +3 -3
  21. package/dist/shared/{vue.D218Ow7t.mjs → vue.DY9VadLH.mjs} +1 -8
  22. package/dist/shared/vue.DZVdVfy1.mjs +7 -0
  23. package/dist/shared/{vue.CvenSbZZ.mjs → vue.DZhPXLZz.mjs} +4 -4
  24. package/dist/shared/{vue.o98pAmDg.mjs → vue.DbJG4-ku.mjs} +2 -2
  25. package/dist/shared/{vue.BFSboXMB.mjs → vue.DbJKnSrl.mjs} +3 -3
  26. package/dist/shared/{vue.1Qwco2kv.mjs → vue.DcZ_R_RH.mjs} +4 -4
  27. package/dist/shared/{vue.C_4qJDiF.mjs → vue.DyLgoSbX.mjs} +1 -1
  28. package/dist/shared/{vue.XlfH_nkc.mjs → vue.FehFeE3w.mjs} +3 -3
  29. package/dist/shared/{vue.C1JbUTpJ.mjs → vue.UrCjmf0T.mjs} +2 -2
  30. package/dist/swr.d.ts +7 -1
  31. package/dist/swr.mjs +16 -1
  32. package/dist/useAccountInfo.mjs +1 -1
  33. package/dist/useAirdrop.mjs +1 -1
  34. package/dist/useBalance.mjs +1 -1
  35. package/dist/useClientCapability.d.ts +6 -1
  36. package/dist/useClientCapability.mjs +1 -1
  37. package/dist/useConnection.d.ts +14 -7
  38. package/dist/useConnection.mjs +1 -1
  39. package/dist/useIdentity.mjs +1 -1
  40. package/dist/usePayer.mjs +1 -1
  41. package/dist/usePlanTransaction.mjs +1 -1
  42. package/dist/usePlanTransactions.mjs +1 -1
  43. package/dist/useProgramAccounts.mjs +1 -1
  44. package/dist/useRpc.d.ts +12 -6
  45. package/dist/useRpc.mjs +1 -1
  46. package/dist/useSelectedWalletAccount.d.ts +3 -0
  47. package/dist/useSendTransaction.mjs +1 -1
  48. package/dist/useSendTransactions.mjs +1 -1
  49. package/dist/useSignAndSendTransaction.mjs +1 -1
  50. package/dist/useSignAndSendTransactions.mjs +1 -1
  51. package/dist/useSignIn.mjs +1 -1
  52. package/dist/useSignMessage.mjs +1 -1
  53. package/dist/useSignTransactions.mjs +1 -1
  54. package/dist/useSignatureStatus.d.ts +0 -1
  55. package/dist/useSignatureStatus.mjs +1 -1
  56. package/dist/useSolana.mjs +1 -1
  57. package/dist/useSolanaClient.d.ts +12 -6
  58. package/dist/useSolanaClient.mjs +1 -1
  59. package/dist/useTokenAccounts.mjs +1 -1
  60. package/dist/useTokenBalance.mjs +1 -1
  61. package/dist/useTransactionConfirmation.mjs +1 -1
  62. package/dist/useWallet.mjs +1 -1
  63. package/dist/useWallets.mjs +1 -1
  64. package/package.json +8 -5
  65. package/dist/buffer-polyfill.d.mts +0 -2
  66. package/dist/index.d.mts +0 -64
  67. package/dist/kit.d.mts +0 -1
  68. package/dist/shared/vue.BGGixph-.d.mts +0 -5
  69. package/dist/shared/vue.Bfu1dCKe.mjs +0 -10
  70. package/dist/shared/vue.CGv0ip3w.mjs +0 -52
  71. package/dist/shared/vue.DL3z5PxP.d.mts +0 -21
  72. package/dist/swr.d.mts +0 -41
  73. package/dist/useAccountInfo.d.mts +0 -24
  74. package/dist/useAction.d.mts +0 -45
  75. package/dist/useAirdrop.d.mts +0 -27
  76. package/dist/useBalance.d.mts +0 -15
  77. package/dist/useClientCapability.d.mts +0 -39
  78. package/dist/useConnection.d.mts +0 -46
  79. package/dist/useIdentity.d.mts +0 -3
  80. package/dist/usePayer.d.mts +0 -18
  81. package/dist/usePlanTransaction.d.mts +0 -44
  82. package/dist/usePlanTransactions.d.mts +0 -5
  83. package/dist/useProgramAccounts.d.mts +0 -45
  84. package/dist/useRequest.d.mts +0 -73
  85. package/dist/useRpc.d.mts +0 -55
  86. package/dist/useSelectedWalletAccount.d.mts +0 -117
  87. package/dist/useSendTransaction.d.mts +0 -50
  88. package/dist/useSendTransactions.d.mts +0 -5
  89. package/dist/useSignAndSendTransaction.d.mts +0 -40
  90. package/dist/useSignAndSendTransactions.d.mts +0 -35
  91. package/dist/useSignIn.d.mts +0 -23
  92. package/dist/useSignMessage.d.mts +0 -236
  93. package/dist/useSignTransactions.d.mts +0 -24
  94. package/dist/useSignatureStatus.d.mts +0 -26
  95. package/dist/useSolana.d.mts +0 -9
  96. package/dist/useSolanaClient.d.mts +0 -45
  97. package/dist/useSubscription.d.mts +0 -70
  98. package/dist/useTokenAccounts.d.mts +0 -19
  99. package/dist/useTokenBalance.d.mts +0 -17
  100. package/dist/useTrackedData.d.mts +0 -98
  101. package/dist/useTransaction.d.mts +0 -27
  102. package/dist/useTransactionConfirmation.d.mts +0 -38
  103. package/dist/useWallet.d.mts +0 -31
  104. package/dist/useWallets.d.mts +0 -11
package/README.md CHANGED
@@ -98,7 +98,7 @@ createApp(App).use(
98
98
 
99
99
  Supported clusters are `mainnet` (legacy alias `mainnet-beta`), `devnet`, `testnet`, and `localnet`. Use `mainnet` for Solana mainnet; this is Solana's official mainnet cluster name.
100
100
 
101
- `payer` and `payerSecretKey` are supported by direct Vue/core clients. A client-sent transaction requires a payer. Never put a raw secret or `payerSecretKey` in Nuxt public runtime config, and never ship a funded signing key to an end-user browser. The default `createSolanaPlugin()` client composes the official `solanaRpc()`, `rpcTransactionPlanner()`, and `rpcTransactionPlanSendingExecutor()` stack; the old custom fallback sender is not used.
101
+ `payer` and `payerSecretKey` are supported by direct Vue/core clients. A client-sent transaction requires a payer. Never put a raw secret or `payerSecretKey` in Nuxt public runtime config, and never ship a funded signing key to an end-user browser. The default `createSolanaPlugin()` client composes `solanaRpc()` followed by `rpcAirdrop()`; `solanaRpc()` installs `rpcTransactionPlanner()`, `rpcTransactionPlanSigningExecutor()`, and `rpcTransactionPlanSendingExecutor()` itself, so the client exposes RPC reads and subscriptions plus `planTransaction(s)`, `signTransaction(s)`, and `sendTransaction(s)` with no extra plugin. The old custom fallback sender is not used.
102
102
 
103
103
  ### Plugin Options
104
104
 
@@ -312,7 +312,7 @@ async function signIn() {
312
312
  </template>
313
313
  ```
314
314
 
315
- Message signing proves wallet ownership for flows like authentication challenges. It does not sign, submit, or authorize Solana transactions. For authentication, issue a server-generated nonce, include domain and expiration details in the message, and verify the returned signature server-side.
315
+ Message signing proves wallet ownership for flows like authentication challenges. It does not sign, submit, or authorize Solana transactions. For authentication, issue a server-generated nonce, include domain and expiration details in the message, and verify the returned signature server-side. The adapter also checks the signature against the account's public key before resolving, where Ed25519 (WebCrypto) is available — a best-effort client-side guard that rejects with `INVALID_SIGNATURE`, not a replacement for server-side verification.
316
316
 
317
317
  ## Transaction State
318
318
 
@@ -391,7 +391,7 @@ Configure a direct client with `payer` or `payerSecretKey`. `payerSecretKey` is
391
391
 
392
392
  Use the client flow for automated or server-backed signing (airdrop faucet, cron jobs, relayer fees paid by your keypair), and the wallet flow when the end user must own and approve each transaction.
393
393
 
394
- **Default client composition.** `createSolanaPlugin()` and `createSolanaClient()` install the official `solanaRpc()`, `rpcTransactionPlanner()`, and `rpcTransactionPlanSendingExecutor()` plugins by default. The old custom fallback sender is not used, so `useSendTransaction()` and `useSendTransactions()` do not need a second manual plugin installation. The official executor waits for `confirmed` commitment before `execute()` resolves and the composable reports `sent`. A custom client must still provide the planner and sending executor and a `payer`.
394
+ **Default client composition.** `createSolanaPlugin()` and `createSolanaClient()` compose `solanaRpc()` followed by `rpcAirdrop()` by default; `solanaRpc()` installs `rpcTransactionPlanner()`, `rpcTransactionPlanSigningExecutor()`, and `rpcTransactionPlanSendingExecutor()` itself. The old custom fallback sender is not used, so `useSendTransaction()` and `useSendTransactions()` do not need a second manual plugin installation. The official executor waits for `confirmed` commitment before `execute()` resolves and the composable reports `sent`. A custom client must still provide the planner and sending executor and a `payer`.
395
395
 
396
396
  ```ts
397
397
  import { useSendTransaction } from "@vue-solana/vue/useSendTransaction";
@@ -488,6 +488,8 @@ import { useRequestSwr } from "@vue-solana/vue/swr";
488
488
  const balance = useRequestSwr(["balance", address], () => client.rpc.getBalance(address).send());
489
489
  ```
490
490
 
491
+ The SWR cache is module-level and shared across the whole app, including across SSR requests. Namespace cache keys per request (for example by including a request or session id) or call `clearSwrCache()` at the start of each server request to avoid leaking one request's cached data into another. On the client the cache is intentionally shared for stale-while-revalidate seeding.
492
+
491
493
  ### Cancellation & Timeouts
492
494
 
493
495
  `useRequest()`, `useSubscription()`, and `useTrackedData()` accept a `getAbortSignal` option — a factory called once per attempt (request) or connection (subscription/tracked data). It is the natural place for a timeout:
@@ -592,7 +594,9 @@ Docs: [Vue Solana Agent Skill](https://vue-solana-docs.vercel.app/agent-skill)
592
594
  | ------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------- |
593
595
  | `createSolanaPlugin(options?)` | Installs the Vue Solana context. |
594
596
  | `VueSolana` | Alias for `createSolanaPlugin`. |
595
- | `useSolana()` | Returns the full injected Solana context. |
597
+ | `useSolana()` | Returns the full injected Solana context; throws when no plugin context is available. |
598
+ | `tryUseSolana()` | Returns the injected Solana context or `null` when no plugin context is available (SSR-safe). |
599
+ | `MissingClientCapabilityError` | Thrown by `useClientCapability()` (and client-sent composables) when the configured client lacks a required capability. |
596
600
  | `useRpc()` | Returns cluster, endpoint, connection status, latest blockhash, the Kit `client`, and `checkConnection()`. |
597
601
  | `useSolanaClient()` | Returns the injected Kit client as `{ client, rpc }`. The recommended RPC path. |
598
602
  | `useConnection()` | Returns the Kit `client`. Deprecated in favor of `useSolanaClient()`. |
package/dist/index.d.ts CHANGED
@@ -56,6 +56,18 @@ interface VueSolanaPlugin {
56
56
  * context to consumers that run outside a component setup.
57
57
  */
58
58
  context: VueSolanaContext | null;
59
+ /**
60
+ * Stops the wallet discovery subscription and the deferred bootstrap started
61
+ * by `install`. Vue has no plugin unmount hook, so call this from an app
62
+ * teardown hook (`app.onUnmount` in Nuxt, after `app.unmount()` in Vue).
63
+ * Safe to call more than once and before `install`. Without it the Wallet
64
+ * Standard subscription outlives the app that created it. After `dispose()`,
65
+ * `refreshWallets()` is a no-op, so a late mobile-wallet import or manual
66
+ * call cannot resurrect the subscription. A connected wallet stays
67
+ * connected; `dispose()` does not disconnect it or abort an in-flight
68
+ * `connect()`.
69
+ */
70
+ dispose(): void;
59
71
  }
60
72
  declare function createSolanaPlugin(options?: VueSolanaPluginOptions): VueSolanaPlugin;
61
73
  declare const VueSolana: typeof createSolanaPlugin;
package/dist/index.mjs CHANGED
@@ -1,32 +1,32 @@
1
- export { u as useAccountInfo } from './shared/vue.CvenSbZZ.mjs';
1
+ export { u as useAccountInfo } from './shared/vue.DZhPXLZz.mjs';
2
2
  export { u as useAction } from './shared/vue.DYk0Xfr3.mjs';
3
- export { u as useAirdrop } from './shared/vue.DDQxNX5q.mjs';
4
- export { u as useBalance } from './shared/vue.BFSboXMB.mjs';
5
- export { M as MissingClientCapabilityError, u as useClientCapability } from './shared/vue.B9uRpSDJ.mjs';
6
- export { u as useConnection } from './shared/vue.CpkwC8Th.mjs';
7
- export { u as usePlanTransaction, a as usePlanTransactions } from './shared/vue.CsFY1mfz.mjs';
8
- export { u as useProgramAccounts } from './shared/vue.1Qwco2kv.mjs';
9
- export { u as useIdentity, a as usePayer } from './shared/vue.B-xtuLtD.mjs';
3
+ export { u as useAirdrop } from './shared/vue.CELGI7de.mjs';
4
+ export { u as useBalance } from './shared/vue.DbJKnSrl.mjs';
5
+ export { M as MissingClientCapabilityError, u as useClientCapability } from './shared/vue.CAltY57B.mjs';
6
+ export { u as useConnection } from './shared/vue.C6OkOK2W.mjs';
7
+ export { u as usePlanTransaction, a as usePlanTransactions } from './shared/vue.BXNjyJ2c.mjs';
8
+ export { u as useProgramAccounts } from './shared/vue.DcZ_R_RH.mjs';
9
+ export { u as useIdentity, a as usePayer } from './shared/vue.D2J7_NpQ.mjs';
10
10
  export { u as useRequest } from './shared/vue.B8VvC8d2.mjs';
11
- export { u as useRpc } from './shared/vue.BsGwt78R.mjs';
11
+ export { u as useRpc } from './shared/vue.D8zNWw2t.mjs';
12
12
  export { S as SelectedWalletAccountProvider, c as createSelectedWalletAccountContext, p as provideSelectedWalletAccount, s as selectedWalletAccountInjectionKey, u as useSelectedWalletAccount } from './shared/vue.BngCaeXJ.mjs';
13
- export { u as useSendTransaction, a as useSendTransactions } from './shared/vue.BxtOHzY_.mjs';
14
- export { u as useSignMessage } from './shared/vue.o98pAmDg.mjs';
15
- export { u as useSignAndSendTransaction } from './shared/vue.CjSw0Hh1.mjs';
16
- export { u as useSignTransactions } from './shared/vue.hG_FieA3.mjs';
17
- export { P as PartialSignAndSendError, u as useSignAndSendTransactions } from './shared/vue.C1JbUTpJ.mjs';
18
- export { u as useSignatureStatus } from './shared/vue.CXB4NEMA.mjs';
19
- export { u as useSignIn } from './shared/vue.C9OOmvkD.mjs';
20
- export { t as tryUseSolana, u as useSolana } from './shared/vue.CQh3AUE8.mjs';
21
- export { u as useSolanaClient } from './shared/vue.Mxc8w6Qk.mjs';
13
+ export { u as useSendTransaction, a as useSendTransactions } from './shared/vue.CLAqNcLp.mjs';
14
+ export { u as useSignMessage } from './shared/vue.DbJG4-ku.mjs';
15
+ export { u as useSignAndSendTransaction } from './shared/vue.4ne5EBD9.mjs';
16
+ export { u as useSignTransactions } from './shared/vue.CW-1AuSO.mjs';
17
+ export { P as PartialSignAndSendError, u as useSignAndSendTransactions } from './shared/vue.UrCjmf0T.mjs';
18
+ export { u as useSignatureStatus } from './shared/vue.Bu2cQ690.mjs';
19
+ export { u as useSignIn } from './shared/vue.BBN9aU6D.mjs';
20
+ export { t as tryUseSolana, u as useSolana } from './shared/vue.CcOZ9858.mjs';
21
+ export { u as useSolanaClient } from './shared/vue.CZApWQAY.mjs';
22
22
  export { u as useSubscription } from './shared/vue.D5pqR5Fa.mjs';
23
- export { u as useTokenAccounts } from './shared/vue.DNKD6jqM.mjs';
24
- export { u as useTokenBalance } from './shared/vue.XlfH_nkc.mjs';
23
+ export { u as useTokenAccounts } from './shared/vue.DTpvqDyY.mjs';
24
+ export { u as useTokenBalance } from './shared/vue.FehFeE3w.mjs';
25
25
  export { u as useTrackedData } from './shared/vue.BNJTXSnQ.mjs';
26
26
  export { u as useTransaction } from './shared/vue.5euWd6-V.mjs';
27
- export { g as getConfirmedTransactionStatus, u as useTransactionConfirmation } from './shared/vue.CaAgH2Xt.mjs';
28
- export { u as useWallet } from './shared/vue.D218Ow7t.mjs';
29
- export { u as useWallets } from './shared/vue.C_4qJDiF.mjs';
27
+ export { g as getConfirmedTransactionStatus, u as useTransactionConfirmation } from './shared/vue.DPwdqRO_.mjs';
28
+ export { u as useWallet } from './shared/vue.DY9VadLH.mjs';
29
+ export { u as useWallets } from './shared/vue.DyLgoSbX.mjs';
30
30
  import { s as solanaInjectionKey } from './shared/vue.D_P5SqqD.mjs';
31
31
  import { createSolanaError, normalizeSolanaError } from '@vue-solana/core/errors';
32
32
  export { SolanaError, isSolanaError } from '@vue-solana/core/errors';
@@ -114,20 +114,39 @@ function getLocalStorage() {
114
114
 
115
115
  function createSolanaWalletRegistry(options) {
116
116
  const adaptedWallets = /* @__PURE__ */ new WeakMap();
117
+ const adaptedPrimitiveWallets = /* @__PURE__ */ new Map();
118
+ const trackedAdapters = /* @__PURE__ */ new Set();
119
+ function trackAdapter(adapter) {
120
+ trackedAdapters.add(adapter);
121
+ return adapter;
122
+ }
117
123
  function getAdaptedWallet(walletInfo) {
118
124
  if (isSolanaIosWalletInfo(walletInfo)) {
119
- return adaptSolanaIosWallet(walletInfo, {
125
+ const cachedIosWallet = adaptedWallets.get(walletInfo.wallet);
126
+ if (cachedIosWallet) {
127
+ return cachedIosWallet;
128
+ }
129
+ const iosAdapter = adaptSolanaIosWallet(walletInfo, {
120
130
  chain: getSolanaChain(options.cluster),
121
131
  cluster: options.cluster,
122
132
  onChange: options.onWalletChange,
123
133
  ...options.iosWallet || {}
124
134
  });
135
+ adaptedWallets.set(walletInfo.wallet, iosAdapter);
136
+ return trackAdapter(iosAdapter);
125
137
  }
126
138
  if (!isObject(walletInfo.wallet)) {
127
- return adaptSolanaStandardWallet(walletInfo, {
139
+ const primitiveKey = `${walletInfo.name}::${String(walletInfo.wallet)}`;
140
+ const cachedPrimitiveWallet = adaptedPrimitiveWallets.get(primitiveKey);
141
+ if (cachedPrimitiveWallet) {
142
+ return cachedPrimitiveWallet;
143
+ }
144
+ const primitiveAdapter = adaptSolanaStandardWallet(walletInfo, {
128
145
  chain: getSolanaChain(options.cluster),
129
146
  onChange: options.onWalletChange
130
147
  });
148
+ adaptedPrimitiveWallets.set(primitiveKey, primitiveAdapter);
149
+ return trackAdapter(primitiveAdapter);
131
150
  }
132
151
  const cachedWallet = adaptedWallets.get(walletInfo.wallet);
133
152
  if (cachedWallet) {
@@ -155,7 +174,7 @@ function createSolanaWalletRegistry(options) {
155
174
  );
156
175
  }
157
176
  adaptedWallets.set(walletInfo.wallet, cachedAdapter);
158
- return cachedAdapter;
177
+ return trackAdapter(cachedAdapter);
159
178
  }
160
179
  function getDiscoveredWallets() {
161
180
  return [
@@ -171,7 +190,6 @@ function createSolanaWalletRegistry(options) {
171
190
  try {
172
191
  handleSolanaIosWalletCallback({ clearUrl: true });
173
192
  } catch (cause) {
174
- console.error("[Vue Solana] iOS wallet callback failed", cause);
175
193
  }
176
194
  }
177
195
  function* getCachedWallets() {
@@ -184,10 +202,16 @@ function createSolanaWalletRegistry(options) {
184
202
  }
185
203
  }
186
204
  }
205
+ function disposeAllAdaptedWallets() {
206
+ for (const adapter of trackedAdapters) {
207
+ adapter.dispose?.();
208
+ }
209
+ }
187
210
  return {
188
211
  getAdaptedWallet,
189
212
  getDiscoveredWallets,
190
- handleIosWalletCallback
213
+ handleIosWalletCallback,
214
+ disposeAllAdaptedWallets
191
215
  };
192
216
  }
193
217
  function isObject(value) {
@@ -196,6 +220,7 @@ function isObject(value) {
196
220
 
197
221
  const RPC_CHECK_TIMEOUT_MS = 1e4;
198
222
  function createSolanaPlugin(options = {}) {
223
+ let teardown = null;
199
224
  const plugin = {
200
225
  context: null,
201
226
  install(app) {
@@ -216,15 +241,12 @@ function createSolanaPlugin(options = {}) {
216
241
  let mobileWalletRegistrationPromise = null;
217
242
  let attemptedAutoConnectWallet = null;
218
243
  let rpcCheckId = 0;
244
+ let bootstrapId = 0;
245
+ let disposed = false;
219
246
  async function checkConnection() {
220
247
  const checkId = ++rpcCheckId;
221
248
  status.value = "checking";
222
249
  error.value = null;
223
- console.info("[Vue Solana] Checking RPC connection", {
224
- cluster: context.cluster,
225
- endpoint: context.endpoint,
226
- wsEndpoint: context.wsEndpoint
227
- });
228
250
  try {
229
251
  const response = await withTimeout(
230
252
  context.client.rpc.getLatestBlockhash().send(),
@@ -239,22 +261,18 @@ function createSolanaPlugin(options = {}) {
239
261
  }
240
262
  latestBlockhash.value = blockhash.blockhash;
241
263
  status.value = "connected";
242
- console.info("[Vue Solana] Connected", {
243
- cluster: context.cluster,
244
- endpoint: context.endpoint,
245
- wsEndpoint: context.wsEndpoint,
246
- blockhash
247
- });
248
264
  } catch (cause) {
249
265
  if (checkId !== rpcCheckId) {
250
266
  return;
251
267
  }
252
268
  status.value = "error";
253
269
  error.value = normalizeSolanaError(cause, "RPC_FAILURE");
254
- console.error("[Vue Solana] Connection failed", cause);
255
270
  }
256
271
  }
257
272
  function refreshWallets() {
273
+ if (disposed) {
274
+ return;
275
+ }
258
276
  unsubscribeWallets ??= subscribeSolanaWallets(refreshWallets);
259
277
  walletRegistry.handleIosWalletCallback();
260
278
  wallets.value = walletRegistry.getDiscoveredWallets();
@@ -268,7 +286,6 @@ function createSolanaPlugin(options = {}) {
268
286
  const persistedWallet = readSelectedWallet();
269
287
  if (persistedWallet.error) {
270
288
  error.value = persistedWallet.error;
271
- console.error("[Vue Solana] Selected wallet restoration failed", persistedWallet.error);
272
289
  }
273
290
  restoredWallet = persistedWallet.wallet ? wallets.value.find(
274
291
  (nextWallet) => isSameWallet(nextWallet, persistedWallet.wallet)
@@ -286,14 +303,19 @@ function createSolanaPlugin(options = {}) {
286
303
  }
287
304
  }
288
305
  function registerMobileWallets() {
306
+ if (disposed) {
307
+ return;
308
+ }
289
309
  mobileWalletRegistrationPromise ??= import('@vue-solana/core/mobile-wallet').then(({ registerSolanaMobileWallet }) => {
310
+ if (disposed) {
311
+ return;
312
+ }
290
313
  registerSolanaMobileWallet({
291
314
  chains: [getSolanaChain(context.cluster)],
292
315
  ...options.mobileWallet || {}
293
316
  });
294
317
  refreshWallets();
295
318
  }).catch((cause) => {
296
- console.error("[Vue Solana] Mobile wallet registration failed", cause);
297
319
  }).finally(() => {
298
320
  mobileWalletRegistrationPromise = null;
299
321
  });
@@ -304,7 +326,6 @@ function createSolanaPlugin(options = {}) {
304
326
  const storageError = writeSelectedWallet(nextWallet);
305
327
  if (storageError) {
306
328
  error.value = storageError;
307
- console.error("[Vue Solana] Selected wallet persistence failed", storageError);
308
329
  }
309
330
  }
310
331
  function autoConnectWallet(walletInfo) {
@@ -318,7 +339,6 @@ function createSolanaPlugin(options = {}) {
318
339
  }
319
340
  attemptedAutoConnectWallet = storageValue;
320
341
  void activeWallet.connect().catch((cause) => {
321
- console.error("[Vue Solana] Wallet auto-connect failed", cause);
322
342
  }).finally(() => {
323
343
  triggerRef(wallet);
324
344
  });
@@ -340,22 +360,34 @@ function createSolanaPlugin(options = {}) {
340
360
  const storageError = writeSelectedWallet(null);
341
361
  if (storageError) {
342
362
  error.value = storageError;
343
- console.error("[Vue Solana] Selected wallet persistence failed", storageError);
344
363
  }
345
364
  }
346
365
  };
347
366
  app.provide(solanaInjectionKey, vueContext);
348
367
  plugin.context = vueContext;
368
+ teardown = () => {
369
+ disposed = true;
370
+ if (typeof window !== "undefined") {
371
+ window.clearTimeout(bootstrapId);
372
+ }
373
+ unsubscribeWallets?.();
374
+ unsubscribeWallets = null;
375
+ walletRegistry.disposeAllAdaptedWallets();
376
+ rpcCheckId += 1;
377
+ teardown = null;
378
+ };
349
379
  if (typeof window !== "undefined") {
350
- window.setTimeout(() => {
380
+ bootstrapId = window.setTimeout(() => {
351
381
  try {
352
382
  refreshWallets();
353
383
  } catch (cause) {
354
- console.error("[Vue Solana] Wallet refresh failed", cause);
355
384
  }
356
385
  void checkConnection();
357
386
  }, 0);
358
387
  }
388
+ },
389
+ dispose() {
390
+ teardown?.();
359
391
  }
360
392
  };
361
393
  return plugin;
@@ -3,13 +3,13 @@ import { withSolanaTimeout } from '@vue-solana/core/timeout';
3
3
  import { signAndSendTransaction, confirmTransactionSignature } from '@vue-solana/core/transaction';
4
4
  import { createNoWalletSelectedError } from '@vue-solana/core/wallet';
5
5
  import { ref, computed } from 'vue';
6
- import { u as useConnection } from './vue.CpkwC8Th.mjs';
7
- import { u as useWallet } from './vue.D218Ow7t.mjs';
8
- import { g as getConfirmedTransactionStatus } from './vue.CaAgH2Xt.mjs';
6
+ import { u as useSolanaClient } from './vue.CZApWQAY.mjs';
7
+ import { u as useWallet } from './vue.DY9VadLH.mjs';
8
+ import { g as getConfirmedTransactionStatus } from './vue.DPwdqRO_.mjs';
9
9
 
10
10
  const SIGN_AND_SEND_TIMEOUT_MS = 12e4;
11
11
  function useSignAndSendTransaction() {
12
- const client = useConnection();
12
+ const client = useSolanaClient().client;
13
13
  const { wallet } = useWallet();
14
14
  const signature = ref(null);
15
15
  const confirmation = ref(null);
@@ -1,7 +1,7 @@
1
1
  import { createNoWalletSelectedError, assertWalletCanSignIn } from '@vue-solana/core/wallet';
2
2
  import { shallowRef } from 'vue';
3
- import { u as useExecution } from './vue.DlihC5GG.mjs';
4
- import { u as useWallet } from './vue.D218Ow7t.mjs';
3
+ import { u as useExecution } from './vue.BiKE9Xuv.mjs';
4
+ import { u as useWallet } from './vue.DY9VadLH.mjs';
5
5
 
6
6
  function useSignIn() {
7
7
  const { wallet } = useWallet();
@@ -1,4 +1,4 @@
1
- import { u as useClientAction } from './vue.CGv0ip3w.mjs';
1
+ import { u as useClientAction } from './vue.CbmzveKF.mjs';
2
2
 
3
3
  const PLANNING_PROVIDER_HINT = "Install a planner plugin, e.g. `createClient().use(rpcTransactionPlanner())` from `@solana/kit-plugin-rpc`, and plan with a client that has a `payer` signer \u2014 Kit reads `client.payer` to set the fee payer.";
4
4
  const PLAN_STATUSES = {
@@ -1,8 +1,10 @@
1
1
  import { normalizeSolanaError } from '@vue-solana/core/errors';
2
2
  import { shallowRef, onScopeDispose, computed } from 'vue';
3
3
 
4
- function useExecution(busy) {
5
- const status = shallowRef("idle");
4
+ function useExecution(busy, statuses = {}) {
5
+ const errorStatus = statuses.error ?? "error";
6
+ const idleStatus = statuses.idle ?? "idle";
7
+ const status = shallowRef(idleStatus);
6
8
  const error = shallowRef(null);
7
9
  let id = 0;
8
10
  async function execute(run, settled) {
@@ -19,14 +21,14 @@ function useExecution(busy) {
19
21
  const normalizedError = normalizeSolanaError(cause, "RPC_FAILURE");
20
22
  if (currentId === id) {
21
23
  error.value = normalizedError;
22
- status.value = "error";
24
+ status.value = errorStatus;
23
25
  }
24
26
  throw normalizedError;
25
27
  }
26
28
  }
27
29
  function reset() {
28
30
  id += 1;
29
- status.value = "idle";
31
+ status.value = idleStatus;
30
32
  error.value = null;
31
33
  }
32
34
  onScopeDispose(() => {
@@ -1,19 +1,36 @@
1
- import { normalizeSolanaError, createSolanaError } from '@vue-solana/core/errors';
2
- import bs58 from 'bs58';
3
- import { shallowRef, onMounted, onUnmounted, watch, toValue } from 'vue';
4
- import { u as useConnection } from './vue.CpkwC8Th.mjs';
5
- import { t as tryUseSolana } from './vue.CQh3AUE8.mjs';
1
+ import { getBase58Encoder } from '@vue-solana/core/kit';
2
+ import { createSolanaError, normalizeSolanaError } from '@vue-solana/core/errors';
3
+ import { shallowRef, onMounted, onScopeDispose, watch, toValue } from 'vue';
4
+ import { u as useSolanaClient } from './vue.CZApWQAY.mjs';
5
+ import { t as tryUseSolana } from './vue.CcOZ9858.mjs';
6
6
 
7
7
  function useSignatureStatus(signature, options = {}) {
8
8
  const solana = tryUseSolana();
9
- const client = solana?.client ?? useConnection();
9
+ const client = solana?.client ?? useSolanaClient().client;
10
10
  const status = shallowRef(null);
11
11
  const loading = shallowRef(false);
12
12
  const error = shallowRef(null);
13
13
  let refreshId = 0;
14
- let pollId = null;
14
+ let pollTimer = null;
15
15
  let manuallyStoppedPolling = false;
16
+ let refreshInFlight = null;
16
17
  async function refresh() {
18
+ const value = toValue(signature);
19
+ const inFlight = refreshInFlight;
20
+ if (inFlight && inFlight.value === value) {
21
+ return inFlight.promise;
22
+ }
23
+ const promise = runRefresh();
24
+ refreshInFlight = { value, promise };
25
+ try {
26
+ return await promise;
27
+ } finally {
28
+ if (refreshInFlight?.promise === promise) {
29
+ refreshInFlight = null;
30
+ }
31
+ }
32
+ }
33
+ async function runRefresh() {
17
34
  const requestId = ++refreshId;
18
35
  const value = toValue(signature);
19
36
  if (!value || !solana) {
@@ -53,16 +70,22 @@ function useSignatureStatus(signature, options = {}) {
53
70
  stopCurrentPolling();
54
71
  }
55
72
  function stopCurrentPolling() {
56
- if (pollId === null) {
73
+ if (pollTimer === null) {
57
74
  return;
58
75
  }
59
- clearInterval(pollId);
60
- pollId = null;
76
+ clearTimeout(pollTimer);
77
+ pollTimer = null;
61
78
  }
62
- function startPolling() {
79
+ function schedulePoll() {
63
80
  stopCurrentPolling();
81
+ if (!options.pollIntervalMs || !solana) {
82
+ return;
83
+ }
84
+ if (manuallyStoppedPolling) {
85
+ return;
86
+ }
64
87
  const value = toValue(signature);
65
- if (manuallyStoppedPolling || !options.pollIntervalMs || !value || !solana) {
88
+ if (!value) {
66
89
  return;
67
90
  }
68
91
  if (options.pollIntervalMs <= 0) {
@@ -75,15 +98,22 @@ function useSignatureStatus(signature, options = {}) {
75
98
  error.value = normalizeSolanaError(cause, "RPC_FAILURE");
76
99
  return;
77
100
  }
78
- pollId = setInterval(() => {
79
- void refresh().catch(() => void 0);
101
+ pollTimer = setTimeout(() => {
102
+ pollTimer = null;
103
+ void refresh().catch(() => void 0).finally(() => {
104
+ schedulePoll();
105
+ });
80
106
  }, options.pollIntervalMs);
81
107
  }
108
+ function startPolling() {
109
+ manuallyStoppedPolling = false;
110
+ schedulePoll();
111
+ }
82
112
  onMounted(() => {
83
113
  void refresh().catch(() => void 0);
84
- startPolling();
114
+ schedulePoll();
85
115
  });
86
- onUnmounted(() => {
116
+ onScopeDispose(() => {
87
117
  refreshId += 1;
88
118
  stopPolling();
89
119
  });
@@ -105,7 +135,7 @@ function useSignatureStatus(signature, options = {}) {
105
135
  function parseTransactionSignature(signature) {
106
136
  let decodedSignature;
107
137
  try {
108
- decodedSignature = bs58.decode(signature);
138
+ decodedSignature = getBase58Encoder().encode(signature);
109
139
  } catch (cause) {
110
140
  throw new TypeError("Invalid Solana transaction signature", { cause });
111
141
  }
@@ -1,4 +1,4 @@
1
- import { u as useSolanaClient } from './vue.Mxc8w6Qk.mjs';
1
+ import { u as useSolanaClient } from './vue.CZApWQAY.mjs';
2
2
 
3
3
  function useConnection() {
4
4
  return useSolanaClient().client;
@@ -1,6 +1,7 @@
1
- import { u as useSolanaClient } from './vue.Mxc8w6Qk.mjs';
1
+ import { SolanaError } from '@vue-solana/core/errors';
2
+ import { u as useSolanaClient } from './vue.CZApWQAY.mjs';
2
3
 
3
- class MissingClientCapabilityError extends Error {
4
+ class MissingClientCapabilityError extends SolanaError {
4
5
  /** Name of the hook that requested the capability, e.g. `usePayer`. */
5
6
  hookName;
6
7
  /** Human guidance for installing the capability on the client. */
@@ -9,7 +10,9 @@ class MissingClientCapabilityError extends Error {
9
10
  capabilities;
10
11
  constructor(hookName, capabilities, providerHint) {
11
12
  super(
12
- `${hookName} requires the ${capabilities.map((capability) => `\`${capability}\``).join(" and ")} capabilit${capabilities.length === 1 ? "y" : "ies"} on the Solana client, but none ${capabilities.length === 1 ? "was" : "were"} found. ` + providerHint
13
+ "MISSING_CLIENT_CAPABILITY",
14
+ `${hookName} requires the ${capabilities.map((capability) => `\`${capability}\``).join(" and ")} capabilit${capabilities.length === 1 ? "y" : "ies"} on the Solana client, but none ${capabilities.length === 1 ? "was" : "were"} found. ` + providerHint,
15
+ {}
13
16
  );
14
17
  this.name = "MissingClientCapabilityError";
15
18
  this.hookName = hookName;
@@ -1,6 +1,7 @@
1
+ import { createSolanaError } from '@vue-solana/core/errors';
1
2
  import { u as useAction } from './vue.DYk0Xfr3.mjs';
2
- import { u as useClientCapability } from './vue.B9uRpSDJ.mjs';
3
- import { u as useSolanaClient } from './vue.Mxc8w6Qk.mjs';
3
+ import { u as useClientCapability, M as MissingClientCapabilityError } from './vue.CAltY57B.mjs';
4
+ import { u as useSolanaClient } from './vue.CZApWQAY.mjs';
4
5
 
5
6
  function toReadableAirdropError(error) {
6
7
  if (error instanceof Error) {
@@ -9,23 +10,22 @@ function toReadableAirdropError(error) {
9
10
  if (!isThrottled) {
10
11
  throw error;
11
12
  }
12
- throw new Error(
13
+ throw createSolanaError(
14
+ "RPC_FAILURE",
13
15
  `${error.message} This likely means the network's airdrop faucet is rate-limited or out of test SOL. Retry later, use https://faucet.solana.com, or point the client at a local validator.`,
14
16
  { cause: error }
15
17
  );
16
18
  }
17
- throw new Error(`Airdrop failed: ${String(error)}`);
19
+ throw createSolanaError("RPC_FAILURE", `Airdrop failed: ${String(error)}`, { cause: error });
18
20
  }
19
21
  function useAirdrop() {
20
- useClientCapability("airdrop", {
21
- hookName: "useAirdrop",
22
- providerHint: "Install it by adding the airdrop capability with `createClient().use(solanaRpc({ ... })).use(rpcAirdrop())` from `@solana/kit-plugin-rpc`, on a client that has a `payer` signer to sign the request."
23
- });
22
+ const providerHint = "Install it by adding the airdrop capability with `createClient().use(solanaRpc({ ... })).use(rpcAirdrop())` from `@solana/kit-plugin-rpc`, on a client that has a `payer` signer to sign the request.";
23
+ useClientCapability("airdrop", { hookName: "useAirdrop", providerHint });
24
24
  const { client } = useSolanaClient();
25
25
  return useAction((abortSignal, address, amount) => {
26
26
  const airdrop = client.airdrop;
27
27
  if (!airdrop) {
28
- throw new Error("useAirdrop requires the `airdrop` capability on the Solana client.");
28
+ throw new MissingClientCapabilityError("useAirdrop", ["airdrop"], providerHint);
29
29
  }
30
30
  return airdrop(address, amount, abortSignal).catch(toReadableAirdropError);
31
31
  });
@@ -1,4 +1,4 @@
1
- import { u as useClientAction } from './vue.CGv0ip3w.mjs';
1
+ import { u as useClientAction } from './vue.CbmzveKF.mjs';
2
2
 
3
3
  const SENDING_PROVIDER_HINT = "Use a client created by `createSolanaClient({ payer })` \u2014 or any client with a `payer` signer. Kit reads `client.payer` when it plans a transaction from an instruction plan.";
4
4
  const SEND_STATUSES = {
@@ -1,7 +1,7 @@
1
1
  import { createNoWalletSelectedError, assertWalletCanSignTransactions } from '@vue-solana/core/wallet';
2
2
  import { shallowRef } from 'vue';
3
- import { u as useExecution } from './vue.DlihC5GG.mjs';
4
- import { u as useWallet } from './vue.D218Ow7t.mjs';
3
+ import { u as useExecution } from './vue.BiKE9Xuv.mjs';
4
+ import { u as useWallet } from './vue.DY9VadLH.mjs';
5
5
 
6
6
  function useSignTransactions() {
7
7
  const { wallet } = useWallet();
@@ -1,4 +1,4 @@
1
- import { u as useSolana } from './vue.CQh3AUE8.mjs';
1
+ import { u as useSolana } from './vue.CcOZ9858.mjs';
2
2
 
3
3
  function useSolanaClient() {
4
4
  const { client } = useSolana();
@@ -0,0 +1,44 @@
1
+ import { shallowRef, onScopeDispose } from 'vue';
2
+ import { u as useExecution } from './vue.BiKE9Xuv.mjs';
3
+ import { u as useClientCapability, M as MissingClientCapabilityError } from './vue.CAltY57B.mjs';
4
+ import { u as useSolanaClient } from './vue.CZApWQAY.mjs';
5
+
6
+ function useClientAction(config) {
7
+ const { capability, hookName, providerHint, statuses } = config;
8
+ useClientCapability([capability, "payer"], { hookName, providerHint });
9
+ const { client } = useSolanaClient();
10
+ const data = shallowRef(null);
11
+ let abortController;
12
+ onScopeDispose(() => abortController?.abort());
13
+ const execution = useExecution(statuses.working, {
14
+ error: statuses.error,
15
+ idle: statuses.idle
16
+ });
17
+ async function execute(input, options = {}) {
18
+ const method = client?.[capability];
19
+ if (typeof method !== "function") {
20
+ throw new MissingClientCapabilityError(hookName, [capability], providerHint);
21
+ }
22
+ abortController?.abort();
23
+ const controller = new AbortController();
24
+ abortController = controller;
25
+ data.value = null;
26
+ const abortSignal = options.abortSignal ? AbortSignal.any([controller.signal, options.abortSignal]) : controller.signal;
27
+ return execution.execute(
28
+ () => method(input, { abortSignal }),
29
+ (result) => {
30
+ data.value = result;
31
+ return statuses.success;
32
+ }
33
+ );
34
+ }
35
+ return {
36
+ data,
37
+ status: execution.status,
38
+ loading: execution.loading,
39
+ error: execution.error,
40
+ execute
41
+ };
42
+ }
43
+
44
+ export { useClientAction as u };