@particle-academy/fancy-flow 0.77.1 → 0.78.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-G5RAGPVD.js → chunk-3EDB6ELL.js} +3 -3
  6. package/dist/{chunk-G5RAGPVD.js.map → chunk-3EDB6ELL.js.map} +1 -1
  7. package/dist/{chunk-CQAMEPPI.js → chunk-4GNDRRJ3.js} +3 -3
  8. package/dist/{chunk-CQAMEPPI.js.map → chunk-4GNDRRJ3.js.map} +1 -1
  9. package/dist/{chunk-EGXA4SYJ.js → chunk-BBFHGAEN.js} +4 -4
  10. package/dist/{chunk-EGXA4SYJ.js.map → chunk-BBFHGAEN.js.map} +1 -1
  11. package/dist/{chunk-ARSOSEJV.js → chunk-GPFIJR2K.js} +4 -4
  12. package/dist/{chunk-ARSOSEJV.js.map → chunk-GPFIJR2K.js.map} +1 -1
  13. package/dist/{chunk-EEIWAIXW.js → chunk-J5YUYDMK.js} +106 -6
  14. package/dist/chunk-J5YUYDMK.js.map +1 -0
  15. package/dist/{chunk-2VI24UPB.js → chunk-S4MOUOG4.js} +3 -3
  16. package/dist/{chunk-2VI24UPB.js.map → chunk-S4MOUOG4.js.map} +1 -1
  17. package/dist/{chunk-T6WIIGSK.js → chunk-XO4ODEY2.js} +3 -3
  18. package/dist/{chunk-T6WIIGSK.js.map → chunk-XO4ODEY2.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 +104 -4
  24. package/dist/durable.cjs.map +1 -1
  25. package/dist/durable.js +1 -1
  26. package/dist/engine.cjs +104 -4
  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 +104 -4
  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 +104 -4
  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 +104 -4
  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 +104 -4
  63. package/dist/schema.cjs.map +1 -1
  64. package/dist/schema.js +3 -3
  65. package/dist/screens.cjs +104 -4
  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 +104 -4
  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/durable.js CHANGED
@@ -1,4 +1,4 @@
1
- import { getNodeKind, runFlow, resolveKindId, kindIds, pauseForHuman, truthy, RunIdentity, decodePause, undeliveredEdgeWarnings } from './chunk-EEIWAIXW.js';
1
+ import { getNodeKind, runFlow, resolveKindId, kindIds, pauseForHuman, truthy, RunIdentity, decodePause, undeliveredEdgeWarnings } from './chunk-J5YUYDMK.js';
2
2
  import './chunk-2QXKDLGT.js';
3
3
 
4
4
  // src/durable/state.ts
package/dist/engine.cjs CHANGED
@@ -493,14 +493,102 @@ var mergeExecutor = (ctx) => {
493
493
  }
494
494
  return merged;
495
495
  };
496
- var forEachExecutor = (ctx) => {
497
- const source = resolve(configOf(ctx.node).source, ctx.inputs);
496
+ var FOR_EACH_DEFAULT_MAX_ITEMS = 1e3;
497
+ var FOR_EACH_HARD_MAX_ITEMS = 1e4;
498
+ function reachable(adjacency, starts) {
499
+ const seen = /* @__PURE__ */ new Set();
500
+ const queue = [...starts];
501
+ for (let cursor = 0; cursor < queue.length; cursor += 1) {
502
+ const id = queue[cursor];
503
+ if (seen.has(id)) continue;
504
+ seen.add(id);
505
+ for (const target of adjacency.get(id) ?? []) queue.push(target);
506
+ }
507
+ return seen;
508
+ }
509
+ function forEachLane(graph, nodeId) {
510
+ const handleOf = (edge) => edge.sourceHandle ?? "out";
511
+ const itemEdges = graph.edges.filter((e) => e.source === nodeId && handleOf(e) === "item");
512
+ if (itemEdges.length === 0) return null;
513
+ const adjacency = /* @__PURE__ */ new Map();
514
+ for (const edge of graph.edges) {
515
+ const list = adjacency.get(edge.source) ?? [];
516
+ list.push(edge.target);
517
+ adjacency.set(edge.source, list);
518
+ }
519
+ const done = reachable(
520
+ adjacency,
521
+ graph.edges.filter((e) => e.source === nodeId && handleOf(e) === "done").map((e) => e.target)
522
+ );
523
+ const body = new Set(
524
+ [...reachable(adjacency, itemEdges.map((e) => e.target))].filter(
525
+ (id) => !done.has(id) && id !== nodeId
526
+ )
527
+ );
528
+ return {
529
+ graph: {
530
+ nodes: graph.nodes.filter((n) => body.has(n.id)),
531
+ edges: graph.edges.filter((e) => body.has(e.source) && body.has(e.target)),
532
+ ...graph.inputs === void 0 ? {} : { inputs: graph.inputs }
533
+ },
534
+ entries: itemEdges.filter((e) => body.has(e.target))
535
+ };
536
+ }
537
+ var forEachExecutor = async (ctx) => {
538
+ const config = configOf(ctx.node);
539
+ const source = resolve(config.source, ctx.inputs);
498
540
  let items;
499
541
  if (Array.isArray(source)) items = source;
500
542
  else if (source === null || source === void 0) items = [];
501
543
  else if (typeof source === "object") items = Object.values(source);
502
544
  else items = [source];
503
- return { items, count: items.length };
545
+ const lane = ctx.graph ? forEachLane(ctx.graph, ctx.node.id) : null;
546
+ if (lane === null || config.mode === "collect") {
547
+ return { items, count: items.length };
548
+ }
549
+ if (lane.graph.nodes.length === 0) {
550
+ ctx.abort(`for_each "${ctx.node.id}" has an item edge but its derived lane is empty`);
551
+ }
552
+ const maxItems = Number(config.maxItems ?? FOR_EACH_DEFAULT_MAX_ITEMS);
553
+ if (!Number.isFinite(maxItems) || maxItems < 1 || maxItems > FOR_EACH_HARD_MAX_ITEMS) {
554
+ ctx.abort(`for_each "${ctx.node.id}" maxItems must be between 1 and ${FOR_EACH_HARD_MAX_ITEMS}`);
555
+ }
556
+ if (items.length > maxItems) {
557
+ ctx.abort(
558
+ `for_each "${ctx.node.id}" resolved ${items.length} items exceeds its maxItems cap of ${maxItems}`
559
+ );
560
+ }
561
+ const results = [];
562
+ const failures = [];
563
+ for (const [index, item] of items.entries()) {
564
+ const initialInputs = {};
565
+ for (const edge of lane.entries) {
566
+ initialInputs[edge.target] = {
567
+ ...initialInputs[edge.target] ?? {},
568
+ [edge.targetHandle ?? "in"]: item
569
+ };
570
+ }
571
+ const nested = await runFlow(lane.graph, { ...ctx.executors ?? {} }, () => {
572
+ }, {
573
+ initialInputs,
574
+ depth: (ctx.depth ?? 0) + 1,
575
+ // The index rides on the identity, so a node in iteration 3 cannot share
576
+ // an idempotency key with the same node in iteration 4.
577
+ run: ctx.run?.descend(ctx.node.id, index)
578
+ });
579
+ if (!nested.ok) {
580
+ const reason = nested.error ?? "unknown error";
581
+ if (decodePause(reason)) ctx.abort(reason);
582
+ results.push(null);
583
+ failures.push({ index, item, error: reason });
584
+ continue;
585
+ }
586
+ results.push(nested.outputs);
587
+ }
588
+ return {
589
+ __port: "done",
590
+ value: { items, results, failures, count: items.length }
591
+ };
504
592
  };
505
593
  var manualTriggerExecutor = (ctx) => ctx.inputs;
506
594
  var outputExecutor = (ctx) => {
@@ -1057,6 +1145,15 @@ var KINDS = [
1057
1145
  name: "@particle-academy/for_each",
1058
1146
  outputShape: [
1059
1147
  { path: "items", type: "array" },
1148
+ // `results` and `failures` are the WIRED shape -- what a lane produces
1149
+ // when the `item` port is connected. Declared unconditionally because a
1150
+ // declaration describes the kind, not one graph's use of it, and because
1151
+ // the shared table requires them: fancy-conformance 0.30.0 added
1152
+ // `results` and 0.31.0 added `failures`, both BREAKING, both so that a
1153
+ // runtime which has not implemented iteration fails here rather than
1154
+ // reporting surface parity it does not have.
1155
+ { path: "results", type: "array" },
1156
+ { path: "failures", type: "array" },
1060
1157
  { path: "count", type: "number" }
1061
1158
  ],
1062
1159
  aliases: ["for_each", "@fancy/for_each"],
@@ -1065,7 +1162,7 @@ var KINDS = [
1065
1162
  executor: forEachExecutor,
1066
1163
  category: "logic",
1067
1164
  label: "For Each",
1068
- description: "Publishes the resolved list and its size on BOTH `item` and `done`. Fan-out as DATA, not as jobs -- nothing runs per item.",
1165
+ 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.",
1069
1166
  icon: "\u21BB",
1070
1167
  inputs: [{ id: "in" }],
1071
1168
  outputs: [{ id: "item", label: "item" }, { id: "done", label: "done" }],
@@ -2631,6 +2728,9 @@ async function runFlow(graph, executors, onEvent = () => {
2631
2728
  executors,
2632
2729
  depth,
2633
2730
  run,
2731
+ // Structural executors derive a nested lane from the graph they are
2732
+ // in; `for_each`'s `item` port cannot be implemented without it.
2733
+ graph,
2634
2734
  terminal: terminalAccessorFor(node)
2635
2735
  })
2636
2736
  );