| |
| |
| |
| |
| |
| |
| |
|
|
|
|
| const DEFAULT_TIMEOUT_MS =
|
| parseInt(process.env.OMNIROUTE_DEFAULT_FETCH_TIMEOUT_MS || "", 10) || 120000;
|
| const FETCH_TIMEOUT_MS = parseInt(process.env.FETCH_TIMEOUT_MS || "", 10) || DEFAULT_TIMEOUT_MS;
|
|
|
| interface FetchTimeoutOptions extends RequestInit {
|
| timeoutMs?: number;
|
| |
|
|
| fetchFn?: typeof globalThis.fetch;
|
| }
|
|
|
| export async function fetchWithTimeout(url: string | URL, options: FetchTimeoutOptions = {}) {
|
| const { timeoutMs = FETCH_TIMEOUT_MS, signal: externalSignal, fetchFn, ...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 doFetch = fetchFn || globalThis.fetch;
|
| const response = await doFetch(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;
|
| }
|
|
|