credl-parser-evaluator
Version:
TypeScript-based CREDL Parser and Evaluator that processes CREDL files and outputs complete Intermediate Representations
225 lines • 14.6 kB
JavaScript
;
/**
* Standardized validation messages for consistent error/warning reporting
*
* Guidelines:
* - Keep messages concise and focused on the core issue
* - Use present tense, avoid parenthetical commentary
* - Additional guidance goes in the help field
* - Template functions for dynamic content, constants for static messages
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.VALIDATION_HELP = exports.VALIDATION_MESSAGES = void 0;
exports.VALIDATION_MESSAGES = {
METADATA: {
// Core messages - clean and testable
MISSING_BLOCK: 'metadata block is required',
INVALID_STRUCTURE: 'metadata must be an object',
MISSING_VERSION: 'version is required and must be a non-empty string',
INVALID_VERSION: 'version must be a valid CREDL specification version',
MISSING_NAME: 'name is required and must be a non-empty string',
MISSING_DESCRIPTION: 'description is required and must be a non-empty string',
MISSING_ANALYSIS_START: 'analysis_start_date is required and must be a non-empty string',
INVALID_DATE_FORMAT: 'must be in ISO 8601 format (YYYY-MM-DD)',
INVALID_DATE: 'must be a valid date',
// Template functions for dynamic content
UNSUPPORTED_VERSION: (version, min) => `CREDL specification version ${version} is not supported (minimum: ${min})`,
NEWER_VERSION: (version, max) => `CREDL specification version ${version} is newer than supported by parser (maximum: ${max}). Some features may not be supported.`,
},
ASSETS: {
MISSING_ARRAY: 'assets must be an array',
EMPTY_ARRAY: 'at least one asset is required',
INVALID_ASSET: 'asset must be an object',
MISSING_ID: 'id is required and must be a non-empty string',
MISSING_NAME: 'name is required and must be a non-empty string',
MISSING_LOCATION: 'location is required and must be a non-empty string',
MISSING_AREA: 'total_area_sf is required and must be a positive number',
MISSING_BUILDINGS: 'buildings must be an array',
EMPTY_BUILDINGS: 'at least one building is required',
// Template functions
DUPLICATE_ID: (id) => `duplicate asset id: ${id}`,
INVALID_PROPERTY_TYPE: (validTypes) => `property_type must be one of: ${validTypes.join(', ')}`,
BUILDING_AREA_EXCEEDED: (buildingArea, assetArea, percent) => `Building areas (${buildingArea.toLocaleString()} SF) exceed asset area (${assetArea.toLocaleString()} SF) by ${percent}%. Consider accounting for common areas.`,
BUILDING_AREA_LOW: (buildingArea, assetArea, percent) => `Building areas (${buildingArea.toLocaleString()} SF) are only ${percent}% of asset area (${assetArea.toLocaleString()} SF). This seems unusually low.`,
},
SPACES: {
MISSING_ARRAY: 'spaces must be an array',
INVALID_SPACE: 'space must be an object',
MISSING_ID: 'id is required and must be a non-empty string',
MISSING_PARENT_BUILDING: 'parent_building is required and must be a non-empty string',
MISSING_AREA: 'area_sf is required and must be a positive number',
AUTO_POPULATED_FIELD: 'source_template is auto-populated by template resolver and cannot be manually specified',
PRESET_LEASE_CONFLICT: 'Cannot specify both preset_lease and lease fields. Choose either preset reference or direct specification.',
PRESET_EXPENSE_CONFLICT: 'Cannot specify both preset_expense and expense fields. Choose either preset reference or direct specification.',
MISSING_LEASE_INFO: 'space must have either lease/lease_assumptions blocks or preset references (preset_lease, preset_expenses)',
// Template functions
DUPLICATE_ID: (id) => `duplicate space id: ${id}`,
INVALID_TYPE: (validTypes) => `type must be one of: ${validTypes.join(', ')}`,
},
LEASE: {
INVALID_STRUCTURE: 'lease must be an object',
MISSING_RENT: 'rent_psf is required and must be a non-negative number',
TENANT_REQUIRED: 'tenant is required for leased spaces and must be a non-empty string',
TENANT_NOT_ALLOWED: 'tenant field is not allowed for vacant spaces',
DATES_REQUIRED: 'start_date and end_date are required for leased spaces',
INVALID_RENEWAL_PROBABILITY: 'renewal_probability must be a number between 0 and 1 if provided',
// Template functions
INVALID_STATUS: (validStatuses) => `status must be one of: ${validStatuses.join(', ')}`,
INVALID_TYPE: (validTypes) => `lease_type must be one of: ${validTypes.join(', ')}`,
},
ASSUMPTIONS: {
MISSING_ARRAY: 'assumptions must be an array',
INVALID_ASSUMPTION: 'assumption must be an object',
MISSING_NAME: 'name is required and must be a non-empty string',
MISSING_VALUE: 'value is required and must be a number for fixed assumptions',
MISSING_FORMULA: 'formula is required and must be a non-empty string for expression assumptions',
MISSING_PARAMETERS: 'parameters is required and must be an object for distribution assumptions',
MISSING_VALUES: 'values is required and must be an array for table assumptions',
EMPTY_VALUES: 'values array cannot be empty for table assumptions',
INVALID_TABLE_VALUE: 'table value must be an object',
// Template functions
INVALID_TYPE: (validTypes) => `type must be one of: ${validTypes.join(', ')}`,
SCOPE_CONFLICT: (name, scope) => `Scope conflict detected: Assumption '${name}' is defined multiple times for scope '${scope}'`,
INVALID_DISTRIBUTION: (validTypes) => `distribution must be one of: ${validTypes.join(', ')}`,
DISTRIBUTION_PARAM_REQUIRED: (param, distType) => `${param} is required for ${distType} distribution`,
DISTRIBUTION_PARAM_POSITIVE: (param, distType) => `${param} is required and must be positive for ${distType} distribution`,
MIN_MAX_ORDER: (distType) => `min must be less than max for ${distType} distribution`,
},
MODELS: {
MISSING_ARRAY: 'models must be an array',
INVALID_MODEL: 'model must be an object',
MISSING_ID: 'id is required and must be a non-empty string',
MISSING_TYPE: 'type is required and must be a non-empty string',
MISSING_INPUTS: 'inputs is required and must be an array',
MISSING_OUTPUTS: 'outputs is required and must be an array',
// Template functions
DUPLICATE_ID: (id) => `duplicate model id: ${id}`,
INVALID_TYPE: (validTypes) => `type must be one of: ${validTypes.join(', ')}`,
},
SIMULATION: {
MISSING_BLOCK: 'simulation block is required',
INVALID_STRUCTURE: 'simulation must be an object',
MISSING_TYPE: 'type is required and must be a non-empty string',
MISSING_ITERATIONS: 'iterations is required and must be a positive number',
HIGH_ITERATIONS: 'very high iteration count may impact performance',
// Template functions
INVALID_TYPE: (validTypes) => `type must be one of: ${validTypes.join(', ')}`,
ITERATIONS_LIMIT: (count, max) => `iterations (${count}) exceeds recommended maximum (${max})`,
},
OUTPUTS: {
MISSING_BLOCK: 'outputs block is required',
INVALID_STRUCTURE: 'outputs must be an object',
MISSING_METRICS: 'metrics is required and must be an array',
EMPTY_METRICS: 'at least one metric is required',
// Template functions
INVALID_FORMAT: (validFormats) => `format must be one of: ${validFormats.join(', ')}`,
},
SCENARIOS: {
// Core messages - clean and testable
HYBRID_TYPE: 'combines case analysis with options analysis',
EMPTY_CONTENT: 'has no overrides, triggers, or actions',
MISSING_NAME: 'scenario name is required',
MISSING_DESCRIPTION: 'scenario description is required',
INVALID_NAME: 'scenario name must be a non-empty string',
INVALID_DESCRIPTION: 'scenario description must be a non-empty string',
INVALID_CONDITION: 'trigger condition is required and must be a non-empty string',
MISSING_ACTION_TYPE: 'action type is required',
// Template functions for dynamic content
UNUSUAL_ACTION_TYPE: (type) => `has unusual action type "${type}"`,
MISSING_ACTION_FIELD: (actionType, field) => `${actionType} action should specify ${field}`,
},
WATERFALL: {
// Core messages
INVALID_STRUCTURE: 'waterfall must be an object',
MISSING_TIERS: 'waterfall tiers must be an array',
EMPTY_TIERS: 'waterfall must have at least one tier',
INVALID_TIER: 'tier must be an object',
MISSING_HURDLE: 'tier hurdle rate is required',
MISSING_PROMOTE: 'tier promote percentage is required',
INVALID_ENABLED: 'waterfall enabled field must be a boolean',
INVALID_TIER_NUMBER: 'tier number must be an integer',
INVALID_DESCRIPTION: 'tier description must be a string',
NON_ASCENDING_HURDLES: 'tier hurdle rates should be in ascending order',
NON_SEQUENTIAL_TIERS: 'tier numbers should be sequential integers starting from 1',
// Template functions
PROMOTE_OUT_OF_RANGE: (value) => `promote percentage ${value} should be between 0 and 1`,
},
GENERAL: {
VALIDATION_FAILED: 'Validation failed due to an unexpected error',
YAML_SYNTAX_ERROR: 'YAML syntax error',
INVALID_STRUCTURE: 'must be a valid YAML object',
PARSE_FAILED: 'Failed to parse YAML',
// Template functions
UNEXPECTED_ERROR: (error) => `Unexpected error: ${error}`,
}
};
exports.VALIDATION_HELP = {
METADATA: {
VERSION_FORMAT: 'Use semantic versioning like "0.1" or "0.2". Check documentation for supported versions.',
DATE_FORMAT: 'Use ISO 8601 date format: YYYY-MM-DD (e.g., "2024-01-15")',
ANALYSIS_START: 'This date marks the beginning of your financial analysis period',
CREATED_DATE: 'Record when this CREDL file was originally created',
},
ASSETS: {
REQUIRED_FIELDS: 'Assets need: id, name, property_type, location, total_area_sf, and buildings array',
PROPERTY_TYPES: 'Valid property types: Office, Retail, Industrial, Residential, Mixed-Use',
BUILDING_AREAS: 'Building areas should typically be 70-90% of total asset area to account for common areas',
DUPLICATE_IDS: 'Each asset must have a unique identifier across your entire CREDL file',
},
SPACES: {
REQUIRED_FIELDS: 'Spaces need: id, parent_building, type, area_sf, and lease information',
PARENT_BUILDING: 'Must reference a building ID that exists in your assets',
LEASE_OPTIONS: 'Provide either direct lease/lease_assumptions OR use preset references (preset_lease, preset_expenses)',
PRESET_CONFLICTS: 'Use either preset references OR direct values, not both to avoid ambiguity',
SOURCE_TEMPLATE: 'This field is automatically set when spaces are generated from templates',
},
LEASE: {
REQUIRED_FOR_LEASED: 'Leased spaces must specify: tenant, start_date, end_date',
VACANT_SPACES: 'Vacant spaces should not have tenant specified; dates are optional market assumptions',
RENEWAL_PROBABILITY: 'Value between 0 (never renews) and 1 (always renews) for lease renewal likelihood',
LEASE_TYPES: 'Common types: gross, net, double_net, triple_net, modified_gross',
},
ASSUMPTIONS: {
SCOPE_HIERARCHY: 'Scopes follow hierarchy: global → asset → building → space_type → space_id',
SCOPE_CONFLICTS: 'Each assumption name can only be defined once per scope level',
DISTRIBUTION_TYPES: 'Supported distributions: normal, uniform, triangular, lognormal',
NORMAL_PARAMS: 'Normal distribution requires: mean (number), stddev (positive number)',
UNIFORM_PARAMS: 'Uniform distribution requires: min (number), max (number, > min)',
TRIANGULAR_PARAMS: 'Triangular distribution requires: min, max, mode (all numbers, min < mode < max)',
TABLE_STRUCTURE: 'Table values must be objects with consistent structure across all entries',
},
MODELS: {
REQUIRED_FIELDS: 'Models need: id, type, inputs (array), outputs (array)',
MODEL_TYPES: 'Common types: discounted_cash_flow, stochastic, sensitivity_analysis',
INPUT_REFERENCES: 'Inputs should reference assumption names defined in your assumptions block',
OUTPUT_CONSISTENCY: 'Outputs should match what your simulation and outputs blocks expect',
},
SIMULATION: {
ITERATION_GUIDELINES: 'Typical ranges: 1,000-10,000 iterations. Higher counts improve accuracy but slow processing',
MONTE_CARLO: 'Monte Carlo simulations should include sampling processes and statistical metrics',
PERFORMANCE_IMPACT: 'Very high iteration counts (>10,000) may significantly impact processing time',
},
OUTPUTS: {
REQUIRED_METRICS: 'Common metrics: npv, irr, cash_flow, roi, payback_period',
FORMAT_OPTIONS: 'Supported formats: json, csv, excel, pdf',
METRIC_CONSISTENCY: 'Ensure requested metrics are produced by your models',
},
SCENARIOS: {
HYBRID_TYPE: 'Consider using either overrides (case analysis) OR triggers/actions (options analysis) for clarity',
UNUSUAL_ACTION_TYPE: 'Supported action types: add_spaces, delay_project, accelerate_project, expand_building, contract_scope',
ADD_SPACES_TEMPLATE: 'add_spaces actions typically require: template, count, parent_building',
ADD_SPACES_BUILDING: 'add_spaces actions should reference an existing building ID',
},
WATERFALL: {
TIER_STRUCTURE: 'Each tier requires: hurdle (number), promote (number). Optional: tier (integer), description (string)',
PROMOTE_RANGE: 'Promote percentages typically range from 0 (0%) to 1 (100%)',
HURDLE_ORDERING: 'Hurdle rates should increase with each tier (e.g., 0.08, 0.12, 0.16)',
SEQUENTIAL_NUMBERING: 'If using tier numbers, they should be 1, 2, 3, etc. without gaps',
},
GENERAL: {
YAML_SYNTAX: 'Check for proper indentation, quotes, and YAML structure. Use a YAML validator if needed',
VALIDATION_ERRORS: 'Fix errors in order - some errors may cascade and resolve multiple issues',
CROSS_REFERENCES: 'Ensure all referenced IDs (buildings, assumptions, etc.) exist in their respective blocks',
}
};
//# sourceMappingURL=validation-messages.js.map