/** * Widget Registry * * Central registry der håndterer widget discovery, queries og relationship management. * Gør det muligt for widgets at finde hinanden og etablere dataflow. */ import { WidgetMetadata, WidgetQuery, WidgetRegistry, DataType, securityWidgetSchemas, calendarScheduleWidgetSchemas } from '@/types/WidgetSchema'; class WidgetRegistryImpl implements WidgetRegistry { widgets: Map = new Map(); constructor() { // Initialize with security widget schemas Object.values(securityWidgetSchemas).forEach(widget => { this.register(widget); }); // Initialize with calendar/schedule widget schemas Object.values(calendarScheduleWidgetSchemas).forEach(widget => { this.register(widget); }); } register(widget: WidgetMetadata): void { this.widgets.set(widget.id, widget); } unregister(widgetId: string): void { this.widgets.delete(widgetId); } get(widgetId: string): WidgetMetadata | undefined { return this.widgets.get(widgetId); } // Find widgets that provide a specific data type findByCapability(type: DataType): WidgetMetadata[] { return Array.from(this.widgets.values()).filter(widget => widget.provides.some(cap => cap.outputType === type) ); } // Find widgets that require a specific data type findByRequirement(type: DataType): WidgetMetadata[] { return Array.from(this.widgets.values()).filter(widget => widget.requires.some(req => req.type === type) ); } // Find widgets that can connect to a given widget findCompatible(widgetId: string): WidgetMetadata[] { const widget = this.widgets.get(widgetId); if (!widget) return []; const compatible: Set = new Set(); // Find widgets that can provide what this widget requires widget.requires.forEach(req => { this.findByCapability(req.type).forEach(provider => { if (provider.id !== widgetId) { compatible.add(provider); } }); }); // Find widgets that require what this widget provides widget.provides.forEach(cap => { this.findByRequirement(cap.outputType).forEach(consumer => { if (consumer.id !== widgetId) { compatible.add(consumer); } }); }); return Array.from(compatible); } // Advanced query for widget discovery query(query: WidgetQuery): WidgetMetadata[] { return Array.from(this.widgets.values()).filter(widget => { // Filter by provided type if (query.providesType && !widget.provides.some(cap => cap.outputType === query.providesType)) { return false; } // Filter by specific capability if (query.providesCapability && !widget.provides.some(cap => cap.id === query.providesCapability)) { return false; } // Filter by required type if (query.requiresType && !widget.requires.some(req => req.type === query.requiresType)) { return false; } // Filter by required source if (query.requiresSource && !widget.requires.some(req => req.source === query.requiresSource)) { return false; } // Filter by category if (query.category && widget.category !== query.category) { return false; } // Filter by tags if (query.tags && !query.tags.some(tag => widget.tags.includes(tag))) { return false; } // Filter by priority if (query.priority && widget.priority !== query.priority) { return false; } return true; }); } // Get widgets that provide data to the given widget getProviders(widgetId: string): WidgetMetadata[] { const widget = this.widgets.get(widgetId); if (!widget) return []; return widget.relations .filter(rel => rel.type === 'depends_on') .map(rel => this.widgets.get(rel.targetWidgetId)) .filter((w): w is WidgetMetadata => w !== undefined); } // Get widgets that consume data from the given widget getConsumers(widgetId: string): WidgetMetadata[] { const widget = this.widgets.get(widgetId); if (!widget) return []; return widget.relations .filter(rel => rel.type === 'provides_to') .map(rel => this.widgets.get(rel.targetWidgetId)) .filter((w): w is WidgetMetadata => w !== undefined); } // Build complete dependency graph getDependencyGraph(): Map { const graph = new Map(); this.widgets.forEach((widget, id) => { const dependencies = widget.relations .filter(rel => rel.type === 'depends_on') .map(rel => rel.targetWidgetId); graph.set(id, dependencies); }); return graph; } // Get widget's introduction/description for ecosystem describeWidget(widgetId: string): string { const widget = this.widgets.get(widgetId); if (!widget) return 'Widget ikke fundet'; const requires = widget.requires.map(r => ` - ${r.name} (${r.type}): ${r.description}`).join('\n'); const provides = widget.provides.map(p => ` - ${p.name} (${p.outputType}): ${p.description}`).join('\n'); const configs = widget.config.map(c => ` - ${c.name}: ${c.description}`).join('\n'); return ` ## ${widget.name} (v${widget.version}) ${widget.description} **Kategori:** ${widget.category} **Tags:** ${widget.tags.join(', ')} **Prioritet:** ${widget.priority || 'medium'} ### Kræver Data: ${requires || ' Ingen data-afhængigheder'} ### Leverer Data: ${provides || ' Ingen outputs'} ### Konfiguration: ${configs || ' Ingen konfigurerbare indstillinger'} ${widget.documentation ? `### Dokumentation:\n${widget.documentation}` : ''} `.trim(); } // Export registry state for persistence or debugging export(): Record { const result: Record = {}; this.widgets.forEach((widget, id) => { result[id] = widget; }); return result; } } // Singleton instance export const widgetRegistry = new WidgetRegistryImpl(); // React hook for widget discovery export function useWidgetRegistry() { return widgetRegistry; }