credl-parser-evaluator
Version:
TypeScript-based CREDL Parser and Evaluator that processes CREDL files and outputs complete Intermediate Representations
900 lines • 39.4 kB
JavaScript
"use strict";
/**
* CREDL YAML Parser
*
* Parses CREDL YAML files into strongly typed TypeScript objects with comprehensive
* error handling and validation. Supports parsing from files, strings, and streams.
*/
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.CREDLParser = void 0;
const fs_1 = require("fs");
const yaml = __importStar(require("yaml"));
const validation_messages_1 = require("./validation-messages");
class CREDLParser {
/**
* Parse a CREDL file from the filesystem
*/
static async parseFile(filePath, options) {
const opts = { ...this.DEFAULT_OPTIONS, ...options, filename: filePath };
try {
const content = await fs_1.promises.readFile(filePath, 'utf8');
return this.parseString(content, opts);
}
catch (error) {
// Enhanced file reading error handling with categorized messages
let errorMessage = validation_messages_1.VALIDATION_MESSAGES.GENERAL.PARSE_FAILED;
let helpText = validation_messages_1.VALIDATION_HELP.GENERAL.YAML_SYNTAX;
// Check for Node.js file system errors by error code (more reliable than instanceof)
const nodeError = error;
const errorMessage_text = (error instanceof Error ? error.message : '') || '';
if (nodeError.code === 'ENOENT' || errorMessage_text.includes('ENOENT')) {
errorMessage = `File not found: ${filePath}`;
helpText = 'Check that the file path is correct and the file exists.';
}
else if (nodeError.code === 'EACCES' || errorMessage_text.includes('EACCES')) {
errorMessage = `Permission denied: ${filePath}`;
helpText = 'Check file permissions. You may need read access to this file.';
}
else if (nodeError.code === 'EISDIR' || errorMessage_text.includes('EISDIR')) {
errorMessage = `Path is a directory, not a file: ${filePath}`;
helpText = 'Specify a .credl file path, not a directory.';
}
else if (error instanceof Error) {
errorMessage = `Failed to read file: ${error.message}`;
helpText = 'Ensure the file is accessible and not corrupted.';
}
else {
errorMessage = `Failed to read file: Unknown error`;
helpText = 'Ensure the file is accessible and not corrupted.';
}
return {
success: false,
errors: [{
field: 'file',
message: errorMessage,
severity: 'error',
help: helpText
}],
warnings: []
};
}
}
/**
* Parse a CREDL file from a readable stream
*/
static async parseStream(stream, options) {
const opts = { ...this.DEFAULT_OPTIONS, ...options };
try {
// Collect all chunks from the stream
const chunks = [];
return new Promise((resolve) => {
stream.on('data', (chunk) => {
chunks.push(chunk);
});
stream.on('end', () => {
try {
const content = Buffer.concat(chunks).toString('utf8');
const result = this.parseString(content, opts);
resolve(result);
}
catch (error) {
resolve({
success: false,
errors: [{
field: 'stream',
message: `Failed to process stream content: ${error instanceof Error ? error.message : 'Unknown error'}`,
severity: 'error'
}],
warnings: []
});
}
});
stream.on('error', (error) => {
resolve({
success: false,
errors: [{
field: 'stream',
message: `Stream error: ${error.message}`,
severity: 'error'
}],
warnings: []
});
});
// Handle timeout for streams that might hang
const timeout = setTimeout(() => {
resolve({
success: false,
errors: [{
field: 'stream',
message: 'Stream parsing timeout after 30 seconds',
severity: 'error'
}],
warnings: []
});
}, 30000); // 30 second timeout
stream.on('end', () => clearTimeout(timeout));
stream.on('error', () => clearTimeout(timeout));
});
}
catch (error) {
return {
success: false,
errors: [{
field: 'stream',
message: `Failed to parse stream: ${error instanceof Error ? error.message : 'Unknown error'}`,
severity: 'error'
}],
warnings: []
};
}
}
/**
* Parse a CREDL YAML string
*/
static parseString(content, options) {
const opts = { ...this.DEFAULT_OPTIONS, ...options };
const errors = [];
const warnings = [];
try {
// Parse YAML with position tracking for better error reporting
const yamlDocument = yaml.parseDocument(content, {
keepSourceTokens: true,
strict: opts.strict
});
// Check for YAML syntax errors with enhanced error context
if (yamlDocument.errors && yamlDocument.errors.length > 0) {
for (const error of yamlDocument.errors) {
// Categorize common YAML syntax errors for better guidance
let helpText = validation_messages_1.VALIDATION_HELP.GENERAL.YAML_SYNTAX;
if (error.message.includes('indentation')) {
helpText = 'YAML requires consistent indentation. Use 2 spaces for each level, not tabs.';
}
else if (error.message.includes('quote') || error.message.includes('string')) {
helpText = 'Check for unmatched quotes or special characters that need to be quoted.';
}
else if (error.message.includes('colon') || error.message.includes('mapping')) {
helpText = 'Ensure proper key: value syntax with spaces after colons.';
}
else if (error.message.includes('bracket') || error.message.includes('array')) {
helpText = 'Check for unmatched brackets or incorrect array syntax.';
}
errors.push({
field: 'yaml',
message: `${validation_messages_1.VALIDATION_MESSAGES.GENERAL.YAML_SYNTAX_ERROR}: ${error.message}`,
line: error.pos?.[0],
column: error.pos?.[1],
severity: 'error',
help: helpText
});
}
}
// Check for YAML warnings
if (yamlDocument.warnings && yamlDocument.warnings.length > 0) {
for (const warning of yamlDocument.warnings) {
warnings.push({
field: 'yaml',
message: `YAML warning: ${warning.message}`,
line: warning.pos?.[0],
column: warning.pos?.[1],
severity: 'warning'
});
}
}
// If there are syntax errors, return early
if (errors.length > 0) {
return { success: false, errors, warnings };
}
// Convert to JavaScript object
const rawData = yamlDocument.toJS();
if (!rawData || typeof rawData !== 'object') {
errors.push({
field: 'root',
message: validation_messages_1.VALIDATION_MESSAGES.GENERAL.INVALID_STRUCTURE,
severity: 'error',
help: 'CREDL files must start with a YAML object containing metadata, assets, spaces, and other required blocks.'
});
return { success: false, errors, warnings };
}
// Parse into typed CREDL structure
const parseResult = this.parseRawData(rawData, opts.filename);
if (parseResult.success) {
return {
success: true,
data: parseResult.data,
errors: [...errors, ...parseResult.errors],
warnings: [...warnings, ...parseResult.warnings]
};
}
else {
return {
success: false,
errors: [...errors, ...parseResult.errors],
warnings: [...warnings, ...parseResult.warnings]
};
}
}
catch (error) {
errors.push({
field: 'parser',
message: `Failed to parse YAML: ${error instanceof Error ? error.message : 'Unknown error'}`,
severity: 'error'
});
return { success: false, errors, warnings };
}
}
/**
* Parse raw JavaScript object into typed CREDL structure
*/
static parseRawData(rawData, _filename) {
const errors = [];
const warnings = [];
try {
// Parse each section with error collection
const metadata = this.parseMetadata(rawData.metadata, errors, warnings);
const assets = this.parseAssets(rawData.assets, errors);
const spaces = this.parseSpaces(rawData.spaces, errors);
const assumptions = this.parseAssumptions(rawData.assumptions, errors);
const models = this.parseModels(rawData.models, errors);
const simulation = this.parseSimulation(rawData.simulation, errors);
const outputs = this.parseOutput(rawData.outputs, errors);
// Parse optional first-class blocks (per Behavioral Specification 8.1)
const scenarios = rawData.scenarios ? this.parseScenarios(rawData.scenarios, errors) : undefined;
const waterfall = rawData.waterfall ? this.parseWaterfall(rawData.waterfall, errors) : undefined;
// Parse other optional sections (legacy support)
const extensions = rawData.extensions ? this.parseExtensions(rawData.extensions, errors) : undefined;
const presets = rawData.presets ? this.parsePresets(rawData.presets, errors) : undefined;
const templates = rawData.templates ? this.parseTemplates(rawData.templates, errors) : undefined;
const use_templates = rawData.use_templates ? this.parseUseTemplates(rawData.use_templates, errors) : undefined;
// If parsing failed, return errors
if (errors.length > 0) {
return { success: false, errors, warnings };
}
// Construct the complete CREDL file
const credlFile = {
metadata: metadata,
assets: assets,
spaces: spaces,
assumptions: assumptions,
models: models,
simulation: simulation,
outputs: outputs,
...(scenarios && { scenarios }),
...(waterfall && { waterfall }),
...(extensions && { extensions }),
...(presets && { presets }),
...(templates && { templates }),
...(use_templates && { use_templates })
};
return { success: true, data: credlFile, errors, warnings };
}
catch (error) {
// Enhanced structure parsing error handling with categorized messages
let errorMessage = validation_messages_1.VALIDATION_MESSAGES.GENERAL.VALIDATION_FAILED;
let helpText = validation_messages_1.VALIDATION_HELP.GENERAL.VALIDATION_ERRORS;
if (error instanceof Error) {
// Categorize common parsing errors
if (error.message.includes('circular') || error.message.includes('reference')) {
errorMessage = 'Circular reference detected in CREDL structure';
helpText = 'Remove circular references between blocks (e.g., asset → building → space → asset).';
}
else if (error.message.includes('memory') || error.message.includes('heap')) {
errorMessage = 'File too large or complex to process';
helpText = 'Consider breaking large files into smaller components or reducing complexity.';
}
else {
errorMessage = validation_messages_1.VALIDATION_MESSAGES.GENERAL.UNEXPECTED_ERROR(error.message);
helpText = validation_messages_1.VALIDATION_HELP.GENERAL.VALIDATION_ERRORS;
}
}
errors.push({
field: 'structure',
message: errorMessage,
severity: 'error',
help: helpText
});
return { success: false, errors, warnings };
}
}
/**
* Parse metadata block with standardized validation messages
*/
static parseMetadata(data, errors, warnings) {
if (!data) {
errors.push({
field: 'metadata',
message: validation_messages_1.VALIDATION_MESSAGES.METADATA.MISSING_BLOCK,
severity: 'error'
});
return null;
}
if (typeof data !== 'object') {
errors.push({
field: 'metadata',
message: validation_messages_1.VALIDATION_MESSAGES.METADATA.INVALID_STRUCTURE,
severity: 'error'
});
return null;
}
// Version validation with behavioral specification compliance
// Parser version 1.0 supports CREDL specification versions 0.1 and 0.2
const SUPPORTED_CREDL_VERSIONS = { min: 0.1, max: 0.2 };
if (typeof data.version !== 'string' || data.version.trim().length === 0) {
errors.push({
field: 'metadata.version',
message: validation_messages_1.VALIDATION_MESSAGES.METADATA.MISSING_VERSION,
severity: 'error',
help: validation_messages_1.VALIDATION_HELP.METADATA.VERSION_FORMAT
});
}
else {
// Parse CREDL specification version string and validate compatibility
const credlSpecVersion = parseFloat(data.version);
if (isNaN(credlSpecVersion)) {
errors.push({
field: 'metadata.version',
message: validation_messages_1.VALIDATION_MESSAGES.METADATA.INVALID_VERSION,
severity: 'error',
help: validation_messages_1.VALIDATION_HELP.METADATA.VERSION_FORMAT
});
}
else if (credlSpecVersion < SUPPORTED_CREDL_VERSIONS.min) {
errors.push({
field: 'metadata.version',
message: validation_messages_1.VALIDATION_MESSAGES.METADATA.UNSUPPORTED_VERSION(data.version, SUPPORTED_CREDL_VERSIONS.min.toString()),
severity: 'error',
help: validation_messages_1.VALIDATION_HELP.METADATA.VERSION_FORMAT
});
}
else if (credlSpecVersion > SUPPORTED_CREDL_VERSIONS.max) {
// Future CREDL specification versions generate warnings but don't stop processing
warnings.push({
field: 'metadata.version',
message: validation_messages_1.VALIDATION_MESSAGES.METADATA.NEWER_VERSION(data.version, SUPPORTED_CREDL_VERSIONS.max.toString()),
severity: 'warning',
help: validation_messages_1.VALIDATION_HELP.METADATA.VERSION_FORMAT
});
}
// Versions 0.1 and 0.2 are fully supported - no warnings
}
// Validate name field
if (typeof data.name !== 'string' || data.name.trim().length === 0) {
errors.push({
field: 'metadata.name',
message: validation_messages_1.VALIDATION_MESSAGES.METADATA.MISSING_NAME,
severity: 'error'
});
}
if (typeof data.description !== 'string' || data.description.trim().length === 0) {
errors.push({
field: 'metadata.description',
message: validation_messages_1.VALIDATION_MESSAGES.METADATA.MISSING_DESCRIPTION,
severity: 'error'
});
}
// New required field: analysis_start_date
if (typeof data.analysis_start_date !== 'string') {
errors.push({
field: 'metadata.analysis_start_date',
message: validation_messages_1.VALIDATION_MESSAGES.METADATA.MISSING_ANALYSIS_START,
severity: 'error',
help: validation_messages_1.VALIDATION_HELP.METADATA.ANALYSIS_START
});
}
else {
// Validate ISO 8601 date format (YYYY-MM-DD)
const dateRegex = /^\d{4}-\d{2}-\d{2}$/;
if (!dateRegex.test(data.analysis_start_date)) {
errors.push({
field: 'metadata.analysis_start_date',
message: validation_messages_1.VALIDATION_MESSAGES.METADATA.INVALID_DATE_FORMAT,
severity: 'error',
help: validation_messages_1.VALIDATION_HELP.METADATA.DATE_FORMAT
});
}
else {
// Validate it's a real date
const date = new Date(data.analysis_start_date);
if (isNaN(date.getTime())) {
errors.push({
field: 'metadata.analysis_start_date',
message: validation_messages_1.VALIDATION_MESSAGES.METADATA.INVALID_DATE,
severity: 'error',
help: validation_messages_1.VALIDATION_HELP.METADATA.DATE_FORMAT
});
}
}
}
if (typeof data.created_date !== 'string') {
errors.push({
field: 'metadata.created_date',
message: 'created_date is required and must be a string',
severity: 'error',
help: validation_messages_1.VALIDATION_HELP.METADATA.CREATED_DATE
});
}
else {
// Validate ISO 8601 date format (YYYY-MM-DD)
const dateRegex = /^\d{4}-\d{2}-\d{2}$/;
if (!dateRegex.test(data.created_date)) {
errors.push({
field: 'metadata.created_date',
message: validation_messages_1.VALIDATION_MESSAGES.METADATA.INVALID_DATE_FORMAT,
severity: 'error',
help: validation_messages_1.VALIDATION_HELP.METADATA.DATE_FORMAT
});
}
else {
// Validate it's a real date
const date = new Date(data.created_date);
if (isNaN(date.getTime())) {
errors.push({
field: 'metadata.created_date',
message: validation_messages_1.VALIDATION_MESSAGES.METADATA.INVALID_DATE,
severity: 'error',
help: validation_messages_1.VALIDATION_HELP.METADATA.DATE_FORMAT
});
}
}
}
// Validate optional modified_date field if present
if (data.modified_date !== undefined) {
if (typeof data.modified_date !== 'string') {
errors.push({
field: 'metadata.modified_date',
message: 'modified_date must be a string if provided',
severity: 'error',
help: validation_messages_1.VALIDATION_HELP.METADATA.DATE_FORMAT
});
}
else {
const dateRegex = /^\d{4}-\d{2}-\d{2}$/;
if (!dateRegex.test(data.modified_date)) {
errors.push({
field: 'metadata.modified_date',
message: validation_messages_1.VALIDATION_MESSAGES.METADATA.INVALID_DATE_FORMAT,
severity: 'error',
help: validation_messages_1.VALIDATION_HELP.METADATA.DATE_FORMAT
});
}
else {
// Validate it's a real date
const date = new Date(data.modified_date);
if (isNaN(date.getTime())) {
errors.push({
field: 'metadata.modified_date',
message: validation_messages_1.VALIDATION_MESSAGES.METADATA.INVALID_DATE,
severity: 'error',
help: validation_messages_1.VALIDATION_HELP.METADATA.DATE_FORMAT
});
}
}
}
}
if (errors.some(e => e.field.startsWith('metadata'))) {
return null;
}
return {
version: data.version,
name: data.name,
description: data.description,
analysis_start_date: data.analysis_start_date,
created_date: data.created_date,
modified_date: data.modified_date,
author: data.author
};
}
/**
* Parse assets block with enhanced error handling and recovery
*/
static parseAssets(data, errors) {
if (!Array.isArray(data)) {
errors.push({
field: 'assets',
message: validation_messages_1.VALIDATION_MESSAGES.ASSETS.MISSING_ARRAY,
severity: 'error',
help: validation_messages_1.VALIDATION_HELP.ASSETS.REQUIRED_FIELDS
});
return null;
}
if (data.length === 0) {
errors.push({
field: 'assets',
message: validation_messages_1.VALIDATION_MESSAGES.ASSETS.EMPTY_ARRAY,
severity: 'error',
help: validation_messages_1.VALIDATION_HELP.ASSETS.REQUIRED_FIELDS
});
return null;
}
const assets = [];
let hasGlobalErrors = false;
for (let i = 0; i < data.length; i++) {
const asset = data[i];
const fieldPrefix = `assets[${i}]`;
let hasAssetErrors = false;
if (!asset || typeof asset !== 'object') {
errors.push({
field: fieldPrefix,
message: validation_messages_1.VALIDATION_MESSAGES.ASSETS.INVALID_ASSET,
severity: 'error',
help: validation_messages_1.VALIDATION_HELP.ASSETS.REQUIRED_FIELDS
});
hasAssetErrors = true;
hasGlobalErrors = true;
continue;
}
// Enhanced field validation with specific error messages and help text
const requiredFields = [
{ name: 'id', message: validation_messages_1.VALIDATION_MESSAGES.ASSETS.MISSING_ID },
{ name: 'name', message: validation_messages_1.VALIDATION_MESSAGES.ASSETS.MISSING_NAME },
{ name: 'property_type', message: 'property_type is required and must be a valid type', help: validation_messages_1.VALIDATION_HELP.ASSETS.PROPERTY_TYPES },
{ name: 'location', message: validation_messages_1.VALIDATION_MESSAGES.ASSETS.MISSING_LOCATION },
{ name: 'total_area_sf', message: validation_messages_1.VALIDATION_MESSAGES.ASSETS.MISSING_AREA },
{ name: 'buildings', message: validation_messages_1.VALIDATION_MESSAGES.ASSETS.MISSING_BUILDINGS }
];
for (const field of requiredFields) {
const value = asset[field.name];
let isInvalid = false;
if (field.name === 'total_area_sf') {
// Numeric field: check if missing, not a number, or not positive
isInvalid = value === undefined || value === null || typeof value !== 'number' || value <= 0;
}
else {
// String field: check if missing or empty string
isInvalid = !value || (typeof value === 'string' && value.trim() === '');
}
if (isInvalid) {
errors.push({
field: `${fieldPrefix}.${field.name}`,
message: field.message,
severity: 'error',
help: field.help || validation_messages_1.VALIDATION_HELP.ASSETS.REQUIRED_FIELDS
});
hasAssetErrors = true;
hasGlobalErrors = true;
}
}
// Validate buildings array with improved error recovery
if (asset.buildings !== undefined && !Array.isArray(asset.buildings)) {
errors.push({
field: `${fieldPrefix}.buildings`,
message: validation_messages_1.VALIDATION_MESSAGES.ASSETS.MISSING_BUILDINGS,
severity: 'error',
help: validation_messages_1.VALIDATION_HELP.ASSETS.REQUIRED_FIELDS
});
hasAssetErrors = true;
hasGlobalErrors = true;
}
else if (Array.isArray(asset.buildings) && asset.buildings.length === 0) {
errors.push({
field: `${fieldPrefix}.buildings`,
message: validation_messages_1.VALIDATION_MESSAGES.ASSETS.EMPTY_BUILDINGS,
severity: 'error',
help: validation_messages_1.VALIDATION_HELP.ASSETS.REQUIRED_FIELDS
});
hasAssetErrors = true;
hasGlobalErrors = true;
}
// Continue parsing valid assets even if some have errors (improved error recovery)
if (!hasAssetErrors) {
assets.push(asset);
}
}
// Return partial results if we have some valid assets (improved error recovery)
return hasGlobalErrors && assets.length === 0 ? null : assets;
}
/**
* Parse spaces block with enhanced error handling and recovery
*/
static parseSpaces(data, errors) {
if (!Array.isArray(data)) {
errors.push({
field: 'spaces',
message: validation_messages_1.VALIDATION_MESSAGES.SPACES.MISSING_ARRAY,
severity: 'error',
help: validation_messages_1.VALIDATION_HELP.SPACES.REQUIRED_FIELDS
});
return null;
}
const spaces = [];
let hasGlobalErrors = false;
for (let i = 0; i < data.length; i++) {
const space = data[i];
const fieldPrefix = `spaces[${i}]`;
let hasSpaceErrors = false;
if (!space || typeof space !== 'object') {
errors.push({
field: fieldPrefix,
message: validation_messages_1.VALIDATION_MESSAGES.SPACES.INVALID_SPACE,
severity: 'error',
help: validation_messages_1.VALIDATION_HELP.SPACES.REQUIRED_FIELDS
});
hasSpaceErrors = true;
hasGlobalErrors = true;
continue;
}
// Enhanced field validation with specific error messages and help text
const requiredFields = [
{ name: 'id', message: validation_messages_1.VALIDATION_MESSAGES.SPACES.MISSING_ID },
{ name: 'parent_building', message: validation_messages_1.VALIDATION_MESSAGES.SPACES.MISSING_PARENT_BUILDING, help: validation_messages_1.VALIDATION_HELP.SPACES.PARENT_BUILDING },
{ name: 'type', message: 'type is required and must be a valid space type' },
{ name: 'area_sf', message: validation_messages_1.VALIDATION_MESSAGES.SPACES.MISSING_AREA }
];
for (const field of requiredFields) {
const value = space[field.name];
let isInvalid = false;
if (field.name === 'area_sf') {
// Numeric field: check if missing, not a number, or not positive
isInvalid = value === undefined || value === null || typeof value !== 'number' || value <= 0;
}
else {
// String field: check if missing or empty string
isInvalid = !value || (typeof value === 'string' && value.trim() === '');
}
if (isInvalid) {
errors.push({
field: `${fieldPrefix}.${field.name}`,
message: field.message,
severity: 'error',
help: field.help || validation_messages_1.VALIDATION_HELP.SPACES.REQUIRED_FIELDS
});
hasSpaceErrors = true;
hasGlobalErrors = true;
}
}
// Check for auto-populated fields that shouldn't be manually specified
if (space.source_template !== undefined) {
errors.push({
field: `${fieldPrefix}.source_template`,
message: validation_messages_1.VALIDATION_MESSAGES.SPACES.AUTO_POPULATED_FIELD,
severity: 'error',
help: validation_messages_1.VALIDATION_HELP.SPACES.SOURCE_TEMPLATE
});
hasSpaceErrors = true;
hasGlobalErrors = true;
}
// Continue parsing valid spaces even if some have errors (improved error recovery)
if (!hasSpaceErrors) {
spaces.push(space);
}
}
// Return partial results if we have some valid spaces (improved error recovery)
return hasGlobalErrors && spaces.length === 0 ? null : spaces;
}
/**
* Parse assumptions block with standardized validation messages
*/
static parseAssumptions(data, errors) {
if (!Array.isArray(data)) {
errors.push({
field: 'assumptions',
message: validation_messages_1.VALIDATION_MESSAGES.ASSUMPTIONS.MISSING_ARRAY,
severity: 'error'
});
return null;
}
const assumptions = [];
let hasErrors = false;
for (let i = 0; i < data.length; i++) {
const assumption = data[i];
const fieldPrefix = `assumptions[${i}]`;
if (!assumption || typeof assumption !== 'object') {
errors.push({
field: fieldPrefix,
message: validation_messages_1.VALIDATION_MESSAGES.ASSUMPTIONS.INVALID_ASSUMPTION,
severity: 'error'
});
hasErrors = true;
continue;
}
// Validate required fields
if (!assumption.name) {
errors.push({
field: `${fieldPrefix}.name`,
message: validation_messages_1.VALIDATION_MESSAGES.ASSUMPTIONS.MISSING_NAME,
severity: 'error'
});
hasErrors = true;
}
if (!assumption.type) {
errors.push({
field: `${fieldPrefix}.type`,
message: 'type is required and must be a valid assumption type',
severity: 'error'
});
hasErrors = true;
}
// Type-specific validation will be done in SchemaValidator
if (!hasErrors) {
assumptions.push(assumption);
}
}
return hasErrors ? null : assumptions;
}
/**
* Parse models block with standardized validation messages
*/
static parseModels(data, errors) {
if (!Array.isArray(data)) {
errors.push({
field: 'models',
message: validation_messages_1.VALIDATION_MESSAGES.MODELS.MISSING_ARRAY,
severity: 'error',
help: validation_messages_1.VALIDATION_HELP.MODELS.REQUIRED_FIELDS
});
return null;
}
const models = [];
let hasErrors = false;
for (let i = 0; i < data.length; i++) {
const model = data[i];
const fieldPrefix = `models[${i}]`;
if (!model || typeof model !== 'object') {
errors.push({
field: fieldPrefix,
message: validation_messages_1.VALIDATION_MESSAGES.MODELS.INVALID_MODEL,
severity: 'error',
help: validation_messages_1.VALIDATION_HELP.MODELS.REQUIRED_FIELDS
});
hasErrors = true;
continue;
}
// Basic field validation - detailed validation in SchemaValidator
if (!model.name) {
errors.push({
field: `${fieldPrefix}.name`,
message: 'name is required and must be a non-empty string',
severity: 'error'
});
hasErrors = true;
}
if (!hasErrors) {
models.push(model);
}
}
return hasErrors ? null : models;
}
/**
* Parse simulation block with standardized validation messages
*/
static parseSimulation(data, errors) {
if (!data) {
errors.push({
field: 'simulation',
message: validation_messages_1.VALIDATION_MESSAGES.SIMULATION.MISSING_BLOCK,
severity: 'error'
});
return null;
}
if (typeof data !== 'object') {
errors.push({
field: 'simulation',
message: validation_messages_1.VALIDATION_MESSAGES.SIMULATION.INVALID_STRUCTURE,
severity: 'error'
});
return null;
}
return data;
}
/**
* Parse outputs block with standardized validation messages
*/
static parseOutput(data, errors) {
if (!data) {
errors.push({
field: 'outputs',
message: validation_messages_1.VALIDATION_MESSAGES.OUTPUTS.MISSING_BLOCK,
severity: 'error'
});
return null;
}
if (typeof data !== 'object') {
errors.push({
field: 'outputs',
message: validation_messages_1.VALIDATION_MESSAGES.OUTPUTS.INVALID_STRUCTURE,
severity: 'error'
});
return null;
}
return data;
}
/**
* Parse extensions block (optional)
*/
static parseExtensions(data, _errors) {
return data;
}
/**
* Parse presets block (optional)
*/
static parsePresets(data, _errors) {
return data;
}
/**
* Parse templates block (optional)
*/
static parseTemplates(data, _errors) {
return data;
}
/**
* Parse use_templates block (optional)
*/
static parseUseTemplates(data, errors) {
if (!Array.isArray(data)) {
errors.push({
field: 'use_templates',
message: 'use_templates must be an array',
severity: 'error'
});
return [];
}
return data;
}
/**
* Parse scenarios block (optional first-class block per Behavioral Specification 8.1)
*/
static parseScenarios(data, errors) {
if (!Array.isArray(data)) {
errors.push({
field: 'scenarios',
message: 'scenarios must be an array',
severity: 'error'
});
return [];
}
return data;
}
/**
* Parse waterfall block (optional first-class block per Behavioral Specification 8.1)
*/
static parseWaterfall(data, errors) {
if (!data || typeof data !== 'object') {
errors.push({
field: 'waterfall',
message: 'waterfall must be an object',
severity: 'error'
});
return null;
}
return data;
}
}
exports.CREDLParser = CREDLParser;
CREDLParser.DEFAULT_OPTIONS = {
strict: true,
filename: 'unknown',
validateStructure: true
};
//# sourceMappingURL=CREDLParser.js.map