@bian-womp/spark-graph 0.2.11 → 0.2.13

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
@@ -409,6 +409,10 @@ class GraphRuntime {
409
409
  this.listeners = new Map();
410
410
  this.environment = {};
411
411
  this.paused = false;
412
+ // For array-typed target inputs, keep per-edge contributions so successive runs
413
+ // from the same source replace their slice instead of accumulating forever.
414
+ // Structure: nodeId -> handle -> edgeId -> values[]
415
+ this.arrayInputBuckets = new Map();
412
416
  }
413
417
  // Shallow/deep-ish equality to avoid unnecessary runs on identical values
414
418
  valuesEqual(a, b) {
@@ -842,11 +846,31 @@ class GraphRuntime {
842
846
  return;
843
847
  const dstIsArray = typeof e.dstDeclared === "string" && e.dstDeclared.endsWith("[]");
844
848
  let next = v;
845
- // If target input is an array type, append incoming values instead of last-write wins
849
+ // If target input is an array type, merge per-edge contributions deterministically
846
850
  if (dstIsArray) {
847
851
  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)];
852
+ // Update this edge's contribution
853
+ let forNode = this.arrayInputBuckets.get(e.target.nodeId);
854
+ if (!forNode) {
855
+ forNode = new Map();
856
+ this.arrayInputBuckets.set(e.target.nodeId, forNode);
857
+ }
858
+ let forHandle = forNode.get(e.target.handle);
859
+ if (!forHandle) {
860
+ forHandle = new Map();
861
+ forNode.set(e.target.handle, forHandle);
862
+ }
863
+ forHandle.set(e.id, toArray(v));
864
+ // Compute merged array in the order of current edges list
865
+ const merged = [];
866
+ for (const ed of this.edges) {
867
+ if (ed.target.nodeId === e.target.nodeId &&
868
+ ed.target.handle === e.target.handle) {
869
+ const part = forHandle.get(ed.id);
870
+ if (part && part.length)
871
+ merged.push(...part);
872
+ }
873
+ }
850
874
  next = merged;
851
875
  }
852
876
  const prev = dstNode.inputs[e.target.handle];
@@ -997,6 +1021,7 @@ class GraphRuntime {
997
1021
  this.nodes.clear();
998
1022
  this.edges = [];
999
1023
  this.listeners.clear();
1024
+ this.arrayInputBuckets.clear();
1000
1025
  }
1001
1026
  getNodeIds() {
1002
1027
  return Array.from(this.nodes.keys());
@@ -1124,6 +1149,8 @@ class GraphRuntime {
1124
1149
  setState: (next) => Object.assign(node.state, next),
1125
1150
  });
1126
1151
  this.nodes.delete(nodeId);
1152
+ // Clear any array buckets for this node
1153
+ this.arrayInputBuckets.delete(nodeId);
1127
1154
  }
1128
1155
  }
1129
1156
  // Add or update existing nodes
@@ -1301,6 +1328,16 @@ class GraphRuntime {
1301
1328
  }
1302
1329
  }
1303
1330
  }
1331
+ // Clear buckets for handles that lost inbound
1332
+ const bucketsForNode = this.arrayInputBuckets.get(nodeId);
1333
+ if (bucketsForNode) {
1334
+ for (const handle of Array.from(prevSet)) {
1335
+ if (!currSet.has(handle))
1336
+ bucketsForNode.delete(handle);
1337
+ }
1338
+ if (bucketsForNode.size === 0)
1339
+ this.arrayInputBuckets.delete(nodeId);
1340
+ }
1304
1341
  this.scheduleInputsChanged(nodeId);
1305
1342
  }
1306
1343
  }
@@ -1348,6 +1385,29 @@ class GraphRuntime {
1348
1385
  }
1349
1386
  }
1350
1387
  }
1388
+ // Prune array bucket contributions for edges that no longer exist
1389
+ const validPerTarget = new Map();
1390
+ for (const ed of this.edges) {
1391
+ const m = validPerTarget.get(ed.target.nodeId) ?? new Map();
1392
+ const s = m.get(ed.target.handle) ?? new Set();
1393
+ s.add(ed.id);
1394
+ m.set(ed.target.handle, s);
1395
+ validPerTarget.set(ed.target.nodeId, m);
1396
+ }
1397
+ for (const [nodeId, byHandle] of Array.from(this.arrayInputBuckets)) {
1398
+ const validHandles = validPerTarget.get(nodeId) ?? new Map();
1399
+ for (const [handle, perEdge] of Array.from(byHandle)) {
1400
+ const validEdgeIds = validHandles.get(handle) ?? new Set();
1401
+ for (const edgeId of Array.from(perEdge.keys())) {
1402
+ if (!validEdgeIds.has(edgeId))
1403
+ perEdge.delete(edgeId);
1404
+ }
1405
+ if (perEdge.size === 0)
1406
+ byHandle.delete(handle);
1407
+ }
1408
+ if (byHandle.size === 0)
1409
+ this.arrayInputBuckets.delete(nodeId);
1410
+ }
1351
1411
  }
1352
1412
  }
1353
1413