@routstr/sdk 0.2.6 → 0.2.7

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.
@@ -447,8 +447,9 @@ var CashuSpender = class {
447
447
  return null;
448
448
  }
449
449
  /**
450
- * Refund all xcashu tokens from storage and increment tryCounts on failure.
451
- * Reuses receiveToken from BalanceManager/CashuSpender for receiving refunds.
450
+ * Refund all xcashu tokens from storage by calling the provider's refund endpoint.
451
+ * The xcashu token acts as an API key to claim the refund, and the response contains
452
+ * the actual refunded Cashu token which is then received into the wallet.
452
453
  * @param mintUrl - The mint URL for receiving tokens
453
454
  * @param excludeBaseUrls - Base URLs to exclude from refund (optional)
454
455
  * @returns Results for each xcashu token refund attempt
@@ -461,7 +462,20 @@ var CashuSpender = class {
461
462
  if (excludedUrls.has(baseUrl)) continue;
462
463
  for (const xcashuToken of tokens) {
463
464
  try {
464
- const receiveResult = await this.receiveToken(xcashuToken.token);
465
+ if (!this.balanceManager) {
466
+ throw new Error("BalanceManager not available for xcashu refund");
467
+ }
468
+ const fetchResult = await this.balanceManager.fetchRefundToken(
469
+ baseUrl,
470
+ xcashuToken.token,
471
+ true
472
+ );
473
+ if (!fetchResult.success || !fetchResult.token) {
474
+ throw new Error(
475
+ fetchResult.error || "Failed to fetch refund token from provider"
476
+ );
477
+ }
478
+ const receiveResult = await this.receiveToken(fetchResult.token);
465
479
  if (receiveResult.success) {
466
480
  this.storageAdapter.removeXcashuToken(baseUrl, xcashuToken.token);
467
481
  results.push({
@@ -476,7 +490,10 @@ var CashuSpender = class {
476
490
  } else {
477
491
  const currentTryCount = xcashuToken.tryCount ?? 0;
478
492
  const newTryCount = currentTryCount + 1;
479
- this.storageAdapter.updateXcashuTokenTryCount(xcashuToken.token, newTryCount);
493
+ this.storageAdapter.updateXcashuTokenTryCount(
494
+ xcashuToken.token,
495
+ newTryCount
496
+ );
480
497
  results.push({
481
498
  baseUrl,
482
499
  token: xcashuToken.token,
@@ -485,13 +502,16 @@ var CashuSpender = class {
485
502
  });
486
503
  this._log(
487
504
  "DEBUG",
488
- `[CashuSpender] refundXcashuTokens: Failed to refund xcashu token for ${baseUrl}, incremented tryCount to ${newTryCount}`
505
+ `[CashuSpender] refundXcashuTokens: Failed to receive refund token for ${baseUrl}, incremented tryCount to ${newTryCount}`
489
506
  );
490
507
  }
491
508
  } catch (error) {
492
509
  const currentTryCount = xcashuToken.tryCount ?? 0;
493
510
  const newTryCount = currentTryCount + 1;
494
- this.storageAdapter.updateXcashuTokenTryCount(xcashuToken.token, newTryCount);
511
+ this.storageAdapter.updateXcashuTokenTryCount(
512
+ xcashuToken.token,
513
+ newTryCount
514
+ );
495
515
  const errorMessage = error instanceof Error ? error.message : String(error);
496
516
  results.push({
497
517
  baseUrl,
@@ -528,7 +548,10 @@ var CashuSpender = class {
528
548
  if (refundResult.success) {
529
549
  this.storageAdapter.removeApiKey(apiKeyEntry.baseUrl);
530
550
  } else {
531
- this.storageAdapter.updateApiKeyBalance(apiKeyEntry.baseUrl, apiKeyEntry.amount);
551
+ this.storageAdapter.updateApiKeyBalance(
552
+ apiKeyEntry.baseUrl,
553
+ apiKeyEntry.amount
554
+ );
532
555
  }
533
556
  results.push({
534
557
  baseUrl: apiKeyEntry.baseUrl,
@@ -666,7 +689,7 @@ var BalanceManager = class {
666
689
  }
667
690
  let fetchResult;
668
691
  try {
669
- fetchResult = await this._fetchRefundTokenWithApiKey(baseUrl, apiKey);
692
+ fetchResult = await this.fetchRefundToken(baseUrl, apiKey);
670
693
  if (!fetchResult.success) {
671
694
  return {
672
695
  success: false,
@@ -702,9 +725,9 @@ var BalanceManager = class {
702
725
  }
703
726
  }
704
727
  /**
705
- * Fetch refund token from provider API using API key authentication
728
+ * Fetch refund token from provider API using API key (or xcashu token) authentication
706
729
  */
707
- async _fetchRefundTokenWithApiKey(baseUrl, apiKey) {
730
+ async fetchRefundToken(baseUrl, apiKeyOrToken, xCashu = false) {
708
731
  if (!baseUrl) {
709
732
  return {
710
733
  success: false,
@@ -718,12 +741,17 @@ var BalanceManager = class {
718
741
  controller.abort();
719
742
  }, 6e4);
720
743
  try {
744
+ const headers = {
745
+ "Content-Type": "application/json"
746
+ };
747
+ if (xCashu) {
748
+ headers["X-Cashu"] = apiKeyOrToken;
749
+ } else {
750
+ headers["Authorization"] = `Bearer ${apiKeyOrToken}`;
751
+ }
721
752
  const response = await fetch(url, {
722
753
  method: "POST",
723
- headers: {
724
- Authorization: `Bearer ${apiKey}`,
725
- "Content-Type": "application/json"
726
- },
754
+ headers,
727
755
  signal: controller.signal
728
756
  });
729
757
  clearTimeout(timeoutId);
@@ -744,10 +772,7 @@ var BalanceManager = class {
744
772
  };
745
773
  } catch (error) {
746
774
  clearTimeout(timeoutId);
747
- console.error(
748
- "[BalanceManager._fetchRefundTokenWithApiKey] Fetch error",
749
- error
750
- );
775
+ console.error("[BalanceManager.fetchRefundToken] Fetch error", error);
751
776
  if (error instanceof Error) {
752
777
  if (error.name === "AbortError") {
753
778
  return {
@@ -794,11 +819,7 @@ var BalanceManager = class {
794
819
  };
795
820
  }
796
821
  cashuToken = tokenResult.token;
797
- const topUpResult = await this._postTopUp(
798
- baseUrl,
799
- apiKey,
800
- cashuToken
801
- );
822
+ const topUpResult = await this._postTopUp(baseUrl, apiKey, cashuToken);
802
823
  requestId = topUpResult.requestId;
803
824
  console.log(topUpResult);
804
825
  if (!topUpResult.success) {
@@ -1164,7 +1185,7 @@ var BalanceManager = class {
1164
1185
  console.log(response.status);
1165
1186
  const data = await response.json();
1166
1187
  console.log("FAILED ", data);
1167
- const isInvalidApiKey = response.status === 401 && data?.code === "invalid_api_key" && data?.message?.includes("proofs already spent");
1188
+ const isInvalidApiKey = response.status === 401 && data?.detail?.error?.code === "invalid_api_key" && data?.detail?.error?.message?.includes("proofs already spent");
1168
1189
  return {
1169
1190
  amount: -1,
1170
1191
  reserved: data.reserved ?? 0,
@@ -1606,8 +1627,13 @@ function isInsecureHttpUrl(url) {
1606
1627
  return url.startsWith("http://");
1607
1628
  }
1608
1629
  var ProviderManager = class _ProviderManager {
1609
- constructor(providerRegistry) {
1630
+ constructor(providerRegistry, store) {
1610
1631
  this.providerRegistry = providerRegistry;
1632
+ this.instanceId = `pm_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 8)}`;
1633
+ if (store) {
1634
+ this.store = store;
1635
+ this.hydrateFromStore();
1636
+ }
1611
1637
  }
1612
1638
  failedProviders = /* @__PURE__ */ new Set();
1613
1639
  /** Track when each provider last failed (provider URL -> timestamp) */
@@ -1616,14 +1642,57 @@ var ProviderManager = class _ProviderManager {
1616
1642
  providersOnCoolDown = [];
1617
1643
  /** Cooldown duration in milliseconds (5 minutes) */
1618
1644
  static COOLDOWN_DURATION_MS = 5 * 60 * 1e3;
1645
+ /** Optional persistent store for failure tracking */
1646
+ store = null;
1647
+ /** Instance ID for debugging */
1648
+ instanceId;
1649
+ /**
1650
+ * Hydrate in-memory state from persistent store
1651
+ */
1652
+ hydrateFromStore() {
1653
+ if (!this.store) return;
1654
+ const state = this.store.getState();
1655
+ this.failedProviders = new Set(state.failedProviders);
1656
+ this.lastFailed = new Map(Object.entries(state.lastFailed));
1657
+ const now = Date.now();
1658
+ this.providersOnCoolDown = state.providersOnCooldown.filter(
1659
+ (entry) => now - entry.timestamp < _ProviderManager.COOLDOWN_DURATION_MS
1660
+ ).map((entry) => [entry.baseUrl, entry.timestamp]);
1661
+ console.log(`[ProviderManager:${this.instanceId}] Hydrated from store:`);
1662
+ console.log(` failedProviders: ${this.failedProviders.size}`);
1663
+ console.log(` lastFailed: ${this.lastFailed.size}`);
1664
+ console.log(` providersOnCooldown: ${this.providersOnCoolDown.length}`);
1665
+ }
1666
+ /**
1667
+ * Get instance ID for debugging
1668
+ */
1669
+ getInstanceId() {
1670
+ return this.instanceId;
1671
+ }
1619
1672
  /**
1620
1673
  * Clean up expired cooldown entries
1621
1674
  */
1622
1675
  cleanupExpiredCooldowns() {
1623
1676
  const now = Date.now();
1677
+ const before = this.providersOnCoolDown.length;
1624
1678
  this.providersOnCoolDown = this.providersOnCoolDown.filter(
1625
- ([, timestamp]) => now - timestamp < _ProviderManager.COOLDOWN_DURATION_MS
1679
+ ([url, timestamp]) => {
1680
+ const age = now - timestamp;
1681
+ const isExpired = age >= _ProviderManager.COOLDOWN_DURATION_MS;
1682
+ if (isExpired) {
1683
+ console.log(
1684
+ `[cleanupExpiredCooldowns:${this.instanceId}] Removing expired cooldown for ${url} (age: ${age}ms, cooldown: ${_ProviderManager.COOLDOWN_DURATION_MS}ms)`
1685
+ );
1686
+ }
1687
+ return !isExpired;
1688
+ }
1626
1689
  );
1690
+ const after = this.providersOnCoolDown.length;
1691
+ if (before !== after) {
1692
+ console.log(
1693
+ `[cleanupExpiredCooldowns:${this.instanceId}] Cleaned up ${before - after} expired cooldown(s), ${after} remaining`
1694
+ );
1695
+ }
1627
1696
  }
1628
1697
  /**
1629
1698
  * Get the cooldown duration in milliseconds
@@ -1636,7 +1705,8 @@ var ProviderManager = class _ProviderManager {
1636
1705
  */
1637
1706
  isOnCooldown(baseUrl) {
1638
1707
  this.cleanupExpiredCooldowns();
1639
- return this.providersOnCoolDown.some(([url]) => url === baseUrl);
1708
+ const result = this.providersOnCoolDown.some(([url]) => url === baseUrl);
1709
+ return result;
1640
1710
  }
1641
1711
  /**
1642
1712
  * Get all providers currently on cooldown
@@ -1650,6 +1720,9 @@ var ProviderManager = class _ProviderManager {
1650
1720
  */
1651
1721
  resetFailedProviders() {
1652
1722
  this.failedProviders.clear();
1723
+ if (this.store) {
1724
+ this.store.getState().setFailedProviders([]);
1725
+ }
1653
1726
  }
1654
1727
  /**
1655
1728
  * Get the last failed timestamp for a provider
@@ -1670,13 +1743,62 @@ var ProviderManager = class _ProviderManager {
1670
1743
  markFailed(baseUrl) {
1671
1744
  const now = Date.now();
1672
1745
  const lastFailure = this.lastFailed.get(baseUrl);
1746
+ console.log(`[markFailed:${this.instanceId}] baseUrl: ${baseUrl}`);
1747
+ console.log(
1748
+ `[markFailed:${this.instanceId}] lastFailure from map: ${lastFailure}`
1749
+ );
1750
+ console.log(
1751
+ `[markFailed:${this.instanceId}] current timestamp (now): ${now}`
1752
+ );
1753
+ console.log(
1754
+ `[markFailed:${this.instanceId}] COOLDOWN_DURATION_MS: ${_ProviderManager.COOLDOWN_DURATION_MS}`
1755
+ );
1756
+ if (lastFailure !== void 0) {
1757
+ const timeSinceLastFailure = now - lastFailure;
1758
+ console.log(
1759
+ `[markFailed:${this.instanceId}] timeSinceLastFailure: ${timeSinceLastFailure}ms`
1760
+ );
1761
+ console.log(
1762
+ `[markFailed:${this.instanceId}] isWithinCooldownWindow: ${timeSinceLastFailure < _ProviderManager.COOLDOWN_DURATION_MS}`
1763
+ );
1764
+ }
1673
1765
  this.lastFailed.set(baseUrl, now);
1674
1766
  this.failedProviders.add(baseUrl);
1767
+ if (this.store) {
1768
+ this.store.getState().setLastFailedTimestamp(baseUrl, now);
1769
+ this.store.getState().addFailedProvider(baseUrl);
1770
+ }
1771
+ console.log(
1772
+ `[markFailed:${this.instanceId}] Updated lastFailed map for ${baseUrl} to ${now}`
1773
+ );
1774
+ console.log(
1775
+ `[markFailed:${this.instanceId}] failedProviders set size: ${this.failedProviders.size}`
1776
+ );
1675
1777
  if (lastFailure !== void 0 && now - lastFailure < _ProviderManager.COOLDOWN_DURATION_MS) {
1778
+ console.log(
1779
+ `[markFailed:${this.instanceId}] Second failure detected within cooldown window for ${baseUrl}`
1780
+ );
1676
1781
  if (!this.isOnCooldown(baseUrl)) {
1677
1782
  this.providersOnCoolDown.push([baseUrl, now]);
1783
+ if (this.store) {
1784
+ this.store.getState().addProviderOnCooldown(baseUrl, now);
1785
+ }
1786
+ console.log(
1787
+ `[markFailed:${this.instanceId}] Provider ${baseUrl} added to cooldown after second failure within 5 minutes`
1788
+ );
1789
+ } else {
1790
+ console.log(
1791
+ `[markFailed:${this.instanceId}] Provider ${baseUrl} is already on cooldown`
1792
+ );
1793
+ }
1794
+ } else {
1795
+ if (lastFailure === void 0) {
1678
1796
  console.log(
1679
- `Provider ${baseUrl} added to cooldown after second failure within 5 minutes`
1797
+ `[markFailed:${this.instanceId}] First failure for ${baseUrl} - not adding to cooldown yet`
1798
+ );
1799
+ } else {
1800
+ console.log(
1801
+ `[markFailed:${this.instanceId}] Failure outside cooldown window for ${baseUrl} (timeSinceLastFailure: ${now - lastFailure}ms)`
1680
1802
  );
1681
1803
  }
1682
1804
  }
@@ -1688,18 +1810,27 @@ var ProviderManager = class _ProviderManager {
1688
1810
  this.providersOnCoolDown = this.providersOnCoolDown.filter(
1689
1811
  ([url]) => url !== baseUrl
1690
1812
  );
1813
+ if (this.store) {
1814
+ this.store.getState().removeProviderFromCooldown(baseUrl);
1815
+ }
1691
1816
  }
1692
1817
  /**
1693
1818
  * Clear all cooldown tracking
1694
1819
  */
1695
1820
  clearCooldowns() {
1696
1821
  this.providersOnCoolDown = [];
1822
+ if (this.store) {
1823
+ this.store.getState().clearProvidersOnCooldown();
1824
+ }
1697
1825
  }
1698
1826
  /**
1699
1827
  * Clear all failure tracking (lastFailed timestamps)
1700
1828
  */
1701
1829
  clearFailureHistory() {
1702
1830
  this.lastFailed.clear();
1831
+ if (this.store) {
1832
+ this.store.getState().setLastFailed({});
1833
+ }
1703
1834
  }
1704
1835
  /**
1705
1836
  * Check if a provider has failed
@@ -2121,7 +2252,10 @@ var SDK_STORAGE_KEYS = {
2121
2252
  LAST_ROUTSTR21_MODELS_UPDATE: "lastRoutstr21ModelsUpdate",
2122
2253
  CACHED_RECEIVE_TOKENS: "cached_receive_tokens",
2123
2254
  USAGE_TRACKING: "usage_tracking",
2124
- CLIENT_IDS: "client_ids"
2255
+ CLIENT_IDS: "client_ids",
2256
+ FAILED_PROVIDERS: "failed_providers",
2257
+ LAST_FAILED: "last_failed",
2258
+ PROVIDERS_ON_COOLDOWN: "providers_on_cooldown"
2125
2259
  };
2126
2260
 
2127
2261
  // storage/usageTracking/indexedDB.ts
@@ -2768,6 +2902,9 @@ var createEmptyStore = (driver) => createStore((set, get) => ({
2768
2902
  lastRoutstr21ModelsUpdate: null,
2769
2903
  cachedReceiveTokens: [],
2770
2904
  clientIds: [],
2905
+ failedProviders: [],
2906
+ lastFailed: {},
2907
+ providersOnCooldown: [],
2771
2908
  setModelsFromAllProviders: (value) => {
2772
2909
  const normalized = {};
2773
2910
  for (const [baseUrl, models] of Object.entries(value)) {
@@ -2907,6 +3044,71 @@ var createEmptyStore = (driver) => createStore((set, get) => ({
2907
3044
  void driver.setItem(SDK_STORAGE_KEYS.CLIENT_IDS, normalized);
2908
3045
  return { clientIds: normalized };
2909
3046
  });
3047
+ },
3048
+ // ========== Failure Tracking ==========
3049
+ setFailedProviders: (value) => {
3050
+ const normalized = value.map((url) => normalizeBaseUrl5(url));
3051
+ void driver.setItem(SDK_STORAGE_KEYS.FAILED_PROVIDERS, normalized);
3052
+ set({ failedProviders: normalized });
3053
+ },
3054
+ addFailedProvider: (baseUrl) => {
3055
+ const normalized = normalizeBaseUrl5(baseUrl);
3056
+ const current = get().failedProviders;
3057
+ if (!current.includes(normalized)) {
3058
+ const updated = [...current, normalized];
3059
+ void driver.setItem(SDK_STORAGE_KEYS.FAILED_PROVIDERS, updated);
3060
+ set({ failedProviders: updated });
3061
+ }
3062
+ },
3063
+ removeFailedProvider: (baseUrl) => {
3064
+ const normalized = normalizeBaseUrl5(baseUrl);
3065
+ const current = get().failedProviders;
3066
+ const updated = current.filter((url) => url !== normalized);
3067
+ void driver.setItem(SDK_STORAGE_KEYS.FAILED_PROVIDERS, updated);
3068
+ set({ failedProviders: updated });
3069
+ },
3070
+ setLastFailed: (value) => {
3071
+ const normalized = {};
3072
+ for (const [baseUrl, timestamp] of Object.entries(value)) {
3073
+ normalized[normalizeBaseUrl5(baseUrl)] = timestamp;
3074
+ }
3075
+ void driver.setItem(SDK_STORAGE_KEYS.LAST_FAILED, normalized);
3076
+ set({ lastFailed: normalized });
3077
+ },
3078
+ setLastFailedTimestamp: (baseUrl, timestamp) => {
3079
+ const normalized = normalizeBaseUrl5(baseUrl);
3080
+ const current = get().lastFailed;
3081
+ const updated = { ...current, [normalized]: timestamp };
3082
+ void driver.setItem(SDK_STORAGE_KEYS.LAST_FAILED, updated);
3083
+ set({ lastFailed: updated });
3084
+ },
3085
+ setProvidersOnCooldown: (value) => {
3086
+ const normalized = value.map((entry) => ({
3087
+ baseUrl: normalizeBaseUrl5(entry.baseUrl),
3088
+ timestamp: entry.timestamp
3089
+ }));
3090
+ void driver.setItem(SDK_STORAGE_KEYS.PROVIDERS_ON_COOLDOWN, normalized);
3091
+ set({ providersOnCooldown: normalized });
3092
+ },
3093
+ addProviderOnCooldown: (baseUrl, timestamp) => {
3094
+ const normalized = normalizeBaseUrl5(baseUrl);
3095
+ const current = get().providersOnCooldown;
3096
+ if (!current.some((entry) => entry.baseUrl === normalized)) {
3097
+ const updated = [...current, { baseUrl: normalized, timestamp }];
3098
+ void driver.setItem(SDK_STORAGE_KEYS.PROVIDERS_ON_COOLDOWN, updated);
3099
+ set({ providersOnCooldown: updated });
3100
+ }
3101
+ },
3102
+ removeProviderFromCooldown: (baseUrl) => {
3103
+ const normalized = normalizeBaseUrl5(baseUrl);
3104
+ const current = get().providersOnCooldown;
3105
+ const updated = current.filter((entry) => entry.baseUrl !== normalized);
3106
+ void driver.setItem(SDK_STORAGE_KEYS.PROVIDERS_ON_COOLDOWN, updated);
3107
+ set({ providersOnCooldown: updated });
3108
+ },
3109
+ clearProvidersOnCooldown: () => {
3110
+ void driver.setItem(SDK_STORAGE_KEYS.PROVIDERS_ON_COOLDOWN, []);
3111
+ set({ providersOnCooldown: [] });
2910
3112
  }
2911
3113
  }));
2912
3114
  var hydrateStoreFromDriver = async (store, driver) => {
@@ -2925,7 +3127,10 @@ var hydrateStoreFromDriver = async (store, driver) => {
2925
3127
  rawRoutstr21Models,
2926
3128
  rawLastRoutstr21ModelsUpdate,
2927
3129
  rawCachedReceiveTokens,
2928
- rawClientIds
3130
+ rawClientIds,
3131
+ rawFailedProviders,
3132
+ rawLastFailed,
3133
+ rawProvidersOnCooldown
2929
3134
  ] = await Promise.all([
2930
3135
  driver.getItem(
2931
3136
  SDK_STORAGE_KEYS.MODELS_FROM_ALL_PROVIDERS,
@@ -2956,7 +3161,10 @@ var hydrateStoreFromDriver = async (store, driver) => {
2956
3161
  null
2957
3162
  ),
2958
3163
  driver.getItem(SDK_STORAGE_KEYS.CACHED_RECEIVE_TOKENS, []),
2959
- driver.getItem(SDK_STORAGE_KEYS.CLIENT_IDS, [])
3164
+ driver.getItem(SDK_STORAGE_KEYS.CLIENT_IDS, []),
3165
+ driver.getItem(SDK_STORAGE_KEYS.FAILED_PROVIDERS, []),
3166
+ driver.getItem(SDK_STORAGE_KEYS.LAST_FAILED, {}),
3167
+ driver.getItem(SDK_STORAGE_KEYS.PROVIDERS_ON_COOLDOWN, [])
2960
3168
  ]);
2961
3169
  const modelsFromAllProviders = Object.fromEntries(
2962
3170
  Object.entries(rawModels).map(([baseUrl, models]) => [
@@ -3024,6 +3232,17 @@ var hydrateStoreFromDriver = async (store, driver) => {
3024
3232
  createdAt: entry.createdAt ?? Date.now(),
3025
3233
  lastUsed: entry.lastUsed ?? null
3026
3234
  }));
3235
+ const failedProviders = rawFailedProviders.map((url) => normalizeBaseUrl5(url));
3236
+ const lastFailed = Object.fromEntries(
3237
+ Object.entries(rawLastFailed).map(([baseUrl, timestamp]) => [
3238
+ normalizeBaseUrl5(baseUrl),
3239
+ timestamp
3240
+ ])
3241
+ );
3242
+ const providersOnCooldown = rawProvidersOnCooldown.map((entry) => ({
3243
+ baseUrl: normalizeBaseUrl5(entry.baseUrl),
3244
+ timestamp: entry.timestamp
3245
+ }));
3027
3246
  store.setState({
3028
3247
  modelsFromAllProviders,
3029
3248
  lastUsedModel,
@@ -3039,7 +3258,10 @@ var hydrateStoreFromDriver = async (store, driver) => {
3039
3258
  routstr21Models,
3040
3259
  lastRoutstr21ModelsUpdate,
3041
3260
  cachedReceiveTokens,
3042
- clientIds
3261
+ clientIds,
3262
+ failedProviders,
3263
+ lastFailed,
3264
+ providersOnCooldown
3043
3265
  });
3044
3266
  };
3045
3267
  var createSdkStore = ({
@@ -3205,11 +3427,11 @@ var RoutstrClient = class {
3205
3427
  this.balanceManager
3206
3428
  );
3207
3429
  this.streamProcessor = new StreamProcessor();
3208
- this.providerManager = new ProviderManager(providerRegistry);
3209
3430
  this.alertLevel = alertLevel;
3210
3431
  this.mode = mode;
3211
3432
  this.usageTrackingDriver = options.usageTrackingDriver;
3212
3433
  this.sdkStore = options.sdkStore;
3434
+ this.providerManager = options.providerManager ?? new ProviderManager(providerRegistry, this.sdkStore);
3213
3435
  }
3214
3436
  cashuSpender;
3215
3437
  balanceManager;
@@ -3739,6 +3961,10 @@ var RoutstrClient = class {
3739
3961
  const currentBalance = currentBalanceInfo.unit === "msat" ? currentBalanceInfo.amount / 1e3 : currentBalanceInfo.amount;
3740
3962
  const shortfall = Math.max(0, params.requiredSats - currentBalance);
3741
3963
  topupAmount = shortfall > 0 ? shortfall : params.requiredSats;
3964
+ this._log(
3965
+ "DEBUG",
3966
+ `The shortfall is: ${shortfall}. requiredSats: ${params.requiredSats}. Current Balance: ${currentBalance} `
3967
+ );
3742
3968
  } catch (e) {
3743
3969
  this._log(
3744
3970
  "WARN",
@@ -3868,6 +4094,20 @@ var RoutstrClient = class {
3868
4094
  tryNextProvider = true;
3869
4095
  }
3870
4096
  }
4097
+ if (status === 401 && this.mode === "apikeys") {
4098
+ this._log(
4099
+ "DEBUG",
4100
+ `[RoutstrClient] _handleErrorResponse: Checking balance for ${baseUrl}, key preview=${token}`
4101
+ );
4102
+ const latestBalanceInfo = await this.balanceManager.getTokenBalance(
4103
+ token,
4104
+ baseUrl
4105
+ );
4106
+ if (latestBalanceInfo.isInvalidApiKey) {
4107
+ this.storageAdapter.removeApiKey(baseUrl);
4108
+ tryNextProvider = true;
4109
+ }
4110
+ }
3871
4111
  if ((status === 401 || status === 403 || status === 413 || status === 400 || status === 500 || status === 502 || status === 503 || status === 504 || status === 521) && !tryNextProvider) {
3872
4112
  this._log(
3873
4113
  "DEBUG",
@@ -3878,13 +4118,13 @@ var RoutstrClient = class {
3878
4118
  "DEBUG",
3879
4119
  `[RoutstrClient] _handleErrorResponse: Attempting API key refund for ${baseUrl}, key preview=${token}`
3880
4120
  );
3881
- const initialBalance = await this.balanceManager.getTokenBalance(
4121
+ const latestBalanceInfo = await this.balanceManager.getTokenBalance(
3882
4122
  token,
3883
4123
  baseUrl
3884
4124
  );
3885
4125
  this._log(
3886
4126
  "DEBUG",
3887
- `[RoutstrClient] _handleErrorResponse: Initial API key balance: ${initialBalance.amount}`
4127
+ `[RoutstrClient] _handleErrorResponse: Initial API key balance: ${latestBalanceInfo.amount}`
3888
4128
  );
3889
4129
  const refundResult = await this.balanceManager.refundApiKey({
3890
4130
  mintUrl,
@@ -3896,7 +4136,7 @@ var RoutstrClient = class {
3896
4136
  "DEBUG",
3897
4137
  `[RoutstrClient] _handleErrorResponse: API key refund result: success=${refundResult.success}, message=${refundResult.message}`
3898
4138
  );
3899
- if (!refundResult.success && initialBalance.amount > 0) {
4139
+ if (!refundResult.success && latestBalanceInfo.amount > 0) {
3900
4140
  throw new ProviderError(
3901
4141
  baseUrl,
3902
4142
  status,
@@ -4036,7 +4276,6 @@ var RoutstrClient = class {
4036
4276
  try {
4037
4277
  const xcashuResults = await this.cashuSpender.refundXcashuTokens(mintUrl);
4038
4278
  this._log("DEBUG", "Refund xcashu tokens results:", xcashuResults);
4039
- const results = await this.cashuSpender.refundProviders(mintUrl);
4040
4279
  } catch (error) {
4041
4280
  this._log("ERROR", "Failed to refund providers:", error);
4042
4281
  }