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