import type { AuditRow } from "./api"; // The audit log's scheduler noise, folded (the owner's pick): the background jobs write a row per // run that changed something, so an hour of "enrichment done" filled the first screen and pushed the // admins' own actions down. A job's SUCCESSFUL runs become one row that names how many there were and // opens to each — whatever ran between them (another job, an admin's action): the owner read a fold // that broke at every other job as the same row five times. Only that job's own failed or skipped run // breaks it: that row stays on its own, at its moment, where it is seen, and the job's older successes // start a new fold below it. Rows arrive newest first; a fold stands at its newest run's place. /** An audit row as the table lists it: a lone row, or the newest of a job's folded runs (`runs`, the * whole fold newest first, the row itself included). */ export type AuditListRow = AuditRow & { runs?: AuditRow[] }; /** The job a row is a run of, or null for any other row. */ function jobOf(row: AuditRow): string | null { return row.action === "job_run" && row.target_type === "job" && row.target_id ? row.target_id : null; } export function foldJobRuns(rows: readonly AuditRow[]): AuditListRow[] { const out: AuditListRow[] = []; // Each job's open fold: the row its next older success joins. const open = new Map(); for (const row of rows) { const job = jobOf(row); if (job === null) { out.push(row); continue; } if (row.after?.status !== "ok") { // A failure or a skip stands alone and closes the job's fold. open.delete(job); out.push(row); continue; } const head = open.get(job); if (head) { head.runs = [...(head.runs ?? [head]), row]; continue; } const fresh: AuditListRow = { ...row }; open.set(job, fresh); out.push(fresh); } return out; }