Files
suaveplan/packages/persistence/database/migration/src/status/migration-status/migration-status.ts
2026-05-27 23:47:53 +01:00

78 lines
2.6 KiB
TypeScript

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<ExtendedMigrationStatus[]> {
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<MigrationSummary> {
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<MigrationHealthCheck> {
const summary = await this.getSummary();
return {
healthy: summary.pending === 0 && summary.failed === 0,
pendingCount: summary.pending,
failedCount: summary.failed,
};
}
async getHistory(id: string): Promise<MigrationHistoryEntry[]> {
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 }),
})
);
}
}