/** * Storage access for the application delivery outbox (WFT-85): bound keys, * record loads that keep the exact bytes for compare-and-swap, index * maintenance, and the transition plan the shared commit executes. * * @module core/outbox-storage */ import type { BatchOperation, ConditionalBatchCondition, Storage } from '../storage/interface.ts'; import { type ApplicationCommitPlan, type ApplicationEventSink } from './application-primitive-commit.ts'; import type { LoadedDeliveryRecord } from './outbox-contract.ts'; import { type ApplicationDeliveryIdempotencyRecord, type ApplicationDeliveryRecord, type OutboxRecord } from './outbox-types.ts'; /** Every key builder for one `(namespace, ownerId)` outbox, bound once. */ export type OutboxKeys = Readonly<{ header: string; sinkProbe: (nonce: string) => string; deliveryPrefix: string; delivery: (deliveryId: string) => string; duePrefix: string; due: (availableAt: number, deliveryId: string) => string; bySequencePrefix: string; bySequence: (sequence: number) => string; idempotency: (key: string) => string; terminalPrefix: string; terminal: (terminalAt: number, deliveryId: string) => string; }>; /** Bind every outbox storage key to one namespace and owner. */ export declare function createOutboxKeys(namespace: string, ownerId: string): OutboxKeys; /** The empty header an outbox starts from. */ export declare function emptyOutboxRecord(namespace: string, ownerId: string): OutboxRecord; /** A header read together with the exact bytes it decoded from. */ export type LoadedOutboxRecord = { readonly record: OutboxRecord; /** `null` when the outbox has never been written. */ readonly bytes: Uint8Array | null; }; /** * Read the per-outbox header, treating an absent key as a fresh outbox. * * @throws {PersistedDataCorruptError} When the stored header is malformed. */ export declare function loadOutboxHeader(storage: Storage, keys: OutboxKeys, namespace: string, ownerId: string): Promise; /** * Read one delivery record with the exact bytes it decoded from. * * @throws {PersistedDataCorruptError} When the stored record is malformed. */ export declare function loadDelivery(storage: Storage, keys: OutboxKeys, deliveryId: string): Promise; /** * Read the idempotency binding for a retry key. * * @throws {PersistedDataCorruptError} When the stored binding is malformed. */ export declare function loadDeliveryIdempotencyBinding(storage: Storage, keys: OutboxKeys, idempotencyKey: string): Promise<{ readonly record: ApplicationDeliveryIdempotencyRecord; readonly bytes: Uint8Array; } | null>; /** One entry of the time-keyed due index. */ export type DueEntry = { readonly key: string; readonly bytes: Uint8Array; readonly deliveryId: string; }; /** * Read the earliest entries of the due index. * * The index is keyed by `availableAt`, so the first entry is the earliest * delivery, due or not. The caller decides whether it is claimable now. * * @throws {PersistedDataCorruptError} When an index entry is malformed, or names a delivery other than the one its key names. */ export declare function loadDueHead(storage: Storage, keys: OutboxKeys, limit: number): Promise; /** * Put/delete operations that keep the due and terminal indexes consistent * with a record's new state. * * Unlike the mailbox's FIFO index, the due key embeds `availableAt`, which a * reschedule changes: the entry under the previous instant is deleted and one * under the new instant is written. A terminal record moving between terminal * dispositions (an operator dead-letter of a parked delivery) likewise moves * its retention entry to its new `terminalAt`. */ export declare function indexOperationsFor(keys: OutboxKeys, previous: ApplicationDeliveryRecord | null, next: ApplicationDeliveryRecord): BatchOperation[]; /** * Commit one outbox transition, atomically with its fleet event when a sink is * configured. The outbox's binding of the shared commit. */ export declare function commitOutboxTransition(storage: Storage, events: ApplicationEventSink | undefined, plan: ApplicationCommitPlan): Promise; /** * Persist a delivery record plus its index maintenance as one plan. * * `expectedBytes` must be the exact bytes the record was read as, never a * re-encoding of the decoded value. */ export declare function planDeliveryTransition(keys: OutboxKeys, options: { readonly previous: ApplicationDeliveryRecord | null; readonly expectedBytes: Uint8Array | null; readonly next: ApplicationDeliveryRecord; readonly event: { readonly kind: string; readonly payload: unknown; } | null; readonly now: number; readonly extraConditions?: readonly ConditionalBatchCondition[] | undefined; readonly extraOperations?: readonly BatchOperation[] | undefined; }): ApplicationCommitPlan; /** The put operation that advances the outbox header. */ export declare function headerOperation(keys: OutboxKeys, record: OutboxRecord): BatchOperation;