/**
 * Identity-tracked database migration runner.
 *
 * Migrations are named `{name, up}` objects in ordered arrays, grouped by
 * namespace. A `schema_version` table records one row per applied migration —
 * `(namespace, name, sequence, applied_at)` — and the runner verifies the
 * applied list is a name-prefix of the code's migration array at boot.
 *
 * **Schema is not stabilized yet — append-only is NOT the rule.** While
 * fuz_app is pre-stable, migration bodies, names, and positions can change
 * freely between versions; consumers upgrading across a schema change are
 * expected to drop and re-bootstrap their dev/test databases (production
 * deployments are not yet a supported use case). Once the schema is
 * declared stable a hard append-only-after-publish rule will apply and the
 * cliff will be called out in that release's notes; until then, body edits
 * to a published migration slip past the runner (no content hashing) by
 * design — they're the recommended way to evolve the schema.
 *
 * **Chain-level transactions**: All pending migrations in a namespace run in
 * a single transaction. Any failure rolls back every migration in that run —
 * no partial-state recovery. This rules out non-transactional DDL (e.g.,
 * `CREATE INDEX CONCURRENTLY`); run those out of band.
 *
 * **Chain idempotency, not migration idempotency**: the chain-tx wraps every
 * migration replayed in a single boot, so an individual migration may
 * temporarily produce intermediate state that a later migration reverses
 * (e.g. v0's `ROLE_GRANT_INDEXES` recreates an index that v1 drops; chain-tx
 * hides this from observers). What matters is that the *committed end state*
 * matches; the in-tx steps may not be individually idempotent against an
 * arbitrary mid-chain target.
 *
 * **Forward-only**: No down-migrations. Schema changes are additive.
 *
 * **Advisory locking**: Per-namespace `pg_advisory_lock` reduces contention
 * in multi-instance deployments — best-effort, not load-bearing. The locks
 * are session-scoped, but `Db.query` runs against a pool that may check out
 * a different backend per call, so two concurrent boots can both "hold"
 * the lock on different sessions. The real serialization comes from chain-
 * tx atomicity + the `(namespace, name)` PK on `schema_version`: the
 * loser's INSERT hits a PK violation, the chain-tx rolls back, and the
 * next boot reads the committed state and proceeds cleanly. Environments
 * without `pg_advisory_lock` (some PGlite versions) silently fall through.
 *
 * @module
 */
import type { Db } from './db.js';
/**
 * A single migration: a name + an `up` function applied inside a transaction.
 *
 * Throw from `up` to roll back the entire chain.
 */
export interface Migration {
    name: string;
    up: (db: Db) => Promise<void>;
}
/**
 * A named group of ordered migrations.
 *
 * Array index = position in the chain. Pre-stable: bodies, names, and
 * positions can change between versions (consumers re-bootstrap on upgrade).
 */
export interface MigrationNamespace {
    namespace: string;
    migrations: Array<Migration>;
}
/** Result of running migrations for a single namespace. */
export interface MigrationResult {
    namespace: string;
    /** Migrations applied in this run, in sequence-ascending (execution) order. */
    applied_names: Array<string>;
}
/**
 * Tagged error vocabulary for `run_migrations` and `baseline`.
 *
 * Callers branch on `.kind` rather than matching error messages — message
 * text is for operators, not control flow.
 */
export type MigrationErrorKind = 'binary-older-than-db' | 'name-divergence-at-N' | 'old-tracker-shape' | 'migration-failed' | 'baseline-name-not-in-code' | 'baseline-name-out-of-order' | 'baseline-namespace-already-populated';
/** Structured context passed alongside a `MigrationError`. */
export interface MigrationErrorContext {
    namespace?: string;
    at_index?: number;
    unknown_names?: ReadonlyArray<string>;
    cause?: unknown;
}
/**
 * Tagged error thrown by `run_migrations` and `baseline`.
 *
 * Branch on `.kind`; the message carries an operator-facing remediation hint.
 */
export declare class MigrationError extends Error {
    readonly kind: MigrationErrorKind;
    readonly namespace?: string;
    readonly at_index?: number;
    readonly unknown_names?: ReadonlyArray<string>;
    constructor(kind: MigrationErrorKind, message: string, context?: MigrationErrorContext);
}
/**
 * Run pending migrations for each namespace.
 *
 * For each namespace: acquires an advisory lock, reads applied rows ordered
 * by `sequence`, length-checks (binary-older-than-db short-circuits), name-
 * prefix-verifies, then runs the pending tail in a single chain transaction.
 * Each migration's row is INSERTed with `sequence = max(sequence) + 1` for
 * the namespace.
 *
 * **Length check before name verify** is load-bearing: a binary-older case
 * with a rename in the overlap would otherwise fire `name-divergence-at-N`
 * first and the operator would chase a phantom source-revert before
 * discovering the binary is the real problem.
 *
 * **Atomicity**: any failure rolls back every migration that ran in that
 * invocation. Namespaces are independent: a later namespace's failure does
 * not roll back an earlier namespace that already committed.
 *
 * **Concurrency**: per-namespace advisory locks reduce contention in
 * multi-instance deployments but are best-effort on pool drivers (see
 * the module docstring's "Advisory locking" notes). Correctness on concurrent boots
 * falls out of chain-tx atomicity + the `(namespace, name)` PK — the
 * loser's INSERT triggers PK violation and rollback; subsequent boots
 * see the committed state.
 *
 * @param db - the database instance
 * @param namespaces - migration namespaces, processed in the order passed
 * @returns one result per namespace where work happened (already-up-to-date
 *   namespaces are omitted)
 * @mutates schema_version - inserts one row per applied migration
 * @throws MigrationError with `kind` of `binary-older-than-db`,
 *   `name-divergence-at-N`, `old-tracker-shape`, or `migration-failed`
 */
export declare const run_migrations: (db: Db, namespaces: Array<MigrationNamespace>) => Promise<Array<MigrationResult>>;
/**
 * Insert tracker rows for the named migrations of a namespace **without
 * executing them**.
 *
 * Used to promote an existing schema (e.g. produced by a pre-0.42 build,
 * preserved through a tracker-shape upgrade) into the new identity tracker.
 * `baseline()` trusts the operator-supplied list — it does not verify that
 * the schema actually matches what the named migrations would have produced.
 * Pair with a schema-assertion script post-baseline before re-enabling traffic.
 *
 * Contract:
 * - Probes for the pre-0.42 tracker shape; throws `old-tracker-shape` if
 *   found (DDL with `IF NOT EXISTS` would otherwise no-op against the old
 *   table and the INSERT would fail with a confusing column-not-found).
 * - Creates the new-shape `schema_version` table if missing — cutover
 *   scripts that just dropped the old-shape table can call `baseline()`
 *   directly with no separate DDL step.
 * - Acquires the same per-namespace advisory lock as `run_migrations` (with
 *   the same try/catch fallback for environments lacking `pg_advisory_lock`).
 * - Refuses if any tracker rows already exist *for this namespace* — lets
 *   multi-call baseline scripts resume after partial failure (completed
 *   namespaces guard themselves while remaining ones still run).
 * - Verifies the supplied names are a strict prefix of the namespace's
 *   current migrations array — a name not in the array, or out of order,
 *   errors before any INSERT.
 * - Writes sequences `0..N-1` in one transaction.
 *
 * @param db - the database instance
 * @param ns - the namespace whose migrations are being baselined
 * @param names - prefix of `ns.migrations[].name` to record as already-applied
 * @mutates schema_version - inserts tracker rows for `names` without running
 *   the corresponding migration bodies
 * @throws MigrationError with `kind` of `old-tracker-shape`,
 *   `baseline-name-not-in-code`, `baseline-name-out-of-order`, or
 *   `baseline-namespace-already-populated`
 */
export declare const baseline: (db: Db, ns: MigrationNamespace, names: ReadonlyArray<string>) => Promise<void>;
//# sourceMappingURL=migrate.d.ts.map