credl-parser-evaluator
Version:
TypeScript-based CREDL Parser and Evaluator that processes CREDL files and outputs complete Intermediate Representations
265 lines • 9.27 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.PresetResolver = void 0;
/**
* PresetResolver handles parsing and storage of preset definitions
*/
class PresetResolver {
constructor() {
this.presets = new Map();
}
/**
* Parse and store preset definitions from a CREDL file
*/
parsePresets(credlFile, errors, warnings) {
if (credlFile.presets === undefined) {
return; // No presets to process
}
// Validate presets structure
if (typeof credlFile.presets !== 'object' || credlFile.presets === null) {
errors.push({
field: 'presets',
message: 'must be an object',
severity: 'error'
});
return;
}
// Process each preset category (lease_profiles, expense_profiles, etc.)
Object.keys(credlFile.presets).forEach(category => {
const categoryData = credlFile.presets[category];
if (categoryData && typeof categoryData === 'object') {
this.processPresetCategory(category, categoryData, errors, warnings);
}
});
}
/**
* Process a preset category
*/
processPresetCategory(category, categoryData, errors, warnings) {
Object.keys(categoryData).forEach(presetName => {
const presetData = categoryData[presetName];
// Always validate, even if presetData is null/undefined, so we can generate errors
this.validateAndStorePreset(category, presetName, presetData, errors, warnings);
});
}
/**
* Validate and store a single preset definition
*/
validateAndStorePreset(category, presetName, presetData, errors, warnings) {
const fieldPrefix = `presets.${category}.${presetName}`;
// Validate preset structure
if (!presetData || typeof presetData !== 'object') {
errors.push({
field: fieldPrefix,
message: 'Preset data must be an object',
severity: 'error'
});
return;
}
// Validate preset name format
if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(presetName)) {
warnings.push({
field: fieldPrefix,
message: `Preset name '${presetName}' should follow identifier naming conventions (alphanumeric and underscore only)`,
severity: 'warning'
});
}
// Validate preset values
this.validatePresetValues(presetData, fieldPrefix, errors, warnings);
// Store preset with full category.name path
const fullPresetName = `${category}.${presetName}`;
this.presets.set(fullPresetName, presetData);
}
/**
* Validate preset values structure and types
*/
validatePresetValues(values, fieldPrefix, errors, warnings) {
if (!values || typeof values !== 'object') {
errors.push({
field: fieldPrefix,
message: 'Preset values must be an object',
severity: 'error'
});
return;
}
// Validate each preset value
Object.keys(values).forEach(key => {
const value = values[key];
const valueFieldPrefix = `${fieldPrefix}.${key}`;
// Validate value key format
if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(key)) {
warnings.push({
field: valueFieldPrefix,
message: `Preset value key '${key}' should follow identifier naming conventions`,
severity: 'warning'
});
}
// Validate value types (allow primitives and simple objects)
if (!this.isValidPresetValue(value)) {
errors.push({
field: valueFieldPrefix,
message: 'Preset values must be primitives (string, number, boolean) or simple objects',
severity: 'error'
});
}
// Warn about complex nested structures
if (typeof value === 'object' && value !== null && this.getObjectDepth(value) > 2) {
warnings.push({
field: valueFieldPrefix,
message: 'Deep nested objects in preset values may be difficult to reference',
severity: 'warning'
});
}
});
}
/**
* Check if a value is valid for preset storage
*/
isValidPresetValue(value) {
if (value === null || value === undefined) {
return true;
}
const type = typeof value;
if (type === 'string' || type === 'number' || type === 'boolean') {
return true;
}
if (type === 'object') {
if (Array.isArray(value)) {
return value.every(item => this.isValidPresetValue(item));
}
return Object.values(value).every(val => this.isValidPresetValue(val));
}
return false;
}
/**
* Calculate the depth of nested objects
*/
getObjectDepth(obj) {
if (typeof obj !== 'object' || obj === null) {
return 0;
}
let maxDepth = 0;
Object.values(obj).forEach(value => {
const depth = this.getObjectDepth(value);
maxDepth = Math.max(maxDepth, depth);
});
return maxDepth + 1;
}
/**
* Get a preset by name
*/
getPreset(name) {
return this.presets.get(name);
}
/**
* Get a specific preset value by key path
*/
getPresetValue(key) {
// Parse key path like "lease_profiles.standard_office" or "lease_profiles.standard_office.rent_psf"
const parts = key.split('.');
if (parts.length < 2) {
return undefined;
}
// Handle full preset object reference (2 parts: category.preset)
if (parts.length === 2) {
const presetName = key;
return this.presets.get(presetName);
}
// Handle individual field reference (3+ parts: category.preset.field[.subfield])
const presetName = `${parts[0]}.${parts[1]}`;
const preset = this.presets.get(presetName);
if (!preset) {
return undefined;
}
// Navigate through the remaining path parts
let current = preset;
for (let i = 2; i < parts.length; i++) {
const part = parts[i];
if (current && typeof current === 'object' && part && part in current) {
current = current[part];
}
else {
return undefined;
}
}
return current;
}
/**
* Check if a preset exists
*/
hasPreset(name) {
return this.presets.has(name);
}
/**
* Get all preset names
*/
getPresetNames() {
return Array.from(this.presets.keys());
}
/**
* Resolve a preset reference (e.g., "@lease_profiles.standard_office.rent_psf" or "lease_profiles.standard_office.rent_psf")
*/
resolvePresetReference(reference) {
if (!reference || typeof reference !== 'string') {
return reference;
}
let key;
// Handle both @-prefixed and raw preset references for backward compatibility
if (this.isPresetReference(reference)) {
// Remove the @ prefix
key = reference.substring(1);
}
else {
// Assume it's a raw preset reference
key = reference;
}
const resolvedValue = this.getPresetValue(key);
// If we couldn't resolve it and it wasn't @-prefixed, return the original reference
// If it was @-prefixed but couldn't resolve, return undefined to indicate failure
if (resolvedValue === undefined) {
return this.isPresetReference(reference) ? undefined : reference;
}
return resolvedValue;
}
/**
* Check if a string is a preset reference
*/
isPresetReference(fieldName) {
return typeof fieldName === 'string' && fieldName.startsWith('@');
}
/**
* Get validation summary
*/
getValidationSummary() {
const presetNames = Array.from(this.presets.keys());
const sampleValues = [];
// Get sample values from first few presets
presetNames.slice(0, 3).forEach(presetName => {
const preset = this.presets.get(presetName);
if (preset && typeof preset === 'object') {
Object.keys(preset).slice(0, 2).forEach(key => {
sampleValues.push(`${presetName}.${key}`);
});
}
});
return {
totalPresets: this.presets.size,
presetNames,
sampleValues
};
}
/**
* Clear all presets
*/
clear() {
this.presets.clear();
}
/**
* Get all presets (for debugging/inspection)
*/
getAllPresets() {
return new Map(this.presets);
}
}
exports.PresetResolver = PresetResolver;
//# sourceMappingURL=PresetResolver.js.map