UNPKG

credl-parser-evaluator

Version:

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

519 lines (486 loc) 17.6 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.templateGenerators = exports.PROPERTY_TYPES = void 0; exports.generateOfficeTemplate = generateOfficeTemplate; exports.generateRetailTemplate = generateRetailTemplate; exports.generateMixedUseTemplate = generateMixedUseTemplate; exports.generateIndustrialTemplate = generateIndustrialTemplate; exports.generateResidentialTemplate = generateResidentialTemplate; exports.getTemplateGenerator = getTemplateGenerator; exports.validateTemplateContext = validateTemplateContext; exports.generateTemplateFile = generateTemplateFile; const files_js_1 = require("./files.js"); // Property type definitions exports.PROPERTY_TYPES = { office: { name: 'Office Building', description: 'Commercial office space template', icon: '🏢', defaultUnits: 'sf' }, retail: { name: 'Retail Property', description: 'Retail commercial space template', icon: '🛍️', defaultUnits: 'sf' }, 'mixed-use': { name: 'Mixed-Use Development', description: 'Mixed-use property with multiple space types', icon: '🏬', defaultUnits: 'sf' }, industrial: { name: 'Industrial Property', description: 'Manufacturing, warehouse, and distribution facilities', icon: '🏭', defaultUnits: 'sf' }, residential: { name: 'Residential Property', description: 'Apartment, condo, and residential developments', icon: '🏠', defaultUnits: 'sf' } }; // Template generators function generateOfficeTemplate(context) { const spaceCount = Math.min(parseInt(context.space_count || '10'), 50); const rentPsf = parseFloat(context.rent_psf || '35'); const buildingSize = parseInt(context.building_size || '50000'); return `metadata: version: "0.2" name: "${context.project_name}" description: "${context.description || `Office building in ${context.location || 'Prime Location'}`}" created_date: "${context.created_date}" author: "${context.author || 'CREDL User'}" assets: - id: "asset-main" name: "${context.project_name}" property_type: "Office" location: "${context.location || 'Downtown Business District'}" total_area_sf: ${buildingSize} year_built: ${context.year_built || new Date().getFullYear()} buildings: - id: "building-main" name: "Main Building" floors: ${Math.ceil(spaceCount / 10)} total_area_sf: ${buildingSize} spaces: ${generateOfficeSpaces(spaceCount, buildingSize, rentPsf)} assumptions: ${generateBasicAssumptions(context)} models: - name: "office_dcf_model" type: "deterministic" duration_years: ${context.analysis_period || '10'} steps_per_year: 12 inputs: ["rent_growth", "expense_growth", "vacancy_rate", "cap_rate"] outputs: ["NOI", "IRR", "NPV", "Cash_Flow"] description: "Office building DCF analysis" simulation: type: "monte_carlo" iterations: ${context.simulation_iterations || '1000'} processes: {} outputs: summary_metrics: ["IRR", "NPV"] outputs: format: "json" metrics: ["IRR", "NPV", "Cash_Flow", "NOI"]`; } function generateRetailTemplate(context) { const spaceCount = Math.min(parseInt(context.space_count || '8'), 30); const rentPsf = parseFloat(context.rent_psf || '45'); const buildingSize = parseInt(context.building_size || '40000'); return `metadata: version: "0.2" name: "${context.project_name}" description: "${context.description || `Retail center in ${context.location || 'High-Traffic Area'}`}" created_date: "${context.created_date}" author: "${context.author || 'CREDL User'}" assets: - id: "asset-main" name: "${context.project_name}" property_type: "Retail" location: "${context.location || 'Shopping District'}" total_area_sf: ${buildingSize} year_built: ${context.year_built || new Date().getFullYear()} buildings: - id: "building-main" name: "Retail Center" floors: ${Math.ceil(spaceCount / 12)} total_area_sf: ${buildingSize} spaces: ${generateRetailSpaces(spaceCount, buildingSize, rentPsf)} assumptions: ${generateBasicAssumptions(context)} models: - name: "retail_dcf_model" type: "deterministic" duration_years: ${context.analysis_period || '10'} steps_per_year: 12 inputs: ["rent_growth", "expense_growth", "vacancy_rate", "cap_rate"] outputs: ["NOI", "IRR", "NPV", "Cash_Flow"] description: "Retail property DCF analysis" simulation: type: "monte_carlo" iterations: ${context.simulation_iterations || '1000'} processes: {} outputs: summary_metrics: ["IRR", "NPV"] outputs: format: "json" metrics: ["IRR", "NPV", "Cash_Flow", "NOI"]`; } function generateMixedUseTemplate(context) { const spaceCount = Math.min(parseInt(context.space_count || '15'), 40); const buildingSize = parseInt(context.building_size || '100000'); return `metadata: version: "0.2" name: "${context.project_name}" description: "${context.description || `Mixed-use development in ${context.location || 'Urban Center'}`}" created_date: "${context.created_date}" author: "${context.author || 'CREDL User'}" assets: - id: "asset-main" name: "${context.project_name}" property_type: "Mixed Use" location: "${context.location || 'Downtown Mixed-Use District'}" total_area_sf: ${buildingSize} year_built: ${context.year_built || new Date().getFullYear()} buildings: - id: "building-main" name: "Mixed-Use Tower" floors: ${Math.ceil(spaceCount / 8)} total_area_sf: ${buildingSize} spaces: ${generateMixedUseSpaces(spaceCount, buildingSize)} assumptions: ${generateBasicAssumptions(context)} models: - name: "mixed_use_dcf_model" type: "stochastic" duration_years: ${context.analysis_period || '15'} steps_per_year: 12 inputs: ["rent_growth", "expense_growth", "vacancy_rate", "cap_rate"] outputs: ["NOI", "IRR", "NPV", "Cash_Flow"] description: "Mixed-use property DCF analysis" simulation: type: "monte_carlo" iterations: ${context.simulation_iterations || '2000'} processes: {} outputs: summary_metrics: ["IRR", "NPV"] outputs: format: "json" metrics: ["IRR", "NPV", "Cash_Flow", "NOI"]`; } function generateIndustrialTemplate(context) { const spaceCount = Math.min(parseInt(context.space_count || '5'), 20); const rentPsf = parseFloat(context.rent_psf || '8'); const buildingSize = parseInt(context.building_size || '200000'); return `metadata: version: "0.2" name: "${context.project_name}" description: "${context.description || `Industrial facility in ${context.location || 'Industrial Park'}`}" created_date: "${context.created_date}" author: "${context.author || 'CREDL User'}" assets: - id: "asset-main" name: "${context.project_name}" property_type: "Industrial" location: "${context.location || 'Industrial District'}" total_area_sf: ${buildingSize} year_built: ${context.year_built || new Date().getFullYear()} buildings: - id: "building-main" name: "Industrial Facility" floors: 1 total_area_sf: ${buildingSize} spaces: ${generateIndustrialSpaces(spaceCount, buildingSize, rentPsf)} assumptions: ${generateBasicAssumptions(context)} models: - name: "industrial_dcf_model" type: "deterministic" duration_years: ${context.analysis_period || '10'} steps_per_year: 12 inputs: ["rent_growth", "expense_growth", "vacancy_rate", "cap_rate"] outputs: ["NOI", "IRR", "NPV", "Cash_Flow"] description: "Industrial property DCF analysis" simulation: type: "monte_carlo" iterations: ${context.simulation_iterations || '1000'} processes: {} outputs: summary_metrics: ["IRR", "NPV"] outputs: format: "json" metrics: ["IRR", "NPV", "Cash_Flow", "NOI"]`; } function generateResidentialTemplate(context) { const unitCount = Math.min(parseInt(context.unit_count || '20'), 100); const rentPerUnit = parseFloat(context.rent_per_unit || '2500'); const buildingSize = parseInt(context.building_size || '50000'); return `metadata: version: "0.2" name: "${context.project_name}" description: "${context.description || `Residential development in ${context.location || 'Desirable Neighborhood'}`}" created_date: "${context.created_date}" author: "${context.author || 'CREDL User'}" assets: - id: "asset-main" name: "${context.project_name}" property_type: "Residential" location: "${context.location || 'Residential District'}" total_area_sf: ${buildingSize} year_built: ${context.year_built || new Date().getFullYear()} buildings: - id: "building-main" name: "Residential Building" floors: ${Math.ceil(unitCount / 8)} total_area_sf: ${buildingSize} spaces: ${generateResidentialSpaces(unitCount, rentPerUnit)} assumptions: ${generateBasicAssumptions(context)} models: - name: "residential_dcf_model" type: "stochastic" duration_years: ${context.analysis_period || '10'} steps_per_year: 12 inputs: ["rent_growth", "expense_growth", "vacancy_rate", "cap_rate"] outputs: ["NOI", "IRR", "NPV", "Cash_Flow"] description: "Residential property DCF analysis" simulation: type: "monte_carlo" iterations: ${context.simulation_iterations || '1500'} processes: {} outputs: summary_metrics: ["IRR", "NPV"] outputs: format: "json" metrics: ["IRR", "NPV", "Cash_Flow", "NOI"]`; } // Helper functions for space generation function generateOfficeSpaces(count, buildingSize, rentPsf) { const spaces = []; const avgSize = Math.floor(buildingSize / count); for (let i = 1; i <= count; i++) { const isCommonArea = i > count * 0.9; const spaceType = isCommonArea ? 'common_area' : 'office'; const size = isCommonArea ? Math.floor(avgSize * 0.5) : avgSize + Math.floor(Math.random() * 1000 - 500); const isVacant = i % 8 === 0; spaces.push(` - id: "space-${i.toString().padStart(3, '0')}" parent_building: "building-main" type: "${spaceType}" area_sf: ${size} ${isCommonArea ? 'common_area: true' : `lease: status: "${isVacant ? 'vacant' : 'leased'}" ${isVacant ? '' : `tenant: "Tenant ${i}"`} rent_psf: ${rentPsf} lease_type: "NNN" start_date: "2024-01-01" end_date: "2029-01-01"`}`); } return spaces.join('\n\n'); } function generateRetailSpaces(count, buildingSize, rentPsf) { const spaces = []; const avgSize = Math.floor(buildingSize / count); for (let i = 1; i <= count; i++) { const isCommonArea = i > count * 0.85; const spaceType = isCommonArea ? 'common_area' : 'retail'; const size = isCommonArea ? Math.floor(avgSize * 0.4) : avgSize + Math.floor(Math.random() * 800 - 400); const isVacant = i % 6 === 0; spaces.push(` - id: "space-${i.toString().padStart(3, '0')}" parent_building: "building-main" type: "${spaceType}" area_sf: ${size} ${isCommonArea ? 'common_area: true' : `lease: status: "${isVacant ? 'vacant' : 'leased'}" ${isVacant ? '' : `tenant: "Retailer ${i}"`} rent_psf: ${rentPsf} lease_type: "NNN" start_date: "2024-01-01" end_date: "2029-01-01"`}`); } return spaces.join('\n\n'); } function generateMixedUseSpaces(count, _buildingSize) { const spaces = []; const officeCount = Math.floor(count * 0.4); const retailCount = Math.floor(count * 0.3); const residentialCount = count - officeCount - retailCount; let spaceId = 1; // Office spaces for (let i = 0; i < officeCount; i++) { spaces.push(` - id: "space-${spaceId.toString().padStart(3, '0')}" parent_building: "building-main" type: "office" area_sf: ${2000 + Math.floor(Math.random() * 3000)} lease: status: "${spaceId % 8 === 0 ? 'vacant' : 'leased'}" ${spaceId % 8 === 0 ? '' : `tenant: "Office Tenant ${spaceId}"`} rent_psf: 35 lease_type: "NNN" start_date: "2024-01-01" end_date: "2029-01-01"`); spaceId++; } // Retail spaces for (let i = 0; i < retailCount; i++) { spaces.push(` - id: "space-${spaceId.toString().padStart(3, '0')}" parent_building: "building-main" type: "retail" area_sf: ${1500 + Math.floor(Math.random() * 2500)} lease: status: "${spaceId % 6 === 0 ? 'vacant' : 'leased'}" ${spaceId % 6 === 0 ? '' : `tenant: "Retail Tenant ${spaceId}"`} rent_psf: 45 lease_type: "NNN" start_date: "2024-01-01" end_date: "2029-01-01"`); spaceId++; } // Residential spaces for (let i = 0; i < residentialCount; i++) { spaces.push(` - id: "space-${spaceId.toString().padStart(3, '0')}" parent_building: "building-main" type: "residential" area_sf: ${800 + Math.floor(Math.random() * 800)} lease: status: "${spaceId % 10 === 0 ? 'vacant' : 'leased'}" ${spaceId % 10 === 0 ? '' : `tenant: "Resident ${spaceId}"`} rent_monthly: 2500 lease_type: "residential" start_date: "2024-01-01" end_date: "2024-12-31"`); spaceId++; } return spaces.join('\n\n'); } function generateIndustrialSpaces(count, buildingSize, rentPsf) { const spaces = []; const avgSize = Math.floor(buildingSize / count); for (let i = 1; i <= count; i++) { const spaceType = i === 1 ? 'office' : i <= count * 0.9 ? 'warehouse' : 'dock'; const size = spaceType === 'office' ? 2000 : spaceType === 'dock' ? 500 : avgSize; const rent = spaceType === 'office' ? rentPsf * 3 : spaceType === 'dock' ? 0 : rentPsf; const isVacant = i % 10 === 0; spaces.push(` - id: "space-${i.toString().padStart(3, '0')}" parent_building: "building-main" type: "${spaceType}" area_sf: ${size} ${spaceType === 'dock' ? 'dock_door: true' : `lease: status: "${isVacant ? 'vacant' : 'leased'}" ${isVacant ? '' : `tenant: "Industrial Tenant ${i}"`} rent_psf: ${rent} lease_type: "NNN" start_date: "2024-01-01" end_date: "2029-01-01"`}`); } return spaces.join('\n\n'); } function generateResidentialSpaces(unitCount, avgRent) { const spaces = []; const unitTypes = ['studio', '1br', '2br', '3br']; for (let i = 1; i <= unitCount; i++) { const unitType = unitTypes[i % unitTypes.length] || '1br'; const size = getUnitSize(unitType); const rent = getUnitRent(unitType, avgRent); spaces.push(` - id: "unit-${i.toString().padStart(3, '0')}" parent_building: "building-main" type: "residential" unit_type: "${unitType}" area_sf: ${size} lease: status: "${i % 8 === 0 ? 'vacant' : 'leased'}" ${i % 8 === 0 ? '' : `tenant: "Resident ${i}"`} rent_monthly: ${rent} lease_type: "residential" start_date: "2024-01-01" end_date: "2024-12-31"`); } return spaces.join('\n\n'); } function generateBasicAssumptions(context) { return ` - name: "rent_growth" type: "distribution" distribution: "normal" parameters: mean: ${parseFloat(context.rent_growth || '3') / 100} stddev: 0.01 scope: "global" tags: ["revenue", "growth"] - name: "expense_growth" type: "distribution" distribution: "normal" parameters: mean: ${parseFloat(context.expense_growth || '2.5') / 100} stddev: 0.005 scope: "global" tags: ["expense", "growth"] - name: "vacancy_rate" type: "distribution" distribution: "beta" parameters: alpha: 2 beta: 20 min: 0.02 max: 0.15 scope: "global" tags: ["occupancy", "risk"] - name: "cap_rate" type: "fixed" value: ${parseFloat(context.cap_rate || '6.5') / 100} scope: "global" tags: ["valuation", "yield"]`; } function getUnitSize(unitType) { const sizes = { studio: 500, '1br': 750, '2br': 1100, '3br': 1400 }; return sizes[unitType] || 750; } function getUnitRent(unitType, avgRent) { const multipliers = { studio: 0.7, '1br': 1.0, '2br': 1.4, '3br': 1.8 }; return Math.floor(avgRent * (multipliers[unitType] || 1.0)); } // Template management exports.templateGenerators = { office: generateOfficeTemplate, retail: generateRetailTemplate, 'mixed-use': generateMixedUseTemplate, industrial: generateIndustrialTemplate, residential: generateResidentialTemplate }; function getTemplateGenerator(propertyType) { return exports.templateGenerators[propertyType]; } function validateTemplateContext(context, _propertyType) { const errors = []; if (!context.project_name || context.project_name.trim().length === 0) { errors.push('Project name is required'); } if (!context.created_date) { errors.push('Created date is required'); } return errors; } async function generateTemplateFile(propertyType, context, outputPath) { const errors = validateTemplateContext(context, propertyType); if (errors.length > 0) { throw new Error(`Template validation failed: ${errors.join(', ')}`); } const generator = getTemplateGenerator(propertyType); if (!generator) { throw new Error(`No template generator found for property type: ${propertyType}`); } const content = generator(context); await (0, files_js_1.safeWriteFile)(outputPath, content, { createDirectories: true, backup: false, overwrite: false }); } //# sourceMappingURL=templates.js.map