ton-provider-system 0.2.4 → 0.3.0
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 +292 -203
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +26 -1
- package/dist/index.d.ts +26 -1
- package/dist/index.js +292 -204
- package/dist/index.js.map +1 -1
- package/package.json +17 -19
package/dist/index.js
CHANGED
|
@@ -1035,6 +1035,238 @@ function createProvider(provider) {
|
|
|
1035
1035
|
}
|
|
1036
1036
|
}
|
|
1037
1037
|
|
|
1038
|
+
// src/utils/endpoint.ts
|
|
1039
|
+
function normalizeV2Endpoint(endpoint, provider) {
|
|
1040
|
+
if (provider) {
|
|
1041
|
+
const providerImpl = createProvider(provider);
|
|
1042
|
+
return providerImpl.normalizeEndpoint(endpoint);
|
|
1043
|
+
}
|
|
1044
|
+
return normalizeV2EndpointFallback(endpoint);
|
|
1045
|
+
}
|
|
1046
|
+
function normalizeV2EndpointFallback(endpoint) {
|
|
1047
|
+
let normalized = endpoint.trim();
|
|
1048
|
+
if (normalized.endsWith("/")) {
|
|
1049
|
+
normalized = normalized.slice(0, -1);
|
|
1050
|
+
}
|
|
1051
|
+
if (normalized.toLowerCase().endsWith("/jsonrpc")) {
|
|
1052
|
+
return normalized;
|
|
1053
|
+
}
|
|
1054
|
+
if (normalized.includes("gateway.tatum.io")) {
|
|
1055
|
+
try {
|
|
1056
|
+
const url = new URL(normalized);
|
|
1057
|
+
if (!url.pathname || url.pathname === "/") {
|
|
1058
|
+
return normalized + "/jsonRPC";
|
|
1059
|
+
}
|
|
1060
|
+
if (!url.pathname.toLowerCase().endsWith("/jsonrpc")) {
|
|
1061
|
+
return normalized + "/jsonRPC";
|
|
1062
|
+
}
|
|
1063
|
+
} catch {
|
|
1064
|
+
return normalized + "/jsonRPC";
|
|
1065
|
+
}
|
|
1066
|
+
}
|
|
1067
|
+
if (normalized.includes("onfinality.io")) {
|
|
1068
|
+
try {
|
|
1069
|
+
const url = new URL(normalized);
|
|
1070
|
+
const baseUrl = normalized.split("?")[0];
|
|
1071
|
+
if (!url.pathname || url.pathname === "/") {
|
|
1072
|
+
const apikey = url.searchParams.get("apikey");
|
|
1073
|
+
if (apikey && apikey !== "{key}" && apikey.length > 0) {
|
|
1074
|
+
return baseUrl.replace(/\/?$/, "/rpc");
|
|
1075
|
+
}
|
|
1076
|
+
return baseUrl.replace(/\/?$/, "/public");
|
|
1077
|
+
}
|
|
1078
|
+
if (url.pathname === "/rpc" || url.pathname === "/public") {
|
|
1079
|
+
return baseUrl;
|
|
1080
|
+
}
|
|
1081
|
+
return baseUrl;
|
|
1082
|
+
} catch {
|
|
1083
|
+
if (normalized.includes("{key}")) {
|
|
1084
|
+
return normalized.split("?")[0].replace(/\/?$/, "/public");
|
|
1085
|
+
}
|
|
1086
|
+
if (!normalized.includes("/rpc") && !normalized.includes("/public")) {
|
|
1087
|
+
return normalized.split("?")[0] + "/public";
|
|
1088
|
+
}
|
|
1089
|
+
return normalized.split("?")[0];
|
|
1090
|
+
}
|
|
1091
|
+
}
|
|
1092
|
+
if (normalized.endsWith("/api/v2")) {
|
|
1093
|
+
return normalized + "/jsonRPC";
|
|
1094
|
+
}
|
|
1095
|
+
if (normalized.endsWith("/api/v3")) {
|
|
1096
|
+
return normalized.replace("/api/v3", "/api/v2/jsonRPC");
|
|
1097
|
+
}
|
|
1098
|
+
if (normalized.includes("quiknode.pro") || normalized.includes("getblock.io")) {
|
|
1099
|
+
try {
|
|
1100
|
+
const url = new URL(normalized);
|
|
1101
|
+
if (!url.pathname || url.pathname === "/") {
|
|
1102
|
+
return normalized + "/jsonRPC";
|
|
1103
|
+
}
|
|
1104
|
+
if (!url.pathname.toLowerCase().endsWith("/jsonrpc")) {
|
|
1105
|
+
return normalized + "/jsonRPC";
|
|
1106
|
+
}
|
|
1107
|
+
} catch {
|
|
1108
|
+
return normalized + "/jsonRPC";
|
|
1109
|
+
}
|
|
1110
|
+
}
|
|
1111
|
+
try {
|
|
1112
|
+
const url = new URL(normalized);
|
|
1113
|
+
if (!url.pathname || url.pathname === "/") {
|
|
1114
|
+
return normalized + "/jsonRPC";
|
|
1115
|
+
}
|
|
1116
|
+
} catch {
|
|
1117
|
+
}
|
|
1118
|
+
return normalized;
|
|
1119
|
+
}
|
|
1120
|
+
function toV2Base(endpoint) {
|
|
1121
|
+
let normalized = endpoint.trim();
|
|
1122
|
+
if (normalized.endsWith("/")) {
|
|
1123
|
+
normalized = normalized.slice(0, -1);
|
|
1124
|
+
}
|
|
1125
|
+
if (normalized.toLowerCase().endsWith("/jsonrpc")) {
|
|
1126
|
+
normalized = normalized.slice(0, -8);
|
|
1127
|
+
}
|
|
1128
|
+
normalized = normalized.replace(/\/api\/v3\b/, "/api/v2");
|
|
1129
|
+
if (!normalized.endsWith("/api/v2")) {
|
|
1130
|
+
if (normalized.includes("/api/v2")) {
|
|
1131
|
+
const idx = normalized.indexOf("/api/v2");
|
|
1132
|
+
normalized = normalized.slice(0, idx + 7);
|
|
1133
|
+
}
|
|
1134
|
+
}
|
|
1135
|
+
return normalized;
|
|
1136
|
+
}
|
|
1137
|
+
function toV3Base(endpoint) {
|
|
1138
|
+
const normalized = toV2Base(endpoint);
|
|
1139
|
+
return normalized.replace("/api/v2", "/api/v3");
|
|
1140
|
+
}
|
|
1141
|
+
function getBaseUrl(endpoint) {
|
|
1142
|
+
try {
|
|
1143
|
+
const url = new URL(endpoint);
|
|
1144
|
+
return `${url.protocol}//${url.host}`;
|
|
1145
|
+
} catch {
|
|
1146
|
+
return endpoint;
|
|
1147
|
+
}
|
|
1148
|
+
}
|
|
1149
|
+
function isChainstackUrl(url) {
|
|
1150
|
+
try {
|
|
1151
|
+
const parsed = new URL(url.trim());
|
|
1152
|
+
return parsed.hostname.includes("chainstack.com");
|
|
1153
|
+
} catch {
|
|
1154
|
+
return false;
|
|
1155
|
+
}
|
|
1156
|
+
}
|
|
1157
|
+
function isQuickNodeUrl(url) {
|
|
1158
|
+
try {
|
|
1159
|
+
const parsed = new URL(url.trim());
|
|
1160
|
+
return parsed.hostname.includes("quiknode.pro");
|
|
1161
|
+
} catch {
|
|
1162
|
+
return false;
|
|
1163
|
+
}
|
|
1164
|
+
}
|
|
1165
|
+
function isTonCenterUrl(url) {
|
|
1166
|
+
try {
|
|
1167
|
+
const parsed = new URL(url.trim());
|
|
1168
|
+
return parsed.hostname.includes("toncenter.com");
|
|
1169
|
+
} catch {
|
|
1170
|
+
return false;
|
|
1171
|
+
}
|
|
1172
|
+
}
|
|
1173
|
+
function isOrbsUrl(url) {
|
|
1174
|
+
try {
|
|
1175
|
+
const parsed = new URL(url.trim());
|
|
1176
|
+
return parsed.hostname.includes("orbs.network") || parsed.hostname.includes("ton-access");
|
|
1177
|
+
} catch {
|
|
1178
|
+
return false;
|
|
1179
|
+
}
|
|
1180
|
+
}
|
|
1181
|
+
function buildRestUrl(baseEndpoint, method) {
|
|
1182
|
+
const base = toV2Base(baseEndpoint);
|
|
1183
|
+
return `${base}/${method}`;
|
|
1184
|
+
}
|
|
1185
|
+
function buildGetAddressStateUrl(baseEndpoint, address) {
|
|
1186
|
+
const base = toV2Base(baseEndpoint);
|
|
1187
|
+
return `${base}/getAddressState?address=${encodeURIComponent(address)}`;
|
|
1188
|
+
}
|
|
1189
|
+
function buildGetAddressBalanceUrl(baseEndpoint, address) {
|
|
1190
|
+
const base = toV2Base(baseEndpoint);
|
|
1191
|
+
return `${base}/getAddressBalance?address=${encodeURIComponent(address)}`;
|
|
1192
|
+
}
|
|
1193
|
+
function buildGetAddressInfoUrl(baseEndpoint, address) {
|
|
1194
|
+
const base = toV2Base(baseEndpoint);
|
|
1195
|
+
return `${base}/getAddressInformation?address=${encodeURIComponent(address)}`;
|
|
1196
|
+
}
|
|
1197
|
+
function detectNetworkFromEndpoint(endpoint) {
|
|
1198
|
+
const lower = endpoint.toLowerCase();
|
|
1199
|
+
if (lower.includes("testnet") || lower.includes("test") || lower.includes("sandbox")) {
|
|
1200
|
+
return "testnet";
|
|
1201
|
+
}
|
|
1202
|
+
if (lower.includes("mainnet") || lower.includes("main") || // TonCenter mainnet doesn't have 'mainnet' in URL
|
|
1203
|
+
lower.includes("toncenter.com") && !lower.includes("testnet")) {
|
|
1204
|
+
return "mainnet";
|
|
1205
|
+
}
|
|
1206
|
+
return null;
|
|
1207
|
+
}
|
|
1208
|
+
function isValidHttpUrl(str) {
|
|
1209
|
+
try {
|
|
1210
|
+
const url = new URL(str);
|
|
1211
|
+
return url.protocol === "http:" || url.protocol === "https:";
|
|
1212
|
+
} catch {
|
|
1213
|
+
return false;
|
|
1214
|
+
}
|
|
1215
|
+
}
|
|
1216
|
+
function isValidWsUrl(str) {
|
|
1217
|
+
try {
|
|
1218
|
+
const url = new URL(str);
|
|
1219
|
+
return url.protocol === "ws:" || url.protocol === "wss:";
|
|
1220
|
+
} catch {
|
|
1221
|
+
return false;
|
|
1222
|
+
}
|
|
1223
|
+
}
|
|
1224
|
+
function isCredentialLike(segment) {
|
|
1225
|
+
return /^[0-9a-f]{16,}$/i.test(segment) || // hex API keys (e.g. Chainstack/GetBlock)
|
|
1226
|
+
/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(segment) || // UUID
|
|
1227
|
+
/^[A-Za-z0-9_-]{20,}$/.test(segment);
|
|
1228
|
+
}
|
|
1229
|
+
function redactUrl(url) {
|
|
1230
|
+
const MASK = "***";
|
|
1231
|
+
const SENSITIVE_PARAM = /^(apikey|api[-_]?key|key|token|secret|password)$/i;
|
|
1232
|
+
const STRUCTURAL = /* @__PURE__ */ new Set([
|
|
1233
|
+
"api",
|
|
1234
|
+
"v1",
|
|
1235
|
+
"v2",
|
|
1236
|
+
"v3",
|
|
1237
|
+
"v4",
|
|
1238
|
+
"jsonrpc",
|
|
1239
|
+
"rpc",
|
|
1240
|
+
"public",
|
|
1241
|
+
"ws",
|
|
1242
|
+
"testnet",
|
|
1243
|
+
"mainnet",
|
|
1244
|
+
"toncenter-api-v2"
|
|
1245
|
+
]);
|
|
1246
|
+
try {
|
|
1247
|
+
const u = new URL(url);
|
|
1248
|
+
for (const name of Array.from(u.searchParams.keys())) {
|
|
1249
|
+
if (SENSITIVE_PARAM.test(name)) {
|
|
1250
|
+
u.searchParams.set(name, MASK);
|
|
1251
|
+
}
|
|
1252
|
+
}
|
|
1253
|
+
u.pathname = u.pathname.split("/").map(
|
|
1254
|
+
(seg) => seg && !STRUCTURAL.has(seg.toLowerCase()) && isCredentialLike(seg) ? MASK : seg
|
|
1255
|
+
).join("/");
|
|
1256
|
+
const hostParts = u.hostname.split(".");
|
|
1257
|
+
if (hostParts.length > 2 && isCredentialLike(hostParts[0])) {
|
|
1258
|
+
hostParts[0] = MASK;
|
|
1259
|
+
u.hostname = hostParts.join(".");
|
|
1260
|
+
}
|
|
1261
|
+
return u.toString();
|
|
1262
|
+
} catch {
|
|
1263
|
+
return url.replace(
|
|
1264
|
+
/\b(apikey|api[-_]?key|key|token|secret|password)=[^&\s]+/gi,
|
|
1265
|
+
`$1=${MASK}`
|
|
1266
|
+
);
|
|
1267
|
+
}
|
|
1268
|
+
}
|
|
1269
|
+
|
|
1038
1270
|
// src/core/healthChecker.ts
|
|
1039
1271
|
var consoleLogger2 = {
|
|
1040
1272
|
debug: (msg, data) => console.debug(`[HealthChecker] ${msg}`, data || ""),
|
|
@@ -1051,6 +1283,8 @@ var HealthChecker = class {
|
|
|
1051
1283
|
constructor(config, logger, rateLimiter) {
|
|
1052
1284
|
this.results = /* @__PURE__ */ new Map();
|
|
1053
1285
|
this.highestSeqno = /* @__PURE__ */ new Map();
|
|
1286
|
+
/** Rolling window of recent valid seqnos per network (for outlier-robust blocksBehind). */
|
|
1287
|
+
this.seqnoSamples = /* @__PURE__ */ new Map();
|
|
1054
1288
|
this.rateLimiter = null;
|
|
1055
1289
|
this.config = { ...DEFAULT_CONFIG, ...config };
|
|
1056
1290
|
this.logger = logger || consoleLogger2;
|
|
@@ -1096,22 +1330,36 @@ var HealthChecker = class {
|
|
|
1096
1330
|
if (!validation.valid) {
|
|
1097
1331
|
throw new Error(validation.error || "Provider configuration invalid");
|
|
1098
1332
|
}
|
|
1099
|
-
|
|
1333
|
+
let normalizedEndpoint = providerImpl.normalizeEndpoint(endpoint);
|
|
1100
1334
|
if (provider.type === "onfinality") {
|
|
1101
|
-
this.logger.debug(`OnFinality endpoint: ${endpoint} -> ${normalizedEndpoint}, API key: ${provider.apiKey ? "set" : "not set"}`);
|
|
1335
|
+
this.logger.debug(`OnFinality endpoint: ${redactUrl(endpoint)} -> ${redactUrl(normalizedEndpoint)}, API key: ${provider.apiKey ? "set" : "not set"}`);
|
|
1102
1336
|
}
|
|
1337
|
+
const orbsRetries = provider.isDynamic && provider.type === "orbs" ? 2 : 0;
|
|
1103
1338
|
let info;
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1339
|
+
for (let attempt = 0; ; attempt++) {
|
|
1340
|
+
try {
|
|
1341
|
+
info = await this.callGetMasterchainInfo(normalizedEndpoint, provider, providerImpl);
|
|
1342
|
+
break;
|
|
1343
|
+
} catch (error) {
|
|
1344
|
+
if (provider.type === "onfinality" && normalizedEndpoint.includes("/rpc") && provider.apiKey && error.message?.includes("backend error")) {
|
|
1345
|
+
this.logger.debug(`OnFinality /rpc failed, retrying with /public endpoint`);
|
|
1346
|
+
const baseUrl = normalizedEndpoint.split("?")[0];
|
|
1347
|
+
const publicEndpoint = baseUrl.replace("/rpc", "/public");
|
|
1348
|
+
const publicProvider = { ...provider, apiKey: void 0 };
|
|
1349
|
+
const publicProviderImpl = createProvider(publicProvider);
|
|
1350
|
+
info = await this.callGetMasterchainInfo(publicEndpoint, publicProvider, publicProviderImpl);
|
|
1351
|
+
break;
|
|
1352
|
+
}
|
|
1353
|
+
if (attempt < orbsRetries && this.isNonJsonResponseError(error)) {
|
|
1354
|
+
this.logger.debug(
|
|
1355
|
+
`Orbs gateway returned a non-JSON response; re-discovering endpoint (retry ${attempt + 1}/${orbsRetries})`
|
|
1356
|
+
);
|
|
1357
|
+
const freshEndpoint = await this.getEndpoint(provider);
|
|
1358
|
+
if (freshEndpoint) {
|
|
1359
|
+
normalizedEndpoint = providerImpl.normalizeEndpoint(freshEndpoint);
|
|
1360
|
+
continue;
|
|
1361
|
+
}
|
|
1362
|
+
}
|
|
1115
1363
|
throw error;
|
|
1116
1364
|
}
|
|
1117
1365
|
}
|
|
@@ -1126,7 +1374,8 @@ var HealthChecker = class {
|
|
|
1126
1374
|
if (seqno > currentHighest) {
|
|
1127
1375
|
this.highestSeqno.set(provider.network, seqno);
|
|
1128
1376
|
}
|
|
1129
|
-
const
|
|
1377
|
+
const baselineSeqno = this.recordSeqnoAndGetBaseline(provider.network, seqno);
|
|
1378
|
+
const blocksBehind = Math.max(0, baselineSeqno - seqno);
|
|
1130
1379
|
let status = "available";
|
|
1131
1380
|
if (blocksBehind > this.config.maxBlocksBehind) {
|
|
1132
1381
|
status = "stale";
|
|
@@ -1270,6 +1519,7 @@ var HealthChecker = class {
|
|
|
1270
1519
|
clearResults() {
|
|
1271
1520
|
this.results.clear();
|
|
1272
1521
|
this.highestSeqno.clear();
|
|
1522
|
+
this.seqnoSamples.clear();
|
|
1273
1523
|
}
|
|
1274
1524
|
/**
|
|
1275
1525
|
* Mark a provider as degraded (e.g., on 429 error)
|
|
@@ -1339,6 +1589,31 @@ var HealthChecker = class {
|
|
|
1339
1589
|
getResultKey(providerId, network) {
|
|
1340
1590
|
return `${providerId}-${network}`;
|
|
1341
1591
|
}
|
|
1592
|
+
/**
|
|
1593
|
+
* Record a valid seqno sample and return an outlier-robust freshness baseline
|
|
1594
|
+
* (median of recent samples for the network). Using the median instead of the
|
|
1595
|
+
* raw maximum prevents a single provider that reports an inflated seqno from
|
|
1596
|
+
* poisoning `blocksBehind` for every other (healthy) provider. See P2-2.
|
|
1597
|
+
*/
|
|
1598
|
+
recordSeqnoAndGetBaseline(network, seqno) {
|
|
1599
|
+
const samples = this.seqnoSamples.get(network) ?? [];
|
|
1600
|
+
samples.push(seqno);
|
|
1601
|
+
if (samples.length > 20) {
|
|
1602
|
+
samples.shift();
|
|
1603
|
+
}
|
|
1604
|
+
this.seqnoSamples.set(network, samples);
|
|
1605
|
+
const sorted = [...samples].sort((a, b) => a - b);
|
|
1606
|
+
const mid = Math.floor((sorted.length - 1) / 2);
|
|
1607
|
+
return sorted[mid];
|
|
1608
|
+
}
|
|
1609
|
+
/**
|
|
1610
|
+
* Detect a "non-JSON response" error (e.g. an HTML error page returned by a
|
|
1611
|
+
* misbehaving gateway), used to decide whether to re-discover an Orbs endpoint.
|
|
1612
|
+
*/
|
|
1613
|
+
isNonJsonResponseError(error) {
|
|
1614
|
+
const msg = (error?.message || String(error) || "").toLowerCase();
|
|
1615
|
+
return msg.includes("invalid response type") || msg.includes("expected json") || msg.includes("got text/html");
|
|
1616
|
+
}
|
|
1342
1617
|
/**
|
|
1343
1618
|
* Get endpoint URL for a provider (handles dynamic providers like Orbs)
|
|
1344
1619
|
*/
|
|
@@ -2180,7 +2455,7 @@ var ProviderSelector = class {
|
|
|
2180
2455
|
*/
|
|
2181
2456
|
setCustomEndpoint(endpoint) {
|
|
2182
2457
|
this.customEndpoint = endpoint?.trim() || null;
|
|
2183
|
-
this.logger.info(`Custom endpoint: ${this.customEndpoint
|
|
2458
|
+
this.logger.info(`Custom endpoint: ${this.customEndpoint ? redactUrl(this.customEndpoint) : "(none)"}`);
|
|
2184
2459
|
}
|
|
2185
2460
|
/**
|
|
2186
2461
|
* Get custom endpoint
|
|
@@ -2324,193 +2599,6 @@ function createSelector(registry, healthChecker, config, logger, adapter = "node
|
|
|
2324
2599
|
return new ProviderSelector(registry, healthChecker, config, logger, adapter);
|
|
2325
2600
|
}
|
|
2326
2601
|
|
|
2327
|
-
// src/utils/endpoint.ts
|
|
2328
|
-
function normalizeV2Endpoint(endpoint, provider) {
|
|
2329
|
-
if (provider) {
|
|
2330
|
-
const providerImpl = createProvider(provider);
|
|
2331
|
-
return providerImpl.normalizeEndpoint(endpoint);
|
|
2332
|
-
}
|
|
2333
|
-
return normalizeV2EndpointFallback(endpoint);
|
|
2334
|
-
}
|
|
2335
|
-
function normalizeV2EndpointFallback(endpoint) {
|
|
2336
|
-
let normalized = endpoint.trim();
|
|
2337
|
-
if (normalized.endsWith("/")) {
|
|
2338
|
-
normalized = normalized.slice(0, -1);
|
|
2339
|
-
}
|
|
2340
|
-
if (normalized.toLowerCase().endsWith("/jsonrpc")) {
|
|
2341
|
-
return normalized;
|
|
2342
|
-
}
|
|
2343
|
-
if (normalized.includes("gateway.tatum.io")) {
|
|
2344
|
-
try {
|
|
2345
|
-
const url = new URL(normalized);
|
|
2346
|
-
if (!url.pathname || url.pathname === "/") {
|
|
2347
|
-
return normalized + "/jsonRPC";
|
|
2348
|
-
}
|
|
2349
|
-
if (!url.pathname.toLowerCase().endsWith("/jsonrpc")) {
|
|
2350
|
-
return normalized + "/jsonRPC";
|
|
2351
|
-
}
|
|
2352
|
-
} catch {
|
|
2353
|
-
return normalized + "/jsonRPC";
|
|
2354
|
-
}
|
|
2355
|
-
}
|
|
2356
|
-
if (normalized.includes("onfinality.io")) {
|
|
2357
|
-
try {
|
|
2358
|
-
const url = new URL(normalized);
|
|
2359
|
-
const baseUrl = normalized.split("?")[0];
|
|
2360
|
-
if (!url.pathname || url.pathname === "/") {
|
|
2361
|
-
const apikey = url.searchParams.get("apikey");
|
|
2362
|
-
if (apikey && apikey !== "{key}" && apikey.length > 0) {
|
|
2363
|
-
return baseUrl.replace(/\/?$/, "/rpc");
|
|
2364
|
-
}
|
|
2365
|
-
return baseUrl.replace(/\/?$/, "/public");
|
|
2366
|
-
}
|
|
2367
|
-
if (url.pathname === "/rpc" || url.pathname === "/public") {
|
|
2368
|
-
return baseUrl;
|
|
2369
|
-
}
|
|
2370
|
-
return baseUrl;
|
|
2371
|
-
} catch {
|
|
2372
|
-
if (normalized.includes("{key}")) {
|
|
2373
|
-
return normalized.split("?")[0].replace(/\/?$/, "/public");
|
|
2374
|
-
}
|
|
2375
|
-
if (!normalized.includes("/rpc") && !normalized.includes("/public")) {
|
|
2376
|
-
return normalized.split("?")[0] + "/public";
|
|
2377
|
-
}
|
|
2378
|
-
return normalized.split("?")[0];
|
|
2379
|
-
}
|
|
2380
|
-
}
|
|
2381
|
-
if (normalized.endsWith("/api/v2")) {
|
|
2382
|
-
return normalized + "/jsonRPC";
|
|
2383
|
-
}
|
|
2384
|
-
if (normalized.endsWith("/api/v3")) {
|
|
2385
|
-
return normalized.replace("/api/v3", "/api/v2/jsonRPC");
|
|
2386
|
-
}
|
|
2387
|
-
if (normalized.includes("quiknode.pro") || normalized.includes("getblock.io")) {
|
|
2388
|
-
try {
|
|
2389
|
-
const url = new URL(normalized);
|
|
2390
|
-
if (!url.pathname || url.pathname === "/") {
|
|
2391
|
-
return normalized + "/jsonRPC";
|
|
2392
|
-
}
|
|
2393
|
-
if (!url.pathname.toLowerCase().endsWith("/jsonrpc")) {
|
|
2394
|
-
return normalized + "/jsonRPC";
|
|
2395
|
-
}
|
|
2396
|
-
} catch {
|
|
2397
|
-
return normalized + "/jsonRPC";
|
|
2398
|
-
}
|
|
2399
|
-
}
|
|
2400
|
-
try {
|
|
2401
|
-
const url = new URL(normalized);
|
|
2402
|
-
if (!url.pathname || url.pathname === "/") {
|
|
2403
|
-
return normalized + "/jsonRPC";
|
|
2404
|
-
}
|
|
2405
|
-
} catch {
|
|
2406
|
-
}
|
|
2407
|
-
return normalized;
|
|
2408
|
-
}
|
|
2409
|
-
function toV2Base(endpoint) {
|
|
2410
|
-
let normalized = endpoint.trim();
|
|
2411
|
-
if (normalized.endsWith("/")) {
|
|
2412
|
-
normalized = normalized.slice(0, -1);
|
|
2413
|
-
}
|
|
2414
|
-
if (normalized.toLowerCase().endsWith("/jsonrpc")) {
|
|
2415
|
-
normalized = normalized.slice(0, -8);
|
|
2416
|
-
}
|
|
2417
|
-
normalized = normalized.replace(/\/api\/v3\b/, "/api/v2");
|
|
2418
|
-
if (!normalized.endsWith("/api/v2")) {
|
|
2419
|
-
if (normalized.includes("/api/v2")) {
|
|
2420
|
-
const idx = normalized.indexOf("/api/v2");
|
|
2421
|
-
normalized = normalized.slice(0, idx + 7);
|
|
2422
|
-
}
|
|
2423
|
-
}
|
|
2424
|
-
return normalized;
|
|
2425
|
-
}
|
|
2426
|
-
function toV3Base(endpoint) {
|
|
2427
|
-
const normalized = toV2Base(endpoint);
|
|
2428
|
-
return normalized.replace("/api/v2", "/api/v3");
|
|
2429
|
-
}
|
|
2430
|
-
function getBaseUrl(endpoint) {
|
|
2431
|
-
try {
|
|
2432
|
-
const url = new URL(endpoint);
|
|
2433
|
-
return `${url.protocol}//${url.host}`;
|
|
2434
|
-
} catch {
|
|
2435
|
-
return endpoint;
|
|
2436
|
-
}
|
|
2437
|
-
}
|
|
2438
|
-
function isChainstackUrl(url) {
|
|
2439
|
-
try {
|
|
2440
|
-
const parsed = new URL(url.trim());
|
|
2441
|
-
return parsed.hostname.includes("chainstack.com");
|
|
2442
|
-
} catch {
|
|
2443
|
-
return false;
|
|
2444
|
-
}
|
|
2445
|
-
}
|
|
2446
|
-
function isQuickNodeUrl(url) {
|
|
2447
|
-
try {
|
|
2448
|
-
const parsed = new URL(url.trim());
|
|
2449
|
-
return parsed.hostname.includes("quiknode.pro");
|
|
2450
|
-
} catch {
|
|
2451
|
-
return false;
|
|
2452
|
-
}
|
|
2453
|
-
}
|
|
2454
|
-
function isTonCenterUrl(url) {
|
|
2455
|
-
try {
|
|
2456
|
-
const parsed = new URL(url.trim());
|
|
2457
|
-
return parsed.hostname.includes("toncenter.com");
|
|
2458
|
-
} catch {
|
|
2459
|
-
return false;
|
|
2460
|
-
}
|
|
2461
|
-
}
|
|
2462
|
-
function isOrbsUrl(url) {
|
|
2463
|
-
try {
|
|
2464
|
-
const parsed = new URL(url.trim());
|
|
2465
|
-
return parsed.hostname.includes("orbs.network") || parsed.hostname.includes("ton-access");
|
|
2466
|
-
} catch {
|
|
2467
|
-
return false;
|
|
2468
|
-
}
|
|
2469
|
-
}
|
|
2470
|
-
function buildRestUrl(baseEndpoint, method) {
|
|
2471
|
-
const base = toV2Base(baseEndpoint);
|
|
2472
|
-
return `${base}/${method}`;
|
|
2473
|
-
}
|
|
2474
|
-
function buildGetAddressStateUrl(baseEndpoint, address) {
|
|
2475
|
-
const base = toV2Base(baseEndpoint);
|
|
2476
|
-
return `${base}/getAddressState?address=${encodeURIComponent(address)}`;
|
|
2477
|
-
}
|
|
2478
|
-
function buildGetAddressBalanceUrl(baseEndpoint, address) {
|
|
2479
|
-
const base = toV2Base(baseEndpoint);
|
|
2480
|
-
return `${base}/getAddressBalance?address=${encodeURIComponent(address)}`;
|
|
2481
|
-
}
|
|
2482
|
-
function buildGetAddressInfoUrl(baseEndpoint, address) {
|
|
2483
|
-
const base = toV2Base(baseEndpoint);
|
|
2484
|
-
return `${base}/getAddressInformation?address=${encodeURIComponent(address)}`;
|
|
2485
|
-
}
|
|
2486
|
-
function detectNetworkFromEndpoint(endpoint) {
|
|
2487
|
-
const lower = endpoint.toLowerCase();
|
|
2488
|
-
if (lower.includes("testnet") || lower.includes("test") || lower.includes("sandbox")) {
|
|
2489
|
-
return "testnet";
|
|
2490
|
-
}
|
|
2491
|
-
if (lower.includes("mainnet") || lower.includes("main") || // TonCenter mainnet doesn't have 'mainnet' in URL
|
|
2492
|
-
lower.includes("toncenter.com") && !lower.includes("testnet")) {
|
|
2493
|
-
return "mainnet";
|
|
2494
|
-
}
|
|
2495
|
-
return null;
|
|
2496
|
-
}
|
|
2497
|
-
function isValidHttpUrl(str) {
|
|
2498
|
-
try {
|
|
2499
|
-
const url = new URL(str);
|
|
2500
|
-
return url.protocol === "http:" || url.protocol === "https:";
|
|
2501
|
-
} catch {
|
|
2502
|
-
return false;
|
|
2503
|
-
}
|
|
2504
|
-
}
|
|
2505
|
-
function isValidWsUrl(str) {
|
|
2506
|
-
try {
|
|
2507
|
-
const url = new URL(str);
|
|
2508
|
-
return url.protocol === "ws:" || url.protocol === "wss:";
|
|
2509
|
-
} catch {
|
|
2510
|
-
return false;
|
|
2511
|
-
}
|
|
2512
|
-
}
|
|
2513
|
-
|
|
2514
2602
|
// src/core/manager.ts
|
|
2515
2603
|
var consoleLogger5 = {
|
|
2516
2604
|
debug: (msg, data) => console.debug(`[ProviderManager] ${msg}`, data || ""),
|
|
@@ -3110,7 +3198,7 @@ var NodeAdapter = class {
|
|
|
3110
3198
|
network,
|
|
3111
3199
|
createdAt: Date.now()
|
|
3112
3200
|
};
|
|
3113
|
-
this.logger.debug(`Created TonClient for ${network}`, { endpoint });
|
|
3201
|
+
this.logger.debug(`Created TonClient for ${network}`, { endpoint: redactUrl(endpoint) });
|
|
3114
3202
|
return client;
|
|
3115
3203
|
}
|
|
3116
3204
|
/**
|
|
@@ -3647,6 +3735,6 @@ async function createBrowserAdapterForNetwork(network, configPath, logger) {
|
|
|
3647
3735
|
return new BrowserAdapter(manager, logger);
|
|
3648
3736
|
}
|
|
3649
3737
|
|
|
3650
|
-
export { ApiVersionSchema, BaseProvider, BrowserAdapter, CHAINSTACK_RATE_LIMIT, ChainstackProvider, ConfigError, DEFAULT_CONTRACT_TIMEOUT_MS, DEFAULT_HEALTH_CHECK_TIMEOUT_MS, DEFAULT_PROVIDERS, DEFAULT_PROVIDER_TIMEOUT_MS, DEFAULT_RATE_LIMIT, GenericProvider, GetBlockProvider, HealthChecker, NetworkSchema, NodeAdapter, ORBS_RATE_LIMIT, OnFinalityProvider, OrbsProvider, ProviderConfigSchema, ProviderError, ProviderManager, ProviderRegistry, ProviderSelector, ProviderTypeSchema, QUICKNODE_RATE_LIMIT, QuickNodeProvider, RateLimitError, RateLimiterManager, RpcConfigSchema, TatumProvider, TimeoutError, TokenBucketRateLimiter, TonCenterProvider, buildGetAddressBalanceUrl, buildGetAddressInfoUrl, buildGetAddressStateUrl, buildRestUrl, createBrowserAdapter, createBrowserAdapterForNetwork, createDefaultConfig, createDefaultRegistry, createEmptyConfig, createHealthChecker, createNodeAdapter, createProvider, createProviderManager, createRateLimiter, createRateLimiterManager, createRegistry, createRegistryFromData, createRegistryFromFile, createSelector, createTimeoutController, detectNetworkFromEndpoint, fetchWithTimeout, getBaseUrl, getDefaultProvidersForNetwork, getEnvVar, getProviderManager, getProvidersForNetwork, getRateLimitForType, getTonClient, getTonClientForNetwork, getTonClientWithRateLimit, isApiVersion, isChainstackUrl, isNetwork, isOrbsUrl, isProviderType, isQuickNodeUrl, isRateLimitError, isTimeoutError, isTonCenterUrl, isValidHttpUrl, isValidWsUrl, loadBuiltinConfig, loadConfig, loadConfigFromData, loadConfigFromUrl, mergeConfigs, mergeWithDefaults, normalizeV2Endpoint, parseProviderConfig, parseRpcConfig, resetNodeAdapter, resolveAllProviders, resolveEndpoints, resolveKeyPlaceholder, resolveProvider, sleep, toV2Base, toV3Base, withRetry, withTimeout, withTimeoutAndRetry, withTimeoutFn };
|
|
3738
|
+
export { ApiVersionSchema, BaseProvider, BrowserAdapter, CHAINSTACK_RATE_LIMIT, ChainstackProvider, ConfigError, DEFAULT_CONTRACT_TIMEOUT_MS, DEFAULT_HEALTH_CHECK_TIMEOUT_MS, DEFAULT_PROVIDERS, DEFAULT_PROVIDER_TIMEOUT_MS, DEFAULT_RATE_LIMIT, GenericProvider, GetBlockProvider, HealthChecker, NetworkSchema, NodeAdapter, ORBS_RATE_LIMIT, OnFinalityProvider, OrbsProvider, ProviderConfigSchema, ProviderError, ProviderManager, ProviderRegistry, ProviderSelector, ProviderTypeSchema, QUICKNODE_RATE_LIMIT, QuickNodeProvider, RateLimitError, RateLimiterManager, RpcConfigSchema, TatumProvider, TimeoutError, TokenBucketRateLimiter, TonCenterProvider, buildGetAddressBalanceUrl, buildGetAddressInfoUrl, buildGetAddressStateUrl, buildRestUrl, createBrowserAdapter, createBrowserAdapterForNetwork, createDefaultConfig, createDefaultRegistry, createEmptyConfig, createHealthChecker, createNodeAdapter, createProvider, createProviderManager, createRateLimiter, createRateLimiterManager, createRegistry, createRegistryFromData, createRegistryFromFile, createSelector, createTimeoutController, detectNetworkFromEndpoint, fetchWithTimeout, getBaseUrl, getDefaultProvidersForNetwork, getEnvVar, getProviderManager, getProvidersForNetwork, getRateLimitForType, getTonClient, getTonClientForNetwork, getTonClientWithRateLimit, isApiVersion, isChainstackUrl, isNetwork, isOrbsUrl, isProviderType, isQuickNodeUrl, isRateLimitError, isTimeoutError, isTonCenterUrl, isValidHttpUrl, isValidWsUrl, loadBuiltinConfig, loadConfig, loadConfigFromData, loadConfigFromUrl, mergeConfigs, mergeWithDefaults, normalizeV2Endpoint, parseProviderConfig, parseRpcConfig, redactUrl, resetNodeAdapter, resolveAllProviders, resolveEndpoints, resolveKeyPlaceholder, resolveProvider, sleep, toV2Base, toV3Base, withRetry, withTimeout, withTimeoutAndRetry, withTimeoutFn };
|
|
3651
3739
|
//# sourceMappingURL=index.js.map
|
|
3652
3740
|
//# sourceMappingURL=index.js.map
|