evermark-sdk
Version:
Unified SDK for robust image handling and content management with storage orchestration
130 lines • 4.76 kB
JavaScript
/**
* IPFS fetching with multiple gateway fallbacks
*/
import { isValidIpfsHash } from '../core/url-resolver';
export class IPFSClient {
config;
gateways;
timeout;
constructor(config) {
this.config = config;
this.gateways = [
config.gateway,
...(config.fallbackGateways || [])
];
this.timeout = config.timeout || 10000;
}
/**
* Fetch file from IPFS with gateway fallbacks
*/
async fetchFile(ipfsHash, options = {}) {
if (!isValidIpfsHash(ipfsHash)) {
return { success: false, error: 'Invalid IPFS hash' };
}
const { timeout = this.timeout, onProgress } = options;
const startTime = Date.now();
for (const gateway of this.gateways) {
try {
const url = `${gateway}/${ipfsHash}`;
console.log(`Attempting to fetch from gateway: ${gateway}`);
const response = await this.fetchWithProgress(url, timeout, onProgress);
if (response.ok) {
const data = await response.blob();
const loadTime = Date.now() - startTime;
return {
success: true,
data,
gateway,
metadata: {
size: data.size,
type: data.type,
loadTime
}
};
}
}
catch (error) {
console.warn(`Failed to fetch from gateway ${gateway}:`, error);
continue;
}
}
return {
success: false,
error: `Failed to fetch from all ${this.gateways.length} gateways`
};
}
/**
* Fetch with progress tracking and timeout
*/
async fetchWithProgress(url, timeout, onProgress) {
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), timeout);
try {
const response = await fetch(url, { signal: controller.signal });
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
// Handle progress tracking
if (onProgress && response.body) {
const total = parseInt(response.headers.get('content-length') || '0');
let loaded = 0;
const reader = response.body.getReader();
const stream = new ReadableStream({
start(controller) {
function pump() {
return reader.read().then(({ done, value }) => {
if (done) {
controller.close();
return;
}
loaded += value?.length || 0;
if (onProgress) {
onProgress(loaded, total || undefined);
}
controller.enqueue(value);
return pump();
});
}
return pump();
}
});
return new Response(stream, {
headers: response.headers,
status: response.status,
statusText: response.statusText
});
}
return response;
}
finally {
clearTimeout(timeoutId);
}
}
/**
* Test IPFS gateway availability
*/
async testGateways() {
const results = await Promise.allSettled(this.gateways.map(async (gateway) => {
const startTime = Date.now();
try {
const testHash = 'QmYwAPJzv5CZsnA625s3Xf2nemtYgPpHdWEz79ojWnPbdG';
const response = await fetch(`${gateway}/${testHash}`, {
method: 'HEAD',
signal: AbortSignal.timeout(5000)
});
return {
gateway,
available: response.ok,
latency: Date.now() - startTime
};
}
catch {
return { gateway, available: false };
}
}));
return results.map((result, index) => result.status === 'fulfilled'
? result.value
: { gateway: this.gateways[index], available: false });
}
}
//# sourceMappingURL=ipfs-client.js.map