All files / src/utils fetch.ts

70% Statements 14/20
65% Branches 13/20
50% Functions 1/2
72.22% Lines 13/18

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56                              97x     97x 17x       80x 23x     57x 57x 57x   57x   57x       97x       57x 57x                          
/**
 * Fetch with timeout support.
 * Wraps the standard fetch API with an AbortController to enforce request timeouts.
 *
 * @param url - The URL to fetch
 * @param options - Standard fetch options
 * @param timeoutMs - Timeout in milliseconds (optional)
 * @returns Promise<Response>
 * @throws Error if the request times out
 */
export async function fetchWithTimeout(
  url: string,
  options: RequestInit = {},
  timeoutMs?: number
): Promise<Response> {
  const userSignal = options.signal;
 
  // If no timeout is specified and no user signal, use standard fetch
  if ((!timeoutMs || timeoutMs <= 0) && !userSignal) {
    return fetch(url, options);
  }
 
  // If only user signal (no timeout), use it directly
  if (!timeoutMs || timeoutMs <= 0) {
    return fetch(url, options);
  }
 
  const controller = new AbortController();
  const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
  Eif (timeoutId.unref) timeoutId.unref();
 
  try {
    // Merge user signal with timeout signal
    const mergedSignal = userSignal
      ? AbortSignal.any([userSignal, controller.signal])
      : controller.signal;
 
    const response = await fetch(url, {
      ...options,
      signal: mergedSignal
    });
    clearTimeout(timeoutId);
    return response;
  } catch (error: unknown) {
    clearTimeout(timeoutId);
 
    // Check if the error was due to timeout abort
    const isAbortError = error instanceof Error && error.name === "AbortError";
    if (isAbortError && controller.signal.aborted) {
      throw new Error(`Request timeout after ${timeoutMs}ms`);
    }
 
    throw error;
  }
}