semantic-ds-toolkit
Version:
Performance-first semantic layer for modern data stacks - Stable Column Anchors & intelligent inference
479 lines (475 loc) ⢠20 kB
JavaScript
;
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