@particle-academy/fancy-flow 0.71.0 → 0.72.1

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 (53) hide show
  1. package/dist/{chunk-3L47KJ6Q.js → chunk-3RXQGYGH.js} +3 -3
  2. package/dist/{chunk-3L47KJ6Q.js.map → chunk-3RXQGYGH.js.map} +1 -1
  3. package/dist/{chunk-PNTDTSMR.js → chunk-7DJ56PUA.js} +4 -4
  4. package/dist/{chunk-PNTDTSMR.js.map → chunk-7DJ56PUA.js.map} +1 -1
  5. package/dist/{chunk-EVGSL2EM.js → chunk-EX34GCKK.js} +162 -151
  6. package/dist/chunk-EX34GCKK.js.map +1 -0
  7. package/dist/{chunk-XI4AZD2S.js → chunk-GQ5LEA7Q.js} +3 -3
  8. package/dist/{chunk-XI4AZD2S.js.map → chunk-GQ5LEA7Q.js.map} +1 -1
  9. package/dist/{chunk-D2OBU3CE.js → chunk-K7KO3UJ3.js} +3 -3
  10. package/dist/{chunk-D2OBU3CE.js.map → chunk-K7KO3UJ3.js.map} +1 -1
  11. package/dist/{chunk-FPWJYSXX.js → chunk-LYYQO7EY.js} +4 -4
  12. package/dist/{chunk-FPWJYSXX.js.map → chunk-LYYQO7EY.js.map} +1 -1
  13. package/dist/{chunk-KQCX6KN7.js → chunk-VAF2SWTC.js} +3 -3
  14. package/dist/{chunk-KQCX6KN7.js.map → chunk-VAF2SWTC.js.map} +1 -1
  15. package/dist/durable/index.d.cts +90 -16
  16. package/dist/durable/index.d.ts +90 -16
  17. package/dist/durable.cjs +160 -83
  18. package/dist/durable.cjs.map +1 -1
  19. package/dist/durable.js +79 -14
  20. package/dist/durable.js.map +1 -1
  21. package/dist/engine.cjs +82 -71
  22. package/dist/engine.cjs.map +1 -1
  23. package/dist/engine.d.cts +2 -2
  24. package/dist/engine.d.ts +2 -2
  25. package/dist/engine.js +4 -4
  26. package/dist/index.cjs +128 -117
  27. package/dist/index.cjs.map +1 -1
  28. package/dist/index.d.cts +2 -2
  29. package/dist/index.d.ts +2 -2
  30. package/dist/index.js +12 -12
  31. package/dist/registry.cjs +82 -71
  32. package/dist/registry.cjs.map +1 -1
  33. package/dist/registry.js +2 -2
  34. package/dist/{run-cohort-DL9JzzPU.d.cts → run-cohort-DNpPa0mq.d.cts} +1 -1
  35. package/dist/{run-cohort-CjUBWg6X.d.ts → run-cohort-rgT_FX3V.d.ts} +1 -1
  36. package/dist/{run-flow-B_8hgO5_.d.cts → run-flow-BLP9rXfO.d.ts} +22 -1
  37. package/dist/{run-flow-CxEGBOxd.d.ts → run-flow-pW0PllaZ.d.cts} +22 -1
  38. package/dist/runtime/index.d.cts +3 -3
  39. package/dist/runtime/index.d.ts +3 -3
  40. package/dist/runtime.cjs +82 -71
  41. package/dist/runtime.cjs.map +1 -1
  42. package/dist/runtime.js +3 -3
  43. package/dist/schema.cjs +82 -71
  44. package/dist/schema.cjs.map +1 -1
  45. package/dist/schema.js +3 -3
  46. package/dist/screens.cjs +82 -71
  47. package/dist/screens.cjs.map +1 -1
  48. package/dist/screens.js +4 -4
  49. package/dist/ux.cjs +82 -71
  50. package/dist/ux.cjs.map +1 -1
  51. package/dist/ux.js +1 -1
  52. package/package.json +1 -1
  53. package/dist/chunk-EVGSL2EM.js.map +0 -1
package/dist/index.d.cts CHANGED
@@ -16,8 +16,8 @@ export { AutoLayoutDirection } from './layout/index.cjs';
16
16
  export { F as FlowNodeStatus, a as FlowViewer, b as FlowViewerClassNames, c as FlowViewerProps } from './FlowViewer-CoAJ3o52.cjs';
17
17
  import { FlowRunFeedEntry } from './runtime/index.cjs';
18
18
  export { HistoryController, UseFlowHistoryReturn, UseFlowRunOptions, UseFlowRunReturn, UseFlowStateReturn, applyOutputsToNodes, applyStatusesToNodes, createHistory, useFlowHistory, useFlowRun, useFlowState } from './runtime/index.cjs';
19
- export { R as RunOptions, a as RunResult, r as runFlow } from './run-flow-B_8hgO5_.cjs';
20
- export { C as CohortGuard, a as CohortOptions, b as CohortPolicy, c as CohortResult, r as runCohort } from './run-cohort-DL9JzzPU.cjs';
19
+ export { R as RunOptions, a as RunResult, r as runFlow } from './run-flow-pW0PllaZ.cjs';
20
+ export { C as CohortGuard, a as CohortOptions, b as CohortPolicy, c as CohortResult, r as runCohort } from './run-cohort-DNpPa0mq.cjs';
21
21
  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';
22
22
  export { K as KindSchemaOverlay, a as KindSchemaOverlayResult, N as NodeKindPresentation, b as applyKindSchemaOverlay, f as categoryAccent, c as clearKindSchemaOverlays, h as clearNodeKindOverrides, d as defaultConfigFor, i as ensureBuiltinKinds, g as getNodeKind, l as listNodeKinds, o as onNodeKindsChanged, j as overrideNodeKind, r as registerNodeKind, u as unregisterNodeKind, v as validateConfig } from './registry-B6SiTaLZ.cjs';
23
23
  export { L as LEGACY_PAUSE_PREFIXES, a as PAUSE_PREFIX, P as PauseAwaiting, b as PauseSignal, d as decodePause, e as encodePause, i as isPause, p as pauseForHuman } from './pause-9iT4tCEV.cjs';
package/dist/index.d.ts CHANGED
@@ -16,8 +16,8 @@ export { AutoLayoutDirection } from './layout/index.js';
16
16
  export { F as FlowNodeStatus, a as FlowViewer, b as FlowViewerClassNames, c as FlowViewerProps } from './FlowViewer-BCc_BvuD.js';
17
17
  import { FlowRunFeedEntry } from './runtime/index.js';
18
18
  export { HistoryController, UseFlowHistoryReturn, UseFlowRunOptions, UseFlowRunReturn, UseFlowStateReturn, applyOutputsToNodes, applyStatusesToNodes, createHistory, useFlowHistory, useFlowRun, useFlowState } from './runtime/index.js';
19
- export { R as RunOptions, a as RunResult, r as runFlow } from './run-flow-CxEGBOxd.js';
20
- export { C as CohortGuard, a as CohortOptions, b as CohortPolicy, c as CohortResult, r as runCohort } from './run-cohort-CjUBWg6X.js';
19
+ export { R as RunOptions, a as RunResult, r as runFlow } from './run-flow-BLP9rXfO.js';
20
+ export { C as CohortGuard, a as CohortOptions, b as CohortPolicy, c as CohortResult, r as runCohort } from './run-cohort-rgT_FX3V.js';
21
21
  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';
22
22
  export { K as KindSchemaOverlay, a as KindSchemaOverlayResult, N as NodeKindPresentation, b as applyKindSchemaOverlay, f as categoryAccent, c as clearKindSchemaOverlays, h as clearNodeKindOverrides, d as defaultConfigFor, i as ensureBuiltinKinds, g as getNodeKind, l as listNodeKinds, o as onNodeKindsChanged, j as overrideNodeKind, r as registerNodeKind, u as unregisterNodeKind, v as validateConfig } from './registry-JUcN_xWK.js';
23
23
  export { L as LEGACY_PAUSE_PREFIXES, a as PAUSE_PREFIX, P as PauseAwaiting, b as PauseSignal, d as decodePause, e as encodePause, i as isPause, p as pauseForHuman } from './pause-9iT4tCEV.js';
package/dist/index.js CHANGED
@@ -1,19 +1,19 @@
1
- import { removeNodes, removeEdges, cloneSubgraph, alignNodes, distributeNodes, reconnectEdge, assignToLane, removeFromLane, duplicateNode, setEdgeLabel, FlowCanvas } from './chunk-FPWJYSXX.js';
2
- export { ActionNode, DecisionNode, FlowCanvas, FlowViewer, NodeShell, OutputNode, SubgraphNode, TriggerNode, alignNodes, cloneSubgraph, defaultNodeTypes, distributeNodes, reconnectEdge } from './chunk-FPWJYSXX.js';
3
- import { useFlowState, useFlowRun, useFlowHistory, applyOutputsToNodes, applyStatusesToNodes } from './chunk-XI4AZD2S.js';
4
- export { applyOutputsToNodes, applyStatusesToNodes, createHistory, useFlowHistory, useFlowRun, useFlowState } from './chunk-XI4AZD2S.js';
5
- export { runCohort } from './chunk-D2OBU3CE.js';
6
- import { buildNodeTypes, FlowEditorProvider, registerBuiltinKinds } from './chunk-3L47KJ6Q.js';
7
- export { ANY_PORT_TYPE, BUILTIN_KINDS, LaneNode, NoteNode, RegistryNode, buildNodeTypes, createConnectionValidator, defaultPortCompatibility, registerBuiltinKinds, useFlowEditor, useFlowEditorOptional } from './chunk-3L47KJ6Q.js';
1
+ import { removeNodes, removeEdges, cloneSubgraph, alignNodes, distributeNodes, reconnectEdge, assignToLane, removeFromLane, duplicateNode, setEdgeLabel, FlowCanvas } from './chunk-LYYQO7EY.js';
2
+ export { ActionNode, DecisionNode, FlowCanvas, FlowViewer, NodeShell, OutputNode, SubgraphNode, TriggerNode, alignNodes, cloneSubgraph, defaultNodeTypes, distributeNodes, reconnectEdge } from './chunk-LYYQO7EY.js';
3
+ import { useFlowState, useFlowRun, useFlowHistory, applyOutputsToNodes, applyStatusesToNodes } from './chunk-GQ5LEA7Q.js';
4
+ export { applyOutputsToNodes, applyStatusesToNodes, createHistory, useFlowHistory, useFlowRun, useFlowState } from './chunk-GQ5LEA7Q.js';
5
+ export { runCohort } from './chunk-K7KO3UJ3.js';
6
+ import { buildNodeTypes, FlowEditorProvider, registerBuiltinKinds } from './chunk-3RXQGYGH.js';
7
+ export { ANY_PORT_TYPE, BUILTIN_KINDS, LaneNode, NoteNode, RegistryNode, buildNodeTypes, createConnectionValidator, defaultPortCompatibility, registerBuiltinKinds, useFlowEditor, useFlowEditorOptional } from './chunk-3RXQGYGH.js';
8
8
  import { getRichInputAdapter } from './chunk-F5RPRB7A.js';
9
9
  export { RichInputPreview, getRichInputAdapter, isRichInputEnabled, onRichInputAdapterChanged, registerRichInputAdapter } from './chunk-F5RPRB7A.js';
10
10
  import { ReactFlowProvider, useReactFlow, addEdge, applyEdgeChanges, applyNodeChanges, Position, Handle } from './chunk-OWENS2H5.js';
11
11
  export { LEGACY_PAUSE_PREFIXES, PAUSE_PREFIX, decodePause, encodePause, isPause, pauseForHuman } from './chunk-UEOE6B52.js';
12
- import { workflowToBlob, importWorkflow, exportWorkflow } from './chunk-PNTDTSMR.js';
13
- export { WORKFLOW_SCHEMA_URL, WORKFLOW_SCHEMA_VERSION, exportWorkflow, importWorkflow, migrateSchema, workflowToBlob } from './chunk-PNTDTSMR.js';
14
- import './chunk-KQCX6KN7.js';
15
- import { humanInputFields, onNodeKindsChanged, listNodeKinds, getNodeKind, defaultConfigFor, ensureBuiltinKinds, validateConfig, categoryAccent, availableVariables, baseVariables, describeExpressionGrammar } from './chunk-EVGSL2EM.js';
16
- export { UnresolvedPathError, applyKindSchemaOverlay, availableVariables, baseVariables, categoryAccent, clearKindSchemaOverlays, clearNodeKindOverrides, defaultConfigFor, describeExpressionGrammar, ensureBuiltinKinds, evaluateConfig, evaluateExpression, getNodeKind, listNodeKinds, onNodeKindsChanged, outputFieldsFor, overrideNodeKind, registerNodeKind, resolveNodePorts, resolvePath, resolvePortSpec, runFlow, text, truthy, tryResolvePath, unregisterNodeKind, validateConfig } from './chunk-EVGSL2EM.js';
12
+ import { workflowToBlob, importWorkflow, exportWorkflow } from './chunk-7DJ56PUA.js';
13
+ export { WORKFLOW_SCHEMA_URL, WORKFLOW_SCHEMA_VERSION, exportWorkflow, importWorkflow, migrateSchema, workflowToBlob } from './chunk-7DJ56PUA.js';
14
+ import './chunk-VAF2SWTC.js';
15
+ import { humanInputFields, onNodeKindsChanged, listNodeKinds, getNodeKind, defaultConfigFor, ensureBuiltinKinds, validateConfig, categoryAccent, availableVariables, baseVariables, describeExpressionGrammar } from './chunk-EX34GCKK.js';
16
+ export { UnresolvedPathError, applyKindSchemaOverlay, availableVariables, baseVariables, categoryAccent, clearKindSchemaOverlays, clearNodeKindOverrides, defaultConfigFor, describeExpressionGrammar, ensureBuiltinKinds, evaluateConfig, evaluateExpression, getNodeKind, listNodeKinds, onNodeKindsChanged, outputFieldsFor, overrideNodeKind, registerNodeKind, resolveNodePorts, resolvePath, resolvePortSpec, runFlow, text, truthy, tryResolvePath, unregisterNodeKind, validateConfig } from './chunk-EX34GCKK.js';
17
17
  import './chunk-2QXKDLGT.js';
18
18
  import { forwardRef, useState, useMemo, useEffect, useCallback, useRef, useImperativeHandle, useId, memo } from 'react';
19
19
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
package/dist/registry.cjs CHANGED
@@ -187,24 +187,6 @@ var llmRouterExecutor = async (ctx) => {
187
187
  return { __port: port, value: { route: port, reason, input: ctx.inputs } };
188
188
  };
189
189
 
190
- // src/expressions/variables.ts
191
- function outputFieldsFor(node) {
192
- const kindName = node.data?.kind ?? node.type;
193
- if (!kindName) return [];
194
- const kind = getNodeKind(kindName);
195
- const shape = kind?.outputShape;
196
- if (!shape) return [];
197
- if (typeof shape === "function") {
198
- try {
199
- const config = node.data?.config ?? {};
200
- return shape(config) ?? [];
201
- } catch {
202
- return [];
203
- }
204
- }
205
- return shape;
206
- }
207
-
208
190
  // src/runtime/run-identity.ts
209
191
  function escapeSegment(value) {
210
192
  return value.replace(/%/g, "%25").replace(/\//g, "%2F").replace(/#/g, "%23");
@@ -558,6 +540,81 @@ function specForLane(lane) {
558
540
  };
559
541
  }
560
542
 
543
+ // src/expressions/variables.ts
544
+ function outputFieldsFor(node) {
545
+ const kindName = node.data?.kind ?? node.type;
546
+ if (!kindName) return [];
547
+ const kind = getNodeKind(kindName);
548
+ const shape = kind?.outputShape;
549
+ if (!shape) return [];
550
+ if (typeof shape === "function") {
551
+ try {
552
+ const config = node.data?.config ?? {};
553
+ return shape(config) ?? [];
554
+ } catch {
555
+ return [];
556
+ }
557
+ }
558
+ return shape;
559
+ }
560
+
561
+ // src/runtime/undelivered-edges.ts
562
+ function undeliveredEdgeWarnings(target, incoming, portValues, completed, nodesById) {
563
+ const events = [];
564
+ for (const e of incoming) {
565
+ const handle = e.sourceHandle ?? "out";
566
+ if (portValues.has(`${e.source}:${handle}`) || !completed.has(e.source)) continue;
567
+ if (possiblePortIds(nodesById.get(e.source)).includes(handle)) continue;
568
+ events.push({
569
+ type: "log",
570
+ nodeId: target.id,
571
+ level: "warn",
572
+ message: undeliveredEdgeMessage(e, target, portValues, nodesById),
573
+ detail: { edge: e.id, source: e.source, sourceHandle: handle }
574
+ });
575
+ }
576
+ return events;
577
+ }
578
+ function possiblePortIds(node) {
579
+ if (!node) return ["out"];
580
+ const own = node.data?.outputs;
581
+ if (Array.isArray(own)) return own.map((p) => p.id);
582
+ const kind = getNodeKind(node.data?.kind ?? node.type ?? "");
583
+ if (!kind) return ["out"];
584
+ const config = nodeConfig(node);
585
+ const representative = typeof kind.outputs === "function" && !configDeclaresPorts(kind.name, config);
586
+ const ids = (resolvePortSpec(kind.outputs, representative ? defaultConfigFor(kind) : config) ?? []).map((p) => p.id);
587
+ return ids.length > 0 ? ids : ["out"];
588
+ }
589
+ function configDeclaresPorts(kindName, config) {
590
+ const bare = kindName.startsWith("@") ? kindName.slice(kindName.lastIndexOf("/") + 1) : kindName;
591
+ if (bare === "switch_case") {
592
+ const cases = config.cases;
593
+ return !!cases && typeof cases === "object" && Object.values(cases).some((port) => typeof port === "string" && port !== "");
594
+ }
595
+ if (bare === "llm_router") {
596
+ const routes = config.routes;
597
+ return Array.isArray(routes) && routes.some((route) => route?.port != null && route.port !== "");
598
+ }
599
+ return true;
600
+ }
601
+ function undeliveredEdgeMessage(edge, target, portValues, nodesById) {
602
+ const handle = edge.sourceHandle ?? "out";
603
+ const prefix = `${edge.source}:`;
604
+ const available = [];
605
+ for (const key of portValues.keys()) {
606
+ if (key.startsWith(prefix)) available.push(key.slice(prefix.length));
607
+ }
608
+ let message = `Edge ${edge.id} reads port "${handle}" from node ${edge.source}, which never publishes it \u2014 nothing would reach ${target.id} at run time.`;
609
+ if (available.length > 0) message += ` Available: ${available.join(", ")}.`;
610
+ const source = nodesById.get(edge.source);
611
+ if (source && outputFieldsFor(source).some((field) => field?.path === handle)) {
612
+ message += ` Note: "${handle}" is a FIELD this node emits, not a port \u2014 read it downstream as {{ in.${handle} }} rather than naming it as a source handle.`;
613
+ }
614
+ if (edge.sourceHandle != null) message += " Leave sourceHandle off to read the node's output.";
615
+ return message;
616
+ }
617
+
561
618
  // src/runtime/workflow-props.ts
562
619
  function typeOf(value) {
563
620
  if (value === null) return "null";
@@ -616,7 +673,7 @@ function resolveWorkflowProps(declared, passed) {
616
673
  // src/runtime/run-flow.ts
617
674
  async function runFlow(graph, executors, onEvent = () => {
618
675
  }, options = {}) {
619
- const { signal, initialInputs = {}, timeoutMs, depth = 0, resumeOutputs = {}, entryNodes } = options;
676
+ const { signal, initialInputs = {}, timeoutMs, depth = 0, resumeOutputs = {}, entryNodes, nodeExecutors } = options;
620
677
  const run = options.run === void 0 ? void 0 : RunIdentity.from(options.run);
621
678
  const outputs = {};
622
679
  const portValues = /* @__PURE__ */ new Map();
@@ -679,17 +736,8 @@ async function runFlow(graph, executors, onEvent = () => {
679
736
  onEvent({ type: "node-status", nodeId: node.id, status: "idle", text: "skipped" });
680
737
  continue;
681
738
  }
682
- for (const e of incoming) {
683
- const handle = e.sourceHandle ?? "out";
684
- if (portValues.has(`${e.source}:${handle}`) || !completed.has(e.source)) continue;
685
- if (possiblePortIds(nodesById.get(e.source)).includes(handle)) continue;
686
- onEvent({
687
- type: "log",
688
- nodeId: node.id,
689
- level: "warn",
690
- message: undeliveredEdgeMessage(e, node, portValues, nodesById),
691
- detail: { edge: e.id, source: e.source, sourceHandle: handle }
692
- });
739
+ for (const warning of undeliveredEdgeWarnings(node, incoming, portValues, completed, nodesById)) {
740
+ onEvent(warning);
693
741
  }
694
742
  if (incoming.length > 0) {
695
743
  const anyActive = incoming.some((e) => portValues.has(`${e.source}:${e.sourceHandle ?? "out"}`));
@@ -713,7 +761,7 @@ async function runFlow(graph, executors, onEvent = () => {
713
761
  onEvent({ type: "node-status", nodeId: node.id, status: "running" });
714
762
  announce(onEvent, node, "start");
715
763
  const inputs = collectInputs(node, incoming, portValues, initialInputs, props, declaresProps);
716
- const exec = pickExecutor(executors, node);
764
+ const exec = pickExecutor(executors, node, nodeExecutors);
717
765
  if (!exec) {
718
766
  const tried = executorLookupIds(node);
719
767
  const msg = `No executor registered for kind=${node.type} \u2014 tried ${tried.map((id2) => `"${id2}"`).join(", ")}. Key your registry by one of those.`;
@@ -833,7 +881,9 @@ function executorLookupIds(node) {
833
881
  ids.push("*");
834
882
  return [...new Set(ids)];
835
883
  }
836
- function pickExecutor(executors, node) {
884
+ function pickExecutor(executors, node, nodeExecutors) {
885
+ const pinned = nodeExecutors && Object.prototype.hasOwnProperty.call(nodeExecutors, node.id) ? nodeExecutors[node.id] : void 0;
886
+ if (pinned) return pinned;
837
887
  for (const id2 of executorLookupIds(node)) {
838
888
  if (executors[id2]) return executors[id2];
839
889
  }
@@ -853,45 +903,6 @@ function activatedPorts(node, result) {
853
903
  const declared = resolveNodePorts(node, kind).outputs?.map((p) => p.id);
854
904
  return { ports: declared?.length ? declared : ["out"], value: result };
855
905
  }
856
- function possiblePortIds(node) {
857
- if (!node) return ["out"];
858
- const own = node.data?.outputs;
859
- if (Array.isArray(own)) return own.map((p) => p.id);
860
- const kind = getNodeKind(node.data?.kind ?? node.type ?? "");
861
- if (!kind) return ["out"];
862
- const config = nodeConfig(node);
863
- const representative = typeof kind.outputs === "function" && !configDeclaresPorts(kind.name, config);
864
- const ids = (resolvePortSpec(kind.outputs, representative ? defaultConfigFor(kind) : config) ?? []).map((p) => p.id);
865
- return ids.length > 0 ? ids : ["out"];
866
- }
867
- function configDeclaresPorts(kindName, config) {
868
- const bare = kindName.startsWith("@") ? kindName.slice(kindName.lastIndexOf("/") + 1) : kindName;
869
- if (bare === "switch_case") {
870
- const cases = config.cases;
871
- return !!cases && typeof cases === "object" && Object.values(cases).some((port) => typeof port === "string" && port !== "");
872
- }
873
- if (bare === "llm_router") {
874
- const routes = config.routes;
875
- return Array.isArray(routes) && routes.some((route) => route?.port != null && route.port !== "");
876
- }
877
- return true;
878
- }
879
- function undeliveredEdgeMessage(edge, target, portValues, nodesById) {
880
- const handle = edge.sourceHandle ?? "out";
881
- const prefix = `${edge.source}:`;
882
- const available = [];
883
- for (const key of portValues.keys()) {
884
- if (key.startsWith(prefix)) available.push(key.slice(prefix.length));
885
- }
886
- let message = `Edge ${edge.id} reads port "${handle}" from node ${edge.source}, which never publishes it \u2014 nothing would reach ${target.id} at run time.`;
887
- if (available.length > 0) message += ` Available: ${available.join(", ")}.`;
888
- const source = nodesById.get(edge.source);
889
- if (source && outputFieldsFor(source).some((field) => field?.path === handle)) {
890
- message += ` Note: "${handle}" is a FIELD this node emits, not a port \u2014 read it downstream as {{ in.${handle} }} rather than naming it as a source handle.`;
891
- }
892
- if (edge.sourceHandle != null) message += " Leave sourceHandle off to read the node's output.";
893
- return message;
894
- }
895
906
  function announce(onEvent, node, phase) {
896
907
  const data = node.data;
897
908
  const raw = phase === "start" ? data?.startingMsg : data?.stoppingMsg;