/** * Vault-level schema-fence controller. * * Owns the open-time generation snapshot, the pending-cutover registry, * and the cutover orchestration. 3a: single-client (the caller is the * migrator). 3b: a cooperative ack-barrier — after `draining`, the * migrator waits for the active client set (registry heartbeats) to ack * the draining generation before transforming. No leader election. * * The transport is the injected {@link CoordinationProvider} port: the * default {@link StoreCoordinationProvider} maps it onto today's * `_meta/schema-fence` store ops, so behavior is byte-for-byte the same; * `by-tabs` / `by-peer` swap in real-time push transports. */ import { type FenceState } from './fence.js'; import type { TransformFn } from './types.js'; import { type CoordinationProvider } from '../../port/by/index.js'; /** Runs one collection's transform; supplied by the Vault (binds to a Collection). */ export type RunTransform = (collection: string, transform: TransformFn) => Promise; export declare class SchemaFenceController { #private; constructor(opts: { coordination: CoordinationProvider; vault: string; onFlush: () => Promise; /** Stable per-instance id; the migrator excludes itself from the barrier. */ clientId?: string; /** * Accepted for wiring symmetry with {@link FenceWatcher}, but the migrator * never reports its own presence (it excludes itself from the barrier), so * it carries no session of its own. */ sessionId?: string; now?: () => number; staleMs?: number; quiesceTimeoutMs?: number; emit?: (e: { currentSchemaVersion: number; fenceState: FenceState; }) => void; }); /** Capture the generation snapshot at vault-open. */ init(): Promise; /** Record a per-collection pending cutover (from a registration `cutover` decision). */ registerPendingCutover(collection: string, transform: TransformFn): void; /** Write-path gate. Throws when behind, fenced, or this collection is cutover-pending. */ assertWritable(collection: string): Promise; /** * Admin trigger. Drain → wait for the active set to quiesce (or time out) * → migrate each pending transform → bump → complete → normal. The * migrator excludes itself from the barrier (it drained synchronously * inside {@link runDrainBarrier}). `onPoll` (tests) advances other clients * between barrier checks; production falls back to a short real delay. */ runCutover(run: RunTransform, opts?: { onPoll?: () => Promise; }): Promise<{ migrated: number; }>; /** Recover a stuck drain: reset fenceState to normal at the current version (no bump). */ abort(): Promise; }