credl-parser-evaluator
Version:
TypeScript-based CREDL Parser and Evaluator that processes CREDL files and outputs complete Intermediate Representations
362 lines ⢠16 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.createValidateCommand = createValidateCommand;
exports.executeValidateCommand = executeValidateCommand;
const commander_1 = require("commander");
const index_js_1 = require("../../api/index.js");
const path = __importStar(require("path"));
const fs = __importStar(require("fs/promises"));
// 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 = {}));
function createValidateCommand() {
const validateCommand = new commander_1.Command('validate');
validateCommand
.description('Validate CREDL files for syntax and specification compliance')
.argument('<files...>', 'Path(s) to CREDL files or glob patterns (e.g., *.credl, **/*.yaml)')
.option('-o, --output <path>', 'Output validation results to file (default: console output)')
.option('-f, --format <format>', 'Output format: table, json, summary', 'table')
.option('-q, --quiet', 'Suppress output except errors (overrides verbose)')
.option('-v, --verbose', 'Verbose output with detailed validation information')
.option('--fail-fast', 'Stop validation on first error per file')
.option('--check-references', 'Enable cross-reference validation', true)
.option('--strict', 'Enable strict validation mode')
.option('--exit-code', 'Exit with non-zero code if any files are invalid', true)
.action(async (filePaths, options) => {
await executeValidateCommand(filePaths, options);
});
return validateCommand;
}
async function executeValidateCommand(filePaths, options) {
try {
const startTime = Date.now();
if (!options.quiet) {
console.log(`�� Starting CREDL validation for ${filePaths.length} file pattern(s)...`);
}
// Resolve file patterns and collect all files to validate
const resolvedFiles = await resolveFilePatterns(filePaths, options);
if (resolvedFiles.length === 0) {
console.error('ā No CREDL files found matching the provided patterns');
console.error('\nš” Suggestions:');
console.error(' ⢠Check if the file paths are correct');
console.error(' ⢠Ensure the files exist');
console.error(' ⢠Try using glob patterns like *.credl or **/*.yaml');
console.error(` ⢠Searched patterns: ${filePaths.join(', ')}`);
process.exit(ExitCode.FILE_NOT_FOUND);
}
if (options.verbose) {
console.log(`š Found ${resolvedFiles.length} file(s) to validate:`);
resolvedFiles.forEach(file => console.log(` ⢠${file}`));
console.log();
}
// Configure validation options
const validationOptions = {
failFast: options['fail-fast'] || false,
checkCrossReferences: options['check-references'] !== false,
strict: options.strict || false
};
if (options.verbose) {
console.log('š Validation Configuration:');
console.log(` ⢠Mode: ${validationOptions.strict ? 'strict' : 'standard'}`);
console.log(` ⢠Cross-references: ${validationOptions.checkCrossReferences ? 'enabled' : 'disabled'}`);
console.log(` ⢠Fail-fast: ${validationOptions.failFast ? 'enabled' : 'disabled'}`);
console.log(` ⢠Output format: ${options.format}`);
console.log();
}
// Validate all files
const results = [];
let hasProgressIndicator = false;
for (let i = 0; i < resolvedFiles.length; i++) {
const filePath = resolvedFiles[i];
if (!filePath)
continue; // Skip undefined entries
if (options.verbose) {
console.log(`š Validating ${path.basename(filePath)}... (${i + 1}/${resolvedFiles.length})`);
}
else if (!options.quiet && resolvedFiles.length > 1) {
process.stdout.write(`Validating files: ${i + 1}/${resolvedFiles.length}\r`);
hasProgressIndicator = true;
}
try {
const result = await validateSingleFile(filePath, validationOptions);
results.push(result);
}
catch (error) {
// Handle individual file validation errors
const fileError = {
filePath,
isValid: false,
errors: [{
field: 'file_access',
message: error instanceof Error ? error.message : String(error),
severity: 'error'
}],
warnings: [],
processingTime: 0
};
results.push(fileError);
}
}
if (hasProgressIndicator) {
process.stdout.write('\n');
}
const totalProcessingTime = Date.now() - startTime;
// Generate summary
const summary = {
totalFiles: results.length,
validFiles: results.filter(r => r.isValid).length,
invalidFiles: results.filter(r => !r.isValid).length,
totalErrors: results.reduce((sum, r) => sum + r.errors.length, 0),
totalWarnings: results.reduce((sum, r) => sum + r.warnings.length, 0),
processingTime: totalProcessingTime,
results
};
// Output results
await outputResults(summary, options);
// Exit with appropriate code
if (options['exit-code'] !== false && summary.invalidFiles > 0) {
process.exit(ExitCode.VALIDATION_ERROR);
}
}
catch (error) {
console.error('ā Unexpected error:', error instanceof Error ? error.message : String(error));
process.exit(ExitCode.GENERAL_ERROR);
}
}
async function resolveFilePatterns(patterns, options) {
const allFiles = new Set();
for (const pattern of patterns) {
try {
// Check if it's a direct file path first
const resolvedPath = path.resolve(pattern);
const exists = await (0, index_js_1.checkFileExists)(resolvedPath);
if (exists) {
// Direct file path
allFiles.add(resolvedPath);
}
else {
// Try as glob pattern
const { glob: globFunction } = await Promise.resolve().then(() => __importStar(require('glob')));
const matches = await globFunction(pattern, {
ignore: ['node_modules/**', '.git/**', 'dist/**', 'build/**']
});
for (const match of matches) {
const fullPath = path.resolve(match);
const fileExists = await (0, index_js_1.checkFileExists)(fullPath);
if (fileExists) {
allFiles.add(fullPath);
}
}
}
}
catch (error) {
if (options.verbose) {
console.warn(`ā ļø Could not resolve pattern "${pattern}": ${error instanceof Error ? error.message : String(error)}`);
}
}
}
return Array.from(allFiles).sort();
}
async function validateSingleFile(filePath, validationOptions) {
const startTime = Date.now();
try {
// Get file stats for metadata
const fileStats = await (0, index_js_1.getFileStats)(filePath);
// Validate the file using the parseFileFromPath API
const result = await (0, index_js_1.parseFileFromPath)(filePath, validationOptions);
const processingTime = Date.now() - startTime;
return {
filePath,
isValid: result.isValid,
errors: result.errors || [],
warnings: result.warnings || [],
...(fileStats?.size !== undefined && { fileSize: fileStats.size }),
processingTime
};
}
catch (error) {
const processingTime = Date.now() - startTime;
return {
filePath,
isValid: false,
errors: [{
field: 'file_validation',
message: error instanceof Error ? error.message : String(error),
severity: 'error'
}],
warnings: [],
processingTime
};
}
}
async function outputResults(summary, options) {
const outputContent = formatValidationResults(summary, options.format || 'table');
if (options.output) {
try {
// Create output directory if it doesn't exist
const outputDir = path.dirname(options.output);
await fs.mkdir(outputDir, { recursive: true });
// Write to file
await fs.writeFile(options.output, outputContent, 'utf8');
if (!options.quiet) {
console.log(`ā
Validation results written to: ${options.output}`);
}
}
catch (writeError) {
console.error(`ā Failed to write output file: ${options.output}`);
console.error(` Error: ${writeError instanceof Error ? writeError.message : String(writeError)}`);
process.exit(ExitCode.OUTPUT_ERROR);
}
}
else {
// Output to console
console.log(outputContent);
}
// Print summary unless quiet
if (!options.quiet) {
const statusIcon = summary.invalidFiles === 0 ? 'ā
' : 'ā';
const statusText = summary.invalidFiles === 0 ? 'All files valid' : `${summary.invalidFiles} file(s) invalid`;
console.log();
console.log(`${statusIcon} Validation Summary: ${statusText}`);
console.log(` š Files: ${summary.totalFiles} total, ${summary.validFiles} valid, ${summary.invalidFiles} invalid`);
console.log(` š Issues: ${summary.totalErrors} error(s), ${summary.totalWarnings} warning(s)`);
console.log(` ā±ļø Time: ${summary.processingTime}ms`);
}
}
function formatValidationResults(summary, format) {
switch (format) {
case 'json':
return JSON.stringify(summary, null, 2);
case 'summary':
return formatSummaryOutput(summary);
case 'table':
default:
return formatTableOutput(summary);
}
}
function formatSummaryOutput(summary) {
const lines = [];
// Header
lines.push('š CREDL Validation Summary');
lines.push('ā'.repeat(50));
// Overview
lines.push(`š Results: ${summary.validFiles}/${summary.totalFiles} files valid`);
lines.push(`š Issues: ${summary.totalErrors} error(s), ${summary.totalWarnings} warning(s)`);
lines.push(`ā±ļø Processing time: ${summary.processingTime}ms`);
lines.push('');
// Files with issues
const filesWithIssues = summary.results.filter(r => !r.isValid || r.warnings.length > 0);
if (filesWithIssues.length > 0) {
lines.push('š Files with Issues:');
lines.push('ā'.repeat(30));
filesWithIssues.forEach(result => {
const fileName = path.basename(result.filePath);
const status = result.isValid ? 'ā ļø ' : 'ā';
const issueCount = result.errors.length + result.warnings.length;
lines.push(`${status} ${fileName} (${issueCount} issue${issueCount !== 1 ? 's' : ''})`);
// Show first few issues
const allIssues = [...result.errors, ...result.warnings];
const displayIssues = allIssues.slice(0, 3);
displayIssues.forEach(issue => {
const icon = issue.severity === 'error' ? ' ā' : ' ā ļø ';
lines.push(`${icon} ${issue.field}: ${issue.message}`);
});
if (allIssues.length > 3) {
lines.push(` ... and ${allIssues.length - 3} more issue(s)`);
}
lines.push('');
});
}
else {
lines.push('ā
All files are valid with no issues found!');
}
return lines.join('\n');
}
function formatTableOutput(summary) {
const lines = [];
// Header
lines.push('š CREDL File Validation Results');
lines.push('ā'.repeat(80));
// Table header
const headerRow = '| File | Status | Errors | Warnings | Time |';
const separatorRow = '|------|--------|---------|----------|------|';
lines.push(headerRow);
lines.push(separatorRow);
// Table rows
summary.results.forEach(result => {
const fileName = path.basename(result.filePath);
const status = result.isValid ? 'ā
Valid' : 'ā Invalid';
const errors = result.errors.length.toString();
const warnings = result.warnings.length.toString();
const time = `${result.processingTime}ms`;
const row = `| ${fileName.padEnd(20)} | ${status.padEnd(9)} | ${errors.padEnd(6)} | ${warnings.padEnd(8)} | ${time.padEnd(4)} |`;
lines.push(row);
});
lines.push('');
// Detailed errors and warnings for invalid files
const invalidFiles = summary.results.filter(r => !r.isValid);
if (invalidFiles.length > 0) {
lines.push('�� Detailed Issues:');
lines.push('ā'.repeat(40));
invalidFiles.forEach(result => {
lines.push(`š ${result.filePath}`);
if (result.errors.length > 0) {
lines.push(' ā Errors:');
result.errors.forEach(error => {
lines.push(` ⢠${error.field}: ${error.message}`);
});
}
if (result.warnings.length > 0) {
lines.push(' ā ļø Warnings:');
result.warnings.forEach(warning => {
lines.push(` ⢠${warning.field}: ${warning.message}`);
});
}
lines.push('');
});
}
return lines.join('\n');
}
//# sourceMappingURL=validate.js.map