/** * `fetch` with a hard client-side timeout. Node's global fetch otherwise * inherits no timeout — a stalled connection will hang the caller forever, * which has wedged the main loop more than once (incidents on 2026-05-20). * * On timeout, throws an error whose `.name === 'TimeoutError'`. Callers that * want to differentiate timeout-vs-other failures can branch on that. */ export async function fetchWithTimeout( input: string | URL | Request, init: RequestInit & { timeoutMs?: number } = {}, ): Promise { const { timeoutMs = 30_000, ...rest } = init; return fetch(input, { ...rest, signal: AbortSignal.timeout(timeoutMs), }); }