@bian-womp/spark-graph 0.2.9 → 0.2.10

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/lib/cjs/index.cjs CHANGED
@@ -504,6 +504,7 @@ class GraphRuntime {
504
504
  srcUnionTypes: Array.isArray(srcDeclared)
505
505
  ? [...srcDeclared]
506
506
  : undefined,
507
+ dstDeclared,
507
508
  stats: { runs: 0, inFlight: false, progress: 0 },
508
509
  };
509
510
  });
@@ -839,17 +840,26 @@ class GraphRuntime {
839
840
  const dstNode = this.nodes.get(e.target.nodeId);
840
841
  if (!dstNode)
841
842
  return;
843
+ const dstIsArray = typeof e.dstDeclared === "string" && e.dstDeclared.endsWith("[]");
844
+ let next = v;
845
+ // If target input is an array type, append incoming values instead of last-write wins
846
+ if (dstIsArray) {
847
+ const toArray = (x) => Array.isArray(x) ? x : x === undefined ? [] : [x];
848
+ const prev = dstNode.inputs[e.target.handle];
849
+ const merged = [...toArray(prev), ...toArray(v)];
850
+ next = merged;
851
+ }
842
852
  const prev = dstNode.inputs[e.target.handle];
843
- const same = this.valuesEqual(prev, v);
853
+ const same = this.valuesEqual(prev, next);
844
854
  if (!same) {
845
- dstNode.inputs[e.target.handle] = v;
855
+ dstNode.inputs[e.target.handle] = next;
846
856
  // Emit value event for input updates
847
857
  this.emit("value", {
848
858
  nodeId: e.target.nodeId,
849
859
  handle: e.target.handle,
850
- value: v,
860
+ value: next,
851
861
  io: "input",
852
- runtimeTypeId: getTypedOutputTypeId(v),
862
+ runtimeTypeId: getTypedOutputTypeId(next),
853
863
  });
854
864
  if (!this.paused && this.allInboundHaveValue(e.target.nodeId))
855
865
  this.scheduleInputsChanged(e.target.nodeId);
@@ -1182,6 +1192,7 @@ class GraphRuntime {
1182
1192
  typeId: effectiveTypeId ?? "untyped",
1183
1193
  convert,
1184
1194
  convertAsync,
1195
+ dstDeclared,
1185
1196
  stats: { runs: 0, inFlight: false, progress: 0 },
1186
1197
  };
1187
1198
  });
@@ -1327,6 +1338,8 @@ class GraphBuilder {
1327
1338
  }
1328
1339
  // edges validation: nodes exist, handles exist, type exists
1329
1340
  const inboundCounts = new Map();
1341
+ // Track which inbound (nodeId::handle) are declared as array inputs, to allow multi-inbound without warning
1342
+ const inboundArrayOk = new Set();
1330
1343
  for (const e of def.edges) {
1331
1344
  if (edgeIds.has(e.id)) {
1332
1345
  issues.push({
@@ -1367,7 +1380,7 @@ class GraphBuilder {
1367
1380
  if (dstNode) {
1368
1381
  const dstType = this.registry.nodes.get(dstNode.typeId);
1369
1382
  if (dstType)
1370
- _dstDeclared = dstType.inputs[e.target.handle];
1383
+ _dstDeclared = getInputTypeId(dstType.inputs, e.target.handle);
1371
1384
  }
1372
1385
  if (!effectiveTypeId) {
1373
1386
  if (Array.isArray(_srcDeclared) && _dstDeclared) {
@@ -1514,9 +1527,19 @@ class GraphBuilder {
1514
1527
  // Track multiple inbound edges targeting the same input handle
1515
1528
  const inboundKey = `${e.target.nodeId}::${e.target.handle}`;
1516
1529
  inboundCounts.set(inboundKey, (inboundCounts.get(inboundKey) ?? 0) + 1);
1530
+ // If the target input is declared as an array type, allow multi-inbound (runtime will append)
1531
+ if (dstNode) {
1532
+ const dstType = this.registry.nodes.get(dstNode.typeId);
1533
+ const declaredIn = dstType
1534
+ ? getInputTypeId(dstType.inputs, e.target.handle)
1535
+ : undefined;
1536
+ if (typeof declaredIn === "string" && declaredIn.endsWith("[]")) {
1537
+ inboundArrayOk.add(inboundKey);
1538
+ }
1539
+ }
1517
1540
  }
1518
1541
  for (const [key, count] of inboundCounts) {
1519
- if (count > 1) {
1542
+ if (count > 1 && !inboundArrayOk.has(key)) {
1520
1543
  issues.push({
1521
1544
  level: "warning",
1522
1545
  code: "MULTI_INBOUND",