"use strict"; function copy(value) { return value === undefined ? undefined : JSON.parse(JSON.stringify(value)); } function normalizeConceptName(value) { return String(value || "").trim().toLocaleLowerCase(); } /** Index lightweight concept placements across stored CMaps. */ export class CmapReferenceIndex { constructor() { this.byConceptId = new Map(); this.byName = new Map(); this.byPage = new Map(); } clear() { this.byConceptId.clear(); this.byName.clear(); this.byPage.clear(); } load(placements = [], conceptMaps = [], canonicalPageReference = (value) => value, normalizeName = normalizeConceptName) { this.clear(); const mapTitles = new Map(conceptMaps .filter((map) => map && map.slug) .map((map) => [map.slug, map.title || map.slug])); const pageByConcept = new Map(); for (const placement of placements) { if (!placement?.conceptId || !placement.pageSlug) continue; pageByConcept.set(placement.conceptId, canonicalPageReference(placement.pageSlug).toLocaleLowerCase()); } for (const placement of placements) { if (!placement?.conceptId || !placement.cmapSlug) continue; const conceptId = String(placement.conceptId); const slug = String(placement.cmapSlug); const nameKey = normalizeName(placement.label); if (nameKey) this.byName.set(nameKey, conceptId); if (!this.byConceptId.has(conceptId)) this.byConceptId.set(conceptId, new Map()); const maps = this.byConceptId.get(conceptId); const existing = maps.get(slug); maps.set(slug, { slug, title: mapTitles.get(slug) || placement.cmapTitle || slug, count: (existing?.count || 0) + (Number(placement.count) || 0) }); const pageKey = pageByConcept.get(conceptId); if (!pageKey) continue; if (!this.byPage.has(pageKey)) this.byPage.set(pageKey, new Map()); const pageConcepts = this.byPage.get(pageKey); if (!pageConcepts.has(conceptId)) { pageConcepts.set(conceptId, { conceptId, label: placement.label || "Concept", count: 0, maps: new Map() }); } const concept = pageConcepts.get(conceptId); const count = Number(placement.count) || 0; concept.count += count; const pageMap = concept.maps.get(slug); concept.maps.set(slug, { slug, title: mapTitles.get(slug) || placement.cmapTitle || slug, count: count + (pageMap?.count || 0) }); } return this; } mapsFor(conceptId) { return Array.from(this.byConceptId.get(String(conceptId))?.values() || []) .map(copy); } countFor(conceptId) { return this.mapsFor(conceptId).reduce((sum, map) => sum + map.count, 0); } conceptIdForName(label) { return this.byName.get(normalizeConceptName(label)) || null; } pageConcepts(pageReference) { return Array.from(this.byPage.get(String(pageReference).toLocaleLowerCase())?.values() || []) .map((concept) => ({ ...copy(concept), maps: new Map(concept.maps) })); } }