@monygroupcorp/micro-web3 1.3.6 → 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.
@@ -0,0 +1,231 @@
1
+ # micro-web3 IpfsService: Intelligent Gateway Rotation
2
+
3
+ ## Current State
4
+
5
+ IpfsService currently only provides:
6
+ - `setCustomGateway(url)` - Set a custom gateway URL
7
+
8
+ **Missing methods (apps need these now):**
9
+ - `getGateway()` - Get the current gateway URL
10
+ - `resolveUrl(ipfsUri)` - Convert `ipfs://` URI to gateway URL
11
+ - `fetchJson(cid)` - Fetch and parse JSON from IPFS
12
+
13
+ ## Problem
14
+
15
+ Currently, IpfsService uses a single hardcoded gateway which fails when that gateway is down or slow. Users must manually switch gateways when issues occur.
16
+
17
+ ## Proposed Solution
18
+
19
+ Implement dynamic gateway rotation with automatic failover and performance-based selection.
20
+
21
+ ## Feature Requirements
22
+
23
+ ### 1. Gateway Pool
24
+
25
+ Maintain a pool of known reliable IPFS gateways:
26
+
27
+ ```javascript
28
+ const GATEWAY_POOL = [
29
+ 'https://nftstorage.link/ipfs/',
30
+ 'https://dweb.link/ipfs/',
31
+ 'https://cloudflare-ipfs.com/ipfs/',
32
+ 'https://w3s.link/ipfs/',
33
+ 'https://ipfs.io/ipfs/'
34
+ ];
35
+ ```
36
+
37
+ ### 2. Environment-Aware Gateway Prioritization
38
+
39
+ Detect development vs production and prioritize CORS-friendly gateways in dev:
40
+
41
+ ```javascript
42
+ function getEnvironment() {
43
+ const hostname = window.location.hostname;
44
+ return hostname === 'localhost' || hostname === '127.0.0.1' || hostname.includes('.local')
45
+ ? 'development'
46
+ : 'production';
47
+ }
48
+
49
+ // Gateways known to have proper CORS headers for localhost
50
+ const CORS_FRIENDLY_GATEWAYS = [
51
+ 'https://cloudflare-ipfs.com/ipfs/',
52
+ 'https://dweb.link/ipfs/',
53
+ 'https://w3s.link/ipfs/'
54
+ ];
55
+
56
+ function getPrioritizedGateways() {
57
+ if (getEnvironment() === 'development') {
58
+ // In dev, put CORS-friendly gateways first
59
+ return [
60
+ ...CORS_FRIENDLY_GATEWAYS,
61
+ ...GATEWAY_POOL.filter(g => !CORS_FRIENDLY_GATEWAYS.includes(g))
62
+ ];
63
+ }
64
+ return GATEWAY_POOL;
65
+ }
66
+ ```
67
+
68
+ This ensures developers don't hit CORS issues during local development while still allowing the full gateway pool in production.
69
+
70
+ ### 2. Initial Gateway Selection (Race Strategy)
71
+
72
+ When fetching the first batch of assets, race all gateways in parallel:
73
+
74
+ ```javascript
75
+ async function selectBestGateway(testCid) {
76
+ const results = await Promise.allSettled(
77
+ GATEWAY_POOL.map(async (gateway) => {
78
+ const start = Date.now();
79
+ const response = await fetch(`${gateway}${testCid}`, {
80
+ method: 'HEAD',
81
+ signal: AbortSignal.timeout(5000)
82
+ });
83
+ if (!response.ok) throw new Error('Failed');
84
+ return { gateway, latency: Date.now() - start };
85
+ })
86
+ );
87
+
88
+ const successful = results
89
+ .filter(r => r.status === 'fulfilled')
90
+ .map(r => r.value)
91
+ .sort((a, b) => a.latency - b.latency);
92
+
93
+ return successful[0]?.gateway || GATEWAY_POOL[0];
94
+ }
95
+ ```
96
+
97
+ ### 3. Parallel First-Batch Loading
98
+
99
+ For the initial N assets (e.g., first 3), use different gateways simultaneously to determine the fastest:
100
+
101
+ ```javascript
102
+ async function loadWithGatewayDiscovery(cids) {
103
+ // Assign each of first N cids to different gateways
104
+ const gatewayStats = {};
105
+
106
+ const results = await Promise.allSettled(
107
+ cids.slice(0, GATEWAY_POOL.length).map((cid, i) => {
108
+ const gateway = GATEWAY_POOL[i % GATEWAY_POOL.length];
109
+ const start = Date.now();
110
+ return fetch(`${gateway}${cid}`)
111
+ .then(res => res.json())
112
+ .then(data => {
113
+ gatewayStats[gateway] = Date.now() - start;
114
+ return { cid, data, gateway };
115
+ });
116
+ })
117
+ );
118
+
119
+ // Select best gateway based on results
120
+ const bestGateway = Object.entries(gatewayStats)
121
+ .sort(([,a], [,b]) => a - b)[0]?.[0];
122
+
123
+ // Use best gateway for remaining assets
124
+ return { results, bestGateway };
125
+ }
126
+ ```
127
+
128
+ ### 4. Automatic Failover
129
+
130
+ If the selected gateway fails mid-session, automatically rotate to next best:
131
+
132
+ ```javascript
133
+ class IpfsService {
134
+ constructor() {
135
+ this.currentGateway = null;
136
+ this.gatewayLatencies = new Map();
137
+ this.failedGateways = new Set();
138
+ }
139
+
140
+ async fetch(cid) {
141
+ const gateway = this.currentGateway || await this.selectBestGateway(cid);
142
+
143
+ try {
144
+ return await this._fetchWithTimeout(`${gateway}${cid}`);
145
+ } catch (err) {
146
+ this.failedGateways.add(gateway);
147
+ this.currentGateway = this._getNextBestGateway();
148
+ return this.fetch(cid); // Retry with new gateway
149
+ }
150
+ }
151
+
152
+ _getNextBestGateway() {
153
+ return GATEWAY_POOL
154
+ .filter(g => !this.failedGateways.has(g))
155
+ .sort((a, b) =>
156
+ (this.gatewayLatencies.get(a) || Infinity) -
157
+ (this.gatewayLatencies.get(b) || Infinity)
158
+ )[0];
159
+ }
160
+ }
161
+ ```
162
+
163
+ ### 5. Gateway Health Monitoring
164
+
165
+ Periodically test gateway health and update rankings:
166
+
167
+ ```javascript
168
+ async function monitorGatewayHealth() {
169
+ // Run every 60 seconds in background
170
+ for (const gateway of GATEWAY_POOL) {
171
+ try {
172
+ const start = Date.now();
173
+ await fetch(`${gateway}bafybeigdyrzt5sfp7udm7hu76uh7y26nf3efuylqabf3oclgtqy55fbzdi`, {
174
+ method: 'HEAD',
175
+ signal: AbortSignal.timeout(3000)
176
+ });
177
+ this.gatewayLatencies.set(gateway, Date.now() - start);
178
+ this.failedGateways.delete(gateway);
179
+ } catch {
180
+ this.gatewayLatencies.set(gateway, Infinity);
181
+ }
182
+ }
183
+ }
184
+ ```
185
+
186
+ ## API Design
187
+
188
+ ```javascript
189
+ import { IpfsService } from '@monygroupcorp/micro-web3';
190
+
191
+ // Auto-selects best gateway
192
+ const ipfs = new IpfsService();
193
+
194
+ // Fetch with automatic gateway rotation
195
+ const metadata = await ipfs.fetchJson(cid);
196
+ const imageUrl = ipfs.resolveUrl(metadata.image);
197
+
198
+ // Manual gateway override (optional)
199
+ ipfs.setPreferredGateway('https://dweb.link/ipfs/');
200
+
201
+ // Get current gateway stats
202
+ const stats = ipfs.getGatewayStats();
203
+ // { current: 'https://nftstorage.link/ipfs/', latencies: {...}, failed: [...] }
204
+ ```
205
+
206
+ ## Configuration Options
207
+
208
+ ```javascript
209
+ const ipfs = new IpfsService({
210
+ gateways: [...customGateways], // Override default pool
211
+ timeout: 5000, // Request timeout (ms)
212
+ maxRetries: 3, // Retries before marking gateway failed
213
+ healthCheckInterval: 60000, // Health monitoring interval (ms)
214
+ parallelDiscovery: true, // Use parallel first-batch strategy
215
+ cacheResults: true // Cache successful responses
216
+ });
217
+ ```
218
+
219
+ ## Benefits
220
+
221
+ 1. **Reliability**: Automatic failover means no manual intervention when gateways fail
222
+ 2. **Performance**: Always uses the fastest available gateway
223
+ 3. **Resilience**: Survives individual gateway outages transparently
224
+ 4. **Developer Experience**: Simple API, complex logic handled internally
225
+
226
+ ## Migration Path
227
+
228
+ 1. Add new gateway rotation logic to IpfsService
229
+ 2. Keep `setCustomGateway()` for backwards compatibility
230
+ 3. Default to new auto-selection behavior
231
+ 4. Apps using IpfsService get improvements automatically
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@monygroupcorp/micro-web3",
3
- "version": "1.3.6",
3
+ "version": "1.3.8",
4
4
  "description": "A lean, reusable Web3 toolkit with components for wallet connection, IPFS, and common Web3 UI patterns.",
5
5
  "main": "dist/micro-web3.cjs",
6
6
  "module": "dist/micro-web3.esm.js",
@@ -65,6 +65,23 @@ export class WalletModal extends Component {
65
65
  )
66
66
  );
67
67
  }
68
+
69
+ static get styles() {
70
+ return `
71
+ .wallet-option .wallet-icon {
72
+ width: 36px;
73
+ height: 36px;
74
+ display: flex;
75
+ align-items: center;
76
+ justify-content: center;
77
+ }
78
+
79
+ .wallet-option .wallet-icon svg {
80
+ width: 28px;
81
+ height: 28px;
82
+ }
83
+ `;
84
+ }
68
85
  }
69
86
 
70
87
  export default WalletModal;