45 lines
1.2 KiB
TypeScript
45 lines
1.2 KiB
TypeScript
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')
|
|
}
|