ton-provider-system 0.1.10 → 0.1.12
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 +109 -19
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +36 -2
- package/dist/index.d.ts +36 -2
- package/dist/index.js +109 -19
- package/dist/index.js.map +1 -1
- package/package.json +3 -1
- package/rpc.json +4 -2
package/dist/index.cjs
CHANGED
|
@@ -70,6 +70,7 @@ var ProviderConfigSchema = zod.z.object({
|
|
|
70
70
|
priority: zod.z.number().int().nonnegative().default(10),
|
|
71
71
|
enabled: zod.z.boolean().default(true),
|
|
72
72
|
isDynamic: zod.z.boolean().optional().default(false),
|
|
73
|
+
browserCompatible: zod.z.boolean().optional(),
|
|
73
74
|
description: zod.z.string().optional()
|
|
74
75
|
});
|
|
75
76
|
var NetworkDefaultsSchema = zod.z.object({
|
|
@@ -217,7 +218,8 @@ function resolveProvider(id, config) {
|
|
|
217
218
|
apiKey,
|
|
218
219
|
rps: config.rps,
|
|
219
220
|
priority: config.priority,
|
|
220
|
-
isDynamic: config.isDynamic || false
|
|
221
|
+
isDynamic: config.isDynamic || false,
|
|
222
|
+
browserCompatible: config.browserCompatible !== void 0 ? config.browserCompatible : true
|
|
221
223
|
};
|
|
222
224
|
}
|
|
223
225
|
function resolveAllProviders(config) {
|
|
@@ -776,7 +778,8 @@ var HealthChecker = class {
|
|
|
776
778
|
latencyMs: null,
|
|
777
779
|
seqno: null,
|
|
778
780
|
blocksBehind: 0,
|
|
779
|
-
lastTested: null
|
|
781
|
+
lastTested: null,
|
|
782
|
+
browserCompatible: provider.browserCompatible
|
|
780
783
|
};
|
|
781
784
|
this.results.set(key, testingResult);
|
|
782
785
|
try {
|
|
@@ -836,7 +839,8 @@ var HealthChecker = class {
|
|
|
836
839
|
seqno,
|
|
837
840
|
blocksBehind,
|
|
838
841
|
lastTested: /* @__PURE__ */ new Date(),
|
|
839
|
-
cachedEndpoint: normalizedEndpoint
|
|
842
|
+
cachedEndpoint: normalizedEndpoint,
|
|
843
|
+
browserCompatible: provider.browserCompatible
|
|
840
844
|
};
|
|
841
845
|
this.results.set(key, result);
|
|
842
846
|
this.logger.debug(
|
|
@@ -847,6 +851,7 @@ var HealthChecker = class {
|
|
|
847
851
|
const endTime = performance.now();
|
|
848
852
|
const latencyMs = Math.round(endTime - startTime);
|
|
849
853
|
const errorMsg = error.message || String(error) || "Unknown error";
|
|
854
|
+
const isCorsError = this.isCorsError(error, errorMsg);
|
|
850
855
|
const is429 = errorMsg.includes("429") || errorMsg.toLowerCase().includes("rate limit");
|
|
851
856
|
const is404 = errorMsg.includes("404") || errorMsg.toLowerCase().includes("not found");
|
|
852
857
|
const is503 = errorMsg.includes("503") || errorMsg.toLowerCase().includes("service unavailable");
|
|
@@ -861,6 +866,7 @@ var HealthChecker = class {
|
|
|
861
866
|
} else if (isTimeout) {
|
|
862
867
|
status = "offline";
|
|
863
868
|
}
|
|
869
|
+
const browserCompatible = isCorsError ? false : provider.browserCompatible;
|
|
864
870
|
const result = {
|
|
865
871
|
id: provider.id,
|
|
866
872
|
network: provider.network,
|
|
@@ -870,7 +876,8 @@ var HealthChecker = class {
|
|
|
870
876
|
seqno: null,
|
|
871
877
|
blocksBehind: 0,
|
|
872
878
|
lastTested: /* @__PURE__ */ new Date(),
|
|
873
|
-
error: errorMsg
|
|
879
|
+
error: errorMsg,
|
|
880
|
+
browserCompatible
|
|
874
881
|
};
|
|
875
882
|
this.results.set(key, result);
|
|
876
883
|
this.logger.warn(`Provider ${provider.id} health check failed: ${result.error}`);
|
|
@@ -956,7 +963,8 @@ var HealthChecker = class {
|
|
|
956
963
|
// Degraded providers are still usable, just slower/rate-limited
|
|
957
964
|
status: "degraded",
|
|
958
965
|
error: error || "Marked as degraded",
|
|
959
|
-
lastTested: /* @__PURE__ */ new Date()
|
|
966
|
+
lastTested: /* @__PURE__ */ new Date(),
|
|
967
|
+
browserCompatible: existing.browserCompatible ?? true
|
|
960
968
|
} : {
|
|
961
969
|
id: providerId,
|
|
962
970
|
network,
|
|
@@ -967,7 +975,9 @@ var HealthChecker = class {
|
|
|
967
975
|
seqno: null,
|
|
968
976
|
blocksBehind: 0,
|
|
969
977
|
lastTested: /* @__PURE__ */ new Date(),
|
|
970
|
-
error: error || "Marked as degraded"
|
|
978
|
+
error: error || "Marked as degraded",
|
|
979
|
+
browserCompatible: true
|
|
980
|
+
// Default to compatible if unknown
|
|
971
981
|
};
|
|
972
982
|
this.results.set(key, result);
|
|
973
983
|
}
|
|
@@ -983,7 +993,8 @@ var HealthChecker = class {
|
|
|
983
993
|
success: false,
|
|
984
994
|
// Ensure success is false for offline providers
|
|
985
995
|
error: error || "Marked as offline",
|
|
986
|
-
lastTested: /* @__PURE__ */ new Date()
|
|
996
|
+
lastTested: /* @__PURE__ */ new Date(),
|
|
997
|
+
browserCompatible: existing.browserCompatible ?? true
|
|
987
998
|
} : {
|
|
988
999
|
id: providerId,
|
|
989
1000
|
network,
|
|
@@ -993,7 +1004,9 @@ var HealthChecker = class {
|
|
|
993
1004
|
seqno: null,
|
|
994
1005
|
blocksBehind: 0,
|
|
995
1006
|
lastTested: /* @__PURE__ */ new Date(),
|
|
996
|
-
error: error || "Marked as offline"
|
|
1007
|
+
error: error || "Marked as offline",
|
|
1008
|
+
browserCompatible: true
|
|
1009
|
+
// Default to compatible if unknown
|
|
997
1010
|
};
|
|
998
1011
|
this.results.set(key, result);
|
|
999
1012
|
}
|
|
@@ -1125,6 +1138,24 @@ var HealthChecker = class {
|
|
|
1125
1138
|
sleep(ms) {
|
|
1126
1139
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
1127
1140
|
}
|
|
1141
|
+
/**
|
|
1142
|
+
* Detect CORS errors (browser compatibility issues)
|
|
1143
|
+
*
|
|
1144
|
+
* CORS errors occur when:
|
|
1145
|
+
* - Request header field is not allowed by Access-Control-Allow-Headers
|
|
1146
|
+
* - Specifically, x-ton-client-version header is blocked by some providers
|
|
1147
|
+
* - Error message contains "CORS", "Access-Control", or "x-ton-client-version"
|
|
1148
|
+
*/
|
|
1149
|
+
isCorsError(error, errorMsg) {
|
|
1150
|
+
const msg = errorMsg.toLowerCase();
|
|
1151
|
+
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")) {
|
|
1152
|
+
return true;
|
|
1153
|
+
}
|
|
1154
|
+
if (error.name === "TypeError" && (msg.includes("failed to fetch") || msg.includes("network error"))) {
|
|
1155
|
+
return false;
|
|
1156
|
+
}
|
|
1157
|
+
return false;
|
|
1158
|
+
}
|
|
1128
1159
|
};
|
|
1129
1160
|
function createHealthChecker(config, logger, rateLimiter) {
|
|
1130
1161
|
return new HealthChecker(config, logger, rateLimiter);
|
|
@@ -1569,7 +1600,7 @@ var DEFAULT_CONFIG2 = {
|
|
|
1569
1600
|
minStatus: ["available", "degraded"]
|
|
1570
1601
|
};
|
|
1571
1602
|
var ProviderSelector = class {
|
|
1572
|
-
constructor(registry, healthChecker, config, logger) {
|
|
1603
|
+
constructor(registry, healthChecker, config, logger, adapter = "node") {
|
|
1573
1604
|
// Selection state
|
|
1574
1605
|
this.selectedProviderId = null;
|
|
1575
1606
|
this.autoSelect = true;
|
|
@@ -1581,6 +1612,7 @@ var ProviderSelector = class {
|
|
|
1581
1612
|
this.healthChecker = healthChecker;
|
|
1582
1613
|
this.config = { ...DEFAULT_CONFIG2, ...config };
|
|
1583
1614
|
this.logger = logger || consoleLogger4;
|
|
1615
|
+
this.adapter = adapter;
|
|
1584
1616
|
}
|
|
1585
1617
|
// ========================================================================
|
|
1586
1618
|
// Selection Methods
|
|
@@ -1621,9 +1653,19 @@ var ProviderSelector = class {
|
|
|
1621
1653
|
* Find the best provider for a network (recalculates)
|
|
1622
1654
|
*/
|
|
1623
1655
|
findBestProvider(network) {
|
|
1624
|
-
|
|
1656
|
+
let providers = this.registry.getProvidersForNetwork(network);
|
|
1657
|
+
if (this.adapter === "browser") {
|
|
1658
|
+
const beforeCount = providers.length;
|
|
1659
|
+
providers = this.filterBrowserCompatible(providers, network);
|
|
1660
|
+
const filteredCount = beforeCount - providers.length;
|
|
1661
|
+
if (filteredCount > 0) {
|
|
1662
|
+
this.logger.debug(
|
|
1663
|
+
`Filtered out ${filteredCount} browser-incompatible provider(s) for ${network}`
|
|
1664
|
+
);
|
|
1665
|
+
}
|
|
1666
|
+
}
|
|
1625
1667
|
if (providers.length === 0) {
|
|
1626
|
-
this.logger.warn(`No providers available for ${network}`);
|
|
1668
|
+
this.logger.warn(`No browser-compatible providers available for ${network}`);
|
|
1627
1669
|
return null;
|
|
1628
1670
|
}
|
|
1629
1671
|
const scored = providers.map((provider) => ({
|
|
@@ -1704,7 +1746,10 @@ var ProviderSelector = class {
|
|
|
1704
1746
|
* Get all available providers for a network, sorted by score
|
|
1705
1747
|
*/
|
|
1706
1748
|
getAvailableProviders(network) {
|
|
1707
|
-
|
|
1749
|
+
let providers = this.registry.getProvidersForNetwork(network);
|
|
1750
|
+
if (this.adapter === "browser") {
|
|
1751
|
+
providers = this.filterBrowserCompatible(providers, network);
|
|
1752
|
+
}
|
|
1708
1753
|
return providers.map((provider) => ({
|
|
1709
1754
|
provider,
|
|
1710
1755
|
score: this.scoreProvider(provider, network)
|
|
@@ -1714,7 +1759,10 @@ var ProviderSelector = class {
|
|
|
1714
1759
|
* Get the next best provider (for failover)
|
|
1715
1760
|
*/
|
|
1716
1761
|
getNextProvider(network, excludeIds) {
|
|
1717
|
-
|
|
1762
|
+
let providers = this.registry.getProvidersForNetwork(network);
|
|
1763
|
+
if (this.adapter === "browser") {
|
|
1764
|
+
providers = this.filterBrowserCompatible(providers, network);
|
|
1765
|
+
}
|
|
1718
1766
|
const available = providers.filter((p) => !excludeIds.includes(p.id)).map((provider) => ({
|
|
1719
1767
|
provider,
|
|
1720
1768
|
score: this.scoreProvider(provider, network)
|
|
@@ -1905,12 +1953,39 @@ var ProviderSelector = class {
|
|
|
1905
1953
|
endpointV2: this.customEndpoint,
|
|
1906
1954
|
rps: 10,
|
|
1907
1955
|
priority: 0,
|
|
1908
|
-
isDynamic: false
|
|
1956
|
+
isDynamic: false,
|
|
1957
|
+
browserCompatible: true
|
|
1958
|
+
// Custom endpoints are assumed compatible
|
|
1909
1959
|
};
|
|
1910
1960
|
}
|
|
1961
|
+
/**
|
|
1962
|
+
* Filter providers to only include browser-compatible ones
|
|
1963
|
+
*
|
|
1964
|
+
* Checks both:
|
|
1965
|
+
* 1. Provider config browserCompatible flag
|
|
1966
|
+
* 2. Health check result browserCompatible flag (if health check was performed)
|
|
1967
|
+
*/
|
|
1968
|
+
filterBrowserCompatible(providers, network) {
|
|
1969
|
+
return providers.filter((provider) => {
|
|
1970
|
+
if (!provider.browserCompatible) {
|
|
1971
|
+
this.logger.debug(
|
|
1972
|
+
`Provider ${provider.id} marked as browser-incompatible in config`
|
|
1973
|
+
);
|
|
1974
|
+
return false;
|
|
1975
|
+
}
|
|
1976
|
+
const health = this.healthChecker.getResult(provider.id, network);
|
|
1977
|
+
if (health && health.browserCompatible === false) {
|
|
1978
|
+
this.logger.debug(
|
|
1979
|
+
`Provider ${provider.id} marked as browser-incompatible by health check (CORS error detected)`
|
|
1980
|
+
);
|
|
1981
|
+
return false;
|
|
1982
|
+
}
|
|
1983
|
+
return true;
|
|
1984
|
+
});
|
|
1985
|
+
}
|
|
1911
1986
|
};
|
|
1912
|
-
function createSelector(registry, healthChecker, config, logger) {
|
|
1913
|
-
return new ProviderSelector(registry, healthChecker, config, logger);
|
|
1987
|
+
function createSelector(registry, healthChecker, config, logger, adapter = "node") {
|
|
1988
|
+
return new ProviderSelector(registry, healthChecker, config, logger, adapter);
|
|
1914
1989
|
}
|
|
1915
1990
|
|
|
1916
1991
|
// src/core/manager.ts
|
|
@@ -2017,7 +2092,8 @@ var _ProviderManager = class _ProviderManager {
|
|
|
2017
2092
|
this.registry,
|
|
2018
2093
|
this.healthChecker,
|
|
2019
2094
|
void 0,
|
|
2020
|
-
this.options.logger
|
|
2095
|
+
this.options.logger,
|
|
2096
|
+
this.options.adapter
|
|
2021
2097
|
);
|
|
2022
2098
|
this.initialized = true;
|
|
2023
2099
|
this.notifyListeners();
|
|
@@ -2284,7 +2360,20 @@ var _ProviderManager = class _ProviderManager {
|
|
|
2284
2360
|
*/
|
|
2285
2361
|
getProviders() {
|
|
2286
2362
|
if (!this.initialized || !this.network) return [];
|
|
2287
|
-
|
|
2363
|
+
let providers = this.registry.getProvidersForNetwork(this.network);
|
|
2364
|
+
if (this.options.adapter === "browser") {
|
|
2365
|
+
providers = providers.filter((provider) => {
|
|
2366
|
+
if (!provider.browserCompatible) {
|
|
2367
|
+
return false;
|
|
2368
|
+
}
|
|
2369
|
+
const health = this.healthChecker.getResult(provider.id, this.network);
|
|
2370
|
+
if (health && health.browserCompatible === false) {
|
|
2371
|
+
return false;
|
|
2372
|
+
}
|
|
2373
|
+
return true;
|
|
2374
|
+
});
|
|
2375
|
+
}
|
|
2376
|
+
return providers;
|
|
2288
2377
|
}
|
|
2289
2378
|
/**
|
|
2290
2379
|
* Get provider health results for current network
|
|
@@ -2330,7 +2419,8 @@ var _ProviderManager = class _ProviderManager {
|
|
|
2330
2419
|
latencyMs: null,
|
|
2331
2420
|
seqno: null,
|
|
2332
2421
|
blocksBehind: 0,
|
|
2333
|
-
lastTested: null
|
|
2422
|
+
lastTested: null,
|
|
2423
|
+
browserCompatible: provider.browserCompatible
|
|
2334
2424
|
},
|
|
2335
2425
|
rateLimit: rateLimit || {
|
|
2336
2426
|
tokens: 0,
|