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