db/schema_ready.ts

Readiness probe core: live-DB schema-drift detection.

/health is a dumb liveness probe (no DB). /ready is the deploy gate — it introspects the live database's column set and compares it against a committed expected column map (what a fresh full migration-chain bootstrap produces). A live DB missing an expected column is exactly the failure mode that silently broke login when the auth schema gained account.deleted_at via an in-place base-DDL edit instead of an appended migration: the deployed code required a column an older bootstrapped DB never got, and a SELECT * + JS deleted_at === null filter rejected every account. The /ready route (http/common_routes.ts) turns that drift into a loud 503 so a deploy poll rolls the release back instead of promoting code that can't authenticate anyone. The discipline that prevents the drift is the frozen append-only migration chain (auth/migrations.ts); this probe is the runtime net for a lapse.

The check is intentionally column-presence only — not type / constraint / index parity. Column names are DDL-deterministic and engine-portable, so a map generated against PGlite at gen-time compares exactly against a live Postgres at runtime; finer-grained parity would false-positive across the two engines, and a false positive here means a rolled-back deploy — an outage you caused. Full structural parity stays the dev-time cross-backend schema-snapshot suite's job (testing/schema_introspect.ts). In-place *type* changes (a column kept by name, retyped) are out of scope — they rely on the query-time column-named failures instead.

This module is pure DB introspection + comparison: no HTTP, no filesystem, no fixture-path knowledge. The route factory and the committed-fixture loader live in http/common_routes.ts; the gen-time fixture-regeneration helper lives in testing/schema_ready_fixture.ts.

view source

Declarations
#

7 declarations

check_schema_drift
#

db/schema_ready.ts view source

(db: Db, expected: ExpectedSchema): Promise<SchemaDriftResult> import {check_schema_drift} from '@fuzdev/fuz_app/db/schema_ready.js';

Compare the live DB's columns against expected. Reports tables and columns the running code expects that the live DB lacks — the drift that breaks queries. Extra live tables / columns are ignored: forward-compatible, and a newer-than-fixture DB shouldn't fail readiness.

db

live database to introspect

type Db

expected

the committed column map a fresh bootstrap produces

returns

Promise<SchemaDriftResult>

ExpectedSchema
#

db/schema_ready.ts view source

ExpectedSchema import type {ExpectedSchema} from '@fuzdev/fuz_app/db/schema_ready.js';

Expected schema: table name → sorted column names, from a fresh bootstrap.

[key: string]

type readonly string[]

format_schema_drift
#

db/schema_ready.ts view source

(drift: SchemaDriftResult): string import {format_schema_drift} from '@fuzdev/fuz_app/db/schema_ready.js';

Render a drift result as a one-issue-per-line operator string.

drift

returns

string

MissingColumns
#

db/schema_ready.ts view source

MissingColumns import type {MissingColumns} from '@fuzdev/fuz_app/db/schema_ready.js';

Columns the live DB is missing for a table the expected schema declares.

table

type string

columns

type Array<string>

query_public_columns
#

db/schema_ready.ts view source

(db: Db): Promise<Record<string, string[]>> import {query_public_columns} from '@fuzdev/fuz_app/db/schema_ready.js';

Introspect every column in the public schema, grouped by relation. Shared by the runtime /ready check and the fixture-generating helper so both observe the exact same shape. information_schema.columns spans tables and views; for the drift check that's harmless (a never-bootstrapped schema has neither, and extra relations are ignored — see check_schema_drift).

Unlike query_schema_snapshot (which excludes the schema_version migration tracker as framework bookkeeping), this keeps schema_version — a never-migrated DB then correctly fails readiness instead of passing on an empty expectation.

db

type Db

returns

Promise<Record<string, string[]>>

relation name → sorted column names

READY_ERROR
#

db/schema_ready.ts view source

{ readonly schema_drift: "schema_drift"; readonly db_unreachable: "db_unreachable"; } import {READY_ERROR} from '@fuzdev/fuz_app/db/schema_ready.js';

Error codes a readiness check returns at 503 (conforms to {error: string}).

SchemaDriftResult
#

db/schema_ready.ts view source

SchemaDriftResult import type {SchemaDriftResult} from '@fuzdev/fuz_app/db/schema_ready.js';

Outcome of a schema-drift check.

ok

type boolean

missing_tables

Expected tables absent from the live DB.

type Array<string>

missing_columns

Per-table columns the expected schema declares that the live DB lacks.

type Array<MissingColumns>

Depends on
#

Imported by
#