@bian-womp/spark-graph 0.2.86 → 0.2.88

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
@@ -760,6 +760,7 @@ class GraphRuntime {
760
760
  this.emit("stats", {
761
761
  kind: "node-progress",
762
762
  nodeId,
763
+ typeId: node.typeId,
763
764
  runId,
764
765
  progress: node.stats.progress,
765
766
  });
@@ -776,15 +777,16 @@ class GraphRuntime {
776
777
  await node.runtime.onInputsChanged?.(capturedInputs, ctx);
777
778
  }
778
779
  catch (err) {
779
- // Suppress errors caused by expected cancellations (switch, snapshot)
780
+ // Suppress errors caused by expected cancellations
780
781
  if (controller.signal.aborted) {
781
782
  const reason = controller.signal.reason;
782
- if (reason === "switch" || reason === "snapshot") {
783
- return; // ignore switched/snapshot-cancelled runs
783
+ if (reason === "switch" ||
784
+ reason === "snapshot" ||
785
+ reason === "node-deleted") {
786
+ return; // Cancellation events are emitted separately, skip error handling
784
787
  }
785
788
  }
786
789
  hadError = true;
787
- // Record last error for node
788
790
  node.stats.lastError = err;
789
791
  const retry = policy.retry;
790
792
  if (retry && attempt < (retry.attempts ?? 0)) {
@@ -795,6 +797,10 @@ class GraphRuntime {
795
797
  this.emit("error", { kind: "node-run", nodeId, runId, err });
796
798
  }
797
799
  finally {
800
+ // Skip cleanup if node was deleted (cleanup already handled)
801
+ if (!this.nodes.has(nodeId)) {
802
+ return;
803
+ }
798
804
  if (timeoutId)
799
805
  clearTimeout(timeoutId);
800
806
  node.activeControllers.delete(controller);
@@ -805,15 +811,21 @@ class GraphRuntime {
805
811
  node.stats.lastStartAt && node.stats.lastEndAt
806
812
  ? node.stats.lastEndAt - node.stats.lastStartAt
807
813
  : undefined;
808
- // Clear lastError upon successful completion
809
814
  if (!hadError)
810
815
  node.stats.lastError = undefined;
811
- this.emit("stats", {
812
- kind: "node-done",
813
- nodeId,
814
- runId,
815
- durationMs: node.stats.lastDurationMs,
816
- });
816
+ // Only emit node-done if not cancelled (cancellation events emitted separately)
817
+ const isCancelled = controller.signal.aborted &&
818
+ (controller.signal.reason === "snapshot" ||
819
+ controller.signal.reason === "node-deleted");
820
+ if (!isCancelled) {
821
+ this.emit("stats", {
822
+ kind: "node-done",
823
+ nodeId,
824
+ typeId: node.typeId,
825
+ runId,
826
+ durationMs: node.stats.lastDurationMs,
827
+ });
828
+ }
817
829
  ctx.log("debug", "node-done", {
818
830
  durationMs: node.stats.lastDurationMs,
819
831
  hadError,
@@ -823,7 +835,12 @@ class GraphRuntime {
823
835
  }
824
836
  };
825
837
  // fire node-start event
826
- this.emit("stats", { kind: "node-start", nodeId, runId });
838
+ this.emit("stats", {
839
+ kind: "node-start",
840
+ nodeId,
841
+ typeId: node.typeId,
842
+ runId,
843
+ });
827
844
  ctx.log("debug", "node-start");
828
845
  exec(0);
829
846
  };
@@ -1019,6 +1036,7 @@ class GraphRuntime {
1019
1036
  this.emit("stats", {
1020
1037
  kind: "edge-start",
1021
1038
  edgeId: e.id,
1039
+ typeId: e.typeId,
1022
1040
  source: { nodeId: e.source.nodeId, handle: e.source.handle },
1023
1041
  target: { nodeId: e.target.nodeId, handle: e.target.handle },
1024
1042
  });
@@ -1039,6 +1057,7 @@ class GraphRuntime {
1039
1057
  this.emit("stats", {
1040
1058
  kind: "edge-done",
1041
1059
  edgeId: e.id,
1060
+ typeId: e.typeId,
1042
1061
  source: { nodeId: e.source.nodeId, handle: e.source.handle },
1043
1062
  target: { nodeId: e.target.nodeId, handle: e.target.handle },
1044
1063
  durationMs: e.stats.lastDurationMs,
@@ -1352,12 +1371,50 @@ class GraphRuntime {
1352
1371
  scheduleInputsChanged(nodeId) {
1353
1372
  this.scheduleInputsChangedInternal(nodeId);
1354
1373
  }
1374
+ /**
1375
+ * Cancel all active runs for a node and emit cancellation events
1376
+ * @param node - The node to cancel runs for
1377
+ * @param reason - The cancellation reason ("snapshot" | "node-deleted")
1378
+ */
1379
+ cancelNodeActiveRuns(node, reason) {
1380
+ for (const controller of Array.from(node.activeControllers)) {
1381
+ const runId = node.controllerRunIds.get(controller);
1382
+ if (runId) {
1383
+ // Track cancelled runIds for snapshot operations
1384
+ if (reason === "snapshot") {
1385
+ if (!node.snapshotCancelledRunIds) {
1386
+ node.snapshotCancelledRunIds = new Set();
1387
+ }
1388
+ node.snapshotCancelledRunIds.add(runId);
1389
+ }
1390
+ // Emit cancellation event
1391
+ this.emit("stats", {
1392
+ kind: "node-done",
1393
+ nodeId: node.nodeId,
1394
+ typeId: node.typeId,
1395
+ runId,
1396
+ cancelled: true,
1397
+ });
1398
+ }
1399
+ try {
1400
+ controller.abort(reason);
1401
+ }
1402
+ catch {
1403
+ // ignore abort errors
1404
+ }
1405
+ }
1406
+ node.activeControllers.clear();
1407
+ node.controllerRunIds.clear();
1408
+ node.stats.active = 0;
1409
+ node.queue = [];
1410
+ }
1355
1411
  cancelNodeRuns(nodeIds) {
1356
1412
  if (nodeIds.length === 0)
1357
1413
  return;
1358
1414
  const toCancel = new Set(nodeIds);
1359
1415
  const visited = new Set();
1360
1416
  const queue = [...nodeIds];
1417
+ // Collect all downstream nodes to cancel
1361
1418
  for (let i = 0; i < queue.length; i++) {
1362
1419
  const nodeId = queue[i];
1363
1420
  if (visited.has(nodeId))
@@ -1373,29 +1430,12 @@ class GraphRuntime {
1373
1430
  }
1374
1431
  }
1375
1432
  }
1433
+ // Cancel runs for all affected nodes
1376
1434
  for (const nodeId of toCancel) {
1377
1435
  const node = this.nodes.get(nodeId);
1378
1436
  if (!node)
1379
1437
  continue;
1380
- if (!node.snapshotCancelledRunIds) {
1381
- node.snapshotCancelledRunIds = new Set();
1382
- }
1383
- for (const controller of Array.from(node.activeControllers)) {
1384
- const rid = node.controllerRunIds.get(controller);
1385
- if (rid) {
1386
- node.snapshotCancelledRunIds.add(rid);
1387
- }
1388
- try {
1389
- controller.abort("snapshot");
1390
- }
1391
- catch {
1392
- // ignore
1393
- }
1394
- }
1395
- node.activeControllers.clear();
1396
- node.controllerRunIds.clear();
1397
- node.stats.active = 0;
1398
- node.queue = [];
1438
+ this.cancelNodeActiveRuns(node, "snapshot");
1399
1439
  node.runSeq += 1;
1400
1440
  const now = Date.now();
1401
1441
  node.latestRunId = `${nodeId}:${node.runSeq}:${now}:snapshot`;
@@ -1458,6 +1498,9 @@ class GraphRuntime {
1458
1498
  for (const nodeId of Array.from(currentIds)) {
1459
1499
  if (!desiredIds.has(nodeId)) {
1460
1500
  const node = this.nodes.get(nodeId);
1501
+ // Cancel all active runs and emit cancellation events
1502
+ this.cancelNodeActiveRuns(node, "node-deleted");
1503
+ // Cleanup node resources
1461
1504
  node.runtime.onDeactivated?.();
1462
1505
  node.runtime.dispose?.();
1463
1506
  node.lifecycle?.dispose?.({
@@ -1465,7 +1508,6 @@ class GraphRuntime {
1465
1508
  setState: (next) => Object.assign(node.state, next),
1466
1509
  });
1467
1510
  this.nodes.delete(nodeId);
1468
- // Clear any array buckets for this node
1469
1511
  this.arrayInputBuckets.delete(nodeId);
1470
1512
  }
1471
1513
  }