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
TypeScript
/*!
* 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