@bian-womp/spark-graph 0.1.10 → 0.1.12

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
@@ -480,16 +480,20 @@ class GraphRuntime {
480
480
  h(payload);
481
481
  }
482
482
  setInput(nodeId, handle, value) {
483
+ this.setInputs(nodeId, { [handle]: value });
484
+ }
485
+ setInputs(nodeId, inputs) {
483
486
  const node = this.nodes.get(nodeId);
484
487
  if (!node)
485
488
  throw new Error(`Node not found: ${nodeId}`);
486
- // If this input has an inbound edge, prefer propagated runtime value over manual input
487
- const hasInbound = this.edges.some((e) => e.target.nodeId === nodeId && e.target.handle === handle);
488
- if (hasInbound)
489
- return; // respect linked value
490
- node.inputs[handle] = value;
491
- // Emit value event for input updates
492
- this.emit("value", { nodeId, handle, value, io: "input" });
489
+ for (const [handle, value] of Object.entries(inputs)) {
490
+ const hasInbound = this.edges.some((e) => e.target.nodeId === nodeId && e.target.handle === handle);
491
+ if (hasInbound)
492
+ continue;
493
+ node.inputs[handle] = value;
494
+ // Emit value event for input updates
495
+ this.emit("value", { nodeId, handle, value, io: "input" });
496
+ }
493
497
  if (!this.paused)
494
498
  this.scheduleInputsChanged(nodeId);
495
499
  }
@@ -561,6 +565,7 @@ class GraphRuntime {
561
565
  },
562
566
  };
563
567
  const exec = async (attempt) => {
568
+ let hadError = false;
564
569
  try {
565
570
  await node.runtime.onInputsChanged?.(capturedInputs, ctx);
566
571
  }
@@ -572,6 +577,9 @@ class GraphRuntime {
572
577
  return; // ignore switched runs
573
578
  }
574
579
  }
580
+ hadError = true;
581
+ // Record last error for node
582
+ node.stats.lastError = err;
575
583
  const retry = policy.retry;
576
584
  if (retry && attempt < (retry.attempts ?? 0)) {
577
585
  const delay = retry.backoffMs ? retry.backoffMs(attempt) : 0;
@@ -590,6 +598,9 @@ class GraphRuntime {
590
598
  node.stats.lastStartAt && node.stats.lastEndAt
591
599
  ? node.stats.lastEndAt - node.stats.lastStartAt
592
600
  : undefined;
601
+ // Clear lastError upon successful completion
602
+ if (!hadError)
603
+ node.stats.lastError = undefined;
593
604
  this.emit("stats", {
594
605
  kind: "node-done",
595
606
  nodeId,
@@ -651,7 +662,8 @@ class GraphRuntime {
651
662
  // fan-out along all edges from this output
652
663
  const outEdges = this.edges.filter((e) => e.source.nodeId === srcNodeId && e.source.handle === srcHandle);
653
664
  for (const e of outEdges) {
654
- let nextVal = value;
665
+ // Clone per edge to isolate conversions from mutating the shared source value
666
+ let nextVal = structuredClone(value);
655
667
  const applyToTarget = (v) => {
656
668
  const dstNode = this.nodes.get(e.target.nodeId);
657
669
  if (!dstNode)
@@ -688,6 +700,8 @@ class GraphRuntime {
688
700
  e.stats.inFlight = false;
689
701
  e.stats.lastEndAt = Date.now();
690
702
  e.stats.lastDurationMs = e.stats.lastEndAt - startAt;
703
+ // Clear lastError on successful conversion
704
+ e.stats.lastError = undefined;
691
705
  this.emit("stats", {
692
706
  kind: "edge-done",
693
707
  edgeId: e.id,
@@ -1245,7 +1259,10 @@ class AbstractEngine {
1245
1259
  this.graphRuntime.launch();
1246
1260
  }
1247
1261
  setInput(nodeId, handle, value) {
1248
- this.graphRuntime.setInput(nodeId, handle, value);
1262
+ this.setInputs(nodeId, { [handle]: value });
1263
+ }
1264
+ setInputs(nodeId, inputs) {
1265
+ this.graphRuntime.setInputs(nodeId, inputs);
1249
1266
  }
1250
1267
  triggerExternal(nodeId, event) {
1251
1268
  this.graphRuntime.triggerExternal(nodeId, event);
@@ -1308,8 +1325,8 @@ class BatchedEngine extends AbstractEngine {
1308
1325
  this.timer = setInterval(() => this.flush(), this.opts.flushIntervalMs);
1309
1326
  }
1310
1327
  }
1311
- setInput(nodeId, handle, value) {
1312
- super.setInput(nodeId, handle, value);
1328
+ setInputs(nodeId, inputs) {
1329
+ super.setInputs(nodeId, inputs);
1313
1330
  this.dirtyNodes.add(nodeId);
1314
1331
  }
1315
1332
  triggerExternal(nodeId, event) {
@@ -1385,7 +1402,7 @@ class HybridEngine extends AbstractEngine {
1385
1402
  launch() {
1386
1403
  this.graphRuntime.resume();
1387
1404
  }
1388
- setInput(nodeId, handle, value) {
1405
+ setInputs(nodeId, inputs) {
1389
1406
  this.updateWindow();
1390
1407
  this.countInWindow += 1;
1391
1408
  const threshold = this.opts.batchThreshold ?? 5;
@@ -1408,7 +1425,7 @@ class HybridEngine extends AbstractEngine {
1408
1425
  this.flushTimer = undefined;
1409
1426
  }, windowMs);
1410
1427
  }
1411
- super.setInput(nodeId, handle, value);
1428
+ super.setInputs(nodeId, inputs);
1412
1429
  this.dirtyNodes.add(nodeId);
1413
1430
  if (!this.batching)
1414
1431
  this.graphRuntime.__unsafe_scheduleInputsChanged(nodeId);
@@ -1434,8 +1451,8 @@ class StepEngine extends AbstractEngine {
1434
1451
  this.graphRuntime.pause();
1435
1452
  }
1436
1453
  launch() { }
1437
- setInput(nodeId, handle, value) {
1438
- super.setInput(nodeId, handle, value);
1454
+ setInputs(nodeId, inputs) {
1455
+ super.setInputs(nodeId, inputs);
1439
1456
  this.dirtyNodes.add(nodeId);
1440
1457
  }
1441
1458
  triggerExternal(nodeId, event) {