class FetchTimeoutError extends Error {
  constructor(url, timeoutMs) {
    super(`Request to ${url} timed out after ${timeoutMs}ms`);
    this.name = "FetchTimeoutError";
    this.timeoutMs = timeoutMs;
    this.url = url;
  }
}
const DEFAULT_TIMEOUT_MS = 15e3;
function resolveTimeoutMs(value, fallback = DEFAULT_TIMEOUT_MS) {
  if (typeof value === "number" && Number.isFinite(value) && value > 0) return Math.floor(value);
  return fallback;
}
async function fetchWithTimeout(input, init = {}) {
  const { timeoutMs, signal, ...rest } = init;
  const effectiveTimeout = resolveTimeoutMs(timeoutMs);
  const controller = new AbortController();
  const urlForError = typeof input === "string" ? input : input instanceof URL ? input.toString() : input.url;
  const timer = setTimeout(() => {
    controller.abort(new FetchTimeoutError(urlForError, effectiveTimeout));
  }, effectiveTimeout);
  const onExternalAbort = () => {
    controller.abort(signal?.reason);
  };
  if (signal) {
    if (signal.aborted) {
      clearTimeout(timer);
      throw signal.reason instanceof Error ? signal.reason : new DOMException("Aborted", "AbortError");
    }
    signal.addEventListener("abort", onExternalAbort, { once: true });
  }
  try {
    return await fetch(input, { ...rest, signal: controller.signal });
  } catch (err) {
    if (err?.name === "AbortError") {
      const reason = controller.signal.reason;
      if (reason instanceof FetchTimeoutError) throw reason;
      if (reason instanceof Error) throw reason;
    }
    throw err;
  } finally {
    clearTimeout(timer);
    if (signal) signal.removeEventListener("abort", onExternalAbort);
  }
}
async function withTimeout(task, timeoutMs, label) {
  const effectiveTimeout = resolveTimeoutMs(timeoutMs);
  const controller = new AbortController();
  let timedOut = false;
  const timer = setTimeout(() => {
    timedOut = true;
    controller.abort();
  }, effectiveTimeout);
  try {
    return await task(controller.signal);
  } catch (err) {
    if (timedOut) {
      throw new FetchTimeoutError(label, effectiveTimeout);
    }
    throw err;
  } finally {
    clearTimeout(timer);
  }
}
export {
  FetchTimeoutError,
  fetchWithTimeout,
  resolveTimeoutMs,
  withTimeout
};
//# sourceMappingURL=fetchWithTimeout.js.map
