import type { StartOrSignalSignal } from '../../types.ts'; import { type WorkflowHandle } from '../handles.ts'; import type { EngineInternals } from '../internals.ts'; import { type LifecycleCallbacks } from './shared.ts'; /** * Callbacks `startOrSignal` needs beyond the lifecycle set: a way to deliver a * signal to an already-running workflow through the full engine signal path * (interceptors, events, parked-run wakeups). Supplied by the engine so the * "signal an existing non-terminal run" branch reuses `engine.signal` with the * same `signalId` the create batch would have used. */ export type StartOrSignalCallbacks = LifecycleCallbacks & { signalExistingWorkflow: (workflowId: string, signalName: string, payload: unknown, signalId: string) => Promise; }; /** The value stored at `KEYS.startIdempotency(key)`: the workflow the key created. */ export type StartIdempotencyMapping = { workflowId: string; }; /** Resolve an existing workflow id for an idempotency key, if one was created. */ export declare function resolveIdempotencyKeyWorkflowId(internals: EngineInternals, idempotencyKey: string): Promise; /** * Resolve a key-mapped workflow id to a handle id, asserting its record still * exists. The `start-idem:` mapping is permanent — it survives BOTH terminal * cleanup AND purge/retention (those reclaim the workflow record, never the * `start-idem:` keyspace) — so a present mapping whose record is gone means the * key is spent: a fresh create would fail the still-present mapping CAS and strand * the caller. Surface {@link IdempotencyKeyPurgedError} instead of handing back a * handle to a vanished run. Shared by the synchronous mapping hit and the * post-race winner lookup so both reject a purged key identically. */ export declare function resolveExistingRunOrThrowPurged(internals: EngineInternals, workflowId: string): Promise; /** * Resolve a caller-`id` create-race loss without conflating an in-memory * reservation with a durable record. A loser collides on the winner's * `pendingStarts` reservation (start.ts) BEFORE the winner commits, so the bare * collision proves nothing about whether a run will exist. * * Read the winner's record FIRST: this catches a winner that has already committed * but is still non-terminal before it can complete (a fast workflow consumes its * create-batch signal and finishes the moment it is driven — reading immediately * resolves it instead of racing it to a terminal-conflict). Only when the record is * absent do we wait for the reservation to clear and read once more to discriminate: * * - **record present** — the winner committed: signal it (or conflict if terminal). * - **record absent after the reservation clears** — the winner aborted before * committing (storage failure, oversized payload, throwing start interceptor): no * run exists, so return `undefined` and let the caller retry its own create. * * Either terminal-conflict branch is checked against {@link wasSignalAcceptedByWinner} * before throwing: a fast workflow can reach a terminal status between this * loser's reads, entirely independent of how much slower or faster the winner's * OWN commit path happens to be — so a same-`signalId` convergent caller (the * documented "concurrent absent-target callers" contract) must not depend on * catching the winner mid-flight to avoid a spurious conflict. */ export declare function resolveCallerIdWinnerOrRetry(internals: EngineInternals, winnerId: string, signalSpec: StartOrSignalSignal, signalId: string, callbacks: StartOrSignalCallbacks, allowTerminalRestart?: boolean): Promise; /** * For a workflow that already exists: signal it if non-terminal, conflict if * terminal. Returns the handle on a successful signal, or `undefined` when the * workflow record is not present (so the caller falls through to create). */ export declare function signalOrConflictExistingWorkflow(internals: EngineInternals, workflowId: string, signalSpec: StartOrSignalSignal, signalId: string, callbacks: StartOrSignalCallbacks): Promise; /** * Signal a KEYED race winner, bounded-retrying when its record is not yet readable. * The keyed winner commits its record atomically with the `start-idem:` mapping, so * the record is guaranteed to exist — but the loser may read before the commit * settles, so a short delay between reads lets it land. (Caller-`id` winners can * abort before committing and are handled by `resolveCallerIdWinnerOrRetry`, not * here.) * * After {@link WINNER_RESOLUTION_MAX_ATTEMPTS} reads with no record, the record is * absent for a committed-with-mapping winner only because it was purged: re-read * the mapping, and if it still resolves to this exact `winnerId` the key is spent — * throw {@link IdempotencyKeyPurgedError}. A mapping that now resolves to a * DIFFERENT id (or vanished) cannot prove this winner was purged, so it falls * through to the invariant throw rather than mislabelling external keyspace * mutation as a spent key. */ export declare function resolveWinnerWithSignal(internals: EngineInternals, winnerId: string, signalSpec: StartOrSignalSignal, signalId: string, callbacks: StartOrSignalCallbacks, idempotencyKey: string): Promise; /** * Read the winning workflow id from the idempotency mapping after a lost CAS. The * mapping must exist once any caller's create commits; its absence means the * `start-idem:` keyspace was mutated externally. */ export declare function requireWinnerId(internals: EngineInternals, idempotencyKey: string): Promise;