import { CLEAN_GRAPH, FINDING_INDENT, NO_GRAPHS, NO_INVENTORY, NO_SEEDS, UNTAUGHT_HEAD, } from "#configuration/strings/derivation.strings"; import { GRAPHS_VALIDATED, GROUNDING_SUMMARY, GROUNDING_UNASSESSED_HEAD, PAGES_SUFFIX, PAYLOAD_MISSING, } from "#configuration/strings/coverage.strings"; import type { GraphContext, GraphFinding } from "#types/coverage.types"; import { conceptsTaughtAcross, taughtAcross, untaughtOf, validateGraph, validateUnion, } from "#core/validators/coverage.validator"; import { conclude, refuse, say } from "#core/reporters/derivation.reporter"; import { inventoryTarget, readInventory } from "#core/loaders/inventory.loader"; import { panelCount, validatePanels } from "#core/validators/panel.validator"; import { readSeeds, seedsTarget } from "#core/loaders/seed.loader"; import { COVERAGE_COMMAND } from "#configuration/constants/contract.constants"; import { COVERAGE_SUMMARY } from "#configuration/strings/contract.strings"; import { EVIDENCE } from "@banes-lab/web/configuration/constants/evidence.source.constants.ts"; import { EVIDENCE_ABSENT } from "#configuration/constants/evidence.constants"; import type { Finding } from "#types/derivation.types"; import { PANELS_VALIDATED } from "#configuration/strings/panel.strings"; import { groundingOf } from "#core/validators/evidence.validator"; import { loadContentGraphs } from "#core/loaders/coverage.loader"; import { readPagePayload } from "#core/loaders/payload.loader"; import { readPanelSections } from "#core/loaders/panel.loader"; import { resolveArgv } from "@govlab/argv"; resolveArgv({ command: COVERAGE_COMMAND, flags: [], summary: COVERAGE_SUMMARY }); const SECTION_MARK = "#"; const inventory = readInventory(); if (inventory === null) { refuse(`${NO_INVENTORY}${inventoryTarget()}`); } const seeds = readSeeds(); if (seeds === null) { refuse(`${NO_SEEDS}${seedsTarget()}`); } const graphs = await loadContentGraphs(); if (graphs.length === 0) { say(NO_GRAPHS); } const asFinding = function asFinding(held: GraphFinding): Finding { return { file: held.page + SECTION_MARK + held.section, message: held.message }; }; const known = new Set([...inventory.records.map((record) => record.slug), ...seeds.seeds.map((seed) => seed.id)]); const payloadSections = new Map(); const findings: Finding[] = []; for (const graph of graphs) { const payload = readPagePayload(graph.page); if (payload === null) { findings.push({ file: graph.page, message: `${PAYLOAD_MISSING}${graph.page}` }); continue; } payloadSections.set(graph.page, payload.sections); } const context: GraphContext = { known, payloadSections, taught: taughtAcross(graphs) }; const graphFindings: GraphFinding[] = [ ...graphs.flatMap((graph) => validateGraph(graph, context)), ...validateUnion(graphs), ...conceptsTaughtAcross(graphs), ]; const reported = new Set(); for (const held of graphFindings) { const key = held.page + SECTION_MARK + held.section + held.message; if (!reported.has(key)) { reported.add(key); findings.push(asFinding(held)); } } const grounding = groundingOf(graphs, EVIDENCE, EVIDENCE_ABSENT); findings.push(...grounding.findings.map(asFinding)); say( `${GROUNDING_SUMMARY}${String(grounding.grounded.length)}, ${String(grounding.absent.length)}, ${String(grounding.unassessed.length)}`, ); if (grounding.unassessed.length > 0) { say(GROUNDING_UNASSESSED_HEAD); for (const node of grounding.unassessed) { say(`${FINDING_INDENT}${node}`); } } let panels = 0; for (const graph of graphs) { const sections = readPanelSections(graph.page) ?? []; panels += panelCount(sections); findings.push(...validatePanels(graph.page, sections).map(asFinding)); } say(`${String(panels)}${PANELS_VALIDATED}${String(graphs.length)}${PAGES_SUFFIX}`); const untaught = untaughtOf( graphs, inventory.records.map((record) => record.slug), ); if (untaught.length > 0) { say(UNTAUGHT_HEAD); for (const slug of untaught) { say(`${FINDING_INDENT}${slug}`); } } say(`${String(graphs.length)}${GRAPHS_VALIDATED}${String(payloadSections.size)}${PAGES_SUFFIX}`); conclude(COVERAGE_COMMAND, findings, CLEAN_GRAPH);