/** * Handler Registry Implementation * Manages handler metadata and configuration without circular dependencies */ import type { JobHandler, ScheduledJob } from '@stock-bot/types'; import type { HandlerConfiguration, HandlerMetadata, RegistryStats } from './types'; export class HandlerRegistry { private handlers = new Map(); private configurations = new Map(); private handlerServices = new Map(); /** * Register handler metadata */ registerMetadata(metadata: HandlerMetadata): void { this.handlers.set(metadata.name, metadata); if (metadata.service) { this.handlerServices.set(metadata.name, metadata.service); } } /** * Register handler configuration with operation implementations */ registerConfiguration(config: HandlerConfiguration): void { this.configurations.set(config.name, config); } /** * Register both metadata and configuration */ register(metadata: HandlerMetadata, config: HandlerConfiguration): void { this.registerMetadata(metadata); this.registerConfiguration(config); } /** * Get handler metadata */ getMetadata(handlerName: string): HandlerMetadata | undefined { return this.handlers.get(handlerName); } /** * Get handler configuration */ getConfiguration(handlerName: string): HandlerConfiguration | undefined { return this.configurations.get(handlerName); } /** * Get a specific operation handler */ getOperation(handlerName: string, operationName: string): JobHandler | undefined { const config = this.configurations.get(handlerName); return config?.operations[operationName]; } /** * Get all handler metadata */ getAllMetadata(): Map { return new Map(this.handlers); } /** * Get all handler names */ getHandlerNames(): string[] { return Array.from(this.handlers.keys()); } /** * Check if a handler is registered */ hasHandler(handlerName: string): boolean { return this.handlers.has(handlerName); } /** * Set service ownership for a handler */ setHandlerService(handlerName: string, serviceName: string): void { this.handlerServices.set(handlerName, serviceName); // Update metadata if it exists const metadata = this.handlers.get(handlerName); if (metadata) { metadata.service = serviceName; } } /** * Get the service that owns a handler */ getHandlerService(handlerName: string): string | undefined { return this.handlerServices.get(handlerName); } /** * Get all handlers for a specific service */ getServiceHandlers(serviceName: string): HandlerMetadata[] { const handlers: HandlerMetadata[] = []; for (const [handlerName, metadata] of this.handlers) { if ( this.handlerServices.get(handlerName) === serviceName || metadata.service === serviceName ) { handlers.push(metadata); } } return handlers; } /** * Get scheduled jobs for a handler */ getScheduledJobs(handlerName: string): ScheduledJob[] { const config = this.configurations.get(handlerName); return config?.scheduledJobs || []; } /** * Get all handlers with their scheduled jobs */ getAllHandlersWithSchedule(): Map< string, { metadata: HandlerMetadata; scheduledJobs: ScheduledJob[] } > { const result = new Map(); for (const [name, metadata] of this.handlers) { const config = this.configurations.get(name); result.set(name, { metadata, scheduledJobs: config?.scheduledJobs || [], }); } return result; } /** * Get registry statistics */ getStats(): RegistryStats { let operationCount = 0; let scheduledJobCount = 0; const services = new Set(); for (const metadata of this.handlers.values()) { operationCount += metadata.operations.length; scheduledJobCount += metadata.schedules?.length || 0; if (metadata.service) { services.add(metadata.service); } } return { handlers: this.handlers.size, operations: operationCount, scheduledJobs: scheduledJobCount, services: services.size, }; } /** * Clear all registrations (useful for testing) */ clear(): void { this.handlers.clear(); this.configurations.clear(); this.handlerServices.clear(); } /** * Export registry data for debugging or persistence */ export(): { handlers: Array<[string, HandlerMetadata]>; configurations: Array<[string, HandlerConfiguration]>; services: Array<[string, string]>; } { return { handlers: Array.from(this.handlers.entries()), configurations: Array.from(this.configurations.entries()), services: Array.from(this.handlerServices.entries()), }; } /** * Import registry data */ import(data: { handlers: Array<[string, HandlerMetadata]>; configurations: Array<[string, HandlerConfiguration]>; services: Array<[string, string]>; }): void { this.clear(); for (const [name, metadata] of data.handlers) { this.handlers.set(name, metadata); } for (const [name, config] of data.configurations) { this.configurations.set(name, config); } for (const [handler, service] of data.services) { this.handlerServices.set(handler, service); } } }