UNPKG

@syntropysoft/praetorian

Version:

Praetorian CLI – A universal multi-environment configuration validator for DevSecOps teams. Validate, compare, and secure YAML/ENV files with ease.

257 lines 7.64 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.parsePropertiesValue = exports.parsePropertiesContent = exports.PropertiesFileAdapter = void 0; const AbstractFileAdapter_1 = require("../base/AbstractFileAdapter"); /** * Properties File Adapter - Functional Programming * * Single Responsibility: Parse Java Properties files only * Pure functions, no state, no side effects */ class PropertiesFileAdapter extends AbstractFileAdapter_1.AbstractFileAdapter { canHandle(filePath) { // Guard clause: no file path if (!filePath || typeof filePath !== 'string') { return false; } return filePath.endsWith('.properties'); } async read(filePath) { // Guard clause: no file path if (!filePath || typeof filePath !== 'string') { throw new Error('File path is required'); } this.validateFileExists(filePath); try { const content = await this.readFileContent(filePath); return (0, exports.parsePropertiesContent)(content); } catch (error) { throw new Error(`Failed to parse Properties file ${filePath}: ${error instanceof Error ? error.message : 'Unknown error'}`); } } getFormat() { return 'properties'; } getSupportedExtensions() { return ['.properties']; } } exports.PropertiesFileAdapter = PropertiesFileAdapter; /** * Pure function to parse properties content */ const parsePropertiesContent = (content) => { // Guard clause: no content if (!content || typeof content !== 'string') { return {}; } const lines = splitIntoLines(content); const processedLines = processMultilineProperties(lines); const keyValuePairs = extractKeyValuePairs(processedLines); return keyValuePairs.reduce((result, pair) => { if (pair.key) { result[pair.key] = (0, exports.parsePropertiesValue)(pair.value); } return result; }, {}); }; exports.parsePropertiesContent = parsePropertiesContent; /** * Pure function to split content into lines */ const splitIntoLines = (content) => { // Guard clause: empty content if (!content) { return []; } return content.split('\n'); }; /** * Pure function to process multi-line properties */ const processMultilineProperties = (lines) => { // Guard clause: no lines if (!lines || lines.length === 0) { return []; } return lines.reduce((processed, line) => { const trimmedLine = line.trim(); // Skip empty lines and comments if (isCommentOrEmpty(trimmedLine)) { return processed; } // Handle continuation from previous line if (processed.length > 0 && processed[processed.length - 1].endsWith('\\')) { const lastLine = processed[processed.length - 1]; processed[processed.length - 1] = lastLine.slice(0, -1) + trimmedLine; } else { processed.push(trimmedLine); } return processed; }, []); }; /** * Pure function to check if line is comment or empty */ const isCommentOrEmpty = (line) => { // Guard clause: no line if (!line) { return true; } return line === '' || line.startsWith('#') || line.startsWith('!'); }; /** * Pure function to extract key-value pairs from lines */ const extractKeyValuePairs = (lines) => { // Guard clause: no lines if (!lines || lines.length === 0) { return []; } return lines .map(extractKeyValuePair) .filter(pair => pair !== null); }; /** * Pure function to extract key-value pair from a single line */ const extractKeyValuePair = (line) => { // Guard clause: no line if (!line) { return null; } const separatorInfo = findSeparator(line); if (separatorInfo.index > 0) { const key = line.substring(0, separatorInfo.index).trim(); const value = line.substring(separatorInfo.index + separatorInfo.separator.length).trim(); if (key) { return { key, value }; } } else { // Handle cases where there's no clear separator - treat as key with empty value // This handles cases like "key " (key with trailing space) const trimmedLine = line.trim(); if (trimmedLine && !trimmedLine.includes('=') && !trimmedLine.includes(':')) { return { key: trimmedLine, value: '' }; } } return null; }; /** * Pure function to find the best separator in a line */ const findSeparator = (line) => { // Guard clause: no line if (!line) { return { index: -1, separator: '' }; } const separators = ['=', ':', ' ']; for (const separator of separators) { const index = line.indexOf(separator); if (index >= 0) { return { index, separator }; } } return { index: -1, separator: '' }; }; /** * Pure function to check if line has any separator */ const hasSeparator = (line) => { // Guard clause: no line if (!line) { return false; } return line.includes('=') || line.includes(':') || line.includes(' '); }; /** * Pure function to parse a property value */ const parsePropertiesValue = (value) => { // Guard clause: no value if (!value || value === null || value === undefined) { return ''; } // Check if value is quoted - if so, return as string without quotes const isQuoted = isQuotedValue(value); if (isQuoted) { return removeQuotes(value); } // Parse boolean values (only for unquoted values) const booleanValue = parseBoolean(value); if (booleanValue !== null) { return booleanValue; } // Parse numeric values (only for unquoted values) const numericValue = parseNumber(value); if (numericValue !== null) { return numericValue; } // Return as string return value; }; exports.parsePropertiesValue = parsePropertiesValue; /** * Pure function to check if value is quoted */ const isQuotedValue = (value) => { // Guard clause: no value if (!value || value.length < 2) { return false; } return (value.startsWith('"') && value.endsWith('"')) || (value.startsWith("'") && value.endsWith("'")); }; /** * Pure function to remove quotes from value */ const removeQuotes = (value) => { // Guard clause: no value if (!value) { return value; } if (isQuotedValue(value)) { return value.slice(1, -1); } return value; }; /** * Pure function to parse boolean values */ const parseBoolean = (value) => { // Guard clause: no value if (!value) { return null; } const lowerValue = value.toLowerCase(); if (lowerValue === 'true' || lowerValue === 'yes' || lowerValue === 'on') { return true; } if (lowerValue === 'false' || lowerValue === 'no' || lowerValue === 'off') { return false; } return null; }; /** * Pure function to parse numeric values */ const parseNumber = (value) => { // Guard clause: no value or empty string if (!value || value === '') { return null; } // Guard clause: values starting with 0 but not "0" or "0.x" are not valid numbers if (value.length > 1 && value.startsWith('0') && !value.includes('.')) { return null; } const numericValue = Number(value); // Guard clause: not a valid number if (isNaN(numericValue)) { return null; } return numericValue; }; //# sourceMappingURL=PropertiesFileAdapter.js.map