harmony-plugin-manager
Version:
A comprehensive TypeScript library for generating harmonious color palettes with WCAG 2.1 accessibility compliance
573 lines • 20.5 kB
TypeScript
/**
* 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