import { sortMigrations } from "../../planner/migration-planner/migration-planner.js"; import type { ExtendedMigrationStatus, MigrationHealthCheck, MigrationHistoryEntry, MigrationSummary, StatusConfig, } from "../../types/migration.types.js"; /** * Provides a read-only dashboard over the migration store: per-migration * status, aggregate summaries, health checks, and history per migration id. */ export class MigrationStatusDashboard { protected readonly config: StatusConfig; constructor(config: StatusConfig) { this.config = config; } async getAll(): Promise { const { store, migrations } = this.config; const applied = await store.getApplied(); const appliedMap = new Map(applied.map((r) => [r.id, r])); return sortMigrations(migrations).map((m): ExtendedMigrationStatus => { const record = appliedMap.get(m.id); const status = record ? record.direction === "down" ? ("rolled-back" as const) : ("applied" as const) : ("pending" as const); return { id: m.id, name: m.name, status, ...(record !== undefined && { appliedAt: record.appliedAt }), ...(record?.checksum !== undefined && { checksum: record.checksum, }), }; }); } async getSummary(): Promise { const all = await this.getAll(); return { total: all.length, applied: all.filter((s) => s.status === "applied").length, pending: all.filter((s) => s.status === "pending").length, failed: all.filter((s) => s.status === "failed").length, }; } async healthCheck(): Promise { const summary = await this.getSummary(); return { healthy: summary.pending === 0 && summary.failed === 0, pendingCount: summary.pending, failedCount: summary.failed, }; } async getHistory(id: string): Promise { const { store } = this.config; const applied = await store.getApplied(); return applied .filter((r) => r.id === id) .map( (r): MigrationHistoryEntry => ({ id: r.id, direction: r.direction, appliedAt: r.appliedAt, ...(r.checksum !== undefined && { checksum: r.checksum }), }) ); } }