/** * Waiting durably for something this run cannot produce itself. * * ```ts * const approval = yield* suspendFor({ * request: { kind: "approval", release: "1.4" }, * responseSchema: { type: "object", properties: { approved: { type: "boolean" } } }, * }); * ``` * * The document says what it is waiting for and what shape an answer takes. What * happens next is not an error and not a value: the run stops. Its executor lock * is released, its status becomes `suspended`, and a later execution reaches * this same wait rather than starting the procedure again. * * ## Three steps, and each means only what it says * * **Refusal first.** A request that cannot be retained, or a schema that is not * an object, is refused before anything is published — a durable event nothing * can act on would be worse than no wait at all. * * **Publication next.** One ordinary durable Yield retains the suspension ID, * the request and the response schema. It crosses the same secret gate as every * other event and receives the same Workspace-root association. A successful * Yield means the request was retained. It does not mean the wait ended, and it * does not authorize anything. * * **Admission last, and it is the request at this position.** The controller is * reached through a stable contextual name, which is how a component's own * loaded copy of this package finds the controller the running binary installed. * That route is composition rather than admission: middleware may refuse it, but * no value it returns is an answer and none of them makes this operation * continue. What authorizes the wait is the retained request at the exact * position this operation just published from. * * ## Why replay reaches the same wait * * The suspension ID is derived from the run and the durable coroutine position * — not from a name, a counter the caller keeps, or anything the caller could * supply. A resumed execution replaying the same procedure arrives at the same * position, derives the same ID, restores the published request instead of * appending a second one, and reports the same wait. Two different waits in one * procedure sit at different positions and cannot receive each other's answer. */ import type { Operation } from "effection"; import { type Json } from "@executablemd/core"; import type { DurablePosition } from "@executablemd/durable-streams"; import { type WorkflowSuspensionRequest } from "./api.js"; /** The effect type one durable wait's request is retained under. */ export declare const SUSPENSION_REQUEST = "suspension_request"; /** * The opaque name one wait has, in this run, at this position. * * A digest rather than the three values joined, because the parts are a run * identifier a caller chose and a coroutine identifier with its own separators; * joined, two different triples could spell one string. It is opaque on * purpose: #300 will correlate an answer to it, and a correlation key that * revealed the position it came from would invite guessing a neighbouring wait. */ export declare function suspensionId(runId: string, position: DurablePosition): string; /** * Wait durably for a value this run cannot produce. * * Returns the value that ended the wait. With no delivered answer it does not * return: the execution settles `suspended` around it. */ export declare function suspendFor(request: WorkflowSuspensionRequest): Operation;