Spaces:
Sleeping
Sleeping
| import { logger } from './logger'; | |
| interface RetryOptions { | |
| maxAttempts: number; | |
| baseDelayMs?: number; | |
| maxDelayMs?: number; | |
| retryIf?: (error: unknown) => boolean; | |
| label?: string; | |
| } | |
| /** | |
| * Retry a function with exponential backoff. | |
| * Returns the result on success or throws after all attempts fail. | |
| */ | |
| export async function retry<T>( | |
| fn: () => Promise<T>, | |
| options: RetryOptions | |
| ): Promise<T> { | |
| const { | |
| maxAttempts, | |
| baseDelayMs = 1000, | |
| maxDelayMs = 30000, | |
| retryIf, | |
| label = 'operation', | |
| } = options; | |
| let lastError: unknown; | |
| for (let attempt = 1; attempt <= maxAttempts; attempt++) { | |
| try { | |
| return await fn(); | |
| } catch (error) { | |
| lastError = error; | |
| if (retryIf && !retryIf(error)) { | |
| throw error; | |
| } | |
| if (attempt === maxAttempts) { | |
| break; | |
| } | |
| const delay = Math.min(baseDelayMs * Math.pow(2, attempt - 1), maxDelayMs); | |
| const jitter = delay * (0.5 + Math.random() * 0.5); | |
| logger.warn(`${label} attempt ${attempt}/${maxAttempts} failed, retrying in ${Math.round(jitter)}ms`, { | |
| error: error instanceof Error ? error.message : String(error), | |
| attempt, | |
| maxAttempts, | |
| }); | |
| await sleep(jitter); | |
| } | |
| } | |
| throw lastError; | |
| } | |
| /** Promise-based sleep */ | |
| export function sleep(ms: number): Promise<void> { | |
| return new Promise((resolve) => setTimeout(resolve, ms)); | |
| } | |