credl-parser-evaluator
Version:
TypeScript-based CREDL Parser and Evaluator that processes CREDL files and outputs complete Intermediate Representations
428 lines ⢠19.5 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;
};
})();
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