ton-provider-system 0.1.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 ADDED
@@ -0,0 +1,2696 @@
1
+ 'use strict';
2
+
3
+ var zod = require('zod');
4
+ var ton = require('@ton/ton');
5
+
6
+ // src/types.ts
7
+ var TimeoutError = class extends Error {
8
+ constructor(operation, timeoutMs, message) {
9
+ super(message || `Operation "${operation}" timed out after ${timeoutMs}ms`);
10
+ this.operation = operation;
11
+ this.timeoutMs = timeoutMs;
12
+ this.name = "TimeoutError";
13
+ }
14
+ };
15
+ var ProviderError = class extends Error {
16
+ constructor(providerId, operation, message, cause) {
17
+ super(`[${providerId}] ${operation}: ${message}`);
18
+ this.providerId = providerId;
19
+ this.operation = operation;
20
+ this.cause = cause;
21
+ this.name = "ProviderError";
22
+ }
23
+ };
24
+ var RateLimitError = class extends Error {
25
+ constructor(providerId, retryAfterMs) {
26
+ super(`Provider ${providerId} rate limited${retryAfterMs ? `, retry after ${retryAfterMs}ms` : ""}`);
27
+ this.providerId = providerId;
28
+ this.retryAfterMs = retryAfterMs;
29
+ this.name = "RateLimitError";
30
+ }
31
+ };
32
+ var ConfigError = class extends Error {
33
+ constructor(message) {
34
+ super(message);
35
+ this.name = "ConfigError";
36
+ }
37
+ };
38
+ var NetworkSchema = zod.z.enum(["testnet", "mainnet"]);
39
+ var ProviderTypeSchema = zod.z.enum([
40
+ "chainstack",
41
+ "quicknode",
42
+ "toncenter",
43
+ "orbs",
44
+ "onfinality",
45
+ "ankr",
46
+ "getblock",
47
+ "tatum",
48
+ "tonhub",
49
+ "custom"
50
+ ]);
51
+ var ApiVersionSchema = zod.z.enum(["v2", "v3", "v4"]);
52
+ var ProviderEndpointsSchema = zod.z.object({
53
+ v2: zod.z.string().url().optional(),
54
+ v3: zod.z.string().url().optional(),
55
+ v4: zod.z.string().url().optional(),
56
+ ws: zod.z.string().url().optional()
57
+ }).refine(
58
+ (data) => data.v2 || data.v3 || data.v4,
59
+ { message: "At least one endpoint (v2, v3, or v4) must be provided" }
60
+ );
61
+ var ProviderConfigSchema = zod.z.object({
62
+ name: zod.z.string().min(1, "Provider name is required"),
63
+ type: ProviderTypeSchema,
64
+ network: NetworkSchema,
65
+ endpoints: ProviderEndpointsSchema,
66
+ keyEnvVar: zod.z.string().optional(),
67
+ apiKeyEnvVar: zod.z.string().optional(),
68
+ rps: zod.z.number().int().positive().default(1),
69
+ priority: zod.z.number().int().nonnegative().default(10),
70
+ enabled: zod.z.boolean().default(true),
71
+ isDynamic: zod.z.boolean().optional().default(false),
72
+ description: zod.z.string().optional()
73
+ });
74
+ var NetworkDefaultsSchema = zod.z.object({
75
+ testnet: zod.z.array(zod.z.string()).default([]),
76
+ mainnet: zod.z.array(zod.z.string()).default([])
77
+ });
78
+ var RpcConfigSchema = zod.z.object({
79
+ $schema: zod.z.string().optional(),
80
+ version: zod.z.string().default("1.0"),
81
+ providers: zod.z.record(zod.z.string(), ProviderConfigSchema),
82
+ defaults: NetworkDefaultsSchema
83
+ }).refine(
84
+ (data) => {
85
+ const providerIds = Object.keys(data.providers);
86
+ const allDefaults = [...data.defaults.testnet, ...data.defaults.mainnet];
87
+ const invalidIds = allDefaults.filter((id) => !providerIds.includes(id));
88
+ return invalidIds.length === 0;
89
+ },
90
+ {
91
+ message: "Default provider IDs must reference existing providers"
92
+ }
93
+ );
94
+ function parseRpcConfig(data) {
95
+ const result = RpcConfigSchema.safeParse(data);
96
+ if (!result.success) {
97
+ const errors = result.error.errors.map((e) => {
98
+ const path = e.path.join(".");
99
+ return ` - ${path}: ${e.message}`;
100
+ }).join("\n");
101
+ throw new Error(`Invalid rpc.json configuration:
102
+ ${errors}`);
103
+ }
104
+ return result.data;
105
+ }
106
+ function parseProviderConfig(id, data) {
107
+ const result = ProviderConfigSchema.safeParse(data);
108
+ if (!result.success) {
109
+ const errors = result.error.errors.map((e) => {
110
+ const path = e.path.join(".");
111
+ return `${path}: ${e.message}`;
112
+ }).join(", ");
113
+ throw new Error(`Invalid provider "${id}": ${errors}`);
114
+ }
115
+ return result.data;
116
+ }
117
+ function createEmptyConfig() {
118
+ return {
119
+ version: "1.0",
120
+ providers: {},
121
+ defaults: {
122
+ testnet: [],
123
+ mainnet: []
124
+ }
125
+ };
126
+ }
127
+ function mergeConfigs(base, override) {
128
+ return {
129
+ ...base,
130
+ ...override,
131
+ providers: {
132
+ ...base.providers,
133
+ ...override.providers || {}
134
+ },
135
+ defaults: {
136
+ testnet: override.defaults?.testnet || base.defaults.testnet,
137
+ mainnet: override.defaults?.mainnet || base.defaults.mainnet
138
+ }
139
+ };
140
+ }
141
+ function isNetwork(value) {
142
+ return value === "testnet" || value === "mainnet";
143
+ }
144
+ function isProviderType(value) {
145
+ const types = [
146
+ "chainstack",
147
+ "quicknode",
148
+ "toncenter",
149
+ "orbs",
150
+ "onfinality",
151
+ "ankr",
152
+ "getblock",
153
+ "tatum",
154
+ "tonhub",
155
+ "custom"
156
+ ];
157
+ return typeof value === "string" && types.includes(value);
158
+ }
159
+ function isApiVersion(value) {
160
+ return value === "v2" || value === "v3" || value === "v4";
161
+ }
162
+
163
+ // src/config/parser.ts
164
+ var RPC_CONFIG_FILENAME = "rpc.json";
165
+ function getEnvVar(name) {
166
+ if (typeof process !== "undefined" && process.env) {
167
+ return process.env[name];
168
+ }
169
+ if (typeof window !== "undefined" && window.__ENV__) {
170
+ return window.__ENV__[name];
171
+ }
172
+ return void 0;
173
+ }
174
+ function resolveKeyPlaceholder(url, keyEnvVar) {
175
+ if (!keyEnvVar || !url.includes("{key}")) {
176
+ return url;
177
+ }
178
+ const key = getEnvVar(keyEnvVar);
179
+ if (!key) {
180
+ console.warn(`[ConfigParser] Environment variable ${keyEnvVar} not set for URL: ${url}`);
181
+ return url;
182
+ }
183
+ return url.replace("{key}", key);
184
+ }
185
+ function resolveEndpoints(endpoints, keyEnvVar) {
186
+ return {
187
+ v2: endpoints.v2 ? resolveKeyPlaceholder(endpoints.v2, keyEnvVar) : void 0,
188
+ v3: endpoints.v3 ? resolveKeyPlaceholder(endpoints.v3, keyEnvVar) : void 0,
189
+ v4: endpoints.v4 ? resolveKeyPlaceholder(endpoints.v4, keyEnvVar) : void 0,
190
+ ws: endpoints.ws ? resolveKeyPlaceholder(endpoints.ws, keyEnvVar) : void 0
191
+ };
192
+ }
193
+ function resolveProvider(id, config) {
194
+ if (!config.enabled) {
195
+ return null;
196
+ }
197
+ const resolved = resolveEndpoints(config.endpoints, config.keyEnvVar);
198
+ if (!resolved.v2 && !resolved.v3 && !resolved.v4) {
199
+ console.warn(`[ConfigParser] Provider ${id} has no valid endpoints after resolution`);
200
+ return null;
201
+ }
202
+ const apiKey = config.apiKeyEnvVar ? getEnvVar(config.apiKeyEnvVar) : void 0;
203
+ return {
204
+ id,
205
+ name: config.name,
206
+ type: config.type,
207
+ network: config.network,
208
+ endpointV2: resolved.v2 || resolved.v3 || "",
209
+ // Fallback to v3 if v2 not available
210
+ endpointV3: resolved.v3,
211
+ endpointV4: resolved.v4,
212
+ endpointWs: resolved.ws,
213
+ apiKey,
214
+ rps: config.rps,
215
+ priority: config.priority,
216
+ isDynamic: config.isDynamic || false
217
+ };
218
+ }
219
+ function resolveAllProviders(config) {
220
+ const resolved = [];
221
+ for (const [id, providerConfig] of Object.entries(config.providers)) {
222
+ const provider = resolveProvider(id, providerConfig);
223
+ if (provider) {
224
+ resolved.push(provider);
225
+ }
226
+ }
227
+ return resolved;
228
+ }
229
+ function getProvidersForNetwork(config, network) {
230
+ const all = resolveAllProviders(config);
231
+ return all.filter((p) => p.network === network);
232
+ }
233
+ function getDefaultProvidersForNetwork(config, network) {
234
+ const defaultOrder = config.defaults[network];
235
+ const networkProviders = getProvidersForNetwork(config, network);
236
+ const inOrder = [];
237
+ const remaining = [];
238
+ for (const provider of networkProviders) {
239
+ const defaultIndex = defaultOrder.indexOf(provider.id);
240
+ if (defaultIndex !== -1) {
241
+ inOrder[defaultIndex] = provider;
242
+ } else {
243
+ remaining.push(provider);
244
+ }
245
+ }
246
+ const orderedProviders = inOrder.filter(Boolean);
247
+ remaining.sort((a, b) => a.priority - b.priority);
248
+ return [...orderedProviders, ...remaining];
249
+ }
250
+ async function loadBuiltinConfig() {
251
+ const fs = await import('fs').then((m) => m.promises);
252
+ const path = await import('path');
253
+ const possiblePaths = [
254
+ // When running from project root (e.g., ts-node scripts/...)
255
+ path.resolve(process.cwd(), "provider_system", RPC_CONFIG_FILENAME),
256
+ // When running from provider_system folder
257
+ path.resolve(process.cwd(), RPC_CONFIG_FILENAME),
258
+ // Relative to this file (CommonJS style)
259
+ path.resolve(__dirname, "..", RPC_CONFIG_FILENAME)
260
+ ];
261
+ for (const configPath of possiblePaths) {
262
+ try {
263
+ const content = await fs.readFile(configPath, "utf-8");
264
+ const data = JSON.parse(content);
265
+ return parseRpcConfig(data);
266
+ } catch (error) {
267
+ if (error.code !== "ENOENT") {
268
+ throw new Error(`Failed to load RPC config from ${configPath}: ${error.message}`);
269
+ }
270
+ }
271
+ }
272
+ console.warn(`[ConfigParser] Config file ${RPC_CONFIG_FILENAME} not found, using defaults`);
273
+ return createDefaultConfig();
274
+ }
275
+ async function loadConfigFromUrl(url) {
276
+ try {
277
+ const response = await fetch(url);
278
+ if (!response.ok) {
279
+ throw new Error(`HTTP ${response.status}: ${response.statusText}`);
280
+ }
281
+ const data = await response.json();
282
+ return parseRpcConfig(data);
283
+ } catch (error) {
284
+ throw new Error(`Failed to load RPC config from ${url}: ${error.message}`);
285
+ }
286
+ }
287
+ function loadConfigFromData(data) {
288
+ return parseRpcConfig(data);
289
+ }
290
+ async function loadConfig() {
291
+ if (typeof window !== "undefined" && window.__RPC_CONFIG__) {
292
+ return parseRpcConfig(window.__RPC_CONFIG__);
293
+ }
294
+ if (typeof process !== "undefined" && typeof process.cwd === "function") {
295
+ return loadBuiltinConfig();
296
+ }
297
+ console.warn("[ConfigParser] No config source available, using defaults");
298
+ return createDefaultConfig();
299
+ }
300
+ var DEFAULT_PROVIDERS = {
301
+ toncenter_testnet: {
302
+ name: "TON Center Testnet",
303
+ type: "toncenter",
304
+ network: "testnet",
305
+ endpoints: {
306
+ v2: "https://testnet.toncenter.com/api/v2"
307
+ },
308
+ rps: 1,
309
+ // Without API key
310
+ priority: 100,
311
+ enabled: true,
312
+ description: "Official TON Center public endpoint"
313
+ },
314
+ orbs_testnet: {
315
+ name: "Orbs TON Access Testnet",
316
+ type: "orbs",
317
+ network: "testnet",
318
+ endpoints: {
319
+ v2: "https://ton-testnet.orbs.network/api/v2"
320
+ },
321
+ rps: 10,
322
+ priority: 50,
323
+ enabled: true,
324
+ isDynamic: true,
325
+ description: "Decentralized gateway - no API key needed"
326
+ },
327
+ toncenter_mainnet: {
328
+ name: "TON Center Mainnet",
329
+ type: "toncenter",
330
+ network: "mainnet",
331
+ endpoints: {
332
+ v2: "https://toncenter.com/api/v2"
333
+ },
334
+ rps: 1,
335
+ // Without API key
336
+ priority: 100,
337
+ enabled: true,
338
+ description: "Official TON Center public endpoint"
339
+ },
340
+ orbs_mainnet: {
341
+ name: "Orbs TON Access Mainnet",
342
+ type: "orbs",
343
+ network: "mainnet",
344
+ endpoints: {
345
+ v2: "https://ton-mainnet.orbs.network/api/v2"
346
+ },
347
+ rps: 10,
348
+ priority: 50,
349
+ enabled: true,
350
+ isDynamic: true,
351
+ description: "Decentralized gateway - no API key needed"
352
+ }
353
+ };
354
+ function createDefaultConfig() {
355
+ return {
356
+ version: "1.0",
357
+ providers: { ...DEFAULT_PROVIDERS },
358
+ defaults: {
359
+ testnet: ["orbs_testnet", "toncenter_testnet"],
360
+ mainnet: ["orbs_mainnet", "toncenter_mainnet"]
361
+ }
362
+ };
363
+ }
364
+ function mergeWithDefaults(config) {
365
+ const defaults = createDefaultConfig();
366
+ return {
367
+ ...config,
368
+ providers: {
369
+ ...defaults.providers,
370
+ ...config.providers
371
+ },
372
+ defaults: {
373
+ testnet: config.defaults.testnet.length > 0 ? config.defaults.testnet : defaults.defaults.testnet,
374
+ mainnet: config.defaults.mainnet.length > 0 ? config.defaults.mainnet : defaults.defaults.mainnet
375
+ }
376
+ };
377
+ }
378
+
379
+ // src/core/registry.ts
380
+ var consoleLogger = {
381
+ debug: (msg, data) => console.debug(`[ProviderRegistry] ${msg}`, data || ""),
382
+ info: (msg, data) => console.log(`[ProviderRegistry] ${msg}`, data || ""),
383
+ warn: (msg, data) => console.warn(`[ProviderRegistry] ${msg}`, data || ""),
384
+ error: (msg, data) => console.error(`[ProviderRegistry] ${msg}`, data || "")
385
+ };
386
+ var ProviderRegistry = class {
387
+ constructor(config, logger) {
388
+ this.providers = /* @__PURE__ */ new Map();
389
+ this.config = config || createDefaultConfig();
390
+ this.logger = logger || consoleLogger;
391
+ this.loadProviders();
392
+ }
393
+ /**
394
+ * Load and resolve all providers from config
395
+ */
396
+ loadProviders() {
397
+ this.providers.clear();
398
+ const resolved = resolveAllProviders(this.config);
399
+ for (const provider of resolved) {
400
+ this.providers.set(provider.id, provider);
401
+ }
402
+ this.logger.info(`Loaded ${this.providers.size} providers`);
403
+ }
404
+ /**
405
+ * Get a provider by ID
406
+ */
407
+ getProvider(id) {
408
+ return this.providers.get(id);
409
+ }
410
+ /**
411
+ * Get all providers
412
+ */
413
+ getAllProviders() {
414
+ return Array.from(this.providers.values());
415
+ }
416
+ /**
417
+ * Get providers for a specific network
418
+ */
419
+ getProvidersForNetwork(network) {
420
+ return Array.from(this.providers.values()).filter(
421
+ (p) => p.network === network
422
+ );
423
+ }
424
+ /**
425
+ * Get providers in default order for a network
426
+ */
427
+ getDefaultOrderForNetwork(network) {
428
+ return getDefaultProvidersForNetwork(this.config, network);
429
+ }
430
+ /**
431
+ * Get providers by type
432
+ */
433
+ getProvidersByType(type) {
434
+ return Array.from(this.providers.values()).filter(
435
+ (p) => p.type === type
436
+ );
437
+ }
438
+ /**
439
+ * Get providers that have v2 API endpoints
440
+ */
441
+ getV2Providers() {
442
+ return Array.from(this.providers.values()).filter(
443
+ (p) => p.endpointV2 && p.endpointV2.length > 0
444
+ );
445
+ }
446
+ /**
447
+ * Get v2 providers for a specific network
448
+ */
449
+ getV2ProvidersForNetwork(network) {
450
+ return this.getProvidersForNetwork(network).filter(
451
+ (p) => p.endpointV2 && p.endpointV2.length > 0
452
+ );
453
+ }
454
+ /**
455
+ * Check if a provider exists
456
+ */
457
+ hasProvider(id) {
458
+ return this.providers.has(id);
459
+ }
460
+ /**
461
+ * Get provider count
462
+ */
463
+ get size() {
464
+ return this.providers.size;
465
+ }
466
+ /**
467
+ * Get the underlying config
468
+ */
469
+ getConfig() {
470
+ return this.config;
471
+ }
472
+ /**
473
+ * Update config and reload providers
474
+ */
475
+ updateConfig(config) {
476
+ this.config = config;
477
+ this.loadProviders();
478
+ }
479
+ /**
480
+ * Add or update a provider at runtime
481
+ */
482
+ setProvider(id, provider) {
483
+ this.providers.set(id, provider);
484
+ this.logger.debug(`Provider ${id} added/updated`);
485
+ }
486
+ /**
487
+ * Remove a provider
488
+ */
489
+ removeProvider(id) {
490
+ const removed = this.providers.delete(id);
491
+ if (removed) {
492
+ this.logger.debug(`Provider ${id} removed`);
493
+ }
494
+ return removed;
495
+ }
496
+ /**
497
+ * Get provider IDs
498
+ */
499
+ getProviderIds() {
500
+ return Array.from(this.providers.keys());
501
+ }
502
+ /**
503
+ * Get network default provider IDs
504
+ */
505
+ getDefaultProviderIds(network) {
506
+ return this.config.defaults[network];
507
+ }
508
+ /**
509
+ * Find provider by endpoint URL (useful for error reporting)
510
+ */
511
+ findProviderByEndpoint(endpoint) {
512
+ const normalizedEndpoint = endpoint.toLowerCase().replace(/\/jsonrpc$/i, "");
513
+ for (const provider of this.providers.values()) {
514
+ const v2Normalized = provider.endpointV2?.toLowerCase().replace(/\/jsonrpc$/i, "");
515
+ const v3Normalized = provider.endpointV3?.toLowerCase().replace(/\/jsonrpc$/i, "");
516
+ if (v2Normalized && normalizedEndpoint.includes(v2Normalized.split("/api/")[0])) {
517
+ return provider;
518
+ }
519
+ if (v3Normalized && normalizedEndpoint.includes(v3Normalized.split("/api/")[0])) {
520
+ return provider;
521
+ }
522
+ }
523
+ return void 0;
524
+ }
525
+ };
526
+ async function createRegistry(logger) {
527
+ const config = await loadConfig();
528
+ const mergedConfig = mergeWithDefaults(config);
529
+ return new ProviderRegistry(mergedConfig, logger);
530
+ }
531
+ async function createRegistryFromFile(_filePath, logger) {
532
+ return createRegistry(logger);
533
+ }
534
+ function createDefaultRegistry(logger) {
535
+ const config = createDefaultConfig();
536
+ return new ProviderRegistry(config, logger);
537
+ }
538
+ function createRegistryFromData(data, logger) {
539
+ const mergedConfig = mergeWithDefaults(data);
540
+ return new ProviderRegistry(mergedConfig, logger);
541
+ }
542
+
543
+ // src/utils/endpoint.ts
544
+ function normalizeV2Endpoint(endpoint) {
545
+ let normalized = endpoint.trim();
546
+ if (normalized.endsWith("/")) {
547
+ normalized = normalized.slice(0, -1);
548
+ }
549
+ if (normalized.toLowerCase().endsWith("/jsonrpc")) {
550
+ return normalized;
551
+ }
552
+ if (normalized.endsWith("/api/v2")) {
553
+ return normalized + "/jsonRPC";
554
+ }
555
+ if (normalized.endsWith("/api/v3")) {
556
+ return normalized.replace("/api/v3", "/api/v2/jsonRPC");
557
+ }
558
+ try {
559
+ const url = new URL(normalized);
560
+ if (!url.pathname || url.pathname === "/") {
561
+ return normalized + "/jsonRPC";
562
+ }
563
+ } catch {
564
+ }
565
+ return normalized;
566
+ }
567
+ function toV2Base(endpoint) {
568
+ let normalized = endpoint.trim();
569
+ if (normalized.endsWith("/")) {
570
+ normalized = normalized.slice(0, -1);
571
+ }
572
+ if (normalized.toLowerCase().endsWith("/jsonrpc")) {
573
+ normalized = normalized.slice(0, -8);
574
+ }
575
+ normalized = normalized.replace(/\/api\/v3\b/, "/api/v2");
576
+ if (!normalized.endsWith("/api/v2")) {
577
+ if (normalized.includes("/api/v2")) {
578
+ const idx = normalized.indexOf("/api/v2");
579
+ normalized = normalized.slice(0, idx + 7);
580
+ }
581
+ }
582
+ return normalized;
583
+ }
584
+ function toV3Base(endpoint) {
585
+ let normalized = toV2Base(endpoint);
586
+ return normalized.replace("/api/v2", "/api/v3");
587
+ }
588
+ function getBaseUrl(endpoint) {
589
+ try {
590
+ const url = new URL(endpoint);
591
+ return `${url.protocol}//${url.host}`;
592
+ } catch {
593
+ return endpoint;
594
+ }
595
+ }
596
+ function isChainstackUrl(url) {
597
+ try {
598
+ const parsed = new URL(url.trim());
599
+ return parsed.hostname.includes("chainstack.com");
600
+ } catch {
601
+ return false;
602
+ }
603
+ }
604
+ function isQuickNodeUrl(url) {
605
+ try {
606
+ const parsed = new URL(url.trim());
607
+ return parsed.hostname.includes("quiknode.pro");
608
+ } catch {
609
+ return false;
610
+ }
611
+ }
612
+ function isTonCenterUrl(url) {
613
+ try {
614
+ const parsed = new URL(url.trim());
615
+ return parsed.hostname.includes("toncenter.com");
616
+ } catch {
617
+ return false;
618
+ }
619
+ }
620
+ function isOrbsUrl(url) {
621
+ try {
622
+ const parsed = new URL(url.trim());
623
+ return parsed.hostname.includes("orbs.network") || parsed.hostname.includes("ton-access");
624
+ } catch {
625
+ return false;
626
+ }
627
+ }
628
+ function buildRestUrl(baseEndpoint, method) {
629
+ const base = toV2Base(baseEndpoint);
630
+ return `${base}/${method}`;
631
+ }
632
+ function buildGetAddressStateUrl(baseEndpoint, address) {
633
+ const base = toV2Base(baseEndpoint);
634
+ return `${base}/getAddressState?address=${encodeURIComponent(address)}`;
635
+ }
636
+ function buildGetAddressBalanceUrl(baseEndpoint, address) {
637
+ const base = toV2Base(baseEndpoint);
638
+ return `${base}/getAddressBalance?address=${encodeURIComponent(address)}`;
639
+ }
640
+ function buildGetAddressInfoUrl(baseEndpoint, address) {
641
+ const base = toV2Base(baseEndpoint);
642
+ return `${base}/getAddressInformation?address=${encodeURIComponent(address)}`;
643
+ }
644
+ function detectNetworkFromEndpoint(endpoint) {
645
+ const lower = endpoint.toLowerCase();
646
+ if (lower.includes("testnet") || lower.includes("test") || lower.includes("sandbox")) {
647
+ return "testnet";
648
+ }
649
+ if (lower.includes("mainnet") || lower.includes("main") || // TonCenter mainnet doesn't have 'mainnet' in URL
650
+ lower.includes("toncenter.com") && !lower.includes("testnet")) {
651
+ return "mainnet";
652
+ }
653
+ return null;
654
+ }
655
+ function isValidHttpUrl(str) {
656
+ try {
657
+ const url = new URL(str);
658
+ return url.protocol === "http:" || url.protocol === "https:";
659
+ } catch {
660
+ return false;
661
+ }
662
+ }
663
+ function isValidWsUrl(str) {
664
+ try {
665
+ const url = new URL(str);
666
+ return url.protocol === "ws:" || url.protocol === "wss:";
667
+ } catch {
668
+ return false;
669
+ }
670
+ }
671
+
672
+ // src/core/healthChecker.ts
673
+ var consoleLogger2 = {
674
+ debug: (msg, data) => console.debug(`[HealthChecker] ${msg}`, data || ""),
675
+ info: (msg, data) => console.log(`[HealthChecker] ${msg}`, data || ""),
676
+ warn: (msg, data) => console.warn(`[HealthChecker] ${msg}`, data || ""),
677
+ error: (msg, data) => console.error(`[HealthChecker] ${msg}`, data || "")
678
+ };
679
+ var DEFAULT_CONFIG = {
680
+ timeoutMs: 1e4,
681
+ maxBlocksBehind: 10,
682
+ degradedLatencyMs: 3e3
683
+ };
684
+ var HealthChecker = class {
685
+ constructor(config, logger) {
686
+ this.results = /* @__PURE__ */ new Map();
687
+ this.highestSeqno = /* @__PURE__ */ new Map();
688
+ this.config = { ...DEFAULT_CONFIG, ...config };
689
+ this.logger = logger || consoleLogger2;
690
+ }
691
+ /**
692
+ * Test a single provider's health
693
+ */
694
+ async testProvider(provider) {
695
+ const startTime = performance.now();
696
+ const key = this.getResultKey(provider.id, provider.network);
697
+ const testingResult = {
698
+ id: provider.id,
699
+ network: provider.network,
700
+ success: false,
701
+ status: "testing",
702
+ latencyMs: null,
703
+ seqno: null,
704
+ blocksBehind: 0,
705
+ lastTested: null
706
+ };
707
+ this.results.set(key, testingResult);
708
+ try {
709
+ const endpoint = await this.getEndpoint(provider);
710
+ if (!endpoint) {
711
+ throw new Error("No valid endpoint available");
712
+ }
713
+ const normalizedEndpoint = normalizeV2Endpoint(endpoint);
714
+ const info = await this.callGetMasterchainInfo(normalizedEndpoint);
715
+ const endTime = performance.now();
716
+ const latencyMs = Math.round(endTime - startTime);
717
+ const seqno = info.last?.seqno || 0;
718
+ const currentHighest = this.highestSeqno.get(provider.network) || 0;
719
+ if (seqno > currentHighest) {
720
+ this.highestSeqno.set(provider.network, seqno);
721
+ }
722
+ const blocksBehind = Math.max(0, (this.highestSeqno.get(provider.network) || seqno) - seqno);
723
+ let status = "available";
724
+ if (blocksBehind > this.config.maxBlocksBehind) {
725
+ status = "stale";
726
+ } else if (latencyMs > this.config.degradedLatencyMs) {
727
+ status = "degraded";
728
+ }
729
+ const result = {
730
+ id: provider.id,
731
+ network: provider.network,
732
+ success: true,
733
+ status,
734
+ latencyMs,
735
+ seqno,
736
+ blocksBehind,
737
+ lastTested: /* @__PURE__ */ new Date(),
738
+ cachedEndpoint: normalizedEndpoint
739
+ };
740
+ this.results.set(key, result);
741
+ this.logger.debug(
742
+ `Provider ${provider.id} health check: ${status} (${latencyMs}ms, seqno=${seqno}, behind=${blocksBehind})`
743
+ );
744
+ return result;
745
+ } catch (error) {
746
+ const endTime = performance.now();
747
+ const latencyMs = Math.round(endTime - startTime);
748
+ const errorMsg = error.message || String(error) || "Unknown error";
749
+ const is429 = errorMsg.includes("429") || errorMsg.toLowerCase().includes("rate limit");
750
+ const is404 = errorMsg.includes("404") || errorMsg.toLowerCase().includes("not found");
751
+ const isTimeout = error.name === "AbortError" || errorMsg.includes("timeout");
752
+ let status = "offline";
753
+ if (is429) {
754
+ status = "degraded";
755
+ } else if (is404) {
756
+ status = "offline";
757
+ } else if (isTimeout) {
758
+ status = "offline";
759
+ }
760
+ const result = {
761
+ id: provider.id,
762
+ network: provider.network,
763
+ success: false,
764
+ status,
765
+ latencyMs: isTimeout ? null : latencyMs,
766
+ seqno: null,
767
+ blocksBehind: 0,
768
+ lastTested: /* @__PURE__ */ new Date(),
769
+ error: errorMsg
770
+ };
771
+ this.results.set(key, result);
772
+ this.logger.warn(`Provider ${provider.id} health check failed: ${result.error}`);
773
+ return result;
774
+ }
775
+ }
776
+ /**
777
+ * Test multiple providers in parallel with staggered batches
778
+ */
779
+ async testProviders(providers, batchSize = 2, batchDelayMs = 300) {
780
+ const results = [];
781
+ for (let i = 0; i < providers.length; i += batchSize) {
782
+ const batch = providers.slice(i, i + batchSize);
783
+ const batchResults = await Promise.all(
784
+ batch.map((p) => this.testProvider(p))
785
+ );
786
+ results.push(...batchResults);
787
+ if (i + batchSize < providers.length && batchDelayMs > 0) {
788
+ await this.sleep(batchDelayMs);
789
+ }
790
+ }
791
+ return results;
792
+ }
793
+ /**
794
+ * Get the last health result for a provider
795
+ */
796
+ getResult(providerId, network) {
797
+ const key = this.getResultKey(providerId, network);
798
+ return this.results.get(key);
799
+ }
800
+ /**
801
+ * Get all results for a network
802
+ */
803
+ getResultsForNetwork(network) {
804
+ const results = [];
805
+ for (const [key, result] of this.results) {
806
+ if (result.network === network) {
807
+ results.push(result);
808
+ }
809
+ }
810
+ return results;
811
+ }
812
+ /**
813
+ * Get available providers for a network (status = available or degraded)
814
+ */
815
+ getAvailableProviders(network) {
816
+ return this.getResultsForNetwork(network).filter(
817
+ (r) => r.status === "available" || r.status === "degraded"
818
+ );
819
+ }
820
+ /**
821
+ * Get the best provider for a network (lowest latency among available)
822
+ */
823
+ getBestProvider(network) {
824
+ const available = this.getAvailableProviders(network).filter((r) => r.latencyMs !== null).sort((a, b) => (a.latencyMs ?? Infinity) - (b.latencyMs ?? Infinity));
825
+ return available[0];
826
+ }
827
+ /**
828
+ * Get highest known seqno for a network
829
+ */
830
+ getHighestSeqno(network) {
831
+ return this.highestSeqno.get(network) || 0;
832
+ }
833
+ /**
834
+ * Clear all results
835
+ */
836
+ clearResults() {
837
+ this.results.clear();
838
+ this.highestSeqno.clear();
839
+ }
840
+ /**
841
+ * Mark a provider as degraded (e.g., on 429 error)
842
+ */
843
+ markDegraded(providerId, network, error) {
844
+ const key = this.getResultKey(providerId, network);
845
+ const existing = this.results.get(key);
846
+ if (existing) {
847
+ this.results.set(key, {
848
+ ...existing,
849
+ status: "degraded",
850
+ error: error || "Marked as degraded",
851
+ lastTested: /* @__PURE__ */ new Date()
852
+ });
853
+ }
854
+ }
855
+ /**
856
+ * Mark a provider as offline
857
+ */
858
+ markOffline(providerId, network, error) {
859
+ const key = this.getResultKey(providerId, network);
860
+ const existing = this.results.get(key);
861
+ if (existing) {
862
+ this.results.set(key, {
863
+ ...existing,
864
+ status: "offline",
865
+ error: error || "Marked as offline",
866
+ lastTested: /* @__PURE__ */ new Date()
867
+ });
868
+ }
869
+ }
870
+ // ========================================================================
871
+ // Private Methods
872
+ // ========================================================================
873
+ getResultKey(providerId, network) {
874
+ return `${providerId}-${network}`;
875
+ }
876
+ /**
877
+ * Get endpoint URL for a provider (handles dynamic providers like Orbs)
878
+ */
879
+ async getEndpoint(provider) {
880
+ if (provider.isDynamic && provider.type === "orbs") {
881
+ try {
882
+ const { getHttpEndpoint } = await import('@orbs-network/ton-access');
883
+ const endpoint = await getHttpEndpoint({ network: provider.network });
884
+ return endpoint;
885
+ } catch (error) {
886
+ this.logger.warn(`Failed to get Orbs endpoint: ${error.message}`);
887
+ return null;
888
+ }
889
+ }
890
+ return provider.endpointV2 || provider.endpointV3 || null;
891
+ }
892
+ /**
893
+ * Call getMasterchainInfo API
894
+ */
895
+ async callGetMasterchainInfo(endpoint) {
896
+ const controller = new AbortController();
897
+ const timeoutId = setTimeout(() => controller.abort(), this.config.timeoutMs);
898
+ try {
899
+ const response = await fetch(endpoint, {
900
+ method: "POST",
901
+ headers: { "Content-Type": "application/json" },
902
+ body: JSON.stringify({
903
+ id: "1",
904
+ jsonrpc: "2.0",
905
+ method: "getMasterchainInfo",
906
+ params: {}
907
+ }),
908
+ signal: controller.signal
909
+ });
910
+ clearTimeout(timeoutId);
911
+ if (!response.ok) {
912
+ throw new Error(`HTTP ${response.status}`);
913
+ }
914
+ const data = await response.json();
915
+ if (data && typeof data === "object" && "ok" in data) {
916
+ if (!data.ok) {
917
+ throw new Error(data.error || "API returned ok=false");
918
+ }
919
+ return data.result || data;
920
+ }
921
+ if (data.result) {
922
+ return data.result;
923
+ }
924
+ return data;
925
+ } catch (error) {
926
+ clearTimeout(timeoutId);
927
+ throw error;
928
+ }
929
+ }
930
+ sleep(ms) {
931
+ return new Promise((resolve) => setTimeout(resolve, ms));
932
+ }
933
+ };
934
+ function createHealthChecker(config, logger) {
935
+ return new HealthChecker(config, logger);
936
+ }
937
+
938
+ // src/utils/timeout.ts
939
+ var DEFAULT_PROVIDER_TIMEOUT_MS = 3e4;
940
+ var DEFAULT_CONTRACT_TIMEOUT_MS = 45e3;
941
+ var DEFAULT_HEALTH_CHECK_TIMEOUT_MS = 1e4;
942
+ async function withTimeout(promise, timeoutMs, operationName) {
943
+ const timeoutPromise = new Promise((_, reject) => {
944
+ const timeoutId = setTimeout(() => {
945
+ reject(new TimeoutError(operationName, timeoutMs));
946
+ }, timeoutMs);
947
+ promise.finally(() => clearTimeout(timeoutId));
948
+ });
949
+ return Promise.race([promise, timeoutPromise]);
950
+ }
951
+ async function withTimeoutFn(fn, timeoutMs, operationName) {
952
+ return withTimeout(fn(), timeoutMs, operationName);
953
+ }
954
+ function createTimeoutController(timeoutMs) {
955
+ const controller = new AbortController();
956
+ const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
957
+ return {
958
+ controller,
959
+ clear: () => clearTimeout(timeoutId)
960
+ };
961
+ }
962
+ async function fetchWithTimeout(url, options, timeoutMs) {
963
+ const { controller, clear } = createTimeoutController(timeoutMs);
964
+ try {
965
+ const response = await fetch(url, {
966
+ ...options,
967
+ signal: controller.signal
968
+ });
969
+ return response;
970
+ } catch (error) {
971
+ if (error.name === "AbortError") {
972
+ throw new TimeoutError(url, timeoutMs, `Fetch to ${url} timed out after ${timeoutMs}ms`);
973
+ }
974
+ throw error;
975
+ } finally {
976
+ clear();
977
+ }
978
+ }
979
+ var DEFAULT_RETRY_OPTIONS = {
980
+ maxRetries: 3,
981
+ baseDelayMs: 1e3,
982
+ maxDelayMs: 1e4,
983
+ backoffMultiplier: 2
984
+ };
985
+ async function withRetry(fn, options) {
986
+ const opts = { ...DEFAULT_RETRY_OPTIONS, ...options };
987
+ let lastError = null;
988
+ for (let attempt = 0; attempt <= opts.maxRetries; attempt++) {
989
+ try {
990
+ return await fn();
991
+ } catch (error) {
992
+ lastError = error;
993
+ if (opts.isRetryable && !opts.isRetryable(error)) {
994
+ throw error;
995
+ }
996
+ if (attempt < opts.maxRetries) {
997
+ const delay = Math.min(
998
+ opts.baseDelayMs * Math.pow(opts.backoffMultiplier, attempt),
999
+ opts.maxDelayMs
1000
+ );
1001
+ await sleep(delay);
1002
+ }
1003
+ }
1004
+ }
1005
+ throw lastError || new Error("Retry failed");
1006
+ }
1007
+ async function withTimeoutAndRetry(fn, timeoutMs, operationName, retryOptions) {
1008
+ return withRetry(
1009
+ () => withTimeout(fn(), timeoutMs, operationName),
1010
+ {
1011
+ ...retryOptions,
1012
+ isRetryable: (error) => {
1013
+ if (error instanceof TimeoutError) {
1014
+ return true;
1015
+ }
1016
+ if (retryOptions?.isRetryable) {
1017
+ return retryOptions.isRetryable(error);
1018
+ }
1019
+ const message = error.message?.toLowerCase() || "";
1020
+ return message.includes("network") || message.includes("fetch") || message.includes("econnrefused") || message.includes("etimedout");
1021
+ }
1022
+ }
1023
+ );
1024
+ }
1025
+ function sleep(ms) {
1026
+ return new Promise((resolve) => setTimeout(resolve, ms));
1027
+ }
1028
+ function isTimeoutError(error) {
1029
+ return error instanceof TimeoutError;
1030
+ }
1031
+ function isRateLimitError(error) {
1032
+ if (!error) return false;
1033
+ const message = error.message?.toLowerCase() || "";
1034
+ const status = error.status || error.response?.status;
1035
+ return status === 429 || message.includes("rate limit") || message.includes("429");
1036
+ }
1037
+
1038
+ // src/core/rateLimiter.ts
1039
+ var consoleLogger3 = {
1040
+ debug: (msg, data) => console.debug(`[RateLimiter] ${msg}`, data || ""),
1041
+ info: (msg, data) => console.log(`[RateLimiter] ${msg}`, data || ""),
1042
+ warn: (msg, data) => console.warn(`[RateLimiter] ${msg}`, data || ""),
1043
+ error: (msg, data) => console.error(`[RateLimiter] ${msg}`, data || "")
1044
+ };
1045
+ var DEFAULT_RATE_LIMIT = {
1046
+ rps: 1,
1047
+ burstSize: 3,
1048
+ minDelayMs: 1e3,
1049
+ backoffMultiplier: 2,
1050
+ maxBackoffMs: 3e4
1051
+ };
1052
+ var CHAINSTACK_RATE_LIMIT = {
1053
+ rps: 25,
1054
+ burstSize: 30,
1055
+ minDelayMs: 40,
1056
+ backoffMultiplier: 2,
1057
+ maxBackoffMs: 1e4
1058
+ };
1059
+ var QUICKNODE_RATE_LIMIT = {
1060
+ rps: 10,
1061
+ burstSize: 15,
1062
+ minDelayMs: 100,
1063
+ backoffMultiplier: 2,
1064
+ maxBackoffMs: 1e4
1065
+ };
1066
+ var ORBS_RATE_LIMIT = {
1067
+ rps: 10,
1068
+ burstSize: 20,
1069
+ minDelayMs: 100,
1070
+ backoffMultiplier: 2,
1071
+ maxBackoffMs: 1e4
1072
+ };
1073
+ function getRateLimitForType(type) {
1074
+ switch (type.toLowerCase()) {
1075
+ case "chainstack":
1076
+ return CHAINSTACK_RATE_LIMIT;
1077
+ case "quicknode":
1078
+ return QUICKNODE_RATE_LIMIT;
1079
+ case "orbs":
1080
+ return ORBS_RATE_LIMIT;
1081
+ default:
1082
+ return DEFAULT_RATE_LIMIT;
1083
+ }
1084
+ }
1085
+ var TokenBucketRateLimiter = class {
1086
+ constructor(config, logger) {
1087
+ this.currentBackoff = 0;
1088
+ this.consecutiveErrors = 0;
1089
+ this.requestQueue = [];
1090
+ this.processing = false;
1091
+ this.config = { ...DEFAULT_RATE_LIMIT, ...config };
1092
+ this.tokens = this.config.burstSize;
1093
+ this.lastRefill = Date.now();
1094
+ this.logger = logger || consoleLogger3;
1095
+ }
1096
+ /**
1097
+ * Get current state
1098
+ */
1099
+ getState() {
1100
+ this.refill();
1101
+ return {
1102
+ tokens: this.tokens,
1103
+ lastRefill: this.lastRefill,
1104
+ currentBackoff: this.currentBackoff,
1105
+ consecutiveErrors: this.consecutiveErrors,
1106
+ processing: this.processing,
1107
+ queueLength: this.requestQueue.length
1108
+ };
1109
+ }
1110
+ /**
1111
+ * Acquire a token (wait if necessary)
1112
+ *
1113
+ * @param timeoutMs - Maximum time to wait for a token (default: 60s)
1114
+ * @returns true if token acquired, false if timeout
1115
+ */
1116
+ async acquire(timeoutMs = 6e4) {
1117
+ const startTime = Date.now();
1118
+ if (this.processing) {
1119
+ const acquired = await new Promise((resolve) => {
1120
+ const checkTimeout = () => {
1121
+ if (Date.now() - startTime > timeoutMs) {
1122
+ const idx = this.requestQueue.indexOf(resolveCallback);
1123
+ if (idx >= 0) {
1124
+ this.requestQueue.splice(idx, 1);
1125
+ }
1126
+ resolve(false);
1127
+ }
1128
+ };
1129
+ const resolveCallback = () => resolve(true);
1130
+ this.requestQueue.push(resolveCallback);
1131
+ const timeoutInterval = setInterval(checkTimeout, 1e3);
1132
+ const cleanup = () => clearInterval(timeoutInterval);
1133
+ Promise.resolve().then(() => {
1134
+ if (this.requestQueue.includes(resolveCallback)) ; else {
1135
+ cleanup();
1136
+ }
1137
+ });
1138
+ });
1139
+ if (!acquired) {
1140
+ return false;
1141
+ }
1142
+ }
1143
+ this.processing = true;
1144
+ try {
1145
+ this.refill();
1146
+ if (this.currentBackoff > 0) {
1147
+ this.logger.debug(`Applying backoff: ${this.currentBackoff}ms`);
1148
+ await sleep(this.currentBackoff);
1149
+ }
1150
+ while (this.tokens <= 0) {
1151
+ if (Date.now() - startTime > timeoutMs) {
1152
+ return false;
1153
+ }
1154
+ await sleep(Math.min(100, this.config.minDelayMs));
1155
+ this.refill();
1156
+ }
1157
+ this.tokens--;
1158
+ const timeSinceLastRefill = Date.now() - this.lastRefill;
1159
+ if (timeSinceLastRefill < this.config.minDelayMs) {
1160
+ await sleep(this.config.minDelayMs - timeSinceLastRefill);
1161
+ }
1162
+ this.lastRefill = Date.now();
1163
+ return true;
1164
+ } finally {
1165
+ this.processing = false;
1166
+ if (this.requestQueue.length > 0) {
1167
+ const next = this.requestQueue.shift();
1168
+ next();
1169
+ }
1170
+ }
1171
+ }
1172
+ /**
1173
+ * Release a token (call on request completion)
1174
+ */
1175
+ release() {
1176
+ }
1177
+ /**
1178
+ * Report a successful request (resets backoff)
1179
+ */
1180
+ reportSuccess() {
1181
+ this.currentBackoff = 0;
1182
+ this.consecutiveErrors = 0;
1183
+ }
1184
+ /**
1185
+ * Report a rate limit error (applies backoff)
1186
+ */
1187
+ reportRateLimitError() {
1188
+ this.consecutiveErrors++;
1189
+ if (this.currentBackoff === 0) {
1190
+ this.currentBackoff = this.config.minDelayMs * this.config.backoffMultiplier;
1191
+ } else {
1192
+ this.currentBackoff = Math.min(
1193
+ this.currentBackoff * this.config.backoffMultiplier,
1194
+ this.config.maxBackoffMs
1195
+ );
1196
+ }
1197
+ this.logger.warn(`Rate limit hit, backoff: ${this.currentBackoff}ms, errors: ${this.consecutiveErrors}`);
1198
+ }
1199
+ /**
1200
+ * Report a general error
1201
+ */
1202
+ reportError() {
1203
+ this.consecutiveErrors++;
1204
+ if (this.consecutiveErrors >= 3) {
1205
+ this.currentBackoff = Math.min(
1206
+ this.config.minDelayMs * this.consecutiveErrors,
1207
+ this.config.maxBackoffMs / 2
1208
+ );
1209
+ }
1210
+ }
1211
+ /**
1212
+ * Reset rate limiter state
1213
+ */
1214
+ reset() {
1215
+ this.tokens = this.config.burstSize;
1216
+ this.lastRefill = Date.now();
1217
+ this.currentBackoff = 0;
1218
+ this.consecutiveErrors = 0;
1219
+ }
1220
+ /**
1221
+ * Update configuration
1222
+ */
1223
+ updateConfig(config) {
1224
+ this.config = { ...this.config, ...config };
1225
+ if (this.tokens > this.config.burstSize) {
1226
+ this.tokens = this.config.burstSize;
1227
+ }
1228
+ }
1229
+ /**
1230
+ * Refill tokens based on time elapsed
1231
+ */
1232
+ refill() {
1233
+ const now = Date.now();
1234
+ const elapsed = now - this.lastRefill;
1235
+ const tokensToAdd = Math.floor(elapsed / 1e3 * this.config.rps);
1236
+ if (tokensToAdd > 0) {
1237
+ this.tokens = Math.min(this.config.burstSize, this.tokens + tokensToAdd);
1238
+ this.lastRefill = now;
1239
+ }
1240
+ }
1241
+ };
1242
+ var RateLimiterManager = class {
1243
+ constructor(logger) {
1244
+ this.limiters = /* @__PURE__ */ new Map();
1245
+ this.configs = /* @__PURE__ */ new Map();
1246
+ this.logger = logger || consoleLogger3;
1247
+ }
1248
+ /**
1249
+ * Get or create rate limiter for a provider
1250
+ */
1251
+ getLimiter(providerId, config) {
1252
+ let limiter = this.limiters.get(providerId);
1253
+ if (!limiter) {
1254
+ const savedConfig = this.configs.get(providerId);
1255
+ limiter = new TokenBucketRateLimiter(
1256
+ { ...savedConfig, ...config },
1257
+ this.logger
1258
+ );
1259
+ this.limiters.set(providerId, limiter);
1260
+ }
1261
+ return limiter;
1262
+ }
1263
+ /**
1264
+ * Set rate limit config for a provider
1265
+ */
1266
+ setConfig(providerId, config) {
1267
+ this.configs.set(providerId, config);
1268
+ const limiter = this.limiters.get(providerId);
1269
+ if (limiter) {
1270
+ limiter.updateConfig(config);
1271
+ }
1272
+ }
1273
+ /**
1274
+ * Get rate limit state for a provider
1275
+ */
1276
+ getState(providerId) {
1277
+ const limiter = this.limiters.get(providerId);
1278
+ return limiter ? limiter.getState() : null;
1279
+ }
1280
+ /**
1281
+ * Acquire token for a provider
1282
+ */
1283
+ async acquire(providerId, timeoutMs) {
1284
+ const limiter = this.getLimiter(providerId);
1285
+ return limiter.acquire(timeoutMs);
1286
+ }
1287
+ /**
1288
+ * Report success for a provider
1289
+ */
1290
+ reportSuccess(providerId) {
1291
+ const limiter = this.limiters.get(providerId);
1292
+ if (limiter) {
1293
+ limiter.reportSuccess();
1294
+ }
1295
+ }
1296
+ /**
1297
+ * Report rate limit error for a provider
1298
+ */
1299
+ reportRateLimitError(providerId) {
1300
+ const limiter = this.getLimiter(providerId);
1301
+ limiter.reportRateLimitError();
1302
+ }
1303
+ /**
1304
+ * Report general error for a provider
1305
+ */
1306
+ reportError(providerId) {
1307
+ const limiter = this.getLimiter(providerId);
1308
+ limiter.reportError();
1309
+ }
1310
+ /**
1311
+ * Reset a provider's rate limiter
1312
+ */
1313
+ reset(providerId) {
1314
+ const limiter = this.limiters.get(providerId);
1315
+ if (limiter) {
1316
+ limiter.reset();
1317
+ }
1318
+ }
1319
+ /**
1320
+ * Reset all rate limiters
1321
+ */
1322
+ resetAll() {
1323
+ for (const limiter of this.limiters.values()) {
1324
+ limiter.reset();
1325
+ }
1326
+ }
1327
+ /**
1328
+ * Remove a provider's rate limiter
1329
+ */
1330
+ remove(providerId) {
1331
+ this.limiters.delete(providerId);
1332
+ this.configs.delete(providerId);
1333
+ }
1334
+ /**
1335
+ * Clear all limiters
1336
+ */
1337
+ clear() {
1338
+ this.limiters.clear();
1339
+ this.configs.clear();
1340
+ }
1341
+ };
1342
+ function createRateLimiter(rps, logger) {
1343
+ return new TokenBucketRateLimiter(
1344
+ {
1345
+ rps,
1346
+ burstSize: Math.max(3, Math.ceil(rps * 1.5)),
1347
+ minDelayMs: Math.ceil(1e3 / rps),
1348
+ backoffMultiplier: 2,
1349
+ maxBackoffMs: 3e4
1350
+ },
1351
+ logger
1352
+ );
1353
+ }
1354
+ function createRateLimiterManager(logger) {
1355
+ return new RateLimiterManager(logger);
1356
+ }
1357
+
1358
+ // src/core/selector.ts
1359
+ var consoleLogger4 = {
1360
+ debug: (msg, data) => console.debug(`[ProviderSelector] ${msg}`, data || ""),
1361
+ info: (msg, data) => console.log(`[ProviderSelector] ${msg}`, data || ""),
1362
+ warn: (msg, data) => console.warn(`[ProviderSelector] ${msg}`, data || ""),
1363
+ error: (msg, data) => console.error(`[ProviderSelector] ${msg}`, data || "")
1364
+ };
1365
+ var DEFAULT_CONFIG2 = {
1366
+ preferredLatencyMs: 1e3,
1367
+ latencyWeight: 0.4,
1368
+ priorityWeight: 0.3,
1369
+ freshnessWeight: 0.3,
1370
+ minStatus: ["available", "degraded"]
1371
+ };
1372
+ var ProviderSelector = class {
1373
+ constructor(registry, healthChecker, config, logger) {
1374
+ // Selection state
1375
+ this.selectedProviderId = null;
1376
+ this.autoSelect = true;
1377
+ this.customEndpoint = null;
1378
+ this.bestProviderByNetwork = /* @__PURE__ */ new Map();
1379
+ this.registry = registry;
1380
+ this.healthChecker = healthChecker;
1381
+ this.config = { ...DEFAULT_CONFIG2, ...config };
1382
+ this.logger = logger || consoleLogger4;
1383
+ }
1384
+ // ========================================================================
1385
+ // Selection Methods
1386
+ // ========================================================================
1387
+ /**
1388
+ * Get the best provider for a network
1389
+ */
1390
+ getBestProvider(network) {
1391
+ if (this.customEndpoint) {
1392
+ return this.createCustomProvider(network);
1393
+ }
1394
+ if (!this.autoSelect && this.selectedProviderId) {
1395
+ const provider = this.registry.getProvider(this.selectedProviderId);
1396
+ if (provider && provider.network === network) {
1397
+ return provider;
1398
+ }
1399
+ this.logger.warn(
1400
+ `Selected provider ${this.selectedProviderId} not found or wrong network, using auto-select`
1401
+ );
1402
+ }
1403
+ const cachedBestId = this.bestProviderByNetwork.get(network);
1404
+ if (cachedBestId) {
1405
+ const cached = this.registry.getProvider(cachedBestId);
1406
+ const health = this.healthChecker.getResult(cachedBestId, network);
1407
+ if (cached && health && this.config.minStatus.includes(health.status)) {
1408
+ return cached;
1409
+ }
1410
+ }
1411
+ return this.findBestProvider(network);
1412
+ }
1413
+ /**
1414
+ * Find the best provider for a network (recalculates)
1415
+ */
1416
+ findBestProvider(network) {
1417
+ const providers = this.registry.getProvidersForNetwork(network);
1418
+ if (providers.length === 0) {
1419
+ this.logger.warn(`No providers available for ${network}`);
1420
+ return null;
1421
+ }
1422
+ const scored = providers.map((provider) => ({
1423
+ provider,
1424
+ score: this.scoreProvider(provider, network)
1425
+ })).filter((item) => item.score > 0).sort((a, b) => b.score - a.score);
1426
+ if (scored.length === 0) {
1427
+ const defaults = this.registry.getDefaultOrderForNetwork(network);
1428
+ if (defaults.length > 0) {
1429
+ this.logger.warn(`No healthy providers for ${network}, using first default`);
1430
+ return defaults[0];
1431
+ }
1432
+ return providers[0];
1433
+ }
1434
+ const best = scored[0].provider;
1435
+ this.bestProviderByNetwork.set(network, best.id);
1436
+ this.logger.debug(
1437
+ `Best provider for ${network}: ${best.id} (score: ${scored[0].score.toFixed(2)})`
1438
+ );
1439
+ return best;
1440
+ }
1441
+ /**
1442
+ * Get all available providers for a network, sorted by score
1443
+ */
1444
+ getAvailableProviders(network) {
1445
+ const providers = this.registry.getProvidersForNetwork(network);
1446
+ return providers.map((provider) => ({
1447
+ provider,
1448
+ score: this.scoreProvider(provider, network)
1449
+ })).filter((item) => item.score > 0).sort((a, b) => b.score - a.score).map((item) => item.provider);
1450
+ }
1451
+ /**
1452
+ * Get the next best provider (for failover)
1453
+ */
1454
+ getNextProvider(network, excludeIds) {
1455
+ const providers = this.registry.getProvidersForNetwork(network);
1456
+ const available = providers.filter((p) => !excludeIds.includes(p.id)).map((provider) => ({
1457
+ provider,
1458
+ score: this.scoreProvider(provider, network)
1459
+ })).filter((item) => item.score > 0).sort((a, b) => b.score - a.score);
1460
+ if (available.length === 0) {
1461
+ return null;
1462
+ }
1463
+ return available[0].provider;
1464
+ }
1465
+ // ========================================================================
1466
+ // Scoring
1467
+ // ========================================================================
1468
+ /**
1469
+ * Calculate a score for a provider (higher is better)
1470
+ */
1471
+ scoreProvider(provider, network) {
1472
+ const health = this.healthChecker.getResult(provider.id, network);
1473
+ if (!health || health.status === "untested") {
1474
+ return 0.1 * (1 / (provider.priority + 1));
1475
+ }
1476
+ if (health.success === false) {
1477
+ return 0;
1478
+ }
1479
+ if (health.status === "offline") {
1480
+ return 0;
1481
+ }
1482
+ if (!this.config.minStatus.includes(health.status)) {
1483
+ return 0;
1484
+ }
1485
+ const statusScore = this.getStatusScore(health.status);
1486
+ const latencyScore = this.getLatencyScore(health.latencyMs);
1487
+ const priorityScore = this.getPriorityScore(provider.priority);
1488
+ const freshnessScore = this.getFreshnessScore(health.blocksBehind);
1489
+ const score = statusScore * 0.2 + // Base status score
1490
+ latencyScore * this.config.latencyWeight + priorityScore * this.config.priorityWeight + freshnessScore * this.config.freshnessWeight;
1491
+ return score;
1492
+ }
1493
+ getStatusScore(status) {
1494
+ switch (status) {
1495
+ case "available":
1496
+ return 1;
1497
+ case "degraded":
1498
+ return 0.5;
1499
+ case "stale":
1500
+ return 0.3;
1501
+ default:
1502
+ return 0;
1503
+ }
1504
+ }
1505
+ getLatencyScore(latencyMs) {
1506
+ if (latencyMs === null) {
1507
+ return 0.5;
1508
+ }
1509
+ const ratio = latencyMs / this.config.preferredLatencyMs;
1510
+ return Math.max(0, 1 - Math.log(ratio + 1) / Math.log(11));
1511
+ }
1512
+ getPriorityScore(priority) {
1513
+ return Math.max(0, 1 - priority / 100);
1514
+ }
1515
+ getFreshnessScore(blocksBehind) {
1516
+ return Math.max(0, 1 - blocksBehind / 10);
1517
+ }
1518
+ // ========================================================================
1519
+ // Selection Control
1520
+ // ========================================================================
1521
+ /**
1522
+ * Set manual provider selection
1523
+ */
1524
+ setSelectedProvider(providerId) {
1525
+ this.selectedProviderId = providerId;
1526
+ if (providerId !== null) {
1527
+ this.autoSelect = false;
1528
+ }
1529
+ this.logger.info(`Selected provider: ${providerId || "(auto)"}`);
1530
+ }
1531
+ /**
1532
+ * Get currently selected provider ID
1533
+ */
1534
+ getSelectedProviderId() {
1535
+ return this.selectedProviderId;
1536
+ }
1537
+ /**
1538
+ * Enable/disable auto-selection
1539
+ */
1540
+ setAutoSelect(enabled) {
1541
+ this.autoSelect = enabled;
1542
+ if (enabled) {
1543
+ this.selectedProviderId = null;
1544
+ }
1545
+ this.logger.info(`Auto-select: ${enabled}`);
1546
+ }
1547
+ /**
1548
+ * Check if auto-selection is enabled
1549
+ */
1550
+ isAutoSelectEnabled() {
1551
+ return this.autoSelect;
1552
+ }
1553
+ /**
1554
+ * Set custom endpoint override
1555
+ */
1556
+ setCustomEndpoint(endpoint) {
1557
+ this.customEndpoint = endpoint?.trim() || null;
1558
+ this.logger.info(`Custom endpoint: ${this.customEndpoint || "(none)"}`);
1559
+ }
1560
+ /**
1561
+ * Get custom endpoint
1562
+ */
1563
+ getCustomEndpoint() {
1564
+ return this.customEndpoint;
1565
+ }
1566
+ /**
1567
+ * Check if using custom endpoint
1568
+ */
1569
+ isUsingCustomEndpoint() {
1570
+ return this.customEndpoint !== null && this.customEndpoint.length > 0;
1571
+ }
1572
+ /**
1573
+ * Clear cached best providers (forces recalculation)
1574
+ */
1575
+ clearCache() {
1576
+ this.bestProviderByNetwork.clear();
1577
+ }
1578
+ /**
1579
+ * Update best provider after health check
1580
+ */
1581
+ updateBestProvider(network) {
1582
+ this.findBestProvider(network);
1583
+ }
1584
+ /**
1585
+ * Handle provider failure (switch to next best)
1586
+ */
1587
+ handleProviderFailure(providerId, network) {
1588
+ if (this.bestProviderByNetwork.get(network) === providerId) {
1589
+ this.bestProviderByNetwork.delete(network);
1590
+ }
1591
+ return this.getNextProvider(network, [providerId]);
1592
+ }
1593
+ // ========================================================================
1594
+ // Info
1595
+ // ========================================================================
1596
+ /**
1597
+ * Get active provider info
1598
+ */
1599
+ getActiveProviderInfo(network) {
1600
+ if (this.customEndpoint) {
1601
+ return { id: "custom", name: "Custom Endpoint", isCustom: true };
1602
+ }
1603
+ const provider = this.getBestProvider(network);
1604
+ if (provider) {
1605
+ return { id: provider.id, name: provider.name, isCustom: false };
1606
+ }
1607
+ return null;
1608
+ }
1609
+ // ========================================================================
1610
+ // Private Helpers
1611
+ // ========================================================================
1612
+ /**
1613
+ * Create a pseudo-provider for custom endpoint
1614
+ */
1615
+ createCustomProvider(network) {
1616
+ return {
1617
+ id: "custom",
1618
+ name: "Custom Endpoint",
1619
+ type: "custom",
1620
+ network,
1621
+ endpointV2: this.customEndpoint,
1622
+ rps: 10,
1623
+ priority: 0,
1624
+ isDynamic: false
1625
+ };
1626
+ }
1627
+ };
1628
+ function createSelector(registry, healthChecker, config, logger) {
1629
+ return new ProviderSelector(registry, healthChecker, config, logger);
1630
+ }
1631
+
1632
+ // src/core/manager.ts
1633
+ var consoleLogger5 = {
1634
+ debug: (msg, data) => console.debug(`[ProviderManager] ${msg}`, data || ""),
1635
+ info: (msg, data) => console.log(`[ProviderManager] ${msg}`, data || ""),
1636
+ warn: (msg, data) => console.warn(`[ProviderManager] ${msg}`, data || ""),
1637
+ error: (msg, data) => console.error(`[ProviderManager] ${msg}`, data || "")
1638
+ };
1639
+ var DEFAULT_OPTIONS = {
1640
+ configPath: "",
1641
+ // Unused - config is loaded from provider_system/rpc.json
1642
+ adapter: "node",
1643
+ autoInit: true,
1644
+ requestTimeoutMs: 1e4,
1645
+ healthCheckIntervalMs: 0,
1646
+ // Disabled by default
1647
+ maxBlocksBehind: 10,
1648
+ logger: consoleLogger5
1649
+ };
1650
+ var _ProviderManager = class _ProviderManager {
1651
+ constructor(options) {
1652
+ // Components
1653
+ this.registry = null;
1654
+ this.healthChecker = null;
1655
+ this.rateLimiter = null;
1656
+ this.selector = null;
1657
+ this.network = null;
1658
+ this.initialized = false;
1659
+ this.isTesting = false;
1660
+ this.healthCheckInterval = null;
1661
+ this.listeners = /* @__PURE__ */ new Set();
1662
+ this.options = { ...DEFAULT_OPTIONS, ...options };
1663
+ }
1664
+ // ========================================================================
1665
+ // Singleton Pattern
1666
+ // ========================================================================
1667
+ /**
1668
+ * Get singleton instance (recommended for Node.js)
1669
+ */
1670
+ static getInstance(options) {
1671
+ if (!_ProviderManager.instance) {
1672
+ _ProviderManager.instance = new _ProviderManager(options);
1673
+ }
1674
+ return _ProviderManager.instance;
1675
+ }
1676
+ /**
1677
+ * Reset singleton instance (for testing)
1678
+ */
1679
+ static resetInstance() {
1680
+ if (_ProviderManager.instance) {
1681
+ _ProviderManager.instance.destroy();
1682
+ _ProviderManager.instance = null;
1683
+ }
1684
+ }
1685
+ // ========================================================================
1686
+ // Initialization
1687
+ // ========================================================================
1688
+ /**
1689
+ * Initialize the provider manager
1690
+ *
1691
+ * @param network - Network to initialize for
1692
+ * @param testProviders - Whether to test providers immediately (default: true)
1693
+ */
1694
+ async init(network, testProviders = true) {
1695
+ if (this.initialized && this.network === network) {
1696
+ this.options.logger.debug("Already initialized for this network");
1697
+ return;
1698
+ }
1699
+ this.options.logger.info(`Initializing for ${network}...`);
1700
+ this.network = network;
1701
+ const config = await loadConfig();
1702
+ const mergedConfig = mergeWithDefaults(config);
1703
+ this.registry = new ProviderRegistry(mergedConfig, this.options.logger);
1704
+ this.healthChecker = createHealthChecker(
1705
+ {
1706
+ timeoutMs: this.options.requestTimeoutMs,
1707
+ maxBlocksBehind: this.options.maxBlocksBehind
1708
+ },
1709
+ this.options.logger
1710
+ );
1711
+ this.rateLimiter = createRateLimiterManager(this.options.logger);
1712
+ this.selector = createSelector(
1713
+ this.registry,
1714
+ this.healthChecker,
1715
+ void 0,
1716
+ this.options.logger
1717
+ );
1718
+ for (const provider of this.registry.getAllProviders()) {
1719
+ const config2 = getRateLimitForType(provider.type);
1720
+ this.rateLimiter.setConfig(provider.id, {
1721
+ ...config2,
1722
+ rps: provider.rps,
1723
+ minDelayMs: Math.ceil(1e3 / provider.rps)
1724
+ });
1725
+ }
1726
+ this.initialized = true;
1727
+ this.notifyListeners();
1728
+ if (testProviders) {
1729
+ await this.testAllProviders();
1730
+ }
1731
+ if (this.options.healthCheckIntervalMs > 0) {
1732
+ this.startHealthCheckInterval();
1733
+ }
1734
+ this.options.logger.info("Initialization complete");
1735
+ }
1736
+ /**
1737
+ * Check if manager is initialized
1738
+ */
1739
+ isInitialized() {
1740
+ return this.initialized;
1741
+ }
1742
+ /**
1743
+ * Destroy the manager (cleanup)
1744
+ */
1745
+ destroy() {
1746
+ this.stopHealthCheckInterval();
1747
+ this.listeners.clear();
1748
+ this.registry = null;
1749
+ this.healthChecker = null;
1750
+ this.rateLimiter = null;
1751
+ this.selector = null;
1752
+ this.initialized = false;
1753
+ this.network = null;
1754
+ }
1755
+ // ========================================================================
1756
+ // Provider Testing
1757
+ // ========================================================================
1758
+ /**
1759
+ * Test all providers for current network
1760
+ */
1761
+ async testAllProviders() {
1762
+ this.ensureInitialized();
1763
+ if (this.isTesting) {
1764
+ this.options.logger.debug("Already testing providers");
1765
+ return [];
1766
+ }
1767
+ this.isTesting = true;
1768
+ this.notifyListeners();
1769
+ this.options.logger.info(`Testing all providers for ${this.network}...`);
1770
+ try {
1771
+ const providers = this.registry.getProvidersForNetwork(this.network);
1772
+ const results = await this.healthChecker.testProviders(providers);
1773
+ this.selector.updateBestProvider(this.network);
1774
+ const available = results.filter((r) => r.success);
1775
+ this.options.logger.info(
1776
+ `Provider testing complete: ${available.length}/${results.length} available`
1777
+ );
1778
+ return results;
1779
+ } finally {
1780
+ this.isTesting = false;
1781
+ this.notifyListeners();
1782
+ }
1783
+ }
1784
+ /**
1785
+ * Test a specific provider
1786
+ */
1787
+ async testProvider(providerId) {
1788
+ this.ensureInitialized();
1789
+ const provider = this.registry.getProvider(providerId);
1790
+ if (!provider) {
1791
+ this.options.logger.warn(`Provider ${providerId} not found`);
1792
+ return null;
1793
+ }
1794
+ return this.healthChecker.testProvider(provider);
1795
+ }
1796
+ /**
1797
+ * Check if testing is in progress
1798
+ */
1799
+ isTestingProviders() {
1800
+ return this.isTesting;
1801
+ }
1802
+ // ========================================================================
1803
+ // Endpoint Access
1804
+ // ========================================================================
1805
+ /**
1806
+ * Get endpoint URL for current network
1807
+ *
1808
+ * Handles: custom endpoint > manual selection > auto-selection > fallback
1809
+ */
1810
+ async getEndpoint() {
1811
+ this.ensureInitialized();
1812
+ const provider = this.selector.getBestProvider(this.network);
1813
+ if (!provider) {
1814
+ this.options.logger.warn("No providers available, using fallback");
1815
+ return this.getFallbackEndpoint();
1816
+ }
1817
+ if (provider.isDynamic && provider.type === "orbs") {
1818
+ try {
1819
+ const { getHttpEndpoint } = await import('@orbs-network/ton-access');
1820
+ const endpoint = await getHttpEndpoint({ network: this.network });
1821
+ return normalizeV2Endpoint(endpoint);
1822
+ } catch (error) {
1823
+ this.options.logger.warn(`Failed to get Orbs endpoint: ${error.message}`);
1824
+ }
1825
+ }
1826
+ return normalizeV2Endpoint(provider.endpointV2);
1827
+ }
1828
+ /**
1829
+ * Get endpoint with rate limiting
1830
+ *
1831
+ * Waits for rate limit token before returning endpoint.
1832
+ */
1833
+ async getEndpointWithRateLimit(timeoutMs) {
1834
+ this.ensureInitialized();
1835
+ const provider = this.selector.getBestProvider(this.network);
1836
+ if (!provider) {
1837
+ return this.getFallbackEndpoint();
1838
+ }
1839
+ const acquired = await this.rateLimiter.acquire(provider.id, timeoutMs);
1840
+ if (!acquired) {
1841
+ this.options.logger.warn(`Rate limit timeout for ${provider.id}`);
1842
+ const next = this.selector.getNextProvider(this.network, [provider.id]);
1843
+ if (next) {
1844
+ return normalizeV2Endpoint(next.endpointV2);
1845
+ }
1846
+ return this.getFallbackEndpoint();
1847
+ }
1848
+ return normalizeV2Endpoint(provider.endpointV2);
1849
+ }
1850
+ /**
1851
+ * Get current active provider
1852
+ */
1853
+ getActiveProvider() {
1854
+ if (!this.initialized || !this.network) {
1855
+ return null;
1856
+ }
1857
+ return this.selector.getBestProvider(this.network);
1858
+ }
1859
+ /**
1860
+ * Get active provider info
1861
+ */
1862
+ getActiveProviderInfo() {
1863
+ if (!this.initialized || !this.network) {
1864
+ return null;
1865
+ }
1866
+ return this.selector.getActiveProviderInfo(this.network);
1867
+ }
1868
+ // ========================================================================
1869
+ // Error Reporting
1870
+ // ========================================================================
1871
+ /**
1872
+ * Report a successful request
1873
+ */
1874
+ reportSuccess() {
1875
+ if (!this.initialized || !this.network) return;
1876
+ const provider = this.selector.getBestProvider(this.network);
1877
+ if (provider) {
1878
+ this.rateLimiter.reportSuccess(provider.id);
1879
+ }
1880
+ }
1881
+ /**
1882
+ * Report an error (triggers provider switch if needed)
1883
+ */
1884
+ reportError(error) {
1885
+ if (!this.initialized || !this.network) return;
1886
+ const provider = this.selector.getBestProvider(this.network);
1887
+ if (!provider) return;
1888
+ const errorMsg = error instanceof Error ? error.message : error;
1889
+ if (isRateLimitError(error)) {
1890
+ this.rateLimiter.reportRateLimitError(provider.id);
1891
+ this.healthChecker.markDegraded(provider.id, this.network, errorMsg);
1892
+ } else {
1893
+ this.rateLimiter.reportError(provider.id);
1894
+ }
1895
+ this.selector.handleProviderFailure(provider.id, this.network);
1896
+ this.notifyListeners();
1897
+ }
1898
+ // ========================================================================
1899
+ // Selection Control
1900
+ // ========================================================================
1901
+ /**
1902
+ * Set manual provider selection
1903
+ */
1904
+ setSelectedProvider(providerId) {
1905
+ this.ensureInitialized();
1906
+ this.selector.setSelectedProvider(providerId);
1907
+ this.notifyListeners();
1908
+ }
1909
+ /**
1910
+ * Get selected provider ID
1911
+ */
1912
+ getSelectedProviderId() {
1913
+ if (!this.initialized) return null;
1914
+ return this.selector.getSelectedProviderId();
1915
+ }
1916
+ /**
1917
+ * Set auto-select mode
1918
+ */
1919
+ setAutoSelect(enabled) {
1920
+ this.ensureInitialized();
1921
+ this.selector.setAutoSelect(enabled);
1922
+ this.notifyListeners();
1923
+ }
1924
+ /**
1925
+ * Check if auto-select is enabled
1926
+ */
1927
+ isAutoSelectEnabled() {
1928
+ if (!this.initialized) return true;
1929
+ return this.selector.isAutoSelectEnabled();
1930
+ }
1931
+ /**
1932
+ * Set custom endpoint override
1933
+ */
1934
+ setCustomEndpoint(endpoint) {
1935
+ this.ensureInitialized();
1936
+ this.selector.setCustomEndpoint(endpoint);
1937
+ this.notifyListeners();
1938
+ }
1939
+ /**
1940
+ * Get custom endpoint
1941
+ */
1942
+ getCustomEndpoint() {
1943
+ if (!this.initialized) return null;
1944
+ return this.selector.getCustomEndpoint();
1945
+ }
1946
+ /**
1947
+ * Check if using custom endpoint
1948
+ */
1949
+ isUsingCustomEndpoint() {
1950
+ if (!this.initialized) return false;
1951
+ return this.selector.isUsingCustomEndpoint();
1952
+ }
1953
+ // ========================================================================
1954
+ // State Access
1955
+ // ========================================================================
1956
+ /**
1957
+ * Get current network
1958
+ */
1959
+ getNetwork() {
1960
+ return this.network;
1961
+ }
1962
+ /**
1963
+ * Get all providers for current network
1964
+ */
1965
+ getProviders() {
1966
+ if (!this.initialized || !this.network) return [];
1967
+ return this.registry.getProvidersForNetwork(this.network);
1968
+ }
1969
+ /**
1970
+ * Get provider health results for current network
1971
+ */
1972
+ getProviderHealthResults() {
1973
+ if (!this.initialized || !this.network) return [];
1974
+ return this.healthChecker.getResultsForNetwork(this.network);
1975
+ }
1976
+ /**
1977
+ * Get registry (for advanced usage)
1978
+ */
1979
+ getRegistry() {
1980
+ return this.registry;
1981
+ }
1982
+ /**
1983
+ * Get health checker (for advanced usage)
1984
+ */
1985
+ getHealthChecker() {
1986
+ return this.healthChecker;
1987
+ }
1988
+ /**
1989
+ * Get rate limiter manager (for advanced usage)
1990
+ */
1991
+ getRateLimiter() {
1992
+ return this.rateLimiter;
1993
+ }
1994
+ /**
1995
+ * Get current state (for UI)
1996
+ */
1997
+ getState() {
1998
+ const providers = /* @__PURE__ */ new Map();
1999
+ if (this.initialized && this.network && this.registry) {
2000
+ for (const provider of this.registry.getProvidersForNetwork(this.network)) {
2001
+ const health = this.healthChecker?.getResult(provider.id, this.network);
2002
+ const rateLimit = this.rateLimiter?.getState(provider.id);
2003
+ providers.set(provider.id, {
2004
+ id: provider.id,
2005
+ health: health || {
2006
+ id: provider.id,
2007
+ network: this.network,
2008
+ success: false,
2009
+ status: "untested",
2010
+ latencyMs: null,
2011
+ seqno: null,
2012
+ blocksBehind: 0,
2013
+ lastTested: null
2014
+ },
2015
+ rateLimit: rateLimit || {
2016
+ tokens: 0,
2017
+ lastRefill: 0,
2018
+ currentBackoff: 0,
2019
+ consecutiveErrors: 0,
2020
+ processing: false,
2021
+ queueLength: 0
2022
+ }
2023
+ });
2024
+ }
2025
+ }
2026
+ return {
2027
+ network: this.network,
2028
+ initialized: this.initialized,
2029
+ isTesting: this.isTesting,
2030
+ providers,
2031
+ bestProviderByNetwork: new Map(
2032
+ this.network && this.selector ? [[this.network, this.selector.getBestProvider(this.network)?.id || ""]] : []
2033
+ ),
2034
+ selectedProviderId: this.selector?.getSelectedProviderId() || null,
2035
+ autoSelect: this.selector?.isAutoSelectEnabled() ?? true,
2036
+ customEndpoint: this.selector?.getCustomEndpoint() || null
2037
+ };
2038
+ }
2039
+ // ========================================================================
2040
+ // State Listeners
2041
+ // ========================================================================
2042
+ /**
2043
+ * Subscribe to state changes
2044
+ */
2045
+ subscribe(listener) {
2046
+ this.listeners.add(listener);
2047
+ return () => {
2048
+ this.listeners.delete(listener);
2049
+ };
2050
+ }
2051
+ /**
2052
+ * Notify all listeners
2053
+ */
2054
+ notifyListeners() {
2055
+ const state = this.getState();
2056
+ this.listeners.forEach((listener) => listener(state));
2057
+ }
2058
+ // ========================================================================
2059
+ // Private Helpers
2060
+ // ========================================================================
2061
+ ensureInitialized() {
2062
+ if (!this.initialized) {
2063
+ throw new Error("ProviderManager not initialized. Call init() first.");
2064
+ }
2065
+ }
2066
+ getFallbackEndpoint() {
2067
+ if (this.network === "mainnet") {
2068
+ return "https://toncenter.com/api/v2/jsonRPC";
2069
+ }
2070
+ return "https://testnet.toncenter.com/api/v2/jsonRPC";
2071
+ }
2072
+ startHealthCheckInterval() {
2073
+ this.stopHealthCheckInterval();
2074
+ this.healthCheckInterval = setInterval(() => {
2075
+ this.testAllProviders().catch((error) => {
2076
+ this.options.logger.error(`Health check interval failed: ${error.message}`);
2077
+ });
2078
+ }, this.options.healthCheckIntervalMs);
2079
+ this.options.logger.debug(
2080
+ `Started health check interval: ${this.options.healthCheckIntervalMs}ms`
2081
+ );
2082
+ }
2083
+ stopHealthCheckInterval() {
2084
+ if (this.healthCheckInterval) {
2085
+ clearInterval(this.healthCheckInterval);
2086
+ this.healthCheckInterval = null;
2087
+ }
2088
+ }
2089
+ };
2090
+ // Singleton instance
2091
+ _ProviderManager.instance = null;
2092
+ var ProviderManager = _ProviderManager;
2093
+ function createProviderManager(options) {
2094
+ return new ProviderManager(options);
2095
+ }
2096
+ function getProviderManager(options) {
2097
+ return ProviderManager.getInstance(options);
2098
+ }
2099
+ var consoleLogger6 = {
2100
+ debug: (msg, data) => console.debug(`[NodeAdapter] ${msg}`, data || ""),
2101
+ info: (msg, data) => console.log(`[NodeAdapter] ${msg}`, data || ""),
2102
+ warn: (msg, data) => console.warn(`[NodeAdapter] ${msg}`, data || ""),
2103
+ error: (msg, data) => console.error(`[NodeAdapter] ${msg}`, data || "")
2104
+ };
2105
+ var cachedClient = null;
2106
+ var NodeAdapter = class {
2107
+ constructor(manager, logger) {
2108
+ this.manager = manager;
2109
+ this.logger = logger || consoleLogger6;
2110
+ }
2111
+ /**
2112
+ * Get TonClient instance
2113
+ *
2114
+ * Creates a new client if endpoint changed, otherwise returns cached.
2115
+ */
2116
+ async getClient() {
2117
+ const endpoint = await this.manager.getEndpoint();
2118
+ const network = this.manager.getNetwork();
2119
+ if (!network) {
2120
+ throw new Error("ProviderManager not initialized");
2121
+ }
2122
+ if (cachedClient && cachedClient.endpoint === endpoint && cachedClient.network === network) {
2123
+ return cachedClient.client;
2124
+ }
2125
+ const provider = this.manager.getActiveProvider();
2126
+ const apiKey = provider?.apiKey;
2127
+ const client = new ton.TonClient({
2128
+ endpoint,
2129
+ apiKey
2130
+ });
2131
+ cachedClient = {
2132
+ client,
2133
+ endpoint,
2134
+ network,
2135
+ createdAt: Date.now()
2136
+ };
2137
+ this.logger.debug(`Created TonClient for ${network}`, { endpoint });
2138
+ return client;
2139
+ }
2140
+ /**
2141
+ * Reset client cache (forces new client creation)
2142
+ */
2143
+ resetClient() {
2144
+ cachedClient = null;
2145
+ this.logger.debug("Client cache cleared");
2146
+ }
2147
+ /**
2148
+ * Get cached client info (for debugging)
2149
+ */
2150
+ getClientInfo() {
2151
+ if (!cachedClient) return null;
2152
+ return {
2153
+ endpoint: cachedClient.endpoint,
2154
+ network: cachedClient.network,
2155
+ age: Date.now() - cachedClient.createdAt
2156
+ };
2157
+ }
2158
+ // ========================================================================
2159
+ // Direct REST API Methods
2160
+ // ========================================================================
2161
+ /**
2162
+ * Get address state via REST API
2163
+ */
2164
+ async getAddressState(address, timeoutMs = 1e4) {
2165
+ const endpoint = await this.manager.getEndpoint();
2166
+ const baseV2 = toV2Base(endpoint);
2167
+ const addrStr = typeof address === "string" ? address : address.toString();
2168
+ const url = `${baseV2}/getAddressState?address=${encodeURIComponent(addrStr)}`;
2169
+ try {
2170
+ const response = await fetchWithTimeout(
2171
+ url,
2172
+ { headers: { accept: "application/json" } },
2173
+ timeoutMs
2174
+ );
2175
+ const json = await response.json();
2176
+ const data = this.unwrapResponse(json);
2177
+ if (typeof data === "string") {
2178
+ this.manager.reportSuccess();
2179
+ return data;
2180
+ }
2181
+ if (data && typeof data === "object" && typeof data.state === "string") {
2182
+ this.manager.reportSuccess();
2183
+ return data.state;
2184
+ }
2185
+ throw new Error("Unexpected response format");
2186
+ } catch (error) {
2187
+ this.manager.reportError(error);
2188
+ throw error;
2189
+ }
2190
+ }
2191
+ /**
2192
+ * Get address balance via REST API
2193
+ */
2194
+ async getAddressBalance(address, timeoutMs = 1e4) {
2195
+ const endpoint = await this.manager.getEndpoint();
2196
+ const baseV2 = toV2Base(endpoint);
2197
+ const addrStr = typeof address === "string" ? address : address.toString();
2198
+ const url = `${baseV2}/getAddressBalance?address=${encodeURIComponent(addrStr)}`;
2199
+ try {
2200
+ const response = await fetchWithTimeout(
2201
+ url,
2202
+ { headers: { accept: "application/json" } },
2203
+ timeoutMs
2204
+ );
2205
+ const json = await response.json();
2206
+ const data = this.unwrapResponse(json);
2207
+ if (typeof data === "string" || typeof data === "number") {
2208
+ this.manager.reportSuccess();
2209
+ return BigInt(data);
2210
+ }
2211
+ if (data && typeof data === "object" && data.balance !== void 0) {
2212
+ this.manager.reportSuccess();
2213
+ return BigInt(String(data.balance));
2214
+ }
2215
+ throw new Error("Unexpected response format");
2216
+ } catch (error) {
2217
+ this.manager.reportError(error);
2218
+ throw error;
2219
+ }
2220
+ }
2221
+ /**
2222
+ * Run a get method via REST API
2223
+ */
2224
+ async runGetMethod(address, method, stack = [], timeoutMs = 15e3) {
2225
+ const endpoint = await this.manager.getEndpoint();
2226
+ const baseV2 = toV2Base(endpoint);
2227
+ const addrStr = typeof address === "string" ? address : address.toString();
2228
+ const url = `${baseV2}/runGetMethod`;
2229
+ try {
2230
+ const response = await fetchWithTimeout(
2231
+ url,
2232
+ {
2233
+ method: "POST",
2234
+ headers: {
2235
+ "Content-Type": "application/json",
2236
+ accept: "application/json"
2237
+ },
2238
+ body: JSON.stringify({
2239
+ address: addrStr,
2240
+ method,
2241
+ stack
2242
+ })
2243
+ },
2244
+ timeoutMs
2245
+ );
2246
+ const json = await response.json();
2247
+ const data = this.unwrapResponse(json);
2248
+ if (data.exit_code === void 0) {
2249
+ throw new Error("Missing exit_code in response");
2250
+ }
2251
+ this.manager.reportSuccess();
2252
+ return {
2253
+ exit_code: data.exit_code,
2254
+ stack: data.stack || []
2255
+ };
2256
+ } catch (error) {
2257
+ this.manager.reportError(error);
2258
+ throw error;
2259
+ }
2260
+ }
2261
+ /**
2262
+ * Send BOC via REST API
2263
+ */
2264
+ async sendBoc(boc, timeoutMs = 3e4) {
2265
+ const endpoint = await this.manager.getEndpoint();
2266
+ const baseV2 = toV2Base(endpoint);
2267
+ const url = `${baseV2}/sendBoc`;
2268
+ const bocBase64 = typeof boc === "string" ? boc : boc.toString("base64");
2269
+ try {
2270
+ const response = await fetchWithTimeout(
2271
+ url,
2272
+ {
2273
+ method: "POST",
2274
+ headers: { "Content-Type": "application/json" },
2275
+ body: JSON.stringify({ boc: bocBase64 })
2276
+ },
2277
+ timeoutMs
2278
+ );
2279
+ const json = await response.json();
2280
+ this.unwrapResponse(json);
2281
+ this.manager.reportSuccess();
2282
+ } catch (error) {
2283
+ this.manager.reportError(error);
2284
+ throw error;
2285
+ }
2286
+ }
2287
+ /**
2288
+ * Check if contract is deployed
2289
+ */
2290
+ async isContractDeployed(address, timeoutMs = 1e4) {
2291
+ try {
2292
+ const state = await this.getAddressState(address, timeoutMs);
2293
+ return state === "active";
2294
+ } catch {
2295
+ return false;
2296
+ }
2297
+ }
2298
+ // ========================================================================
2299
+ // Private Helpers
2300
+ // ========================================================================
2301
+ /**
2302
+ * Unwrap TON API response
2303
+ */
2304
+ unwrapResponse(json) {
2305
+ if (json && typeof json === "object" && "ok" in json) {
2306
+ const resp = json;
2307
+ if (!resp.ok) {
2308
+ throw new Error(resp.error || "API returned ok=false");
2309
+ }
2310
+ return resp.result ?? json;
2311
+ }
2312
+ if (json && typeof json === "object" && "result" in json) {
2313
+ return json.result;
2314
+ }
2315
+ return json;
2316
+ }
2317
+ };
2318
+ function createNodeAdapter(manager, logger) {
2319
+ return new NodeAdapter(manager, logger);
2320
+ }
2321
+ async function getTonClient(manager) {
2322
+ const adapter = new NodeAdapter(manager);
2323
+ return adapter.getClient();
2324
+ }
2325
+ async function getTonClientForNetwork(network, configPath) {
2326
+ const manager = ProviderManager.getInstance({ configPath });
2327
+ if (!manager.isInitialized() || manager.getNetwork() !== network) {
2328
+ await manager.init(network);
2329
+ }
2330
+ return getTonClient(manager);
2331
+ }
2332
+ function resetNodeAdapter() {
2333
+ cachedClient = null;
2334
+ ProviderManager.resetInstance();
2335
+ }
2336
+
2337
+ // src/adapters/browser.ts
2338
+ var consoleLogger7 = {
2339
+ debug: (msg, data) => console.debug(`[BrowserAdapter] ${msg}`, data || ""),
2340
+ info: (msg, data) => console.log(`[BrowserAdapter] ${msg}`, data || ""),
2341
+ warn: (msg, data) => console.warn(`[BrowserAdapter] ${msg}`, data || ""),
2342
+ error: (msg, data) => console.error(`[BrowserAdapter] ${msg}`, data || "")
2343
+ };
2344
+ var BrowserAdapter = class {
2345
+ constructor(manager, logger) {
2346
+ this.manager = manager;
2347
+ this.logger = logger || consoleLogger7;
2348
+ }
2349
+ /**
2350
+ * Get current endpoint URL
2351
+ */
2352
+ async getEndpoint() {
2353
+ return this.manager.getEndpoint();
2354
+ }
2355
+ /**
2356
+ * Get endpoint with rate limiting
2357
+ */
2358
+ async getEndpointWithRateLimit(timeoutMs) {
2359
+ return this.manager.getEndpointWithRateLimit(timeoutMs);
2360
+ }
2361
+ // ========================================================================
2362
+ // JSON-RPC Methods
2363
+ // ========================================================================
2364
+ /**
2365
+ * Make a JSON-RPC call to the TON API
2366
+ */
2367
+ async jsonRpc(method, params = {}, timeoutMs = 1e4) {
2368
+ const endpoint = await this.manager.getEndpoint();
2369
+ const controller = new AbortController();
2370
+ const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
2371
+ try {
2372
+ const response = await fetch(endpoint, {
2373
+ method: "POST",
2374
+ headers: { "Content-Type": "application/json" },
2375
+ body: JSON.stringify({
2376
+ id: "1",
2377
+ jsonrpc: "2.0",
2378
+ method,
2379
+ params
2380
+ }),
2381
+ signal: controller.signal
2382
+ });
2383
+ clearTimeout(timeoutId);
2384
+ if (!response.ok) {
2385
+ throw new Error(`HTTP ${response.status}`);
2386
+ }
2387
+ const json = await response.json();
2388
+ const data = this.unwrapResponse(json);
2389
+ this.manager.reportSuccess();
2390
+ return data;
2391
+ } catch (error) {
2392
+ clearTimeout(timeoutId);
2393
+ if (error.name === "AbortError") {
2394
+ const timeoutError = new Error(`Request timed out after ${timeoutMs}ms`);
2395
+ this.manager.reportError(timeoutError);
2396
+ throw timeoutError;
2397
+ }
2398
+ this.manager.reportError(error);
2399
+ throw error;
2400
+ }
2401
+ }
2402
+ // ========================================================================
2403
+ // REST API Methods
2404
+ // ========================================================================
2405
+ /**
2406
+ * Get address state
2407
+ */
2408
+ async getAddressState(address, timeoutMs = 1e4) {
2409
+ const endpoint = await this.manager.getEndpoint();
2410
+ const baseV2 = toV2Base(endpoint);
2411
+ const url = `${baseV2}/getAddressState?address=${encodeURIComponent(address)}`;
2412
+ const controller = new AbortController();
2413
+ const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
2414
+ try {
2415
+ const response = await fetch(url, {
2416
+ headers: { accept: "application/json" },
2417
+ signal: controller.signal
2418
+ });
2419
+ clearTimeout(timeoutId);
2420
+ const json = await response.json();
2421
+ const data = this.unwrapResponse(json);
2422
+ if (typeof data === "string") {
2423
+ this.manager.reportSuccess();
2424
+ return data;
2425
+ }
2426
+ if (data && typeof data === "object" && typeof data.state === "string") {
2427
+ this.manager.reportSuccess();
2428
+ return data.state;
2429
+ }
2430
+ throw new Error("Unexpected response format");
2431
+ } catch (error) {
2432
+ clearTimeout(timeoutId);
2433
+ this.manager.reportError(error);
2434
+ throw error;
2435
+ }
2436
+ }
2437
+ /**
2438
+ * Get address balance
2439
+ */
2440
+ async getAddressBalance(address, timeoutMs = 1e4) {
2441
+ const endpoint = await this.manager.getEndpoint();
2442
+ const baseV2 = toV2Base(endpoint);
2443
+ const url = `${baseV2}/getAddressBalance?address=${encodeURIComponent(address)}`;
2444
+ const controller = new AbortController();
2445
+ const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
2446
+ try {
2447
+ const response = await fetch(url, {
2448
+ headers: { accept: "application/json" },
2449
+ signal: controller.signal
2450
+ });
2451
+ clearTimeout(timeoutId);
2452
+ const json = await response.json();
2453
+ const data = this.unwrapResponse(json);
2454
+ if (typeof data === "string" || typeof data === "number") {
2455
+ this.manager.reportSuccess();
2456
+ return BigInt(data);
2457
+ }
2458
+ if (data && typeof data === "object" && data.balance !== void 0) {
2459
+ this.manager.reportSuccess();
2460
+ return BigInt(String(data.balance));
2461
+ }
2462
+ throw new Error("Unexpected response format");
2463
+ } catch (error) {
2464
+ clearTimeout(timeoutId);
2465
+ this.manager.reportError(error);
2466
+ throw error;
2467
+ }
2468
+ }
2469
+ /**
2470
+ * Get address information
2471
+ */
2472
+ async getAddressInfo(address, timeoutMs = 1e4) {
2473
+ const endpoint = await this.manager.getEndpoint();
2474
+ const baseV2 = toV2Base(endpoint);
2475
+ const url = `${baseV2}/getAddressInformation?address=${encodeURIComponent(address)}`;
2476
+ const controller = new AbortController();
2477
+ const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
2478
+ try {
2479
+ const response = await fetch(url, {
2480
+ headers: { accept: "application/json" },
2481
+ signal: controller.signal
2482
+ });
2483
+ clearTimeout(timeoutId);
2484
+ const json = await response.json();
2485
+ const data = this.unwrapResponse(json);
2486
+ this.manager.reportSuccess();
2487
+ return {
2488
+ state: data.state,
2489
+ balance: BigInt(String(data.balance || 0)),
2490
+ lastTransactionLt: data.last_transaction_id?.lt,
2491
+ lastTransactionHash: data.last_transaction_id?.hash
2492
+ };
2493
+ } catch (error) {
2494
+ clearTimeout(timeoutId);
2495
+ this.manager.reportError(error);
2496
+ throw error;
2497
+ }
2498
+ }
2499
+ /**
2500
+ * Run get method
2501
+ */
2502
+ async runGetMethod(address, method, stack = [], timeoutMs = 15e3) {
2503
+ const endpoint = await this.manager.getEndpoint();
2504
+ const baseV2 = toV2Base(endpoint);
2505
+ const url = `${baseV2}/runGetMethod`;
2506
+ const controller = new AbortController();
2507
+ const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
2508
+ try {
2509
+ const response = await fetch(url, {
2510
+ method: "POST",
2511
+ headers: {
2512
+ "Content-Type": "application/json",
2513
+ accept: "application/json"
2514
+ },
2515
+ body: JSON.stringify({
2516
+ address,
2517
+ method,
2518
+ stack
2519
+ }),
2520
+ signal: controller.signal
2521
+ });
2522
+ clearTimeout(timeoutId);
2523
+ const json = await response.json();
2524
+ const data = this.unwrapResponse(json);
2525
+ if (data.exit_code === void 0) {
2526
+ throw new Error("Missing exit_code in response");
2527
+ }
2528
+ this.manager.reportSuccess();
2529
+ return {
2530
+ exit_code: data.exit_code,
2531
+ stack: data.stack || []
2532
+ };
2533
+ } catch (error) {
2534
+ clearTimeout(timeoutId);
2535
+ this.manager.reportError(error);
2536
+ throw error;
2537
+ }
2538
+ }
2539
+ /**
2540
+ * Get masterchain info
2541
+ */
2542
+ async getMasterchainInfo(timeoutMs = 1e4) {
2543
+ const data = await this.jsonRpc("getMasterchainInfo", {}, timeoutMs);
2544
+ return {
2545
+ seqno: data.last?.seqno || 0,
2546
+ stateRootHash: data.state_root_hash || ""
2547
+ };
2548
+ }
2549
+ // ========================================================================
2550
+ // Provider Management
2551
+ // ========================================================================
2552
+ /**
2553
+ * Get provider manager
2554
+ */
2555
+ getManager() {
2556
+ return this.manager;
2557
+ }
2558
+ /**
2559
+ * Get active provider info
2560
+ */
2561
+ getActiveProviderInfo() {
2562
+ return this.manager.getActiveProviderInfo();
2563
+ }
2564
+ /**
2565
+ * Get provider health results
2566
+ */
2567
+ getProviderHealthResults() {
2568
+ return this.manager.getProviderHealthResults();
2569
+ }
2570
+ /**
2571
+ * Test all providers
2572
+ */
2573
+ async testAllProviders() {
2574
+ return this.manager.testAllProviders();
2575
+ }
2576
+ // ========================================================================
2577
+ // Private Helpers
2578
+ // ========================================================================
2579
+ /**
2580
+ * Unwrap TON API response
2581
+ */
2582
+ unwrapResponse(json) {
2583
+ if (json && typeof json === "object" && "ok" in json) {
2584
+ const resp = json;
2585
+ if (!resp.ok) {
2586
+ throw new Error(resp.error || "API returned ok=false");
2587
+ }
2588
+ return resp.result ?? json;
2589
+ }
2590
+ if (json && typeof json === "object" && "result" in json) {
2591
+ return json.result;
2592
+ }
2593
+ return json;
2594
+ }
2595
+ };
2596
+ function createBrowserAdapter(manager, logger) {
2597
+ return new BrowserAdapter(manager, logger);
2598
+ }
2599
+ async function createBrowserAdapterForNetwork(network, configPath, logger) {
2600
+ const manager = new ProviderManager({
2601
+ configPath,
2602
+ adapter: "browser",
2603
+ logger
2604
+ });
2605
+ await manager.init(network);
2606
+ return new BrowserAdapter(manager, logger);
2607
+ }
2608
+
2609
+ exports.ApiVersionSchema = ApiVersionSchema;
2610
+ exports.BrowserAdapter = BrowserAdapter;
2611
+ exports.CHAINSTACK_RATE_LIMIT = CHAINSTACK_RATE_LIMIT;
2612
+ exports.ConfigError = ConfigError;
2613
+ exports.DEFAULT_CONTRACT_TIMEOUT_MS = DEFAULT_CONTRACT_TIMEOUT_MS;
2614
+ exports.DEFAULT_HEALTH_CHECK_TIMEOUT_MS = DEFAULT_HEALTH_CHECK_TIMEOUT_MS;
2615
+ exports.DEFAULT_PROVIDERS = DEFAULT_PROVIDERS;
2616
+ exports.DEFAULT_PROVIDER_TIMEOUT_MS = DEFAULT_PROVIDER_TIMEOUT_MS;
2617
+ exports.DEFAULT_RATE_LIMIT = DEFAULT_RATE_LIMIT;
2618
+ exports.HealthChecker = HealthChecker;
2619
+ exports.NetworkSchema = NetworkSchema;
2620
+ exports.NodeAdapter = NodeAdapter;
2621
+ exports.ORBS_RATE_LIMIT = ORBS_RATE_LIMIT;
2622
+ exports.ProviderConfigSchema = ProviderConfigSchema;
2623
+ exports.ProviderError = ProviderError;
2624
+ exports.ProviderManager = ProviderManager;
2625
+ exports.ProviderRegistry = ProviderRegistry;
2626
+ exports.ProviderSelector = ProviderSelector;
2627
+ exports.ProviderTypeSchema = ProviderTypeSchema;
2628
+ exports.QUICKNODE_RATE_LIMIT = QUICKNODE_RATE_LIMIT;
2629
+ exports.RateLimitError = RateLimitError;
2630
+ exports.RateLimiterManager = RateLimiterManager;
2631
+ exports.RpcConfigSchema = RpcConfigSchema;
2632
+ exports.TimeoutError = TimeoutError;
2633
+ exports.TokenBucketRateLimiter = TokenBucketRateLimiter;
2634
+ exports.buildGetAddressBalanceUrl = buildGetAddressBalanceUrl;
2635
+ exports.buildGetAddressInfoUrl = buildGetAddressInfoUrl;
2636
+ exports.buildGetAddressStateUrl = buildGetAddressStateUrl;
2637
+ exports.buildRestUrl = buildRestUrl;
2638
+ exports.createBrowserAdapter = createBrowserAdapter;
2639
+ exports.createBrowserAdapterForNetwork = createBrowserAdapterForNetwork;
2640
+ exports.createDefaultConfig = createDefaultConfig;
2641
+ exports.createDefaultRegistry = createDefaultRegistry;
2642
+ exports.createEmptyConfig = createEmptyConfig;
2643
+ exports.createHealthChecker = createHealthChecker;
2644
+ exports.createNodeAdapter = createNodeAdapter;
2645
+ exports.createProviderManager = createProviderManager;
2646
+ exports.createRateLimiter = createRateLimiter;
2647
+ exports.createRateLimiterManager = createRateLimiterManager;
2648
+ exports.createRegistry = createRegistry;
2649
+ exports.createRegistryFromData = createRegistryFromData;
2650
+ exports.createRegistryFromFile = createRegistryFromFile;
2651
+ exports.createSelector = createSelector;
2652
+ exports.createTimeoutController = createTimeoutController;
2653
+ exports.detectNetworkFromEndpoint = detectNetworkFromEndpoint;
2654
+ exports.fetchWithTimeout = fetchWithTimeout;
2655
+ exports.getBaseUrl = getBaseUrl;
2656
+ exports.getDefaultProvidersForNetwork = getDefaultProvidersForNetwork;
2657
+ exports.getEnvVar = getEnvVar;
2658
+ exports.getProviderManager = getProviderManager;
2659
+ exports.getProvidersForNetwork = getProvidersForNetwork;
2660
+ exports.getRateLimitForType = getRateLimitForType;
2661
+ exports.getTonClient = getTonClient;
2662
+ exports.getTonClientForNetwork = getTonClientForNetwork;
2663
+ exports.isApiVersion = isApiVersion;
2664
+ exports.isChainstackUrl = isChainstackUrl;
2665
+ exports.isNetwork = isNetwork;
2666
+ exports.isOrbsUrl = isOrbsUrl;
2667
+ exports.isProviderType = isProviderType;
2668
+ exports.isQuickNodeUrl = isQuickNodeUrl;
2669
+ exports.isRateLimitError = isRateLimitError;
2670
+ exports.isTimeoutError = isTimeoutError;
2671
+ exports.isTonCenterUrl = isTonCenterUrl;
2672
+ exports.isValidHttpUrl = isValidHttpUrl;
2673
+ exports.isValidWsUrl = isValidWsUrl;
2674
+ exports.loadBuiltinConfig = loadBuiltinConfig;
2675
+ exports.loadConfig = loadConfig;
2676
+ exports.loadConfigFromData = loadConfigFromData;
2677
+ exports.loadConfigFromUrl = loadConfigFromUrl;
2678
+ exports.mergeConfigs = mergeConfigs;
2679
+ exports.mergeWithDefaults = mergeWithDefaults;
2680
+ exports.normalizeV2Endpoint = normalizeV2Endpoint;
2681
+ exports.parseProviderConfig = parseProviderConfig;
2682
+ exports.parseRpcConfig = parseRpcConfig;
2683
+ exports.resetNodeAdapter = resetNodeAdapter;
2684
+ exports.resolveAllProviders = resolveAllProviders;
2685
+ exports.resolveEndpoints = resolveEndpoints;
2686
+ exports.resolveKeyPlaceholder = resolveKeyPlaceholder;
2687
+ exports.resolveProvider = resolveProvider;
2688
+ exports.sleep = sleep;
2689
+ exports.toV2Base = toV2Base;
2690
+ exports.toV3Base = toV3Base;
2691
+ exports.withRetry = withRetry;
2692
+ exports.withTimeout = withTimeout;
2693
+ exports.withTimeoutAndRetry = withTimeoutAndRetry;
2694
+ exports.withTimeoutFn = withTimeoutFn;
2695
+ //# sourceMappingURL=index.cjs.map
2696
+ //# sourceMappingURL=index.cjs.map