41 lines · 1.2 KB · TypeScript
RawDownload
export interface RetryOptions {  attempts?: number  delay?: number  factor?: number  maxDelay?: number  retryIf?: (error: unknown) => boolean} const TRANSIENT_MESSAGES = [  "load failed",  "network connection was lost",  "network request failed",  "failed to fetch",  "econnreset",  "econnrefused",  "etimedout",  "socket hang up",] function isTransientError(error: unknown): boolean {  if (!error) return false  const message = String(error instanceof Error ? error.message : error).toLowerCase()  return TRANSIENT_MESSAGES.some((m) => message.includes(m))} export async function retry<T>(fn: () => Promise<T>, options: RetryOptions = {}): Promise<T> {  const { attempts = 3, delay = 500, factor = 2, maxDelay = 10000, retryIf = isTransientError } = options   let lastError: unknown  for (let attempt = 0; attempt < attempts; attempt++) {    try {      return await fn()    } catch (error) {      lastError = error      if (attempt === attempts - 1 || !retryIf(error)) throw error      const wait = Math.min(delay * Math.pow(factor, attempt), maxDelay)      await new Promise((resolve) => setTimeout(resolve, wait))    }  }  throw lastError}