| |
| |
| |
| |
| |
| |
| |
| |
|
|
| const DEFAULT_TIMEOUT_MS = 120000; |
| const FETCH_TIMEOUT_MS = parseInt(process.env.FETCH_TIMEOUT_MS || "", 10) || DEFAULT_TIMEOUT_MS; |
|
|
| interface FetchTimeoutOptions extends RequestInit { |
| timeoutMs?: number; |
| } |
|
|
| export async function fetchWithTimeout(url: string | URL, options: FetchTimeoutOptions = {}) { |
| const { timeoutMs = FETCH_TIMEOUT_MS, signal: externalSignal, ...fetchOptions } = options; |
|
|
| const controller = new AbortController(); |
| const timeoutId = setTimeout(() => controller.abort(), timeoutMs); |
|
|
| |
| if (externalSignal) { |
| if (externalSignal.aborted) { |
| controller.abort(); |
| } else { |
| externalSignal.addEventListener("abort", () => controller.abort(), { once: true }); |
| } |
| } |
|
|
| try { |
| const response = await fetch(url, { |
| ...fetchOptions, |
| signal: controller.signal, |
| }); |
| return response; |
| } catch (error: any) { |
| if (error.name === "AbortError") { |
| throw new FetchTimeoutError( |
| `Request to ${url} timed out after ${timeoutMs}ms`, |
| timeoutMs, |
| String(url) |
| ); |
| } |
| throw error; |
| } finally { |
| clearTimeout(timeoutId); |
| } |
| } |
|
|
| |
| |
| |
| export class FetchTimeoutError extends Error { |
| timeoutMs: number; |
| url: string; |
|
|
| constructor(message: string, timeoutMs: number, url: string) { |
| super(message); |
| this.name = "FetchTimeoutError"; |
| this.timeoutMs = timeoutMs; |
| this.url = url; |
| } |
| } |
|
|
| |
| |
| |
| |
| export function getConfiguredTimeout() { |
| return FETCH_TIMEOUT_MS; |
| } |
|
|