ton-provider-system 0.1.7 → 0.1.9

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
@@ -950,14 +950,25 @@ var HealthChecker = class {
950
950
  markDegraded(providerId, network, error) {
951
951
  const key = this.getResultKey(providerId, network);
952
952
  const existing = this.results.get(key);
953
- if (existing) {
954
- this.results.set(key, {
955
- ...existing,
956
- status: "degraded",
957
- error: error || "Marked as degraded",
958
- lastTested: /* @__PURE__ */ new Date()
959
- });
960
- }
953
+ const result = existing ? {
954
+ ...existing,
955
+ success: false,
956
+ // CRITICAL: Always set success: false for degraded providers
957
+ status: "degraded",
958
+ error: error || "Marked as degraded",
959
+ lastTested: /* @__PURE__ */ new Date()
960
+ } : {
961
+ id: providerId,
962
+ network,
963
+ success: false,
964
+ status: "degraded",
965
+ latencyMs: null,
966
+ seqno: null,
967
+ blocksBehind: 0,
968
+ lastTested: /* @__PURE__ */ new Date(),
969
+ error: error || "Marked as degraded"
970
+ };
971
+ this.results.set(key, result);
961
972
  }
962
973
  /**
963
974
  * Mark a provider as offline
@@ -965,14 +976,25 @@ var HealthChecker = class {
965
976
  markOffline(providerId, network, error) {
966
977
  const key = this.getResultKey(providerId, network);
967
978
  const existing = this.results.get(key);
968
- if (existing) {
969
- this.results.set(key, {
970
- ...existing,
971
- status: "offline",
972
- error: error || "Marked as offline",
973
- lastTested: /* @__PURE__ */ new Date()
974
- });
975
- }
979
+ const result = existing ? {
980
+ ...existing,
981
+ status: "offline",
982
+ success: false,
983
+ // Ensure success is false for offline providers
984
+ error: error || "Marked as offline",
985
+ lastTested: /* @__PURE__ */ new Date()
986
+ } : {
987
+ id: providerId,
988
+ network,
989
+ success: false,
990
+ status: "offline",
991
+ latencyMs: null,
992
+ seqno: null,
993
+ blocksBehind: 0,
994
+ lastTested: /* @__PURE__ */ new Date(),
995
+ error: error || "Marked as offline"
996
+ };
997
+ this.results.set(key, result);
976
998
  }
977
999
  // ========================================================================
978
1000
  // Private Methods
@@ -1552,6 +1574,8 @@ var ProviderSelector = class {
1552
1574
  this.autoSelect = true;
1553
1575
  this.customEndpoint = null;
1554
1576
  this.bestProviderByNetwork = /* @__PURE__ */ new Map();
1577
+ // Track currently active provider per network (the one actually being used)
1578
+ this.activeProviderByNetwork = /* @__PURE__ */ new Map();
1555
1579
  this.registry = registry;
1556
1580
  this.healthChecker = healthChecker;
1557
1581
  this.config = { ...DEFAULT_CONFIG2, ...config };
@@ -1570,6 +1594,7 @@ var ProviderSelector = class {
1570
1594
  if (!this.autoSelect && this.selectedProviderId) {
1571
1595
  const provider = this.registry.getProvider(this.selectedProviderId);
1572
1596
  if (provider && provider.network === network) {
1597
+ this.activeProviderByNetwork.set(network, provider.id);
1573
1598
  return provider;
1574
1599
  }
1575
1600
  this.logger.warn(
@@ -1580,8 +1605,13 @@ var ProviderSelector = class {
1580
1605
  if (cachedBestId) {
1581
1606
  const cached = this.registry.getProvider(cachedBestId);
1582
1607
  const health = this.healthChecker.getResult(cachedBestId, network);
1583
- if (cached && health && health.success !== false && this.config.minStatus.includes(health.status)) {
1608
+ if (cached && health && health.success !== false && health.success !== void 0 && // Explicitly check for undefined
1609
+ this.config.minStatus.includes(health.status)) {
1610
+ this.activeProviderByNetwork.set(network, cachedBestId);
1584
1611
  return cached;
1612
+ } else {
1613
+ this.bestProviderByNetwork.delete(network);
1614
+ this.activeProviderByNetwork.delete(network);
1585
1615
  }
1586
1616
  }
1587
1617
  return this.findBestProvider(network);
@@ -1603,10 +1633,17 @@ var ProviderSelector = class {
1603
1633
  const defaults = this.registry.getDefaultOrderForNetwork(network);
1604
1634
  for (const defaultProvider of defaults) {
1605
1635
  const health = this.healthChecker.getResult(defaultProvider.id, network);
1606
- if (!health || health.status === "untested" || health.success === true) {
1636
+ if (!health || health.status === "untested") {
1637
+ this.logger.warn(
1638
+ `No healthy providers for ${network}, using untested default: ${defaultProvider.id}`
1639
+ );
1640
+ this.activeProviderByNetwork.set(network, defaultProvider.id);
1641
+ return defaultProvider;
1642
+ } else if (health.success === true) {
1607
1643
  this.logger.warn(
1608
1644
  `No healthy providers for ${network}, using default: ${defaultProvider.id}`
1609
1645
  );
1646
+ this.activeProviderByNetwork.set(network, defaultProvider.id);
1610
1647
  return defaultProvider;
1611
1648
  }
1612
1649
  }
@@ -1616,6 +1653,7 @@ var ProviderSelector = class {
1616
1653
  this.logger.warn(
1617
1654
  `No tested healthy providers for ${network}, using untested: ${provider.id}`
1618
1655
  );
1656
+ this.activeProviderByNetwork.set(network, provider.id);
1619
1657
  return provider;
1620
1658
  }
1621
1659
  }
@@ -1626,10 +1664,12 @@ var ProviderSelector = class {
1626
1664
  const bestHealth = this.healthChecker.getResult(best.id, network);
1627
1665
  if (bestHealth && bestHealth.success === true) {
1628
1666
  this.bestProviderByNetwork.set(network, best.id);
1667
+ this.activeProviderByNetwork.set(network, best.id);
1629
1668
  this.logger.debug(
1630
1669
  `Best provider for ${network}: ${best.id} (score: ${scored[0].score.toFixed(2)})`
1631
1670
  );
1632
1671
  } else {
1672
+ this.activeProviderByNetwork.set(network, best.id);
1633
1673
  this.logger.debug(
1634
1674
  `Best provider for ${network}: ${best.id} (score: ${scored[0].score.toFixed(2)}, untested)`
1635
1675
  );
@@ -1769,9 +1809,16 @@ var ProviderSelector = class {
1769
1809
  }
1770
1810
  /**
1771
1811
  * Clear cached best providers (forces recalculation)
1812
+ * @param network - Optional network to clear cache for. If not provided, clears all networks.
1772
1813
  */
1773
- clearCache() {
1774
- this.bestProviderByNetwork.clear();
1814
+ clearCache(network) {
1815
+ if (network) {
1816
+ this.bestProviderByNetwork.delete(network);
1817
+ this.activeProviderByNetwork.delete(network);
1818
+ } else {
1819
+ this.bestProviderByNetwork.clear();
1820
+ this.activeProviderByNetwork.clear();
1821
+ }
1775
1822
  }
1776
1823
  /**
1777
1824
  * Update best provider after health check
@@ -1786,8 +1833,16 @@ var ProviderSelector = class {
1786
1833
  if (this.bestProviderByNetwork.get(network) === providerId) {
1787
1834
  this.bestProviderByNetwork.delete(network);
1788
1835
  }
1836
+ this.activeProviderByNetwork.delete(network);
1789
1837
  return this.getNextProvider(network, [providerId]);
1790
1838
  }
1839
+ /**
1840
+ * Get the currently active provider ID for a network
1841
+ * (the one that was last selected and is being used)
1842
+ */
1843
+ getActiveProviderId(network) {
1844
+ return this.activeProviderByNetwork.get(network) || null;
1845
+ }
1791
1846
  // ========================================================================
1792
1847
  // Info
1793
1848
  // ========================================================================
@@ -2024,6 +2079,7 @@ var _ProviderManager = class _ProviderManager {
2024
2079
  this.options.logger.warn("No providers available, using fallback");
2025
2080
  return this.getFallbackEndpoint();
2026
2081
  }
2082
+ this.selector.getActiveProviderId(this.network) || this.selector.getBestProvider(this.network);
2027
2083
  if (provider.isDynamic && provider.type === "orbs") {
2028
2084
  try {
2029
2085
  const { getHttpEndpoint } = await import('@orbs-network/ton-access');
@@ -2039,9 +2095,14 @@ var _ProviderManager = class _ProviderManager {
2039
2095
  * Get endpoint with rate limiting
2040
2096
  *
2041
2097
  * Waits for rate limit token before returning endpoint.
2098
+ *
2099
+ * @param forceRefresh - If true, clears cache and forces re-selection
2042
2100
  */
2043
- async getEndpointWithRateLimit(timeoutMs) {
2101
+ async getEndpointWithRateLimit(timeoutMs, forceRefresh = false) {
2044
2102
  this.ensureInitialized();
2103
+ if (forceRefresh) {
2104
+ this.selector.clearCache(this.network);
2105
+ }
2045
2106
  const provider = this.selector.getBestProvider(this.network);
2046
2107
  if (!provider) {
2047
2108
  return this.getFallbackEndpoint();
@@ -2093,15 +2154,33 @@ var _ProviderManager = class _ProviderManager {
2093
2154
  */
2094
2155
  reportError(error) {
2095
2156
  if (!this.initialized || !this.network) return;
2096
- const provider = this.selector.getBestProvider(this.network);
2157
+ const activeProviderId = this.selector.getActiveProviderId(this.network);
2158
+ let provider = null;
2159
+ if (activeProviderId) {
2160
+ provider = this.registry.getProvider(activeProviderId) || null;
2161
+ }
2162
+ if (!provider) {
2163
+ provider = this.selector.getBestProvider(this.network);
2164
+ }
2097
2165
  if (!provider) return;
2098
- const errorMsg = error instanceof Error ? error.message : error;
2099
- if (isRateLimitError(error)) {
2166
+ const errorMsg = error instanceof Error ? error.message : String(error);
2167
+ const errorMsgLower = errorMsg.toLowerCase();
2168
+ const is429 = errorMsgLower.includes("429") || errorMsgLower.includes("rate limit");
2169
+ const is503 = errorMsgLower.includes("503") || errorMsgLower.includes("service unavailable");
2170
+ const is502 = errorMsgLower.includes("502") || errorMsgLower.includes("bad gateway");
2171
+ const is404 = errorMsgLower.includes("404") || errorMsgLower.includes("not found");
2172
+ const isTimeout = errorMsgLower.includes("timeout") || errorMsgLower.includes("abort");
2173
+ if (isRateLimitError(error) || is429) {
2100
2174
  this.rateLimiter.reportRateLimitError(provider.id);
2101
2175
  this.healthChecker.markDegraded(provider.id, this.network, errorMsg);
2176
+ } else if (is503 || is502 || is404 || isTimeout) {
2177
+ this.rateLimiter.reportError(provider.id);
2178
+ this.healthChecker.markOffline(provider.id, this.network, errorMsg);
2102
2179
  } else {
2103
2180
  this.rateLimiter.reportError(provider.id);
2181
+ this.healthChecker.markDegraded(provider.id, this.network, errorMsg);
2104
2182
  }
2183
+ this.selector.clearCache(this.network);
2105
2184
  this.selector.handleProviderFailure(provider.id, this.network);
2106
2185
  this.notifyListeners();
2107
2186
  }