# Spec: database/adapter **Package**: `@suaveplan/db` **Version**: `0.1.0` **Status**: Shipped 2026-04-23 ## Purpose Universal relational database adapter interface for the genesis monorepo. Provides the `DatabaseAdapter` contract (extending `DatabaseLike` from `@suaveplan/types`), injectable connection singletons, structured `DB.*` error codes, Zod-validated config types, a `MigrationRunner` interface, and full OpenTelemetry instrumentation. ## Public API ### `DatabaseAdapter` (interface, extends `DatabaseLike`) ```ts interface DatabaseAdapter extends DatabaseLike { connect(config?: Partial, opts?: DbOperationOptions): Promise; disconnect(opts?: DbOperationOptions): Promise; query(sql: string, params?: readonly unknown[], opts?: DbOperationOptions): Promise; execute(sql: string, params?: readonly unknown[], opts?: DbOperationOptions): Promise<{ rowsAffected: number }>; transaction(fn: (tx: DatabaseLike) => Promise, opts?: DbOperationOptions): Promise; migrate(config?: MigrationConfig, opts?: DbOperationOptions): Promise; readonly isConnected: boolean; } ``` ### Connection singletons ```ts getSingleton(name, factory, config?, opts?, registry?): Promise closeSingleton(name, opts?, registry?): Promise hasSingleton(name, registry?): boolean closeAllSingletons(opts?, registry?): Promise ``` `registry` defaults to module-level `Map`; pass an explicit `Map` for test isolation. ### Error codes (`DB_CODES`) | Code | When thrown | |---|---| | `DB.CONNECTION_FAILED` | `connect()` fails | | `DB.NOT_CONNECTED` | Operation before `connect()` | | `DB.QUERY_FAILED` | `query()` or `execute()` throws | | `DB.TRANSACTION_FAILED` | `transaction()` throws | | `DB.MIGRATION_FAILED` | Migration fails | `DatabaseError extends SuaveplanError` · `MigrationError extends DatabaseError` (adds `migration: string`) ### Config types - `DatabaseConfig` — `filename?`, `url?`, `readonlyMode?` (default `false`) - `MigrationConfig` — `migrationsFolder?` (default `"./drizzle"`), `migrationsTable?` (default `"__drizzle_migrations"`) - `MigrationResult` — `{ applied: number, migrations: string[] }` - `MigrationStatus` — `{ name: string, applied: boolean, appliedAt?: Date }` Zod schemas: `DatabaseConfigSchema`, `MigrationConfigSchema` (both `.strict()`). ### `MigrationRunner` (interface) ```ts interface MigrationRunner { pendingMigrations(config?: MigrationConfig): Promise; applyMigration(name: string, sql: string, opts?: DbOperationOptions): Promise; run(config?: MigrationConfig, opts?: DbOperationOptions): Promise; } ``` ### OTel instrumentation - `withDbSpan(name, fn, attrs?)` — wraps async fn in a named OTel span - `recordQueryDuration(ms, operation)` — histogram `db.query.duration_ms` - `recordConnectionDelta(±1, dbName)` — UpDownCounter `db.connection.count` - `recordTransaction(outcome)` — counter `db.transaction.count` - `recordMigration(name)` — counter `db.migration.count` - `resetInstruments()` — clears lazy singleton (test isolation) ## Quality - 100% Istanbul coverage (statements/branches/functions/lines) - 63 unit tests + property-based tests (fast-check) - Benchmarks (mitata): `src/__benchmarks__/singleton.bench.ts`, `otel.bench.ts`