ton-provider-system 0.1.9 → 0.1.11

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
@@ -217,7 +217,8 @@ function resolveProvider(id, config) {
217
217
  apiKey,
218
218
  rps: config.rps,
219
219
  priority: config.priority,
220
- isDynamic: config.isDynamic || false
220
+ isDynamic: config.isDynamic || false,
221
+ browserCompatible: config.browserCompatible !== void 0 ? config.browserCompatible : true
221
222
  };
222
223
  }
223
224
  function resolveAllProviders(config) {
@@ -776,7 +777,8 @@ var HealthChecker = class {
776
777
  latencyMs: null,
777
778
  seqno: null,
778
779
  blocksBehind: 0,
779
- lastTested: null
780
+ lastTested: null,
781
+ browserCompatible: provider.browserCompatible
780
782
  };
781
783
  this.results.set(key, testingResult);
782
784
  try {
@@ -836,7 +838,8 @@ var HealthChecker = class {
836
838
  seqno,
837
839
  blocksBehind,
838
840
  lastTested: /* @__PURE__ */ new Date(),
839
- cachedEndpoint: normalizedEndpoint
841
+ cachedEndpoint: normalizedEndpoint,
842
+ browserCompatible: provider.browserCompatible
840
843
  };
841
844
  this.results.set(key, result);
842
845
  this.logger.debug(
@@ -847,6 +850,7 @@ var HealthChecker = class {
847
850
  const endTime = performance.now();
848
851
  const latencyMs = Math.round(endTime - startTime);
849
852
  const errorMsg = error.message || String(error) || "Unknown error";
853
+ const isCorsError = this.isCorsError(error, errorMsg);
850
854
  const is429 = errorMsg.includes("429") || errorMsg.toLowerCase().includes("rate limit");
851
855
  const is404 = errorMsg.includes("404") || errorMsg.toLowerCase().includes("not found");
852
856
  const is503 = errorMsg.includes("503") || errorMsg.toLowerCase().includes("service unavailable");
@@ -861,6 +865,7 @@ var HealthChecker = class {
861
865
  } else if (isTimeout) {
862
866
  status = "offline";
863
867
  }
868
+ const browserCompatible = isCorsError ? false : provider.browserCompatible;
864
869
  const result = {
865
870
  id: provider.id,
866
871
  network: provider.network,
@@ -870,7 +875,8 @@ var HealthChecker = class {
870
875
  seqno: null,
871
876
  blocksBehind: 0,
872
877
  lastTested: /* @__PURE__ */ new Date(),
873
- error: errorMsg
878
+ error: errorMsg,
879
+ browserCompatible
874
880
  };
875
881
  this.results.set(key, result);
876
882
  this.logger.warn(`Provider ${provider.id} health check failed: ${result.error}`);
@@ -952,21 +958,25 @@ var HealthChecker = class {
952
958
  const existing = this.results.get(key);
953
959
  const result = existing ? {
954
960
  ...existing,
955
- success: false,
956
- // CRITICAL: Always set success: false for degraded providers
961
+ success: true,
962
+ // Degraded providers are still usable, just slower/rate-limited
957
963
  status: "degraded",
958
964
  error: error || "Marked as degraded",
959
- lastTested: /* @__PURE__ */ new Date()
965
+ lastTested: /* @__PURE__ */ new Date(),
966
+ browserCompatible: existing.browserCompatible ?? true
960
967
  } : {
961
968
  id: providerId,
962
969
  network,
963
- success: false,
970
+ success: true,
971
+ // Degraded providers are still usable
964
972
  status: "degraded",
965
973
  latencyMs: null,
966
974
  seqno: null,
967
975
  blocksBehind: 0,
968
976
  lastTested: /* @__PURE__ */ new Date(),
969
- error: error || "Marked as degraded"
977
+ error: error || "Marked as degraded",
978
+ browserCompatible: true
979
+ // Default to compatible if unknown
970
980
  };
971
981
  this.results.set(key, result);
972
982
  }
@@ -982,7 +992,8 @@ var HealthChecker = class {
982
992
  success: false,
983
993
  // Ensure success is false for offline providers
984
994
  error: error || "Marked as offline",
985
- lastTested: /* @__PURE__ */ new Date()
995
+ lastTested: /* @__PURE__ */ new Date(),
996
+ browserCompatible: existing.browserCompatible ?? true
986
997
  } : {
987
998
  id: providerId,
988
999
  network,
@@ -992,7 +1003,9 @@ var HealthChecker = class {
992
1003
  seqno: null,
993
1004
  blocksBehind: 0,
994
1005
  lastTested: /* @__PURE__ */ new Date(),
995
- error: error || "Marked as offline"
1006
+ error: error || "Marked as offline",
1007
+ browserCompatible: true
1008
+ // Default to compatible if unknown
996
1009
  };
997
1010
  this.results.set(key, result);
998
1011
  }
@@ -1124,6 +1137,24 @@ var HealthChecker = class {
1124
1137
  sleep(ms) {
1125
1138
  return new Promise((resolve) => setTimeout(resolve, ms));
1126
1139
  }
1140
+ /**
1141
+ * Detect CORS errors (browser compatibility issues)
1142
+ *
1143
+ * CORS errors occur when:
1144
+ * - Request header field is not allowed by Access-Control-Allow-Headers
1145
+ * - Specifically, x-ton-client-version header is blocked by some providers
1146
+ * - Error message contains "CORS", "Access-Control", or "x-ton-client-version"
1147
+ */
1148
+ isCorsError(error, errorMsg) {
1149
+ const msg = errorMsg.toLowerCase();
1150
+ if (msg.includes("cors") || msg.includes("access-control") || msg.includes("x-ton-client-version") || msg.includes("not allowed by access-control-allow-headers") || msg.includes("blocked by cors policy")) {
1151
+ return true;
1152
+ }
1153
+ if (error.name === "TypeError" && (msg.includes("failed to fetch") || msg.includes("network error"))) {
1154
+ return false;
1155
+ }
1156
+ return false;
1157
+ }
1127
1158
  };
1128
1159
  function createHealthChecker(config, logger, rateLimiter) {
1129
1160
  return new HealthChecker(config, logger, rateLimiter);
@@ -1568,7 +1599,7 @@ var DEFAULT_CONFIG2 = {
1568
1599
  minStatus: ["available", "degraded"]
1569
1600
  };
1570
1601
  var ProviderSelector = class {
1571
- constructor(registry, healthChecker, config, logger) {
1602
+ constructor(registry, healthChecker, config, logger, adapter = "node") {
1572
1603
  // Selection state
1573
1604
  this.selectedProviderId = null;
1574
1605
  this.autoSelect = true;
@@ -1580,6 +1611,7 @@ var ProviderSelector = class {
1580
1611
  this.healthChecker = healthChecker;
1581
1612
  this.config = { ...DEFAULT_CONFIG2, ...config };
1582
1613
  this.logger = logger || consoleLogger4;
1614
+ this.adapter = adapter;
1583
1615
  }
1584
1616
  // ========================================================================
1585
1617
  // Selection Methods
@@ -1620,9 +1652,19 @@ var ProviderSelector = class {
1620
1652
  * Find the best provider for a network (recalculates)
1621
1653
  */
1622
1654
  findBestProvider(network) {
1623
- const providers = this.registry.getProvidersForNetwork(network);
1655
+ let providers = this.registry.getProvidersForNetwork(network);
1656
+ if (this.adapter === "browser") {
1657
+ const beforeCount = providers.length;
1658
+ providers = this.filterBrowserCompatible(providers, network);
1659
+ const filteredCount = beforeCount - providers.length;
1660
+ if (filteredCount > 0) {
1661
+ this.logger.debug(
1662
+ `Filtered out ${filteredCount} browser-incompatible provider(s) for ${network}`
1663
+ );
1664
+ }
1665
+ }
1624
1666
  if (providers.length === 0) {
1625
- this.logger.warn(`No providers available for ${network}`);
1667
+ this.logger.warn(`No browser-compatible providers available for ${network}`);
1626
1668
  return null;
1627
1669
  }
1628
1670
  const scored = providers.map((provider) => ({
@@ -1639,13 +1681,25 @@ var ProviderSelector = class {
1639
1681
  );
1640
1682
  this.activeProviderByNetwork.set(network, defaultProvider.id);
1641
1683
  return defaultProvider;
1642
- } else if (health.success === true) {
1684
+ }
1685
+ if (health.success === true) {
1643
1686
  this.logger.warn(
1644
1687
  `No healthy providers for ${network}, using default: ${defaultProvider.id}`
1645
1688
  );
1646
1689
  this.activeProviderByNetwork.set(network, defaultProvider.id);
1647
1690
  return defaultProvider;
1648
1691
  }
1692
+ if (health.success === false && health.lastTested) {
1693
+ const timeSinceFailure = Date.now() - health.lastTested.getTime();
1694
+ const cooldownMs = 3e4;
1695
+ if (timeSinceFailure > cooldownMs) {
1696
+ this.logger.warn(
1697
+ `No healthy providers for ${network}, retrying failed default after cooldown: ${defaultProvider.id}`
1698
+ );
1699
+ this.activeProviderByNetwork.set(network, defaultProvider.id);
1700
+ return defaultProvider;
1701
+ }
1702
+ }
1649
1703
  }
1650
1704
  for (const provider of providers) {
1651
1705
  const health = this.healthChecker.getResult(provider.id, network);
@@ -1656,8 +1710,19 @@ var ProviderSelector = class {
1656
1710
  this.activeProviderByNetwork.set(network, provider.id);
1657
1711
  return provider;
1658
1712
  }
1713
+ if (health.success === false && health.lastTested) {
1714
+ const timeSinceFailure = Date.now() - health.lastTested.getTime();
1715
+ const cooldownMs = 3e4;
1716
+ if (timeSinceFailure > cooldownMs) {
1717
+ this.logger.warn(
1718
+ `No healthy providers for ${network}, retrying failed provider after cooldown: ${provider.id}`
1719
+ );
1720
+ this.activeProviderByNetwork.set(network, provider.id);
1721
+ return provider;
1722
+ }
1723
+ }
1659
1724
  }
1660
- this.logger.error(`No available providers for ${network} (all tested and failed)`);
1725
+ this.logger.error(`No available providers for ${network} (all tested and failed, cooldown active)`);
1661
1726
  return null;
1662
1727
  }
1663
1728
  const best = scored[0].provider;
@@ -1680,7 +1745,10 @@ var ProviderSelector = class {
1680
1745
  * Get all available providers for a network, sorted by score
1681
1746
  */
1682
1747
  getAvailableProviders(network) {
1683
- const providers = this.registry.getProvidersForNetwork(network);
1748
+ let providers = this.registry.getProvidersForNetwork(network);
1749
+ if (this.adapter === "browser") {
1750
+ providers = this.filterBrowserCompatible(providers, network);
1751
+ }
1684
1752
  return providers.map((provider) => ({
1685
1753
  provider,
1686
1754
  score: this.scoreProvider(provider, network)
@@ -1690,7 +1758,10 @@ var ProviderSelector = class {
1690
1758
  * Get the next best provider (for failover)
1691
1759
  */
1692
1760
  getNextProvider(network, excludeIds) {
1693
- const providers = this.registry.getProvidersForNetwork(network);
1761
+ let providers = this.registry.getProvidersForNetwork(network);
1762
+ if (this.adapter === "browser") {
1763
+ providers = this.filterBrowserCompatible(providers, network);
1764
+ }
1694
1765
  const available = providers.filter((p) => !excludeIds.includes(p.id)).map((provider) => ({
1695
1766
  provider,
1696
1767
  score: this.scoreProvider(provider, network)
@@ -1712,6 +1783,13 @@ var ProviderSelector = class {
1712
1783
  return 0.01 * (1 / (provider.priority + 1));
1713
1784
  }
1714
1785
  if (health.success === false) {
1786
+ if (health.lastTested) {
1787
+ const timeSinceFailure = Date.now() - health.lastTested.getTime();
1788
+ const cooldownMs = 3e4;
1789
+ if (timeSinceFailure > cooldownMs) {
1790
+ return 1e-3 * (1 / (provider.priority + 1));
1791
+ }
1792
+ }
1715
1793
  return 0;
1716
1794
  }
1717
1795
  if (health.status === "offline") {
@@ -1874,12 +1952,39 @@ var ProviderSelector = class {
1874
1952
  endpointV2: this.customEndpoint,
1875
1953
  rps: 10,
1876
1954
  priority: 0,
1877
- isDynamic: false
1955
+ isDynamic: false,
1956
+ browserCompatible: true
1957
+ // Custom endpoints are assumed compatible
1878
1958
  };
1879
1959
  }
1960
+ /**
1961
+ * Filter providers to only include browser-compatible ones
1962
+ *
1963
+ * Checks both:
1964
+ * 1. Provider config browserCompatible flag
1965
+ * 2. Health check result browserCompatible flag (if health check was performed)
1966
+ */
1967
+ filterBrowserCompatible(providers, network) {
1968
+ return providers.filter((provider) => {
1969
+ if (!provider.browserCompatible) {
1970
+ this.logger.debug(
1971
+ `Provider ${provider.id} marked as browser-incompatible in config`
1972
+ );
1973
+ return false;
1974
+ }
1975
+ const health = this.healthChecker.getResult(provider.id, network);
1976
+ if (health && health.browserCompatible === false) {
1977
+ this.logger.debug(
1978
+ `Provider ${provider.id} marked as browser-incompatible by health check (CORS error detected)`
1979
+ );
1980
+ return false;
1981
+ }
1982
+ return true;
1983
+ });
1984
+ }
1880
1985
  };
1881
- function createSelector(registry, healthChecker, config, logger) {
1882
- return new ProviderSelector(registry, healthChecker, config, logger);
1986
+ function createSelector(registry, healthChecker, config, logger, adapter = "node") {
1987
+ return new ProviderSelector(registry, healthChecker, config, logger, adapter);
1883
1988
  }
1884
1989
 
1885
1990
  // src/core/manager.ts
@@ -1986,7 +2091,8 @@ var _ProviderManager = class _ProviderManager {
1986
2091
  this.registry,
1987
2092
  this.healthChecker,
1988
2093
  void 0,
1989
- this.options.logger
2094
+ this.options.logger,
2095
+ this.options.adapter
1990
2096
  );
1991
2097
  this.initialized = true;
1992
2098
  this.notifyListeners();
@@ -2253,7 +2359,20 @@ var _ProviderManager = class _ProviderManager {
2253
2359
  */
2254
2360
  getProviders() {
2255
2361
  if (!this.initialized || !this.network) return [];
2256
- return this.registry.getProvidersForNetwork(this.network);
2362
+ let providers = this.registry.getProvidersForNetwork(this.network);
2363
+ if (this.options.adapter === "browser") {
2364
+ providers = providers.filter((provider) => {
2365
+ if (!provider.browserCompatible) {
2366
+ return false;
2367
+ }
2368
+ const health = this.healthChecker.getResult(provider.id, this.network);
2369
+ if (health && health.browserCompatible === false) {
2370
+ return false;
2371
+ }
2372
+ return true;
2373
+ });
2374
+ }
2375
+ return providers;
2257
2376
  }
2258
2377
  /**
2259
2378
  * Get provider health results for current network
@@ -2299,7 +2418,8 @@ var _ProviderManager = class _ProviderManager {
2299
2418
  latencyMs: null,
2300
2419
  seqno: null,
2301
2420
  blocksBehind: 0,
2302
- lastTested: null
2421
+ lastTested: null,
2422
+ browserCompatible: provider.browserCompatible
2303
2423
  },
2304
2424
  rateLimit: rateLimit || {
2305
2425
  tokens: 0,