/** * The receive inbox (T12344; journal spec §3.1, §3.2). * * Every received transaction is staged in `_sync_inbox` in stream order, * keyed by `(stream, seq, txn_idx)`, so a re-pull stages nothing twice. The * applier ({@link stagedTxns} → apply → {@link markTxns}) moves each through * its statuses: * * - `staged`: received, not yet tried; * - `pending`: waits for something it needs (a never-seen row or reference, * or a pending transaction it depends on); retried on every pass; * - `held-skew`: its HLC is beyond the skew bound; retried, FIFO per replica; * - `applied`, `conflict`, `void`: tried; `conflict` and `void` carry their * records in `_sync_conflict` and are revivable; * - `refused-schema`: written by a newer schema or format, or naming a table * or column this store lacks; retried by every apply call * ({@link reopenRefusedSchema}), so it applies once the schema has what it * names (T13719), never skipped. * * A split transaction (`part: [i, n]`) is returned only once all n parts are * staged, as one transaction whose ops are the parts' ops in order (Q18). * * @module store/sync/inbox * @task T12344 */ import type { DatabaseSync } from 'node:sqlite'; import { LedgerTxn } from '@cleocode/contracts/ledger'; /** A transaction's apply status in the inbox. */ export type InboxStatus = 'staged' | 'pending' | 'applied' | 'void' | 'conflict' | 'held-skew' | 'refused-schema'; /** The statuses the applier tries again. */ export declare const OPEN_INBOX_STATUSES: readonly InboxStatus[]; /** * The statuses of a transaction not applied yet: the open ones and * `refused-schema`. A journal checkpoint lists them as pending and carries * their rows in its bundle (journal spec §2.11 §6; T12999). */ export declare const UNRESOLVED_INBOX_STATUSES: readonly InboxStatus[]; /** One staged row's position in the stream. */ export interface InboxKey { readonly stream: string; readonly seq: number; readonly txnIdx: number; } /** One verified segment's transactions, as the receiver stages them. */ export interface InboxSegment { /** The segment's stream sequence number. */ readonly seq: number; readonly replicaId: string; /** The origin replica's gap-free segment number. */ readonly replicaSeq: number; readonly deviceId: string; /** The segment's `schemaVersion` (§2.9). */ readonly schemaVersion: number; /** The decoded transactions, in segment order (secrets still inner-sealed). */ readonly txns: readonly LedgerTxn[]; } /** One transaction the applier may try, its parts joined. */ export interface StagedTxn { /** Position of its first part. */ readonly key: InboxKey; /** Every part's position, in part order (one entry when not split). */ readonly parts: readonly InboxKey[]; readonly replicaId: string; readonly replicaSeq: number; readonly deviceId: string; readonly schemaVersion: number; /** The status it was staged under: `staged`, `pending` or `held-skew`. */ readonly status: InboxStatus; /** The transaction, ops of every part in order, `hlc` the largest. */ readonly txn: LedgerTxn; } /** * Stage a verified segment's transactions. Rows already staged (same * stream, seq, index) are left alone, so a re-pull is idempotent. * * Exactly-once beyond the inbox's own rows is the receiver's: it stages only * segments past its persisted `_sync_cursor` and advances the cursor in the * same transaction (§3.1 step 3), so a segment whose applied rows were * pruned after a verified checkpoint is never staged again. Counter deltas * rely on it. * * @param db - The store (the journal schema applied); opens its own * transaction unless the caller holds one. * @param stream - The stream id. * @param segment - The segment and its decoded transactions. * @param nowIso - The staging time. * @returns How many transactions were newly staged. */ export declare function stageTxns(db: DatabaseSync, stream: string, segment: InboxSegment, nowIso: string): number; /** * The transactions the applier may try, in `(seq, txn_idx)` order: every * `staged`, `pending` and `held-skew` row, a split transaction once all of * its parts are staged. * * @param db - The store. * @param stream - The stream id. * @param limit - At most this many transactions; 0 for all. * @returns The transactions, parts joined. */ export declare function stagedTxns(db: DatabaseSync, stream: string, limit?: number): StagedTxn[]; /** * Re-open a stream's `refused-schema` transactions for one apply call * (T13719). Each is planned again against the live schema: it applies once * the store has the table or column it names (an upgrade, or a lazily * migrated table brought current), and is refused again otherwise. * shortcut: a permanently refused row is re-planned, and counted in the * report's `refusedSchema`, on every apply call; gate the re-open on a schema * fingerprint if refused rows ever grow past a handful. * * @param db - The store, outside a transaction. * @param stream - The stream id. * @returns How many rows were re-opened. */ export declare function reopenRefusedSchema(db: DatabaseSync, stream: string): number; /** * Set the status of a transaction's rows (every part of it). * * @param db - The store, inside the applier's transaction (or its own). * @param keys - The transaction's parts. * @param status - The new status. * @param opts - The apply frame, a reason, and the time (`applied_at` for a tried status). */ export declare function markTxns(db: DatabaseSync, keys: readonly InboxKey[], status: InboxStatus, opts: { readonly frame?: string | null; readonly reason?: string | null; readonly nowIso: string; }): void; /** * Inbox rows per status for a stream (doctor and tests). * * @param db - The store. * @param stream - The stream id. * @returns Status → row count; absent statuses omitted. */ export declare function inboxCounts(db: DatabaseSync, stream: string): Partial>; //# sourceMappingURL=inbox.d.ts.map