# @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` via `defineCodes("MIGRATION", [...])` — six codes, every one raised from a production call site. ## 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… ## 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.