Deploy to production
This commit is contained in:
@@ -0,0 +1,81 @@
|
||||
/**
|
||||
* 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<Response> {
|
||||
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<T>(
|
||||
pollFn: () => Promise<{ status: 'processing' | 'success' | 'failed', data?: T, progress?: number }>,
|
||||
options: PollOptions = {}
|
||||
): Promise<T> {
|
||||
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.");
|
||||
}
|
||||
Reference in New Issue
Block a user