/** * @module close-cycle * @category Internal * * Pure orchestration of the close-the-books flow: scan stream heads, * partition by reaction safety, guard with tombstones, optionally seed * restart state, run user archive callbacks, atomically truncate, and * update the cache. * * The Act orchestrator owns lifecycle (correlate gate, emit("closed")) and * the registry-derived inputs (reactive-event count, event→state map). All * sequential phase work between those state touches lives here. * * @internal */ import type { CloseResult, CloseTarget, Logger, State } from "../types/index.js"; import type { EsOps } from "./event-sourcing.js"; /** * Dependencies the close cycle needs from the Act orchestrator. Decoupled * from `Act` itself so the cycle can be exercised from tests in isolation. * * @internal */ export type CloseCycleDeps = { readonly reactive_events_size: number; readonly event_to_state: ReadonlyMap>; readonly load: EsOps["load"]; readonly tombstone: EsOps["tombstone"]; readonly logger: Logger; /** * Correlation id for the close transaction. Caller (`Act.close`) * computes this via the configured {@link Correlator}, so close * commits share the user's chosen id scheme instead of stamping a * UUID. */ readonly correlation: string; /** * Page size for the safety probe's `query_streams` pagination. * Defaults to {@link SAFETY_PROBE_PAGE_SIZE}; production callers omit * it, tests set a small value to exercise the multi-page path. */ readonly probe_page_size?: number; /** * Advance correlation to at least `until` (an event id) and return how * far it actually got (#1487). * * The safety probe asks each subscription whether it has unconsumed * work, which is only a fair question about events correlate has already * resolved. An event past the read cursor has raised no mark yet, so * every reader answers "caught up" about it — including a reader that * does not exist yet, because the subscription a dynamic resolver would * create is itself a product of correlating that event. * * Refusing to close is the wrong answer: the autoclose path fires *from* * the event that reaches the terminal state, so its own trigger is * routinely uncorrelated, and a retired stream gets no further commits to * retry with. So the cycle makes the precondition true instead — it * correlates the tail, then decides. Whatever remains above the cursor * afterwards is held back as pending. */ readonly catch_up_correlation: (until: number) => Promise; /** * Per-stream critical section (#1222). The windowed branch is * deliberately guard-free at the store level — a past cutoff makes the * boundary immutable, so a concurrent append can never race the prune. * But that assumes a *single* closer per stream. A manual * `app.close([{stream, before}])` runs `run_close_cycle` directly, * bypassing the `__autoclose__:X` lease that would otherwise exclude a * concurrent autoclose windowed close, so both closers can archive the * same prefix — a double S3 upload / double JSONL append. This runs the * given work under a process-local per-stream lock so the two closers * serialize; the second sees the already-pruned prefix and skips its * archive. Provided by the Act orchestrator (shared across `app.close` * and the drain's `on_close`); defaults to identity (no serialization) * when the cycle is exercised in isolation. */ readonly with_stream_lock?: (stream: string, work: () => Promise) => Promise; }; /** * Page size for the safety probe's keyset pagination over the * subscriptions table. Above `query_streams`'s default `limit` of 100 * to keep the round-trip count low while bounding per-page work. * * @internal */ export declare const SAFETY_PROBE_PAGE_SIZE = 1000; /** * Run the full close cycle for the given targets. Caller owns the * lifecycle event emission. * * Targets carrying a `before` cutoff take the **windowed** branch — a * pure prefix delete behind an existing snapshot (see * {@link run_windowed_closes}); the rest run the guarded * tombstone/restart pipeline below. * * @internal */ export declare function run_close_cycle(targets: CloseTarget[], deps: CloseCycleDeps): Promise; //# sourceMappingURL=close-cycle.d.ts.map