UNPKG

adpa-enterprise-framework-automation

Version:

Modular, standards-compliant Node.js/TypeScript automation framework for enterprise requirements, project, and data management. Provides CLI and API for BABOK v3, PMBOK 7th Edition, and DMBOK 2.0 (in progress). Production-ready Express.js API with TypeSpe

461 lines • 16.6 kB
/** * Interactive Error Handler * * Provides specialized error handling for the interactive CLI system. * Ensures that errors don't crash the application and provides user-friendly * error messages with recovery options. * * @version 1.0.0 * @author ADPA Team */ import { ValidationError } from '../../commands/utils/validation.js'; export class InteractiveErrorHandler { static errorHistory = []; static maxHistorySize = 50; /** * Handle validation errors in interactive context */ static handleValidationError(validationResult, context) { const error = { type: 'validation', message: validationResult.error || 'Validation failed', context, recoveryOptions: { canRetry: true, canGoBack: false, canSkip: false, suggestedActions: validationResult.suggestions || ['Please correct your input and try again'] } }; this.logError(error); return error; } /** * Handle system errors (file system, permissions, etc.) */ static handleSystemError(originalError, context) { let message = 'System error occurred'; let suggestedActions = []; // Categorize common system errors if (originalError.message.includes('ENOENT')) { message = 'File or directory not found'; suggestedActions = [ 'Check that the file path is correct', 'Ensure the file exists', 'Check file permissions' ]; } else if (originalError.message.includes('EACCES')) { message = 'Permission denied'; suggestedActions = [ 'Check file/directory permissions', 'Run with appropriate privileges', 'Contact system administrator' ]; } else if (originalError.message.includes('ENOSPC')) { message = 'No space left on device'; suggestedActions = [ 'Free up disk space', 'Choose a different output directory', 'Contact system administrator' ]; } else { message = originalError.message; suggestedActions = [ 'Try the operation again', 'Check system resources', 'Contact support if the problem persists' ]; } const error = { type: 'system', message, context, recoveryOptions: { canRetry: true, canGoBack: true, canSkip: false, suggestedActions }, originalError }; this.logError(error); return error; } /** * Handle network errors (API calls, downloads, etc.) */ static handleNetworkError(originalError, context) { let message = 'Network error occurred'; let suggestedActions = []; if (originalError.message.includes('timeout')) { message = 'Request timed out'; suggestedActions = [ 'Check your internet connection', 'Try again in a few moments', 'Check if the service is available' ]; } else if (originalError.message.includes('ENOTFOUND')) { message = 'Service not found'; suggestedActions = [ 'Check the URL or endpoint', 'Verify your internet connection', 'Check if the service is online' ]; } else if (originalError.message.includes('401')) { message = 'Authentication failed'; suggestedActions = [ 'Check your API key or credentials', 'Verify the credentials are still valid', 'Re-configure the provider if needed' ]; } else if (originalError.message.includes('403')) { message = 'Access forbidden'; suggestedActions = [ 'Check your account permissions', 'Verify your subscription status', 'Contact the service provider' ]; } else if (originalError.message.includes('429')) { message = 'Rate limit exceeded'; suggestedActions = [ 'Wait a few minutes before trying again', 'Check your API usage limits', 'Consider upgrading your plan' ]; } else { message = originalError.message; suggestedActions = [ 'Check your internet connection', 'Try again in a few moments', 'Contact support if the problem persists' ]; } const error = { type: 'network', message, context, recoveryOptions: { canRetry: true, canGoBack: true, canSkip: true, suggestedActions }, originalError }; this.logError(error); return error; } /** * Handle configuration errors */ static handleConfigurationError(originalError, context) { let message = 'Configuration error'; let suggestedActions = []; if (originalError.message.includes('API key')) { message = 'API key configuration issue'; suggestedActions = [ 'Check your API key is correctly set', 'Verify the API key format', 'Run the setup wizard to reconfigure' ]; } else if (originalError.message.includes('provider')) { message = 'AI provider configuration issue'; suggestedActions = [ 'Check your provider configuration', 'Run "adpa setup" to reconfigure', 'Verify your provider credentials' ]; } else { message = originalError.message; suggestedActions = [ 'Check your configuration files', 'Run the setup wizard', 'Reset configuration if needed' ]; } const error = { type: 'configuration', message, context, recoveryOptions: { canRetry: true, canGoBack: true, canSkip: false, suggestedActions }, originalError }; this.logError(error); return error; } /** * Handle user cancellation or interruption */ static handleUserCancellation(context) { const error = { type: 'user', message: 'Operation cancelled by user', context, recoveryOptions: { canRetry: false, canGoBack: true, canSkip: false, suggestedActions: ['Return to previous menu', 'Try a different operation'] } }; this.logError(error); return error; } /** * Handle unknown errors */ static handleUnknownError(originalError, context) { const error = { type: 'unknown', message: `Unexpected error: ${originalError.message}`, context, recoveryOptions: { canRetry: true, canGoBack: true, canSkip: true, suggestedActions: [ 'Try the operation again', 'Return to previous menu', 'Contact support with error details' ] }, originalError }; this.logError(error); return error; } /** * Display error to user with recovery options */ static async displayError(error) { console.log('\n' + '─'.repeat(60)); console.log(`āŒ Error: ${error.message}`); if (error.context.userInput) { console.log(`šŸ“ Your input: "${error.context.userInput}"`); } if (error.context.operation) { console.log(`šŸ”§ Operation: ${error.context.operation}`); } if (error.recoveryOptions.suggestedActions.length > 0) { console.log('\nšŸ’” Suggestions:'); for (const action of error.recoveryOptions.suggestedActions) { console.log(` • ${action}`); } } console.log('\nšŸ”„ Recovery Options:'); if (error.recoveryOptions.canRetry) { console.log(' • Press Enter to try again'); } if (error.recoveryOptions.canGoBack) { console.log(' • Type "back" to return to previous menu'); } if (error.recoveryOptions.canSkip) { console.log(' • Type "skip" to skip this step'); } console.log(' • Type "help" for more assistance'); console.log('─'.repeat(60)); } /** * Get recovery action from user */ static async getRecoveryAction(error, promptFunction) { while (true) { const input = await promptFunction('\nWhat would you like to do? '); const choice = input.trim().toLowerCase(); switch (choice) { case '': case 'retry': case 'r': if (error.recoveryOptions.canRetry) { return 'retry'; } console.log('āŒ Retry is not available for this error'); break; case 'back': case 'b': if (error.recoveryOptions.canGoBack) { return 'back'; } console.log('āŒ Going back is not available for this error'); break; case 'skip': case 's': if (error.recoveryOptions.canSkip) { return 'skip'; } console.log('āŒ Skipping is not available for this error'); break; case 'help': case 'h': case '?': return 'help'; case 'exit': case 'quit': case 'q': return 'exit'; default: console.log('āŒ Invalid choice. Please try again.'); this.showRecoveryHelp(error); } } } /** * Show recovery help */ static showRecoveryHelp(error) { console.log('\nā“ Available Recovery Actions:'); if (error.recoveryOptions.canRetry) { console.log(' • "retry" or Enter - Try the operation again'); } if (error.recoveryOptions.canGoBack) { console.log(' • "back" - Return to previous menu'); } if (error.recoveryOptions.canSkip) { console.log(' • "skip" - Skip this step and continue'); } console.log(' • "help" - Show this help message'); console.log(' • "exit" - Exit the application'); } /** * Log error to history */ static logError(error) { this.errorHistory.push(error); // Maintain history size limit if (this.errorHistory.length > this.maxHistorySize) { this.errorHistory.shift(); } // Log to console in development mode if (process.env.NODE_ENV === 'development') { console.error('[DEBUG] Interactive Error:', { type: error.type, message: error.message, context: error.context, originalError: error.originalError?.stack }); } } /** * Get error statistics */ static getErrorStatistics() { const byType = {}; for (const error of this.errorHistory) { byType[error.type] = (byType[error.type] || 0) + 1; } return { total: this.errorHistory.length, byType, recent: this.errorHistory.slice(-10) }; } /** * Clear error history */ static clearErrorHistory() { this.errorHistory = []; } /** * Wrap async operation with error handling */ static async withErrorHandling(operation, context, promptFunction) { while (true) { try { return await operation(); } catch (originalError) { const error = this.categorizeError(originalError, context); await this.displayError(error); const action = await this.getRecoveryAction(error, promptFunction); switch (action) { case 'retry': continue; // Try again case 'back': case 'skip': case 'exit': return null; // Let caller handle these case 'help': await this.showDetailedHelp(error); continue; } } } } /** * Categorize error based on type and message */ static categorizeError(error, context) { if (error instanceof ValidationError) { return this.handleValidationError({ isValid: false, error: error.message }, context); } const message = error.message.toLowerCase(); if (message.includes('network') || message.includes('fetch') || message.includes('timeout') || message.includes('enotfound') || message.includes('econnrefused')) { return this.handleNetworkError(error, context); } if (message.includes('enoent') || message.includes('eacces') || message.includes('enospc')) { return this.handleSystemError(error, context); } if (message.includes('config') || message.includes('api key') || message.includes('provider')) { return this.handleConfigurationError(error, context); } return this.handleUnknownError(error, context); } /** * Show detailed help for an error */ static async showDetailedHelp(error) { console.log('\nšŸ“š Detailed Help'); console.log('─'.repeat(40)); switch (error.type) { case 'validation': console.log('šŸ” Input Validation Error:'); console.log(' This error occurs when your input doesn\'t meet the required format.'); console.log(' Please check the suggestions above and correct your input.'); break; case 'network': console.log('🌐 Network Error:'); console.log(' This error occurs when there\'s a problem connecting to external services.'); console.log(' Check your internet connection and try again.'); break; case 'system': console.log('šŸ’» System Error:'); console.log(' This error occurs when there\'s a problem with file system operations.'); console.log(' Check file permissions and available disk space.'); break; case 'configuration': console.log('āš™ļø Configuration Error:'); console.log(' This error occurs when there\'s a problem with your settings.'); console.log(' Run the setup wizard to reconfigure your environment.'); break; default: console.log('ā“ Unknown Error:'); console.log(' An unexpected error occurred. Please try again or contact support.'); } console.log('\nšŸ’” General Tips:'); console.log(' • Use "back" to return to the previous menu'); console.log(' • Use "exit" to quit the application'); console.log(' • Check the system status for configuration issues'); console.log(' • Run diagnostics from the System Configuration menu'); await new Promise(resolve => { process.stdin.once('data', () => resolve(void 0)); console.log('\nPress Enter to continue...'); }); } } //# sourceMappingURL=InteractiveErrorHandler.js.map