/** * Pure conditional-transition functions for the durable application delivery * outbox (WFT-85) — one function per legal edge of the outbox state machine. * * These functions are storage-agnostic. They take the currently decoded * {@link ApplicationDeliveryRecord} and proposed inputs and return either the * next record to persist or a stable rejection reason. They never read or write * storage and never see encoded bytes; the caller that commits a transition * keeps the raw bytes it read as the compare-and-swap `expectedValue`. * * The legal edges are: * * ```text * (none) --enqueue--> queued * queued | retry-scheduled --claim(due)--> claimed * claimed --begin--> attempting * claimed | attempting | cancel-req --heartbeat--> (same state, visibility extended) * attempting --settle(acknowledged)--> acknowledged * attempting --settle(rejected)--> rejected * attempting --settle(retryable)--> retry-scheduled | dead-lettered * attempting --settle(unknown)--> unknown-outcome | dead-lettered | retry-scheduled * cancellation-requested --settle(ack)--> acknowledged (the effect happened) * cancellation-requested --settle(other)--> cancelled | unknown-outcome | dead-lettered * queued | retry-scheduled | claimed --cancel--> cancelled * attempting --cancel--> cancellation-requested * claimed --expire--> retry-scheduled | dead-lettered (recovery) * attempting | cancel-req --expire--> unknown-outcome | dead-lettered | retry-scheduled * unknown-outcome | dead-lettered | rejected --retry--> queued (operator) * unknown-outcome --deadLetter--> dead-lettered (operator) * ``` * * @module core/outbox-transitions */ import type { JSONValue } from './json.ts'; import { type OutboxTransition, type RetryPolicy } from './outbox-transition-helpers.ts'; import type { ApplicationDeliveryAttempting, ApplicationDeliveryCancelling, ApplicationDeliveryClaimed, ApplicationDeliveryLeasedRecord, ApplicationDeliveryQueued, ApplicationDeliveryRecord, ApplicationDeliveryRetryScheduled, ApplicationDeliveryTerminalRecord } from './outbox-types.ts'; import type { ValidatedDeliveryInput, ValidatedOutcome } from './outbox-validation.ts'; export { isTerminalDeliveryRecord, nonTerminalDeliveryRecord, } from './outbox-transition-helpers.ts'; export type { OutboxTransition, OutboxTransitionRejection } from './outbox-transition-helpers.ts'; /** Build the record for a freshly enqueued delivery. */ export declare function createEnqueuedDeliveryRecord(input: ValidatedDeliveryInput, context: { readonly namespace: string; readonly ownerId: string; readonly deliveryId: string; readonly sequence: number; readonly now: number; }): ApplicationDeliveryQueued; /** * Lease a waiting delivery to one attempt. * * The attempt deadline is fixed here, at `now + attemptTimeoutMs`, and nothing * later moves it. Visibility starts at the lesser of the renewal window and * that deadline. */ export declare function claimWaitingDelivery(record: ApplicationDeliveryRecord, options: { readonly now: number; readonly attemptToken: string; }): OutboxTransition; /** * Durably mark that the current attempt is about to call the transport. * * This is the edge that makes recovery honest: a lease that expires in * `claimed` provably sent nothing and is safe to retry, while one that expires * in `attempting` may have, and follows the unknown-outcome policy. */ export declare function beginDeliveryAttempt(record: ApplicationDeliveryRecord, options: { readonly attemptToken: string; readonly now: number; }): OutboxTransition; /** * Record liveness for the current attempt and extend its visibility, clamped * to the fixed attempt deadline. `transportActivity` is evidence of transport * progress — bytes written, a request id — and is never fencing. */ export declare function heartbeatDeliveryAttempt(record: ApplicationDeliveryRecord, options: { readonly attemptToken: string; readonly now: number; readonly transportActivity?: JSONValue | undefined; }): OutboxTransition; /** * Apply the unknown-outcome policy to a leased delivery whose transport result * is lost. Shared by settlement (adapter reported `unknown`) and recovery (the * lease expired in `attempting`). * * A delivery whose cancellation was requested is never retried: `park` and * `retry-with-idempotency` both park it, because a retry would send work the * caller asked to stop. `abandonedAttemptToken` is set by recovery only. */ export declare function applyUnknownOutcomePolicy(leased: ApplicationDeliveryLeasedRecord, options: { readonly now: number; readonly failure: { readonly reason: 'unknown-outcome'; readonly message?: string | undefined; }; readonly abandonedAttemptToken?: string | undefined; } & RetryPolicy): ApplicationDeliveryRetryScheduled | ApplicationDeliveryTerminalRecord; /** * Settle the current attempt on what the transport reported. * * Only an `attempting` (or cancellation-requested) delivery can settle: a * `claimed` one never called the transport, so it has no outcome to record. * An acknowledgement always wins, even after cancellation was requested — the * effect happened and the receipt must say so. Any other outcome on a * cancelling delivery honours the cancellation: `cancelled` when nothing was * confirmed, the unknown-outcome policy when the result was lost. */ export declare function settleDeliveryAttempt(record: ApplicationDeliveryRecord, options: { readonly attemptToken: string; readonly now: number; readonly outcome: ValidatedOutcome; } & RetryPolicy): OutboxTransition; /** * Record a durable cancellation request. * * A waiting or merely claimed delivery cancels at once: nothing has been sent. * (The claimant's `attempting` commit then loses its compare-and-swap and it * re-reads a terminal record.) An attempting delivery keeps its lease and moves * to `cancellation-requested`, so only the current attempt can settle it. * Requesting cancellation twice rejects with `not-leased` against a record * already in `cancellation-requested`, which the caller reports as the same * outcome. */ export declare function requestDeliveryCancellation(record: ApplicationDeliveryRecord, options: { readonly now: number; readonly reason?: string | undefined; }): OutboxTransition; /** * Operator retry: return a parked, dead-lettered, or rejected delivery to the * due index with at least one more attempt to spend. * * A budget the delivery never used up — a permanent rejection on its first * attempt of five, say — is kept, so the reopened delivery may retry on its * own up to `maxAttempts`; a spent budget is raised to `attempt + 1`, so the * delivery can be claimed exactly once more, and a second operator retry * grants one more again. The record's provenance (`attempt`, `retryCount`) is * preserved. */ export declare function retryDeliveryByOperator(record: ApplicationDeliveryRecord, options: { readonly now: number; }): OutboxTransition; /** Operator dead-letter: close a parked `unknown-outcome` delivery for good. */ export declare function deadLetterDeliveryByOperator(record: ApplicationDeliveryRecord, options: { readonly now: number; readonly reason?: string | undefined; }): OutboxTransition;