@use-stellar/vue 0.0.0-stage → 0.2.3

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/dist/index.js ADDED
@@ -0,0 +1,829 @@
1
+ 'use strict';
2
+
3
+ var vue = require('vue');
4
+ var core = require('use-stellar/core');
5
+
6
+ // src/plugin.ts
7
+
8
+ // src/types.ts
9
+ var NETWORK_PASSPHRASES = {
10
+ testnet: "Test SDF Network ; September 2015",
11
+ mainnet: "Public Global Stellar Network ; September 2015",
12
+ futurenet: "Test SDF Future Network ; October 2022"
13
+ };
14
+ var NETWORK_CONFIGS = {
15
+ testnet: {
16
+ network: "testnet",
17
+ horizonUrl: "https://horizon-testnet.stellar.org",
18
+ sorobanUrl: "https://soroban-testnet.stellar.org",
19
+ networkPassphrase: NETWORK_PASSPHRASES.testnet
20
+ },
21
+ mainnet: {
22
+ network: "mainnet",
23
+ horizonUrl: "https://horizon.stellar.org",
24
+ sorobanUrl: "https://soroban.stellar.org",
25
+ networkPassphrase: NETWORK_PASSPHRASES.mainnet
26
+ },
27
+ futurenet: {
28
+ network: "futurenet",
29
+ horizonUrl: "https://horizon-futurenet.stellar.org",
30
+ sorobanUrl: "https://rpc-futurenet.stellar.org",
31
+ networkPassphrase: NETWORK_PASSPHRASES.futurenet
32
+ }
33
+ };
34
+
35
+ // src/plugin.ts
36
+ var STELLAR_RUNTIME_KEY = /* @__PURE__ */ Symbol("use-stellar/vue:runtime");
37
+ var DISCONNECTED_WALLET = {
38
+ connected: false,
39
+ connecting: false,
40
+ address: null,
41
+ network: null,
42
+ wallet: null,
43
+ walletName: null,
44
+ error: null,
45
+ walletNetwork: null,
46
+ walletNetworkPassphrase: null
47
+ };
48
+ function resolveNetworkConfig(network, override) {
49
+ const builtIn = NETWORK_CONFIGS[network];
50
+ if (!override) return builtIn;
51
+ return {
52
+ network,
53
+ horizonUrl: override.horizonUrl ?? builtIn.horizonUrl,
54
+ sorobanUrl: override.sorobanUrl ?? builtIn.sorobanUrl,
55
+ networkPassphrase: override.networkPassphrase ?? builtIn.networkPassphrase
56
+ };
57
+ }
58
+ function createStellarRuntime(options = {}) {
59
+ const network = options.network ?? "testnet";
60
+ const walletAddress = vue.shallowRef(options.walletAddress ?? null);
61
+ const runtime = {
62
+ network,
63
+ networkConfig: resolveNetworkConfig(network, options.networkConfig),
64
+ queryStore: new core.QueryStore(options.queryConfig),
65
+ wallet: { ...DISCONNECTED_WALLET, address: options.walletAddress ?? null },
66
+ setWallet(wallet) {
67
+ runtime.wallet = wallet;
68
+ walletAddress.value = wallet.address;
69
+ },
70
+ walletAddress,
71
+ setWalletAddress(address) {
72
+ walletAddress.value = address;
73
+ }
74
+ };
75
+ return runtime;
76
+ }
77
+ function createStellarPlugin(options = {}) {
78
+ return {
79
+ install(app) {
80
+ app.provide(STELLAR_RUNTIME_KEY, createStellarRuntime(options));
81
+ }
82
+ };
83
+ }
84
+ function useStellarRuntime() {
85
+ const runtime = vue.inject(STELLAR_RUNTIME_KEY);
86
+ if (!runtime) {
87
+ throw new Error(
88
+ "@use-stellar/vue: no plugin installed. Call app.use(createStellarPlugin()) before using any composable."
89
+ );
90
+ }
91
+ return runtime;
92
+ }
93
+ function useQuery(options) {
94
+ const data = vue.shallowRef(null);
95
+ const loading = vue.ref(false);
96
+ const error = vue.shallowRef(null);
97
+ const updatedAt = vue.ref(null);
98
+ let observer = null;
99
+ let unsubscribe = null;
100
+ const resolved = vue.computed(() => ({
101
+ queryKey: options.queryKey(),
102
+ queryFn: options.queryFn,
103
+ store: options.store,
104
+ enabled: options.enabled?.() ?? true,
105
+ staleTime: vue.toValue(options.staleTime),
106
+ maxRetries: vue.toValue(options.maxRetries)
107
+ }));
108
+ function project(snapshot) {
109
+ data.value = snapshot.data;
110
+ loading.value = snapshot.loading;
111
+ error.value = snapshot.error;
112
+ updatedAt.value = snapshot.updatedAt;
113
+ }
114
+ function start() {
115
+ if (observer) return;
116
+ observer = core.createQueryObserver(resolved.value);
117
+ unsubscribe = observer.subscribe(project);
118
+ project(observer.getSnapshot());
119
+ }
120
+ vue.watch(
121
+ () => [
122
+ core.serializeKey(resolved.value.queryKey),
123
+ resolved.value.enabled,
124
+ resolved.value.staleTime,
125
+ resolved.value.maxRetries
126
+ ],
127
+ () => {
128
+ observer?.setOptions(resolved.value);
129
+ if (observer) project(observer.getSnapshot());
130
+ }
131
+ // Batched (the default "pre" flush), not "sync": inputs that change in
132
+ // the same tick — a contract id and a method, say — must produce one
133
+ // query for the final combination, not an extra request for every
134
+ // half-updated one in between.
135
+ );
136
+ if (vue.getCurrentInstance()) {
137
+ if (typeof globalThis.window === "undefined" && options.ssr) start();
138
+ else vue.onMounted(start);
139
+ } else {
140
+ start();
141
+ }
142
+ vue.onScopeDispose(() => {
143
+ unsubscribe?.();
144
+ observer?.destroy();
145
+ unsubscribe = null;
146
+ observer = null;
147
+ });
148
+ return {
149
+ data,
150
+ loading,
151
+ error,
152
+ updatedAt,
153
+ rateLimitedUntil: vue.computed(() => observer?.getRateLimitedUntil() ?? null),
154
+ refetch: () => observer?.fetch({ force: true }) ?? Promise.resolve()
155
+ };
156
+ }
157
+
158
+ // src/useBalance.ts
159
+ function matchesAsset(balance, asset) {
160
+ if (asset === "XLM") return balance.asset === "XLM";
161
+ if (typeof asset !== "object") return balance.asset === asset;
162
+ return typeof balance.asset === "object" && balance.asset.code === asset.code && balance.asset.issuer === asset.issuer;
163
+ }
164
+ function useBalance(options) {
165
+ const runtime = useStellarRuntime();
166
+ const address = vue.computed(() => vue.toValue(options.address)?.trim() || null);
167
+ const asset = options.asset ?? "XLM";
168
+ const query = useQuery({
169
+ store: runtime.queryStore,
170
+ queryKey: () => core.accountKey(
171
+ runtime.networkConfig.horizonUrl,
172
+ runtime.networkConfig.network,
173
+ address.value ?? ""
174
+ ),
175
+ queryFn: () => core.fetchAccount(runtime.networkConfig, { address: address.value }),
176
+ enabled: () => address.value !== null,
177
+ staleTime: options.staleTime,
178
+ maxRetries: options.maxRetries
179
+ });
180
+ const balances = vue.computed(() => query.data.value?.balances ?? []);
181
+ return {
182
+ balance: vue.computed(
183
+ () => balances.value.find((item) => matchesAsset(item, asset))?.balance ?? null
184
+ ),
185
+ balances,
186
+ loading: vue.computed(() => query.loading.value),
187
+ error: vue.computed(() => query.error.value),
188
+ refetch: query.refetch
189
+ };
190
+ }
191
+ function unwrapArgs(value) {
192
+ if (!value) return [];
193
+ const raw = vue.toRaw(value);
194
+ return raw.map(
195
+ (argument) => argument !== null && typeof argument === "object" ? vue.toRaw(argument) : argument
196
+ );
197
+ }
198
+ function useSorobanContract(options) {
199
+ const { networkConfig, queryStore } = useStellarRuntime();
200
+ const call = vue.computed(() => {
201
+ const contractId = vue.toValue(options.contractId)?.trim() ?? "";
202
+ const method = vue.toValue(options.method)?.trim() ?? "";
203
+ if (!contractId || !method) return null;
204
+ const spec = options.spec === void 0 ? void 0 : vue.toValue(options.spec);
205
+ const sourceAccount = (options.sourceAccount === void 0 ? void 0 : vue.toValue(options.sourceAccount))?.trim() || core.ANONYMOUS_SIMULATION_SOURCE;
206
+ return {
207
+ contractId,
208
+ method,
209
+ args: unwrapArgs(options.args === void 0 ? [] : vue.toValue(options.args)),
210
+ spec: spec ? vue.toRaw(spec) : void 0,
211
+ sourceAccount
212
+ };
213
+ });
214
+ const query = useQuery({
215
+ store: queryStore,
216
+ queryKey: () => {
217
+ const current = call.value;
218
+ if (!current) return ["soroban", "disabled"];
219
+ return core.sorobanContractKey(
220
+ networkConfig.sorobanUrl,
221
+ networkConfig.network,
222
+ current.contractId,
223
+ current.method,
224
+ core.argsKey(current.args),
225
+ current.sourceAccount,
226
+ current.spec ? "spec" : "raw"
227
+ );
228
+ },
229
+ queryFn: () => core.simulateContractCall(networkConfig, call.value),
230
+ enabled: () => call.value !== null,
231
+ staleTime: options.staleTime
232
+ });
233
+ return {
234
+ data: vue.computed(() => query.data.value),
235
+ loading: vue.computed(() => query.loading.value),
236
+ error: vue.computed(
237
+ () => query.error.value ? core.toStellarError(query.error.value) : null
238
+ ),
239
+ refetch: () => {
240
+ void query.refetch();
241
+ }
242
+ };
243
+ }
244
+ function useContractEvents(options) {
245
+ const { networkConfig } = useStellarRuntime();
246
+ const events = vue.shallowRef([]);
247
+ const latestLedgerState = vue.ref(null);
248
+ const loadingState = vue.ref(false);
249
+ const errorState = vue.shallowRef(null);
250
+ let activePoller = null;
251
+ let activeUnsubscribe = null;
252
+ const input = vue.computed(() => {
253
+ const contractIds = [...vue.toValue(options.contractIds)];
254
+ const rawTopics = options.topics === void 0 ? void 0 : vue.toValue(options.topics);
255
+ const topics = rawTopics?.map((topic) => [...topic]);
256
+ return {
257
+ contractIds,
258
+ contractKey: JSON.stringify(contractIds),
259
+ topics,
260
+ topicKey: JSON.stringify(topics ?? []),
261
+ startLedger: options.startLedger === void 0 ? void 0 : vue.toValue(options.startLedger),
262
+ interval: (options.interval === void 0 ? void 0 : vue.toValue(options.interval)) ?? core.DEFAULT_POLL_INTERVAL,
263
+ bufferSize: (options.bufferSize === void 0 ? void 0 : vue.toValue(options.bufferSize)) ?? core.DEFAULT_BUFFER_SIZE,
264
+ enabled: (options.enabled === void 0 ? void 0 : vue.toValue(options.enabled)) ?? true
265
+ };
266
+ });
267
+ function stopActivePoller() {
268
+ activeUnsubscribe?.();
269
+ activeUnsubscribe = null;
270
+ activePoller?.stop();
271
+ activePoller = null;
272
+ }
273
+ vue.watch(
274
+ () => [
275
+ input.value.contractKey,
276
+ input.value.topicKey,
277
+ input.value.startLedger,
278
+ input.value.interval,
279
+ input.value.bufferSize,
280
+ input.value.enabled
281
+ ],
282
+ () => {
283
+ stopActivePoller();
284
+ const current = input.value;
285
+ if (!current.enabled || current.contractIds.length === 0) {
286
+ events.value = [];
287
+ latestLedgerState.value = null;
288
+ loadingState.value = false;
289
+ errorState.value = null;
290
+ return;
291
+ }
292
+ const poller = core.createContractEventPoller(
293
+ { sorobanUrl: networkConfig.sorobanUrl },
294
+ {
295
+ contractIds: current.contractIds,
296
+ topics: current.topics,
297
+ startLedger: current.startLedger,
298
+ interval: current.interval,
299
+ bufferSize: current.bufferSize
300
+ }
301
+ );
302
+ activePoller = poller;
303
+ activeUnsubscribe = poller.subscribe((snapshot) => {
304
+ events.value = snapshot.events;
305
+ latestLedgerState.value = snapshot.latestLedger;
306
+ loadingState.value = snapshot.loading;
307
+ errorState.value = snapshot.error;
308
+ });
309
+ poller.start();
310
+ },
311
+ { immediate: true }
312
+ );
313
+ vue.onScopeDispose(stopActivePoller);
314
+ const latestLedger = vue.computed(() => latestLedgerState.value);
315
+ const loading = vue.computed(() => loadingState.value);
316
+ const error = vue.computed(() => errorState.value);
317
+ function clear() {
318
+ activePoller?.clear();
319
+ }
320
+ return { events, latestLedger, loading, error, clear };
321
+ }
322
+ function useSendPayment() {
323
+ const runtime = useStellarRuntime();
324
+ const loading = vue.ref(false);
325
+ const error = vue.shallowRef(null);
326
+ const result = vue.shallowRef(null);
327
+ let operationId = 0;
328
+ let disposed = false;
329
+ const isCurrent = (id) => !disposed && id === operationId;
330
+ async function send(options) {
331
+ if (!core.onlineManager.isOnline()) throw core.offlineError();
332
+ const id = ++operationId;
333
+ const previousError = error.value;
334
+ const previousResult = result.value;
335
+ loading.value = true;
336
+ error.value = null;
337
+ result.value = null;
338
+ try {
339
+ const outcome = await core.sendPayment(
340
+ {
341
+ network: runtime.network,
342
+ networkConfig: runtime.networkConfig,
343
+ wallet: runtime.wallet,
344
+ queryStore: runtime.queryStore
345
+ },
346
+ options
347
+ );
348
+ if (isCurrent(id)) result.value = outcome;
349
+ return outcome;
350
+ } catch (caught) {
351
+ if (caught instanceof core.SendPaymentAbortedError) {
352
+ return { hash: "", status: "failed", error: caught.message };
353
+ }
354
+ if (core.isPreflightError(caught)) {
355
+ if (isCurrent(id)) {
356
+ error.value = previousError;
357
+ result.value = previousResult;
358
+ }
359
+ throw caught;
360
+ }
361
+ const stellarError = caught;
362
+ if (isCurrent(id)) {
363
+ if (stellarError.code === "TX_TIMEOUT") {
364
+ result.value = { hash: stellarError.hash ?? "", status: "pending" };
365
+ } else if (stellarError.isSubmissionFailure) {
366
+ result.value = { hash: stellarError.hash ?? "", status: "failed" };
367
+ }
368
+ error.value = stellarError;
369
+ }
370
+ throw caught;
371
+ } finally {
372
+ if (isCurrent(id)) loading.value = false;
373
+ }
374
+ }
375
+ function reset() {
376
+ operationId += 1;
377
+ loading.value = false;
378
+ error.value = null;
379
+ result.value = null;
380
+ }
381
+ vue.onScopeDispose(() => {
382
+ disposed = true;
383
+ operationId += 1;
384
+ });
385
+ return { send, loading, error, result, reset };
386
+ }
387
+ function useAddTrustline() {
388
+ const runtime = useStellarRuntime();
389
+ const loading = vue.ref(false);
390
+ const error = vue.shallowRef(null);
391
+ const result = vue.shallowRef(null);
392
+ let operationId = 0;
393
+ let disposed = false;
394
+ const isCurrent = (id) => !disposed && id === operationId;
395
+ async function addTrustline(options) {
396
+ if (!core.onlineManager.isOnline()) throw core.offlineError();
397
+ const id = ++operationId;
398
+ const previousError = error.value;
399
+ const previousResult = result.value;
400
+ loading.value = true;
401
+ error.value = null;
402
+ result.value = null;
403
+ try {
404
+ const outcome = await core.addTrustline(
405
+ {
406
+ network: runtime.network,
407
+ networkConfig: runtime.networkConfig,
408
+ wallet: runtime.wallet,
409
+ queryStore: runtime.queryStore
410
+ },
411
+ options
412
+ );
413
+ if (isCurrent(id)) result.value = outcome;
414
+ return outcome;
415
+ } catch (caught) {
416
+ if (caught instanceof core.AddTrustlineAbortedError) {
417
+ return { hash: "", status: "failed" };
418
+ }
419
+ if (core.isAddTrustlinePreflightError(caught)) {
420
+ if (isCurrent(id)) {
421
+ error.value = previousError;
422
+ result.value = previousResult;
423
+ }
424
+ throw caught;
425
+ }
426
+ const stellarError = caught;
427
+ if (isCurrent(id)) {
428
+ if (stellarError.code === "TX_TIMEOUT") {
429
+ result.value = { hash: stellarError.hash ?? "", status: "pending" };
430
+ } else if (stellarError.isSubmissionFailure) {
431
+ result.value = { hash: stellarError.hash ?? "", status: "failed" };
432
+ }
433
+ error.value = stellarError;
434
+ }
435
+ throw caught;
436
+ } finally {
437
+ if (isCurrent(id)) loading.value = false;
438
+ }
439
+ }
440
+ function reset() {
441
+ operationId += 1;
442
+ loading.value = false;
443
+ error.value = null;
444
+ result.value = null;
445
+ }
446
+ vue.onScopeDispose(() => {
447
+ disposed = true;
448
+ operationId += 1;
449
+ });
450
+ return { addTrustline, loading, error, result, reset };
451
+ }
452
+ function usePathPayment() {
453
+ const runtime = useStellarRuntime();
454
+ const loading = vue.ref(false);
455
+ const error = vue.shallowRef(null);
456
+ const result = vue.shallowRef(null);
457
+ let operationId = 0;
458
+ let disposed = false;
459
+ const isCurrent = (id) => !disposed && id === operationId;
460
+ async function pathPayment(options) {
461
+ if (!core.onlineManager.isOnline()) throw core.offlineError();
462
+ const id = ++operationId;
463
+ const previousError = error.value;
464
+ const previousResult = result.value;
465
+ loading.value = true;
466
+ error.value = null;
467
+ result.value = null;
468
+ try {
469
+ const outcome = await core.pathPayment(
470
+ {
471
+ network: runtime.network,
472
+ networkConfig: runtime.networkConfig,
473
+ wallet: runtime.wallet,
474
+ queryStore: runtime.queryStore
475
+ },
476
+ options
477
+ );
478
+ if (isCurrent(id)) result.value = outcome;
479
+ return outcome;
480
+ } catch (caught) {
481
+ if (core.isPathPaymentPreflightError(caught)) {
482
+ if (isCurrent(id)) {
483
+ error.value = previousError;
484
+ result.value = previousResult;
485
+ }
486
+ throw caught;
487
+ }
488
+ if (isCurrent(id)) error.value = caught;
489
+ throw caught;
490
+ } finally {
491
+ if (isCurrent(id)) loading.value = false;
492
+ }
493
+ }
494
+ function reset() {
495
+ operationId += 1;
496
+ loading.value = false;
497
+ error.value = null;
498
+ result.value = null;
499
+ }
500
+ vue.onScopeDispose(() => {
501
+ disposed = true;
502
+ operationId += 1;
503
+ });
504
+ return { pathPayment, loading, error, result, reset };
505
+ }
506
+ function useClaimableBalance(options = {}) {
507
+ const runtime = useStellarRuntime();
508
+ const address = vue.computed(() => {
509
+ const value = options.address === void 0 ? runtime.walletAddress.value : vue.toValue(options.address);
510
+ return value?.trim() || null;
511
+ });
512
+ const query = useQuery({
513
+ store: runtime.queryStore,
514
+ queryKey: () => core.claimableBalanceKey(
515
+ runtime.networkConfig.horizonUrl,
516
+ runtime.networkConfig.network,
517
+ address.value ?? ""
518
+ ),
519
+ queryFn: () => core.fetchClaimableBalances(runtime.networkConfig, { address: address.value }),
520
+ enabled: () => address.value !== null,
521
+ staleTime: options.staleTime
522
+ });
523
+ return {
524
+ balances: vue.computed(() => query.data.value ?? []),
525
+ loading: vue.computed(() => query.loading.value),
526
+ error: vue.computed(() => query.error.value),
527
+ refetch: query.refetch
528
+ };
529
+ }
530
+ function usePayments(options = {}) {
531
+ const runtime = useStellarRuntime();
532
+ const address = vue.computed(() => {
533
+ const value = options.address === void 0 ? runtime.walletAddress.value : vue.toValue(options.address);
534
+ return value?.trim() || null;
535
+ });
536
+ const limit = vue.computed(() => vue.toValue(options.limit) ?? 10);
537
+ const order = vue.computed(() => vue.toValue(options.order) ?? "desc");
538
+ const inputCursor = vue.computed(() => vue.toValue(options.cursor));
539
+ const activeCursor = vue.ref(inputCursor.value);
540
+ vue.watch(inputCursor, (cursor) => activeCursor.value = cursor, { flush: "sync" });
541
+ vue.watch(
542
+ [address, limit, order],
543
+ (_next, previous) => {
544
+ if (previous.length > 0) activeCursor.value = void 0;
545
+ },
546
+ { flush: "sync" }
547
+ );
548
+ const queryKey = vue.computed(
549
+ () => address.value ? core.paymentsKey(
550
+ runtime.networkConfig.horizonUrl,
551
+ runtime.networkConfig.network,
552
+ address.value,
553
+ limit.value,
554
+ order.value,
555
+ activeCursor.value
556
+ ) : ["payments", "disabled"]
557
+ );
558
+ const query = useQuery({
559
+ store: runtime.queryStore,
560
+ queryKey: () => queryKey.value,
561
+ queryFn: () => core.fetchPaymentsPage(runtime.networkConfig, {
562
+ address: address.value,
563
+ limit: limit.value,
564
+ order: order.value,
565
+ cursor: activeCursor.value
566
+ }),
567
+ enabled: () => address.value !== null,
568
+ maxRetries: options.maxRetries
569
+ });
570
+ const displayed = vue.shallowRef([]);
571
+ const nextPage = vue.shallowRef(null);
572
+ const prevPage = vue.shallowRef(null);
573
+ const pageHasNext = vue.ref(false);
574
+ const pageHasPrev = vue.ref(false);
575
+ const pageLoading = vue.ref(false);
576
+ const pageError = vue.shallowRef(null);
577
+ let requestId = 0;
578
+ function install(page) {
579
+ displayed.value = page.records;
580
+ nextPage.value = page.nextCursor;
581
+ prevPage.value = page.prevCursor;
582
+ pageHasNext.value = page.hasNext;
583
+ pageHasPrev.value = page.hasPrev;
584
+ pageError.value = null;
585
+ }
586
+ vue.watch(
587
+ query.data,
588
+ (page) => {
589
+ requestId += 1;
590
+ pageLoading.value = false;
591
+ if (page) install(page);
592
+ else {
593
+ displayed.value = [];
594
+ nextPage.value = null;
595
+ prevPage.value = null;
596
+ pageHasNext.value = false;
597
+ pageHasPrev.value = false;
598
+ pageError.value = null;
599
+ }
600
+ },
601
+ { immediate: true, flush: "sync" }
602
+ );
603
+ vue.watch(
604
+ () => core.serializeKey(queryKey.value),
605
+ () => {
606
+ requestId += 1;
607
+ displayed.value = [];
608
+ nextPage.value = null;
609
+ prevPage.value = null;
610
+ pageHasNext.value = false;
611
+ pageHasPrev.value = false;
612
+ pageLoading.value = false;
613
+ pageError.value = null;
614
+ },
615
+ { flush: "sync" }
616
+ );
617
+ async function navigate(direction) {
618
+ const load = direction === "next" ? nextPage.value : prevPage.value;
619
+ if (!load || !address.value) return;
620
+ const id = ++requestId;
621
+ pageLoading.value = true;
622
+ pageError.value = null;
623
+ try {
624
+ const response = await load();
625
+ if (id !== requestId || !address.value) return;
626
+ const hasMore = response.records.length > limit.value;
627
+ const records = hasMore ? response.records.slice(0, limit.value) : response.records;
628
+ const normalized = records.map((record) => core.normalizePayment(record, address.value));
629
+ if (normalized.length > 0) displayed.value = normalized;
630
+ nextPage.value = () => response.next();
631
+ prevPage.value = () => response.prev();
632
+ pageHasNext.value = direction === "next" ? hasMore : true;
633
+ pageHasPrev.value = direction === "prev" ? hasMore : true;
634
+ } catch (cause) {
635
+ if (id !== requestId) return;
636
+ pageError.value = core.toStellarError(cause);
637
+ } finally {
638
+ if (id === requestId) pageLoading.value = false;
639
+ }
640
+ }
641
+ async function refetch() {
642
+ requestId += 1;
643
+ pageError.value = null;
644
+ await query.refetch();
645
+ }
646
+ const error = vue.computed(() => pageError.value ?? query.error.value);
647
+ return {
648
+ payments: vue.computed(() => displayed.value),
649
+ loading: vue.computed(() => pageLoading.value || query.loading.value),
650
+ error,
651
+ isStale: vue.computed(() => error.value !== null && displayed.value.length > 0),
652
+ refetch,
653
+ fetchNext: () => navigate("next"),
654
+ fetchPrev: () => navigate("prev"),
655
+ hasNext: vue.computed(() => pageHasNext.value),
656
+ hasPrev: vue.computed(() => pageHasPrev.value)
657
+ };
658
+ }
659
+ function usePaymentHistory(options = {}) {
660
+ const payments = usePayments({
661
+ address: options.address,
662
+ limit: options.limit,
663
+ order: options.order,
664
+ cursor: options.cursor,
665
+ maxRetries: options.maxRetries
666
+ });
667
+ return {
668
+ ...payments,
669
+ payments: vue.computed(
670
+ () => core.filterPayments(payments.payments.value, {
671
+ direction: vue.toValue(options.direction) ?? "all",
672
+ asset: vue.toValue(options.asset) ?? "all"
673
+ })
674
+ )
675
+ };
676
+ }
677
+ function normalizeKey(key) {
678
+ return typeof key === "string" ? key : JSON.stringify(key);
679
+ }
680
+ var DEFAULT_STALE_TIME = 3e4;
681
+ var DEFAULT_GC_TIME = 3e5;
682
+ var VueQueryStore = class {
683
+ constructor(config = {}) {
684
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
685
+ this.entries = vue.reactive(/* @__PURE__ */ new Map());
686
+ this.staleTime = config.staleTime ?? DEFAULT_STALE_TIME;
687
+ this.gcTime = config.gcTime ?? DEFAULT_GC_TIME;
688
+ }
689
+ ensure(key) {
690
+ const existing = this.entries.get(key);
691
+ if (existing) return existing;
692
+ const entry = {
693
+ data: null,
694
+ updatedAt: null,
695
+ loading: false,
696
+ error: null,
697
+ promise: null,
698
+ subscribers: 0,
699
+ gcTimer: null
700
+ };
701
+ this.entries.set(key, entry);
702
+ return entry;
703
+ }
704
+ /**
705
+ * The live, reactive entry for a key. Read its fields inside a `computed`
706
+ * or `watchEffect` — Vue tracks the read and re-runs when the store writes
707
+ * to it.
708
+ */
709
+ getEntry(key) {
710
+ return this.ensure(key);
711
+ }
712
+ /**
713
+ * Registers a subscriber for `key` and cancels any pending GC eviction.
714
+ * Returns the unsubscribe function — call it from `onCleanup` /
715
+ * `onScopeDispose` so an idle entry is eventually evicted.
716
+ */
717
+ subscribe(key) {
718
+ const entry = this.ensure(key);
719
+ entry.subscribers += 1;
720
+ if (entry.gcTimer !== null) {
721
+ clearTimeout(entry.gcTimer);
722
+ entry.gcTimer = null;
723
+ }
724
+ return () => {
725
+ entry.subscribers = Math.max(0, entry.subscribers - 1);
726
+ if (entry.subscribers === 0) this.scheduleGc(key, entry);
727
+ };
728
+ }
729
+ /** Number of active subscribers for a key. Exposed for tests. */
730
+ subscriberCount(key) {
731
+ return this.entries.get(key)?.subscribers ?? 0;
732
+ }
733
+ scheduleGc(key, entry) {
734
+ if (this.gcTime === 0) {
735
+ this.entries.delete(key);
736
+ return;
737
+ }
738
+ entry.gcTimer = setTimeout(() => {
739
+ if (entry.subscribers === 0) this.entries.delete(key);
740
+ }, this.gcTime);
741
+ }
742
+ isFresh(key, staleTime) {
743
+ const entry = this.entries.get(key);
744
+ if (!entry?.updatedAt) return false;
745
+ return Date.now() - entry.updatedAt < (staleTime ?? this.staleTime);
746
+ }
747
+ /**
748
+ * Runs `queryFn` for `key`, deduplicated: a call made while a fetch for the
749
+ * same key is already in flight awaits that same promise instead of
750
+ * issuing a second request — this is what lets two Vue scopes reading the
751
+ * same query share one network call. Skipped entirely when cached data is
752
+ * still within `staleTime`, unless `force` is set.
753
+ */
754
+ run(key, queryFn, opts = {}) {
755
+ const entry = this.ensure(key);
756
+ if (!opts.force) {
757
+ if (entry.promise) return entry.promise;
758
+ if (entry.data !== null && this.isFresh(key, opts.staleTime)) {
759
+ return Promise.resolve(entry.data);
760
+ }
761
+ }
762
+ entry.loading = true;
763
+ entry.error = null;
764
+ const promise = queryFn().then(
765
+ (data) => {
766
+ entry.data = data;
767
+ entry.updatedAt = Date.now();
768
+ entry.loading = false;
769
+ entry.error = null;
770
+ entry.promise = null;
771
+ return data;
772
+ },
773
+ (err) => {
774
+ entry.loading = false;
775
+ entry.error = err;
776
+ entry.promise = null;
777
+ throw err;
778
+ }
779
+ );
780
+ entry.promise = promise;
781
+ return promise;
782
+ }
783
+ /** Marks a cached query stale so its next read fetches fresh data. */
784
+ invalidate(key) {
785
+ const entry = this.entries.get(normalizeKey(key));
786
+ if (entry) entry.updatedAt = null;
787
+ }
788
+ /** Empties the store. Useful in tests; not meant for app code. */
789
+ clear() {
790
+ for (const entry of this.entries.values()) {
791
+ if (entry.gcTimer !== null) clearTimeout(entry.gcTimer);
792
+ }
793
+ this.entries.clear();
794
+ }
795
+ };
796
+
797
+ Object.defineProperty(exports, "ANONYMOUS_SIMULATION_SOURCE", {
798
+ enumerable: true,
799
+ get: function () { return core.ANONYMOUS_SIMULATION_SOURCE; }
800
+ });
801
+ Object.defineProperty(exports, "accountKey", {
802
+ enumerable: true,
803
+ get: function () { return core.accountKey; }
804
+ });
805
+ Object.defineProperty(exports, "claimableBalanceKey", {
806
+ enumerable: true,
807
+ get: function () { return core.claimableBalanceKey; }
808
+ });
809
+ Object.defineProperty(exports, "paymentsKey", {
810
+ enumerable: true,
811
+ get: function () { return core.paymentsKey; }
812
+ });
813
+ exports.NETWORK_CONFIGS = NETWORK_CONFIGS;
814
+ exports.STELLAR_RUNTIME_KEY = STELLAR_RUNTIME_KEY;
815
+ exports.VueQueryStore = VueQueryStore;
816
+ exports.createStellarPlugin = createStellarPlugin;
817
+ exports.createStellarRuntime = createStellarRuntime;
818
+ exports.useAddTrustline = useAddTrustline;
819
+ exports.useBalance = useBalance;
820
+ exports.useClaimableBalance = useClaimableBalance;
821
+ exports.useContractEvents = useContractEvents;
822
+ exports.usePathPayment = usePathPayment;
823
+ exports.usePaymentHistory = usePaymentHistory;
824
+ exports.usePayments = usePayments;
825
+ exports.useSendPayment = useSendPayment;
826
+ exports.useSorobanContract = useSorobanContract;
827
+ exports.useStellarRuntime = useStellarRuntime;
828
+ //# sourceMappingURL=index.js.map
829
+ //# sourceMappingURL=index.js.map