Files
2026-05-27 23:29:53 +01:00

127 lines
6.8 KiB
Markdown

# @suaveplan/api-migrations - Database-agnostic migration runner for the Suaveplan API — narrow MigrationAdapter surface with per-dialect DDL bootstrap, real SHA-256 checksum validation against on-disk files, AbortSignal cancellation between migrations, OTel counter + histogram per apply, MigrationError with six MIGRATION.* codes. Ships three adapters (surrealql, postgres, sqlite); MySQL slot reserved in the dialect union. Server-tier — Bun + Node.
Database-agnostic migration runner for the Suaveplan API. Ships a narrow
`MigrationAdapter` surface (six methods), three shipped adapters (`surrealql`,
`postgres`, `sqlite`), a `MigrationRunner` class that ACTUALLY validates
SHA-256 checksums against on-disk migration files before each apply phase,
`AbortSignal` propagation between migrations, OTel counter + histogram per
apply attempt, and `MigrationError` with six namespaced `MIGRATION.*` codes.
Splits and rewrites the upstream `@suaveplan/api`'s `database/migration-runner.ts`
(331 LOC). fixes baked in:
1. Narrow `MigrationAdapter` surface — the runner depends on six methods, not
the whole `DatabaseLike`. Each adapter ships its own `ensureTable` DDL.
2. Checksums are ACTUALLY validated — drift halts apply with
`MigrationError(MIGRATION.CHECKSUM_DRIFT)` and `{name, expected, actual}`
context. Upstream's `validateChecksums: true` was a no-op; this one isn't.
3. `AbortSignal` propagation — `runFromDirectory(dir, { signal })` honors
cancellation between migrations.
4. OTel counter `migrations.applied_total{dialect, outcome}` and histogram
`migrations.apply_duration_ms{name}` emit on every attempt regardless of
success.
5. `MigrationError extends SuaveplanError<MigrationErrorCode>` via
`defineCodes("MIGRATION", [...])` — six codes, every one raised from a
production call site.
<!-- begin:auto-docs -->
## Documentation tree
- [postgres](./src/adapters/postgres/postgres.md) — Implements the `MigrationAdapter` contract against any Postgres-wire-protocol server (Postgres ≥ 12, CockroachDB, Yugab…
- [postgres](./src/adapters/postgres/postgres.md) — Implements the `MigrationAdapter` contract against any Postgres-wire-protocol server (Postgres ≥ 12, CockroachDB, Yugab…
- [sqlite](./src/adapters/sqlite/sqlite.md) — Implements the `MigrationAdapter` contract for SQLite-family engines. The factory `createSqliteAdapter(client)` accepts…
- [sqlite](./src/adapters/sqlite/sqlite.md) — Implements the `MigrationAdapter` contract for SQLite-family engines. The factory `createSqliteAdapter(client)` accepts…
- [surrealql](./src/adapters/surrealql/surrealql.md) — Implements the `MigrationAdapter` contract for SurrealDB. The factory `createSurrealqlAdapter(client)` wraps any Surrea…
- [surrealql](./src/adapters/surrealql/surrealql.md) — Implements the `MigrationAdapter` contract for SurrealDB. The factory `createSurrealqlAdapter(client)` wraps any Surrea…
- [error-codes](./src/error-codes/error-codes.md) — Defines the stable, namespaced error registry for `@suaveplan/api-migrations`. Every public failure path in the package…
- [error-codes](./src/error-codes/error-codes.md) — Defines the stable, namespaced error registry for `@suaveplan/api-migrations`. Every public failure path in the package…
- [otel](./src/otel/instruments.md) — Lazily builds and caches the two OpenTelemetry instruments that `@suaveplan/api-migrations` emits on every migration ap…
- [instruments](./src/otel/instruments.md) — Lazily builds and caches the two OpenTelemetry instruments that `@suaveplan/api-migrations` emits on every migration ap…
- [runner](./src/runner/runner.md) — The `MigrationRunner` is the dialect-agnostic core of the package. It reads migration files from a directory, sorts the…
- [runner](./src/runner/runner.md) — The `MigrationRunner` is the dialect-agnostic core of the package. It reads migration files from a directory, sorts the…
- [types](./src/types/migration.md) — Declares every shared type that the runner, adapters, and consumers exchange across the `@suaveplan/api-migrations` pac…
- [migration](./src/types/migration.md) — Declares every shared type that the runner, adapters, and consumers exchange across the `@suaveplan/api-migrations` pac…
<!-- end:auto-docs -->
## Install
```bash
bun add @suaveplan/api-migrations
```
## Quick start
```ts
import { MigrationRunner } from "@suaveplan/api-migrations/runner/runner";
import { createSqliteAdapter } from "@suaveplan/api-migrations/adapters/sqlite/sqlite";
const runner = new MigrationRunner(createSqliteAdapter(client), {
fileExtension: ".sql",
});
const controller = new AbortController();
const results = await runner.runFromDirectory("./db/migrations", {
signal: controller.signal,
});
for (const r of results) {
if (!r.success) throw new Error(`Apply halted at ${r.name}: ${r.error}`);
}
```
## Checksum-drift recovery
When `validateChecksums: true` (default) and a previously applied
migration's on-disk content has changed, the runner halts before applying
any pending migration:
```ts
try {
await runner.runFromDirectory("./db/migrations");
} catch (err) {
if (
err instanceof MigrationError &&
err.code === MIGRATION_CODES.CHECKSUM_DRIFT
) {
// err.context = { name, expected, actual }
// Resolution:
// 1. Restore the migration file to its applied form (`git checkout`
// the file at the commit it was applied from).
// 2. If the change is intentional, ship a NEW migration file with
// the additive change. Never rewrite history.
}
throw err;
}
```
## Modules
- [`runner/runner`](./src/runner/runner.md) — `MigrationRunner` class +
`createMigrationRunner` factory + `computeChecksum`.
- [`adapters/surrealql/surrealql`](./src/adapters/surrealql/surrealql.md) —
SurrealDB adapter with SurrealQL DDL.
- [`adapters/postgres/postgres`](./src/adapters/postgres/postgres.md) —
PostgreSQL adapter with standard SQL DDL.
- [`adapters/sqlite/sqlite`](./src/adapters/sqlite/sqlite.md) — SQLite
adapter compatible with `sqlite-wasm` / `bun:sqlite` / `better-sqlite3`.
- [`otel/instruments`](./src/otel/instruments.md) — counter + histogram.
- [`error-codes/error-codes`](./src/error-codes/error-codes.md) — six
`MIGRATION.*` codes + `MigrationError`.
- [`types/migration`](./src/types/migration.md) — public type surface.
## Subpath exports
Every feature module is independently importable for maximum
tree-shakeability:
```ts
import { MigrationRunner } from "@suaveplan/api-migrations/runner/runner";
import { createPostgresAdapter } from "@suaveplan/api-migrations/adapters/postgres/postgres";
import { MIGRATION_CODES } from "@suaveplan/api-migrations/error-codes/error-codes";
```
## Tier
Server (Bun + Node). Uses `node:crypto` for SHA-256 and `node:fs/promises`
for directory scanning and file reads.