UNPKG

@allma/core-cdk

Version:

Core AWS CDK constructs for deploying the Allma serverless AI orchestration platform.

128 lines 5.89 kB
import { log_warn } from '@allma/core-sdk'; import { getSmartValueByJsonPath } from '../data-mapper.js'; /** * Evaluates a single, simple condition. * This can be either a JSONPath for a truthiness check, or a simple comparison * of the form `JSONPath operator literal`. * @private */ const evaluateSingleCondition = async (condition, context, correlationId) => { let conditionMet = false; let resolvedValue; const allEvents = []; // Regular expression to parse simple comparison expressions. // It captures: 1=JSONPath, 2=operator, 3=literal value. // Updated to include 'undefined' explicitly as a literal. const expressionRegex = /^\s*(\$\..+?)\s*([<>=!]{1,3})\s*(true|false|null|undefined|-?\d+(?:\.\d+)?|'[^']*'|"[^"]*")\s*$/; const match = condition.match(expressionRegex); if (match) { const [, jsonPath, operator, literalValueStr] = match; // Resolve the value from the context using the smart getter. const { value: pathValue, events } = await getSmartValueByJsonPath(jsonPath, context, correlationId); resolvedValue = pathValue; allEvents.push(...events); // Parse the literal value from the regex match. let compareValue; if (literalValueStr === 'true') compareValue = true; else if (literalValueStr === 'false') compareValue = false; else if (literalValueStr === 'null') compareValue = null; else if (literalValueStr === 'undefined') compareValue = undefined; else if (!isNaN(Number(literalValueStr))) compareValue = Number(literalValueStr); else compareValue = literalValueStr.slice(1, -1); // Unquote the string literal // Perform the comparison based on the operator. switch (operator) { case '>': conditionMet = pathValue > compareValue; break; case '<': conditionMet = pathValue < compareValue; break; case '>=': conditionMet = pathValue >= compareValue; break; case '<=': conditionMet = pathValue <= compareValue; break; case '==': case '=': conditionMet = pathValue == compareValue; break; // Loose equality case '===': conditionMet = pathValue === compareValue; break; // Strict equality case '!=': conditionMet = pathValue != compareValue; break; case '!==': conditionMet = pathValue !== compareValue; break; default: log_warn(`Unsupported operator '${operator}' in condition. Evaluating to false.`, { condition }, correlationId); conditionMet = false; } } else { // If it's not a simple expression, treat it as a JSONPath to be checked for truthiness. const { value, events } = await getSmartValueByJsonPath(condition, context, correlationId); resolvedValue = value; allEvents.push(...events); // For filter expressions like $[?(@.a==1)], the result is an array. // An empty array should be treated as falsy. if (Array.isArray(value)) { conditionMet = value.length > 0; } else { // Standard JavaScript truthiness for other types (null, undefined, 0, "", false are falsy). conditionMet = !!value; } } return { result: conditionMet, resolvedValue, events: allEvents }; }; /** * Evaluates a condition string, supporting simple expressions, JSONPath truthiness checks, * and compound conditions joined by '&&' (AND) and '||' (OR). * This function respects operator precedence: OR has lower precedence than AND, so the expression * is split by '||' first, and then each segment is evaluated as an AND chain. * * @param condition The condition string (e.g., "$.path.value > 10 || $.path.value == null"). * @param context The data context against which the condition is evaluated. * @param correlationId For logging. * @returns A promise that resolves to an object containing the boolean result and any mapping events. */ export const evaluateCondition = async (condition, context, correlationId) => { // 1. Split by OR (||) first to handle logical disjunction. const orSegments = condition.split('||').map(c => c.trim()); const allEvents = []; const collectedResolvedValues = []; for (const orSegment of orSegments) { // 2. For each OR segment, split by AND (&&) to handle logical conjunction. const andSegments = orSegment.split('&&').map(c => c.trim()); let isAndChainTrue = true; const currentChainValues = []; for (const subCondition of andSegments) { // Evaluate the atomic condition const { result: subResult, resolvedValue, events } = await evaluateSingleCondition(subCondition, context, correlationId); allEvents.push(...events); currentChainValues.push(resolvedValue); if (!subResult) { // Short-circuit: One false in an AND chain makes the chain false. isAndChainTrue = false; break; } } collectedResolvedValues.push(currentChainValues); if (isAndChainTrue) { // Short-circuit: One true chain in an OR expression makes the whole expression true. return { result: true, resolvedValue: collectedResolvedValues, events: allEvents }; } } // If we exit the loop, no OR segment evaluated to true. return { result: false, resolvedValue: collectedResolvedValues, events: allEvents }; }; //# sourceMappingURL=condition-evaluator.js.map