import { SafeFnWithUtils } from './safe'; import { abortableSymbol, isAbortable } from './abortable-guard'; export { isAbortable }; export type AbortableStatus = "running" | "success" | "error" | "paused" | "waiting" | "aborted"; /** * Send function type. * - When TYield is void: `() => void` (checkpoint/nudge pattern) * - When TYield is object: `(key: K, value: TYield[K]) => void` */ export type AbortableSend = void extends TYield ? () => void : (key: TKey, value: TYield[TKey]) => void; /** * Take function type. * Returns a Promise that resolves when the event arrives. * - When TYield is void: `() => Promise` (checkpoint pattern) * - When TYield is object: `(key: K) => Promise` */ export type AbortableTake = void extends TYield ? () => Promise : (key: TKey) => Promise; /** * Join function type for coordinating multiple abortable results. * When the parent abortable is aborted, all joined results are also aborted. */ export type AbortableJoin = { /** Join a single abortable result */ (result: AbortableResult): Promise; /** Join multiple abortable results (like Promise.all with abort propagation) */ []>(results: T): Promise<{ -readonly [K in keyof T]: Awaited; }>; }; /** * Result returned when invoking an abortable function. * Extends Promise for async consumption while providing control methods. * * @example * ```ts * const result = myAbortable(args); * * // Promise-like usage * const value = await result; * * // Control methods * result.pause(); * result.resume(); * result.abort(); * * // Status checks * result.running(); // boolean * result.status(); // "running" | "success" | ... * * // Event sending * result.send("eventKey", eventValue); * ``` */ export type AbortableResult = Promise & { /** Send an event to the abortable */ send: AbortableSend; /** Check if abortable has failed */ failed(): boolean; /** Check if abortable has completed (success, error, or aborted) */ completed(): boolean; /** Check if abortable is currently running */ running(): boolean; /** Check if abortable succeeded */ succeeded(): boolean; /** Check if abortable is paused */ paused(): boolean; /** Check if abortable is waiting for async operation or event */ waiting(): boolean; /** Check if abortable was aborted */ aborted(): boolean; /** Get current status */ status(): AbortableStatus; /** Get result if succeeded, undefined otherwise */ result(): Awaited | undefined; /** Get error if failed, undefined otherwise */ error(): Error | undefined; /** * Pause execution at current await point. * @returns false if already paused or completed */ pause(): boolean; /** * Resume execution from paused state. * @returns false if not paused */ resume(): boolean; /** * Abort execution. * Does NOT affect parent signal - only this abortable's internal signal. * @returns false if already aborted or completed */ abort(): boolean; }; /** * Context passed to abortable function handlers. * * @example * ```ts * const myFn = abortable<[string], Result, { confirm: boolean }>( * async ({ signal, safe, take, aborted, abort }, id) => { * const data = await safe(fetchData, id); * * if (aborted()) return null; * * const confirmed = await take("confirm"); * if (!confirmed) { * abort(); * return null; * } * * return processData(data); * } * ); * ``` */ export interface AbortableContext { /** * AbortSignal for this abortable instance. * This is the abortable's OWN signal, not the parent's. * Use this for fetch, timers, etc. */ signal: AbortSignal; /** * Safe execution utility. * Wraps async operations to handle abort gracefully. * * Includes utilities: `.all()`, `.race()`, `.any()`, `.settled()`, `.callback()` */ safe: SafeFnWithUtils; /** * Wait for an external event. * Returns a Promise that resolves when `send(key, value)` is called. * * @example * ```ts * // With typed events * const confirmed = await take("confirm"); * * // Checkpoint pattern (TYield = void) * await take(); // Waits for send() * ``` */ take: AbortableTake; /** * Check if this abortable has been aborted. */ aborted(): boolean; /** * Abort this abortable from inside. * Does NOT affect parent signal. * @returns false if already aborted */ abort(): boolean; /** * Check for pause point. * Call this between async operations to allow pause/resume. * Throws AbortableAbortedError if aborted. * * @example * ```ts * const myFn = abortable(async (ctx) => { * const data = await fetchData(); * await ctx.checkpoint(); // Allow pause here * const processed = await process(data); * await ctx.checkpoint(); // Allow pause here * return processed; * }); * ``` */ checkpoint(): Promise; /** * Join one or more abortable results. * When this abortable is aborted, all joined results are also aborted. * * @example * ```ts * // Single result * const user = await ctx.join(fetchUser(id)); * * // Multiple results (like Promise.all) * const [user, posts, comments] = await ctx.join([ * fetchUser(id), * fetchPosts(id), * fetchComments(id), * ]); * ``` */ join: AbortableJoin; } /** * Handler function signature for abortable. */ export type AbortableFn = (ctx: AbortableContext, ...args: TArgs) => Promise; /** * Wrapper type for use() method. */ export type AbortableWrapper = (next: AbortableFn) => AbortableFn; /** * Identity wrapper that preserves all input types including TYield. */ export type IdentityWrapper = (next: AbortableFn) => AbortableFn; /** * An abortable function with full lifecycle control. * * - Direct call: `fn(...args)` - creates new AbortController * - With signal: `fn.withSignal(signal, ...args)` - links to parent signal * - Chainable: `fn.use(wrapper)` - returns new Abortable with wrapper applied * * Signal relationship: * - Abortable creates its own internal AbortController * - When `withSignal(parentSignal)` is called: * - If parent aborts → this abortable aborts * - If this abortable aborts → parent NOT affected */ export interface Abortable { /** Call without signal (creates new AbortController) */ (...args: TArgs): AbortableResult; /** * Call with parent signal. * Parent abort → this aborts. This abort → parent unaffected. */ withSignal(signal: AbortSignal | undefined, ...args: TArgs): AbortableResult; /** * Apply a wrapper and return a new Abortable. * * @example * ```ts * const fetchUserWithRetry = fetchUser.use(withRetry(3)); * ``` */ use(wrapper: AbortableWrapper): Abortable; /** * Type assertion for return type. */ as(): Abortable; /** Type brand for discrimination */ readonly [abortableSymbol]: true; } /** * Error thrown when abortable is aborted. */ export declare class AbortableAbortedError extends Error { readonly name = "AbortableAbortedError"; constructor(message?: string); } /** * Create an abortable function with full lifecycle control. * * Features: * - Pause/Resume execution * - Abort with cleanup * - External event injection (take/send) * - Status tracking * - Parent signal linkage (parent abort → this aborts, not vice versa) * * @example * ```ts * // Simple abortable * const fetchUser = abortable(async ({ signal, safe }, id: string) => { * const res = await fetch(`/api/users/${id}`, { signal }); * return res.json(); * }); * * // With events * const checkout = abortable<[Cart], Receipt, { confirm: boolean }>( * async ({ signal, safe, take }, cart) => { * const validated = await safe(validateCart, cart); * * const confirmed = await take("confirm"); * if (!confirmed) throw new Error("Cancelled"); * * return await safe(processPayment, validated); * } * ); * * // Usage * const result = checkout(cart); * result.send("confirm", true); * const receipt = await result; * * // With pause/resume * result.pause(); * result.resume(); * * // With abort * result.abort(); * ``` */ export declare function abortable(fn: AbortableFn): Abortable; //# sourceMappingURL=abortable.d.ts.map