Files
suaveplan/openspec/specs/database/adapter/spec.md
2026-05-27 23:29:53 +01:00

3.3 KiB

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)

interface DatabaseAdapter extends DatabaseLike {
    connect(config?: Partial<DatabaseConfig>, opts?: DbOperationOptions): Promise<void>;
    disconnect(opts?: DbOperationOptions): Promise<void>;
    query<T>(sql: string, params?: readonly unknown[], opts?: DbOperationOptions): Promise<T[]>;
    execute(sql: string, params?: readonly unknown[], opts?: DbOperationOptions): Promise<{ rowsAffected: number }>;
    transaction<T>(fn: (tx: DatabaseLike) => Promise<T>, opts?: DbOperationOptions): Promise<T>;
    migrate(config?: MigrationConfig, opts?: DbOperationOptions): Promise<MigrationResult>;
    readonly isConnected: boolean;
}

Connection singletons

getSingleton(name, factory, config?, opts?, registry?): Promise<DatabaseAdapter>
closeSingleton(name, opts?, registry?): Promise<void>
hasSingleton(name, registry?): boolean
closeAllSingletons(opts?, registry?): Promise<void>

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<DbCode> · 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)

interface MigrationRunner {
    pendingMigrations(config?: MigrationConfig): Promise<MigrationStatus[]>;
    applyMigration(name: string, sql: string, opts?: DbOperationOptions): Promise<void>;
    run(config?: MigrationConfig, opts?: DbOperationOptions): Promise<MigrationResult>;
}

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