UNPKG

credl-parser-evaluator

Version:

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

801 lines (752 loc) • 25.5 kB
"use strict"; 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; }; })(); var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.createInitCommand = createInitCommand; exports.executeInitCommand = executeInitCommand; const commander_1 = require("commander"); const inquirer_1 = __importDefault(require("inquirer")); const path = __importStar(require("path")); const fs = __importStar(require("fs/promises")); // Available property types for templates var PropertyType; (function (PropertyType) { PropertyType["OFFICE"] = "office"; PropertyType["RETAIL"] = "retail"; PropertyType["MIXED_USE"] = "mixed-use"; PropertyType["INDUSTRIAL"] = "industrial"; PropertyType["RESIDENTIAL"] = "residential"; })(PropertyType || (PropertyType = {})); // Error exit codes var ExitCode; (function (ExitCode) { ExitCode[ExitCode["SUCCESS"] = 0] = "SUCCESS"; ExitCode[ExitCode["FILE_EXISTS_ERROR"] = 1] = "FILE_EXISTS_ERROR"; ExitCode[ExitCode["FILE_ACCESS_ERROR"] = 3] = "FILE_ACCESS_ERROR"; ExitCode[ExitCode["INVALID_ARGUMENTS"] = 6] = "INVALID_ARGUMENTS"; ExitCode[ExitCode["GENERAL_ERROR"] = 10] = "GENERAL_ERROR"; })(ExitCode || (ExitCode = {})); function createInitCommand() { const initCommand = new commander_1.Command('init'); initCommand .description('Initialize a new CREDL project with interactive prompts') .option('-n, --name <name>', 'Project name (skip interactive prompt)') .option('-t, --type <type>', 'Property type: office, retail, mixed-use, industrial, residential') .option('-l, --location <location>', 'Property location (skip interactive prompt)') .option('-o, --output <path>', 'Output file path (default: {name}.credl)') .option('-f, --force', 'Overwrite existing files without confirmation') .option('-q, --quiet', 'Suppress output except errors') .option('-v, --verbose', 'Verbose output with detailed information') .action(async (options) => { await executeInitCommand(options); }); return initCommand; } async function executeInitCommand(options) { try { if (!options.quiet) { console.log('šŸ—ļø CREDL Project Initialization'); console.log(''); console.log('This wizard will help you create a new CREDL project file.'); console.log('Press Ctrl+C at any time to exit.'); console.log(''); } // Collect project configuration through interactive prompts or CLI options const config = await collectProjectConfig(options); if (options.verbose) { console.log('šŸ“‹ Project Configuration:'); console.log(` • Name: ${config.name}`); console.log(` • Type: ${config.propertyType}`); console.log(` • Location: ${config.location}`); console.log(` • Total Area: ${config.totalAreaSf.toLocaleString()} sq ft`); console.log(` • Output: ${config.outputPath}`); console.log(''); } // Check if output file exists and handle overwrite protection await handleFileOverwrite(config.outputPath, options); // Generate CREDL content based on configuration if (options.verbose) { console.log('šŸ“ Generating CREDL template...'); } const credlContent = generateCREDLTemplate(config); // Create output directory if needed const outputDir = path.dirname(config.outputPath); if (outputDir !== '.') { await fs.mkdir(outputDir, { recursive: true }); } // Write CREDL file await fs.writeFile(config.outputPath, credlContent, 'utf8'); if (!options.quiet) { console.log(''); console.log('āœ… CREDL project created successfully!'); console.log(''); console.log(`šŸ“„ File: ${config.outputPath}`); console.log(`šŸ¢ Type: ${config.propertyType}`); console.log(`šŸ“ Location: ${config.location}`); console.log(''); console.log('Next steps:'); console.log(` • Review and customize: ${config.outputPath}`); console.log(` • Validate: credl validate ${config.outputPath}`); console.log(` • Process: credl run ${config.outputPath}`); console.log(''); } } catch (error) { if (error instanceof Error && error.message === 'User cancelled') { if (!options.quiet) { console.log('\nāŒ Project creation cancelled by user.'); } process.exit(0); } console.error('āŒ Error creating CREDL project:'); if (error instanceof Error) { console.error(` ${error.message}`); if (options.verbose && error.stack) { console.error('\nšŸ› Stack trace:'); console.error(error.stack); } } else { console.error(` ${String(error)}`); } process.exit(ExitCode.GENERAL_ERROR); } } async function collectProjectConfig(options) { const questions = []; // Project name if (!options.name) { questions.push({ type: 'input', name: 'name', message: 'Project name:', validate: (input) => { if (!input.trim()) { return 'Project name is required'; } return true; } }); } // Project description questions.push({ type: 'input', name: 'description', message: 'Project description:', default: (answers) => { const name = options.name || answers.name; return `${name} - Commercial real estate analysis`; } }); // Property type if (!options.type) { questions.push({ type: 'list', name: 'propertyType', message: 'Property type:', choices: [ { name: 'šŸ¢ Office - Office building or corporate headquarters', value: PropertyType.OFFICE }, { name: 'šŸ›ļø Retail - Shopping center, strip mall, or standalone retail', value: PropertyType.RETAIL }, { name: 'šŸ¬ Mixed-Use - Combined office, retail, and/or residential', value: PropertyType.MIXED_USE }, { name: 'šŸ­ Industrial - Warehouse, manufacturing, or distribution center', value: PropertyType.INDUSTRIAL }, { name: 'šŸ  Residential - Apartment complex or residential development', value: PropertyType.RESIDENTIAL } ] }); } // Location if (!options.location) { questions.push({ type: 'input', name: 'location', message: 'Property location (City, State):', validate: (input) => { if (!input.trim()) { return 'Location is required'; } return true; } }); } // Property size questions.push({ type: 'input', name: 'totalAreaSf', message: 'Total building area (square feet):', default: (answers) => { const type = options.type || answers.propertyType; switch (type) { case PropertyType.OFFICE: return '50000'; case PropertyType.RETAIL: return '25000'; case PropertyType.MIXED_USE: return '100000'; case PropertyType.INDUSTRIAL: return '75000'; case PropertyType.RESIDENTIAL: return '80000'; default: return '50000'; } }, validate: (input) => { const num = parseInt(input); if (isNaN(num) || num <= 0) { return 'Please enter a valid positive number'; } return true; }, filter: (input) => parseInt(input) }); // Building name questions.push({ type: 'input', name: 'buildingName', message: 'Main building name:', default: (answers) => { const name = options.name || answers.name; return `${name} Main Building`; } }); // Number of floors questions.push({ type: 'input', name: 'floors', message: 'Number of floors:', default: (answers) => { const type = options.type || answers.propertyType; switch (type) { case PropertyType.OFFICE: return '5'; case PropertyType.RETAIL: return '2'; case PropertyType.MIXED_USE: return '8'; case PropertyType.INDUSTRIAL: return '1'; case PropertyType.RESIDENTIAL: return '4'; default: return '3'; } }, validate: (input) => { const num = parseInt(input); if (isNaN(num) || num <= 0) { return 'Please enter a valid positive number'; } return true; }, filter: (input) => parseInt(input) }); // Year built questions.push({ type: 'input', name: 'yearBuilt', message: 'Year built:', default: '2020', validate: (input) => { const num = parseInt(input); const currentYear = new Date().getFullYear(); if (isNaN(num) || num < 1900 || num > currentYear + 5) { return `Please enter a valid year between 1900 and ${currentYear + 5}`; } return true; }, filter: (input) => parseInt(input) }); // Get answers from prompts const answers = questions.length > 0 ? await inquirer_1.default.prompt(questions) : {}; // Determine output path const projectName = options.name || answers.name; const outputPath = options.output || `${projectName.toLowerCase().replace(/\s+/g, '-')}.credl`; return { name: projectName, description: answers.description, propertyType: options.type ? options.type : answers.propertyType, location: options.location || answers.location, totalAreaSf: answers.totalAreaSf, buildingName: answers.buildingName, floors: answers.floors || 1, yearBuilt: answers.yearBuilt || 2020, outputPath }; } async function handleFileOverwrite(outputPath, options) { try { await fs.access(outputPath); // File exists if (options.force) { if (options.verbose) { console.log(`āš ļø Overwriting existing file: ${outputPath}`); } return; } // Ask for confirmation const { overwrite } = await inquirer_1.default.prompt([{ type: 'confirm', name: 'overwrite', message: `File "${outputPath}" already exists. Overwrite?`, default: false }]); if (!overwrite) { throw new Error('User cancelled'); } } catch (error) { if (error.code === 'ENOENT') { // File doesn't exist, continue return; } throw error; } } function generateCREDLTemplate(config) { const currentDate = new Date().toISOString().split('T')[0]; switch (config.propertyType) { case PropertyType.OFFICE: return generateOfficeTemplate(config, currentDate); case PropertyType.RETAIL: return generateRetailTemplate(config, currentDate); case PropertyType.MIXED_USE: return generateMixedUseTemplate(config, currentDate); case PropertyType.INDUSTRIAL: return generateIndustrialTemplate(config, currentDate); case PropertyType.RESIDENTIAL: return generateResidentialTemplate(config, currentDate); default: return generateOfficeTemplate(config, currentDate); } } function generateOfficeTemplate(config, currentDate) { const spaceAreaSf = Math.floor(config.totalAreaSf * 0.2); const rentPsf = 45; return `# CREDL Project: ${config.name} # Generated on ${currentDate} metadata: version: 0.2 name: "${config.name}" description: "${config.description}" created_date: "${currentDate}" author: "CREDL Init" assets: - id: "asset-1" name: "${config.name}" property_type: "Office" location: "${config.location}" total_area_sf: ${config.totalAreaSf} buildings: - id: "building-main" name: "${config.buildingName}" floors: ${config.floors} total_area_sf: ${config.totalAreaSf} year_built: ${config.yearBuilt} spaces: - id: "suite-100" parent_building: "building-main" type: "office" area_sf: ${spaceAreaSf} lease: status: "leased" tenant: "Example Tenant" rent_psf: ${rentPsf} lease_type: "NNN" start_date: "${currentDate}" end_date: "${new Date(new Date().setFullYear(new Date().getFullYear() + 5)).toISOString().split('T')[0]}" escalation_rate: 0.03 - id: "suite-200" parent_building: "building-main" type: "office" area_sf: ${Math.floor(spaceAreaSf * 0.8)} lease_assumptions: rent_psf: ${rentPsf - 2} lease_type: "gross" expected_turnover_months: 6 absorption_sf_per_month: 500 assumptions: - name: "discount_rate" type: "fixed" value: 0.085 description: "Required return rate for investment" tags: ["capital_market"] - name: "rent_growth" type: "fixed" value: 0.025 scope: "global" description: "Annual rent growth rate" tags: ["revenue"] - name: "vacancy_rate" type: "fixed" value: 0.05 scope: "space_type:office" description: "Long-term vacancy assumption" tags: ["revenue", "risk"] models: - name: "base_dcf" type: "deterministic" duration_years: 10 steps_per_year: 12 inputs: ["discount_rate", "rent_growth", "vacancy_rate"] outputs: ["IRR", "NPV", "cash_flow"] description: "Basic DCF model for office property" simulation: type: "monte_carlo" iterations: 1000 processes: {} outputs: summary_metrics: ["IRR", "NPV"] outputs: format: "json" metrics: ["IRR", "NPV", "cash_flow"] `; } function generateRetailTemplate(config, currentDate) { const anchorSpaceAreaSf = Math.floor(config.totalAreaSf * 0.6); const smallSpaceAreaSf = Math.floor(config.totalAreaSf * 0.15); const rentPsf = 32; return `# CREDL Project: ${config.name} # Generated on ${currentDate} metadata: version: 0.2 name: "${config.name}" description: "${config.description}" created_date: "${currentDate}" author: "CREDL Init" assets: - id: "asset-1" name: "${config.name}" property_type: "Retail" location: "${config.location}" total_area_sf: ${config.totalAreaSf} buildings: - id: "building-main" name: "${config.buildingName}" floors: ${config.floors} total_area_sf: ${config.totalAreaSf} year_built: ${config.yearBuilt} spaces: - id: "anchor-space" parent_building: "building-main" type: "retail" area_sf: ${anchorSpaceAreaSf} lease: status: "leased" tenant: "Anchor Retailer" rent_psf: ${rentPsf} lease_type: "NNN" start_date: "${currentDate}" end_date: "${new Date(new Date().setFullYear(new Date().getFullYear() + 10)).toISOString().split('T')[0]}" escalation_rate: 0.02 - id: "small-retail-1" parent_building: "building-main" type: "retail" area_sf: ${smallSpaceAreaSf} lease: status: "leased" tenant: "Boutique Store" rent_psf: ${rentPsf + 8} lease_type: "gross" start_date: "${currentDate}" end_date: "${new Date(new Date().setFullYear(new Date().getFullYear() + 5)).toISOString().split('T')[0]}" escalation_rate: 0.03 assumptions: - name: "discount_rate" type: "fixed" value: 0.09 description: "Required return rate for retail investment" tags: ["capital_market"] - name: "rent_growth" type: "fixed" value: 0.02 scope: "global" description: "Annual rent growth rate" tags: ["revenue"] - name: "vacancy_rate" type: "fixed" value: 0.08 scope: "space_type:retail" description: "Long-term vacancy assumption" tags: ["revenue", "risk"] models: - name: "retail_dcf" type: "deterministic" duration_years: 10 steps_per_year: 12 inputs: ["discount_rate", "rent_growth", "vacancy_rate"] outputs: ["IRR", "NPV", "cash_flow"] description: "DCF model for retail property" simulation: type: "monte_carlo" iterations: 1000 processes: {} outputs: summary_metrics: ["IRR", "NPV"] outputs: format: "json" metrics: ["IRR", "NPV", "cash_flow"] `; } function generateMixedUseTemplate(config, currentDate) { const officeAreaSf = Math.floor(config.totalAreaSf * 0.5); const retailAreaSf = Math.floor(config.totalAreaSf * 0.3); return `# CREDL Project: ${config.name} # Generated on ${currentDate} metadata: version: 0.2 name: "${config.name}" description: "${config.description}" created_date: "${currentDate}" author: "CREDL Init" assets: - id: "asset-1" name: "${config.name}" property_type: "Mixed Use" location: "${config.location}" total_area_sf: ${config.totalAreaSf} buildings: - id: "building-main" name: "${config.buildingName}" floors: ${config.floors} total_area_sf: ${config.totalAreaSf} year_built: ${config.yearBuilt} spaces: - id: "office-floor-2" parent_building: "building-main" type: "office" area_sf: ${Math.floor(officeAreaSf * 0.4)} lease: status: "leased" tenant: "Professional Services" rent_psf: 48 lease_type: "NNN" start_date: "${currentDate}" end_date: "${new Date(new Date().setFullYear(new Date().getFullYear() + 7)).toISOString().split('T')[0]}" escalation_rate: 0.025 - id: "ground-retail" parent_building: "building-main" type: "retail" area_sf: ${retailAreaSf} lease: status: "leased" tenant: "Restaurant Chain" rent_psf: 35 lease_type: "NNN" start_date: "${currentDate}" end_date: "${new Date(new Date().setFullYear(new Date().getFullYear() + 8)).toISOString().split('T')[0]}" escalation_rate: 0.02 assumptions: - name: "discount_rate" type: "fixed" value: 0.08 description: "Required return rate for mixed-use investment" tags: ["capital_market"] - name: "office_rent_growth" type: "fixed" value: 0.025 scope: "space_type:office" description: "Annual office rent growth rate" tags: ["revenue", "office"] - name: "retail_rent_growth" type: "fixed" value: 0.02 scope: "space_type:retail" description: "Annual retail rent growth rate" tags: ["revenue", "retail"] models: - name: "mixed_use_dcf" type: "deterministic" duration_years: 12 steps_per_year: 12 inputs: ["discount_rate", "office_rent_growth", "retail_rent_growth"] outputs: ["IRR", "NPV", "cash_flow"] description: "DCF model for mixed-use property" simulation: type: "monte_carlo" iterations: 1000 processes: {} outputs: summary_metrics: ["IRR", "NPV"] outputs: format: "json" metrics: ["IRR", "NPV", "cash_flow"] `; } function generateIndustrialTemplate(config, currentDate) { const warehouseAreaSf = Math.floor(config.totalAreaSf * 0.9); const rentPsf = 8; return `# CREDL Project: ${config.name} # Generated on ${currentDate} metadata: version: 0.2 name: "${config.name}" description: "${config.description}" created_date: "${currentDate}" author: "CREDL Init" assets: - id: "asset-1" name: "${config.name}" property_type: "Industrial" location: "${config.location}" total_area_sf: ${config.totalAreaSf} buildings: - id: "building-main" name: "${config.buildingName}" floors: ${config.floors} total_area_sf: ${config.totalAreaSf} year_built: ${config.yearBuilt} spaces: - id: "warehouse-bay-1" parent_building: "building-main" type: "industrial" area_sf: ${warehouseAreaSf} lease: status: "leased" tenant: "Logistics Company" rent_psf: ${rentPsf} lease_type: "NNN" start_date: "${currentDate}" end_date: "${new Date(new Date().setFullYear(new Date().getFullYear() + 10)).toISOString().split('T')[0]}" escalation_rate: 0.025 assumptions: - name: "discount_rate" type: "fixed" value: 0.075 description: "Required return rate for industrial investment" tags: ["capital_market"] - name: "rent_growth" type: "fixed" value: 0.03 scope: "global" description: "Annual rent growth rate" tags: ["revenue"] - name: "vacancy_rate" type: "fixed" value: 0.03 scope: "space_type:industrial" description: "Long-term vacancy assumption" tags: ["revenue", "risk"] models: - name: "industrial_dcf" type: "deterministic" duration_years: 15 steps_per_year: 12 inputs: ["discount_rate", "rent_growth", "vacancy_rate"] outputs: ["IRR", "NPV", "cash_flow"] description: "DCF model for industrial property" simulation: type: "monte_carlo" iterations: 1000 processes: {} outputs: summary_metrics: ["IRR", "NPV"] outputs: format: "json" metrics: ["IRR", "NPV", "cash_flow"] `; } function generateResidentialTemplate(config, currentDate) { const unitAreaSf = 1200; const rentPsf = 2.5; return `# CREDL Project: ${config.name} # Generated on ${currentDate} metadata: version: 0.2 name: "${config.name}" description: "${config.description}" created_date: "${currentDate}" author: "CREDL Init" assets: - id: "asset-1" name: "${config.name}" property_type: "Residential" location: "${config.location}" total_area_sf: ${config.totalAreaSf} buildings: - id: "building-main" name: "${config.buildingName}" floors: ${config.floors} total_area_sf: ${config.totalAreaSf} year_built: ${config.yearBuilt} spaces: - id: "unit-1a" parent_building: "building-main" type: "residential" area_sf: ${unitAreaSf} lease: status: "leased" tenant: "Resident A" rent_psf: ${rentPsf} lease_type: "gross" start_date: "${currentDate}" end_date: "${new Date(new Date().setFullYear(new Date().getFullYear() + 1)).toISOString().split('T')[0]}" escalation_rate: 0.04 - id: "unit-1b" parent_building: "building-main" type: "residential" area_sf: ${unitAreaSf * 0.9} lease: status: "vacant" rent_psf: ${rentPsf + 0.2} lease_type: "gross" start_date: "${new Date(new Date().setMonth(new Date().getMonth() + 2)).toISOString().split('T')[0]}" end_date: "${new Date(new Date().setFullYear(new Date().getFullYear() + 1, new Date().getMonth() + 2)).toISOString().split('T')[0]}" assumptions: - name: "discount_rate" type: "fixed" value: 0.07 description: "Required return rate for residential investment" tags: ["capital_market"] - name: "rent_growth" type: "fixed" value: 0.04 scope: "global" description: "Annual rent growth rate" tags: ["revenue"] - name: "vacancy_rate" type: "fixed" value: 0.06 scope: "space_type:residential" description: "Long-term vacancy assumption" tags: ["revenue", "risk"] models: - name: "residential_dcf" type: "deterministic" duration_years: 10 steps_per_year: 12 inputs: ["discount_rate", "rent_growth", "vacancy_rate"] outputs: ["IRR", "NPV", "cash_flow"] description: "DCF model for residential property" simulation: type: "monte_carlo" iterations: 1000 processes: {} outputs: summary_metrics: ["IRR", "NPV"] outputs: format: "json" metrics: ["IRR", "NPV", "cash_flow"] `; } //# sourceMappingURL=init.js.map