UNPKG

harmony-plugin-manager

Version:

A comprehensive TypeScript library for generating harmonious color palettes with WCAG 2.1 accessibility compliance

573 lines 20.5 kB
/** * Plugin Manager - Core orchestration and execution engine * * The PluginManager is the central component that orchestrates plugin execution. * It handles the complete plugin lifecycle from registration through execution, * including dependency resolution, error handling, and result aggregation. * * ## Key Features * * - **Type-safe plugin registration** with compile-time validation * - **Automatic dependency resolution** with cycle detection * - **Isolated execution contexts** with shared state management * - **Comprehensive error handling** with graceful degradation * - **Performance monitoring** with detailed execution metrics * - **Flexible configuration** with priority-based ordering * * ## Usage Patterns * * ### Basic Usage * ```typescript * const manager = new PluginManager<string, string>() * .register(validationPlugin) * .register(transformPlugin) * .build(); * * const result = await manager.execute('input', { requestId: '123' }); * ``` * * ### Advanced Configuration * ```typescript * const manager = new PluginManager<ApiRequest, ApiResponse>() * .register(authPlugin, { priority: 100, enabled: true }) * .register(rateLimitPlugin, { priority: 90 }) * .register(validationPlugin, { priority: 80 }) * .register(businessLogicPlugin, { priority: 50 }) * .register(responseFormatterPlugin, { priority: 10 }) * .build(); * ``` */ import { type ExecutionOptions } from 'harmony-pipeline'; import type { IPluginManager, IPlugin } from '../core/interfaces'; import type { PluginSettings, PluginContext, ManagerResult } from '../core/types'; import { PluginRegistration } from './plugin-registration'; export declare class PluginManager<TInput = unknown, TOutput = unknown, TMetadata extends Record<string, unknown> = Record<string, unknown>> implements IPluginManager<TInput, TOutput, TMetadata> { private readonly registrations; private pipeline?; private built; /** * Registers a plugin with the manager * * Plugin registration is the process of adding a plugin to the manager's * execution pipeline. Each plugin must have a unique name and will be * executed according to its dependencies and priority settings. * * **Important**: Plugin registration can only be done before calling build(). * Once the manager is built, the plugin configuration is frozen. * * @param plugin - The plugin instance to register * @param settings - Optional configuration for the plugin's execution behavior * * @returns The manager instance for method chaining * * @throws {Error} When plugin name conflicts with existing plugin * @throws {Error} When manager is already built * * @example * ```typescript * // Basic plugin registration * const manager = new PluginManager<string, string>() * .register(uppercasePlugin) * .register(trimPlugin); * ``` * * @example * ```typescript * // Registration with custom settings * const manager = new PluginManager<UserData, ProcessedUser>() * .register(validationPlugin, { * enabled: true, * priority: 100, // Execute first (highest priority) * config: { * strictMode: true, * requiredFields: ['id', 'email', 'name'] * } * }) * .register(enrichmentPlugin, { * priority: 50, // Execute after validation * config: { * includePreferences: true, * loadAvatars: false * } * }) * .register(debugPlugin, { * enabled: process.env.NODE_ENV === 'development' * }); * ``` * * @example * ```typescript * // Registration with dependency handling * const manager = new PluginManager<ApiRequest, ApiResponse>() * // Core plugins that others depend on * .register(authenticationPlugin, { priority: 100 }) * .register(authorizationPlugin, { priority: 90 }) * * // Business logic plugins (depend on auth) * .register(userServicePlugin, { priority: 50 }) * .register(notificationPlugin, { priority: 40 }) * * // Response plugins (depend on business logic) * .register(responseFormatterPlugin, { priority: 10 }) * .register(compressionPlugin, { priority: 5 }); * ``` */ register(plugin: IPlugin<TInput, TOutput, PluginContext<TMetadata>>, settings?: Partial<PluginSettings>): this; /** * Removes a plugin from the manager * * Unregistration removes a previously registered plugin from the manager. * This is useful for dynamic plugin management or removing plugins based * on runtime conditions. * * **Important**: Can only be called before build(). * * @param pluginName - Name of the plugin to remove * * @returns The manager instance for method chaining * * @throws {Error} When manager is already built * * @example * ```typescript * const manager = new PluginManager<string, string>() * .register(pluginA) * .register(pluginB) * .register(debugPlugin); * * // Remove debug plugin in production * if (process.env.NODE_ENV === 'production') { * manager.unregister('debug-plugin'); * } * * manager.build(); * ``` * * @example * ```typescript * // Conditional plugin removal based on configuration * const manager = new PluginManager<UserData, ProcessedUser>() * .register(corePlugin) * .register(analyticsPlugin) * .register(trackingPlugin); * * // Remove tracking plugins if privacy mode is enabled * const config = await loadConfiguration(); * if (config.privacyMode) { * manager * .unregister('analytics-plugin') * .unregister('tracking-plugin'); * } * ``` */ unregister(pluginName: string): this; /** * Updates configuration for an existing plugin * * Configuration allows you to modify plugin settings after registration * but before building. This is useful for dynamic configuration based on * environment variables, user preferences, or runtime conditions. * * **Important**: Can only be called before build(). * * @param pluginName - Name of the plugin to configure * @param settings - Partial settings to merge with existing configuration * * @returns The manager instance for method chaining * * @throws {Error} When plugin is not found * @throws {Error} When manager is already built * * @example * ```typescript * const manager = new PluginManager<UserData, ProcessedUser>() * .register(databasePlugin) * .register(cachePlugin); * * // Configure database plugin based on environment * const dbConfig = process.env.NODE_ENV === 'production' * ? { connectionPool: 20, timeout: 5000 } * : { connectionPool: 5, timeout: 10000 }; * * manager.configure('database-plugin', { * config: dbConfig * }); * * // Disable caching in development * if (process.env.NODE_ENV === 'development') { * manager.configure('cache-plugin', { enabled: false }); * } * ``` * * @example * ```typescript * // Runtime configuration based on user settings * const userPreferences = await loadUserPreferences(userId); * * const manager = new PluginManager<UserContent, ProcessedContent>() * .register(contentFilterPlugin) * .register(personalizationPlugin) * .register(recommendationPlugin); * * // Configure plugins based on user preferences * manager * .configure('content-filter', { * config: { * contentRating: userPreferences.maxContentRating, * hideExplicit: userPreferences.hideExplicitContent * } * }) * .configure('personalization', { * enabled: userPreferences.enablePersonalization, * config: { * interests: userPreferences.interests, * learningEnabled: userPreferences.adaptiveLearning * } * }); * ``` */ configure(pluginName: string, settings: Partial<PluginSettings>): this; /** * Retrieves plugin registration information by name * * @param name - Name of the plugin to retrieve * @returns Plugin registration or undefined if not found * * @example * ```typescript * const registration = manager.getPlugin('validation-plugin'); * if (registration) { * console.log(`Plugin version: ${registration.version}`); * console.log(`Is enabled: ${registration.isEnabled}`); * console.log(`Dependencies: ${registration.dependencies}`); * } * ``` */ getPlugin(name: string): PluginRegistration | undefined; /** * Gets all registered plugin names * * @returns Array of all registered plugin names * * @example * ```typescript * const pluginNames = manager.getPluginNames(); * console.log(`Registered plugins: ${pluginNames.join(', ')}`); * ``` */ getPluginNames(): readonly string[]; /** * Checks if a plugin is registered * * @param name - Plugin name to check * @returns True if plugin is registered * * @example * ```typescript * if (manager.hasPlugin('optional-feature')) { * console.log('Optional feature is available'); * } * ``` */ hasPlugin(name: string): boolean; /** * Gets all enabled plugin registrations * * @returns Array of enabled plugin registrations * * @example * ```typescript * const enabledPlugins = manager.getEnabledPlugins(); * console.log(`${enabledPlugins.length} plugins will execute`); * ``` */ getEnabledPlugins(): readonly PluginRegistration[]; /** * Gets plugins filtered by tag * * @param tag - Tag to filter by * @returns Array of plugin registrations with the specified tag * * @example * ```typescript * const validationPlugins = manager.getPluginsByTag('validation'); * const performancePlugins = manager.getPluginsByTag('performance'); * ``` */ getPluginsByTag(tag: string): readonly PluginRegistration[]; /** * Builds the plugin manager for execution * * Building is the process of finalizing the plugin configuration and constructing * the execution pipeline. During this phase, the manager: * * 1. **Validates all plugin dependencies** - ensures all required plugins are registered * 2. **Resolves execution order** - performs topological sort based on dependencies * 3. **Detects circular dependencies** - prevents infinite dependency loops * 4. **Constructs execution pipeline** - creates optimized execution chain * 5. **Freezes configuration** - prevents further plugin modifications * * **Important**: After calling build(), no further plugin registration or * configuration changes are allowed. The manager is ready for execution. * * @returns The manager instance for method chaining * * @throws {Error} When dependencies are invalid or circular * @throws {Error} When pipeline construction fails * @throws {Error} When manager is already built * * @example * ```typescript * // Basic build process * const manager = new PluginManager<string, string>() * .register(validationPlugin) * .register(transformPlugin) * .register(outputPlugin) * .build(); // Manager is now ready for execution * * // Can now execute, but cannot register more plugins * const result = await manager.execute('input', {}); * ``` * * @example * ```typescript * // Build with error handling * try { * const manager = new PluginManager<ApiRequest, ApiResponse>() * .register(authPlugin) * .register(businessLogicPlugin, { * dependencies: ['auth-plugin'] * }) * .register(responsePlugin, { * dependencies: ['business-logic-plugin'] * }) * .build(); * * console.log('Manager built successfully'); * console.log(`Execution order: ${manager.getEnabledPlugins().map(p => p.name)}`); * * } catch (error) { * if (error.message.includes('dependency')) { * console.error('Dependency resolution failed:', error.message); * } else if (error.message.includes('circular')) { * console.error('Circular dependency detected:', error.message); * } else { * console.error('Build failed:', error.message); * } * } * ``` * * @example * ```typescript * // Conditional building with validation * const manager = new PluginManager<UserData, ProcessedUser>(); * * // Register core plugins * manager.register(coreValidationPlugin); * manager.register(userTransformPlugin); * * // Add optional plugins based on configuration * const config = await loadConfiguration(); * * if (config.features.analytics) { * manager.register(analyticsPlugin); * } * * if (config.features.notifications) { * manager.register(notificationPlugin, { * dependencies: ['user-transform-plugin'] * }); * } * * // Validate plugin count before building * const enabledCount = manager.getEnabledPlugins().length; * if (enabledCount === 0) { * throw new Error('No plugins enabled for execution'); * } * * console.log(`Building manager with ${enabledCount} plugins`); * manager.build(); * ``` */ build(): this; /** * Executes all registered plugins in dependency order * * Execution is the core operation where the manager processes input data * through all registered and enabled plugins. The execution process: * * 1. **Creates isolated execution context** - provides plugins with logging, settings, and shared state * 2. **Initializes all plugins** - calls initialize() lifecycle method * 3. **Executes plugins in dependency order** - respects dependencies and priorities * 4. **Handles errors gracefully** - continues execution when possible, tracks failures * 5. **Cleans up resources** - calls cleanup() lifecycle methods * 6. **Aggregates results** - combines individual plugin results into manager result * * **Context Isolation**: Each execution creates a fresh context, preventing * data leakage between different execution calls. * * @param input - Input data to process through the plugin pipeline * @param metadata - Execution metadata made available to all plugins via context * @param options - Optional execution configuration (timeouts, error handling, etc.) * * @returns Promise resolving to comprehensive execution results * * @throws {Error} When manager is not built * @throws {Error} When execution fails catastrophically * * @example * ```typescript * // Basic execution * const manager = new PluginManager<string, string>() * .register(uppercasePlugin) * .register(trimPlugin) * .build(); * * const result = await manager.execute(' hello world ', { * requestId: 'req-123', * timestamp: Date.now() * }); * * if (result.success) { * console.log('Final output:', result.results[result.results.length - 1].output); * console.log(`Execution took ${result.duration}ms`); * } else { * console.error('Execution failed:', result.errors); * } * ``` * * @example * ```typescript * // Complex execution with comprehensive result handling * interface UserRequest { * userId: string; * action: string; * data: Record<string, any>; * } * * interface UserResponse { * success: boolean; * data: any; * metadata: Record<string, any>; * } * * const manager = new PluginManager<UserRequest, UserResponse>() * .register(authenticationPlugin) * .register(authorizationPlugin) * .register(validationPlugin) * .register(businessLogicPlugin) * .register(auditPlugin) * .build(); * * const request: UserRequest = { * userId: 'user-456', * action: 'update-profile', * data: { name: 'John Doe', email: 'john@example.com' } * }; * * const executionMetadata = { * requestId: 'req-789', * clientIp: '192.168.1.1', * userAgent: 'Mozilla/5.0...', * timestamp: Date.now() * }; * * try { * const result = await manager.execute(request, executionMetadata, { * timeout: 30000, // 30 second timeout * stopOnError: false // Continue execution even if some plugins fail * }); * * // Analyze results * console.log(`Execution ${result.success ? 'succeeded' : 'failed'}`); * console.log(`Total duration: ${result.duration}ms`); * console.log(`Plugins executed: ${result.results.length}`); * * // Check individual plugin results * result.results.forEach(pluginResult => { * if (pluginResult.success) { * console.log(`✓ ${pluginResult.pluginName} (${pluginResult.duration}ms)`); * } else { * console.error(`✗ ${pluginResult.pluginName} failed:`, pluginResult.error?.message); * } * * // Handle warnings * if (pluginResult.warnings.length > 0) { * console.warn(`⚠ ${pluginResult.pluginName} warnings:`, pluginResult.warnings); * } * }); * * // Handle overall warnings and errors * if (result.warnings.length > 0) { * console.warn('Execution warnings:', result.warnings); * } * * if (result.errors.length > 0) { * console.error('Execution errors:', result.errors); * } * * } catch (error) { * console.error('Execution failed catastrophically:', error); * } * ``` * * @example * ```typescript * // Batch execution with shared context * const batchManager = new PluginManager<UserData[], ProcessedUser[]>() * .register(batchValidationPlugin) * .register(batchEnrichmentPlugin) * .register(batchPersistencePlugin) * .build(); * * const userBatch = await loadUserBatch(); * * const result = await batchManager.execute(userBatch, { * batchId: 'batch-001', * batchSize: userBatch.length, * processingMode: 'parallel' * }); * * // Track batch processing metrics * const totalProcessed = result.success ? * result.results[result.results.length - 1].output?.length || 0 : 0; * * console.log(`Batch processing complete:`); * console.log(`- Input: ${userBatch.length} users`); * console.log(`- Processed: ${totalProcessed} users`); * console.log(`- Duration: ${result.duration}ms`); * console.log(`- Average per user: ${(result.duration / userBatch.length).toFixed(2)}ms`); * ``` */ execute(input: TInput, metadata: TMetadata, options?: ExecutionOptions): Promise<ManagerResult<TOutput>>; /** * Validates that plugin name is unique within manager */ private validatePluginUniqueness; /** * Creates plugin settings with sensible defaults */ private createPluginSettings; /** * Retrieves registration or throws if not found */ private getRegistration; /** * Creates execution pipeline from sorted registrations * Wraps each plugin in a processor for pipeline compatibility */ private createPipeline; /** * Creates plugin execution context with settings map */ private createExecutionContext; /** * Transforms pipeline result into manager result format * Aggregates plugin results, errors, and warnings */ private createManagerResult; private ensureNotBuilt; private ensureNotAlreadyBuilt; private ensureBuilt; } //# sourceMappingURL=plugin-manager.d.ts.map