UNPKG

harmony-plugin-manager

Version:

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

1,433 lines (1,422 loc) 167 kB
/*! * harmony-plugin-manager v1.0.0 * A comprehensive TypeScript library for generating harmonious color palettes with WCAG 2.1 accessibility compliance * * Copyright (c) 2025 Khaled Sameer <khaled.smq@hotmail.com> * Licensed under MIT * * Repository: https://github.com/KhaledSMQ/harmony-plugin-manager.git * * Built: 2025-06-13T17:20:53.178Z */ /** * Plugin Type/** * Plugin Type Definitions * * Core types for plugin identification and configuration. These types form * the foundation of the plugin system's type safety and provide the structure * for plugin metadata, settings, and runtime configuration. * * ## Key Concepts * * - **PluginMetadata**: Immutable plugin identity and relationship information * - **PluginSettings**: Mutable runtime configuration and execution control * - **Type Safety**: Compile-time validation of plugin configurations * - **Semantic Versioning**: Standard version management for compatibility * * ## Usage Examples * * ### Basic Plugin Metadata * ```typescript * const metadata: PluginMetadata = { * name: 'user-validator', * version: '1.2.3', * description: 'Validates user data according to business rules' * }; * ``` * * ### Plugin with Dependencies * ```typescript * const metadata: PluginMetadata = { * name: 'user-enricher', * version: '2.1.0', * description: 'Enriches user data with external information', * dependencies: ['user-validator', 'cache-manager'], * tags: ['enrichment', 'user-management', 'external-api'] * }; * ``` * * ### Runtime Settings Configuration * ```typescript * const settings: PluginSettings = { * enabled: true, * priority: 50, * config: { * apiUrl: 'https://api.example.com', * timeout: 5000, * retryAttempts: 3, * enableCaching: true * } * }; * ``` // ═══════════════════════════════════════════════════════════════════════════════════════════ // Plugin Metadata Types // ═══════════════════════════════════════════════════════════════════════════════════════════ /** * Plugin metadata for identification and dependency management * * Metadata provides immutable information about a plugin's identity, capabilities, * and relationships with other plugins. This information is used throughout the * plugin system for registration, dependency resolution, and documentation. * * @example * ```typescript * // Simple plugin metadata * const basicMetadata: PluginMetadata = { * name: 'text-formatter', * version: '1.0.0', * description: 'Formats text according to specified rules' * }; * ``` * * @example * ```typescript * // Complex plugin with dependencies and tags * const advancedMetadata: PluginMetadata = { * name: 'user-notification-sender', * version: '2.3.1', * description: 'Sends notifications to users via multiple channels', * dependencies: [ * 'user-validator', // Must run after user validation * 'notification-formatter', // Must run after formatting * 'rate-limiter' // Must respect rate limits * ], * tags: [ * 'notification', // Functional category * 'user-management', // Domain area * 'external-service', // Integration type * 'async-processing' // Processing characteristic * ] * }; * ``` * * @example * ```typescript * // Plugin metadata for different environments * const createMetadata = (environment: string): PluginMetadata => ({ * name: `analytics-${environment}`, * version: '1.5.0', * description: `Analytics plugin configured for ${environment}`, * dependencies: environment === 'production' * ? ['data-validator', 'performance-monitor'] * : ['data-validator'], * tags: [ * 'analytics', * 'data-processing', * environment * ] * }); * * const prodMetadata = createMetadata('production'); * const devMetadata = createMetadata('development'); * ``` */ interface PluginMetadata { /** * Unique plugin identifier within the manager * * The name serves as the primary identifier for the plugin and must be * unique within a single plugin manager instance. It's used for dependency * references, configuration lookup, and error reporting. * * **Naming Guidelines:** * - Use kebab-case (lowercase with hyphens) * - Be descriptive but concise * - Include functional purpose * - Avoid generic names like "plugin" or "processor" * * @example * ```typescript * // Good names * name: 'user-validator' * name: 'payment-processor' * name: 'email-notification-sender' * name: 'data-encryption-handler' * * // Avoid * name: 'plugin1' * name: 'processor' * name: 'handler' * ``` */ readonly name: string; /** * Semantic version string following semver specification * * Version information is used for compatibility checking, dependency * resolution, and change management. Must follow semantic versioning * format: MAJOR.MINOR.PATCH with optional pre-release and build metadata. * * **Version Guidelines:** * - MAJOR: Breaking changes to plugin interface * - MINOR: New features, backward compatible * - PATCH: Bug fixes, backward compatible * - Use pre-release for beta/alpha versions * * @example * ```typescript * // Standard versions * version: '1.0.0' // Initial stable release * version: '1.2.3' // Patch update * version: '2.0.0' // Major breaking change * * // Pre-release versions * version: '1.0.0-alpha.1' // Alpha release * version: '1.0.0-beta.2' // Beta release * version: '1.0.0-rc.1' // Release candidate * * // With build metadata * version: '1.0.0+20231201' // With build identifier * ``` */ readonly version: string; /** * Human-readable description of plugin functionality * * Provides clear, concise documentation of what the plugin does. * Used for documentation generation, plugin catalogs, and developer * understanding. Should focus on the plugin's purpose and value. * * @example * ```typescript * // Good descriptions * description: 'Validates user input against defined schema rules' * description: 'Processes payment transactions through secure gateway' * description: 'Sends email notifications with templating support' * description: 'Compresses and optimizes image files for web delivery' * * // More detailed description * description: 'Advanced user authentication plugin supporting OAuth2, SAML, and JWT tokens with automatic session management and security monitoring' * ``` */ readonly description?: string; /** * Array of plugin names this plugin depends on * * Dependencies define the execution order requirements for this plugin. * All listed dependencies must be registered and enabled for this plugin * to execute. Dependencies are resolved through topological sorting. * * **Dependency Guidelines:** * - List only direct dependencies * - Use exact plugin names as registered * - Keep dependency chains minimal * - Avoid circular dependencies * - Consider optional vs required dependencies * * @example * ```typescript * // Simple dependency chain * // validation -> transformation -> output * * // Validation plugin (no dependencies) * dependencies: undefined * * // Transformation plugin (depends on validation) * dependencies: ['input-validator'] * * // Output plugin (depends on transformation) * dependencies: ['data-transformer'] * * // Complex plugin with multiple dependencies * dependencies: [ * 'authentication-handler', // Must authenticate first * 'rate-limiter', // Must check rate limits * 'input-sanitizer', // Must sanitize input * 'cache-manager' // Must initialize cache * ] * ``` * * @example * ```typescript * // Conditional dependencies based on configuration * const createDependencies = (config: PluginConfig): string[] => { * const deps = ['core-validator']; * * if (config.enableCaching) { * deps.push('cache-manager'); * } * * if (config.enableAnalytics) { * deps.push('analytics-collector'); * } * * return deps; * }; * * dependencies: createDependencies(pluginConfig) * ``` */ readonly dependencies?: readonly string[] | undefined; /** * Categorical tags for organization and filtering * * Tags provide a flexible way to categorize and group plugins for * management, filtering, and organizational purposes. They enable * querying plugins by functionality, domain, or characteristics. * * **Tag Categories:** * - **Functional**: validation, transformation, output, monitoring * - **Domain**: user-management, payment, notification, security * - **Technical**: async, caching, external-api, database * - **Environment**: development, production, testing * - **Performance**: high-priority, background, real-time * * @example * ```typescript * // Functional categorization * tags: ['validation', 'input-processing', 'security'] * * // Domain-based tagging * tags: ['user-management', 'authentication', 'session-handling'] * * // Technical characteristics * tags: ['async-processing', 'external-api', 'caching', 'retry-logic'] * * // Multi-dimensional tagging * tags: [ * 'payment', // Domain * 'transaction', // Function * 'external-service', // Integration * 'high-priority', // Performance * 'production-ready' // Maturity * ] * ``` * * @example * ```typescript * // Environment-specific tags * const createTags = (environment: string): string[] => { * const baseTags = ['data-processing', 'transformation']; * * if (environment === 'development') { * return [...baseTags, 'debug', 'verbose-logging']; * } else if (environment === 'production') { * return [...baseTags, 'optimized', 'monitoring']; * } * * return baseTags; * }; * * tags: createTags(process.env.NODE_ENV) * ``` */ readonly tags?: readonly string[]; } /** * Runtime configuration settings for plugin execution * * Settings control how plugins behave during execution and can be modified * between registrations and builds. They provide runtime flexibility while * maintaining type safety and validation. * * @example * ```typescript * // Basic settings * const settings: PluginSettings = { * enabled: true, * priority: 50, * config: {} * }; * ``` * * @example * ```typescript * // Detailed configuration for API plugin * const apiSettings: PluginSettings = { * enabled: true, * priority: 75, * config: { * apiUrl: 'https://api.example.com/v1', * timeout: 5000, * retryAttempts: 3, * retryDelay: 1000, * enableCaching: true, * cacheSize: 100, * cacheTtl: 300000, * headers: { * 'User-Agent': 'MyApp/1.0', * 'Accept': 'application/json' * }, * authentication: { * type: 'bearer', * tokenProvider: 'oauth2' * } * } * }; * ``` * * @example * ```typescript * // Environment-specific settings * const createSettings = (env: string): PluginSettings => ({ * enabled: true, * priority: env === 'production' ? 100 : 50, * config: { * logLevel: env === 'development' ? 'debug' : 'info', * enableMetrics: env === 'production', * batchSize: env === 'production' ? 100 : 10, * timeout: env === 'production' ? 30000 : 10000 * } * }); * ``` */ interface PluginSettings { /** * Whether the plugin is enabled for execution * * Controls plugin participation in the execution pipeline. Disabled * plugins are registered but skipped during execution. This allows * for dynamic plugin activation based on runtime conditions. * * **Use Cases:** * - Feature flags and A/B testing * - Environment-specific activation * - Conditional feature enablement * - Maintenance mode handling * - User preference-based activation * * @example * ```typescript * // Always enabled * enabled: true * * // Environment-based enabling * enabled: process.env.NODE_ENV === 'production' * * // Feature flag-based enabling * enabled: featureFlags.enableAdvancedProcessing * * // User tier-based enabling * enabled: userTier === 'premium' || userTier === 'enterprise' * * // Configuration-driven enabling * enabled: config.features.includes('data-enrichment') * ``` */ readonly enabled: boolean; /** * Execution priority determining plugin order within dependency levels * * Higher priority values execute before lower priority values within * the same dependency level. Priority doesn't override dependencies * but provides fine-grained control over execution order for plugins * at the same dependency depth. * * **Priority Guidelines:** * - Use ranges for logical grouping (100-90: critical, 50-40: normal, 10-0: cleanup) * - Leave gaps for future insertions (use 100, 90, 80 instead of 100, 99, 98) * - Consider functional priority over arbitrary numbers * - Document priority ranges in your application * * @example * ```typescript * // Priority ranges example: * * // Critical security and validation (100-90) * priority: 100 // Authentication * priority: 95 // Authorization * priority: 90 // Rate limiting * * // Core business logic (80-60) * priority: 80 // Input validation * priority: 70 // Data transformation * priority: 60 // Business rules * * // Data processing (50-30) * priority: 50 // Enrichment * priority: 40 // Calculation * priority: 30 // Aggregation * * // Output and cleanup (20-0) * priority: 20 // Response formatting * priority: 10 // Compression * priority: 0 // Cleanup * ``` * * @example * ```typescript * // Dynamic priority based on configuration * const getPriority = (pluginType: string, config: AppConfig): number => { * const basePriorities = { * 'security': 100, * 'validation': 80, * 'processing': 50, * 'output': 20 * }; * * const base = basePriorities[pluginType] || 50; * * // Adjust based on configuration * if (config.enableHighPriorityProcessing) { * return base + 10; * } * * return base; * }; * * priority: getPriority('validation', appConfig) * ``` */ readonly priority: number; /** * Plugin-specific configuration object * * Provides flexible, type-safe configuration storage for plugin-specific * settings. The structure is defined by individual plugins and can contain * any serializable data needed for plugin operation. * * **Configuration Guidelines:** * - Use clear, descriptive property names * - Provide sensible defaults in plugin code * - Validate configuration in plugin initialization * - Document expected configuration structure * - Consider environment-specific configurations * * @example * ```typescript * // Database plugin configuration * config: { * connectionString: 'postgresql://user:pass@localhost:5432/db', * poolSize: 10, * timeout: 30000, * retryAttempts: 3, * enableSSL: true, * migrationPath: './migrations', * queryLogging: process.env.NODE_ENV === 'development' * } * ``` * * @example * ```typescript * // API client configuration * config: { * baseUrl: 'https://api.example.com', * apiKey: process.env.API_KEY, * timeout: 5000, * retryConfig: { * attempts: 3, * delay: 1000, * backoff: 'exponential' * }, * headers: { * 'User-Agent': 'MyApp/1.0.0', * 'Accept': 'application/json' * }, * rateLimiting: { * requestsPerSecond: 10, * burstSize: 20 * } * } * ``` * * @example * ```typescript * // Processing pipeline configuration * config: { * batchSize: 50, * parallelProcessing: true, * maxConcurrency: 5, * processingRules: [ * { field: 'email', validation: 'email', required: true }, * { field: 'age', validation: 'number', min: 0, max: 120 }, * { field: 'country', validation: 'string', allowedValues: ['US', 'CA', 'UK'] } * ], * outputFormat: 'json', * compressionEnabled: true, * errorHandling: { * strategy: 'continue', * maxErrors: 10, * logErrors: true * } * } * ``` * * @example * ```typescript * // Environment-aware configuration * config: { * ...baseConfig, * ...(process.env.NODE_ENV === 'production' ? { * logLevel: 'warn', * enableMetrics: true, * cacheEnabled: true, * performanceMode: 'optimized' * } : { * logLevel: 'debug', * enableMetrics: false, * cacheEnabled: false, * performanceMode: 'development' * }) * } * ``` */ readonly config: Record<string, unknown>; } /*! * harmony-pipeline v1.0.1 * A robust TypeScript pipeline execution library with stage-based processing, dependency resolution, and comprehensive error handling * * Copyright (c) 2025 Khaled Sameer <khaled.smq@hotmail.com> * Licensed under MIT * * Repository: https://github.com/KhaledSMQ/harmony-pipeline.git * * Built: 2025-06-12T11:21:14.416Z */ /** * Logging interface for pipeline operations. * Provides structured logging capabilities with optional metadata attachment. */ interface Logger { /** * Log debug information for development and troubleshooting. * @param message - Human-readable debug message * @param data - Optional structured data for context */ debug(message: string, data?: unknown): void; /** * Log informational messages about normal operations. * @param message - Human-readable information message * @param data - Optional structured data for context */ info(message: string, data?: unknown): void; /** * Log warning messages for recoverable issues. * @param message - Human-readable warning message * @param data - Optional structured data for context */ warn(message: string, data?: unknown): void; /** * Log error messages for critical issues. * @param message - Human-readable error message * @param data - Optional structured data for context */ error(message: string, data?: unknown): void; } /** * Represents a non-fatal warning emitted during pipeline execution. * Warnings allow processors to report issues without stopping execution. */ interface PipelineWarning { /** Unique identifier for the warning type */ readonly code: string; /** Human-readable description of the warning */ readonly message: string; /** Optional structured data providing additional context */ readonly details?: unknown; /** Timestamp when the warning was created (milliseconds since epoch) */ readonly timestamp: number; } /** * Execution context shared across all processors in a pipeline run. * Provides access to metadata, logging, and warning collection. * * @template M - Shape of the metadata object for type safety */ interface PipelineContext<M extends Record<string, unknown> = Record<string, unknown>> { /** Unique identifier for this pipeline execution */ readonly executionId: string; /** Timestamp when the pipeline execution started */ readonly startTime: number; /** Immutable metadata available to all processors */ readonly metadata: Readonly<M>; /** Logger instance bound to this pipeline execution */ readonly logger: Logger; /** * Records a non-fatal warning without stopping execution. * The warning is logged and stored for inclusion in the final result. * * @param code - Unique identifier for the warning type * @param message - Human-readable description * @param details - Optional structured data for additional context */ addWarning(code: string, message: string, details?: unknown): void; /** * @internal * Returns all warnings collected during execution. * This method is intended for internal use by the pipeline executor. */ _getWarnings(): ReadonlyArray<PipelineWarning>; } /** * Represents a stage in the pipeline execution graph. * Stages form a Directed Acyclic Graph (DAG) where dependencies define execution order. * Each stage can contain multiple processors that execute within that stage. * * @template TCtx - Type of the pipeline context */ interface PipelineStage<TCtx extends PipelineContext = PipelineContext> { /** Unique name identifying this stage */ readonly name: string; /** * Names of other stages that must complete before this stage can execute. * Used to build the execution DAG and ensure proper ordering. */ readonly dependencies?: ReadonlyArray<string>; /** * Optional predicate to determine if this stage should execute. * When this function returns false, the stage is skipped entirely. * Useful for conditional execution based on context or metadata. * * @param context - The current pipeline execution context * @returns true if the stage should execute, false to skip */ canExecute(context: TCtx): boolean; } /** * Configuration options for creating a pipeline stage. * * @template TCtx - Type of the pipeline context */ interface StageOptions<TCtx extends PipelineContext = PipelineContext> { /** Names of stages that must complete before this stage can run */ dependencies?: ReadonlyArray<string>; /** Optional predicate to conditionally execute this stage */ canExecute?(context: TCtx): boolean; } /** * Creates a new pipeline stage with the specified name and options. * This is a convenience function for creating stage objects without * implementing the interface directly. * * @template TCtx - Type of the pipeline context * @param name - Unique name for the stage * @param options - Configuration options for the stage * @returns A new pipeline stage instance * * @example * ```typescript * const validationStage = createStage('validation', { * dependencies: ['preprocessing'], * canExecute: (ctx) => ctx.metadata.enableValidation * }); * ``` */ declare function createStage<TCtx extends PipelineContext = PipelineContext>(name: string, options?: StageOptions<TCtx>): PipelineStage<TCtx>; /** * A processor performs a single unit of work within a stage. */ interface PipelineProcessor<I = unknown, O = unknown, TCtx extends PipelineContext = PipelineContext> { readonly name: string; /** Semantic‑version for change tracking. */ readonly version: `${number}.${number}.${number}`; readonly stage: PipelineStage<TCtx>; /** Unit of work. May be async. Honour the AbortSignal. */ process(input: I, context: TCtx, signal?: AbortSignal): O | Promise<O>; setup?(context: TCtx): void | Promise<void>; teardown?(context: TCtx): void | Promise<void>; onError?(error: Error, context: TCtx): void | Promise<void>; } /** * Configuration options that control pipeline execution behavior. * These options provide fine-grained control over how the pipeline runs, * including error handling, concurrency, timeouts, and logging. */ interface ExecutionOptions { /** * Whether to stop pipeline execution immediately when any processor fails. * When true, the first error stops the entire pipeline. * When false, the pipeline continues executing remaining stages even after errors. * * @default true */ readonly stopOnError?: boolean | undefined; /** * Maximum number of processors that can execute concurrently within a single stage. * Values greater than 1 only make sense when processors within a stage are independent. * Use with caution as concurrent execution may complicate error handling and debugging. * * @default 1 */ readonly concurrency?: number | undefined; /** * AbortSignal for externally cancelling the pipeline execution. * When the signal is aborted, the pipeline will stop processing and reject with an error. * This allows integration with user cancellation, request timeouts, etc. */ readonly signal?: AbortSignal | undefined; /** * Maximum time in milliseconds the pipeline is allowed to run. * When exceeded, the pipeline execution is cancelled and rejects with a timeout error. * Set to undefined to disable timeout (pipeline runs until completion or error). * * @default undefined (no timeout) */ readonly timeoutMs?: number | undefined; /** * Logger instance for recording pipeline execution events. * The logger receives debug, info, warning, and error messages throughout execution. * Use NullLogger to disable logging entirely. * * @default undefined (uses NullLogger internally) */ readonly logger?: Logger | undefined; } /** * Plugin Context Type Definitions * * The PluginContext extends the base pipeline context with plugin-specific features * for managing execution state, shared data, and plugin settings. It serves as the * primary communication interface between the plugin system and individual plugins. * * ## Key Concepts * * - **Execution Context**: Provides execution metadata, timing, and unique identification * - **Shared State**: Enables secure data sharing between plugins in the same execution * - **Settings Access**: Gives plugins access to their configuration and other plugin settings * - **Logging Integration**: Provides structured logging with execution context * - **Warning Management**: Allows plugins to add non-fatal warnings to execution results * * ## Context Lifecycle * * 1. **Creation**: Context is created for each execution with fresh state * 2. **Initialization**: Plugins can share initial data during setup * 3. **Execution**: Plugins access settings, log events, and share data * 4. **Cleanup**: Context is disposed after execution completes * * ## Usage Examples * * ### Basic Context Usage * ```typescript * async execute(input: UserData, context: PluginContext): Promise<ProcessedUser> { * // Access plugin settings * const settings = context.getSettings(this.metadata.name); * const apiUrl = settings?.config.apiUrl as string; * * // Log with context * context.logger.info(`Processing user: ${input.id}`, { * executionId: context.executionId, * startTime: context.startTime * }); * * // Process data * const result = await processUser(input, apiUrl); * * // Share results for other plugins * context.share(`processed-user-${input.id}`, result); * * return result; * } * ``` * * ### Advanced Shared State Management * ```typescript * async execute(input: BatchData, context: PluginContext): Promise<BatchResult> { * // Check if database connection exists from previous plugin * let db = context.retrieve<Database>('database-connection'); * * if (!db) { * // Create and share connection for other plugins * db = await createDatabaseConnection(); * context.share('database-connection', db); * context.logger.info('Database connection created and shared'); * } * * // Access shared cache from caching plugin * const cache = context.retrieve<Map<string, any>>('shared-cache'); * * // Process with shared resources * const results = await processBatch(input, db, cache); * * // Update shared statistics * const stats = context.retrieve<ProcessingStats>('batch-stats') || { * totalProcessed: 0, * totalErrors: 0 * }; * stats.totalProcessed += results.length; * context.share('batch-stats', stats); * * return results; * } * ``` * * ### Cross-Plugin Communication * ```typescript * // Plugin A - Data Collector * async execute(input: CollectionRequest, context: PluginContext): Promise<CollectedData> { * const data = await collectData(input); * * // Share collected data for downstream plugins * context.share('collected-data', data); * context.share('collection-metadata', { * timestamp: Date.now(), * source: input.source, * recordCount: data.length * }); * * return data; * } * * // Plugin B - Data Processor (depends on Plugin A) * async execute(input: CollectedData, context: PluginContext): Promise<ProcessedData> { * // Retrieve metadata from previous plugin * const metadata = context.retrieve<CollectionMetadata>('collection-metadata'); * * if (!metadata) { * context.addWarning('MISSING_METADATA', 'Collection metadata not available'); * } * * // Process with context awareness * const processed = await processData(input, { * timestamp: metadata?.timestamp, * optimizeFor: metadata?.recordCount > 1000 ? 'batch' : 'single' * }); * * return processed; * } * ``` */ /** * Plugin execution context with shared state and settings access * * The PluginContext provides plugins with everything they need for execution: * access to configuration, logging facilities, shared state management, and * execution metadata. It ensures type safety while providing flexibility * for complex plugin interactions. * * @template T - Type of execution metadata provided by the caller * * @example * ```typescript * // Basic plugin with context usage * class UserValidatorPlugin extends BasePlugin<UserData, ValidatedUser> { * async execute(input: UserData, context: PluginContext): Promise<ValidatedUser> { * // Access plugin-specific settings * const settings = context.getSettings(this.metadata.name); * const strictMode = settings?.config.strictMode as boolean; * * // Log with execution context * context.logger.info(`Validating user: ${input.id}`, { * executionId: context.executionId, * strictMode * }); * * // Validate based on settings * const result = await validateUser(input, { strict: strictMode }); * * // Share validation results for other plugins * context.share(`validation-result-${input.id}`, { * isValid: result.isValid, * errors: result.errors, * timestamp: Date.now() * }); * * if (!result.isValid && strictMode) { * throw new Error(`Strict validation failed: ${result.errors.join(', ')}`); * } * * return result.user; * } * } * ``` * * @example * ```typescript * // Advanced context usage with metadata typing * interface ApiExecutionMetadata { * requestId: string; * userId: string; * clientIp: string; * userAgent: string; * } * * class ApiRateLimiterPlugin extends BasePlugin<ApiRequest, ApiRequest, PluginContext<ApiExecutionMetadata>> { * async execute(input: ApiRequest, context: PluginContext<ApiExecutionMetadata>): Promise<ApiRequest> { * // Access typed execution metadata * const { requestId, userId, clientIp } = context.metadata; * * // Check rate limits using shared state * const rateLimiter = context.retrieve<RateLimiter>('rate-limiter') || new RateLimiter(); * * const isAllowed = await rateLimiter.checkLimit(userId, clientIp); * * if (!isAllowed) { * context.addWarning('RATE_LIMIT_EXCEEDED', * `Rate limit exceeded for user ${userId}`, { * requestId, * clientIp, * userId * }); * throw new Error('Rate limit exceeded'); * } * * // Update shared rate limiter state * context.share('rate-limiter', rateLimiter); * * return input; * } * } * ``` */ interface PluginContext<T extends Record<string, unknown> = Record<string, unknown>> extends PipelineContext<T> { /** * Read-only access to all plugin settings by name * * Provides access to the configuration settings for all registered plugins * in the current manager. This allows plugins to access not only their own * settings but also coordinate with other plugins based on their configuration. * * **Security Note**: Settings are read-only to prevent plugins from modifying * each other's configuration during execution. * * @example * ```typescript * async execute(input: Data, context: PluginContext): Promise<ProcessedData> { * // Access own settings * const mySettings = context.settings.get(this.metadata.name); * const timeout = mySettings?.config.timeout as number || 5000; * * // Check if caching plugin is enabled * const cacheSettings = context.settings.get('cache-plugin'); * const isCacheEnabled = cacheSettings?.enabled || false; * * // Coordinate behavior based on other plugin settings * if (isCacheEnabled) { * const cacheKey = `processed:${input.id}`; * const cached = context.retrieve(cacheKey); * if (cached) return cached; * } * * const result = await processData(input, { timeout }); * * if (isCacheEnabled) { * context.share(`processed:${input.id}`, result); * } * * return result; * } * ``` */ readonly settings: ReadonlyMap<string, PluginSettings>; /** * Shared state map for inter-plugin communication * * Provides a secure mechanism for plugins to share data within a single * execution context. The shared state is isolated per execution and * automatically cleaned up when execution completes. * * **Use Cases**: * - Database connections and resources * - Cached computation results * - Processing statistics and metrics * - Configuration derived from multiple plugins * - Cross-plugin coordination data * * **Best Practices**: * - Use descriptive keys with plugin namespacing * - Store only serializable data when possible * - Clean up large objects in plugin cleanup hooks * - Use typed retrieval for better type safety * * @example * ```typescript * // Database plugin shares connection * async initialize(context: PluginContext): Promise<void> { * const db = await createDatabaseConnection(); * context.shared.set('database-connection', db); * context.shared.set('connection-pool-stats', { * activeConnections: 1, * maxConnections: 10, * createdAt: Date.now() * }); * } * * // Another plugin uses shared connection * async execute(input: UserData, context: PluginContext): Promise<SavedUser> { * const db = context.shared.get('database-connection') as Database; * if (!db) { * throw new Error('Database connection not available'); * } * * const result = await db.users.save(input); * * // Update shared statistics * const stats = context.shared.get('connection-pool-stats') as any; * stats.totalQueries = (stats.totalQueries || 0) + 1; * * return result; * } * ``` */ readonly shared: Map<string, unknown>; /** * Retrieves settings for a specific plugin * * Convenient method to access plugin settings with proper typing and * null safety. Preferred over direct settings map access for better * error handling and debugging. * * @param pluginName - Name of plugin to get settings for * @returns Plugin settings or undefined if plugin not found or has no settings * * @example * ```typescript * async execute(input: ApiRequest, context: PluginContext): Promise<ApiResponse> { * // Get own settings with error handling * const settings = context.getSettings(this.metadata.name); * if (!settings) { * throw new Error(`No settings found for plugin: ${this.metadata.name}`); * } * * // Extract typed configuration * const config = settings.config as { * apiUrl: string; * timeout: number; * retryAttempts: number; * }; * * // Check dependencies' settings for coordination * const authSettings = context.getSettings('auth-plugin'); * const requiresAuth = authSettings?.enabled && authSettings?.config.enforceAuth; * * // Adjust behavior based on settings * if (requiresAuth) { * const token = context.retrieve<string>('auth-token'); * if (!token) { * throw new Error('Authentication required but no token available'); * } * } * * return await this.callApi(input, config); * } * ``` * * @example * ```typescript * // Settings-driven feature flags * async execute(input: UserData, context: PluginContext): Promise<ProcessedUser> { * const settings = context.getSettings(this.metadata.name); * const features = settings?.config.enabledFeatures as string[] || []; * * let result = { ...input }; * * // Conditional processing based on settings * if (features.includes('email-validation')) { * result = await this.validateEmail(result, context); * } * * if (features.includes('data-enrichment')) { * const enrichmentSettings = context.getSettings('enrichment-plugin'); * if (enrichmentSettings?.enabled) { * result = await this.enrichUserData(result, context); * } * } * * if (features.includes('audit-logging')) { * const auditData = { * userId: input.id, * action: 'user-processing', * timestamp: Date.now(), * features: features * }; * context.share('audit-log', auditData); * } * * return result; * } * ``` */ getSettings(pluginName: string): PluginSettings | undefined; /** * Stores a value in shared state for other plugins to access * * Provides type-safe storage of data that needs to be shared between * plugins during execution. The shared state is scoped to the current * execution and is automatically cleaned up afterward. * * @template V - Type of value being stored * @param key - Unique key for the value (consider using plugin namespaces) * @param value - Value to store in shared state * * @example * ```typescript * // Authentication plugin shares token * async execute(input: LoginRequest, context: PluginContext): Promise<AuthResult> { * const authResult = await authenticate(input); * * if (authResult.success) { * // Share authentication data for downstream plugins * context.share('auth-token', authResult.token); * context.share('user-permissions', authResult.permissions); * context.share('session-data', { * userId: authResult.userId, * sessionId: authResult.sessionId, * expiresAt: authResult.expiresAt * }); * * context.logger.info(`User authenticated: ${authResult.userId}`); * } * * return authResult; * } * ``` * * @example * ```typescript * // Data processing plugin shares intermediate results * async execute(input: RawData[], context: PluginContext): Promise<ProcessedData[]> { * const startTime = Date.now(); * * // Process data and track statistics * const results: ProcessedData[] = []; * const errors: ProcessingError[] = []; * * for (const item of input) { * try { * const processed = await this.processItem(item); * results.push(processed); * } catch (error) { * errors.push({ item: item.id, error: error.message }); * } * } * * // Share processing statistics * const stats = { * totalItems: input.length, * successfulItems: results.length, * failedItems: errors.length, * processingTime: Date.now() - startTime, * throughputPerSecond: results.length / ((Date.now() - startTime) / 1000) * }; * * context.share('processing-stats', stats); * context.share('processing-errors', errors); * * // Share for audit/monitoring plugins * context.share(`${this.metadata.name}-execution-summary`, { * plugin: this.metadata.name, * version: this.metadata.version, * executedAt: new Date().toISOString(), * stats * }); * * return results; * } * ``` * * @example * ```typescript * // Cache plugin shares cached data * async execute(input: CacheableData, context: PluginContext): Promise<CacheableData> { * const cacheKey = this.generateCacheKey(input); * * // Try to get from shared cache first * const cache = context.retrieve<Map<string, any>>('global-cache') || new Map(); * * if (cache.has(cacheKey)) { * context.logger.debug(`Cache hit for key: ${cacheKey}`); * return cache.get(cacheKey); * } * * // Process and cache result * const result = await this.processData(input); * cache.set(cacheKey, result); * * // Update shared cache * context.share('global-cache', cache); * * // Share cache statistics * const cacheStats = context.retrieve<any>('cache-stats') || { hits: 0, misses: 0 }; * cacheStats.misses++; * context.share('cache-stats', cacheStats); * * context.logger.debug(`Cache miss for key: ${cacheKey}, cached result`); * * return result; * } * ``` */ share<V>(key: string, value: V): void; /** * Retrieves a value from shared state * * Provides type-safe retrieval of shared data with proper null handling. * The generic type parameter helps maintain type safety throughout the * plugin system. * * @template V - Expected type of the retrieved value * @param key - Key of value to retrieve from shared state * @returns Retrieved value with proper typing, or undefined if not found * * @example * ```typescript * // Authorization plugin uses authentication data * async execute(input: AuthorizedRequest, context: PluginContext): Promise<AuthorizedRequest> { * // Retrieve authentication data with type safety * const token = context.retrieve<string>('auth-token'); * const permissions = context.retrieve<string[]>('user-permissions'); * const sessionData = context.retrieve<SessionData>('session-data'); * * if (!token || !permissions) { * throw new Error('Authentication required for authorization'); * } * * // Check permissions for the requested resource * const requiredPermission = input.resource; * if (!permissions.includes(requiredPermission)) { * context.addWarning('INSUFFICIENT_PERMISSIONS', * `User lacks permission: ${requiredPermission}`, { * userId: sessionData?.userId, * requiredPermission, * userPermissions: permissions * }); * throw new Error('Insufficient permissions'); * } * * // Add authorization context to request * return { * ...input, * authContext: { * token, * permissions, * sessionId: sessionData?.sessionId * } * }; * } * ``` * * @example * ```typescript * // Monitoring plugin aggregates data from multiple sources * async execute(input: MonitoringData, context: PluginContext): Promise<MonitoringReport> { * // Retrieve data from various plugins * const processingStats = context.retrieve<ProcessingStats>('processing-stats'); * const cacheStats = context.retrieve<CacheStats>('cache-stats'); * const authStats = context.retrieve<AuthStats>('auth-stats'); * const errorData = context.retrieve<ProcessingError[]>('processing-errors') || []; * * // Aggregate performance metrics * const performanceMetrics = { * totalExecutionTime: context.startTime ? Date.now() - context.startTime : 0, * throughput: processingStats?.throughputPerSecond || 0, * cacheHitRate: cacheStats ? (cacheStats.hits / (cacheStats.hits + cacheStats.misses)) : 0, * errorRate: processingStats ? (errorData.length / processingStats.totalItems) : 0 * }; * * // Create comprehensive monitoring report * const report: MonitoringReport = { * executionId: context.executionId, * timestamp: new Date().toISOString(), * performance: performanceMetrics, * processing: processingStats, * caching: cacheStats, * authentication: authStats, * errors: errorData, * summary: { * status: errorData.length === 0 ? 'success' : 'partial_failure', * totalWarnings: context._getWarnings().length, * executionTimeMs: performanceMetrics.totalExecutionTime * } * }; * * // Share final report for potential audit plugins * context.share('monitoring-report', report); * * return report; * } * ``` * * @example * ```typescript * // Database plugin retrieves connection pool info * async execute(input: DatabaseQuery, context: PluginContext): Promise<QueryResult> { * // Try to get existing database connection * let db = context.retrieve<Database>('database-connection'); * * if (!db) { * // No existing connection, create new one * db = await this.createConnection(); * context.share('database-connection', db); * context.logger.info('Created new database connection'); * } * * // Get connection pool statistics if available * const poolStats = context.retrieve<ConnectionPoolStats>('connection-pool-stats'); * if (poolStats && poolStats.activeConnections > poolStats.maxConnections * 0.8) { * context.addWarning('HIGH_CONNECTION_USAGE', * 'Connection pool usage is high', { * activeConnections: poolStats.activeConnections, * maxConnections: poolStats.maxConnections, * utilizationPercentage: (poolStats.activeConnections / poolStats.maxConnections) * 100 * }); * } * * // Execute query with connection * const result = await db.query(input.sql, input.parameters); * * // Update query statistics * const queryStats = context.retrieve<QueryStats>('query-stats') || { * totalQueries: 0, * totalExecutionTime: 0 * }; * queryStats.totalQueries++; * queryStats.totalExecutionTime += result.executionTime; * context.share('query-stats', queryStats); * * return result; * } * ``` */ retrieve<V>(key: string): V | undefined; } /** * Result Type Definitions for plugin execution outcomes * * Provides structured result types for: * - Individual plugin execution results * - Manager-level execution results * - Success/failure state tracking * - Performance metrics and warnings */ /** * Result of a single plugin execution * Contains success state, output data, timing, and diagnostics */ interface PluginResult<T = unknown> { /** Name of the plugin that produced this result */ readonly pluginName: string; /** Whether the plugin executed successfully */ readonly success: boolean; /** Output data from successful execution (undefined if failed) */ readonly output?: T; /** Error information from failed execution (undefined if successful) */ readonly error?: Error; /** Execution duration in milliseconds */ readonly duration: number; /** Non-fatal warnings generated during execution */ readonly warnings: readonly string[]; } /** * Overall result of plugin manager execution * Aggregates individual plugin results with manager-level metrics */ interface ManagerResult<T = unknown> { /** Whether the overall execution was successful */ readonly success: boolean; /** Results from individual plugin executions */ readonly results: readonly PluginResult<T>[]; /** Total execution duration in milliseconds */ readonly duration: number; /** Fatal errors that stopped execution */ readonly errors: readonly Error[]; /** Non-fatal warnings from plugins and manager */ readonly warnings: readonly string[]; } /** * Plugin Interface - Core contract for all plugins * * Defines the essential plugin contract: * - Plugin metadata for identification and dependencies * - Main execution method for processing data * - Lifecycle hooks fo