@routstr/sdk 0.2.2 → 0.2.4

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.
@@ -79,7 +79,7 @@ var auditLogger = AuditLogger.getInstance();
79
79
 
80
80
  // wallet/tokenUtils.ts
81
81
  function isNetworkErrorMessage(message) {
82
- return message.includes("NetworkError when attempting to fetch resource") || message.includes("Failed to fetch") || message.includes("Load failed");
82
+ return message.includes("NetworkError when attempting to fetch resource") || message.includes("Failed to fetch") || message.includes("Load failed") || message.includes("ERR_TLS_CERT_ALTNAME_INVALID") || message.includes("ERR_TLS_CERT_NOT_YET_VALID") || message.includes("ERR_TLS_CERT_EXPIRED") || message.includes("UNABLE_TO_VERIFY_LEAF_SIGNATURE") || message.includes("SELF_SIGNED_CERT_IN_CHAIN");
83
83
  }
84
84
  function getBalanceInSats(balance, unit) {
85
85
  return unit === "msat" ? balance / 1e3 : balance;
@@ -369,7 +369,26 @@ var CashuSpender = class {
369
369
  }
370
370
  }
371
371
  if (token && baseUrl) {
372
- this.storageAdapter.setToken(baseUrl, token);
372
+ try {
373
+ this.storageAdapter.setToken(baseUrl, token);
374
+ } catch (error) {
375
+ if (error instanceof Error && error.message.includes("Token already exists")) {
376
+ this._log(
377
+ "DEBUG",
378
+ `[CashuSpender] _spendInternal: Token already exists for ${baseUrl}, receiving newly created token and using existing`
379
+ );
380
+ const receiveResult = await this.receiveToken(token);
381
+ if (receiveResult.success) {
382
+ this._log(
383
+ "DEBUG",
384
+ `[CashuSpender] _spendInternal: Token restored successfully, amount=${receiveResult.amount}`
385
+ );
386
+ }
387
+ token = this.storageAdapter.getToken(baseUrl);
388
+ } else {
389
+ throw error;
390
+ }
391
+ }
373
392
  }
374
393
  this._logTransaction("spend", {
375
394
  amount: spentAmount,
@@ -847,7 +866,13 @@ var BalanceManager = class {
847
866
  p2pkPubkey
848
867
  } = options;
849
868
  const adjustedAmount = Math.ceil(amount);
869
+ console.log(
870
+ `[BalanceManager.createProviderToken] Starting: baseUrl=${baseUrl}, mintUrl=${mintUrl}, amount=${amount}, adjustedAmount=${adjustedAmount}, retryCount=${retryCount}`
871
+ );
850
872
  if (!adjustedAmount || isNaN(adjustedAmount)) {
873
+ console.error(
874
+ `[BalanceManager.createProviderToken] FAILURE: Invalid amount - amount=${amount}, adjustedAmount=${adjustedAmount}`
875
+ );
851
876
  return { success: false, error: "Invalid top up amount" };
852
877
  }
853
878
  const balanceState = await this.getBalanceState();
@@ -878,14 +903,11 @@ var BalanceManager = class {
878
903
  { url: "", balance: 0 }
879
904
  ).url
880
905
  );
906
+ console.error(
907
+ `[BalanceManager.createProviderToken] FAILURE: Insufficient balance - required=${adjustedAmount}, available=${totalMintBalance + targetProviderBalance}, totalMintBalance=${totalMintBalance}, targetProviderBalance=${targetProviderBalance}, refundableProviderBalance=${refundableProviderBalance}`
908
+ );
881
909
  return { success: false, error: error.message };
882
910
  }
883
- if (targetProviderBalance >= adjustedAmount) {
884
- return {
885
- success: true,
886
- amountSpent: 0
887
- };
888
- }
889
911
  const providerMints = baseUrl && this.providerRegistry ? this.providerRegistry.getProviderMints(baseUrl) : [];
890
912
  let requiredAmount = adjustedAmount;
891
913
  const supportedMintsOnly = providerMints.length > 0;
@@ -919,6 +941,9 @@ var BalanceManager = class {
919
941
  maxMintUrl = mintUrl2;
920
942
  }
921
943
  }
944
+ console.error(
945
+ `[BalanceManager.createProviderToken] FAILURE: No candidate mints found - requiredAmount=${requiredAmount}, totalMintBalance=${totalMintBalance}, maxBalance=${maxBalance}, maxMintUrl=${maxMintUrl}, providerMints=${JSON.stringify(providerMints)}`
946
+ );
922
947
  const error = new InsufficientBalanceError(
923
948
  adjustedAmount,
924
949
  totalMintBalance,
@@ -930,11 +955,17 @@ var BalanceManager = class {
930
955
  let lastError;
931
956
  for (const candidateMint of candidates) {
932
957
  try {
958
+ console.log(
959
+ `[BalanceManager.createProviderToken] Attempting mint: ${candidateMint}, amount: ${requiredAmount}`
960
+ );
933
961
  const token = await this.walletAdapter.sendToken(
934
962
  candidateMint,
935
963
  requiredAmount,
936
964
  p2pkPubkey
937
965
  );
966
+ console.log(
967
+ `[BalanceManager.createProviderToken] SUCCESS: Token created from mint ${candidateMint}`
968
+ );
938
969
  return {
939
970
  success: true,
940
971
  token,
@@ -942,9 +973,16 @@ var BalanceManager = class {
942
973
  amountSpent: requiredAmount
943
974
  };
944
975
  } catch (error) {
976
+ const errorMsg = error instanceof Error ? error.message : String(error);
977
+ console.error(
978
+ `[BalanceManager.createProviderToken] FAILURE: Mint ${candidateMint} failed with error: ${errorMsg}`
979
+ );
945
980
  if (error instanceof Error) {
946
- lastError = error.message;
981
+ lastError = errorMsg;
947
982
  if (isNetworkErrorMessage(error.message)) {
983
+ console.warn(
984
+ `[BalanceManager.createProviderToken] Network error from ${candidateMint}, trying next mint...`
985
+ );
948
986
  continue;
949
987
  }
950
988
  }
@@ -954,6 +992,9 @@ var BalanceManager = class {
954
992
  };
955
993
  }
956
994
  }
995
+ console.error(
996
+ `[BalanceManager.createProviderToken] FAILURE: All candidate mints exhausted - lastError=${lastError}, candidates=${JSON.stringify(candidates)}`
997
+ );
957
998
  return {
958
999
  success: false,
959
1000
  error: lastError || "All candidate mints failed while creating top up token"
@@ -1239,7 +1280,6 @@ var BalanceManager = class {
1239
1280
  });
1240
1281
  if (response.ok) {
1241
1282
  const data = await response.json();
1242
- console.log("TOKENA FASJDFAS", data);
1243
1283
  return {
1244
1284
  amount: data.balance,
1245
1285
  reserved: data.reserved ?? 0,
@@ -1612,22 +1652,101 @@ function calculateImageTokens(width, height, detail = "auto") {
1612
1652
  function isInsecureHttpUrl(url) {
1613
1653
  return url.startsWith("http://");
1614
1654
  }
1615
- var ProviderManager = class {
1655
+ var ProviderManager = class _ProviderManager {
1616
1656
  constructor(providerRegistry) {
1617
1657
  this.providerRegistry = providerRegistry;
1618
1658
  }
1619
1659
  failedProviders = /* @__PURE__ */ new Set();
1660
+ /** Track when each provider last failed (provider URL -> timestamp) */
1661
+ lastFailed = /* @__PURE__ */ new Map();
1662
+ /** Providers on cooldown: [provider_url, cooldown_started_timestamp][] */
1663
+ providersOnCoolDown = [];
1664
+ /** Cooldown duration in milliseconds (5 minutes) */
1665
+ static COOLDOWN_DURATION_MS = 5 * 60 * 1e3;
1666
+ /**
1667
+ * Clean up expired cooldown entries
1668
+ */
1669
+ cleanupExpiredCooldowns() {
1670
+ const now = Date.now();
1671
+ this.providersOnCoolDown = this.providersOnCoolDown.filter(
1672
+ ([, timestamp]) => now - timestamp < _ProviderManager.COOLDOWN_DURATION_MS
1673
+ );
1674
+ }
1675
+ /**
1676
+ * Get the cooldown duration in milliseconds
1677
+ */
1678
+ getCooldownDurationMs() {
1679
+ return _ProviderManager.COOLDOWN_DURATION_MS;
1680
+ }
1681
+ /**
1682
+ * Check if a provider is currently on cooldown
1683
+ */
1684
+ isOnCooldown(baseUrl) {
1685
+ this.cleanupExpiredCooldowns();
1686
+ return this.providersOnCoolDown.some(([url]) => url === baseUrl);
1687
+ }
1688
+ /**
1689
+ * Get all providers currently on cooldown
1690
+ */
1691
+ getProvidersOnCooldown() {
1692
+ this.cleanupExpiredCooldowns();
1693
+ return [...this.providersOnCoolDown];
1694
+ }
1620
1695
  /**
1621
1696
  * Reset the failed providers list
1622
1697
  */
1623
1698
  resetFailedProviders() {
1624
1699
  this.failedProviders.clear();
1625
1700
  }
1701
+ /**
1702
+ * Get the last failed timestamp for a provider
1703
+ */
1704
+ getLastFailed(baseUrl) {
1705
+ return this.lastFailed.get(baseUrl);
1706
+ }
1707
+ /**
1708
+ * Get all providers with their last failed timestamps
1709
+ */
1710
+ getAllLastFailed() {
1711
+ return new Map(this.lastFailed);
1712
+ }
1626
1713
  /**
1627
1714
  * Mark a provider as failed
1715
+ * If a provider fails twice within 5 minutes, it's added to cooldown
1628
1716
  */
1629
1717
  markFailed(baseUrl) {
1718
+ const now = Date.now();
1719
+ const lastFailure = this.lastFailed.get(baseUrl);
1720
+ this.lastFailed.set(baseUrl, now);
1630
1721
  this.failedProviders.add(baseUrl);
1722
+ if (lastFailure !== void 0 && now - lastFailure < _ProviderManager.COOLDOWN_DURATION_MS) {
1723
+ if (!this.isOnCooldown(baseUrl)) {
1724
+ this.providersOnCoolDown.push([baseUrl, now]);
1725
+ console.log(
1726
+ `Provider ${baseUrl} added to cooldown after second failure within 5 minutes`
1727
+ );
1728
+ }
1729
+ }
1730
+ }
1731
+ /**
1732
+ * Remove a provider from cooldown (e.g., after successful request)
1733
+ */
1734
+ removeFromCooldown(baseUrl) {
1735
+ this.providersOnCoolDown = this.providersOnCoolDown.filter(
1736
+ ([url]) => url !== baseUrl
1737
+ );
1738
+ }
1739
+ /**
1740
+ * Clear all cooldown tracking
1741
+ */
1742
+ clearCooldowns() {
1743
+ this.providersOnCoolDown = [];
1744
+ }
1745
+ /**
1746
+ * Clear all failure tracking (lastFailed timestamps)
1747
+ */
1748
+ clearFailureHistory() {
1749
+ this.lastFailed.clear();
1631
1750
  }
1632
1751
  /**
1633
1752
  * Check if a provider has failed
@@ -1656,7 +1775,7 @@ var ProviderManager = class {
1656
1775
  const allProviders = this.providerRegistry.getAllProvidersModels();
1657
1776
  const candidates = [];
1658
1777
  for (const [baseUrl, models] of Object.entries(allProviders)) {
1659
- if (baseUrl === currentBaseUrl || this.failedProviders.has(baseUrl) || disabledProviders.has(baseUrl)) {
1778
+ if (baseUrl === currentBaseUrl || this.failedProviders.has(baseUrl) || disabledProviders.has(baseUrl) || this.isOnCooldown(baseUrl)) {
1660
1779
  continue;
1661
1780
  }
1662
1781
  if (!torMode && (isOnionUrl(baseUrl) || isInsecureHttpUrl(baseUrl))) {
@@ -1703,6 +1822,7 @@ var ProviderManager = class {
1703
1822
  const torMode = isTorContext();
1704
1823
  for (const [baseUrl, models] of Object.entries(allProviders)) {
1705
1824
  if (disabledProviders.has(baseUrl)) continue;
1825
+ if (this.isOnCooldown(baseUrl)) continue;
1706
1826
  if (!torMode && (isOnionUrl(baseUrl) || isInsecureHttpUrl(baseUrl)))
1707
1827
  continue;
1708
1828
  const model = models.find((m) => m.id === modelId);
@@ -1726,6 +1846,7 @@ var ProviderManager = class {
1726
1846
  const results = [];
1727
1847
  for (const [baseUrl, models] of Object.entries(allModels)) {
1728
1848
  if (!includeDisabled && disabledProviders.has(baseUrl)) continue;
1849
+ if (this.isOnCooldown(baseUrl)) continue;
1729
1850
  if (torMode && !baseUrl.includes(".onion")) continue;
1730
1851
  if (!torMode && (baseUrl.includes(".onion") || isInsecureHttpUrl(baseUrl)))
1731
1852
  continue;
@@ -2139,7 +2260,6 @@ var RoutstrClient = class {
2139
2260
  body: body === void 0 || method === "GET" ? void 0 : JSON.stringify(body)
2140
2261
  });
2141
2262
  if (this.mode === "xcashu") this._log("DEBUG", "response,", response);
2142
- this._log("DEBUG", "response,", response);
2143
2263
  response.baseUrl = baseUrl;
2144
2264
  response.token = token;
2145
2265
  if (!response.ok) {
@@ -2162,7 +2282,7 @@ var RoutstrClient = class {
2162
2282
  }
2163
2283
  return response;
2164
2284
  } catch (error) {
2165
- if (this._isNetworkError(error?.message || "")) {
2285
+ if (isNetworkErrorMessage(error?.message || "")) {
2166
2286
  return await this._handleErrorResponse(
2167
2287
  params,
2168
2288
  token,
@@ -2297,7 +2417,13 @@ var RoutstrClient = class {
2297
2417
  "DEBUG",
2298
2418
  `[RoutstrClient] _handleErrorResponse: Insufficient balance, need=${required}, have=${available}`
2299
2419
  );
2300
- throw new InsufficientBalanceError(required, available, 0, "", message);
2420
+ throw new InsufficientBalanceError(
2421
+ required,
2422
+ available,
2423
+ 0,
2424
+ "",
2425
+ message
2426
+ );
2301
2427
  } else {
2302
2428
  this._log(
2303
2429
  "DEBUG",
@@ -2510,7 +2636,10 @@ var RoutstrClient = class {
2510
2636
  retryCount: 0
2511
2637
  });
2512
2638
  }
2513
- throw new FailoverError(baseUrl, Array.from(this.providerManager.getFailedProviders()));
2639
+ throw new FailoverError(
2640
+ baseUrl,
2641
+ Array.from(this.providerManager.getFailedProviders())
2642
+ );
2514
2643
  }
2515
2644
  /**
2516
2645
  * Handle post-response balance update for all modes
@@ -2660,12 +2789,6 @@ var RoutstrClient = class {
2660
2789
  const distribution = this.storageAdapter.getCachedTokenDistribution();
2661
2790
  return distribution.reduce((total, item) => total + item.amount, 0);
2662
2791
  }
2663
- /**
2664
- * Check if error message indicates a network error
2665
- */
2666
- _isNetworkError(message) {
2667
- return message.includes("NetworkError when attempting to fetch resource") || message.includes("Failed to fetch") || message.includes("Load failed");
2668
- }
2669
2792
  /**
2670
2793
  * Handle errors and notify callbacks
2671
2794
  */