credl-parser-evaluator
Version:
TypeScript-based CREDL Parser and Evaluator that processes CREDL files and outputs complete Intermediate Representations
470 lines • 16.3 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.fileUtils = void 0;
exports.checkFileExists = checkFileExists;
exports.checkFileExistsSync = checkFileExistsSync;
exports.getFileStats = getFileStats;
exports.getFileStatsSync = getFileStatsSync;
exports.ensureDirectory = ensureDirectory;
exports.ensureDirectorySync = ensureDirectorySync;
exports.isDirectoryEmpty = isDirectoryEmpty;
exports.safeWriteFile = safeWriteFile;
exports.safeReadFile = safeReadFile;
exports.resolvePath = resolvePath;
exports.relativePath = relativePath;
exports.getFileExtension = getFileExtension;
exports.getBaseName = getBaseName;
exports.changeExtension = changeExtension;
exports.findFiles = findFiles;
exports.findCREDLFiles = findCREDLFiles;
exports.validateFile = validateFile;
exports.validateMultipleFiles = validateMultipleFiles;
exports.copyFile = copyFile;
exports.moveFile = moveFile;
exports.deleteFile = deleteFile;
exports.deleteDirectory = deleteDirectory;
exports.createTempFile = createTempFile;
const fs_1 = require("fs");
const path_1 = require("path");
const glob_1 = require("glob");
// File existence and metadata utilities
async function checkFileExists(filePath) {
try {
await fs_1.promises.access(filePath);
return true;
}
catch {
return false;
}
}
function checkFileExistsSync(filePath) {
return (0, fs_1.existsSync)(filePath);
}
async function getFileStats(filePath) {
try {
const stats = await fs_1.promises.stat(filePath);
return convertStatsToFileStats(stats, filePath);
}
catch {
return null;
}
}
function getFileStatsSync(filePath) {
try {
const stats = (0, fs_1.statSync)(filePath);
return convertStatsToFileStats(stats, filePath);
}
catch {
return null;
}
}
function convertStatsToFileStats(stats, filePath) {
return {
size: stats.size,
modified: stats.mtime,
created: stats.birthtime,
isFile: stats.isFile(),
isDirectory: stats.isDirectory(),
permissions: {
readable: checkReadable(filePath),
writable: checkWritable(filePath),
executable: checkExecutable(filePath)
}
};
}
function checkReadable(filePath) {
try {
require('fs').accessSync(filePath, require('fs').constants.R_OK);
return true;
}
catch {
return false;
}
}
function checkWritable(filePath) {
try {
require('fs').accessSync(filePath, require('fs').constants.W_OK);
return true;
}
catch {
return false;
}
}
function checkExecutable(filePath) {
try {
require('fs').accessSync(filePath, require('fs').constants.X_OK);
return true;
}
catch {
return false;
}
}
// Directory operations
async function ensureDirectory(dirPath) {
try {
await fs_1.promises.mkdir(dirPath, { recursive: true });
}
catch (error) {
if (error instanceof Error && 'code' in error && error.code !== 'EEXIST') {
throw new Error(`Failed to create directory ${dirPath}: ${error.message}`);
}
}
}
function ensureDirectorySync(dirPath) {
try {
require('fs').mkdirSync(dirPath, { recursive: true });
}
catch (error) {
if (error instanceof Error && 'code' in error && error.code !== 'EEXIST') {
throw new Error(`Failed to create directory ${dirPath}: ${error.message}`);
}
}
}
async function isDirectoryEmpty(dirPath) {
try {
const files = await fs_1.promises.readdir(dirPath);
return files.length === 0;
}
catch {
return true; // Consider non-existent directories as empty
}
}
// Safe file operations
async function safeWriteFile(filePath, content, options = {}) {
const { backup = false, backupSuffix = '.bak', createDirectories = true, overwrite = true, encoding = 'utf8' } = options;
// Resolve to absolute path
const absolutePath = (0, path_1.resolve)(filePath);
const directory = (0, path_1.dirname)(absolutePath);
// Create directories if needed
if (createDirectories) {
await ensureDirectory(directory);
}
// Check if file exists and handle overwrite
const fileExists = await checkFileExists(absolutePath);
if (fileExists && !overwrite) {
throw new Error(`File already exists and overwrite is disabled: ${filePath}`);
}
// Create backup if requested
if (backup && fileExists) {
const backupPath = absolutePath + backupSuffix;
await fs_1.promises.copyFile(absolutePath, backupPath);
}
// Write the file
try {
await fs_1.promises.writeFile(absolutePath, content, encoding);
}
catch (error) {
throw new Error(`Failed to write file ${filePath}: ${error instanceof Error ? error.message : String(error)}`);
}
}
async function safeReadFile(filePath, encoding = 'utf8') {
try {
const absolutePath = (0, path_1.resolve)(filePath);
// Check if file exists
if (!await checkFileExists(absolutePath)) {
throw new Error(`File not found: ${filePath}`);
}
// Check if it's actually a file
const stats = await getFileStats(absolutePath);
if (!stats?.isFile) {
throw new Error(`Path is not a file: ${filePath}`);
}
// Read the file
return await fs_1.promises.readFile(absolutePath, encoding);
}
catch (error) {
if (error instanceof Error && error.message.includes('File not found')) {
throw error;
}
throw new Error(`Failed to read file ${filePath}: ${error instanceof Error ? error.message : String(error)}`);
}
}
// Path utilities
function resolvePath(inputPath, basePath) {
if (basePath) {
return (0, path_1.resolve)(basePath, inputPath);
}
return (0, path_1.resolve)(inputPath);
}
function relativePath(from, to) {
return (0, path_1.relative)(from, to);
}
function getFileExtension(filePath) {
return (0, path_1.extname)(filePath).toLowerCase();
}
function getBaseName(filePath, ext) {
return (0, path_1.basename)(filePath, ext);
}
function changeExtension(filePath, newExt) {
const dir = (0, path_1.dirname)(filePath);
const name = (0, path_1.basename)(filePath, (0, path_1.extname)(filePath));
const extension = newExt.startsWith('.') ? newExt : `.${newExt}`;
return (0, path_1.join)(dir, name + extension);
}
// Glob pattern matching
async function findFiles(pattern, options = {}) {
const { cwd = process.cwd(), absolute = false, onlyFiles = true, onlyDirectories = false, ignore = [], caseSensitive = true } = options;
try {
const patterns = Array.isArray(pattern) ? pattern : [pattern];
const allFiles = [];
for (const pat of patterns) {
const files = await (0, glob_1.glob)(pat, {
cwd,
absolute,
ignore,
nocase: !caseSensitive,
nodir: onlyFiles && !onlyDirectories
});
allFiles.push(...files);
}
// Remove duplicates and sort
return [...new Set(allFiles)].sort();
}
catch (error) {
throw new Error(`Failed to find files with pattern "${pattern}": ${error instanceof Error ? error.message : String(error)}`);
}
}
async function findCREDLFiles(searchPath = '.', options = {}) {
const { recursive = true } = options;
const patterns = recursive ?
['**/*.credl', '**/*.yaml', '**/*.yml'] :
['*.credl', '*.yaml', '*.yml'];
return await findFiles(patterns, {
cwd: searchPath,
absolute: true,
onlyFiles: true,
ignore: ['node_modules/**', '.git/**', 'dist/**', 'build/**']
});
}
async function validateFile(filePath, options = {}) {
const { maxSize = 50 * 1024 * 1024, // 50MB default
allowedExtensions = ['.credl', '.yaml', '.yml'], requireExtension = true, checkReadable = true } = options;
const result = {
isValid: true,
errors: [],
warnings: [],
info: {
path: (0, path_1.resolve)(filePath),
size: 0,
extension: '',
lastModified: new Date()
}
};
try {
// Check if file exists
if (!await checkFileExists(filePath)) {
result.errors.push(`File does not exist: ${filePath}`);
result.isValid = false;
return result;
}
// Get file stats
const stats = await getFileStats(filePath);
if (!stats) {
result.errors.push(`Cannot access file: ${filePath}`);
result.isValid = false;
return result;
}
// Update info
result.info.size = stats.size;
result.info.lastModified = stats.modified;
result.info.extension = getFileExtension(filePath);
// Check if it's a file
if (!stats.isFile) {
result.errors.push(`Path is not a file: ${filePath}`);
result.isValid = false;
}
// Check file size
if (stats.size > maxSize) {
result.errors.push(`File is too large: ${(stats.size / 1024 / 1024).toFixed(2)}MB (max: ${(maxSize / 1024 / 1024).toFixed(2)}MB)`);
result.isValid = false;
}
// Check if file is empty
if (stats.size === 0) {
result.warnings.push('File is empty');
}
// Check file extension
const extension = getFileExtension(filePath);
if (requireExtension && !extension) {
result.warnings.push('File has no extension');
}
if (extension && allowedExtensions.length > 0 && !allowedExtensions.includes(extension)) {
result.warnings.push(`Unexpected file extension: ${extension} (expected: ${allowedExtensions.join(', ')})`);
}
// Check readability
if (checkReadable && !stats.permissions.readable) {
result.errors.push(`File is not readable: ${filePath}`);
result.isValid = false;
}
}
catch (error) {
result.errors.push(`File validation error: ${error instanceof Error ? error.message : String(error)}`);
result.isValid = false;
}
return result;
}
// Batch file operations
async function validateMultipleFiles(filePaths, options = {}) {
const { continueOnError = true, maxConcurrent = 10 } = options;
const results = [];
// Process files in batches to avoid overwhelming the system
for (let i = 0; i < filePaths.length; i += maxConcurrent) {
const batch = filePaths.slice(i, i + maxConcurrent);
const batchPromises = batch.map(async (filePath) => {
try {
return await validateFile(filePath);
}
catch (error) {
const errorResult = {
isValid: false,
errors: [`Validation failed: ${error instanceof Error ? error.message : String(error)}`],
warnings: [],
info: {
path: (0, path_1.resolve)(filePath),
size: 0,
extension: getFileExtension(filePath),
lastModified: new Date()
}
};
if (!continueOnError) {
throw error;
}
return errorResult;
}
});
const batchResults = await Promise.all(batchPromises);
results.push(...batchResults);
}
return results;
}
// File copying and moving utilities
async function copyFile(source, destination, options = {}) {
const { overwrite = false, createDirectories = true } = options;
const sourcePath = (0, path_1.resolve)(source);
const destPath = (0, path_1.resolve)(destination);
// Check source exists
if (!await checkFileExists(sourcePath)) {
throw new Error(`Source file does not exist: ${source}`);
}
// Check if destination exists
const destExists = await checkFileExists(destPath);
if (destExists && !overwrite) {
throw new Error(`Destination file already exists: ${destination}`);
}
// Create destination directory if needed
if (createDirectories) {
await ensureDirectory((0, path_1.dirname)(destPath));
}
try {
await fs_1.promises.copyFile(sourcePath, destPath);
}
catch (error) {
throw new Error(`Failed to copy file: ${error instanceof Error ? error.message : String(error)}`);
}
}
async function moveFile(source, destination, options = {}) {
const { overwrite = false, createDirectories = true } = options;
const sourcePath = (0, path_1.resolve)(source);
const destPath = (0, path_1.resolve)(destination);
// Check source exists
if (!await checkFileExists(sourcePath)) {
throw new Error(`Source file does not exist: ${source}`);
}
// Check if destination exists
const destExists = await checkFileExists(destPath);
if (destExists && !overwrite) {
throw new Error(`Destination file already exists: ${destination}`);
}
// Create destination directory if needed
if (createDirectories) {
await ensureDirectory((0, path_1.dirname)(destPath));
}
try {
await fs_1.promises.rename(sourcePath, destPath);
}
catch (error) {
throw new Error(`Failed to move file: ${error instanceof Error ? error.message : String(error)}`);
}
}
// Cleanup utilities
async function deleteFile(filePath, options = {}) {
const { force = false } = options;
const absolutePath = (0, path_1.resolve)(filePath);
if (!await checkFileExists(absolutePath)) {
if (!force) {
throw new Error(`File does not exist: ${filePath}`);
}
return; // Silently succeed if force is true
}
try {
await fs_1.promises.unlink(absolutePath);
}
catch (error) {
throw new Error(`Failed to delete file: ${error instanceof Error ? error.message : String(error)}`);
}
}
async function deleteDirectory(dirPath, options = {}) {
const { recursive = false, force = false } = options;
const absolutePath = (0, path_1.resolve)(dirPath);
if (!await checkFileExists(absolutePath)) {
if (!force) {
throw new Error(`Directory does not exist: ${dirPath}`);
}
return; // Silently succeed if force is true
}
try {
await fs_1.promises.rmdir(absolutePath, { recursive });
}
catch (error) {
throw new Error(`Failed to delete directory: ${error instanceof Error ? error.message : String(error)}`);
}
}
// Temporary file utilities
async function createTempFile(prefix = 'credl-', suffix = '.tmp') {
const tmpDir = require('os').tmpdir();
const timestamp = Date.now();
const random = Math.random().toString(36).substring(2, 8);
const tempFileName = `${prefix}${timestamp}-${random}${suffix}`;
const tempPath = (0, path_1.join)(tmpDir, tempFileName);
// Ensure the temp file doesn't already exist
if (await checkFileExists(tempPath)) {
return createTempFile(prefix, suffix); // Retry with different name
}
// Create empty file
await fs_1.promises.writeFile(tempPath, '', 'utf8');
return tempPath;
}
// Export utility functions
exports.fileUtils = {
// Existence and stats
exists: checkFileExists,
existsSync: checkFileExistsSync,
stats: getFileStats,
statsSync: getFileStatsSync,
// Directory operations
ensureDir: ensureDirectory,
ensureDirSync: ensureDirectorySync,
isDirEmpty: isDirectoryEmpty,
// Safe operations
safeWrite: safeWriteFile,
safeRead: safeReadFile,
// Path utilities
resolve: resolvePath,
relative: relativePath,
ext: getFileExtension,
basename: getBaseName,
changeExt: changeExtension,
// File finding
find: findFiles,
findCREDL: findCREDLFiles,
// Validation
validate: validateFile,
validateMultiple: validateMultipleFiles,
// File operations
copy: copyFile,
move: moveFile,
delete: deleteFile,
deleteDir: deleteDirectory,
// Temporary files
createTemp: createTempFile
};
//# sourceMappingURL=files.js.map