profileShare

rasmusjy / roundtable

Read-only snapshot

No repository description.

main default branch 181 files Expires Sep 13, 2026, 9:06 AM
timeout.ts 1,183 bytes
1 /**
2 * Race a promise-returning function against a wall-clock deadline, wiring an
3 * AbortSignal through so the underlying LLM call is actually cancelled (not just
4 * abandoned). Also forwards a parent signal so a client disconnect / debate
5 * abort propagates down into in-flight model calls.
6 */
7 export class TimeoutError extends Error {
8 readonly timeoutMs: number;
9 constructor(ms: number) {
10 super(`Operation timed out after ${ms}ms`);
11 this.name = 'TimeoutError';
12 this.timeoutMs = ms;
13 }
14 }
15
16 export async function withTimeout<T>(
17 fn: (signal: AbortSignal) => Promise<T>,
18 ms: number,
19 parentSignal?: AbortSignal,
20 ): Promise<T> {
21 const controller = new AbortController();
22 const abortFromParent = () => controller.abort(parentSignal?.reason);
23
24 if (parentSignal) {
25 if (parentSignal.aborted) controller.abort(parentSignal.reason);
26 else parentSignal.addEventListener('abort', abortFromParent, { once: true });
27 }
28
29 const timer = setTimeout(() => controller.abort(new TimeoutError(ms)), ms);
30
31 try {
32 return await fn(controller.signal);
33 } finally {
34 clearTimeout(timer);
35 parentSignal?.removeEventListener('abort', abortFromParent);
36 }
37 }
38