diff --git a/src/index.ts b/src/index.ts index 6c0c8c9..1ccdb59 100644 --- a/src/index.ts +++ b/src/index.ts @@ -8,7 +8,7 @@ export { workers } from './worker-pool.ts'; export { transfer } from './transfer.ts'; export { assign } from './assign.ts'; export { roundRobin, leastBusy } from './balancers.ts'; -export type { Task, Balancer, Runner, WorkerHandle, WorkerOptions } from './runner.ts'; +export type { Task, RunResult, Balancer, Runner, WorkerHandle, WorkerOptions } from './runner.ts'; export { shared, int8, diff --git a/src/mo.ts b/src/mo.ts index 34d8cf1..1067295 100644 --- a/src/mo.ts +++ b/src/mo.ts @@ -18,13 +18,13 @@ export type Arg = T | Task; type TaskableArgs = { [K in keyof A]: Arg }; type Moroutine = { - (...args: A): Task>; - (...args: TaskableArgs): Task>; + (...args: A): Task, A> & PromiseLike>; + (...args: TaskableArgs): Task, A> & PromiseLike>; }; type AsyncIterableMoroutine = { - (...args: A): Task>; - (...args: TaskableArgs): Task>; + (...args: A): Task, A> & AsyncIterable; + (...args: TaskableArgs): Task, A> & AsyncIterable; }; type IsNever = [T] extends [never] ? true : false; diff --git a/src/runner.ts b/src/runner.ts index 4f41330..c2066cb 100644 --- a/src/runner.ts +++ b/src/runner.ts @@ -1,27 +1,28 @@ import type { Worker } from 'node:worker_threads'; import type { ChannelOptions } from './channel.ts'; -declare const __task__: unique symbol; +declare const resultBrand: unique symbol; +declare const argsBrand: unique symbol; -/** A dispatchable task. The type parameter determines the dispatch mode: - * - `Task` (non-iterable T) is a `PromiseLike` — await it for a single value. - * - `Task>` is an `AsyncIterable` — iterate it for a stream. - * - `Task` (no param) is the base shape shared by all tasks, useful for balancer signatures. */ -export type Task = { +/** An inert task descriptor. Carries the result type `T` and argument types `A` + * at the type level, but is not itself a `PromiseLike` or `AsyncIterable`. + * Dispatch via a {@link Runner} or `await` a live task returned by `mo()`. */ +export type Task = { readonly uid: number; readonly id: string; readonly args: unknown[]; worker?: WorkerHandle; -} & ([T] extends [never] - ? PromiseLike - : [T] extends [typeof __task__] - ? {} - : T extends AsyncIterable - ? AsyncIterable - : PromiseLike); + /** @internal Type brand for result type inference. Not present at runtime. */ + readonly [resultBrand]?: T; + /** @internal Type brand for argument type inference. Not present at runtime. */ + readonly [argsBrand]?: A; +}; + +/** Resolves the dispatch return type: `AsyncIterable` for streaming tasks, `Promise` otherwise. */ +export type RunResult = T extends AsyncIterable ? AsyncIterable : Promise; type TaskResults[]> = { - [K in keyof T]: T[K] extends PromiseLike ? R : never; + [K in keyof T]: T[K] extends Task ? R : never; }; /** A load balancing strategy for choosing which worker runs a task. */ @@ -44,10 +45,8 @@ export interface WorkerOptions { /** A handle to a specific worker in a pool. */ export interface WorkerHandle { - /** Dispatches a streaming task pinned to this worker. */ - exec(task: Task>, opts?: ChannelOptions): AsyncIterable; - /** Dispatches a task pinned to this worker. */ - exec(task: Task): Promise; + /** Dispatches a task pinned to this worker. Returns `AsyncIterable` for streaming tasks, `Promise` otherwise. */ + exec(task: Task, opts?: ChannelOptions): RunResult; /** The underlying worker thread. */ readonly thread: Worker; /** Number of currently in-flight tasks on this worker. */ @@ -61,12 +60,10 @@ export interface WorkerHandle { * @returns `Promise` for a single task, `Promise<[...results]>` for a batch, or `AsyncIterable` for a streaming task. */ export type Runner = { - /** Dispatches a streaming task and returns an async iterable of yielded values. */ - (task: Task>, opts?: ChannelOptions): AsyncIterable; - /** Dispatches a single task and returns its result. */ - (task: Task): Promise; /** Dispatches a batch of tasks in parallel and returns all results. */ []>(tasks: [...T]): Promise>; + /** Dispatches a task. Returns `AsyncIterable` for streaming tasks, `Promise` otherwise. */ + (task: Task, opts?: ChannelOptions): RunResult; /** AbortSignal that fires when the pool starts disposing. */ readonly signal: AbortSignal; /** Read-only array of worker handles, one per pool worker. */