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
JavaScript
"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