/** * Transaction semantics for embedded runtimes. * * FeltDB has one transaction contract. The authority-backed runtime satisfies * it by handing the staged transaction to the store's own atomic commit path; * the embedded runtimes satisfy it here. * * The semantics live in this module *once*. A runtime does not reimplement * them — it supplies only the thing that is genuinely runtime-specific: its * atomic durable commit boundary. * * ```text * db.transaction() * │ * staged operations * │ * ┌────────┴────────┐ * │ │ * authority embedded * (store's atomic (decidePlan here + * commit path) runtime commit) * │ │ * └────────┬────────┘ * │ * atomic durable * commit * ``` * * The decision step is pure: it reads current state and produces a plan. It * performs no I/O and mutates nothing, so a runtime can evaluate the whole * transaction before touching storage and then apply the plan in one atomic * step. That ordering is what makes partial application impossible. */ import type { AtomicTransactionRequest, AtomicTransactionResult, ConditionalConflictDetail } from './transaction.js'; /** One resolved mutation. An absent `value` means delete. */ export interface EmbeddedMutation { /** Storage key, `collection:id`. */ key: string; collection: string; id: string; value?: Record; } /** What a runtime must be able to tell the decision step. */ export interface EmbeddedTransactionState { /** Whether a record currently exists. */ exists(key: string): boolean; /** Whether this transaction id has already been committed. */ hasApplied(transactionId: string): boolean; /** * The record's current value, for evaluating preconditions. * * A runtime that cannot read committed state here must leave this undefined, * and a transaction carrying preconditions is then refused rather than * committed unconditionally. Silently ignoring a precondition is the exact * defect this primitive exists to remove. */ read?(key: string): Record | undefined; } export type EmbeddedPlan = { kind: 'duplicate'; } /** * `failure` is present exactly when the refusal is a precondition that did * not hold -- a lost race -- and absent when the transaction was malformed. * A caller distinguishes the two by its presence rather than by reading the * reason, which is what lets a conditional helper report one as a value and * raise the other. */ | { kind: 'reject'; reason: string; failure?: ConditionalConflictDetail; } | { kind: 'apply'; mutations: EmbeddedMutation[]; }; /** Storage key for a collection and id. */ export declare function recordKey(collection: string, id: string): string; /** * Decide what a transaction should do, without doing any of it. * * Returns `duplicate` when this transaction id was already committed (so * redelivery is a no-op), `reject` when the transaction cannot commit at all, * or `apply` with every mutation resolved. * * A rejection means nothing may be written. There is no partial plan. */ export declare function decideTransaction(request: AtomicTransactionRequest, state: EmbeddedTransactionState): EmbeddedPlan; /** Shape a runtime returns after committing. */ export declare function embeddedResult(request: AtomicTransactionRequest, duplicate: boolean, stateBefore: number, stateAfter: number): AtomicTransactionResult; /** * Raised when a transaction is refused. Nothing was written. * * A refusal that is a lost race carries `feltdbCode` and `feltdbConflict`, * spelled exactly as the authority's HTTP refusal carries them. That is what * makes a conflict against an embedded runtime the same event as a conflict * against a managed one, rather than a message a caller has to parse. */ export declare class EmbeddedTransactionRejected extends Error { readonly feltdbCode?: 'PRECONDITION_FAILED'; readonly feltdbConflict?: ConditionalConflictDetail; constructor(reason: string, failure?: ConditionalConflictDetail); } //# sourceMappingURL=embedded-transaction.d.ts.map