/** * SDK Catalog Manager - Central Registry * * Public API for browsing, searching, and converting catalog entries. * Registry data definitions live in catalog-registry-data.ts. */ import type { CatalogId, CatalogMeta, CatalogModule, CatalogEntry, CatalogStats, CatalogOverviewStats, CustomCatalogEntry, CustomCatalogs, } from './types' import { CATALOG_REGISTRY, SYSTEM_ENTRIES_MAP } from './catalog-registry-data' // Re-export so existing imports keep working export { CATALOG_REGISTRY } // ============================================================================= // 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) }