import { NodePgDatabase } from "drizzle-orm/node-postgres"; import type { CollectionConfig } from "@rebasepro/types"; /** * The auth schema version this runtime expects to find in the database. * * Bump this whenever a migration in `ensureAuthTablesExist` makes the schema * unreadable by the runtime that came before it — that is, whenever a *previous* * version's auth queries would break against the migrated shape. Additive * changes (a new nullable column nobody older references) do not need a bump. * * History. Note that 1 is a label for an era, not a value any database holds: * stamping did not exist then, so an era-1 database reads as unstamped * (`null`), and 2 is the first version ever actually written. The numbering * starts at 2 only because two schema eras already existed when it was * introduced; it could just as well have started at 1. It is not worth * renumbering now — deployed databases already carry 2, and lowering the * constant would make them look newer than the runtime and refuse the boot. * * 1 — Device-session refresh tokens. A row *was* a session, identified by * `unique_device_session UNIQUE (uid, user_agent, ip_address)`, and * `createToken` upserted with `ON CONFLICT (uid, user_agent, ip_address)`. * 2 — Session-scoped, rotation-safe refresh tokens: `session_id`, `revoked`, * `rotated_at`, `session_started_at`, and `unique_device_session` * dropped because two live tokens of one session share all three columns. * * The 1 → 2 migration is why this file exists. Dropping the constraint is * one-way: a version-1 runtime deployed afterwards boots perfectly, logs * `✅ Auth tables ready` (its `CREATE TABLE IF NOT EXISTS` never revisits the * existing table, so it cannot re-add the constraint), answers `/health` with * 200 — and then fails every single login and refresh with SQLSTATE 42P10, * because its `ON CONFLICT` names a constraint that no longer exists. A silent * total auth outage behind a green health check. The stamp below turns that * into a boot refusal. */ export declare const AUTH_SCHEMA_VERSION = 2; /** * Thrown when the database was migrated by a runtime newer than this one. * * Distinct class rather than a bare `Error` because `ensureAuthTablesExist` * wraps its migrations in a catch that deliberately swallows failures and * continues — every other problem there is better survived than crashed on. * This one is not, so the catch rethrows on this type specifically. */ export declare class AuthSchemaVersionError extends Error { readonly databaseVersion: number; readonly runtimeVersion: number; constructor(databaseVersion: number, runtimeVersion: number); } /** * The schema the auth tables live in, derived exactly as `ensureAuthTablesExist` * derives it. Shared so the two cannot drift: a stamp written to one schema and * read from another would read as "never stamped" forever. */ export declare function resolveAuthSchema(collection?: CollectionConfig): string; /** * Read the stamped version, or `null` when the database has never been stamped. * * `null` is not an error and must not be treated as one: every database * provisioned before this file existed is unstamped, and so is every fresh one. * Uses `to_regclass` rather than selecting straight from the table so a missing * schema or table is a `null` rather than a thrown 42P01. */ export declare function readAuthSchemaVersion(db: NodePgDatabase, authSchema: string): Promise; /** * Refuse to run against a database a newer runtime has already migrated. * * Deliberately one-directional. A database *older* than this runtime is the * normal upgrade path — the migrations in `ensureAuthTablesExist` are about to * bring it forward, so it is not an error. Only the reverse is unrecoverable. */ export declare function assertAuthSchemaCompatible(db: NodePgDatabase, authSchema: string): Promise; /** * Record that this runtime's migrations have been applied. * * Called at the end of `ensureAuthTablesExist`, so a boot that failed partway * through leaves the older stamp in place and the next boot migrates again. */ export declare function stampAuthSchemaVersion(db: NodePgDatabase, authSchema: string): Promise; /** What {@link probeAuthSchema} found. */ export interface AuthSchemaProbeResult { /** False when this runtime cannot be trusted to serve auth against this database. */ healthy: boolean; /** The stamped version, or `null` on a database that predates stamping. */ databaseVersion: number | null; /** {@link AUTH_SCHEMA_VERSION}. */ runtimeVersion: number; /** Human-readable descriptions of each mismatch found. Empty when healthy. */ problems: string[]; } /** * Check that the auth schema is one this runtime can actually write to. * * Two independent checks, because either alone has a blind spot: * * - The **stamp** catches a runtime older than the database. It is the precise * signal, but it is blind on every database provisioned before stamping * existed — which today is all of them. * - The **structure** catches a database older than the runtime, and works on * unstamped databases. It is what makes this useful immediately rather than * one upgrade cycle from now. * * Never throws: a probe that fails to run reports unhealthy with the reason, so * a broken check surfaces as a degraded health response rather than a 500 from * the health endpoint itself. */ export declare function probeAuthSchema(db: NodePgDatabase, authSchema: string): Promise;