···4455type TaskResults<T extends Task<any>[]> = { [K in keyof T]: T[K] extends Task<infer R> ? R : never };
6677+/** Options for configuring a worker pool. */
88+export interface WorkerOptions {
99+ /** Maximum time in ms to wait for in-flight tasks during async dispose. If exceeded, workers are force-terminated. */
1010+ shutdownTimeout?: number;
1111+}
1212+713/**
814 * A callable that dispatches tasks to a worker pool. Disposable via `using` or `[Symbol.dispose]()`.
915 *
···1723 <T extends Task<any>[]>(tasks: [...T]): Promise<TaskResults<T>>;
1824 /** Dispatches a streaming task and returns an async iterable of yielded values. */
1925 <T>(task: StreamTask<T>, opts?: ChannelOptions): AsyncIterable<T>;
2020- /** Terminates all workers in the pool. */
2626+ /** AbortSignal that fires when the pool starts disposing. */
2727+ readonly signal: AbortSignal;
2828+ /** Terminates all workers immediately. */
2129 [Symbol.dispose](): void;
3030+ /** Aborts signal, waits for in-flight tasks to settle, then terminates workers. */
3131+ [Symbol.asyncDispose](): Promise<void>;
2232};
+14
src/worker-pool.ts
···2525 let next = 0;
2626 let disposed = false;
27272828+ const abortController = new AbortController();
2929+2830 function dispatch<T>(task: Task<T>): Promise<T> {
2931 if (disposed) return Promise.reject(new Error('Worker pool is disposed'));
3032 const worker = pool[next % pool.length];
···4648 return dispatch(taskOrTasks);
4749 },
4850 {
5151+ get signal() {
5252+ return abortController.signal;
5353+ },
4954 [Symbol.dispose]() {
5555+ disposed = true;
5656+ abortController.abort();
5757+ for (const worker of pool) {
5858+ worker.terminate();
5959+ }
6060+ pool.length = 0;
6161+ },
6262+ async [Symbol.asyncDispose]() {
6363+ abortController.abort();
5064 disposed = true;
5165 for (const worker of pool) {
5266 worker.terminate();