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.d.cts CHANGED
@@ -1082,6 +1082,7 @@ declare class ProviderSelector {
1082
1082
  private autoSelect;
1083
1083
  private customEndpoint;
1084
1084
  private bestProviderByNetwork;
1085
+ private activeProviderByNetwork;
1085
1086
  constructor(registry: ProviderRegistry, healthChecker: HealthChecker, config?: Partial<SelectionConfig>, logger?: Logger);
1086
1087
  /**
1087
1088
  * Get the best provider for a network
@@ -1137,8 +1138,9 @@ declare class ProviderSelector {
1137
1138
  isUsingCustomEndpoint(): boolean;
1138
1139
  /**
1139
1140
  * Clear cached best providers (forces recalculation)
1141
+ * @param network - Optional network to clear cache for. If not provided, clears all networks.
1140
1142
  */
1141
- clearCache(): void;
1143
+ clearCache(network?: Network): void;
1142
1144
  /**
1143
1145
  * Update best provider after health check
1144
1146
  */
@@ -1147,6 +1149,11 @@ declare class ProviderSelector {
1147
1149
  * Handle provider failure (switch to next best)
1148
1150
  */
1149
1151
  handleProviderFailure(providerId: string, network: Network): ResolvedProvider | null;
1152
+ /**
1153
+ * Get the currently active provider ID for a network
1154
+ * (the one that was last selected and is being used)
1155
+ */
1156
+ getActiveProviderId(network: Network): string | null;
1150
1157
  /**
1151
1158
  * Get active provider info
1152
1159
  */
@@ -1248,8 +1255,10 @@ declare class ProviderManager {
1248
1255
  * Get endpoint with rate limiting
1249
1256
  *
1250
1257
  * Waits for rate limit token before returning endpoint.
1258
+ *
1259
+ * @param forceRefresh - If true, clears cache and forces re-selection
1251
1260
  */
1252
- getEndpointWithRateLimit(timeoutMs?: number): Promise<string>;
1261
+ getEndpointWithRateLimit(timeoutMs?: number, forceRefresh?: boolean): Promise<string>;
1253
1262
  /**
1254
1263
  * Get current active provider
1255
1264
  */
package/dist/index.d.ts CHANGED
@@ -1082,6 +1082,7 @@ declare class ProviderSelector {
1082
1082
  private autoSelect;
1083
1083
  private customEndpoint;
1084
1084
  private bestProviderByNetwork;
1085
+ private activeProviderByNetwork;
1085
1086
  constructor(registry: ProviderRegistry, healthChecker: HealthChecker, config?: Partial<SelectionConfig>, logger?: Logger);
1086
1087
  /**
1087
1088
  * Get the best provider for a network
@@ -1137,8 +1138,9 @@ declare class ProviderSelector {
1137
1138
  isUsingCustomEndpoint(): boolean;
1138
1139
  /**
1139
1140
  * Clear cached best providers (forces recalculation)
1141
+ * @param network - Optional network to clear cache for. If not provided, clears all networks.
1140
1142
  */
1141
- clearCache(): void;
1143
+ clearCache(network?: Network): void;
1142
1144
  /**
1143
1145
  * Update best provider after health check
1144
1146
  */
@@ -1147,6 +1149,11 @@ declare class ProviderSelector {
1147
1149
  * Handle provider failure (switch to next best)
1148
1150
  */
1149
1151
  handleProviderFailure(providerId: string, network: Network): ResolvedProvider | null;
1152
+ /**
1153
+ * Get the currently active provider ID for a network
1154
+ * (the one that was last selected and is being used)
1155
+ */
1156
+ getActiveProviderId(network: Network): string | null;
1150
1157
  /**
1151
1158
  * Get active provider info
1152
1159
  */
@@ -1248,8 +1255,10 @@ declare class ProviderManager {
1248
1255
  * Get endpoint with rate limiting
1249
1256
  *
1250
1257
  * Waits for rate limit token before returning endpoint.
1258
+ *
1259
+ * @param forceRefresh - If true, clears cache and forces re-selection
1251
1260
  */
1252
- getEndpointWithRateLimit(timeoutMs?: number): Promise<string>;
1261
+ getEndpointWithRateLimit(timeoutMs?: number, forceRefresh?: boolean): Promise<string>;
1253
1262
  /**
1254
1263
  * Get current active provider
1255
1264
  */
package/dist/index.js CHANGED
@@ -947,14 +947,25 @@ var HealthChecker = class {
947
947
  markDegraded(providerId, network, error) {
948
948
  const key = this.getResultKey(providerId, network);
949
949
  const existing = this.results.get(key);
950
- if (existing) {
951
- this.results.set(key, {
952
- ...existing,
953
- status: "degraded",
954
- error: error || "Marked as degraded",
955
- lastTested: /* @__PURE__ */ new Date()
956
- });
957
- }
950
+ const result = existing ? {
951
+ ...existing,
952
+ success: false,
953
+ // CRITICAL: Always set success: false for degraded providers
954
+ status: "degraded",
955
+ error: error || "Marked as degraded",
956
+ lastTested: /* @__PURE__ */ new Date()
957
+ } : {
958
+ id: providerId,
959
+ network,
960
+ success: false,
961
+ status: "degraded",
962
+ latencyMs: null,
963
+ seqno: null,
964
+ blocksBehind: 0,
965
+ lastTested: /* @__PURE__ */ new Date(),
966
+ error: error || "Marked as degraded"
967
+ };
968
+ this.results.set(key, result);
958
969
  }
959
970
  /**
960
971
  * Mark a provider as offline
@@ -962,14 +973,25 @@ var HealthChecker = class {
962
973
  markOffline(providerId, network, error) {
963
974
  const key = this.getResultKey(providerId, network);
964
975
  const existing = this.results.get(key);
965
- if (existing) {
966
- this.results.set(key, {
967
- ...existing,
968
- status: "offline",
969
- error: error || "Marked as offline",
970
- lastTested: /* @__PURE__ */ new Date()
971
- });
972
- }
976
+ const result = existing ? {
977
+ ...existing,
978
+ status: "offline",
979
+ success: false,
980
+ // Ensure success is false for offline providers
981
+ error: error || "Marked as offline",
982
+ lastTested: /* @__PURE__ */ new Date()
983
+ } : {
984
+ id: providerId,
985
+ network,
986
+ success: false,
987
+ status: "offline",
988
+ latencyMs: null,
989
+ seqno: null,
990
+ blocksBehind: 0,
991
+ lastTested: /* @__PURE__ */ new Date(),
992
+ error: error || "Marked as offline"
993
+ };
994
+ this.results.set(key, result);
973
995
  }
974
996
  // ========================================================================
975
997
  // Private Methods
@@ -1549,6 +1571,8 @@ var ProviderSelector = class {
1549
1571
  this.autoSelect = true;
1550
1572
  this.customEndpoint = null;
1551
1573
  this.bestProviderByNetwork = /* @__PURE__ */ new Map();
1574
+ // Track currently active provider per network (the one actually being used)
1575
+ this.activeProviderByNetwork = /* @__PURE__ */ new Map();
1552
1576
  this.registry = registry;
1553
1577
  this.healthChecker = healthChecker;
1554
1578
  this.config = { ...DEFAULT_CONFIG2, ...config };
@@ -1567,6 +1591,7 @@ var ProviderSelector = class {
1567
1591
  if (!this.autoSelect && this.selectedProviderId) {
1568
1592
  const provider = this.registry.getProvider(this.selectedProviderId);
1569
1593
  if (provider && provider.network === network) {
1594
+ this.activeProviderByNetwork.set(network, provider.id);
1570
1595
  return provider;
1571
1596
  }
1572
1597
  this.logger.warn(
@@ -1577,8 +1602,13 @@ var ProviderSelector = class {
1577
1602
  if (cachedBestId) {
1578
1603
  const cached = this.registry.getProvider(cachedBestId);
1579
1604
  const health = this.healthChecker.getResult(cachedBestId, network);
1580
- if (cached && health && health.success !== false && this.config.minStatus.includes(health.status)) {
1605
+ if (cached && health && health.success !== false && health.success !== void 0 && // Explicitly check for undefined
1606
+ this.config.minStatus.includes(health.status)) {
1607
+ this.activeProviderByNetwork.set(network, cachedBestId);
1581
1608
  return cached;
1609
+ } else {
1610
+ this.bestProviderByNetwork.delete(network);
1611
+ this.activeProviderByNetwork.delete(network);
1582
1612
  }
1583
1613
  }
1584
1614
  return this.findBestProvider(network);
@@ -1600,10 +1630,17 @@ var ProviderSelector = class {
1600
1630
  const defaults = this.registry.getDefaultOrderForNetwork(network);
1601
1631
  for (const defaultProvider of defaults) {
1602
1632
  const health = this.healthChecker.getResult(defaultProvider.id, network);
1603
- if (!health || health.status === "untested" || health.success === true) {
1633
+ if (!health || health.status === "untested") {
1634
+ this.logger.warn(
1635
+ `No healthy providers for ${network}, using untested default: ${defaultProvider.id}`
1636
+ );
1637
+ this.activeProviderByNetwork.set(network, defaultProvider.id);
1638
+ return defaultProvider;
1639
+ } else if (health.success === true) {
1604
1640
  this.logger.warn(
1605
1641
  `No healthy providers for ${network}, using default: ${defaultProvider.id}`
1606
1642
  );
1643
+ this.activeProviderByNetwork.set(network, defaultProvider.id);
1607
1644
  return defaultProvider;
1608
1645
  }
1609
1646
  }
@@ -1613,6 +1650,7 @@ var ProviderSelector = class {
1613
1650
  this.logger.warn(
1614
1651
  `No tested healthy providers for ${network}, using untested: ${provider.id}`
1615
1652
  );
1653
+ this.activeProviderByNetwork.set(network, provider.id);
1616
1654
  return provider;
1617
1655
  }
1618
1656
  }
@@ -1623,10 +1661,12 @@ var ProviderSelector = class {
1623
1661
  const bestHealth = this.healthChecker.getResult(best.id, network);
1624
1662
  if (bestHealth && bestHealth.success === true) {
1625
1663
  this.bestProviderByNetwork.set(network, best.id);
1664
+ this.activeProviderByNetwork.set(network, best.id);
1626
1665
  this.logger.debug(
1627
1666
  `Best provider for ${network}: ${best.id} (score: ${scored[0].score.toFixed(2)})`
1628
1667
  );
1629
1668
  } else {
1669
+ this.activeProviderByNetwork.set(network, best.id);
1630
1670
  this.logger.debug(
1631
1671
  `Best provider for ${network}: ${best.id} (score: ${scored[0].score.toFixed(2)}, untested)`
1632
1672
  );
@@ -1766,9 +1806,16 @@ var ProviderSelector = class {
1766
1806
  }
1767
1807
  /**
1768
1808
  * Clear cached best providers (forces recalculation)
1809
+ * @param network - Optional network to clear cache for. If not provided, clears all networks.
1769
1810
  */
1770
- clearCache() {
1771
- this.bestProviderByNetwork.clear();
1811
+ clearCache(network) {
1812
+ if (network) {
1813
+ this.bestProviderByNetwork.delete(network);
1814
+ this.activeProviderByNetwork.delete(network);
1815
+ } else {
1816
+ this.bestProviderByNetwork.clear();
1817
+ this.activeProviderByNetwork.clear();
1818
+ }
1772
1819
  }
1773
1820
  /**
1774
1821
  * Update best provider after health check
@@ -1783,8 +1830,16 @@ var ProviderSelector = class {
1783
1830
  if (this.bestProviderByNetwork.get(network) === providerId) {
1784
1831
  this.bestProviderByNetwork.delete(network);
1785
1832
  }
1833
+ this.activeProviderByNetwork.delete(network);
1786
1834
  return this.getNextProvider(network, [providerId]);
1787
1835
  }
1836
+ /**
1837
+ * Get the currently active provider ID for a network
1838
+ * (the one that was last selected and is being used)
1839
+ */
1840
+ getActiveProviderId(network) {
1841
+ return this.activeProviderByNetwork.get(network) || null;
1842
+ }
1788
1843
  // ========================================================================
1789
1844
  // Info
1790
1845
  // ========================================================================
@@ -2021,6 +2076,7 @@ var _ProviderManager = class _ProviderManager {
2021
2076
  this.options.logger.warn("No providers available, using fallback");
2022
2077
  return this.getFallbackEndpoint();
2023
2078
  }
2079
+ this.selector.getActiveProviderId(this.network) || this.selector.getBestProvider(this.network);
2024
2080
  if (provider.isDynamic && provider.type === "orbs") {
2025
2081
  try {
2026
2082
  const { getHttpEndpoint } = await import('@orbs-network/ton-access');
@@ -2036,9 +2092,14 @@ var _ProviderManager = class _ProviderManager {
2036
2092
  * Get endpoint with rate limiting
2037
2093
  *
2038
2094
  * Waits for rate limit token before returning endpoint.
2095
+ *
2096
+ * @param forceRefresh - If true, clears cache and forces re-selection
2039
2097
  */
2040
- async getEndpointWithRateLimit(timeoutMs) {
2098
+ async getEndpointWithRateLimit(timeoutMs, forceRefresh = false) {
2041
2099
  this.ensureInitialized();
2100
+ if (forceRefresh) {
2101
+ this.selector.clearCache(this.network);
2102
+ }
2042
2103
  const provider = this.selector.getBestProvider(this.network);
2043
2104
  if (!provider) {
2044
2105
  return this.getFallbackEndpoint();
@@ -2090,15 +2151,33 @@ var _ProviderManager = class _ProviderManager {
2090
2151
  */
2091
2152
  reportError(error) {
2092
2153
  if (!this.initialized || !this.network) return;
2093
- const provider = this.selector.getBestProvider(this.network);
2154
+ const activeProviderId = this.selector.getActiveProviderId(this.network);
2155
+ let provider = null;
2156
+ if (activeProviderId) {
2157
+ provider = this.registry.getProvider(activeProviderId) || null;
2158
+ }
2159
+ if (!provider) {
2160
+ provider = this.selector.getBestProvider(this.network);
2161
+ }
2094
2162
  if (!provider) return;
2095
- const errorMsg = error instanceof Error ? error.message : error;
2096
- if (isRateLimitError(error)) {
2163
+ const errorMsg = error instanceof Error ? error.message : String(error);
2164
+ const errorMsgLower = errorMsg.toLowerCase();
2165
+ const is429 = errorMsgLower.includes("429") || errorMsgLower.includes("rate limit");
2166
+ const is503 = errorMsgLower.includes("503") || errorMsgLower.includes("service unavailable");
2167
+ const is502 = errorMsgLower.includes("502") || errorMsgLower.includes("bad gateway");
2168
+ const is404 = errorMsgLower.includes("404") || errorMsgLower.includes("not found");
2169
+ const isTimeout = errorMsgLower.includes("timeout") || errorMsgLower.includes("abort");
2170
+ if (isRateLimitError(error) || is429) {
2097
2171
  this.rateLimiter.reportRateLimitError(provider.id);
2098
2172
  this.healthChecker.markDegraded(provider.id, this.network, errorMsg);
2173
+ } else if (is503 || is502 || is404 || isTimeout) {
2174
+ this.rateLimiter.reportError(provider.id);
2175
+ this.healthChecker.markOffline(provider.id, this.network, errorMsg);
2099
2176
  } else {
2100
2177
  this.rateLimiter.reportError(provider.id);
2178
+ this.healthChecker.markDegraded(provider.id, this.network, errorMsg);
2101
2179
  }
2180
+ this.selector.clearCache(this.network);
2102
2181
  this.selector.handleProviderFailure(provider.id, this.network);
2103
2182
  this.notifyListeners();
2104
2183
  }