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