UNPKG

credl-parser-evaluator

Version:

TypeScript-based CREDL Parser and Evaluator that processes CREDL files and outputs complete Intermediate Representations

428 lines • 19.5 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; }; })(); Object.defineProperty(exports, "__esModule", { value: true }); exports.createRunCommand = createRunCommand; exports.executeRunCommand = executeRunCommand; const commander_1 = require("commander"); const index_js_1 = require("../../api/index.js"); const path = __importStar(require("path")); // Error exit codes for different types of failures var ExitCode; (function (ExitCode) { ExitCode[ExitCode["SUCCESS"] = 0] = "SUCCESS"; ExitCode[ExitCode["VALIDATION_ERROR"] = 1] = "VALIDATION_ERROR"; ExitCode[ExitCode["FILE_NOT_FOUND"] = 2] = "FILE_NOT_FOUND"; ExitCode[ExitCode["FILE_ACCESS_ERROR"] = 3] = "FILE_ACCESS_ERROR"; ExitCode[ExitCode["PARSING_ERROR"] = 4] = "PARSING_ERROR"; ExitCode[ExitCode["OUTPUT_ERROR"] = 5] = "OUTPUT_ERROR"; ExitCode[ExitCode["INVALID_ARGUMENTS"] = 6] = "INVALID_ARGUMENTS"; ExitCode[ExitCode["GENERAL_ERROR"] = 10] = "GENERAL_ERROR"; })(ExitCode || (ExitCode = {})); // Enhanced error handling with user-friendly messages and suggestions class CLIError extends Error { constructor(message, exitCode, suggestions = []) { super(message); this.exitCode = exitCode; this.suggestions = suggestions; this.name = 'CLIError'; } } function handleError(error, verbose = false) { if (error instanceof CLIError) { console.error(`āŒ ${error.message}`); if (error.suggestions.length > 0) { console.error('\nšŸ’” Suggestions:'); error.suggestions.forEach(suggestion => { console.error(` • ${suggestion}`); }); } if (verbose && error.stack) { console.error('\nšŸ› Stack trace:'); console.error(error.stack); } process.exit(error.exitCode); } // Handle standard Node.js errors if (error instanceof Error) { if (error.message.includes('ENOENT')) { const suggestions = [ 'Check if the file path is correct', 'Ensure the file exists', 'Try using an absolute path' ]; if (error.message.includes('mkdir')) { suggestions.push('Check if you have permission to create directories in the target location'); suggestions.push('Try using a different output directory'); } console.error(`āŒ File or directory not found: ${error.message}`); if (suggestions.length > 0) { console.error('\nšŸ’” Suggestions:'); suggestions.forEach(suggestion => { console.error(` • ${suggestion}`); }); } if (verbose && error.stack) { console.error('\nšŸ› Stack trace:'); console.error(error.stack); } process.exit(ExitCode.FILE_NOT_FOUND); } if (error.message.includes('EACCES') || error.message.includes('EPERM')) { const suggestions = [ 'Check if you have permission to access the file/directory', 'Try running with appropriate permissions', 'Ensure the file is not in use by another process' ]; console.error(`āŒ Permission denied: ${error.message}`); if (suggestions.length > 0) { console.error('\nšŸ’” Suggestions:'); suggestions.forEach(suggestion => { console.error(` • ${suggestion}`); }); } if (verbose && error.stack) { console.error('\nšŸ› Stack trace:'); console.error(error.stack); } process.exit(ExitCode.FILE_ACCESS_ERROR); } if (error.message.includes('CREDL parsing failed') || error.message.includes('Failed to parse')) { const suggestions = [ 'Check if the file contains valid YAML syntax', 'Ensure the file follows CREDL specification format', 'Try validating the YAML syntax with an online validator' ]; if (error.message.includes('must contain a valid YAML object')) { suggestions.push('The file should start with a YAML object (properties), not a list or scalar value'); } console.error(`āŒ CREDL parsing error: ${error.message}`); if (suggestions.length > 0) { console.error('\nšŸ’” Suggestions:'); suggestions.forEach(suggestion => { console.error(` • ${suggestion}`); }); } if (verbose && error.stack) { console.error('\nšŸ› Stack trace:'); console.error(error.stack); } process.exit(ExitCode.PARSING_ERROR); } // Generic error handling console.error('āŒ Unexpected error:'); console.error(` ${error.message}`); if (verbose && error.stack) { console.error('\nšŸ› Stack trace:'); console.error(error.stack); } process.exit(ExitCode.GENERAL_ERROR); } // Handle any other type of error console.error('āŒ Unknown error occurred:'); console.error(` ${String(error)}`); process.exit(ExitCode.GENERAL_ERROR); } function createRunCommand() { const runCommand = new commander_1.Command('run'); runCommand .description('Process and evaluate a CREDL file to generate complete IR') .argument('<file>', 'Path to the CREDL file to process') .option('-o, --output <path>', 'Output file path (default: console output)') .option('-f, --format <format>', 'Output format: json, pretty, summary', 'pretty') .option('-q, --quiet', 'Suppress output except errors') .option('-v, --verbose', 'Verbose output with detailed processing information') .option('--fail-fast', 'Stop processing on first validation error') .option('--include-metadata', 'Include processing metadata in output', true) .option('--resolve-presets', 'Resolve preset references', true) .option('--resolve-templates', 'Resolve template definitions', true) .option('--validate-references', 'Validate cross-references', true) .action(async (filePath, options) => { await executeRunCommand(filePath, options); }); return runCommand; } async function executeRunCommand(filePath, options) { try { // Validate file path and existence await validateInputFile(filePath, options); if (!options.quiet) { console.log(`šŸ”„ Processing CREDL file: ${filePath}`); } // Configure validation options const validationOptions = { failFast: options['fail-fast'] || false, checkCrossReferences: options['validate-references'] !== false }; // Configure IR builder options const irOptions = { includeMetadata: options['include-metadata'] !== false, resolvePresets: options['resolve-presets'] !== false, resolveTemplates: options['resolve-templates'] !== false, validateReferences: options['validate-references'] !== false, generateIds: true }; if (options.verbose) { console.log('šŸ“‹ Configuration:'); console.log(` • Validation: ${validationOptions.failFast ? 'fail-fast' : 'collect-all'}`); console.log(` • Cross-references: ${validationOptions.checkCrossReferences ? 'enabled' : 'disabled'}`); console.log(` • Presets: ${irOptions.resolvePresets ? 'enabled' : 'disabled'}`); console.log(` • Templates: ${irOptions.resolveTemplates ? 'enabled' : 'disabled'}`); console.log(` • Output format: ${options.format}`); } // Process the CREDL file with progress indicators const startTime = Date.now(); if (options.verbose) { console.log('šŸ”„ Starting CREDL processing pipeline...'); process.stdout.write(' šŸ“ Parsing CREDL file... '); } const ir = await (0, index_js_1.processFileFromPath)(filePath, validationOptions, irOptions); const processingTime = Date.now() - startTime; if (options.verbose) { console.log('āœ…'); console.log(` šŸŽÆ Processing completed successfully`); } if (options.verbose) { console.log(`ā±ļø Processing completed in ${processingTime}ms`); console.log(`šŸ“Š Results:`); console.log(` • Assets: ${ir.assets.length}`); console.log(` • Spaces: ${ir.spaces.length}`); console.log(` • Template-generated spaces: ${ir.template_generated_spaces?.length || 0}`); console.log(` • Assumptions: ${ir.assumptions.length}`); console.log(` • Models: ${ir.models.length}`); console.log(` • Validation: ${ir.validation.isValid ? 'āœ… Valid' : 'āŒ Invalid'}`); if (ir.validation.errors.length > 0) { console.log(` • Errors: ${ir.validation.errors.length}`); } if (ir.validation.warnings.length > 0) { console.log(` • Warnings: ${ir.validation.warnings.length}`); } } // Handle validation results if (!ir.validation.isValid) { const errorDetails = ir.validation.errors.map(error => `${error.field}: ${error.message}`).join('\n • '); const suggestions = [ 'Review the CREDL specification to fix validation errors', 'Check the file syntax and structure', 'Use --verbose flag for more detailed error information' ]; if (ir.validation.errors.some(e => e.field.includes('presets'))) { suggestions.push('Ensure all preset references are defined and valid'); } if (ir.validation.errors.some(e => e.field.includes('templates'))) { suggestions.push('Check template definitions and variable substitutions'); } if (ir.validation.errors.some(e => e.field.includes('cross_references'))) { suggestions.push('Verify all cross-references point to existing elements'); } throw new CLIError(`Validation failed:\n • ${errorDetails}`, ExitCode.VALIDATION_ERROR, suggestions); } // Show warnings even on successful processing (unless format is summary) if (!options.quiet && ir.validation.warnings.length > 0 && options.format !== 'summary') { console.warn('āš ļø Warnings:'); ir.validation.warnings.forEach(warning => { console.warn(` • ${warning.field}: ${warning.message}`); }); } // Format and output results if (options.verbose) { process.stdout.write(' šŸ“„ Formatting output... '); } const outputContent = formatOutput(ir, options.format || 'pretty'); if (options.verbose) { console.log('āœ…'); } if (options.output) { try { if (options.verbose) { process.stdout.write(' šŸ’¾ Writing output file... '); } // Create output directory if it doesn't exist const fs = await Promise.resolve().then(() => __importStar(require('fs/promises'))); const outputDir = path.dirname(options.output); await fs.mkdir(outputDir, { recursive: true }); // Write to file await fs.writeFile(options.output, outputContent, 'utf8'); if (options.verbose) { console.log('āœ…'); } if (!options.quiet) { console.log(`āœ… Results written to: ${options.output}`); } } catch (writeError) { throw new CLIError(`Failed to write output file: ${options.output}`, ExitCode.OUTPUT_ERROR, [ 'Check if you have permission to write to the output directory', 'Ensure the output directory path is valid', 'Try using a different output file path', `Error: ${writeError instanceof Error ? writeError.message : String(writeError)}` ]); } } else { // Output to console if (!options.quiet && options.format !== 'summary') { console.log('šŸ“‹ Generated IR:'); } console.log(outputContent); } if (!options.quiet) { console.log(`āœ… Successfully processed CREDL file in ${processingTime}ms`); } } catch (error) { handleError(error, options.verbose); } } async function validateInputFile(filePath, options) { // Check if file path is provided if (!filePath || filePath.trim().length === 0) { throw new CLIError('No file path provided', ExitCode.INVALID_ARGUMENTS, [ 'Provide a path to a CREDL file as an argument', 'Example: credl run my-file.credl' ]); } // Resolve the file path const resolvedPath = path.resolve(filePath); if (options.verbose) { console.log(`šŸ“ Validating file: ${resolvedPath}`); } // Check if file exists const fileExists = await (0, index_js_1.checkFileExists)(resolvedPath); if (!fileExists) { throw new CLIError(`File not found: ${filePath}\n Resolved path: ${resolvedPath}`, ExitCode.FILE_NOT_FOUND, [ 'Check if the file path is correct', 'Ensure the file exists', 'Try using an absolute path', `Looking for: ${resolvedPath}` ]); } // Get file stats and validate const fileStats = await (0, index_js_1.getFileStats)(resolvedPath); if (!fileStats) { throw new CLIError(`Cannot access file: ${filePath}`, ExitCode.FILE_ACCESS_ERROR, [ 'Check if you have permission to read the file', 'Ensure the file is not locked by another process', 'Verify the file path is correct' ]); } // Check if it's a regular file (not a directory) if (fileStats.size === 0) { throw new CLIError(`File is empty: ${filePath}`, ExitCode.PARSING_ERROR, [ 'Ensure the file contains valid CREDL content', 'Check if the file was saved properly', 'Try with a different CREDL file' ]); } // Validate file extension (optional warning) const fileExtension = path.extname(filePath).toLowerCase(); const validExtensions = ['.credl', '.yaml', '.yml']; if (!validExtensions.includes(fileExtension) && !options.quiet) { console.warn(`āš ļø Warning: File extension '${fileExtension}' is not typical for CREDL files`); console.warn(` Expected extensions: ${validExtensions.join(', ')}`); } if (options.verbose) { console.log(`āœ… File validation passed:`); console.log(` • File size: ${fileStats.size} bytes`); console.log(` • Last modified: ${fileStats.modified.toISOString()}`); console.log(` • Extension: ${fileExtension || '(none)'}`); } } function formatOutput(ir, format) { switch (format) { case 'json': return JSON.stringify(ir); case 'summary': return formatSummary(ir); case 'pretty': default: return JSON.stringify(ir, null, 2); } } function formatSummary(ir) { const lines = []; // Header lines.push('šŸ“Š CREDL Processing Summary'); lines.push('═'.repeat(50)); // Basic info lines.push(`šŸ“‹ File: ${ir.metadata.name || 'Unnamed'}`); lines.push(`šŸ“ Description: ${ir.metadata.description || 'No description'}`); lines.push(`šŸ—“ļø Generated: ${new Date(ir.generated_at).toLocaleString()}`); lines.push(`āš™ļø Generator: ${ir.generator_version}`); lines.push(''); // Processing results lines.push('šŸ“ˆ Processing Results:'); lines.push(` • Assets: ${ir.assets.length}`); lines.push(` • Spaces: ${ir.spaces.length}`); if (ir.template_generated_spaces?.length > 0) { lines.push(` • Template-generated spaces: ${ir.template_generated_spaces.length}`); } lines.push(` • Assumptions: ${ir.assumptions.length}`); lines.push(` • Models: ${ir.models.length}`); lines.push(''); // Validation status const validationIcon = ir.validation.isValid ? 'āœ…' : 'āŒ'; const validationStatus = ir.validation.isValid ? 'Valid' : 'Invalid'; lines.push(`${validationIcon} Validation: ${validationStatus}`); if (ir.validation.errors.length > 0) { lines.push(` • Errors: ${ir.validation.errors.length}`); } if (ir.validation.warnings.length > 0) { lines.push(` • Warnings: ${ir.validation.warnings.length}`); } lines.push(''); // Processing status if (ir.partial_processing_status) { const status = ir.partial_processing_status; lines.push('āš™ļø Processing Status:'); lines.push(` • Completed steps: ${status.completed_steps.length}`); if (status.failed_steps.length > 0) { lines.push(` • Failed steps: ${status.failed_steps.length}`); } lines.push(''); } // Cross-reference summary if (ir.cross_reference_resolution) { const xref = ir.cross_reference_resolution; lines.push('šŸ”— Cross-References:'); lines.push(` • Total nodes: ${xref.dependency_graph.length}`); lines.push(` • Circular dependencies: ${xref.circular_dependencies.length}`); lines.push(` • Orphaned elements: ${Object.values(xref.orphaned_elements).flat().length}`); if (xref.coverage_analysis) { lines.push(` • Assumption coverage: ${xref.coverage_analysis.assumption_coverage}%`); } } return lines.join('\n'); } //# sourceMappingURL=run.js.map