@syntropysoft/praetorian
Version:
Praetorian CLI – A universal multi-environment configuration validator for DevSecOps teams. Validate, compare, and secure YAML/ENV files with ease.
319 lines • 9.66 kB
JavaScript
"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,
key: this.currentKey || undefined // Store the key that this array belongs to
});
this.currentKey = null; // Clear the key since we're starting a new array
}
endArray() {
if (this.stack.length === 0)
return;
const array = this.stack.pop();
if (array.type === 'array') {
// If we're at root level (stack is now empty), add directly to result
if (this.stack.length === 0) {
if (array.key) {
this.result[array.key] = array.data;
}
return;
}
// Add the array to the parent context using the stored key
const parent = this.stack[this.stack.length - 1];
if (parent.type === 'dict' && array.key) {
parent.data[array.key] = array.data;
}
else if (parent.type === 'array') {
parent.data.push(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