* Evaluate Action/Run/ToAssistant/OpenUrl Comp nodes into ActionPlan/ActionStep values.
( name: string, args: ASTNode[], context: EvaluationContext, )
| 304 | * Evaluate Action/Run/ToAssistant/OpenUrl Comp nodes into ActionPlan/ActionStep values. |
| 305 | */ |
| 306 | function evaluateActionCall( |
| 307 | name: string, |
| 308 | args: ASTNode[], |
| 309 | context: EvaluationContext, |
| 310 | ): ActionPlan | ActionStep | null { |
| 311 | switch (name) { |
| 312 | case "Action": { |
| 313 | // Action([step1, step2, ...]) → ActionPlan |
| 314 | const stepsArg = args.length > 0 ? evaluate(args[0], context) : []; |
| 315 | const rawSteps = Array.isArray(stepsArg) ? stepsArg : []; |
| 316 | const steps: ActionStep[] = rawSteps.filter( |
| 317 | (s): s is ActionStep => s != null && typeof s === "object" && "type" in s, |
| 318 | ); |
| 319 | return { steps }; |
| 320 | } |
| 321 | case "Run": { |
| 322 | // Run(runtimeRef) → ActionStep { type: "run", statementId, refType } |
| 323 | if (args.length === 0) return null; |
| 324 | const refNode = args[0]; |
| 325 | if (refNode.k === "RuntimeRef") { |
| 326 | return { type: ACTION_STEPS.Run, statementId: refNode.n, refType: refNode.refType }; |
| 327 | } |
| 328 | // Unresolved Ref — skip (filtered out by Action's step array) |
| 329 | return null; |
| 330 | } |
| 331 | case "ToAssistant": { |
| 332 | // ToAssistant("message") or ToAssistant("message", "context") |
| 333 | const message = args.length > 0 ? String(evaluate(args[0], context) ?? "") : ""; |
| 334 | const ctx = args.length > 1 ? String(evaluate(args[1], context) ?? "") : undefined; |
| 335 | return { type: ACTION_STEPS.ToAssistant, message, context: ctx }; |
| 336 | } |
| 337 | case "OpenUrl": { |
| 338 | // OpenUrl("url") |
| 339 | const url = args.length > 0 ? String(evaluate(args[0], context) ?? "") : ""; |
| 340 | return { type: ACTION_STEPS.OpenUrl, url }; |
| 341 | } |
| 342 | case "Set": { |
| 343 | // Set($varName, value) → ActionStep { type: "set", target, valueAST } |
| 344 | // First arg must be a StateRef (the $variable), second arg is the value expression. |
| 345 | // valueAST is preserved as-is and evaluated at click time by triggerAction. |
| 346 | // Loop variables (e.g. t.id from Each) are pre-resolved by Each's substituteRef. |
| 347 | if (args.length < 2) return null; |
| 348 | const targetNode = args[0]; |
| 349 | if (targetNode.k !== "StateRef") return null; |
| 350 | return { type: ACTION_STEPS.Set, target: targetNode.n, valueAST: args[1] }; |
| 351 | } |
| 352 | case "Reset": { |
| 353 | // Reset($var1, $var2, ...) → ActionStep { type: "reset", targets: [...] } |
| 354 | // All args must be StateRef nodes. Restores to declared defaults at runtime. |
| 355 | const targets = args |
| 356 | .filter((a): a is ASTNode & { k: "StateRef" } => a.k === "StateRef") |
| 357 | .map((a) => a.n); |
| 358 | if (targets.length === 0) return null; |
| 359 | return { type: ACTION_STEPS.Reset, targets }; |
| 360 | } |
| 361 | default: |
| 362 | return null; |
| 363 | } |