@particle-academy/fancy-flow 0.77.1 → 0.79.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (82) hide show
  1. package/dist/{FlowViewer-Ak8kbY0e.d.cts → FlowViewer-BqYQl2vS.d.cts} +1 -1
  2. package/dist/{FlowViewer-BAV29Zpi.d.ts → FlowViewer-_Fq8AX9V.d.ts} +1 -1
  3. package/dist/{HumanPrompt-xYPCL2jO.d.cts → HumanPrompt-Sl1wD9Bx.d.cts} +2 -2
  4. package/dist/{HumanPrompt-Ge_ccCgS.d.ts → HumanPrompt-_SKIdSpx.d.ts} +2 -2
  5. package/dist/{chunk-T6WIIGSK.js → chunk-I5EPWHNM.js} +3 -3
  6. package/dist/{chunk-T6WIIGSK.js.map → chunk-I5EPWHNM.js.map} +1 -1
  7. package/dist/{chunk-EGXA4SYJ.js → chunk-J66LOLJR.js} +4 -4
  8. package/dist/{chunk-EGXA4SYJ.js.map → chunk-J66LOLJR.js.map} +1 -1
  9. package/dist/{chunk-G5RAGPVD.js → chunk-KMONOPAK.js} +3 -3
  10. package/dist/{chunk-G5RAGPVD.js.map → chunk-KMONOPAK.js.map} +1 -1
  11. package/dist/{chunk-ARSOSEJV.js → chunk-LBE4L3DS.js} +4 -4
  12. package/dist/{chunk-ARSOSEJV.js.map → chunk-LBE4L3DS.js.map} +1 -1
  13. package/dist/{chunk-EEIWAIXW.js → chunk-OZHBAXMA.js} +125 -8
  14. package/dist/chunk-OZHBAXMA.js.map +1 -0
  15. package/dist/{chunk-2VI24UPB.js → chunk-QMJVUT7C.js} +3 -3
  16. package/dist/{chunk-2VI24UPB.js.map → chunk-QMJVUT7C.js.map} +1 -1
  17. package/dist/{chunk-CQAMEPPI.js → chunk-R4G7SPLD.js} +3 -3
  18. package/dist/{chunk-CQAMEPPI.js.map → chunk-R4G7SPLD.js.map} +1 -1
  19. package/dist/connectors.d.cts +2 -2
  20. package/dist/connectors.d.ts +2 -2
  21. package/dist/durable/index.d.cts +2 -2
  22. package/dist/durable/index.d.ts +2 -2
  23. package/dist/durable.cjs +123 -6
  24. package/dist/durable.cjs.map +1 -1
  25. package/dist/durable.js +1 -1
  26. package/dist/engine.cjs +123 -6
  27. package/dist/engine.cjs.map +1 -1
  28. package/dist/engine.d.cts +6 -6
  29. package/dist/engine.d.ts +6 -6
  30. package/dist/engine.js +4 -4
  31. package/dist/fields/react-fancy.d.cts +3 -3
  32. package/dist/fields/react-fancy.d.ts +3 -3
  33. package/dist/index.cjs +123 -6
  34. package/dist/index.cjs.map +1 -1
  35. package/dist/index.d.cts +10 -10
  36. package/dist/index.d.ts +10 -10
  37. package/dist/index.js +12 -12
  38. package/dist/layout/index.d.cts +1 -1
  39. package/dist/layout/index.d.ts +1 -1
  40. package/dist/llm/prism.d.cts +1 -1
  41. package/dist/llm/prism.d.ts +1 -1
  42. package/dist/llm/vercel-ai.d.cts +1 -1
  43. package/dist/llm/vercel-ai.d.ts +1 -1
  44. package/dist/registry/index.d.cts +5 -5
  45. package/dist/registry/index.d.ts +5 -5
  46. package/dist/{registry-JUcN_xWK.d.ts → registry-C4GcH583.d.ts} +1 -1
  47. package/dist/{registry-B6SiTaLZ.d.cts → registry-DWIY3bMP.d.cts} +1 -1
  48. package/dist/registry.cjs +123 -6
  49. package/dist/registry.cjs.map +1 -1
  50. package/dist/registry.js +2 -2
  51. package/dist/{run-cohort-rgT_FX3V.d.ts → run-cohort-DAfhZm-p.d.ts} +2 -2
  52. package/dist/{run-cohort-DNpPa0mq.d.cts → run-cohort-DRJvUWO9.d.cts} +2 -2
  53. package/dist/{run-flow-pW0PllaZ.d.cts → run-flow-Bdrn4lQV.d.cts} +1 -1
  54. package/dist/{run-flow-BLP9rXfO.d.ts → run-flow-D2iOjWsj.d.ts} +1 -1
  55. package/dist/runtime/index.d.cts +5 -5
  56. package/dist/runtime/index.d.ts +5 -5
  57. package/dist/runtime.cjs +123 -6
  58. package/dist/runtime.cjs.map +1 -1
  59. package/dist/runtime.js +3 -3
  60. package/dist/schema/index.d.cts +1 -1
  61. package/dist/schema/index.d.ts +1 -1
  62. package/dist/schema.cjs +123 -6
  63. package/dist/schema.cjs.map +1 -1
  64. package/dist/schema.js +3 -3
  65. package/dist/screens.cjs +123 -6
  66. package/dist/screens.cjs.map +1 -1
  67. package/dist/screens.d.cts +2 -2
  68. package/dist/screens.d.ts +2 -2
  69. package/dist/screens.js +4 -4
  70. package/dist/terminal/fancy-term-host.d.cts +1 -1
  71. package/dist/terminal/fancy-term-host.d.ts +1 -1
  72. package/dist/{types-BdkXxjFh.d.cts → types-B1jftRa9.d.cts} +1 -1
  73. package/dist/{types-CbekKMMO.d.ts → types-D5SBBpbe.d.ts} +1 -1
  74. package/dist/{types-B-Syk9-M.d.ts → types-E48Dpesq.d.cts} +20 -0
  75. package/dist/{types-B-Syk9-M.d.cts → types-E48Dpesq.d.ts} +20 -0
  76. package/dist/ux.cjs +123 -6
  77. package/dist/ux.cjs.map +1 -1
  78. package/dist/ux.d.cts +2 -2
  79. package/dist/ux.d.ts +2 -2
  80. package/dist/ux.js +1 -1
  81. package/package.json +2 -2
  82. package/dist/chunk-EEIWAIXW.js.map +0 -1
package/dist/engine.d.cts CHANGED
@@ -1,9 +1,9 @@
1
- export { R as RunOptions, a as RunResult, r as runFlow } from './run-flow-pW0PllaZ.cjs';
2
- import { F as FlowGraph, R as RunEvent, N as NodeExecutor, W as WorkflowResolver, a as FlowNode } from './types-B-Syk9-M.cjs';
3
- export { A as ActionNodeData, B as BaseNodeData, D as DecisionNodeData, E as ExecutorRegistry, e as FlowEdge, f as FlowNodeData, g as FlowNodeKind, h as NodeRunStatus, i as NoteNodeData, O as OutputNodeData, P as PortDescriptor, c as RunIdentity, d as RunIdentityJson, S as SubgraphNodeData, j as TriggerNodeData, k as escapeSegment } from './types-B-Syk9-M.cjs';
4
- export { C as CohortGuard, a as CohortOptions, b as CohortPolicy, c as CohortResult, r as runCohort } from './run-cohort-DNpPa0mq.cjs';
5
- export { K as KindSchemaOverlay, a as KindSchemaOverlayResult, b as applyKindSchemaOverlay, c as clearKindSchemaOverlays, d as defaultConfigFor, g as getNodeKind, k as kindIds, l as listNodeKinds, r as registerNodeKind, e as resolveKindId, u as unregisterNodeKind, v as validateConfig } from './registry-B6SiTaLZ.cjs';
6
- export { b as CapabilityRequirement, C as ConfigField, E as EmitsRelation, M as ManifestProblem, c as ManifestValidation, d as NODE_MANIFEST_SCHEMA_VERSION, a as NodeCategory, N as NodeKindDefinition, e as NodePackageManifest, f as NodeRuntimeId, g as NodeRuntimeSpec, O as OutputField, h as OutputShape, P as PortSpec, S as SideEffects, i as checkCapabilities, j as checkRuntimeSupport, s as satisfiesRange, v as validateNodeManifest } from './types-BdkXxjFh.cjs';
1
+ export { R as RunOptions, a as RunResult, r as runFlow } from './run-flow-Bdrn4lQV.cjs';
2
+ import { F as FlowGraph, R as RunEvent, N as NodeExecutor, W as WorkflowResolver, a as FlowNode } from './types-E48Dpesq.cjs';
3
+ export { A as ActionNodeData, B as BaseNodeData, D as DecisionNodeData, E as ExecutorRegistry, e as FlowEdge, f as FlowNodeData, g as FlowNodeKind, h as NodeRunStatus, i as NoteNodeData, O as OutputNodeData, P as PortDescriptor, c as RunIdentity, d as RunIdentityJson, S as SubgraphNodeData, j as TriggerNodeData, k as escapeSegment } from './types-E48Dpesq.cjs';
4
+ export { C as CohortGuard, a as CohortOptions, b as CohortPolicy, c as CohortResult, r as runCohort } from './run-cohort-DRJvUWO9.cjs';
5
+ export { K as KindSchemaOverlay, a as KindSchemaOverlayResult, b as applyKindSchemaOverlay, c as clearKindSchemaOverlays, d as defaultConfigFor, g as getNodeKind, k as kindIds, l as listNodeKinds, r as registerNodeKind, e as resolveKindId, u as unregisterNodeKind, v as validateConfig } from './registry-DWIY3bMP.cjs';
6
+ export { b as CapabilityRequirement, C as ConfigField, E as EmitsRelation, M as ManifestProblem, c as ManifestValidation, d as NODE_MANIFEST_SCHEMA_VERSION, a as NodeCategory, N as NodeKindDefinition, e as NodePackageManifest, f as NodeRuntimeId, g as NodeRuntimeSpec, O as OutputField, h as OutputShape, P as PortSpec, S as SideEffects, i as checkCapabilities, j as checkRuntimeSupport, s as satisfiesRange, v as validateNodeManifest } from './types-B1jftRa9.cjs';
7
7
  export { E as EvaluateOptions, a as ExprContext, b as ExprValue, R as Resolution, U as UnresolvedPathError, c as UnresolvedPolicy, e as evaluateConfig, d as evaluateExpression, r as resolvePath, t as text, f as truthy, g as tryResolvePath } from './expr-BfXDrs4H.cjs';
8
8
  import { P as PauseAwaiting } from './pause-9iT4tCEV.cjs';
9
9
  export { L as LEGACY_PAUSE_PREFIXES, a as PAUSE_PREFIX, b as PauseSignal, d as decodePause, e as encodePause, i as isPause, p as pauseForHuman } from './pause-9iT4tCEV.cjs';
package/dist/engine.d.ts CHANGED
@@ -1,9 +1,9 @@
1
- export { R as RunOptions, a as RunResult, r as runFlow } from './run-flow-BLP9rXfO.js';
2
- import { F as FlowGraph, R as RunEvent, N as NodeExecutor, W as WorkflowResolver, a as FlowNode } from './types-B-Syk9-M.js';
3
- export { A as ActionNodeData, B as BaseNodeData, D as DecisionNodeData, E as ExecutorRegistry, e as FlowEdge, f as FlowNodeData, g as FlowNodeKind, h as NodeRunStatus, i as NoteNodeData, O as OutputNodeData, P as PortDescriptor, c as RunIdentity, d as RunIdentityJson, S as SubgraphNodeData, j as TriggerNodeData, k as escapeSegment } from './types-B-Syk9-M.js';
4
- export { C as CohortGuard, a as CohortOptions, b as CohortPolicy, c as CohortResult, r as runCohort } from './run-cohort-rgT_FX3V.js';
5
- export { K as KindSchemaOverlay, a as KindSchemaOverlayResult, b as applyKindSchemaOverlay, c as clearKindSchemaOverlays, d as defaultConfigFor, g as getNodeKind, k as kindIds, l as listNodeKinds, r as registerNodeKind, e as resolveKindId, u as unregisterNodeKind, v as validateConfig } from './registry-JUcN_xWK.js';
6
- export { b as CapabilityRequirement, C as ConfigField, E as EmitsRelation, M as ManifestProblem, c as ManifestValidation, d as NODE_MANIFEST_SCHEMA_VERSION, a as NodeCategory, N as NodeKindDefinition, e as NodePackageManifest, f as NodeRuntimeId, g as NodeRuntimeSpec, O as OutputField, h as OutputShape, P as PortSpec, S as SideEffects, i as checkCapabilities, j as checkRuntimeSupport, s as satisfiesRange, v as validateNodeManifest } from './types-CbekKMMO.js';
1
+ export { R as RunOptions, a as RunResult, r as runFlow } from './run-flow-D2iOjWsj.js';
2
+ import { F as FlowGraph, R as RunEvent, N as NodeExecutor, W as WorkflowResolver, a as FlowNode } from './types-E48Dpesq.js';
3
+ export { A as ActionNodeData, B as BaseNodeData, D as DecisionNodeData, E as ExecutorRegistry, e as FlowEdge, f as FlowNodeData, g as FlowNodeKind, h as NodeRunStatus, i as NoteNodeData, O as OutputNodeData, P as PortDescriptor, c as RunIdentity, d as RunIdentityJson, S as SubgraphNodeData, j as TriggerNodeData, k as escapeSegment } from './types-E48Dpesq.js';
4
+ export { C as CohortGuard, a as CohortOptions, b as CohortPolicy, c as CohortResult, r as runCohort } from './run-cohort-DAfhZm-p.js';
5
+ export { K as KindSchemaOverlay, a as KindSchemaOverlayResult, b as applyKindSchemaOverlay, c as clearKindSchemaOverlays, d as defaultConfigFor, g as getNodeKind, k as kindIds, l as listNodeKinds, r as registerNodeKind, e as resolveKindId, u as unregisterNodeKind, v as validateConfig } from './registry-C4GcH583.js';
6
+ export { b as CapabilityRequirement, C as ConfigField, E as EmitsRelation, M as ManifestProblem, c as ManifestValidation, d as NODE_MANIFEST_SCHEMA_VERSION, a as NodeCategory, N as NodeKindDefinition, e as NodePackageManifest, f as NodeRuntimeId, g as NodeRuntimeSpec, O as OutputField, h as OutputShape, P as PortSpec, S as SideEffects, i as checkCapabilities, j as checkRuntimeSupport, s as satisfiesRange, v as validateNodeManifest } from './types-D5SBBpbe.js';
7
7
  export { E as EvaluateOptions, a as ExprContext, b as ExprValue, R as Resolution, U as UnresolvedPathError, c as UnresolvedPolicy, e as evaluateConfig, d as evaluateExpression, r as resolvePath, t as text, f as truthy, g as tryResolvePath } from './expr-BfXDrs4H.js';
8
8
  import { P as PauseAwaiting } from './pause-9iT4tCEV.js';
9
9
  export { L as LEGACY_PAUSE_PREFIXES, a as PAUSE_PREFIX, b as PauseSignal, d as decodePause, e as encodePause, i as isPause, p as pauseForHuman } from './pause-9iT4tCEV.js';
package/dist/engine.js CHANGED
@@ -1,7 +1,7 @@
1
- export { runCohort } from './chunk-CQAMEPPI.js';
2
- export { checkGraphConnectivity, mayFloat } from './chunk-G5RAGPVD.js';
3
- import { runFlow, decodePause, getNodeKind, resolveNodePorts } from './chunk-EEIWAIXW.js';
4
- export { LEGACY_PAUSE_PREFIXES, PAUSE_PREFIX, RunIdentity, UnresolvedPathError, applyKindSchemaOverlay, clearKindSchemaOverlays, decodePause, defaultConfigFor, encodePause, escapeSegment, evaluateConfig, evaluateExpression, getNodeKind, isPause, kindIds, listNodeKinds, pauseForHuman, registerNodeKind, resolveKindId, resolvePath, runFlow, text, truthy, tryResolvePath, unregisterNodeKind, validateConfig } from './chunk-EEIWAIXW.js';
1
+ export { runCohort } from './chunk-R4G7SPLD.js';
2
+ export { checkGraphConnectivity, mayFloat } from './chunk-KMONOPAK.js';
3
+ import { runFlow, decodePause, getNodeKind, resolveNodePorts } from './chunk-OZHBAXMA.js';
4
+ export { LEGACY_PAUSE_PREFIXES, PAUSE_PREFIX, RunIdentity, UnresolvedPathError, applyKindSchemaOverlay, clearKindSchemaOverlays, decodePause, defaultConfigFor, encodePause, escapeSegment, evaluateConfig, evaluateExpression, getNodeKind, isPause, kindIds, listNodeKinds, pauseForHuman, registerNodeKind, resolveKindId, resolvePath, runFlow, text, truthy, tryResolvePath, unregisterNodeKind, validateConfig } from './chunk-OZHBAXMA.js';
5
5
  import { isResolutionFailure, registerLlmClient, registerWorkflowResolver } from './chunk-2QXKDLGT.js';
6
6
 
7
7
  // src/marketplace/manifest.ts
@@ -1,8 +1,8 @@
1
- import { H as HumanFieldRenderers, C as ConfigFieldRenderFn } from '../HumanPrompt-xYPCL2jO.cjs';
1
+ import { H as HumanFieldRenderers, C as ConfigFieldRenderFn } from '../HumanPrompt-Sl1wD9Bx.cjs';
2
2
  import 'react';
3
- import '../types-B-Syk9-M.cjs';
3
+ import '../types-E48Dpesq.cjs';
4
4
  import '@xyflow/react';
5
- import '../types-BdkXxjFh.cjs';
5
+ import '../types-B1jftRa9.cjs';
6
6
  import '../pause-9iT4tCEV.cjs';
7
7
 
8
8
  /**
@@ -1,8 +1,8 @@
1
- import { H as HumanFieldRenderers, C as ConfigFieldRenderFn } from '../HumanPrompt-Ge_ccCgS.js';
1
+ import { H as HumanFieldRenderers, C as ConfigFieldRenderFn } from '../HumanPrompt-_SKIdSpx.js';
2
2
  import 'react';
3
- import '../types-B-Syk9-M.js';
3
+ import '../types-E48Dpesq.js';
4
4
  import '@xyflow/react';
5
- import '../types-CbekKMMO.js';
5
+ import '../types-D5SBBpbe.js';
6
6
  import '../pause-9iT4tCEV.js';
7
7
 
8
8
  /**
package/dist/index.cjs CHANGED
@@ -2532,6 +2532,9 @@ async function runFlow(graph, executors, onEvent = () => {
2532
2532
  executors,
2533
2533
  depth,
2534
2534
  run,
2535
+ // Structural executors derive a nested lane from the graph they are
2536
+ // in; `for_each`'s `item` port cannot be implemented without it.
2537
+ graph,
2535
2538
  terminal: terminalAccessorFor(node)
2536
2539
  })
2537
2540
  );
@@ -2986,6 +2989,7 @@ var branchExecutor = (ctx) => {
2986
2989
  const inputs = ctx.inputs;
2987
2990
  let taken;
2988
2991
  const raw = config.condition;
2992
+ validateRoutingExpression(ctx, raw, "branch", "condition");
2989
2993
  if (typeof raw === "string" && raw.trim() !== "") {
2990
2994
  taken = truthy(resolve(raw, inputs));
2991
2995
  warnIfUnresolved(ctx, raw, taken ? "true" : "false");
@@ -3040,14 +3044,102 @@ var mergeExecutor = (ctx) => {
3040
3044
  }
3041
3045
  return merged;
3042
3046
  };
3043
- var forEachExecutor = (ctx) => {
3044
- const source = resolve(configOf(ctx.node).source, ctx.inputs);
3047
+ var FOR_EACH_DEFAULT_MAX_ITEMS = 1e3;
3048
+ var FOR_EACH_HARD_MAX_ITEMS = 1e4;
3049
+ function reachable(adjacency, starts) {
3050
+ const seen = /* @__PURE__ */ new Set();
3051
+ const queue = [...starts];
3052
+ for (let cursor = 0; cursor < queue.length; cursor += 1) {
3053
+ const id2 = queue[cursor];
3054
+ if (seen.has(id2)) continue;
3055
+ seen.add(id2);
3056
+ for (const target of adjacency.get(id2) ?? []) queue.push(target);
3057
+ }
3058
+ return seen;
3059
+ }
3060
+ function forEachLane(graph, nodeId) {
3061
+ const handleOf = (edge) => edge.sourceHandle ?? "out";
3062
+ const itemEdges = graph.edges.filter((e) => e.source === nodeId && handleOf(e) === "item");
3063
+ if (itemEdges.length === 0) return null;
3064
+ const adjacency = /* @__PURE__ */ new Map();
3065
+ for (const edge of graph.edges) {
3066
+ const list = adjacency.get(edge.source) ?? [];
3067
+ list.push(edge.target);
3068
+ adjacency.set(edge.source, list);
3069
+ }
3070
+ const done = reachable(
3071
+ adjacency,
3072
+ graph.edges.filter((e) => e.source === nodeId && handleOf(e) === "done").map((e) => e.target)
3073
+ );
3074
+ const body = new Set(
3075
+ [...reachable(adjacency, itemEdges.map((e) => e.target))].filter(
3076
+ (id2) => !done.has(id2) && id2 !== nodeId
3077
+ )
3078
+ );
3079
+ return {
3080
+ graph: {
3081
+ nodes: graph.nodes.filter((n) => body.has(n.id)),
3082
+ edges: graph.edges.filter((e) => body.has(e.source) && body.has(e.target)),
3083
+ ...graph.inputs === void 0 ? {} : { inputs: graph.inputs }
3084
+ },
3085
+ entries: itemEdges.filter((e) => body.has(e.target))
3086
+ };
3087
+ }
3088
+ var forEachExecutor = async (ctx) => {
3089
+ const config = configOf(ctx.node);
3090
+ const source = resolve(config.source, ctx.inputs);
3045
3091
  let items;
3046
3092
  if (Array.isArray(source)) items = source;
3047
3093
  else if (source === null || source === void 0) items = [];
3048
3094
  else if (typeof source === "object") items = Object.values(source);
3049
3095
  else items = [source];
3050
- return { items, count: items.length };
3096
+ const lane = ctx.graph ? forEachLane(ctx.graph, ctx.node.id) : null;
3097
+ if (lane === null || config.mode === "collect") {
3098
+ return { items, count: items.length };
3099
+ }
3100
+ if (lane.graph.nodes.length === 0) {
3101
+ ctx.abort(`for_each "${ctx.node.id}" has an item edge but its derived lane is empty`);
3102
+ }
3103
+ const maxItems = Number(config.maxItems ?? FOR_EACH_DEFAULT_MAX_ITEMS);
3104
+ if (!Number.isFinite(maxItems) || maxItems < 1 || maxItems > FOR_EACH_HARD_MAX_ITEMS) {
3105
+ ctx.abort(`for_each "${ctx.node.id}" maxItems must be between 1 and ${FOR_EACH_HARD_MAX_ITEMS}`);
3106
+ }
3107
+ if (items.length > maxItems) {
3108
+ ctx.abort(
3109
+ `for_each "${ctx.node.id}" resolved ${items.length} items exceeds its maxItems cap of ${maxItems}`
3110
+ );
3111
+ }
3112
+ const results = [];
3113
+ const failures = [];
3114
+ for (const [index2, item] of items.entries()) {
3115
+ const initialInputs = {};
3116
+ for (const edge of lane.entries) {
3117
+ initialInputs[edge.target] = {
3118
+ ...initialInputs[edge.target] ?? {},
3119
+ [edge.targetHandle ?? "in"]: item
3120
+ };
3121
+ }
3122
+ const nested = await runFlow(lane.graph, { ...ctx.executors ?? {} }, () => {
3123
+ }, {
3124
+ initialInputs,
3125
+ depth: (ctx.depth ?? 0) + 1,
3126
+ // The index rides on the identity, so a node in iteration 3 cannot share
3127
+ // an idempotency key with the same node in iteration 4.
3128
+ run: ctx.run?.descend(ctx.node.id, index2)
3129
+ });
3130
+ if (!nested.ok) {
3131
+ const reason = nested.error ?? "unknown error";
3132
+ if (decodePause(reason)) ctx.abort(reason);
3133
+ results.push(null);
3134
+ failures.push({ index: index2, item, error: reason });
3135
+ continue;
3136
+ }
3137
+ results.push(nested.outputs);
3138
+ }
3139
+ return {
3140
+ __port: "done",
3141
+ value: { items, results, failures, count: items.length }
3142
+ };
3051
3143
  };
3052
3144
  var manualTriggerExecutor = (ctx) => ctx.inputs;
3053
3145
  var outputExecutor = (ctx) => {
@@ -3067,6 +3159,7 @@ var switchCaseExecutor = (ctx) => {
3067
3159
  const config = configOf(ctx.node);
3068
3160
  const inputs = ctx.inputs;
3069
3161
  const expression = config.value;
3162
+ validateRoutingExpression(ctx, expression, "switch_case", "value");
3070
3163
  const value = text(resolve(expression, inputs));
3071
3164
  const cases = config.cases ?? {};
3072
3165
  const matched = Object.prototype.hasOwnProperty.call(cases, value);
@@ -3074,6 +3167,21 @@ var switchCaseExecutor = (ctx) => {
3074
3167
  warnIfUnresolved(ctx, expression, port, "value");
3075
3168
  return { __port: port, value: inputs.in ?? inputs };
3076
3169
  };
3170
+ function validateRoutingExpression(ctx, value, kind, field) {
3171
+ if (typeof value !== "string" || value.trim() === "") return;
3172
+ const bare = value.trim();
3173
+ const subject = `${kind} "${ctx.node.id}" ${field} "${bare}"`;
3174
+ if (!value.includes("{{")) {
3175
+ ctx.abort(`${subject} is not an expression -- wrap it: {{ ${bare} }}`);
3176
+ }
3177
+ let open = 0;
3178
+ for (const [delimiter] of value.matchAll(/\{\{|\}\}/g)) {
3179
+ open = delimiter === "{{" ? open + 1 : Math.max(0, open - 1);
3180
+ }
3181
+ if (open > 0) {
3182
+ ctx.abort(`${subject} has an unclosed expression -- close every {{ with }}`);
3183
+ }
3184
+ }
3077
3185
 
3078
3186
  // src/registry/terminal-nodes.ts
3079
3187
  var DEFAULT_TIMEOUT_MS = 12e4;
@@ -3565,7 +3673,7 @@ var KINDS = [
3565
3673
  key: "condition",
3566
3674
  label: "Raw expression (advanced)",
3567
3675
  example: "{{ $json.active && $json.score > 10 }}",
3568
- description: "Escape hatch for logic the builder can't express. Overrides the conditions above when set."
3676
+ description: "Escape hatch for logic the builder can't express. Overrides the conditions above when set. String values must be {{ }}-wrapped expressions; bare strings and unclosed templates abort the run."
3569
3677
  }
3570
3678
  ]
3571
3679
  },
@@ -3585,7 +3693,7 @@ var KINDS = [
3585
3693
  // moves the ports on the canvas and the ports the runtime activates.
3586
3694
  outputs: (config) => casePorts(config?.cases),
3587
3695
  configSchema: [
3588
- { type: "expression", key: "value", label: "Switch on", example: "{{ $json.kind }}", required: true },
3696
+ { type: "expression", key: "value", label: "Switch on", description: "String values must be {{ }}-wrapped expressions; bare strings and unclosed templates abort the run.", example: "{{ $json.kind }}", required: true },
3589
3697
  {
3590
3698
  type: "keyvalue",
3591
3699
  key: "cases",
@@ -3604,6 +3712,15 @@ var KINDS = [
3604
3712
  name: "@particle-academy/for_each",
3605
3713
  outputShape: [
3606
3714
  { path: "items", type: "array" },
3715
+ // `results` and `failures` are the WIRED shape -- what a lane produces
3716
+ // when the `item` port is connected. Declared unconditionally because a
3717
+ // declaration describes the kind, not one graph's use of it, and because
3718
+ // the shared table requires them: fancy-conformance 0.30.0 added
3719
+ // `results` and 0.31.0 added `failures`, both BREAKING, both so that a
3720
+ // runtime which has not implemented iteration fails here rather than
3721
+ // reporting surface parity it does not have.
3722
+ { path: "results", type: "array" },
3723
+ { path: "failures", type: "array" },
3607
3724
  { path: "count", type: "number" }
3608
3725
  ],
3609
3726
  aliases: ["for_each", "@fancy/for_each"],
@@ -3612,7 +3729,7 @@ var KINDS = [
3612
3729
  executor: forEachExecutor,
3613
3730
  category: "logic",
3614
3731
  label: "For Each",
3615
- description: "Publishes the resolved list and its size on BOTH `item` and `done`. Fan-out as DATA, not as jobs -- nothing runs per item.",
3732
+ description: "Wire `item` to a lane and it runs once per item, aggregating `results` and `failures` on `done`. Leave `item` unwired (or set `mode: collect`) and it just publishes the list and its size -- one node, one checkpoint.",
3616
3733
  icon: "\u21BB",
3617
3734
  inputs: [{ id: "in" }],
3618
3735
  outputs: [{ id: "item", label: "item" }, { id: "done", label: "done" }],