(
url: string,
options: RequestInit = {},
retryOptions: RetryOptions = {}
)
| 180 | * Wrapper for fetch requests with retry logic |
| 181 | */ |
| 182 | export async function fetchWithRetry( |
| 183 | url: string, |
| 184 | options: RequestInit = {}, |
| 185 | retryOptions: RetryOptions = {} |
| 186 | ): Promise<Response> { |
| 187 | return retryWithExponentialBackoff(async () => { |
| 188 | const response = await fetch(url, options) |
| 189 | |
| 190 | // If response is not ok and status indicates rate limiting, throw an error |
| 191 | if (!response.ok && isRetryableError({ status: response.status })) { |
| 192 | const errorText = await response.text() |
| 193 | const error: HTTPError = new Error( |
| 194 | `HTTP ${response.status}: ${response.statusText} - ${errorText}` |
| 195 | ) |
| 196 | error.status = response.status |
| 197 | error.statusText = response.statusText |
| 198 | |
| 199 | // Pass Retry-After to the retry loop so it replaces exponential backoff |
| 200 | const retryAfter = response.headers.get('Retry-After') |
| 201 | if (retryAfter) { |
| 202 | const waitMs = Number.isNaN(Number(retryAfter)) |
| 203 | ? Math.max(0, new Date(retryAfter).getTime() - Date.now()) |
| 204 | : Number(retryAfter) * 1000 |
| 205 | if (waitMs > 0) { |
| 206 | error.retryAfterMs = waitMs |
| 207 | } |
| 208 | } |
| 209 | |
| 210 | throw error |
| 211 | } |
| 212 | |
| 213 | return response |
| 214 | }, retryOptions) |
| 215 | } |
no test coverage detected