@vue-solana/vue 2.2.0 → 2.3.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.
package/README.md CHANGED
@@ -116,6 +116,28 @@ import { useWallet } from "@vue-solana/vue/useWallet";
116
116
  import { useSignMessage } from "@vue-solana/vue/useSignMessage";
117
117
  ```
118
118
 
119
+ ### Client and Plugin Lifecycle
120
+
121
+ `createSolanaPlugin()` builds the Kit client once, during `install()`. Create the plugin at module scope and reuse the instance:
122
+
123
+ ```ts
124
+ // solana.ts
125
+ import { createSolanaPlugin } from "@vue-solana/vue";
126
+
127
+ export const solana = createSolanaPlugin({ cluster: "devnet" });
128
+ ```
129
+
130
+ Calling `createSolanaPlugin()` again builds a new client and context, discarding the existing wallet selection and RPC state. If your config is reactive — a cluster toggle, for example — memoize on the config so a new plugin (and client) is built only when the value actually changes, not on every render:
131
+
132
+ ```ts
133
+ import { computed, ref } from "vue";
134
+
135
+ const cluster = ref<SolanaCluster>("devnet");
136
+ const plugin = computed(() => createSolanaPlugin({ cluster: cluster.value }));
137
+ ```
138
+
139
+ A Kit client runs its `createClient().use(...)` plugins during construction. When one of those plugins is async, the client — and any context built from it — only activates after that promise resolves. Defer real RPC and wallet work to client lifecycle hooks or user actions after hydration rather than running it during setup or SSR.
140
+
119
141
  For development, use `devnet` and request free test SOL from the official faucet:
120
142
 
121
143
  ```txt
@@ -315,6 +337,106 @@ await confirmation.confirm(signature);
315
337
 
316
338
  `useSignAndSendTransaction()` also clears `loading` if a wallet adapter never returns a result. In that stale case, `error` is set and the chain status may be unknown, so check the connected wallet or an explorer before retrying.
317
339
 
340
+ ### Wallet Request Inputs and Returns
341
+
342
+ Wallet signing flows accept transaction input as raw `Uint8Array` wire bytes that conform to the Solana transaction schema. Build them with `@solana/kit` (or decode them from a base64/base58 RPC response); base64 strings, transaction objects, and instruction lists are not accepted here.
343
+
344
+ ```ts
345
+ import { compileTransaction, getTransactionEncoder } from "@solana/kit";
346
+
347
+ const transaction: Uint8Array = getTransactionEncoder().encode(compileTransaction(message));
348
+ await execute(transaction);
349
+ ```
350
+
351
+ `useSignMessage()` takes the raw message bytes to sign. Every wallet send request also accepts the Kit `SendTransactionOptions`:
352
+
353
+ | Option | Description |
354
+ | --------------------- | ------------------------------------------------------------------------------------------------------------- |
355
+ | `skipPreflight` | Skip preflight simulation before sending. |
356
+ | `maxRetries` | RPC node retry count (`bigint`). |
357
+ | `minContextSlot` | Slot at which any blockhash or nonce in the transaction is known to exist; sending before it can be rejected. |
358
+ | `preflightCommitment` | Commitment used for preflight simulation. |
359
+
360
+ Return shapes:
361
+
362
+ - `useSignMessage().execute(bytes)` resolves to `{ signedMessage, signature }`, both `Uint8Array`.
363
+ - `useSignTransactions().execute(transactions)` resolves to the signed `Uint8Array[]` (also exposed as `signedTransactions`); pass a single-element array for one transaction.
364
+ - `useSignAndSendTransaction().execute(transaction)` resolves to the submitted `signature` string; with `confirm: true` it also fills `confirmation`.
365
+ - `useSignAndSendTransactions().execute(transactions)` resolves to a `string[]` of signatures (also exposed as `signatures`).
366
+
367
+ A wallet may modify the message or transaction before signing — for example to add its own instruction or change the fee payer — and the Wallet Standard explicitly allows it. Re-read the returned `signedMessage` or signed transaction bytes instead of assuming they match your input byte-for-byte.
368
+
369
+ ### Sending with the Client (no wallet popup)
370
+
371
+ `useSendTransaction()` and `useSendTransactions()` send through the Kit client's transaction-sending capability (`ClientWithTransactionSending`) instead of the connected wallet. The client plans the transaction from your input, signs it with its own signers — typically the client identity or `payer` keypair, e.g. a relayer — submits it, and returns the result. There is no wallet extension and no approval popup.
372
+
373
+ **Which to use:**
374
+
375
+ | Flow | `useSendTransaction(s)` (client) | `useSignAndSendTransaction(s)` (wallet) |
376
+ | --------------------- | ------------------------------------------------------------ | ----------------------------------------- |
377
+ | Who authorizes | The client's fee payer / signer keypairs | The connected wallet (user approves) |
378
+ | Environment | Server-side, relayer, or automated flows (no browser wallet) | Browser dapps where the user must approve |
379
+ | Popup | None | Wallet approval popup / mobile handoff |
380
+ | Input | Instructions, instruction plans, transaction messages, plans | Raw transaction bytes (`Uint8Array`) |
381
+ | Multiple transactions | Yes, as a batch in one call | Yes, one or more wallet requests |
382
+
383
+ 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.
384
+
385
+ **Prerequisite.** Your Kit client must install a transaction planner and a transaction-sending executor, e.g. `rpcTransactionPlanner()` and `rpcTransactionPlanSendingExecutor()` from `@solana/kit-plugin-rpc`. The default client built by `createSolanaPlugin()` installs only RPC and airdrop plugins, so without a capable client both composables fail fast at setup with a clear capability error naming what to install. Plan first with `usePlanTransaction()` / `usePlanTransactions()` when you need separate planning and sending steps.
386
+
387
+ ```ts
388
+ import { useSendTransaction } from "@vue-solana/vue/useSendTransaction";
389
+ import { useSendTransactions } from "@vue-solana/vue/useSendTransactions";
390
+
391
+ const single = useSendTransaction();
392
+ const batch = useSendTransactions();
393
+ ```
394
+
395
+ `useSendTransaction().execute()` accepts flexible input and resolves to the successful transaction result:
396
+
397
+ ```ts
398
+ const { data, status, error, execute } = useSendTransaction();
399
+
400
+ // A raw list of instructions
401
+ await execute(instructions);
402
+
403
+ // A planned instruction plan or a single transaction plan
404
+ await execute(plan);
405
+
406
+ // A single transaction message
407
+ await execute(transactionMessage);
408
+
409
+ // data.context.signature is the submitted Signature
410
+ ```
411
+
412
+ `useSendTransactions().execute()` plans, signs, and sends one or more messages at once — parallel where possible, sequential where dependencies require it — and resolves to the full plan result tree:
413
+
414
+ ```ts
415
+ const { data, status, error, execute } = useSendTransactions();
416
+
417
+ // A batch of transaction messages
418
+ await execute([messageA, messageB]);
419
+
420
+ // A message, a plan, or a nested batch of messages/plans
421
+ await execute(nestedBatch);
422
+ ```
423
+
424
+ Both composables surface `status` (`idle`, `sending`, `sent`, `error`), `loading`, `error`, and `data`. Starting a new `execute()` while one is in flight aborts the previous call; the stale attempt rejects and its state is discarded. Pass an `{ abortSignal }` to additionally cancel from outside (unmounting the owning component also aborts in-flight work).
425
+
426
+ Because signing keypairs live on the client, reserve `useSendTransaction(s)` for trusted contexts (your relayer, automated flows). Do not register app-signing keypairs on a client exposed to end-user browsers, where a compromised page could spend funds.
427
+
428
+ Superseded or aborted attempts reject with a wrapped error. To tell "superseded or cancelled" apart from a real failure, inspect the rejection's `cause`:
429
+
430
+ ```ts
431
+ try {
432
+ await execute(instructions);
433
+ } catch (cause) {
434
+ if (cause?.cause instanceof DOMException && cause.cause.name === "AbortError") {
435
+ // superseded by a newer call or cancelled via abortSignal / unmount
436
+ }
437
+ }
438
+ ```
439
+
318
440
  ### Live Data
319
441
 
320
442
  `useRequest()` fetches once per change and revalidates stale data in the background. Pass a request function, a pending Kit RPC request, or a ref/computed of either:
@@ -494,6 +616,8 @@ Docs: [Vue Solana Agent Skill](https://vue-solana-docs.vercel.app/agent-skill)
494
616
  | `useIdentity()` | Reactive Kit client `identity` signer ref (requires a signer plugin on the client). |
495
617
  | `usePlanTransaction()` | Plans a single transaction message from instruction inputs without signing or sending. |
496
618
  | `usePlanTransactions()` | Plans a batch of transaction messages from instruction inputs. |
619
+ | `useSendTransaction()` | Plans, signs, submits, and confirms one transaction through the client's transaction-sending capability (no wallet). |
620
+ | `useSendTransactions()` | Sends a batch of transactions (parallel or sequential) through the client's transaction-sending capability. |
497
621
 
498
622
  Direct composable subpaths:
499
623
 
@@ -527,6 +651,8 @@ Direct composable subpaths:
527
651
  - `@vue-solana/vue/useIdentity`
528
652
  - `@vue-solana/vue/usePlanTransaction`
529
653
  - `@vue-solana/vue/usePlanTransactions`
654
+ - `@vue-solana/vue/useSendTransaction`
655
+ - `@vue-solana/vue/useSendTransactions`
530
656
 
531
657
  Other direct subpaths:
532
658
 
package/dist/index.cjs CHANGED
@@ -6,12 +6,13 @@ const useAirdrop = require('./shared/vue.CTqPbvz3.cjs');
6
6
  const useBalance = require('./shared/vue.E_npPFoV.cjs');
7
7
  const useClientCapability = require('./shared/vue.BY0qpgYr.cjs');
8
8
  const useConnection = require('./shared/vue.BVaMoz9y.cjs');
9
- const usePlanTransaction = require('./shared/vue.B4-LEdSE.cjs');
9
+ const usePlanTransaction = require('./shared/vue.BfGt94sg.cjs');
10
10
  const useProgramAccounts = require('./shared/vue.Di7yjJ-R.cjs');
11
11
  const usePayer = require('./shared/vue.g8fnm_ca.cjs');
12
12
  const useRequest = require('./shared/vue.ZuUCEEab.cjs');
13
13
  const useRpc = require('./shared/vue.FiMmnMHr.cjs');
14
14
  const SelectedWalletAccountProvider = require('./shared/vue.DDbVk0Zh.cjs');
15
+ const useSendTransaction = require('./shared/vue.BFbIDI66.cjs');
15
16
  const useSignMessage = require('./shared/vue.B6ainw2G.cjs');
16
17
  const useSignAndSendTransaction = require('./shared/vue.CWZSMVnB.cjs');
17
18
  const useSignTransactions = require('./shared/vue.DFaMJLAQ.cjs');
@@ -404,6 +405,8 @@ exports.createSelectedWalletAccountContext = SelectedWalletAccountProvider.creat
404
405
  exports.provideSelectedWalletAccount = SelectedWalletAccountProvider.provideSelectedWalletAccount;
405
406
  exports.selectedWalletAccountInjectionKey = SelectedWalletAccountProvider.selectedWalletAccountInjectionKey;
406
407
  exports.useSelectedWalletAccount = SelectedWalletAccountProvider.useSelectedWalletAccount;
408
+ exports.useSendTransaction = useSendTransaction.useSendTransaction;
409
+ exports.useSendTransactions = useSendTransaction.useSendTransactions;
407
410
  exports.useSignMessage = useSignMessage.useSignMessage;
408
411
  exports.useSignAndSendTransaction = useSignAndSendTransaction.useSignAndSendTransaction;
409
412
  exports.useSignTransactions = useSignTransactions.useSignTransactions;
package/dist/index.d.cts CHANGED
@@ -10,6 +10,7 @@ export { useIdentity, usePayer } from './usePayer.cjs';
10
10
  export { UseRequestOptions, UseRequestRefresherOptions, UseRequestReturn, UseRequestSource, UseRequestStatus, useRequest } from './useRequest.cjs';
11
11
  export { useRpc } from './useRpc.cjs';
12
12
  export { SelectedWalletAccount, SelectedWalletAccountContext, SelectedWalletAccountOptions, SelectedWalletAccountProvider, WalletAccountFilter, createSelectedWalletAccountContext, provideSelectedWalletAccount, selectedWalletAccountInjectionKey, useSelectedWalletAccount } from './useSelectedWalletAccount.cjs';
13
+ export { SendTransactionConfig, SendTransactionInput, SendTransactionStatus, SendTransactionsInput, useSendTransaction, useSendTransactions } from './useSendTransaction.cjs';
13
14
  export { SignMessageStatus, useSignMessage } from './useSignMessage.cjs';
14
15
  export { SignAndSendTransactionOptions, SignAndSendTransactionStatus, useSignAndSendTransaction } from './useSignAndSendTransaction.cjs';
15
16
  export { SignTransactionsStatus, useSignTransactions } from './useSignTransactions.cjs';
package/dist/index.d.mts CHANGED
@@ -10,6 +10,7 @@ export { useIdentity, usePayer } from './usePayer.mjs';
10
10
  export { UseRequestOptions, UseRequestRefresherOptions, UseRequestReturn, UseRequestSource, UseRequestStatus, useRequest } from './useRequest.mjs';
11
11
  export { useRpc } from './useRpc.mjs';
12
12
  export { SelectedWalletAccount, SelectedWalletAccountContext, SelectedWalletAccountOptions, SelectedWalletAccountProvider, WalletAccountFilter, createSelectedWalletAccountContext, provideSelectedWalletAccount, selectedWalletAccountInjectionKey, useSelectedWalletAccount } from './useSelectedWalletAccount.mjs';
13
+ export { SendTransactionConfig, SendTransactionInput, SendTransactionStatus, SendTransactionsInput, useSendTransaction, useSendTransactions } from './useSendTransaction.mjs';
13
14
  export { SignMessageStatus, useSignMessage } from './useSignMessage.mjs';
14
15
  export { SignAndSendTransactionOptions, SignAndSendTransactionStatus, useSignAndSendTransaction } from './useSignAndSendTransaction.mjs';
15
16
  export { SignTransactionsStatus, useSignTransactions } from './useSignTransactions.mjs';
package/dist/index.d.ts CHANGED
@@ -10,6 +10,7 @@ export { useIdentity, usePayer } from './usePayer.js';
10
10
  export { UseRequestOptions, UseRequestRefresherOptions, UseRequestReturn, UseRequestSource, UseRequestStatus, useRequest } from './useRequest.js';
11
11
  export { useRpc } from './useRpc.js';
12
12
  export { SelectedWalletAccount, SelectedWalletAccountContext, SelectedWalletAccountOptions, SelectedWalletAccountProvider, WalletAccountFilter, createSelectedWalletAccountContext, provideSelectedWalletAccount, selectedWalletAccountInjectionKey, useSelectedWalletAccount } from './useSelectedWalletAccount.js';
13
+ export { SendTransactionConfig, SendTransactionInput, SendTransactionStatus, SendTransactionsInput, useSendTransaction, useSendTransactions } from './useSendTransaction.js';
13
14
  export { SignMessageStatus, useSignMessage } from './useSignMessage.js';
14
15
  export { SignAndSendTransactionOptions, SignAndSendTransactionStatus, useSignAndSendTransaction } from './useSignAndSendTransaction.js';
15
16
  export { SignTransactionsStatus, useSignTransactions } from './useSignTransactions.js';
package/dist/index.mjs CHANGED
@@ -4,12 +4,13 @@ export { u as useAirdrop } from './shared/vue.BSfiHqwj.mjs';
4
4
  export { u as useBalance } from './shared/vue.BINkrA8h.mjs';
5
5
  export { M as MissingClientCapabilityError, u as useClientCapability } from './shared/vue.B8LptjZP.mjs';
6
6
  export { u as useConnection } from './shared/vue.CpkwC8Th.mjs';
7
- export { u as usePlanTransaction, a as usePlanTransactions } from './shared/vue.C5FVoEZT.mjs';
7
+ export { u as usePlanTransaction, a as usePlanTransactions } from './shared/vue.C4hGlFC8.mjs';
8
8
  export { u as useProgramAccounts } from './shared/vue.C13umiQi.mjs';
9
9
  export { u as useIdentity, a as usePayer } from './shared/vue.CATOh_9G.mjs';
10
10
  export { u as useRequest } from './shared/vue.B8VvC8d2.mjs';
11
11
  export { u as useRpc } from './shared/vue.BsGwt78R.mjs';
12
12
  export { S as SelectedWalletAccountProvider, c as createSelectedWalletAccountContext, p as provideSelectedWalletAccount, s as selectedWalletAccountInjectionKey, u as useSelectedWalletAccount } from './shared/vue.DXQVIRZA.mjs';
13
+ export { u as useSendTransaction, a as useSendTransactions } from './shared/vue.DT4vCUa6.mjs';
13
14
  export { u as useSignMessage } from './shared/vue.JUWqu6kD.mjs';
14
15
  export { u as useSignAndSendTransaction } from './shared/vue.Dyi1OCI1.mjs';
15
16
  export { u as useSignTransactions } from './shared/vue.CMohL_Cz.mjs';
@@ -0,0 +1,121 @@
1
+ 'use strict';
2
+
3
+ const errors = require('@vue-solana/core/errors');
4
+ const vue = require('vue');
5
+ const useClientCapability = require('./vue.BY0qpgYr.cjs');
6
+ const useSolanaClient = require('./vue.BIxphCAq.cjs');
7
+
8
+ const SENDING_PROVIDER_HINT = "Install a transaction planner and a transaction-sending executor plugin, e.g. `createClient().use(rpcTransactionPlanner()).use(rpcTransactionPlanSendingExecutor())` from `@solana/kit-plugin-rpc`.";
9
+ function useSendTransaction() {
10
+ useClientCapability.useClientCapability(["sendTransaction"], {
11
+ hookName: "useSendTransaction",
12
+ providerHint: SENDING_PROVIDER_HINT
13
+ });
14
+ const { client } = useSolanaClient.useSolanaClient();
15
+ const data = vue.shallowRef(null);
16
+ const status = vue.ref("idle");
17
+ const loading = vue.ref(false);
18
+ const error = vue.ref(null);
19
+ let executionId = 0;
20
+ let abortController;
21
+ vue.onScopeDispose(() => {
22
+ abortController?.abort();
23
+ executionId++;
24
+ });
25
+ async function execute(input, config) {
26
+ abortController?.abort();
27
+ const controller = new AbortController();
28
+ abortController = controller;
29
+ const currentExecutionId = ++executionId;
30
+ const sender = client;
31
+ status.value = "sending";
32
+ loading.value = true;
33
+ error.value = null;
34
+ data.value = null;
35
+ try {
36
+ const abortSignal = config?.abortSignal ? AbortSignal.any([controller.signal, config.abortSignal]) : controller.signal;
37
+ const result = await sender.sendTransaction(input, { abortSignal });
38
+ if (currentExecutionId === executionId) {
39
+ data.value = result;
40
+ status.value = "sent";
41
+ }
42
+ return result;
43
+ } catch (cause) {
44
+ const normalizedError = errors.normalizeSolanaError(cause, "RPC_FAILURE");
45
+ if (currentExecutionId === executionId) {
46
+ error.value = normalizedError;
47
+ status.value = "error";
48
+ }
49
+ throw normalizedError;
50
+ } finally {
51
+ if (currentExecutionId === executionId) {
52
+ loading.value = false;
53
+ }
54
+ }
55
+ }
56
+ return {
57
+ data,
58
+ status,
59
+ loading,
60
+ error,
61
+ execute
62
+ };
63
+ }
64
+ function useSendTransactions() {
65
+ useClientCapability.useClientCapability(["sendTransactions"], {
66
+ hookName: "useSendTransactions",
67
+ providerHint: SENDING_PROVIDER_HINT
68
+ });
69
+ const { client } = useSolanaClient.useSolanaClient();
70
+ const data = vue.shallowRef(null);
71
+ const status = vue.ref("idle");
72
+ const loading = vue.ref(false);
73
+ const error = vue.ref(null);
74
+ let executionId = 0;
75
+ let abortController;
76
+ vue.onScopeDispose(() => {
77
+ abortController?.abort();
78
+ executionId++;
79
+ });
80
+ async function execute(input, config) {
81
+ abortController?.abort();
82
+ const controller = new AbortController();
83
+ abortController = controller;
84
+ const currentExecutionId = ++executionId;
85
+ const sender = client;
86
+ status.value = "sending";
87
+ loading.value = true;
88
+ error.value = null;
89
+ data.value = null;
90
+ try {
91
+ const abortSignal = config?.abortSignal ? AbortSignal.any([controller.signal, config.abortSignal]) : controller.signal;
92
+ const result = await sender.sendTransactions(input, { abortSignal });
93
+ if (currentExecutionId === executionId) {
94
+ data.value = result;
95
+ status.value = "sent";
96
+ }
97
+ return result;
98
+ } catch (cause) {
99
+ const normalizedError = errors.normalizeSolanaError(cause, "RPC_FAILURE");
100
+ if (currentExecutionId === executionId) {
101
+ error.value = normalizedError;
102
+ status.value = "error";
103
+ }
104
+ throw normalizedError;
105
+ } finally {
106
+ if (currentExecutionId === executionId) {
107
+ loading.value = false;
108
+ }
109
+ }
110
+ }
111
+ return {
112
+ data,
113
+ status,
114
+ loading,
115
+ error,
116
+ execute
117
+ };
118
+ }
119
+
120
+ exports.useSendTransaction = useSendTransaction;
121
+ exports.useSendTransactions = useSendTransactions;
@@ -16,7 +16,15 @@ function usePlanTransaction() {
16
16
  const loading = vue.ref(false);
17
17
  const error = vue.ref(null);
18
18
  let executionId = 0;
19
+ let abortController;
20
+ vue.onScopeDispose(() => {
21
+ abortController?.abort();
22
+ executionId++;
23
+ });
19
24
  async function execute(input, config) {
25
+ abortController?.abort();
26
+ const controller = new AbortController();
27
+ abortController = controller;
20
28
  const currentExecutionId = ++executionId;
21
29
  const planner = client;
22
30
  status.value = "planning";
@@ -24,7 +32,8 @@ function usePlanTransaction() {
24
32
  error.value = null;
25
33
  transactionMessage.value = null;
26
34
  try {
27
- const message = await planner.planTransaction(input, config);
35
+ const abortSignal = config?.abortSignal ? AbortSignal.any([controller.signal, config.abortSignal]) : controller.signal;
36
+ const message = await planner.planTransaction(input, { abortSignal });
28
37
  if (currentExecutionId === executionId) {
29
38
  transactionMessage.value = message;
30
39
  status.value = "planned";
@@ -62,7 +71,15 @@ function usePlanTransactions() {
62
71
  const loading = vue.ref(false);
63
72
  const error = vue.ref(null);
64
73
  let executionId = 0;
74
+ let abortController;
75
+ vue.onScopeDispose(() => {
76
+ abortController?.abort();
77
+ executionId++;
78
+ });
65
79
  async function execute(input, config) {
80
+ abortController?.abort();
81
+ const controller = new AbortController();
82
+ abortController = controller;
66
83
  const currentExecutionId = ++executionId;
67
84
  const planner = client;
68
85
  status.value = "planning";
@@ -70,7 +87,8 @@ function usePlanTransactions() {
70
87
  error.value = null;
71
88
  transactionPlan.value = null;
72
89
  try {
73
- const plan = await planner.planTransactions(input, config);
90
+ const abortSignal = config?.abortSignal ? AbortSignal.any([controller.signal, config.abortSignal]) : controller.signal;
91
+ const plan = await planner.planTransactions(input, { abortSignal });
74
92
  if (currentExecutionId === executionId) {
75
93
  transactionPlan.value = plan;
76
94
  status.value = "planned";
@@ -1,5 +1,5 @@
1
1
  import { normalizeSolanaError } from '@vue-solana/core/errors';
2
- import { shallowRef, ref } from 'vue';
2
+ import { shallowRef, ref, onScopeDispose } from 'vue';
3
3
  import { u as useClientCapability } from './vue.B8LptjZP.mjs';
4
4
  import { u as useSolanaClient } from './vue.Mxc8w6Qk.mjs';
5
5
 
@@ -14,7 +14,15 @@ function usePlanTransaction() {
14
14
  const loading = ref(false);
15
15
  const error = ref(null);
16
16
  let executionId = 0;
17
+ let abortController;
18
+ onScopeDispose(() => {
19
+ abortController?.abort();
20
+ executionId++;
21
+ });
17
22
  async function execute(input, config) {
23
+ abortController?.abort();
24
+ const controller = new AbortController();
25
+ abortController = controller;
18
26
  const currentExecutionId = ++executionId;
19
27
  const planner = client;
20
28
  status.value = "planning";
@@ -22,7 +30,8 @@ function usePlanTransaction() {
22
30
  error.value = null;
23
31
  transactionMessage.value = null;
24
32
  try {
25
- const message = await planner.planTransaction(input, config);
33
+ const abortSignal = config?.abortSignal ? AbortSignal.any([controller.signal, config.abortSignal]) : controller.signal;
34
+ const message = await planner.planTransaction(input, { abortSignal });
26
35
  if (currentExecutionId === executionId) {
27
36
  transactionMessage.value = message;
28
37
  status.value = "planned";
@@ -60,7 +69,15 @@ function usePlanTransactions() {
60
69
  const loading = ref(false);
61
70
  const error = ref(null);
62
71
  let executionId = 0;
72
+ let abortController;
73
+ onScopeDispose(() => {
74
+ abortController?.abort();
75
+ executionId++;
76
+ });
63
77
  async function execute(input, config) {
78
+ abortController?.abort();
79
+ const controller = new AbortController();
80
+ abortController = controller;
64
81
  const currentExecutionId = ++executionId;
65
82
  const planner = client;
66
83
  status.value = "planning";
@@ -68,7 +85,8 @@ function usePlanTransactions() {
68
85
  error.value = null;
69
86
  transactionPlan.value = null;
70
87
  try {
71
- const plan = await planner.planTransactions(input, config);
88
+ const abortSignal = config?.abortSignal ? AbortSignal.any([controller.signal, config.abortSignal]) : controller.signal;
89
+ const plan = await planner.planTransactions(input, { abortSignal });
72
90
  if (currentExecutionId === executionId) {
73
91
  transactionPlan.value = plan;
74
92
  status.value = "planned";
@@ -0,0 +1,118 @@
1
+ import { normalizeSolanaError } from '@vue-solana/core/errors';
2
+ import { shallowRef, ref, onScopeDispose } from 'vue';
3
+ import { u as useClientCapability } from './vue.B8LptjZP.mjs';
4
+ import { u as useSolanaClient } from './vue.Mxc8w6Qk.mjs';
5
+
6
+ const SENDING_PROVIDER_HINT = "Install a transaction planner and a transaction-sending executor plugin, e.g. `createClient().use(rpcTransactionPlanner()).use(rpcTransactionPlanSendingExecutor())` from `@solana/kit-plugin-rpc`.";
7
+ function useSendTransaction() {
8
+ useClientCapability(["sendTransaction"], {
9
+ hookName: "useSendTransaction",
10
+ providerHint: SENDING_PROVIDER_HINT
11
+ });
12
+ const { client } = useSolanaClient();
13
+ const data = shallowRef(null);
14
+ const status = ref("idle");
15
+ const loading = ref(false);
16
+ const error = ref(null);
17
+ let executionId = 0;
18
+ let abortController;
19
+ onScopeDispose(() => {
20
+ abortController?.abort();
21
+ executionId++;
22
+ });
23
+ async function execute(input, config) {
24
+ abortController?.abort();
25
+ const controller = new AbortController();
26
+ abortController = controller;
27
+ const currentExecutionId = ++executionId;
28
+ const sender = client;
29
+ status.value = "sending";
30
+ loading.value = true;
31
+ error.value = null;
32
+ data.value = null;
33
+ try {
34
+ const abortSignal = config?.abortSignal ? AbortSignal.any([controller.signal, config.abortSignal]) : controller.signal;
35
+ const result = await sender.sendTransaction(input, { abortSignal });
36
+ if (currentExecutionId === executionId) {
37
+ data.value = result;
38
+ status.value = "sent";
39
+ }
40
+ return result;
41
+ } catch (cause) {
42
+ const normalizedError = normalizeSolanaError(cause, "RPC_FAILURE");
43
+ if (currentExecutionId === executionId) {
44
+ error.value = normalizedError;
45
+ status.value = "error";
46
+ }
47
+ throw normalizedError;
48
+ } finally {
49
+ if (currentExecutionId === executionId) {
50
+ loading.value = false;
51
+ }
52
+ }
53
+ }
54
+ return {
55
+ data,
56
+ status,
57
+ loading,
58
+ error,
59
+ execute
60
+ };
61
+ }
62
+ function useSendTransactions() {
63
+ useClientCapability(["sendTransactions"], {
64
+ hookName: "useSendTransactions",
65
+ providerHint: SENDING_PROVIDER_HINT
66
+ });
67
+ const { client } = useSolanaClient();
68
+ const data = shallowRef(null);
69
+ const status = ref("idle");
70
+ const loading = ref(false);
71
+ const error = ref(null);
72
+ let executionId = 0;
73
+ let abortController;
74
+ onScopeDispose(() => {
75
+ abortController?.abort();
76
+ executionId++;
77
+ });
78
+ async function execute(input, config) {
79
+ abortController?.abort();
80
+ const controller = new AbortController();
81
+ abortController = controller;
82
+ const currentExecutionId = ++executionId;
83
+ const sender = client;
84
+ status.value = "sending";
85
+ loading.value = true;
86
+ error.value = null;
87
+ data.value = null;
88
+ try {
89
+ const abortSignal = config?.abortSignal ? AbortSignal.any([controller.signal, config.abortSignal]) : controller.signal;
90
+ const result = await sender.sendTransactions(input, { abortSignal });
91
+ if (currentExecutionId === executionId) {
92
+ data.value = result;
93
+ status.value = "sent";
94
+ }
95
+ return result;
96
+ } catch (cause) {
97
+ const normalizedError = normalizeSolanaError(cause, "RPC_FAILURE");
98
+ if (currentExecutionId === executionId) {
99
+ error.value = normalizedError;
100
+ status.value = "error";
101
+ }
102
+ throw normalizedError;
103
+ } finally {
104
+ if (currentExecutionId === executionId) {
105
+ loading.value = false;
106
+ }
107
+ }
108
+ }
109
+ return {
110
+ data,
111
+ status,
112
+ loading,
113
+ error,
114
+ execute
115
+ };
116
+ }
117
+
118
+ export { useSendTransactions as a, useSendTransaction as u };
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- const usePlanTransaction = require('./shared/vue.B4-LEdSE.cjs');
3
+ const usePlanTransaction = require('./shared/vue.BfGt94sg.cjs');
4
4
  require('@vue-solana/core/errors');
5
5
  require('vue');
6
6
  require('./shared/vue.BY0qpgYr.cjs');
@@ -12,6 +12,10 @@ interface PlanTransactionConfig {
12
12
  * (e.g. `rpcTransactionPlanner` from `@solana/kit-plugin-rpc`).
13
13
  *
14
14
  * Rejects with a clear capability error when the client does not plan.
15
+ *
16
+ * Calling `execute` while a prior execution is in flight aborts the prior call;
17
+ * the superseded attempt rejects, so check its `error.cause` to tell it apart
18
+ * from a real failure.
15
19
  */
16
20
  declare function usePlanTransaction(): {
17
21
  transactionMessage: vue.ShallowRef<TransactionMessage | null, TransactionMessage | null>;
@@ -12,6 +12,10 @@ interface PlanTransactionConfig {
12
12
  * (e.g. `rpcTransactionPlanner` from `@solana/kit-plugin-rpc`).
13
13
  *
14
14
  * Rejects with a clear capability error when the client does not plan.
15
+ *
16
+ * Calling `execute` while a prior execution is in flight aborts the prior call;
17
+ * the superseded attempt rejects, so check its `error.cause` to tell it apart
18
+ * from a real failure.
15
19
  */
16
20
  declare function usePlanTransaction(): {
17
21
  transactionMessage: vue.ShallowRef<TransactionMessage | null, TransactionMessage | null>;
@@ -12,6 +12,10 @@ interface PlanTransactionConfig {
12
12
  * (e.g. `rpcTransactionPlanner` from `@solana/kit-plugin-rpc`).
13
13
  *
14
14
  * Rejects with a clear capability error when the client does not plan.
15
+ *
16
+ * Calling `execute` while a prior execution is in flight aborts the prior call;
17
+ * the superseded attempt rejects, so check its `error.cause` to tell it apart
18
+ * from a real failure.
15
19
  */
16
20
  declare function usePlanTransaction(): {
17
21
  transactionMessage: vue.ShallowRef<TransactionMessage | null, TransactionMessage | null>;
@@ -1,4 +1,4 @@
1
- export { u as usePlanTransaction, a as usePlanTransactions } from './shared/vue.C5FVoEZT.mjs';
1
+ export { u as usePlanTransaction, a as usePlanTransactions } from './shared/vue.C4hGlFC8.mjs';
2
2
  import '@vue-solana/core/errors';
3
3
  import 'vue';
4
4
  import './shared/vue.B8LptjZP.mjs';
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- const usePlanTransaction = require('./shared/vue.B4-LEdSE.cjs');
3
+ const usePlanTransaction = require('./shared/vue.BfGt94sg.cjs');
4
4
  require('@vue-solana/core/errors');
5
5
  require('vue');
6
6
  require('./shared/vue.BY0qpgYr.cjs');
@@ -1,4 +1,4 @@
1
- export { a as usePlanTransactions } from './shared/vue.C5FVoEZT.mjs';
1
+ export { a as usePlanTransactions } from './shared/vue.C4hGlFC8.mjs';
2
2
  import '@vue-solana/core/errors';
3
3
  import 'vue';
4
4
  import './shared/vue.B8LptjZP.mjs';
@@ -0,0 +1,14 @@
1
+ 'use strict';
2
+
3
+ const useSendTransaction = require('./shared/vue.BFbIDI66.cjs');
4
+ require('@vue-solana/core/errors');
5
+ require('vue');
6
+ require('./shared/vue.BY0qpgYr.cjs');
7
+ require('./shared/vue.BIxphCAq.cjs');
8
+ require('./shared/vue.DE3emjSF.cjs');
9
+ require('./shared/vue.DEHxNvUX.cjs');
10
+
11
+
12
+
13
+ exports.useSendTransaction = useSendTransaction.useSendTransaction;
14
+ exports.useSendTransactions = useSendTransaction.useSendTransactions;
@@ -0,0 +1,46 @@
1
+ import * as vue from 'vue';
2
+ import { InstructionPlanInput, SingleTransactionPlan, TransactionPlanInput, SuccessfulSingleTransactionPlanResult, TransactionPlanResult } from '@vue-solana/core/kit';
3
+ import { SolanaError } from '@vue-solana/core/errors';
4
+
5
+ type SendTransactionStatus = "idle" | "sending" | "sent" | "error";
6
+ interface SendTransactionConfig {
7
+ abortSignal?: AbortSignal;
8
+ }
9
+ type SendTransactionInput = InstructionPlanInput | SingleTransactionPlan | SingleTransactionPlan["message"];
10
+ type SendTransactionsInput = InstructionPlanInput | TransactionPlanInput;
11
+ /**
12
+ * Plan, sign with the client's signers (payer/identity), submit, and confirm
13
+ * a single transaction — all through the client's transaction-sending
14
+ * capability (`ClientWithTransactionSending`), with no wallet popup.
15
+ *
16
+ * Accepts flexible input: instructions, an instruction plan, a transaction
17
+ * message, or a single transaction plan.
18
+ *
19
+ * Rejects with a clear capability error when the client cannot send.
20
+ *
21
+ * Calling `execute` while a prior execution is in flight aborts the prior call;
22
+ * the superseded attempt rejects, so check its `error.cause` to tell it apart
23
+ * from a real failure.
24
+ */
25
+ declare function useSendTransaction(): {
26
+ data: vue.ShallowRef<SuccessfulSingleTransactionPlanResult | null, SuccessfulSingleTransactionPlanResult | null>;
27
+ status: vue.Ref<SendTransactionStatus, SendTransactionStatus>;
28
+ loading: vue.Ref<boolean, boolean>;
29
+ error: vue.Ref<SolanaError | null, SolanaError | null>;
30
+ execute: (input: SendTransactionInput, config?: SendTransactionConfig) => Promise<SuccessfulSingleTransactionPlanResult>;
31
+ };
32
+ /**
33
+ * Plan, sign, submit, and confirm one or more transactions — possibly a batch
34
+ * of messages, executed in parallel or sequentially as the plan dictates —
35
+ * through the client's transaction-sending capability.
36
+ */
37
+ declare function useSendTransactions(): {
38
+ data: vue.ShallowRef<TransactionPlanResult | null, TransactionPlanResult | null>;
39
+ status: vue.Ref<SendTransactionStatus, SendTransactionStatus>;
40
+ loading: vue.Ref<boolean, boolean>;
41
+ error: vue.Ref<SolanaError | null, SolanaError | null>;
42
+ execute: (input: SendTransactionsInput, config?: SendTransactionConfig) => Promise<TransactionPlanResult>;
43
+ };
44
+
45
+ export { useSendTransaction, useSendTransactions };
46
+ export type { SendTransactionConfig, SendTransactionInput, SendTransactionStatus, SendTransactionsInput };
@@ -0,0 +1,46 @@
1
+ import * as vue from 'vue';
2
+ import { InstructionPlanInput, SingleTransactionPlan, TransactionPlanInput, SuccessfulSingleTransactionPlanResult, TransactionPlanResult } from '@vue-solana/core/kit';
3
+ import { SolanaError } from '@vue-solana/core/errors';
4
+
5
+ type SendTransactionStatus = "idle" | "sending" | "sent" | "error";
6
+ interface SendTransactionConfig {
7
+ abortSignal?: AbortSignal;
8
+ }
9
+ type SendTransactionInput = InstructionPlanInput | SingleTransactionPlan | SingleTransactionPlan["message"];
10
+ type SendTransactionsInput = InstructionPlanInput | TransactionPlanInput;
11
+ /**
12
+ * Plan, sign with the client's signers (payer/identity), submit, and confirm
13
+ * a single transaction — all through the client's transaction-sending
14
+ * capability (`ClientWithTransactionSending`), with no wallet popup.
15
+ *
16
+ * Accepts flexible input: instructions, an instruction plan, a transaction
17
+ * message, or a single transaction plan.
18
+ *
19
+ * Rejects with a clear capability error when the client cannot send.
20
+ *
21
+ * Calling `execute` while a prior execution is in flight aborts the prior call;
22
+ * the superseded attempt rejects, so check its `error.cause` to tell it apart
23
+ * from a real failure.
24
+ */
25
+ declare function useSendTransaction(): {
26
+ data: vue.ShallowRef<SuccessfulSingleTransactionPlanResult | null, SuccessfulSingleTransactionPlanResult | null>;
27
+ status: vue.Ref<SendTransactionStatus, SendTransactionStatus>;
28
+ loading: vue.Ref<boolean, boolean>;
29
+ error: vue.Ref<SolanaError | null, SolanaError | null>;
30
+ execute: (input: SendTransactionInput, config?: SendTransactionConfig) => Promise<SuccessfulSingleTransactionPlanResult>;
31
+ };
32
+ /**
33
+ * Plan, sign, submit, and confirm one or more transactions — possibly a batch
34
+ * of messages, executed in parallel or sequentially as the plan dictates —
35
+ * through the client's transaction-sending capability.
36
+ */
37
+ declare function useSendTransactions(): {
38
+ data: vue.ShallowRef<TransactionPlanResult | null, TransactionPlanResult | null>;
39
+ status: vue.Ref<SendTransactionStatus, SendTransactionStatus>;
40
+ loading: vue.Ref<boolean, boolean>;
41
+ error: vue.Ref<SolanaError | null, SolanaError | null>;
42
+ execute: (input: SendTransactionsInput, config?: SendTransactionConfig) => Promise<TransactionPlanResult>;
43
+ };
44
+
45
+ export { useSendTransaction, useSendTransactions };
46
+ export type { SendTransactionConfig, SendTransactionInput, SendTransactionStatus, SendTransactionsInput };
@@ -0,0 +1,46 @@
1
+ import * as vue from 'vue';
2
+ import { InstructionPlanInput, SingleTransactionPlan, TransactionPlanInput, SuccessfulSingleTransactionPlanResult, TransactionPlanResult } from '@vue-solana/core/kit';
3
+ import { SolanaError } from '@vue-solana/core/errors';
4
+
5
+ type SendTransactionStatus = "idle" | "sending" | "sent" | "error";
6
+ interface SendTransactionConfig {
7
+ abortSignal?: AbortSignal;
8
+ }
9
+ type SendTransactionInput = InstructionPlanInput | SingleTransactionPlan | SingleTransactionPlan["message"];
10
+ type SendTransactionsInput = InstructionPlanInput | TransactionPlanInput;
11
+ /**
12
+ * Plan, sign with the client's signers (payer/identity), submit, and confirm
13
+ * a single transaction — all through the client's transaction-sending
14
+ * capability (`ClientWithTransactionSending`), with no wallet popup.
15
+ *
16
+ * Accepts flexible input: instructions, an instruction plan, a transaction
17
+ * message, or a single transaction plan.
18
+ *
19
+ * Rejects with a clear capability error when the client cannot send.
20
+ *
21
+ * Calling `execute` while a prior execution is in flight aborts the prior call;
22
+ * the superseded attempt rejects, so check its `error.cause` to tell it apart
23
+ * from a real failure.
24
+ */
25
+ declare function useSendTransaction(): {
26
+ data: vue.ShallowRef<SuccessfulSingleTransactionPlanResult | null, SuccessfulSingleTransactionPlanResult | null>;
27
+ status: vue.Ref<SendTransactionStatus, SendTransactionStatus>;
28
+ loading: vue.Ref<boolean, boolean>;
29
+ error: vue.Ref<SolanaError | null, SolanaError | null>;
30
+ execute: (input: SendTransactionInput, config?: SendTransactionConfig) => Promise<SuccessfulSingleTransactionPlanResult>;
31
+ };
32
+ /**
33
+ * Plan, sign, submit, and confirm one or more transactions — possibly a batch
34
+ * of messages, executed in parallel or sequentially as the plan dictates —
35
+ * through the client's transaction-sending capability.
36
+ */
37
+ declare function useSendTransactions(): {
38
+ data: vue.ShallowRef<TransactionPlanResult | null, TransactionPlanResult | null>;
39
+ status: vue.Ref<SendTransactionStatus, SendTransactionStatus>;
40
+ loading: vue.Ref<boolean, boolean>;
41
+ error: vue.Ref<SolanaError | null, SolanaError | null>;
42
+ execute: (input: SendTransactionsInput, config?: SendTransactionConfig) => Promise<TransactionPlanResult>;
43
+ };
44
+
45
+ export { useSendTransaction, useSendTransactions };
46
+ export type { SendTransactionConfig, SendTransactionInput, SendTransactionStatus, SendTransactionsInput };
@@ -0,0 +1,7 @@
1
+ export { u as useSendTransaction, a as useSendTransactions } from './shared/vue.DT4vCUa6.mjs';
2
+ import '@vue-solana/core/errors';
3
+ import 'vue';
4
+ import './shared/vue.B8LptjZP.mjs';
5
+ import './shared/vue.Mxc8w6Qk.mjs';
6
+ import './shared/vue.CQh3AUE8.mjs';
7
+ import './shared/vue.D_P5SqqD.mjs';
@@ -0,0 +1,13 @@
1
+ 'use strict';
2
+
3
+ const useSendTransaction = require('./shared/vue.BFbIDI66.cjs');
4
+ require('@vue-solana/core/errors');
5
+ require('vue');
6
+ require('./shared/vue.BY0qpgYr.cjs');
7
+ require('./shared/vue.BIxphCAq.cjs');
8
+ require('./shared/vue.DE3emjSF.cjs');
9
+ require('./shared/vue.DEHxNvUX.cjs');
10
+
11
+
12
+
13
+ exports.useSendTransactions = useSendTransaction.useSendTransactions;
@@ -0,0 +1,4 @@
1
+ export { useSendTransactions } from './useSendTransaction.cjs';
2
+ import 'vue';
3
+ import '@vue-solana/core/kit';
4
+ import '@vue-solana/core/errors';
@@ -0,0 +1,4 @@
1
+ export { useSendTransactions } from './useSendTransaction.mjs';
2
+ import 'vue';
3
+ import '@vue-solana/core/kit';
4
+ import '@vue-solana/core/errors';
@@ -0,0 +1,4 @@
1
+ export { useSendTransactions } from './useSendTransaction.js';
2
+ import 'vue';
3
+ import '@vue-solana/core/kit';
4
+ import '@vue-solana/core/errors';
@@ -0,0 +1,7 @@
1
+ export { a as useSendTransactions } from './shared/vue.DT4vCUa6.mjs';
2
+ import '@vue-solana/core/errors';
3
+ import 'vue';
4
+ import './shared/vue.B8LptjZP.mjs';
5
+ import './shared/vue.Mxc8w6Qk.mjs';
6
+ import './shared/vue.CQh3AUE8.mjs';
7
+ import './shared/vue.D_P5SqqD.mjs';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vue-solana/vue",
3
- "version": "2.2.0",
3
+ "version": "2.3.0",
4
4
  "description": "Vue plugin and composables for Solana applications.",
5
5
  "license": "MIT",
6
6
  "keywords": [
@@ -105,6 +105,16 @@
105
105
  "import": "./dist/usePlanTransactions.mjs",
106
106
  "require": "./dist/usePlanTransactions.cjs"
107
107
  },
108
+ "./useSendTransaction": {
109
+ "types": "./dist/useSendTransaction.d.ts",
110
+ "import": "./dist/useSendTransaction.mjs",
111
+ "require": "./dist/useSendTransaction.cjs"
112
+ },
113
+ "./useSendTransactions": {
114
+ "types": "./dist/useSendTransactions.d.ts",
115
+ "import": "./dist/useSendTransactions.mjs",
116
+ "require": "./dist/useSendTransactions.cjs"
117
+ },
108
118
  "./swr": {
109
119
  "types": "./dist/swr.d.ts",
110
120
  "import": "./dist/swr.mjs",
@@ -204,7 +214,7 @@
204
214
  },
205
215
  "dependencies": {
206
216
  "bs58": "^6.0.0",
207
- "@vue-solana/core": "2.2.0"
217
+ "@vue-solana/core": "2.3.0"
208
218
  },
209
219
  "peerDependencies": {
210
220
  "vue": "^3.5.0"