@monygroupcorp/micro-web3 1.3.5 → 1.3.8
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/micro-web3.cjs +2 -2
- package/dist/micro-web3.cjs.map +1 -1
- package/dist/micro-web3.esm.js +2 -2
- package/dist/micro-web3.esm.js.map +1 -1
- package/dist/micro-web3.umd.js +2 -2
- package/dist/micro-web3.umd.js.map +1 -1
- package/micro-web3-ipfs-gateway-rotation-spec.md +231 -0
- package/package.json +1 -1
- package/src/components/Wallet/WalletModal.js +17 -0
- package/src/services/IpfsService.js +359 -65
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* IPFS Service
|
|
3
|
-
*
|
|
3
|
+
*
|
|
4
4
|
* Light client for IPFS resolution with gateway rotation and fallback.
|
|
5
5
|
* Handles both direct IPFS URIs and metadata fetching.
|
|
6
|
-
*
|
|
6
|
+
*
|
|
7
7
|
* No secrets, no backend - pure client-side IPFS gateway resolution.
|
|
8
8
|
*/
|
|
9
9
|
|
|
@@ -19,6 +19,265 @@ const IPFS_GATEWAYS = [
|
|
|
19
19
|
'https://dweb.link/ipfs/'
|
|
20
20
|
];
|
|
21
21
|
|
|
22
|
+
/**
|
|
23
|
+
* CORS-friendly gateways that work well in development environments
|
|
24
|
+
*/
|
|
25
|
+
const CORS_FRIENDLY_GATEWAYS = [
|
|
26
|
+
'https://cloudflare-ipfs.com/ipfs/',
|
|
27
|
+
'https://dweb.link/ipfs/',
|
|
28
|
+
'https://w3s.link/ipfs/'
|
|
29
|
+
];
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Well-known CID for health checks (empty directory)
|
|
33
|
+
*/
|
|
34
|
+
const HEALTH_CHECK_CID = 'bafybeigdyrzt5sfp7udm7hu76uh7y26nf3efuylqabf3oclgtqy55fbzdi';
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Internal GatewayManager class for intelligent gateway selection and rotation
|
|
38
|
+
*/
|
|
39
|
+
class GatewayManager {
|
|
40
|
+
constructor() {
|
|
41
|
+
this.currentGateway = null;
|
|
42
|
+
this.gatewayLatencies = new Map();
|
|
43
|
+
this.failedGateways = new Set();
|
|
44
|
+
this.failureCounts = new Map();
|
|
45
|
+
this.lastHealthCheck = null;
|
|
46
|
+
this.healthCheckInterval = null;
|
|
47
|
+
this.initialized = false;
|
|
48
|
+
this.config = {
|
|
49
|
+
timeout: 5000,
|
|
50
|
+
maxRetries: 3,
|
|
51
|
+
healthCheckInterval: 60000,
|
|
52
|
+
parallelDiscovery: true,
|
|
53
|
+
corsGatewaysFirst: true
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Detect current environment (development vs production)
|
|
59
|
+
* @returns {'development'|'production'}
|
|
60
|
+
*/
|
|
61
|
+
getEnvironment() {
|
|
62
|
+
if (typeof window === 'undefined') return 'production';
|
|
63
|
+
const hostname = window.location.hostname;
|
|
64
|
+
return hostname === 'localhost' || hostname === '127.0.0.1' || hostname.includes('.local')
|
|
65
|
+
? 'development'
|
|
66
|
+
: 'production';
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Get gateways prioritized based on environment and performance
|
|
71
|
+
* @returns {string[]} Prioritized gateway list
|
|
72
|
+
*/
|
|
73
|
+
getPrioritizedGateways() {
|
|
74
|
+
const custom = getCustomGateway();
|
|
75
|
+
const baseGateways = custom ? [custom, ...IPFS_GATEWAYS] : IPFS_GATEWAYS;
|
|
76
|
+
|
|
77
|
+
if (!this.config.corsGatewaysFirst || this.getEnvironment() !== 'development') {
|
|
78
|
+
return baseGateways;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// In dev, put CORS-friendly gateways first
|
|
82
|
+
const corsFirst = baseGateways.filter(g => CORS_FRIENDLY_GATEWAYS.some(c => g.includes(c)));
|
|
83
|
+
const others = baseGateways.filter(g => !CORS_FRIENDLY_GATEWAYS.some(c => g.includes(c)));
|
|
84
|
+
return [...corsFirst, ...others];
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Record a successful request to a gateway
|
|
89
|
+
* @param {string} gateway - Gateway URL
|
|
90
|
+
* @param {number} latency - Request latency in ms
|
|
91
|
+
*/
|
|
92
|
+
recordSuccess(gateway, latency) {
|
|
93
|
+
this.gatewayLatencies.set(gateway, latency);
|
|
94
|
+
this.failedGateways.delete(gateway);
|
|
95
|
+
this.failureCounts.delete(gateway);
|
|
96
|
+
if (!this.currentGateway || latency < (this.gatewayLatencies.get(this.currentGateway) || Infinity)) {
|
|
97
|
+
this.currentGateway = gateway;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Record a failed request to a gateway
|
|
103
|
+
* @param {string} gateway - Gateway URL
|
|
104
|
+
*/
|
|
105
|
+
recordFailure(gateway) {
|
|
106
|
+
const count = (this.failureCounts.get(gateway) || 0) + 1;
|
|
107
|
+
this.failureCounts.set(gateway, count);
|
|
108
|
+
if (count >= this.config.maxRetries) {
|
|
109
|
+
this.failedGateways.add(gateway);
|
|
110
|
+
this.gatewayLatencies.set(gateway, Infinity);
|
|
111
|
+
if (this.currentGateway === gateway) {
|
|
112
|
+
this.currentGateway = this.getNextBestGateway();
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Get the next best gateway based on latency
|
|
119
|
+
* @returns {string|undefined} Next best gateway URL
|
|
120
|
+
*/
|
|
121
|
+
getNextBestGateway() {
|
|
122
|
+
return this.getPrioritizedGateways()
|
|
123
|
+
.filter(g => !this.failedGateways.has(g))
|
|
124
|
+
.sort((a, b) =>
|
|
125
|
+
(this.gatewayLatencies.get(a) || Infinity) -
|
|
126
|
+
(this.gatewayLatencies.get(b) || Infinity)
|
|
127
|
+
)[0];
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Select the best gateway by racing all gateways
|
|
132
|
+
* @param {string} testCid - CID to use for testing
|
|
133
|
+
* @returns {Promise<string>} Best gateway URL
|
|
134
|
+
*/
|
|
135
|
+
async selectBestGateway(testCid = HEALTH_CHECK_CID) {
|
|
136
|
+
const gateways = this.getPrioritizedGateways();
|
|
137
|
+
|
|
138
|
+
const results = await Promise.allSettled(
|
|
139
|
+
gateways.map(async (gateway) => {
|
|
140
|
+
const start = Date.now();
|
|
141
|
+
const response = await fetch(`${gateway}${testCid}`, {
|
|
142
|
+
method: 'HEAD',
|
|
143
|
+
signal: AbortSignal.timeout(this.config.timeout)
|
|
144
|
+
});
|
|
145
|
+
if (!response.ok) throw new Error('Failed');
|
|
146
|
+
return { gateway, latency: Date.now() - start };
|
|
147
|
+
})
|
|
148
|
+
);
|
|
149
|
+
|
|
150
|
+
const successful = results
|
|
151
|
+
.filter(r => r.status === 'fulfilled')
|
|
152
|
+
.map(r => r.value)
|
|
153
|
+
.sort((a, b) => a.latency - b.latency);
|
|
154
|
+
|
|
155
|
+
// Record all successful results
|
|
156
|
+
successful.forEach(({ gateway, latency }) => this.recordSuccess(gateway, latency));
|
|
157
|
+
|
|
158
|
+
// Record failures
|
|
159
|
+
results.forEach((result, i) => {
|
|
160
|
+
if (result.status === 'rejected') {
|
|
161
|
+
this.recordFailure(gateways[i]);
|
|
162
|
+
}
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
this.currentGateway = successful[0]?.gateway || gateways[0];
|
|
166
|
+
this.initialized = true;
|
|
167
|
+
return this.currentGateway;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Fetch with intelligent gateway rotation
|
|
172
|
+
* @param {string} path - IPFS path to fetch
|
|
173
|
+
* @param {object} options - Fetch options
|
|
174
|
+
* @returns {Promise<object>} Parsed JSON response
|
|
175
|
+
*/
|
|
176
|
+
async fetchWithRotation(path, options = {}) {
|
|
177
|
+
const { timeout = this.config.timeout, ...fetchOptions } = options;
|
|
178
|
+
const gateways = this.getPrioritizedGateways();
|
|
179
|
+
const errors = [];
|
|
180
|
+
|
|
181
|
+
// Start with current best or first gateway
|
|
182
|
+
let startIndex = this.currentGateway
|
|
183
|
+
? gateways.indexOf(this.currentGateway)
|
|
184
|
+
: 0;
|
|
185
|
+
if (startIndex === -1) startIndex = 0;
|
|
186
|
+
|
|
187
|
+
for (let attempt = 0; attempt < gateways.length; attempt++) {
|
|
188
|
+
const index = (startIndex + attempt) % gateways.length;
|
|
189
|
+
const gateway = gateways[index];
|
|
190
|
+
|
|
191
|
+
if (this.failedGateways.has(gateway)) continue;
|
|
192
|
+
|
|
193
|
+
const controller = new AbortController();
|
|
194
|
+
const timeoutId = setTimeout(() => controller.abort(), timeout);
|
|
195
|
+
const start = Date.now();
|
|
196
|
+
|
|
197
|
+
try {
|
|
198
|
+
const response = await fetch(`${gateway}${path}`, {
|
|
199
|
+
...fetchOptions,
|
|
200
|
+
signal: controller.signal
|
|
201
|
+
});
|
|
202
|
+
clearTimeout(timeoutId);
|
|
203
|
+
|
|
204
|
+
if (!response.ok) throw new Error(`HTTP ${response.status}`);
|
|
205
|
+
|
|
206
|
+
const json = await response.json();
|
|
207
|
+
this.recordSuccess(gateway, Date.now() - start);
|
|
208
|
+
return json;
|
|
209
|
+
} catch (error) {
|
|
210
|
+
clearTimeout(timeoutId);
|
|
211
|
+
this.recordFailure(gateway);
|
|
212
|
+
errors.push(`${gateway}: ${error.message}`);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
throw new Error(`All gateways failed:\n${errors.join('\n')}`);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Start background health monitoring
|
|
221
|
+
*/
|
|
222
|
+
startHealthMonitoring() {
|
|
223
|
+
if (this.healthCheckInterval) return;
|
|
224
|
+
if (this.config.healthCheckInterval <= 0) return;
|
|
225
|
+
|
|
226
|
+
this.healthCheckInterval = setInterval(
|
|
227
|
+
() => this.runHealthCheck(),
|
|
228
|
+
this.config.healthCheckInterval
|
|
229
|
+
);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* Stop background health monitoring
|
|
234
|
+
*/
|
|
235
|
+
stopHealthMonitoring() {
|
|
236
|
+
if (this.healthCheckInterval) {
|
|
237
|
+
clearInterval(this.healthCheckInterval);
|
|
238
|
+
this.healthCheckInterval = null;
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* Run a health check on all gateways
|
|
244
|
+
*/
|
|
245
|
+
async runHealthCheck() {
|
|
246
|
+
const gateways = this.getPrioritizedGateways();
|
|
247
|
+
this.lastHealthCheck = Date.now();
|
|
248
|
+
|
|
249
|
+
for (const gateway of gateways) {
|
|
250
|
+
try {
|
|
251
|
+
const start = Date.now();
|
|
252
|
+
const response = await fetch(`${gateway}${HEALTH_CHECK_CID}`, {
|
|
253
|
+
method: 'HEAD',
|
|
254
|
+
signal: AbortSignal.timeout(3000)
|
|
255
|
+
});
|
|
256
|
+
if (response.ok) {
|
|
257
|
+
this.recordSuccess(gateway, Date.now() - start);
|
|
258
|
+
}
|
|
259
|
+
} catch {
|
|
260
|
+
// Don't mark as failed from health check, just update latency
|
|
261
|
+
this.gatewayLatencies.set(gateway, Infinity);
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
/**
|
|
267
|
+
* Reset gateway manager state
|
|
268
|
+
*/
|
|
269
|
+
reset() {
|
|
270
|
+
this.currentGateway = null;
|
|
271
|
+
this.gatewayLatencies.clear();
|
|
272
|
+
this.failedGateways.clear();
|
|
273
|
+
this.failureCounts.clear();
|
|
274
|
+
this.initialized = false;
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
// Singleton instance
|
|
279
|
+
const gatewayManager = new GatewayManager();
|
|
280
|
+
|
|
22
281
|
/**
|
|
23
282
|
* Get custom IPFS gateway from localStorage (user preference)
|
|
24
283
|
* @returns {string|null} Custom gateway base URL or null
|
|
@@ -48,8 +307,7 @@ function getCustomGateway() {
|
|
|
48
307
|
* @returns {string[]} Array of gateway base URLs
|
|
49
308
|
*/
|
|
50
309
|
function getAllGateways() {
|
|
51
|
-
|
|
52
|
-
return custom ? [custom, ...IPFS_GATEWAYS] : IPFS_GATEWAYS;
|
|
310
|
+
return gatewayManager.getPrioritizedGateways();
|
|
53
311
|
}
|
|
54
312
|
|
|
55
313
|
/**
|
|
@@ -85,22 +343,28 @@ export function normalizeIpfsPath(ipfsUri) {
|
|
|
85
343
|
/**
|
|
86
344
|
* Resolve IPFS URI to HTTP URL using a specific gateway
|
|
87
345
|
* @param {string} ipfsUri - IPFS URI (e.g., ipfs://Qm...)
|
|
88
|
-
* @param {number} gatewayIndex - Index of gateway to use (0-based)
|
|
346
|
+
* @param {number} [gatewayIndex] - Index of gateway to use (0-based). If omitted, uses intelligent selection.
|
|
89
347
|
* @returns {string|null} HTTP URL or null if invalid
|
|
90
348
|
*/
|
|
91
|
-
export function resolveIpfsToHttp(ipfsUri, gatewayIndex
|
|
349
|
+
export function resolveIpfsToHttp(ipfsUri, gatewayIndex) {
|
|
92
350
|
if (!isIpfsUri(ipfsUri)) {
|
|
93
351
|
return ipfsUri; // Return as-is if not IPFS
|
|
94
352
|
}
|
|
95
|
-
|
|
353
|
+
|
|
354
|
+
const normalizedPath = normalizeIpfsPath(ipfsUri);
|
|
355
|
+
|
|
356
|
+
// If no index provided, use intelligent gateway selection
|
|
357
|
+
if (gatewayIndex === undefined) {
|
|
358
|
+
const gateway = gatewayManager.currentGateway || gatewayManager.getPrioritizedGateways()[0];
|
|
359
|
+
return gateway + normalizedPath;
|
|
360
|
+
}
|
|
361
|
+
|
|
96
362
|
const gateways = getAllGateways();
|
|
97
363
|
if (gatewayIndex < 0 || gatewayIndex >= gateways.length) {
|
|
98
364
|
return null;
|
|
99
365
|
}
|
|
100
|
-
|
|
101
|
-
const normalizedPath = normalizeIpfsPath(ipfsUri);
|
|
366
|
+
|
|
102
367
|
const gateway = gateways[gatewayIndex];
|
|
103
|
-
|
|
104
368
|
return gateway + normalizedPath;
|
|
105
369
|
}
|
|
106
370
|
|
|
@@ -113,22 +377,22 @@ export function resolveIpfsToHttp(ipfsUri, gatewayIndex = 0) {
|
|
|
113
377
|
*/
|
|
114
378
|
export async function fetchJsonWithIpfsSupport(urlOrIpfs, options = {}) {
|
|
115
379
|
const { timeout = 10000, ...fetchOptions } = options;
|
|
116
|
-
|
|
380
|
+
|
|
117
381
|
// If it's not an IPFS URI, use regular fetch
|
|
118
382
|
if (!isIpfsUri(urlOrIpfs)) {
|
|
119
383
|
const controller = new AbortController();
|
|
120
384
|
const timeoutId = setTimeout(() => controller.abort(), timeout);
|
|
121
|
-
|
|
385
|
+
|
|
122
386
|
try {
|
|
123
387
|
const response = await fetch(urlOrIpfs, {
|
|
124
388
|
...fetchOptions,
|
|
125
389
|
signal: controller.signal
|
|
126
390
|
});
|
|
127
|
-
|
|
391
|
+
|
|
128
392
|
if (!response.ok) {
|
|
129
393
|
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
130
394
|
}
|
|
131
|
-
|
|
395
|
+
|
|
132
396
|
const json = await response.json();
|
|
133
397
|
clearTimeout(timeoutId);
|
|
134
398
|
return json;
|
|
@@ -140,58 +404,10 @@ export async function fetchJsonWithIpfsSupport(urlOrIpfs, options = {}) {
|
|
|
140
404
|
throw error;
|
|
141
405
|
}
|
|
142
406
|
}
|
|
143
|
-
|
|
144
|
-
// IPFS URI -
|
|
145
|
-
const gateways = getAllGateways();
|
|
407
|
+
|
|
408
|
+
// IPFS URI - use intelligent gateway rotation
|
|
146
409
|
const normalizedPath = normalizeIpfsPath(urlOrIpfs);
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
for (let i = 0; i < gateways.length; i++) {
|
|
150
|
-
const gateway = gateways[i];
|
|
151
|
-
const httpUrl = gateway + normalizedPath;
|
|
152
|
-
|
|
153
|
-
const controller = new AbortController();
|
|
154
|
-
const timeoutId = setTimeout(() => controller.abort(), timeout);
|
|
155
|
-
|
|
156
|
-
try {
|
|
157
|
-
const response = await fetch(httpUrl, {
|
|
158
|
-
...fetchOptions,
|
|
159
|
-
signal: controller.signal
|
|
160
|
-
});
|
|
161
|
-
|
|
162
|
-
clearTimeout(timeoutId);
|
|
163
|
-
|
|
164
|
-
if (!response.ok) {
|
|
165
|
-
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
const json = await response.json();
|
|
169
|
-
|
|
170
|
-
// Success - log which gateway worked (for debugging)
|
|
171
|
-
if (i > 0) {
|
|
172
|
-
console.log(`[IpfsService] Resolved IPFS via gateway ${i + 1}/${gateways.length}: ${gateway}`);
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
return json;
|
|
176
|
-
} catch (error) {
|
|
177
|
-
clearTimeout(timeoutId);
|
|
178
|
-
|
|
179
|
-
const errorMsg = error.name === 'AbortError'
|
|
180
|
-
? `Timeout after ${timeout}ms`
|
|
181
|
-
: error.message || 'Unknown error';
|
|
182
|
-
|
|
183
|
-
errors.push(`Gateway ${i + 1} (${gateway}): ${errorMsg}`);
|
|
184
|
-
|
|
185
|
-
// Continue to next gateway
|
|
186
|
-
continue;
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
// All gateways failed
|
|
191
|
-
throw new Error(
|
|
192
|
-
`Failed to fetch IPFS content after trying ${gateways.length} gateways:\n` +
|
|
193
|
-
errors.join('\n')
|
|
194
|
-
);
|
|
410
|
+
return gatewayManager.fetchWithRotation(normalizedPath, { timeout, ...fetchOptions });
|
|
195
411
|
}
|
|
196
412
|
|
|
197
413
|
/**
|
|
@@ -242,8 +458,86 @@ export function setCustomGateway(gatewayBaseUrl) {
|
|
|
242
458
|
} else {
|
|
243
459
|
localStorage.removeItem('customIpfsGatewayBaseUrl');
|
|
244
460
|
}
|
|
461
|
+
// Reset gateway manager state when custom gateway changes
|
|
462
|
+
gatewayManager.reset();
|
|
245
463
|
} catch (error) {
|
|
246
464
|
console.warn('[IpfsService] Failed to save custom gateway to localStorage:', error);
|
|
247
465
|
}
|
|
248
466
|
}
|
|
249
467
|
|
|
468
|
+
// ============================================================================
|
|
469
|
+
// New exports for intelligent gateway management
|
|
470
|
+
// ============================================================================
|
|
471
|
+
|
|
472
|
+
/**
|
|
473
|
+
* Get the current best gateway
|
|
474
|
+
* @returns {string} Current best gateway URL
|
|
475
|
+
*/
|
|
476
|
+
export function getGateway() {
|
|
477
|
+
return gatewayManager.currentGateway || gatewayManager.getPrioritizedGateways()[0];
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
/**
|
|
481
|
+
* Resolve an IPFS URI to an HTTP URL using the best gateway
|
|
482
|
+
* Simpler alias for resolveIpfsToHttp without gateway index
|
|
483
|
+
* @param {string} ipfsUri - IPFS URI to resolve
|
|
484
|
+
* @returns {string} HTTP URL
|
|
485
|
+
*/
|
|
486
|
+
export function resolveUrl(ipfsUri) {
|
|
487
|
+
if (!isIpfsUri(ipfsUri)) return ipfsUri;
|
|
488
|
+
return getGateway() + normalizeIpfsPath(ipfsUri);
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
/**
|
|
492
|
+
* Fetch JSON from IPFS with intelligent gateway rotation
|
|
493
|
+
* Cleaner API that assumes IPFS CID input
|
|
494
|
+
* @param {string} cid - IPFS CID or path
|
|
495
|
+
* @param {object} options - Fetch options
|
|
496
|
+
* @returns {Promise<object>} Parsed JSON
|
|
497
|
+
*/
|
|
498
|
+
export function fetchJson(cid, options = {}) {
|
|
499
|
+
return gatewayManager.fetchWithRotation(cid, options);
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
/**
|
|
503
|
+
* Get gateway statistics
|
|
504
|
+
* @returns {object} Gateway stats including current, latencies, failed, lastHealthCheck, initialized
|
|
505
|
+
*/
|
|
506
|
+
export function getGatewayStats() {
|
|
507
|
+
return {
|
|
508
|
+
current: gatewayManager.currentGateway,
|
|
509
|
+
latencies: Object.fromEntries(gatewayManager.gatewayLatencies),
|
|
510
|
+
failed: Array.from(gatewayManager.failedGateways),
|
|
511
|
+
lastHealthCheck: gatewayManager.lastHealthCheck,
|
|
512
|
+
initialized: gatewayManager.initialized
|
|
513
|
+
};
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
/**
|
|
517
|
+
* Initialize gateway discovery by racing all gateways
|
|
518
|
+
* @param {string} [testCid] - Optional CID to use for testing
|
|
519
|
+
* @returns {Promise<string>} Best gateway URL
|
|
520
|
+
*/
|
|
521
|
+
export async function initializeGatewayDiscovery(testCid) {
|
|
522
|
+
return gatewayManager.selectBestGateway(testCid);
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
/**
|
|
526
|
+
* Configure the gateway manager
|
|
527
|
+
* @param {object} options - Configuration options
|
|
528
|
+
* @param {number} [options.timeout] - Request timeout in ms
|
|
529
|
+
* @param {number} [options.maxRetries] - Max retries before marking gateway as failed
|
|
530
|
+
* @param {number} [options.healthCheckInterval] - Health check interval in ms (0 to disable)
|
|
531
|
+
* @param {boolean} [options.parallelDiscovery] - Whether to race gateways in parallel
|
|
532
|
+
* @param {boolean} [options.corsGatewaysFirst] - Whether to prioritize CORS-friendly gateways in dev
|
|
533
|
+
*/
|
|
534
|
+
export function configureGatewayManager(options) {
|
|
535
|
+
Object.assign(gatewayManager.config, options);
|
|
536
|
+
if (options.healthCheckInterval !== undefined) {
|
|
537
|
+
gatewayManager.stopHealthMonitoring();
|
|
538
|
+
if (options.healthCheckInterval > 0) {
|
|
539
|
+
gatewayManager.startHealthMonitoring();
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
|