UNPKG

credl-parser-evaluator

Version:

TypeScript-based CREDL Parser and Evaluator that processes CREDL files and outputs complete Intermediate Representations

900 lines 39.4 kB
"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