export interface WorkflowLaunchPolicyInput { wait?: boolean; background?: boolean; } /** * Resolve the caller's launch preference to one positive runtime mode. * `background` remains only as the published inverse compatibility alias. */ export function resolveWorkflowLaunchMode( input: WorkflowLaunchPolicyInput, canDeliverLater: boolean, ) { if ( input.wait !== undefined && input.background !== undefined && input.wait === input.background ) { throw new Error( "wait and background conflict: background is the deprecated inverse of wait; remove background and provide only wait", ); } const wait = input.wait ?? (input.background !== undefined ? !input.background : !canDeliverLater); if (!wait && !canDeliverLater) { throw new Error( "This host cannot deliver a workflow result later; use wait: true", ); } return wait ? "inline" : "detached"; } /** * Arbitrate an inline wait against caller cancellation without transferring * ownership of the workflow run to the wait signal. */ export async function waitForWorkflowCompletion( completion: Promise, signal?: AbortSignal, ): Promise<"terminal" | "aborted"> { if (!signal) { await completion.catch(() => {}); return "terminal"; } if (signal.aborted) return "aborted"; let onAbort: (() => void) | undefined; const aborted = new Promise<"aborted">((resolve) => { onAbort = () => resolve("aborted"); signal.addEventListener("abort", onAbort, { once: true }); }); const terminal = completion.then( () => "terminal" as const, () => "terminal" as const, ); try { return await Promise.race([terminal, aborted]); } finally { if (onAbort) signal.removeEventListener("abort", onAbort); } }