mlld
Version:
mlld: llm scripting language
310 lines (308 loc) • 12.2 kB
JavaScript
import { normalizeIterableValue } from './chunk-JVJPPTN3.mjs';
import { logger } from './chunk-M3R2H5KU.mjs';
import { extractSecurityDescriptor, varMxToSecurityDescriptor, isStructuredValue, asData, ensureStructuredValue, mergeDescriptors, setExpressionProvenance } from './chunk-RKGZ44GZ.mjs';
import { isExecutable } from './chunk-EIWSEMQ4.mjs';
import { __name } from './chunk-NJQT543K.mjs';
// interpreter/eval/foreach.ts
function hasArrayData(value) {
if (!value || typeof value !== "object") {
return false;
}
if (!("data" in value)) {
return false;
}
const { data } = value;
return Array.isArray(data);
}
__name(hasArrayData, "hasArrayData");
async function evaluateForeachCommand(foreachExpr, env) {
if (process.env.MLLD_DEBUG === "true") {
logger.debug("evaluateForeachCommand called with:", {
foreachExpr
});
}
const node = foreachExpr && typeof foreachExpr === "object" && "value" in foreachExpr ? foreachExpr.value : foreachExpr;
const { execInvocation, arrays } = node;
let commandName;
let commandArgs = [];
if (execInvocation.type === "ExecInvocation") {
commandName = execInvocation.commandRef.name;
commandArgs = execInvocation.commandRef.args || [];
} else if (execInvocation.identifier) {
commandName = execInvocation.identifier;
} else {
throw new Error("Invalid foreach command structure");
}
const cmdVariable = env.getVariable(commandName);
if (!cmdVariable) {
throw new Error(`Command not found: ${commandName}`);
}
if (!isExecutable(cmdVariable)) {
throw new Error(`Variable '${commandName}' cannot be used with foreach. Expected an @exec command or @text template with parameters, but got type: ${cmdVariable.type}`);
}
const { evaluateDataValue } = await import('./data-value-evaluator-6G4NQGOF.mjs');
const evaluatedArrays = [];
const arraySecurityDescriptors = [];
const arrayNodes = arrays || commandArgs;
for (let i = 0; i < arrayNodes.length; i++) {
const arrayVar = arrayNodes[i];
if (process.env.MLLD_DEBUG_FOREACH === "true") {
console.error("[foreach] array node type:", arrayVar?.type);
}
const arrayValue = await evaluateDataValue(arrayVar, env);
let sourceDescriptor = extractSecurityDescriptor(arrayValue);
if (!sourceDescriptor && arrayVar && typeof arrayVar === "object") {
let referencedName;
if ("identifier" in arrayVar && typeof arrayVar.identifier === "string") {
referencedName = arrayVar.identifier;
} else if ("variable" in arrayVar && arrayVar.variable?.identifier) {
referencedName = arrayVar.variable.identifier;
}
const referencedVar = referencedName ? env.getVariable(referencedName) : void 0;
sourceDescriptor = referencedVar?.mx ? varMxToSecurityDescriptor(referencedVar.mx) : void 0;
}
if (isStructuredValue(arrayValue)) {
let structuredData = asData(arrayValue);
if (!Array.isArray(structuredData)) {
if (hasArrayData(structuredData)) {
structuredData = structuredData.data;
} else if (typeof structuredData === "string") {
try {
const parsed = JSON.parse(structuredData);
if (Array.isArray(parsed)) {
structuredData = parsed;
}
} catch {
}
}
}
if (!Array.isArray(structuredData) && typeof arrayValue.text === "string") {
try {
const parsed = JSON.parse(arrayValue.text);
if (Array.isArray(parsed)) {
structuredData = parsed;
}
} catch {
}
}
if (!Array.isArray(structuredData)) {
throw new Error(`Argument ${i + 1} to foreach must be an array, got structured ${arrayValue.type}`);
}
const normalizedStructured = normalizeIterableValue(structuredData);
attachDescriptorRecursively(normalizedStructured, sourceDescriptor);
evaluatedArrays.push(normalizedStructured);
arraySecurityDescriptors.push(sourceDescriptor);
continue;
}
if (!Array.isArray(arrayValue)) {
throw new Error(`Argument ${i + 1} to foreach must be an array, got ${typeof arrayValue}`);
}
const normalizedArray = normalizeIterableValue(arrayValue);
attachDescriptorRecursively(normalizedArray, sourceDescriptor);
evaluatedArrays.push(normalizedArray);
arraySecurityDescriptors.push(sourceDescriptor);
}
validateArrayInputs(evaluatedArrays);
if (!isWithinPerformanceLimit(evaluatedArrays)) {
const totalCombinations = evaluatedArrays.reduce((total, arr) => total * arr.length, 1);
throw new Error(`Foreach operation would generate ${totalCombinations} combinations, which exceeds the performance limit. Consider reducing array sizes or using more specific filtering.`);
}
const definition = cmdVariable.value;
const paramCount = cmdVariable.paramNames?.length || definition.paramNames?.length || 0;
if (evaluatedArrays.length !== paramCount) {
const paramType = definition.sourceDirective === "text" ? "Text template" : "Command";
throw new Error(`${paramType} '${commandName}' expects ${paramCount} parameter${paramCount !== 1 ? "s" : ""}, but foreach is passing ${evaluatedArrays.length} array${evaluatedArrays.length !== 1 ? "s" : ""}`);
}
const tuples = cartesianProduct(evaluatedArrays);
const { evaluateExecInvocation } = await import('./exec-invocation-M54PNM33.mjs');
const results = [];
const mergeTupleDescriptor = /* @__PURE__ */ __name(() => {
let descriptor;
for (const candidate of arraySecurityDescriptors) {
if (!candidate) continue;
descriptor = descriptor ? mergeDescriptors(descriptor, candidate) : candidate;
}
return descriptor;
}, "mergeTupleDescriptor");
for (let i = 0; i < tuples.length; i++) {
const tuple = tuples[i];
try {
const execInvocationNode = {
type: "ExecInvocation",
commandRef: {
identifier: commandName,
args: tuple
},
withClause: null
};
const result = await evaluateExecInvocation(execInvocationNode, env);
const tupleDescriptor = mergeTupleDescriptor();
let structuredResult = isStructuredValue(result.value) ? result.value : ensureStructuredValue(result.value);
if (tupleDescriptor) {
const existingDescriptor = extractSecurityDescriptor(structuredResult);
const merged = existingDescriptor ? mergeDescriptors(existingDescriptor, tupleDescriptor) : tupleDescriptor;
structuredResult = ensureStructuredValue(structuredResult, structuredResult.type, structuredResult.text, {
security: merged
});
}
results.push(structuredResult);
} catch (error) {
const params = cmdVariable.paramNames || definition.paramNames || [];
const iterationContext = params.map((param, index) => `${param}: ${JSON.stringify(tuple[index])}`).join(", ");
throw new Error(`Error in foreach iteration ${i + 1} (${iterationContext}): ${error instanceof Error ? error.message : String(error)}`);
}
}
let finalResults = results;
const withClause = node.with || foreachExpr.with;
const batchPipelineConfig = node.batchPipeline || withClause?.batchPipeline || foreachExpr.batchPipeline;
const batchStages = Array.isArray(batchPipelineConfig) ? batchPipelineConfig : batchPipelineConfig?.pipeline;
if (batchStages && batchStages.length > 0) {
const { processPipeline } = await import('./unified-processor-GTJC5G2K.mjs');
const { createArrayVariable } = await import('./variable-FNPDYIEH.mjs');
const batchInput = createArrayVariable("foreach-batch-input", results, false, {
directive: "foreach",
syntax: "expression",
hasInterpolation: false,
isMultiLine: false
}, {
internal: {
isBatchInput: true
}
});
try {
const pipelineResult = await processPipeline({
value: batchInput,
env,
pipeline: batchStages,
identifier: `foreach-batch-${commandName}`,
location: foreachExpr.location,
isRetryable: false
});
const { isVariable, extractVariableValue } = await import('./variable-resolution-HFG3FTZK.mjs');
if (isStructuredValue(pipelineResult)) {
finalResults = pipelineResult;
} else if (isVariable(pipelineResult)) {
finalResults = await extractVariableValue(pipelineResult, env);
} else {
finalResults = pipelineResult;
}
} catch (error) {
logger.warn(`Batch pipeline failed for foreach: ${error instanceof Error ? error.message : String(error)}`);
finalResults = results;
}
}
return finalResults;
}
__name(evaluateForeachCommand, "evaluateForeachCommand");
async function evaluateForeachSection(foreachExpr, env) {
return evaluateForeachCommand(foreachExpr, env);
}
__name(evaluateForeachSection, "evaluateForeachSection");
async function validateForeachExpression(foreachExpr, env) {
const errors = [];
try {
if (!foreachExpr || typeof foreachExpr !== "object") {
errors.push("Foreach expression must be an object");
return {
valid: false,
errors
};
}
const { execInvocation } = foreachExpr.value || foreachExpr;
if (!execInvocation) {
errors.push("Foreach expression missing exec invocation");
return {
valid: false,
errors
};
}
let commandName;
if (execInvocation.type === "ExecInvocation") {
commandName = execInvocation.commandRef.name;
} else if (execInvocation.identifier) {
commandName = execInvocation.identifier;
} else {
errors.push("Invalid foreach command structure");
return {
valid: false,
errors
};
}
const cmdVariable = env.getVariable(commandName);
if (!cmdVariable) {
errors.push(`Command not found: ${commandName}`);
} else if (!isExecutable(cmdVariable)) {
errors.push(`Variable '${commandName}' is not executable`);
}
return {
valid: errors.length === 0,
errors
};
} catch (error) {
errors.push(`Validation error: ${error instanceof Error ? error.message : String(error)}`);
return {
valid: false,
errors
};
}
}
__name(validateForeachExpression, "validateForeachExpression");
function validateArrayInputs(arrays) {
if (arrays.length === 0) {
throw new Error("Foreach requires at least one array argument");
}
for (let i = 0; i < arrays.length; i++) {
const arr = arrays[i];
if (!Array.isArray(arr)) {
throw new Error(`Argument ${i + 1} must be an array`);
}
if (arr.length === 0) {
throw new Error(`Array ${i + 1} cannot be empty`);
}
}
}
__name(validateArrayInputs, "validateArrayInputs");
function isWithinPerformanceLimit(arrays) {
const MAX_COMBINATIONS = 1e4;
const totalCombinations = arrays.reduce((total, arr) => total * arr.length, 1);
return totalCombinations <= MAX_COMBINATIONS;
}
__name(isWithinPerformanceLimit, "isWithinPerformanceLimit");
function cartesianProduct(arrays) {
if (arrays.length === 0) return [];
if (arrays.length === 1) return arrays[0].map((item) => [
item
]);
const result = [];
const [head, ...tail] = arrays;
const tailProduct = cartesianProduct(tail);
for (const headItem of head) {
for (const tailItem of tailProduct) {
result.push([
headItem,
...tailItem
]);
}
}
return result;
}
__name(cartesianProduct, "cartesianProduct");
function attachDescriptorRecursively(value, descriptor) {
if (!descriptor || !value || typeof value !== "object") {
return;
}
setExpressionProvenance(value, descriptor);
if (Array.isArray(value)) {
for (const entry of value) {
attachDescriptorRecursively(entry, descriptor);
}
return;
}
for (const child of Object.values(value)) {
attachDescriptorRecursively(child, descriptor);
}
}
__name(attachDescriptorRecursively, "attachDescriptorRecursively");
export { evaluateForeachCommand, evaluateForeachSection, validateForeachExpression };
//# sourceMappingURL=chunk-2NIKWPWP.mjs.map
//# sourceMappingURL=chunk-2NIKWPWP.mjs.map