interface TimeoutOptions { promise: Promise; timeoutMs: number; label: string; } export function withTimeout(promise: Promise, timeoutMs: number, message: string): Promise; export function withTimeout(options: TimeoutOptions): Promise; export function withTimeout( promiseOrOptions: Promise | TimeoutOptions, timeoutMs?: number, message?: string, ): Promise { const options = typeof timeoutMs === "number" ? { promise: promiseOrOptions as Promise, timeoutMs, message } : resolveTimeoutOptions(promiseOrOptions as TimeoutOptions); if (typeof options.timeoutMs !== "number" || !options.message) { return Promise.reject(new Error("Timeout duration and message are required")); } let timeout: ReturnType | undefined; const timeoutPromise = new Promise((_, reject) => { timeout = setTimeout(() => reject(new Error(options.message)), options.timeoutMs); }); return Promise.race([options.promise, timeoutPromise]).finally(() => { if (timeout) { clearTimeout(timeout); } }); } function resolveTimeoutOptions(options: TimeoutOptions): { promise: Promise; timeoutMs: number; message: string; } { return { promise: options.promise, timeoutMs: options.timeoutMs, message: `Timed out after ${options.timeoutMs}ms (${options.label})`, }; }