Abort Signal Timeout Wrapper

A typed helper that links an AbortSignal to a deadline so in-flight requests are cancelled deterministically and listeners never leak.

TypescriptData Fetching

Code

export interface DeadlineHandle {
  readonly signal: AbortSignal;
  cleanup(): void;
}

export function withDeadline(timeoutMs: number, parent?: AbortSignal): DeadlineHandle {
  if (!Number.isFinite(timeoutMs) || timeoutMs <= 0) {
    throw new RangeError("timeoutMs must be a positive number");
  }
  const controller = new AbortController();
  if (parent?.aborted) {
    controller.abort(parent.reason);
  } else if (parent) {
    parent.addEventListener("abort", () => controller.abort(parent.reason), { once: true });
  }
  const timer: ReturnType<typeof setTimeout> = setTimeout(() => {
    controller.abort(new Error(`deadline exceeded after ${timeoutMs}ms`));
  }, timeoutMs);
  // unref is a Node extension absent in browser typings
  const maybeUnref = timer as unknown as { unref?: () => void };
  if (typeof maybeUnref.unref === "function") maybeUnref.unref();
  return {
    signal: controller.signal,
    cleanup() {
      clearTimeout(timer);
      controller.abort(new Error("deadline cancelled"));
    },
  };
}