/** * Committing one durable state transition of an application primitive, * atomically with the fleet event that describes it (WFT-84, WFT-85). * * A transition is a compare-and-swap plan: the conditions it was decided * against and the operations that carry it out. Without an event sink the plan * is a plain conditional batch. With one, the sink is asked to append the event * under the same conditions and operations, and the FIRST commit through a sink * also writes a single-use probe key that is read back afterwards: nothing else * ever writes that key, so a concurrent transition on the record between the * commit and the read cannot be mistaken for a sink that committed to a * different backend. * * @module core/application-primitive-commit */ import type { BatchOperation, ConditionalBatchCondition, Storage } from '../storage/interface.ts'; /** * What a primitive needs from a fleet event feed: append one event atomically * with the transition's conditions and operations. Structural on purpose, so * the core never imports the server's feed. */ export type ApplicationEventSink = { append(event: { readonly kind: string; readonly emittedAtMs: number; readonly payload: unknown; }, transaction: { readonly conditions: readonly ConditionalBatchCondition[]; readonly operations: readonly BatchOperation[]; }): Promise; }; /** One durable state transition, optionally paired with the fleet event that describes it. */ export type ApplicationCommitPlan = { readonly conditions: readonly ConditionalBatchCondition[]; readonly operations: readonly BatchOperation[]; readonly event: { readonly kind: string; readonly payload: unknown; } | null; readonly now: number; /** * Where the first commit through an event sink writes its verification * probe. Unique per plan, so a concurrent transition on the record can never * be mistaken for a sink that committed somewhere else. */ readonly sinkProbeKey: string; }; /** * Whether every compare-and-swap condition still matches durable state. * * Used to classify a failed event-sink append: if the caller's own conditions * still hold, the append failed for the feed's own reasons and the error must * propagate; if one moved, another actor won the race and the caller retries. */ export declare function conditionsStillHold(storage: Storage, conditions: readonly ConditionalBatchCondition[]): Promise; /** Byte-for-byte equality of two values, as `conditionalBatch` compares them. */ export declare function bytesEqual(left: Uint8Array, right: Uint8Array): boolean; /** * Commit one transition, atomically with its fleet event when a sink is * configured. * * Returns `false` when a compare-and-swap condition was lost, which means * another actor transitioned the record first and the caller should re-read and * re-decide. Any other failure throws. `subject` names the primitive in the * misconfiguration diagnostic ("application mailbox", "application outbox"). */ export declare function commitApplicationTransition(storage: Storage, events: ApplicationEventSink | undefined, plan: ApplicationCommitPlan, subject: string): Promise;