UNPKG

harmony-plugin-manager

Version:

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

734 lines 28.8 kB
/** * Plugin Factory - Advanced plugin creation with specialized capabilities * * The PluginFactory provides factory methods for creating sophisticated plugins * with built-in patterns like retry logic, caching, throttling, and monitoring. * These factories eliminate boilerplate code and provide battle-tested implementations * of common plugin patterns. * * ## Factory Categories * * - **Core**: Basic plugin creation and composition * - **Resilience**: Retry, error handling, and fault tolerance * - **Performance**: Caching, throttling, and batch processing * - **Control Flow**: Conditional execution and middleware patterns * - **Monitoring**: Observability and performance tracking * * @example * ```typescript * // Create a resilient API plugin with retry and caching * const apiPlugin = PluginFactory.withRetry( * 'api-caller', * async (request: ApiRequest) => { * const response = await fetch(request.url, request.options); * return response.json(); * }, * { maxAttempts: 3, backoffMs: 1000 } * ); * * const cachedApiPlugin = PluginFactory.withCache( * 'cached-api', * apiPlugin.execute.bind(apiPlugin), * { keyGenerator: (req) => `${req.method}:${req.url}`, ttlMs: 300000 } * ); * ``` */ import type { IPlugin } from '../core/interfaces'; import type { PluginContext, PluginMetadata } from '../core/types'; /** * Lifecycle hooks for plugin execution * Provides extension points for custom plugin behavior */ export interface PluginHooks<TInput, TOutput, TContext extends PluginContext> { /** Called once during plugin initialization */ initialize?: (context: TContext) => Promise<void> | void; /** Called after plugin execution completes */ cleanup?: (context: TContext) => Promise<void> | void; /** Called when plugin execution fails */ onError?: (error: Error, context: TContext) => Promise<void> | void; } /** * Configuration for conditional plugin execution */ export interface ConditionalOptions { /** Function that determines whether to execute the plugin */ condition: (input: any, context: PluginContext) => boolean | Promise<boolean>; /** Value to return when condition is false (defaults to input) */ skipValue?: any; /** Callback executed when plugin is skipped */ onSkip?: (input: any, context: PluginContext) => void | Promise<void>; } /** * Configuration for retry behavior */ export interface RetryOptions { /** Maximum number of execution attempts */ maxAttempts: number; /** Base delay between retries in milliseconds */ backoffMs?: number; /** Function to determine if error should trigger retry */ retryOn?: (error: Error) => boolean; /** Callback executed before each retry attempt */ onRetry?: (attempt: number, error: Error, context: PluginContext) => void | Promise<void>; } /** * Configuration for caching behavior */ export interface CacheOptions { /** Function to generate cache keys from input */ keyGenerator: (input: any, context: PluginContext) => string; /** Time-to-live for cached entries in milliseconds */ ttlMs?: number; /** Maximum number of entries in cache */ maxSize?: number; } /** * Configuration for throttling behavior */ export interface ThrottleOptions { /** Maximum number of concurrent executions */ maxConcurrent: number; /** Maximum queue size for pending requests */ queueSize?: number; /** Timeout for queued requests in milliseconds */ timeout?: number; } export declare class PluginFactory { /** * Creates a basic plugin with custom execution logic and optional lifecycle hooks * * This is the foundation method for creating any custom plugin. It provides * a clean interface for wrapping your business logic in a plugin container * with proper lifecycle management and error handling. * * @template TInput - Input data type * @template TOutput - Output data type * @template TContext - Plugin context type * * @param metadata - Plugin identification and dependency information * @param execute - Main execution function that processes input data * @param hooks - Optional lifecycle hooks for initialization, cleanup, and error handling * * @returns A fully configured plugin instance * * @example * ```typescript * // Simple synchronous plugin * const upperCasePlugin = PluginFactory.create( * { * name: 'uppercase', * version: '1.0.0', * description: 'Converts text to uppercase' * }, * (input: string) => input.toUpperCase() * ); * ``` * * @example * ```typescript * // Complex async plugin with resource management * const databasePlugin = PluginFactory.create( * { * name: 'database-saver', * version: '2.1.0', * dependencies: ['validator', 'transformer'] * }, * async (data: UserData, context) => { * const db = context.retrieve<Database>('db-connection'); * const result = await db.users.create(data); * context.logger.info(`Created user: ${result.id}`); * return result; * }, * { * initialize: async (context) => { * const db = await createDbConnection(); * context.share('db-connection', db); * }, * cleanup: async (context) => { * const db = context.retrieve('db-connection'); * await db?.close(); * } * } * ); * ``` */ static create<TInput, TOutput, TContext extends PluginContext = PluginContext>(metadata: PluginMetadata, execute: (input: TInput, context: TContext) => Promise<TOutput> | TOutput, hooks?: PluginHooks<TInput, TOutput, TContext>): IPlugin<TInput, TOutput, TContext>; /** * Creates a batch processing plugin for handling arrays of data * * Batch processing is essential for high-throughput scenarios where you need * to process large amounts of data efficiently. This factory handles chunking, * parallel processing, and result aggregation automatically. * * @template TInput - Type of individual items in the input array * @template TOutput - Type of individual items in the output array * @template TContext - Plugin context type * * @param name - Base name for the batch plugin (will be suffixed with '-batch') * @param processor - Function that processes a batch of items * @param options - Configuration for batch behavior * * @returns Plugin that processes arrays of data in configurable batches * * @example * ```typescript * // Process user records in batches for database insertion * const batchInsertPlugin = PluginFactory.batch( * 'user-insert', * async (users: UserData[], context) => { * const db = context.retrieve<Database>('db'); * const results = await db.users.insertMany(users); * context.logger.info(`Inserted ${results.length} users`); * return results; * }, * { * batchSize: 50, // Process 50 users at a time * parallel: false, // Process batches sequentially * description: 'Batch inserts users into database' * } * ); * ``` * * @example * ```typescript * // Parallel image processing with custom batch size * const imageProcessor = PluginFactory.batch( * 'image-resize', * async (images: ImageFile[], context) => { * return Promise.all(images.map(async (img) => { * const resized = await resizeImage(img, { width: 800, height: 600 }); * context.logger.debug(`Resized image: ${img.name}`); * return resized; * })); * }, * { * batchSize: 10, // 10 images per batch * parallel: true, // Process batches in parallel * version: '1.2.0' * } * ); * ``` */ static batch<TInput, TOutput, TContext extends PluginContext = PluginContext>(name: string, processor: (items: TInput[], context: TContext) => Promise<TOutput[]> | TOutput[], options?: { batchSize?: number; parallel?: boolean; version?: string; description?: string; }): IPlugin<TInput[], TOutput[], TContext>; /** * Creates a conditional execution plugin that runs based on runtime conditions * * Conditional plugins provide dynamic control flow within your pipeline. * They're perfect for implementing feature flags, environment-specific logic, * or data-driven processing decisions. * * @template TInput - Input data type * @template TOutput - Output data type * @template TContext - Plugin context type * * @param name - Base name for the conditional plugin * @param processor - Function to execute when condition is true * @param conditionOptions - Configuration for the conditional behavior * @param options - Additional plugin options * * @returns Plugin that conditionally executes based on runtime conditions * * @example * ```typescript * // Only process data in production environment * const prodOnlyPlugin = PluginFactory.conditional( * 'production-processor', * async (data: UserData, context) => { * // Expensive production-only processing * const result = await complexProcessing(data); * context.logger.info(`Processed ${data.id} in production mode`); * return result; * }, * { * condition: (data, context) => { * const settings = context.getSettings('production-processor'); * return settings?.config.environment === 'production'; * }, * onSkip: (data, context) => { * context.logger.debug(`Skipping production processing for ${data.id}`); * } * } * ); * ``` * * @example * ```typescript * // Data-driven conditional processing * const premiumFeaturePlugin = PluginFactory.conditional( * 'premium-features', * async (user: User, context) => { * const premiumFeatures = await loadPremiumFeatures(user.id); * return { ...user, premiumFeatures }; * }, * { * condition: (user) => user.subscriptionType === 'premium', * skipValue: (user) => ({ ...user, premiumFeatures: [] }), * onSkip: (user, context) => { * context.addWarning('PREMIUM_SKIPPED', `User ${user.id} is not premium`); * } * } * ); * ``` */ static conditional<TInput, TOutput, TContext extends PluginContext = PluginContext>(name: string, processor: (input: TInput, context: TContext) => Promise<TOutput> | TOutput, conditionOptions: ConditionalOptions, options?: { version?: string; description?: string; }): IPlugin<TInput, TInput | TOutput, TContext>; /** * Creates a retry wrapper plugin for resilient execution * * Retry plugins add fault tolerance to your pipeline by automatically * retrying failed operations with configurable backoff strategies. * Essential for handling network issues, temporary service outages, * and other transient failures. * * @template TInput - Input data type * @template TOutput - Output data type * @template TContext - Plugin context type * * @param name - Base name for the retry plugin * @param processor - Function to execute with retry logic * @param retryOptions - Configuration for retry behavior * @param options - Additional plugin options * * @returns Plugin that automatically retries failed executions * * @example * ```typescript * // Retry API calls with exponential backoff * const resilientApiPlugin = PluginFactory.withRetry( * 'api-call', * async (request: ApiRequest, context) => { * const response = await fetch(request.url, { * method: request.method, * headers: request.headers, * body: JSON.stringify(request.data) * }); * * if (!response.ok) { * throw new Error(`API call failed: ${response.status}`); * } * * return response.json(); * }, * { * maxAttempts: 3, * backoffMs: 1000, // Start with 1 second, doubles each retry * retryOn: (error) => { * // Only retry on network errors, not on client errors (4xx) * return !error.message.includes('4'); * }, * onRetry: (attempt, error, context) => { * context.logger.warn(`API call failed, attempt ${attempt}: ${error.message}`); * } * } * ); * ``` * * @example * ```typescript * // Database operation with custom retry logic * const dbRetryPlugin = PluginFactory.withRetry( * 'database-write', * async (data: UserData, context) => { * const db = context.retrieve<Database>('database'); * return await db.users.create(data); * }, * { * maxAttempts: 5, * backoffMs: 500, * retryOn: (error) => { * // Retry on connection errors but not on validation errors * return error.message.includes('connection') || * error.message.includes('timeout'); * } * } * ); * ``` */ static withRetry<TInput, TOutput, TContext extends PluginContext = PluginContext>(name: string, processor: (input: TInput, context: TContext) => Promise<TOutput> | TOutput, retryOptions: RetryOptions, options?: { version?: string; description?: string; }): IPlugin<TInput, TOutput, TContext>; /** * Creates a cached plugin wrapper for performance optimization * * Caching plugins store execution results to avoid expensive recomputation. * They implement LRU eviction and TTL expiration for memory management. * Perfect for expensive computations, API calls, or database queries. * * @template TInput - Input data type * @template TOutput - Output data type * @template TContext - Plugin context type * * @param name - Base name for the cached plugin * @param processor - Function to execute with caching * @param cacheOptions - Configuration for cache behavior * @param options - Additional plugin options * * @returns Plugin that caches execution results for performance * * @example * ```typescript * // Cache expensive API responses * const cachedUserPlugin = PluginFactory.withCache( * 'user-lookup', * async (userId: string, context) => { * context.logger.debug(`Fetching user data for: ${userId}`); * const response = await fetch(`/api/users/${userId}`); * return response.json(); * }, * { * keyGenerator: (userId) => `user:${userId}`, * ttlMs: 300000, // Cache for 5 minutes * maxSize: 1000 // Keep up to 1000 users in cache * } * ); * ``` * * @example * ```typescript * // Cache complex calculations with context-aware keys * const calculationPlugin = PluginFactory.withCache( * 'complex-calc', * async (input: CalculationInput, context) => { * // Expensive mathematical computation * const result = await performComplexCalculation(input); * context.logger.info(`Calculated result: ${result.value}`); * return result; * }, * { * keyGenerator: (input, context) => { * const version = context.getSettings('complex-calc')?.config.version || 'v1'; * return `calc:${version}:${JSON.stringify(input)}`; * }, * ttlMs: 3600000, // Cache for 1 hour * maxSize: 500 * } * ); * ``` */ static withCache<TInput, TOutput, TContext extends PluginContext = PluginContext>(name: string, processor: (input: TInput, context: TContext) => Promise<TOutput> | TOutput, cacheOptions: CacheOptions, options?: { version?: string; description?: string; }): IPlugin<TInput, TOutput, TContext>; /** * Creates a throttled plugin wrapper for concurrency control * * Throttling plugins limit concurrent executions to prevent overwhelming * external services or consuming too many system resources. They queue * requests and process them according to the concurrency limits. * * @template TInput - Input data type * @template TOutput - Output data type * @template TContext - Plugin context type * * @param name - Base name for the throttled plugin * @param processor - Function to execute with throttling * @param throttleOptions - Configuration for throttling behavior * @param options - Additional plugin options * * @returns Plugin that limits concurrent executions * * @example * ```typescript * // Limit concurrent database connections * const dbPlugin = PluginFactory.withThrottle( * 'database-query', * async (query: DatabaseQuery, context) => { * const db = context.retrieve<Database>('database'); * const startTime = Date.now(); * * const result = await db.execute(query); * * const duration = Date.now() - startTime; * context.logger.debug(`Query executed in ${duration}ms`); * * return result; * }, * { * maxConcurrent: 5, // Max 5 simultaneous queries * queueSize: 50, // Queue up to 50 pending queries * timeout: 30000 // 30 second timeout for queued requests * } * ); * ``` * * @example * ```typescript * // Rate-limit API calls to external service * const apiThrottlePlugin = PluginFactory.withThrottle( * 'external-api', * async (request: ApiRequest, context) => { * context.logger.debug(`Making API call to: ${request.endpoint}`); * * const response = await fetch(request.url, { * method: request.method, * headers: { 'Authorization': `Bearer ${request.token}` }, * body: JSON.stringify(request.data) * }); * * if (!response.ok) { * throw new Error(`API error: ${response.status}`); * } * * return response.json(); * }, * { * maxConcurrent: 10, // 10 concurrent API calls max * queueSize: 100, // Queue up to 100 requests * timeout: 60000 // 1 minute timeout * } * ); * ``` */ static withThrottle<TInput, TOutput, TContext extends PluginContext = PluginContext>(name: string, processor: (input: TInput, context: TContext) => Promise<TOutput> | TOutput, throttleOptions: ThrottleOptions, options?: { version?: string; description?: string; }): IPlugin<TInput, TOutput, TContext>; /** * Creates a pipeline composition plugin that chains multiple plugins * * Composition plugins allow you to create higher-order plugins by chaining * multiple existing plugins together. The output of one plugin becomes the * input of the next, creating a processing pipeline. * * @example * ```typescript * const textProcessor = PluginFactory.compose( * 'text-pipeline', * [trimPlugin, uppercasePlugin, validatePlugin], * { * description: 'Complete text processing pipeline', * failFast: true // Stop on first error * } * ); * ``` */ static compose<TInput, TOutput, TContext extends PluginContext = PluginContext>(name: string, plugins: Array<IPlugin<any, any, TContext>>, options?: { version?: string; description?: string; failFast?: boolean; }): IPlugin<TInput, TOutput, TContext>; /** * Creates a parallel execution plugin that runs multiple plugins concurrently * * Parallel plugins execute multiple plugins simultaneously on the same input * and can aggregate their results. Useful for scenarios where you need to * perform multiple independent operations on the same data. * * @example * ```typescript * const multiValidatorPlugin = PluginFactory.parallel( * 'multi-validator', * [schemaValidator, businessRuleValidator, securityValidator], * { * failFast: false, // Continue even if some validators fail * aggregator: (results) => ({ * isValid: results.every(r => r.isValid), * errors: results.flatMap(r => r.errors || []) * }) * } * ); * ``` */ static parallel<TInput, TOutput, TContext extends PluginContext = PluginContext>(name: string, plugins: Array<IPlugin<TInput, TOutput, TContext>>, options?: { version?: string; description?: string; aggregator?: (results: TOutput[]) => TOutput; failFast?: boolean; }): IPlugin<TInput, TOutput | TOutput[], TContext>; /** * Creates a validation plugin for input/output verification * * Validation plugins provide data integrity checking with configurable * error handling. They can either fail fast or continue with warnings * based on configuration. * * @example * ```typescript * const userValidator = PluginFactory.validation( * 'user-data', * async (userData: UserData, context) => { * if (!userData.email || !userData.email.includes('@')) { * throw new Error('Invalid email format'); * } * if (userData.age < 0 || userData.age > 120) { * throw new Error('Invalid age range'); * } * return userData; // Return validated data * }, * { * stopOnError: false // Continue with warnings instead of failing * } * ); * ``` */ static validation<TInput, TOutput, TContext extends PluginContext = PluginContext>(name: string, validator: (input: TInput, context: TContext) => Promise<TOutput> | TOutput, options?: { version?: string; description?: string; stopOnError?: boolean; }): IPlugin<TInput, TOutput, TContext>; /** * Creates a transformation plugin for data manipulation * * Transformation plugins focus on data conversion and manipulation. * They provide a clean interface for data processing operations. * * @example * ```typescript * const userTransformer = PluginFactory.transform( * 'user-normalizer', * async (userData: RawUserData, context) => { * return { * id: userData.user_id, * name: userData.full_name.trim(), * email: userData.email_address.toLowerCase(), * createdAt: new Date(userData.created_timestamp) * }; * }, * { * dependencies: ['user-validator'], * description: 'Normalizes user data structure' * } * ); * ``` */ static transform<TInput, TOutput, TContext extends PluginContext = PluginContext>(name: string, transformer: (input: TInput, context: TContext) => Promise<TOutput> | TOutput, options?: { version?: string; description?: string; dependencies?: string[]; }): IPlugin<TInput, TOutput, TContext>; /** * Creates a middleware plugin for request/response processing * * Middleware plugins provide intercept-style processing where you can * modify data before and after the main processing occurs. * * @example * ```typescript * const loggingMiddleware = PluginFactory.middleware( * 'request-logger', * async (data: ApiRequest, context, next) => { * const startTime = Date.now(); * context.logger.info(`Processing request: ${data.method} ${data.url}`); * * const result = await next(); * * const duration = Date.now() - startTime; * context.logger.info(`Request completed in ${duration}ms`); * * return result; * } * ); * ``` */ static middleware<TData, TContext extends PluginContext = PluginContext>(name: string, middleware: (data: TData, context: TContext, next: () => Promise<TData>) => Promise<TData>, options?: { version?: string; description?: string; }): IPlugin<TData, TData, TContext>; /** * Creates a monitoring wrapper plugin for observability * * Monitoring plugins provide detailed execution metrics, timing information, * and custom callbacks for tracking plugin performance and behavior. * Essential for production systems where you need visibility into plugin execution. * * @example * ```typescript * const monitoredPlugin = PluginFactory.withMonitoring( * 'critical-processor', * async (data: CriticalData, context) => { * // Important business logic * return await processCriticalData(data); * }, * { * onStart: (input, context) => { * context.logger.info('Starting critical processing', { inputId: input.id }); * }, * onSuccess: (output, duration, context) => { * if (duration > 5000) { * context.addWarning('SLOW_PROCESSING', `Processing took ${duration}ms`); * } * // Send metrics to monitoring system * metrics.timing('critical_processor.duration', duration); * }, * onError: (error, duration, context) => { * // Alert on critical failures * alerting.sendAlert('Critical processor failed', error); * } * } * ); * ``` */ static withMonitoring<TInput, TOutput, TContext extends PluginContext = PluginContext>(name: string, processor: (input: TInput, context: TContext) => Promise<TOutput> | TOutput, options?: { version?: string; description?: string; onStart?: (input: TInput, context: TContext) => void | Promise<void>; onSuccess?: (output: TOutput, duration: number, context: TContext) => void | Promise<void>; onError?: (error: Error, duration: number, context: TContext) => void | Promise<void>; }): IPlugin<TInput, TOutput, TContext>; /** * Creates a circuit breaker plugin for fault tolerance * * Circuit breaker plugins protect against cascading failures by monitoring * error rates and temporarily disabling execution when failures exceed thresholds. * * @example * ```typescript * const protectedApiPlugin = PluginFactory.withCircuitBreaker( * 'external-api', * async (request: ApiRequest, context) => { * const response = await fetch(request.url); * return response.json(); * }, * { * failureThreshold: 5, // Open circuit after 5 failures * resetTimeoutMs: 60000, // Try again after 1 minute * monitoringWindowMs: 30000 // Monitor failures over 30 seconds * } * ); * ``` */ static withCircuitBreaker<TInput, TOutput, TContext extends PluginContext = PluginContext>(name: string, processor: (input: TInput, context: TContext) => Promise<TOutput> | TOutput, options: { failureThreshold: number; resetTimeoutMs: number; monitoringWindowMs?: number; version?: string; description?: string; }): IPlugin<TInput, TOutput, TContext>; /** * Creates a rate limiting plugin * * Rate limiting plugins control the frequency of plugin execution to * prevent overwhelming downstream systems or exceeding API limits. * * @example * ```typescript * const rateLimitedPlugin = PluginFactory.withRateLimit( * 'api-caller', * async (request: ApiRequest, context) => { * return await callExternalAPI(request); * }, * { * requestsPerSecond: 10, * burstSize: 20, * queueSize: 100 * } * ); * ``` */ static withRateLimit<TInput, TOutput, TContext extends PluginContext = PluginContext>(name: string, processor: (input: TInput, context: TContext) => Promise<TOutput> | TOutput, options: { requestsPerSecond: number; burstSize?: number; queueSize?: number; version?: string; description?: string; }): IPlugin<TInput, TOutput, TContext>; } //# sourceMappingURL=plugin.factory.d.ts.map