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