/** * Utility functions for API requests with exponential backoff and polling. */ export const delay = (ms: number) => new Promise(resolve => setTimeout(resolve, ms)); export interface FetchOptions extends RequestInit { maxRetries?: number; baseDelayMs?: number; } export async function fetchWithRetry(url: string, options: FetchOptions = {}): Promise { const { maxRetries = 3, baseDelayMs = 1000, ...fetchOptions } = options; let retries = 0; while (true) { try { const response = await fetch(url, fetchOptions); if (response.ok) { return response; } // Don't retry on 4xx errors (client errors) if (response.status >= 400 && response.status < 500 && response.status !== 429) { throw new Error(`HTTP Error: ${response.status} ${response.statusText}`); } // If it's a 429 (Rate Limit) or 5xx, we throw to trigger a retry throw new Error(`HTTP Error: ${response.status} ${response.statusText}`); } catch (error) { if (retries >= maxRetries) { throw error; } retries++; // Exponential backoff with some jitter const waitTime = (baseDelayMs * Math.pow(2, retries - 1)) + (Math.random() * 500); console.warn(`Request failed, retrying in ${Math.round(waitTime)}ms... (Attempt ${retries}/${maxRetries})`); await delay(waitTime); } } } export interface PollOptions { intervalMs?: number; maxAttempts?: number; onProgress?: (progress: number) => void; } export async function pollStatus( pollFn: () => Promise<{ status: 'processing' | 'success' | 'failed', data?: T, progress?: number }>, options: PollOptions = {} ): Promise { const { intervalMs = 2000, maxAttempts = 150, onProgress } = options; // Default to 5 minutes total polling let attempts = 0; while (attempts < maxAttempts) { try { const result = await pollFn(); if (result.status === 'success' && result.data) { return result.data; } if (result.status === 'failed') { throw new Error("Task failed during processing on the remote server."); } if (result.progress !== undefined && onProgress) { onProgress(result.progress); } } catch (err) { // If the poll request itself fails, we log it but keep trying until maxAttempts console.warn("Polling request failed, will retry next interval:", err); } attempts++; await delay(intervalMs); } throw new Error("Polling timeout exceeded."); }