// "CITED IN" — the back-link index a moment page reads: for every moment the // given reports cite, each place that cites it. // // moment key → [{ reportId, sectionId, claimId, entryId?, citationId }] // // Built at compose from the site's published reports, in the order given, each // report in reading order (./uses.ts). A place is listed once however many // times it cites the moment there (a claim that cites a span in its findings // and lists it under itself is one back-link); two citations of the same span // in one claim are two. Citations without a moment (`source`, `page`), and // references to citations the report does not define, are not indexed. // // Pure: the input is parsed reports, the output plain JSON. import { momentKeyOf } from "../citations/moments"; import type { Report } from "./schema"; import { reportCitationUses } from "./uses"; export type CitedIn = { reportId: string; // null when cited in the report's summary or a timeline entry. sectionId: string | null; // null when cited in the summary, an entry or a section's body. claimId: string | null; // The timeline entry, when cited in one; absent elsewhere. entryId?: string; citationId: string; }; export function buildCitedIn(reports: readonly Report[]): Record { const out: Record = {}; const seen = new Set(); for (const report of reports) { const citations = report.citations ?? {}; for (const use of reportCitationUses(report)) { if (!Object.hasOwn(citations, use.citationId)) continue; const key = momentKeyOf(citations[use.citationId]); if (!key) continue; const entry: CitedIn = { reportId: report.id, sectionId: use.sectionId, claimId: use.claimId, ...(use.entryId !== undefined ? { entryId: use.entryId } : {}), citationId: use.citationId, }; const dedup = JSON.stringify([key, entry.reportId, entry.sectionId, entry.claimId, entry.entryId ?? null, entry.citationId]); if (seen.has(dedup)) continue; seen.add(dedup); (out[key] ??= []).push(entry); } } return out; }