@particle-academy/fancy-flow 0.63.0 → 0.64.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.
package/dist/index.cjs CHANGED
@@ -14390,6 +14390,55 @@ function preview(v2) {
14390
14390
  }
14391
14391
  }
14392
14392
 
14393
+ // src/analysis/graph-connectivity.ts
14394
+ function checkGraphConnectivity(graph) {
14395
+ const { nodes, edges } = graph;
14396
+ const issues = [];
14397
+ const hasIncoming = /* @__PURE__ */ new Set();
14398
+ const hasOutgoing = /* @__PURE__ */ new Set();
14399
+ for (const edge of edges) {
14400
+ hasIncoming.add(edge.target);
14401
+ hasOutgoing.add(edge.source);
14402
+ }
14403
+ const single = nodes.length === 1;
14404
+ for (const node of nodes) {
14405
+ if (single || mayFloat(node)) continue;
14406
+ if (!hasIncoming.has(node.id) && !hasOutgoing.has(node.id)) {
14407
+ issues.push({
14408
+ level: "error",
14409
+ nodeId: node.id,
14410
+ message: `Node "${node.id}" is connected to nothing \u2014 no inbound edge and no outbound edge. It still RUNS (a node with no inbound edge is a root), but it receives nothing from the graph and reaches nobody in it, so it is either unwired or left behind by a deletion. Only a note, an annotation or a lane may float.`
14411
+ });
14412
+ }
14413
+ }
14414
+ const byId = new Map(nodes.map((n) => [n.id, n]));
14415
+ for (const edge of edges) {
14416
+ const source = byId.get(edge.source);
14417
+ if (!source || !isTerminator(source)) continue;
14418
+ issues.push({
14419
+ level: "error",
14420
+ edgeId: edge.id,
14421
+ message: `Edge "${edge.id}" reads from "${edge.source}", which is a TERMINAL node and publishes no output ports at all. Nothing can ever travel this edge: it does not fail at run time, it delivers nothing, and "${edge.target}" runs anyway with an empty input.`
14422
+ });
14423
+ }
14424
+ return issues;
14425
+ }
14426
+ function mayFloat(node) {
14427
+ const type = node.type;
14428
+ if (!type) return false;
14429
+ if (type === "note") return true;
14430
+ const kind = getNodeKind(type);
14431
+ if (!kind) return true;
14432
+ return kindIds(kind).includes("note") || kind.category === "annotation" || kind.category === "layout";
14433
+ }
14434
+ function isTerminator(node) {
14435
+ const own = node.data?.outputs;
14436
+ if (Array.isArray(own)) return own.length === 0;
14437
+ const kind = node.type ? getNodeKind(node.type) : null;
14438
+ if (!kind) return false;
14439
+ return Array.isArray(kind.outputs) && kind.outputs.length === 0;
14440
+ }
14441
+
14393
14442
  // src/schema/workflow-schema.ts
14394
14443
  var WORKFLOW_SCHEMA_VERSION = 1;
14395
14444
  var WORKFLOW_SCHEMA_URL = "https://particle.academy/schemas/workflow/v1.json";
@@ -14537,6 +14586,7 @@ function importWorkflow(schema, options = {}) {
14537
14586
  label: e.label
14538
14587
  };
14539
14588
  }).filter((e) => e !== null);
14589
+ issues.push(...checkGraphConnectivity({ nodes, edges }));
14540
14590
  const ok = issues.every((i) => i.level !== "error");
14541
14591
  const declaredInputs = Array.isArray(s.inputs) ? s.inputs.filter(
14542
14592
  (input) => input !== null && typeof input === "object" && typeof input.name === "string"