UNPKG

@boundless-oss/atlas

Version:

Atlas - MCP Server for comprehensive startup project management

154 lines (131 loc) 4.42 kB
export class VariableResolver { /** * Resolve variables in an object by replacing ${variable} patterns * with actual values from the context */ async resolveVariables( obj: any, context: Record<string, any> ): Promise<any> { if (typeof obj === 'string') { return this.resolveString(obj, context); } if (Array.isArray(obj)) { return Promise.all(obj.map(item => this.resolveVariables(item, context))); } if (obj && typeof obj === 'object') { const resolved: Record<string, any> = {}; for (const [key, value] of Object.entries(obj)) { resolved[key] = await this.resolveVariables(value, context); } return resolved; } return obj; } /** * Resolve variables in a string */ private resolveString(str: string, context: Record<string, any>): string { return str.replace(/\$\{([^}]+)\}/g, (match, path) => { const value = this.getValueByPath(context, path.trim()); return value !== undefined ? String(value) : match; }); } /** * Get a value from an object using a dot-notation path */ private getValueByPath(obj: any, path: string): any { const parts = path.split('.'); let current = obj; for (const part of parts) { if (current && typeof current === 'object' && part in current) { current = current[part]; } else { return undefined; } } return current; } /** * Evaluate a condition expression */ async evaluateCondition( condition: string, context: Record<string, any> ): Promise<boolean> { // First resolve any variables in the condition const resolvedCondition = this.resolveString(condition, context); try { // Simple expression evaluation // In production, use a proper expression evaluator like expr-eval // Handle basic comparisons const comparisonMatch = resolvedCondition.match( /^(.+?)\s*(===?|!==?|>=?|<=?)\s*(.+)$/ ); if (comparisonMatch) { const [, left, operator, right] = comparisonMatch; const leftValue = this.parseValue(left.trim(), context); const rightValue = this.parseValue(right.trim(), context); switch (operator) { case '==': case '===': return leftValue == rightValue; case '!=': case '!==': return leftValue != rightValue; case '>': return Number(leftValue) > Number(rightValue); case '>=': return Number(leftValue) >= Number(rightValue); case '<': return Number(leftValue) < Number(rightValue); case '<=': return Number(leftValue) <= Number(rightValue); } } // Handle boolean values if (resolvedCondition === 'true') return true; if (resolvedCondition === 'false') return false; // Check if it's a variable that evaluates to truthy const value = this.getValueByPath(context, resolvedCondition); return Boolean(value); } catch (error) { console.error('Error evaluating condition:', error); return false; } } /** * Parse a value that might be a literal or a variable reference */ private parseValue(str: string, context: Record<string, any>): any { // Remove quotes if present if ((str.startsWith('"') && str.endsWith('"')) || (str.startsWith("'") && str.endsWith("'"))) { return str.slice(1, -1); } // Check if it's a number if (!isNaN(Number(str))) { return Number(str); } // Check if it's a boolean if (str === 'true') return true; if (str === 'false') return false; // Try to get it as a variable const value = this.getValueByPath(context, str); return value !== undefined ? value : str; } /** * Evaluate a complex expression (for future enhancement) */ async evaluateExpression( expression: string, context: Record<string, any> ): Promise<any> { // This could be enhanced to support: // - Mathematical expressions: ${price * quantity} // - Function calls: ${format(date, 'YYYY-MM-DD')} // - Array operations: ${items.filter(i => i.active).length} // For now, just resolve variables return this.resolveString(expression, context); } }