import { type Operation, type Task, run, suspend } from "effection"; export type StartableTask = Promise & { start(): Promise; halt(): Promise; running: Task; }; export type StartedTask> = Awaited>; export type TaskableOperation = Operation & { task(): StartableTask; }; export function taskable>(operation: O): O & TaskableOperation { let target = operation as O & TaskableOperation; target.task = function taskableOperationTask() { let running!: Task; let resolveReady!: (value: T) => void; let rejectReady!: (reason?: unknown) => void; const ready = new Promise((resolve, reject) => { resolveReady = resolve; rejectReady = reject; }); running = run(function* () { try { resolveReady(yield* operation); yield* suspend(); } catch (error) { rejectReady(error); throw error; } }); return Object.assign(ready, { running, start() { return ready; }, halt() { return Promise.resolve(running.halt()); }, }); }; return target; }