ton-provider-system 0.1.8 → 0.1.10

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.cjs CHANGED
@@ -952,13 +952,16 @@ var HealthChecker = class {
952
952
  const existing = this.results.get(key);
953
953
  const result = existing ? {
954
954
  ...existing,
955
+ success: true,
956
+ // Degraded providers are still usable, just slower/rate-limited
955
957
  status: "degraded",
956
958
  error: error || "Marked as degraded",
957
959
  lastTested: /* @__PURE__ */ new Date()
958
960
  } : {
959
961
  id: providerId,
960
962
  network,
961
- success: false,
963
+ success: true,
964
+ // Degraded providers are still usable
962
965
  status: "degraded",
963
966
  latencyMs: null,
964
967
  seqno: null,
@@ -1572,6 +1575,8 @@ var ProviderSelector = class {
1572
1575
  this.autoSelect = true;
1573
1576
  this.customEndpoint = null;
1574
1577
  this.bestProviderByNetwork = /* @__PURE__ */ new Map();
1578
+ // Track currently active provider per network (the one actually being used)
1579
+ this.activeProviderByNetwork = /* @__PURE__ */ new Map();
1575
1580
  this.registry = registry;
1576
1581
  this.healthChecker = healthChecker;
1577
1582
  this.config = { ...DEFAULT_CONFIG2, ...config };
@@ -1590,6 +1595,7 @@ var ProviderSelector = class {
1590
1595
  if (!this.autoSelect && this.selectedProviderId) {
1591
1596
  const provider = this.registry.getProvider(this.selectedProviderId);
1592
1597
  if (provider && provider.network === network) {
1598
+ this.activeProviderByNetwork.set(network, provider.id);
1593
1599
  return provider;
1594
1600
  }
1595
1601
  this.logger.warn(
@@ -1600,8 +1606,13 @@ var ProviderSelector = class {
1600
1606
  if (cachedBestId) {
1601
1607
  const cached = this.registry.getProvider(cachedBestId);
1602
1608
  const health = this.healthChecker.getResult(cachedBestId, network);
1603
- if (cached && health && health.success !== false && this.config.minStatus.includes(health.status)) {
1609
+ if (cached && health && health.success !== false && health.success !== void 0 && // Explicitly check for undefined
1610
+ this.config.minStatus.includes(health.status)) {
1611
+ this.activeProviderByNetwork.set(network, cachedBestId);
1604
1612
  return cached;
1613
+ } else {
1614
+ this.bestProviderByNetwork.delete(network);
1615
+ this.activeProviderByNetwork.delete(network);
1605
1616
  }
1606
1617
  }
1607
1618
  return this.findBestProvider(network);
@@ -1623,12 +1634,31 @@ var ProviderSelector = class {
1623
1634
  const defaults = this.registry.getDefaultOrderForNetwork(network);
1624
1635
  for (const defaultProvider of defaults) {
1625
1636
  const health = this.healthChecker.getResult(defaultProvider.id, network);
1626
- if (!health || health.status === "untested" || health.success === true) {
1637
+ if (!health || health.status === "untested") {
1638
+ this.logger.warn(
1639
+ `No healthy providers for ${network}, using untested default: ${defaultProvider.id}`
1640
+ );
1641
+ this.activeProviderByNetwork.set(network, defaultProvider.id);
1642
+ return defaultProvider;
1643
+ }
1644
+ if (health.success === true) {
1627
1645
  this.logger.warn(
1628
1646
  `No healthy providers for ${network}, using default: ${defaultProvider.id}`
1629
1647
  );
1648
+ this.activeProviderByNetwork.set(network, defaultProvider.id);
1630
1649
  return defaultProvider;
1631
1650
  }
1651
+ if (health.success === false && health.lastTested) {
1652
+ const timeSinceFailure = Date.now() - health.lastTested.getTime();
1653
+ const cooldownMs = 3e4;
1654
+ if (timeSinceFailure > cooldownMs) {
1655
+ this.logger.warn(
1656
+ `No healthy providers for ${network}, retrying failed default after cooldown: ${defaultProvider.id}`
1657
+ );
1658
+ this.activeProviderByNetwork.set(network, defaultProvider.id);
1659
+ return defaultProvider;
1660
+ }
1661
+ }
1632
1662
  }
1633
1663
  for (const provider of providers) {
1634
1664
  const health = this.healthChecker.getResult(provider.id, network);
@@ -1636,20 +1666,34 @@ var ProviderSelector = class {
1636
1666
  this.logger.warn(
1637
1667
  `No tested healthy providers for ${network}, using untested: ${provider.id}`
1638
1668
  );
1669
+ this.activeProviderByNetwork.set(network, provider.id);
1639
1670
  return provider;
1640
1671
  }
1672
+ if (health.success === false && health.lastTested) {
1673
+ const timeSinceFailure = Date.now() - health.lastTested.getTime();
1674
+ const cooldownMs = 3e4;
1675
+ if (timeSinceFailure > cooldownMs) {
1676
+ this.logger.warn(
1677
+ `No healthy providers for ${network}, retrying failed provider after cooldown: ${provider.id}`
1678
+ );
1679
+ this.activeProviderByNetwork.set(network, provider.id);
1680
+ return provider;
1681
+ }
1682
+ }
1641
1683
  }
1642
- this.logger.error(`No available providers for ${network} (all tested and failed)`);
1684
+ this.logger.error(`No available providers for ${network} (all tested and failed, cooldown active)`);
1643
1685
  return null;
1644
1686
  }
1645
1687
  const best = scored[0].provider;
1646
1688
  const bestHealth = this.healthChecker.getResult(best.id, network);
1647
1689
  if (bestHealth && bestHealth.success === true) {
1648
1690
  this.bestProviderByNetwork.set(network, best.id);
1691
+ this.activeProviderByNetwork.set(network, best.id);
1649
1692
  this.logger.debug(
1650
1693
  `Best provider for ${network}: ${best.id} (score: ${scored[0].score.toFixed(2)})`
1651
1694
  );
1652
1695
  } else {
1696
+ this.activeProviderByNetwork.set(network, best.id);
1653
1697
  this.logger.debug(
1654
1698
  `Best provider for ${network}: ${best.id} (score: ${scored[0].score.toFixed(2)}, untested)`
1655
1699
  );
@@ -1692,6 +1736,13 @@ var ProviderSelector = class {
1692
1736
  return 0.01 * (1 / (provider.priority + 1));
1693
1737
  }
1694
1738
  if (health.success === false) {
1739
+ if (health.lastTested) {
1740
+ const timeSinceFailure = Date.now() - health.lastTested.getTime();
1741
+ const cooldownMs = 3e4;
1742
+ if (timeSinceFailure > cooldownMs) {
1743
+ return 1e-3 * (1 / (provider.priority + 1));
1744
+ }
1745
+ }
1695
1746
  return 0;
1696
1747
  }
1697
1748
  if (health.status === "offline") {
@@ -1789,9 +1840,16 @@ var ProviderSelector = class {
1789
1840
  }
1790
1841
  /**
1791
1842
  * Clear cached best providers (forces recalculation)
1843
+ * @param network - Optional network to clear cache for. If not provided, clears all networks.
1792
1844
  */
1793
- clearCache() {
1794
- this.bestProviderByNetwork.clear();
1845
+ clearCache(network) {
1846
+ if (network) {
1847
+ this.bestProviderByNetwork.delete(network);
1848
+ this.activeProviderByNetwork.delete(network);
1849
+ } else {
1850
+ this.bestProviderByNetwork.clear();
1851
+ this.activeProviderByNetwork.clear();
1852
+ }
1795
1853
  }
1796
1854
  /**
1797
1855
  * Update best provider after health check
@@ -1806,8 +1864,16 @@ var ProviderSelector = class {
1806
1864
  if (this.bestProviderByNetwork.get(network) === providerId) {
1807
1865
  this.bestProviderByNetwork.delete(network);
1808
1866
  }
1867
+ this.activeProviderByNetwork.delete(network);
1809
1868
  return this.getNextProvider(network, [providerId]);
1810
1869
  }
1870
+ /**
1871
+ * Get the currently active provider ID for a network
1872
+ * (the one that was last selected and is being used)
1873
+ */
1874
+ getActiveProviderId(network) {
1875
+ return this.activeProviderByNetwork.get(network) || null;
1876
+ }
1811
1877
  // ========================================================================
1812
1878
  // Info
1813
1879
  // ========================================================================
@@ -2044,6 +2110,7 @@ var _ProviderManager = class _ProviderManager {
2044
2110
  this.options.logger.warn("No providers available, using fallback");
2045
2111
  return this.getFallbackEndpoint();
2046
2112
  }
2113
+ this.selector.getActiveProviderId(this.network) || this.selector.getBestProvider(this.network);
2047
2114
  if (provider.isDynamic && provider.type === "orbs") {
2048
2115
  try {
2049
2116
  const { getHttpEndpoint } = await import('@orbs-network/ton-access');
@@ -2059,9 +2126,14 @@ var _ProviderManager = class _ProviderManager {
2059
2126
  * Get endpoint with rate limiting
2060
2127
  *
2061
2128
  * Waits for rate limit token before returning endpoint.
2129
+ *
2130
+ * @param forceRefresh - If true, clears cache and forces re-selection
2062
2131
  */
2063
- async getEndpointWithRateLimit(timeoutMs) {
2132
+ async getEndpointWithRateLimit(timeoutMs, forceRefresh = false) {
2064
2133
  this.ensureInitialized();
2134
+ if (forceRefresh) {
2135
+ this.selector.clearCache(this.network);
2136
+ }
2065
2137
  const provider = this.selector.getBestProvider(this.network);
2066
2138
  if (!provider) {
2067
2139
  return this.getFallbackEndpoint();
@@ -2113,7 +2185,14 @@ var _ProviderManager = class _ProviderManager {
2113
2185
  */
2114
2186
  reportError(error) {
2115
2187
  if (!this.initialized || !this.network) return;
2116
- const provider = this.selector.getBestProvider(this.network);
2188
+ const activeProviderId = this.selector.getActiveProviderId(this.network);
2189
+ let provider = null;
2190
+ if (activeProviderId) {
2191
+ provider = this.registry.getProvider(activeProviderId) || null;
2192
+ }
2193
+ if (!provider) {
2194
+ provider = this.selector.getBestProvider(this.network);
2195
+ }
2117
2196
  if (!provider) return;
2118
2197
  const errorMsg = error instanceof Error ? error.message : String(error);
2119
2198
  const errorMsgLower = errorMsg.toLowerCase();
@@ -2132,6 +2211,7 @@ var _ProviderManager = class _ProviderManager {
2132
2211
  this.rateLimiter.reportError(provider.id);
2133
2212
  this.healthChecker.markDegraded(provider.id, this.network, errorMsg);
2134
2213
  }
2214
+ this.selector.clearCache(this.network);
2135
2215
  this.selector.handleProviderFailure(provider.id, this.network);
2136
2216
  this.notifyListeners();
2137
2217
  }