/** * Catalog Registry — Public API, Entry Converters & Registry Assembly */ import type { CatalogId, CatalogMeta, CatalogModule, CatalogEntry, CatalogStats, CatalogOverviewStats, CustomCatalogEntry, CustomCatalogs, } from '../types' import { DSFA_AI_CATALOG_META, DSFA_AI_SYSTEM_ENTRIES } from './registry-dsfa' import { VENDOR_VVT_CATALOG_META, VENDOR_VVT_SYSTEM_ENTRIES } from './registry-vendor' import { REFERENCE_CATALOG_META, REFERENCE_SYSTEM_ENTRIES } from './registry-reference' // ============================================================================= // CATALOG REGISTRY (assembled from sub-registries) // ============================================================================= export const CATALOG_REGISTRY: Record = { ...DSFA_AI_CATALOG_META, ...VENDOR_VVT_CATALOG_META, ...REFERENCE_CATALOG_META, } as Record // ============================================================================= // SYSTEM ENTRIES MAP (assembled from sub-registries) // ============================================================================= const SYSTEM_ENTRIES_MAP: Record[]> = { ...DSFA_AI_SYSTEM_ENTRIES, ...VENDOR_VVT_SYSTEM_ENTRIES, ...REFERENCE_SYSTEM_ENTRIES, } as Record[]> // ============================================================================= // HELPER: Resolve localized text fields // ============================================================================= function resolveField(value: unknown): string { if (value === null || value === undefined) return '' if (typeof value === 'string') return value if (typeof value === 'number') return String(value) if (typeof value === 'object' && 'de' in (value as Record)) { return String((value as Record).de || '') } return String(value) } // ============================================================================= // ENTRY CONVERTERS // ============================================================================= function systemEntryToCatalogEntry( catalogId: CatalogId, data: Record, ): CatalogEntry { const meta = CATALOG_REGISTRY[catalogId] const idValue = resolveField(data[meta.idField]) const nameValue = resolveField(data[meta.nameField]) const descValue = meta.descriptionField ? resolveField(data[meta.descriptionField]) : undefined const catValue = meta.categoryField ? resolveField(data[meta.categoryField]) : undefined return { id: idValue || crypto.randomUUID(), catalogId, source: 'system', data, displayName: nameValue || idValue || '(Unbenannt)', displayDescription: descValue, category: catValue, } } function customEntryToCatalogEntry( catalogId: CatalogId, entry: CustomCatalogEntry, ): CatalogEntry { const meta = CATALOG_REGISTRY[catalogId] const nameValue = resolveField(entry.data[meta.nameField]) const descValue = meta.descriptionField ? resolveField(entry.data[meta.descriptionField]) : undefined const catValue = meta.categoryField ? resolveField(entry.data[meta.categoryField]) : undefined return { id: entry.id, catalogId, source: 'custom', data: entry.data, displayName: nameValue || '(Unbenannt)', displayDescription: descValue, category: catValue, } } // ============================================================================= // PUBLIC API // ============================================================================= export function getSystemEntries(catalogId: CatalogId): CatalogEntry[] { const raw = SYSTEM_ENTRIES_MAP[catalogId] || [] return raw.map(data => systemEntryToCatalogEntry(catalogId, data)) } export function getAllEntries( catalogId: CatalogId, customEntries: CustomCatalogEntry[] = [], ): CatalogEntry[] { const system = getSystemEntries(catalogId) const custom = customEntries.map(e => customEntryToCatalogEntry(catalogId, e)) return [...system, ...custom] } export function getCatalogsByModule(module: CatalogModule): CatalogMeta[] { return Object.values(CATALOG_REGISTRY).filter(c => c.module === module) } export function getCatalogStats( catalogId: CatalogId, customEntries: CustomCatalogEntry[] = [], ): CatalogStats { const meta = CATALOG_REGISTRY[catalogId] return { catalogId, systemCount: meta.systemCount, customCount: customEntries.length, totalCount: meta.systemCount + customEntries.length, } } export function getOverviewStats(customCatalogs: CustomCatalogs): CatalogOverviewStats { const modules: CatalogModule[] = ['dsfa', 'vvt', 'vendor', 'ai_act', 'reference'] const byModule = {} as Record let totalSystemEntries = 0 let totalCustomEntries = 0 for (const mod of modules) { const cats = getCatalogsByModule(mod) let entries = 0 for (const cat of cats) { const customCount = customCatalogs[cat.id]?.length ?? 0 entries += cat.systemCount + customCount totalSystemEntries += cat.systemCount totalCustomEntries += customCount } byModule[mod] = { catalogs: cats.length, entries } } return { totalCatalogs: Object.keys(CATALOG_REGISTRY).length, totalSystemEntries, totalCustomEntries, totalEntries: totalSystemEntries + totalCustomEntries, byModule, } } export function searchCatalog( catalogId: CatalogId, query: string, customEntries: CustomCatalogEntry[] = [], ): CatalogEntry[] { const allEntries = getAllEntries(catalogId, customEntries) const meta = CATALOG_REGISTRY[catalogId] const q = query.toLowerCase().trim() if (!q) return allEntries return allEntries .map(entry => { let score = 0 for (const field of meta.searchableFields) { const value = resolveField(entry.data[field]).toLowerCase() if (value.includes(q)) { score += value.startsWith(q) ? 10 : 5 } } // Also search display name if (entry.displayName.toLowerCase().includes(q)) { score += 15 } return { entry, score } }) .filter(r => r.score > 0) .sort((a, b) => b.score - a.score) .map(r => r.entry) }