mirror of
https://github.com/Kaelio/ktx.git
synced 2026-06-25 08:48:08 +02:00
93 lines
3.5 KiB
TypeScript
93 lines
3.5 KiB
TypeScript
import { sanitizeMemoryFlowError } from './live-buffer.js';
|
|
import type { MemoryFlowEvent, MemoryFlowReplayInput } from './types.js';
|
|
import { buildMemoryFlowViewModel } from './view-model.js';
|
|
|
|
function latest<T extends MemoryFlowEvent['type']>(
|
|
events: MemoryFlowEvent[],
|
|
type: T,
|
|
): Extract<MemoryFlowEvent, { type: T }> | undefined {
|
|
return events.filter((event): event is Extract<MemoryFlowEvent, { type: T }> => event.type === type).at(-1);
|
|
}
|
|
|
|
function eventsOf<T extends MemoryFlowEvent['type']>(
|
|
events: MemoryFlowEvent[],
|
|
type: T,
|
|
): Array<Extract<MemoryFlowEvent, { type: T }>> {
|
|
return events.filter((event): event is Extract<MemoryFlowEvent, { type: T }> => event.type === type);
|
|
}
|
|
|
|
function replaySourceLine(input: MemoryFlowReplayInput): string | null {
|
|
const metadata = input.metadata;
|
|
if (!metadata) {
|
|
return null;
|
|
}
|
|
|
|
const origin =
|
|
metadata.origin === 'synthetic-report'
|
|
? 'synthetic report replay'
|
|
: metadata.origin === 'packaged'
|
|
? 'packaged replay'
|
|
: 'captured replay';
|
|
return `Replay source: ${origin} (${metadata.timing} timing)`;
|
|
}
|
|
|
|
function humanizeSummaryText(value: string): string {
|
|
return value
|
|
.replace(/\bWORKUNITS\b/g, 'PLAN')
|
|
.replace(/\bWorkUnit\b/g, 'Table review')
|
|
.replace(/\bwork units\b/gi, 'table reviews')
|
|
.replace(/\bWUs\b/g, 'tables')
|
|
.replace(/\braw files\b/gi, 'database files')
|
|
.replace(/\braw file\b/gi, 'database file')
|
|
.replace(/\bSL\b/g, 'semantic layer');
|
|
}
|
|
|
|
export function formatMemoryFlowFinalSummary(input: MemoryFlowReplayInput): string {
|
|
const sources = eventsOf(input.events, 'source_acquired');
|
|
const source = sources.at(-1);
|
|
const totalFiles = sources.reduce((sum, s) => sum + s.fileCount, 0);
|
|
const saved = latest(input.events, 'saved');
|
|
const provenance = latest(input.events, 'provenance_recorded');
|
|
const report = latest(input.events, 'report_created');
|
|
const finished = eventsOf(input.events, 'work_unit_finished');
|
|
const failed = finished.filter((event) => event.status === 'failed');
|
|
const view = buildMemoryFlowViewModel(input);
|
|
const lines = [
|
|
`Memory-flow summary: ${input.status}`,
|
|
`Connection: ${input.connectionId}`,
|
|
...(sources.length > 1
|
|
? [`Sources: ${[...new Set(sources.map((s) => s.adapter))].join(', ')}`]
|
|
: [`Adapter: ${input.adapter}`]),
|
|
`Run: ${input.runId}`,
|
|
`Sync: ${input.syncId}`,
|
|
`Source files: ${totalFiles}`,
|
|
`Table reviews: ${input.plannedWorkUnits.length || finished.length} total, ${finished.length - failed.length} done, ${failed.length} failed`,
|
|
`Saved memory: ${saved?.wikiCount ?? 0} wiki, ${saved?.slCount ?? 0} semantic layer`,
|
|
`Provenance rows: ${provenance?.rowCount ?? 0}`,
|
|
`Report: ${report?.reportPath ?? input.reportPath ?? 'none'}`,
|
|
];
|
|
const sourceLine = replaySourceLine(input);
|
|
if (sourceLine) {
|
|
lines.push(sourceLine);
|
|
}
|
|
if (input.metadata?.capturedAt) {
|
|
lines.push(`Replay captured: ${input.metadata.capturedAt}`);
|
|
}
|
|
if (input.metadata?.fallbackReason) {
|
|
lines.push(`Replay note: ${input.metadata.fallbackReason}`);
|
|
}
|
|
|
|
if (view.trustIssues.length > 0) {
|
|
lines.push(`Trust issues: ${view.trustIssues.length}`);
|
|
for (const issue of view.trustIssues.slice(0, 3)) {
|
|
lines.push(`- ${humanizeSummaryText(issue.title)}: ${humanizeSummaryText(issue.detail)}`);
|
|
}
|
|
}
|
|
|
|
for (const error of input.errors.slice(0, 3)) {
|
|
lines.push(`Error: ${sanitizeMemoryFlowError(error)}`);
|
|
}
|
|
|
|
lines.push('');
|
|
return lines.join('\n');
|
|
}
|