| import { disableKeepAlive, getProxyFetchOptions } from '../../utils/proxy.js' | |
| const RETRYABLE_FETCH_ERROR_PATTERN = | |
| /socket connection was closed unexpectedly|ECONNRESET|EPIPE|socket hang up|Connection reset by peer|fetch failed/i | |
| export function isRetryableFetchError(error: unknown): boolean { | |
| if (!(error instanceof Error)) { | |
| return false | |
| } | |
| if (error.name === 'AbortError') { | |
| return false | |
| } | |
| return RETRYABLE_FETCH_ERROR_PATTERN.test(error.message) | |
| } | |
| export async function fetchWithProxyRetry( | |
| input: string | URL | Request, | |
| init?: RequestInit, | |
| options?: { forAnthropicAPI?: boolean; maxAttempts?: number }, | |
| ): Promise<Response> { | |
| const maxAttempts = Math.max(1, options?.maxAttempts ?? 2) | |
| let lastError: unknown | |
| for (let attempt = 1; attempt <= maxAttempts; attempt++) { | |
| try { | |
| return await fetch(input, { | |
| ...init, | |
| ...getProxyFetchOptions({ | |
| forAnthropicAPI: options?.forAnthropicAPI, | |
| }), | |
| }) | |
| } catch (error) { | |
| lastError = error | |
| if (attempt >= maxAttempts || !isRetryableFetchError(error)) { | |
| throw error | |
| } | |
| disableKeepAlive() | |
| } | |
| } | |
| throw lastError instanceof Error | |
| ? lastError | |
| : new Error('Fetch failed without an error object') | |
| } | |