/** * The primitives every outbox transition shares (WFT-85): the * rejection vocabulary, the transition result shape, non-terminal narrowing, * identity-field carry-over, and retry scheduling. * * Extracted so `outbox-transitions.ts` and * `outbox-transitions-recovery.ts` can both use them without * importing each other. * * @module core/outbox-transition-helpers */ import { computeRetryBackoffMs } from './application-primitive-timing.ts'; import type { ApplicationDeliveryFailure, ApplicationDeliveryLeasedRecord, ApplicationDeliveryRecord, ApplicationDeliveryRetryScheduled, ApplicationDeliveryTerminalRecord, ApplicationDeliveryWaitingRecord } from './outbox-types.ts'; export { computeRetryBackoffMs }; /** * Why a proposed transition is illegal. Stable and low-cardinality, so callers * can map each reason onto a discriminated result without string matching. */ export type OutboxTransitionRejection = 'stale-attempt' | 'not-leased' | 'not-waiting' | 'not-due' | 'not-attempting' | 'deadline-exceeded' | 'already-terminal' | 'cancellation-requested' | 'not-applicable'; /** The outcome of a proposed transition: the record to persist, or why the edge is illegal. */ export type OutboxTransition = { readonly ok: true; readonly next: TNext; } | { readonly ok: false; readonly reason: OutboxTransitionRejection; }; export declare function rejectedTransition(reason: OutboxTransitionRejection): OutboxTransition; export declare function succeededTransition(next: TNext): OutboxTransition; /** Narrow a record to a terminal disposition. */ export declare function isTerminalDeliveryRecord(record: ApplicationDeliveryRecord): record is ApplicationDeliveryTerminalRecord; /** Narrow a record to the five non-terminal states, or `null`. */ export declare function nonTerminalDeliveryRecord(record: ApplicationDeliveryRecord): ApplicationDeliveryWaitingRecord | ApplicationDeliveryLeasedRecord | null; /** Strip the state-specific fields so a transition rebuilds a record from identity alone. */ export declare function applicationDeliveryIdentityFields(record: ApplicationDeliveryRecord): { readonly recordVersion: 1; readonly namespace: string; readonly ownerId: string; readonly deliveryId: string; readonly sequence: number; readonly idempotencyKey: string | undefined; readonly destinationRef: string; readonly credentialRef: string | undefined; readonly kind: string; readonly payload: import("./mailbox-types.ts").ApplicationCommandPayload; readonly payloadDigest: string; readonly payloadMediaType: string | undefined; readonly payloadSchema: string | undefined; readonly causation: Readonly<{ correlationId?: string | undefined; causationId?: string | undefined; traceparent?: string | undefined; }> | undefined; readonly externalIdempotencyKey: string | undefined; readonly unknownOutcomePolicy: import("./outbox-types.ts").ApplicationDeliveryUnknownOutcomePolicy; readonly enqueuedAt: number; readonly maxAttempts: number; readonly visibilityTimeoutMs: number; readonly attemptTimeoutMs: number; readonly generation: number; readonly attempt: number; readonly retryCount: number; readonly firstClaimedAt: number | undefined; readonly availableAt: number; }; /** Backoff policy shared by every reschedule. */ export type RetryPolicy = { readonly retryBackoffMs: number; readonly maxRetryBackoffMs: number; }; /** * Reschedule a leased delivery for another attempt, or dead-letter it when the * attempt budget is spent. * * The delay is the outbox's own backoff or the transport's suggestion, * whichever is longer: a transport asking for more time is honoured, one * asking for less is not allowed to defeat the configured backoff. */ export declare function rescheduleOrDeadLetter(leased: ApplicationDeliveryLeasedRecord, options: { readonly now: number; readonly failure: ApplicationDeliveryFailure; readonly retryAfterMs?: number | undefined; } & RetryPolicy): ApplicationDeliveryRetryScheduled | ApplicationDeliveryTerminalRecord;