UNPKG

@syntropysoft/praetorian

Version:

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

300 lines 8.86 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.PlistFileAdapterV2 = void 0; const AbstractFileAdapter_1 = require("../base/AbstractFileAdapter"); class StringValueParser { canParse(line) { return line.includes('<string>'); } parse(line) { const match = line.match(/<string>(.*?)<\/string>/); return match ? this.parseValue(match[1]) : null; } parseValue(value) { if (value === 'true') return true; if (value === 'false') return false; if (!isNaN(Number(value))) return Number(value); return value; } } class IntegerValueParser { canParse(line) { return line.includes('<integer>'); } parse(line) { const match = line.match(/<integer>(.*?)<\/integer>/); return match ? parseInt(match[1], 10) : null; } } class RealValueParser { canParse(line) { return line.includes('<real>'); } parse(line) { const match = line.match(/<real>(.*?)<\/real>/); return match ? parseFloat(match[1]) : null; } } class BooleanValueParser { canParse(line) { return line.includes('<true/>') || line.includes('<false/>'); } parse(line) { if (line.includes('<true/>')) return true; if (line.includes('<false/>')) return false; return null; } } class DateValueParser { canParse(line) { return line.includes('<date>'); } parse(line) { const match = line.match(/<date>(.*?)<\/date>/); return match ? new Date(match[1]) : null; } } class DataValueParser { canParse(line) { return line.includes('<data>'); } parse(line) { const match = line.match(/<data>(.*?)<\/data>/); return match ? match[1] : null; // Keep as base64 string } } class KeyParser { canParse(line) { return line.includes('<key>'); } parse(line) { const match = line.match(/<key>(.*?)<\/key>/); return match ? { type: 'key', key: match[1] } : { type: 'skip' }; } } class DictStartParser { canParse(line) { return line.includes('<dict>'); } parse(line) { return { type: 'dict_start' }; } } class DictEndParser { canParse(line) { return line.includes('</dict>'); } parse(line) { return { type: 'dict_end' }; } } class ArrayStartParser { canParse(line) { return line.includes('<array>'); } parse(line) { return { type: 'array_start' }; } } class ArrayEndParser { canParse(line) { return line.includes('</array>'); } parse(line) { return { type: 'array_end' }; } } class ValueLineParser { constructor() { this.valueParsers = [ new StringValueParser(), new IntegerValueParser(), new RealValueParser(), new BooleanValueParser(), new DateValueParser(), new DataValueParser() ]; } canParse(line) { return this.valueParsers.some(parser => parser.canParse(line)); } parse(line) { const parser = this.valueParsers.find(p => p.canParse(line)); const value = parser ? parser.parse(line) : null; return { type: 'value', value }; } } // ============================================================================ // STATE MANAGER // ============================================================================ class PlistStateManager { constructor() { this.result = {}; this.currentKey = null; this.stack = []; } processLine(parsedLine) { switch (parsedLine.type) { case 'key': this.currentKey = parsedLine.key; break; case 'value': this.addValue(parsedLine.value); break; case 'dict_start': this.startDict(); break; case 'dict_end': this.endDict(); break; case 'array_start': this.startArray(); break; case 'array_end': this.endArray(); break; } } addValue(value) { if (this.stack.length === 0) { // Root level - add directly to result if (this.currentKey) { this.result[this.currentKey] = value; this.currentKey = null; } } else { // Nested level const current = this.stack[this.stack.length - 1]; if (current.type === 'dict' && this.currentKey) { current.data[this.currentKey] = value; this.currentKey = null; } else if (current.type === 'array') { current.data.push(value); } } } startDict() { const dict = {}; // Store the current key with the dict this.stack.push({ type: 'dict', data: dict, key: this.currentKey || undefined // Store the key that this dict belongs to }); this.currentKey = null; // Clear the key since we're starting a new dict } endDict() { if (this.stack.length === 0) return; const dict = this.stack.pop(); if (dict.type === 'dict') { // If we're at root level (stack is now empty), merge the dict into result if (this.stack.length === 0) { Object.assign(this.result, dict.data); } else { // Otherwise, add the dict as a value to the parent // Use the stored key if available if (dict.key) { // Temporarily set the current key to the stored key const originalKey = this.currentKey; this.currentKey = dict.key; this.addValue(dict.data); this.currentKey = originalKey; } else { this.addValue(dict.data); } } } } startArray() { const array = []; this.stack.push({ type: 'array', data: array }); } endArray() { if (this.stack.length === 0) return; const array = this.stack.pop(); if (array.type === 'array') { this.addValue(array.data); } } getResult() { return this.result; } } // ============================================================================ // MAIN PARSER // ============================================================================ class PlistParser { constructor() { this.lineParsers = [ new KeyParser(), new ValueLineParser(), new DictStartParser(), new DictEndParser(), new ArrayStartParser(), new ArrayEndParser() ]; } parse(content) { const stateManager = new PlistStateManager(); const lines = content .split('\n') .map(line => line.trim()) .filter(line => this.isRelevantLine(line)); lines.forEach((line, index) => { const parser = this.lineParsers.find(p => p.canParse(line)); if (parser) { const parsedLine = parser.parse(line); stateManager.processLine(parsedLine); } }); return stateManager.getResult(); } isRelevantLine(line) { return line.length > 0 && !line.startsWith('<!--') && !line.startsWith('<?xml') && !line.startsWith('<!DOCTYPE') && !line.startsWith('<plist version'); } } // ============================================================================ // FILE ADAPTER // ============================================================================ class PlistFileAdapterV2 extends AbstractFileAdapter_1.AbstractFileAdapter { constructor() { super(...arguments); this.parser = new PlistParser(); } canHandle(filePath) { return filePath.endsWith('.plist'); } async read(filePath) { this.validateFileExists(filePath); try { const content = await this.readFileContent(filePath); return this.parser.parse(content); } catch (error) { throw new Error(`Failed to parse PLIST file ${filePath}: ${error instanceof Error ? error.message : 'Unknown error'}`); } } getFormat() { return 'plist'; } getSupportedExtensions() { return ['.plist']; } } exports.PlistFileAdapterV2 = PlistFileAdapterV2; //# sourceMappingURL=PlistFileAdapterV2.js.map