/** * Fetch wrapper with automatic timeout protection * Prevents thread exhaustion from slow external services */ /** * Wrapper for fetch with automatic timeout using AbortController * * @param url - The URL to fetch * @param options - Standard fetch options * @param timeoutMs - Timeout in milliseconds (default: 5000) * @returns Response from fetch * @throws Error if request times out or fails */ export async function fetchWithTimeout( url: string, options: RequestInit = {}, timeoutMs = 5000 ): Promise { const controller = new AbortController(); const timer = setTimeout(() => controller.abort(), timeoutMs); try { const response = await fetch(url, { ...options, signal: controller.signal, }); clearTimeout(timer); return response; } catch (error) { clearTimeout(timer); if (error instanceof Error && error.name === 'AbortError') { throw new Error(`Request timeout after ${timeoutMs}ms: ${url}`); } throw error; } }