import type { ModuleMigrationAggregate } from "../modules/index.js"; import type { MigrationApplyResult, MigrationPlanResult, RetainedMigrationHistory, } from "../migrations/index.js"; function plural(count: number, singular: string): string { return count === 1 ? singular : `${singular}s`; } export function formatMigrationValidationReport( aggregate: ModuleMigrationAggregate, ): readonly string[] { const moduleCount = aggregate.modules.length; const migrationCount = aggregate.migrations.length; const header = `Migration validation passed (${moduleCount} ${plural(moduleCount, "module")}, ${migrationCount} ${plural(migrationCount, "migration")})${moduleCount === 0 ? "." : ":"}`; const lines = [header]; for (const module of aggregate.modules) { if (module.migrations.length === 0) { lines.push(`- ${module.moduleId} (no migrations)`); continue; } for (const migration of module.migrations) { lines.push( `- ${migration.identity} sha256=${migration.checksum} destructive=${migration.destructive.mode}`, ); } } return Object.freeze(lines); } /** An absent module's applied history (ADR 0048): kept, reported, and never a reason to stop. */ function retainedLines(retained: readonly RetainedMigrationHistory[]): readonly string[] { return retained.map( (module) => `- [retained] ${module.moduleId}: ${module.appliedCount} applied ${plural(module.appliedCount, "migration")} kept; the module is not installed`, ); } export function formatMigrationPlanReport(plan: MigrationPlanResult): readonly string[] { const readyCount = plan.pendingCount - plan.approvalRequiredCount; const retainedSummary = plan.retained.length === 0 ? "" : `, ${plan.retained.length} retained`; const lines = [ `Migration plan (${plan.appliedCount} applied, ${readyCount} pending, ${plan.approvalRequiredCount} approval-blocked${retainedSummary}):`, ]; for (const entry of plan.entries) { lines.push(`- [${entry.status}] ${entry.identity} sha256=${entry.checksum}`); } if (plan.entries.length === 0) { lines.push("- No installed module migrations."); } lines.push(...retainedLines(plan.retained)); lines.push( "Informational only: this plan is read-only and may become stale; apply revalidates history under the migration lock.", ); return Object.freeze(lines); } export function formatMigrationApplyReport(result: MigrationApplyResult): readonly string[] { if (result.noOp) { return Object.freeze([ "Migration apply completed: database is already current (no changes).", ...retainedLines(result.retained), ]); } const lines = [ `Migration apply completed (${result.appliedCount} ${plural(result.appliedCount, "migration")} applied):`, ]; for (const migration of result.applied) { lines.push(`- ${migration.identity} sha256=${migration.checksum}`); } lines.push(...retainedLines(result.retained)); return Object.freeze(lines); }