UNPKG

semantic-ds-toolkit

Version:

Performance-first semantic layer for modern data stacks - Stable Column Anchors & intelligent inference

479 lines (475 loc) • 20 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.errorHandler = exports.EnhancedErrorHandler = void 0; exports.withErrorHandler = withErrorHandler; const chalk_1 = __importDefault(require("chalk")); const promises_1 = __importDefault(require("fs/promises")); const path_1 = __importDefault(require("path")); class EnhancedErrorHandler { context; errorMappings = [ { pattern: /ENOENT.*no such file or directory.*semantic-config\.yaml/i, type: 'file_not_found', title: 'Configuration file not found', description: 'The semantic-config.yaml file is missing from your project.', recoveryActions: [ { id: 'init_project', title: 'Initialize new project', description: 'Create a new semantic data science project with default configuration', command: 'semantic-ds init', automated: false, priority: 'high' }, { id: 'create_basic_config', title: 'Create basic configuration', description: 'Generate a minimal semantic-config.yaml file', automated: true, priority: 'medium' } ] }, { pattern: /EACCES.*permission denied/i, type: 'permission_denied', title: 'Permission denied', description: 'Insufficient permissions to access file or directory.', recoveryActions: [ { id: 'check_permissions', title: 'Check file permissions', description: 'Verify and fix file/directory permissions', command: 'ls -la', automated: false, priority: 'high' }, { id: 'run_with_sudo', title: 'Run with elevated permissions', description: 'Try running the command with sudo (if appropriate)', automated: false, priority: 'low' } ] }, { pattern: /module not found|cannot resolve module/i, type: 'dependency_missing', title: 'Missing dependencies', description: 'Required Node.js modules are not installed.', recoveryActions: [ { id: 'install_deps', title: 'Install dependencies', description: 'Install missing Node.js dependencies', command: 'npm install', automated: true, priority: 'high' }, { id: 'clear_cache', title: 'Clear npm cache', description: 'Clear npm cache and reinstall dependencies', command: 'npm cache clean --force && npm install', automated: false, priority: 'medium' } ] }, { pattern: /invalid.*yaml|yaml.*parse.*error/i, type: 'invalid_config', title: 'Invalid configuration file', description: 'The semantic-config.yaml file contains syntax errors.', recoveryActions: [ { id: 'validate_yaml', title: 'Validate YAML syntax', description: 'Check and fix YAML syntax errors in configuration file', automated: true, priority: 'high' }, { id: 'restore_backup', title: 'Restore from backup', description: 'Restore configuration from backup file if available', automated: true, priority: 'medium' }, { id: 'regenerate_config', title: 'Regenerate configuration', description: 'Create a new configuration file with default settings', command: 'semantic-ds init --force', automated: false, priority: 'low' } ] }, { pattern: /network|timeout|connection.*refused|dns/i, type: 'network_error', title: 'Network connectivity issue', description: 'Unable to connect to external services or repositories.', recoveryActions: [ { id: 'check_connection', title: 'Check internet connection', description: 'Verify your internet connection is working', automated: false, priority: 'high' }, { id: 'retry_offline', title: 'Work in offline mode', description: 'Continue with cached data and offline functionality', automated: true, priority: 'medium' } ] }, { pattern: /validation.*failed|invalid.*data.*format/i, type: 'validation_error', title: 'Data validation failed', description: 'The provided data does not meet validation requirements.', recoveryActions: [ { id: 'check_data_format', title: 'Check data format', description: 'Verify your data files are in the correct format (CSV, JSON, etc.)', automated: false, priority: 'high' }, { id: 'run_with_relaxed_validation', title: 'Use relaxed validation', description: 'Run with lower confidence thresholds or relaxed validation', command: 'semantic-ds infer --confidence 0.5 --relaxed', automated: false, priority: 'medium' } ] } ]; constructor(command, args = []) { this.context = { command, args, workingDir: process.cwd(), timestamp: new Date(), environment: { nodeVersion: process.version, platform: process.platform, arch: process.arch } }; } async handleError(error) { console.log(); // Add spacing // Identify error type and get recovery actions const errorInfo = this.analyzeError(error); // Display error information this.displayErrorHeader(error, errorInfo); this.displayErrorDetails(error, errorInfo); // Show recovery actions await this.displayRecoveryActions(errorInfo.recoveryActions); // Log error for debugging await this.logError(error, errorInfo); // Exit with error code process.exit(1); } analyzeError(error) { const errorMessage = error.message + (error.stack || ''); // Find matching error pattern const mapping = this.errorMappings.find(mapping => mapping.pattern.test(errorMessage)); if (mapping) { return { mapping, recoveryActions: mapping.recoveryActions }; } // Default recovery actions for unknown errors const defaultActions = [ { id: 'check_logs', title: 'Check error logs', description: 'Review detailed error logs for more information', automated: false, priority: 'high' }, { id: 'reinstall', title: 'Reinstall toolkit', description: 'Reinstall the Semantic Data Science Toolkit', command: 'npm uninstall -g @semantic-toolkit/anchor && npm install -g @semantic-toolkit/anchor', automated: false, priority: 'medium' }, { id: 'report_issue', title: 'Report issue', description: 'Report this issue to the development team', automated: false, priority: 'low' } ]; return { mapping: null, recoveryActions: defaultActions }; } displayErrorHeader(error, errorInfo) { console.log(chalk_1.default.red.bold('āŒ Error occurred while running semantic-ds')); if (errorInfo.mapping) { console.log(chalk_1.default.white(`šŸ“ ${errorInfo.mapping.title}`)); console.log(chalk_1.default.gray(` ${errorInfo.mapping.description}`)); } else { console.log(chalk_1.default.white(`šŸ“ ${error.name || 'Unknown Error'}`)); } console.log(chalk_1.default.red(`šŸ’„ ${error.message}`)); } displayErrorDetails(error, errorInfo) { console.log(chalk_1.default.cyan('\nšŸ” Error Details:')); console.log(chalk_1.default.white(` Command: ${this.context.command} ${this.context.args.join(' ')}`)); console.log(chalk_1.default.white(` Working Directory: ${this.context.workingDir}`)); console.log(chalk_1.default.white(` Time: ${this.context.timestamp.toISOString()}`)); if (errorInfo.mapping) { console.log(chalk_1.default.white(` Error Type: ${errorInfo.mapping.type}`)); } // Show stack trace for debugging (abbreviated) if (error.stack && process.env.DEBUG) { console.log(chalk_1.default.gray('\nšŸ› Stack Trace (DEBUG):')); const stackLines = error.stack.split('\n').slice(0, 5); stackLines.forEach(line => { console.log(chalk_1.default.gray(` ${line}`)); }); console.log(chalk_1.default.gray(' ... (use DEBUG=* for full trace)')); } } async displayRecoveryActions(actions) { if (actions.length === 0) return; console.log(chalk_1.default.cyan('\nšŸ› ļø Suggested Recovery Actions:')); // Sort by priority const sortedActions = actions.sort((a, b) => { const priorityOrder = { high: 0, medium: 1, low: 2 }; return priorityOrder[a.priority] - priorityOrder[b.priority]; }); for (let i = 0; i < sortedActions.length; i++) { const action = sortedActions[i]; const number = `${i + 1}.`; const priority = this.getPriorityIcon(action.priority); console.log(chalk_1.default.white(` ${number} ${priority} ${action.title}`)); console.log(chalk_1.default.gray(` ${action.description}`)); if (action.command) { console.log(chalk_1.default.yellow(` Command: ${action.command}`)); } // Auto-execute high priority automated actions if (action.automated && action.priority === 'high') { console.log(chalk_1.default.blue(' šŸ¤– Attempting automatic recovery...')); try { await this.executeRecoveryAction(action); console.log(chalk_1.default.green(' āœ… Recovery action completed successfully')); } catch (recoveryError) { console.log(chalk_1.default.red(' āŒ Automatic recovery failed')); } } } console.log(chalk_1.default.gray('\nšŸ’” Try the highest priority action first.')); } getPriorityIcon(priority) { const icons = { high: 'šŸ”“', medium: '🟔', low: 'šŸ”µ' }; return icons[priority]; } async executeRecoveryAction(action) { switch (action.id) { case 'create_basic_config': await this.createBasicConfig(); break; case 'install_deps': await this.installDependencies(); break; case 'validate_yaml': await this.validateYamlConfig(); break; case 'restore_backup': await this.restoreConfigBackup(); break; case 'retry_offline': console.log(chalk_1.default.blue(' Switching to offline mode...')); process.env.SEMANTIC_DS_OFFLINE = 'true'; break; default: throw new Error(`Unknown recovery action: ${action.id}`); } } async createBasicConfig() { const configPath = path_1.default.join(this.context.workingDir, 'semantic-config.yaml'); const basicConfig = `# Basic Semantic Data Science Configuration project: name: "semantic-project" version: "1.0.0" inference: confidence_threshold: 0.7 auto_reconcile: true anchors: storage_path: "./anchors" backup_enabled: true evidence: persistence: true storage_path: "./evidence" data_types: - csv - json `; await promises_1.default.writeFile(configPath, basicConfig, 'utf-8'); console.log(chalk_1.default.green(` āœ… Created basic configuration at ${configPath}`)); } async installDependencies() { const { spawn } = await Promise.resolve().then(() => __importStar(require('child_process'))); return new Promise((resolve, reject) => { const npm = spawn('npm', ['install'], { cwd: this.context.workingDir, stdio: 'pipe' }); npm.on('close', (code) => { if (code === 0) { resolve(); } else { reject(new Error(`npm install failed with code ${code}`)); } }); }); } async validateYamlConfig() { const configPath = path_1.default.join(this.context.workingDir, 'semantic-config.yaml'); try { const configContent = await promises_1.default.readFile(configPath, 'utf-8'); const yaml = await Promise.resolve().then(() => __importStar(require('yaml'))); yaml.parse(configContent); // Will throw if invalid console.log(chalk_1.default.green(' āœ… YAML configuration is valid')); } catch (error) { console.log(chalk_1.default.red(' āŒ YAML validation failed')); // Try to create a backup and fix common issues const backupPath = `${configPath}.backup.${Date.now()}`; await promises_1.default.copyFile(configPath, backupPath); console.log(chalk_1.default.blue(` šŸ“ Backup created: ${backupPath}`)); throw error; } } async restoreConfigBackup() { const configDir = this.context.workingDir; const files = await promises_1.default.readdir(configDir); const backupFiles = files .filter(file => file.match(/semantic-config\.yaml\.backup\.\d+/)) .sort() .reverse(); // Most recent first if (backupFiles.length === 0) { throw new Error('No backup files found'); } const latestBackup = backupFiles[0]; const configPath = path_1.default.join(configDir, 'semantic-config.yaml'); const backupPath = path_1.default.join(configDir, latestBackup); await promises_1.default.copyFile(backupPath, configPath); console.log(chalk_1.default.green(` āœ… Restored configuration from ${latestBackup}`)); } async logError(error, errorInfo) { try { const logDir = path_1.default.join(this.context.workingDir, '.semantic-ds', 'logs'); await promises_1.default.mkdir(logDir, { recursive: true }); const logEntry = { timestamp: this.context.timestamp.toISOString(), command: this.context.command, args: this.context.args, workingDir: this.context.workingDir, environment: this.context.environment, error: { name: error.name, message: error.message, stack: error.stack, type: errorInfo.mapping?.type || 'unknown' } }; const logFile = path_1.default.join(logDir, `error-${Date.now()}.json`); await promises_1.default.writeFile(logFile, JSON.stringify(logEntry, null, 2), 'utf-8'); console.log(chalk_1.default.gray(`\nšŸ“ Error logged to: ${logFile}`)); } catch (logError) { // Silently fail logging - don't compound the original error } } // Static utility methods static handleUncaughtException(error) { const handler = new EnhancedErrorHandler('unknown', []); console.log(chalk_1.default.red.bold('\nšŸ’„ Uncaught Exception:')); handler.handleError(error); } static handleUnhandledRejection(reason) { const error = reason instanceof Error ? reason : new Error(String(reason)); const handler = new EnhancedErrorHandler('unknown', []); console.log(chalk_1.default.red.bold('\nšŸ’„ Unhandled Promise Rejection:')); handler.handleError(error); } static setupGlobalHandlers() { process.on('uncaughtException', EnhancedErrorHandler.handleUncaughtException); process.on('unhandledRejection', EnhancedErrorHandler.handleUnhandledRejection); } } exports.EnhancedErrorHandler = EnhancedErrorHandler; // Utility function for command error handling function withErrorHandler(command, fn) { return async (...args) => { const errorHandler = new EnhancedErrorHandler(command, args.map(String)); try { return await fn(...args); } catch (error) { await errorHandler.handleError(error instanceof Error ? error : new Error(String(error))); throw error; // Won't reach here due to process.exit in handleError } }; } // Export default instance exports.errorHandler = new EnhancedErrorHandler('semantic-ds'); //# sourceMappingURL=error-handler.js.map