@bian-womp/spark-graph 0.3.50 → 0.3.51

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.
Files changed (173) hide show
  1. package/lib/cjs/index.cjs +129 -107
  2. package/lib/cjs/index.cjs.map +1 -1
  3. package/lib/cjs/src/core/types.d.ts +1 -0
  4. package/lib/cjs/src/core/types.d.ts.map +1 -1
  5. package/lib/cjs/src/runtime/GraphRuntime.d.ts +7 -2
  6. package/lib/cjs/src/runtime/GraphRuntime.d.ts.map +1 -1
  7. package/lib/cjs/src/runtime/LocalEngine.d.ts.map +1 -1
  8. package/lib/cjs/src/runtime/components/EdgePropagator.d.ts.map +1 -1
  9. package/lib/cjs/src/runtime/components/NodeExecutor.d.ts +8 -4
  10. package/lib/cjs/src/runtime/components/NodeExecutor.d.ts.map +1 -1
  11. package/lib/cjs/src/runtime/components/interfaces.d.ts +5 -1
  12. package/lib/cjs/src/runtime/components/interfaces.d.ts.map +1 -1
  13. package/lib/cjs/src/runtime/components/types.d.ts +1 -0
  14. package/lib/cjs/src/runtime/components/types.d.ts.map +1 -1
  15. package/lib/esm/index.js +129 -107
  16. package/lib/esm/index.js.map +1 -1
  17. package/lib/esm/src/core/types.d.ts +1 -0
  18. package/lib/esm/src/core/types.d.ts.map +1 -1
  19. package/lib/esm/src/runtime/GraphRuntime.d.ts +7 -2
  20. package/lib/esm/src/runtime/GraphRuntime.d.ts.map +1 -1
  21. package/lib/esm/src/runtime/LocalEngine.d.ts.map +1 -1
  22. package/lib/esm/src/runtime/components/EdgePropagator.d.ts.map +1 -1
  23. package/lib/esm/src/runtime/components/NodeExecutor.d.ts +8 -4
  24. package/lib/esm/src/runtime/components/NodeExecutor.d.ts.map +1 -1
  25. package/lib/esm/src/runtime/components/interfaces.d.ts +5 -1
  26. package/lib/esm/src/runtime/components/interfaces.d.ts.map +1 -1
  27. package/lib/esm/src/runtime/components/types.d.ts +1 -0
  28. package/lib/esm/src/runtime/components/types.d.ts.map +1 -1
  29. package/package.json +2 -2
  30. package/lib/src/builder/GraphBuilder.d.ts +0 -43
  31. package/lib/src/builder/GraphBuilder.d.ts.map +0 -1
  32. package/lib/src/builder/GraphBuilder.js +0 -284
  33. package/lib/src/builder/GraphBuilder.js.map +0 -1
  34. package/lib/src/builder/Registry.d.ts +0 -93
  35. package/lib/src/builder/Registry.d.ts.map +0 -1
  36. package/lib/src/builder/Registry.js +0 -393
  37. package/lib/src/builder/Registry.js.map +0 -1
  38. package/lib/src/core/categories.d.ts +0 -22
  39. package/lib/src/core/categories.d.ts.map +0 -1
  40. package/lib/src/core/categories.js +0 -2
  41. package/lib/src/core/categories.js.map +0 -1
  42. package/lib/src/core/order.d.ts +0 -7
  43. package/lib/src/core/order.d.ts.map +0 -1
  44. package/lib/src/core/order.js +0 -66
  45. package/lib/src/core/order.js.map +0 -1
  46. package/lib/src/core/type-utils.d.ts +0 -29
  47. package/lib/src/core/type-utils.d.ts.map +0 -1
  48. package/lib/src/core/type-utils.js +0 -97
  49. package/lib/src/core/type-utils.js.map +0 -1
  50. package/lib/src/core/types.d.ts +0 -92
  51. package/lib/src/core/types.d.ts.map +0 -1
  52. package/lib/src/core/types.js +0 -2
  53. package/lib/src/core/types.js.map +0 -1
  54. package/lib/src/examples/arrays.d.ts +0 -5
  55. package/lib/src/examples/arrays.d.ts.map +0 -1
  56. package/lib/src/examples/arrays.js +0 -49
  57. package/lib/src/examples/arrays.js.map +0 -1
  58. package/lib/src/examples/async.d.ts +0 -5
  59. package/lib/src/examples/async.d.ts.map +0 -1
  60. package/lib/src/examples/async.js +0 -91
  61. package/lib/src/examples/async.js.map +0 -1
  62. package/lib/src/examples/progress.d.ts +0 -5
  63. package/lib/src/examples/progress.d.ts.map +0 -1
  64. package/lib/src/examples/progress.js +0 -51
  65. package/lib/src/examples/progress.js.map +0 -1
  66. package/lib/src/examples/run.d.ts +0 -2
  67. package/lib/src/examples/run.d.ts.map +0 -1
  68. package/lib/src/examples/run.js +0 -32
  69. package/lib/src/examples/run.js.map +0 -1
  70. package/lib/src/examples/runMode.d.ts +0 -2
  71. package/lib/src/examples/runMode.d.ts.map +0 -1
  72. package/lib/src/examples/runMode.js +0 -223
  73. package/lib/src/examples/runMode.js.map +0 -1
  74. package/lib/src/examples/shared.d.ts +0 -5
  75. package/lib/src/examples/shared.d.ts.map +0 -1
  76. package/lib/src/examples/shared.js +0 -49
  77. package/lib/src/examples/shared.js.map +0 -1
  78. package/lib/src/examples/simple.d.ts +0 -5
  79. package/lib/src/examples/simple.d.ts.map +0 -1
  80. package/lib/src/examples/simple.js +0 -79
  81. package/lib/src/examples/simple.js.map +0 -1
  82. package/lib/src/examples/snapshot.d.ts +0 -4
  83. package/lib/src/examples/snapshot.d.ts.map +0 -1
  84. package/lib/src/examples/snapshot.js +0 -58
  85. package/lib/src/examples/snapshot.js.map +0 -1
  86. package/lib/src/examples/validation.d.ts +0 -5
  87. package/lib/src/examples/validation.d.ts.map +0 -1
  88. package/lib/src/examples/validation.js +0 -105
  89. package/lib/src/examples/validation.js.map +0 -1
  90. package/lib/src/index.d.ts +0 -27
  91. package/lib/src/index.d.ts.map +0 -1
  92. package/lib/src/index.js +0 -19
  93. package/lib/src/index.js.map +0 -1
  94. package/lib/src/misc/base.d.ts +0 -51
  95. package/lib/src/misc/base.d.ts.map +0 -1
  96. package/lib/src/misc/base.js +0 -1122
  97. package/lib/src/misc/base.js.map +0 -1
  98. package/lib/src/misc/utils/json.d.ts +0 -22
  99. package/lib/src/misc/utils/json.d.ts.map +0 -1
  100. package/lib/src/misc/utils/json.js +0 -239
  101. package/lib/src/misc/utils/json.js.map +0 -1
  102. package/lib/src/misc/utils/merge.d.ts +0 -51
  103. package/lib/src/misc/utils/merge.d.ts.map +0 -1
  104. package/lib/src/misc/utils/merge.js +0 -600
  105. package/lib/src/misc/utils/merge.js.map +0 -1
  106. package/lib/src/plugins/composite.d.ts +0 -22
  107. package/lib/src/plugins/composite.d.ts.map +0 -1
  108. package/lib/src/plugins/composite.js +0 -59
  109. package/lib/src/plugins/composite.js.map +0 -1
  110. package/lib/src/plugins/compute.d.ts +0 -5
  111. package/lib/src/plugins/compute.d.ts.map +0 -1
  112. package/lib/src/plugins/compute.js +0 -39
  113. package/lib/src/plugins/compute.js.map +0 -1
  114. package/lib/src/runtime/Engine.d.ts +0 -28
  115. package/lib/src/runtime/Engine.d.ts.map +0 -1
  116. package/lib/src/runtime/Engine.js +0 -2
  117. package/lib/src/runtime/Engine.js.map +0 -1
  118. package/lib/src/runtime/GraphLifecycleApi.d.ts +0 -46
  119. package/lib/src/runtime/GraphLifecycleApi.d.ts.map +0 -1
  120. package/lib/src/runtime/GraphLifecycleApi.js +0 -2
  121. package/lib/src/runtime/GraphLifecycleApi.js.map +0 -1
  122. package/lib/src/runtime/GraphRuntime.d.ts +0 -94
  123. package/lib/src/runtime/GraphRuntime.d.ts.map +0 -1
  124. package/lib/src/runtime/GraphRuntime.js +0 -729
  125. package/lib/src/runtime/GraphRuntime.js.map +0 -1
  126. package/lib/src/runtime/LocalEngine.d.ts +0 -45
  127. package/lib/src/runtime/LocalEngine.d.ts.map +0 -1
  128. package/lib/src/runtime/LocalEngine.js +0 -89
  129. package/lib/src/runtime/LocalEngine.js.map +0 -1
  130. package/lib/src/runtime/components/EdgePropagator.d.ts +0 -101
  131. package/lib/src/runtime/components/EdgePropagator.d.ts.map +0 -1
  132. package/lib/src/runtime/components/EdgePropagator.js +0 -372
  133. package/lib/src/runtime/components/EdgePropagator.js.map +0 -1
  134. package/lib/src/runtime/components/EventEmitter.d.ts +0 -12
  135. package/lib/src/runtime/components/EventEmitter.d.ts.map +0 -1
  136. package/lib/src/runtime/components/EventEmitter.js +0 -33
  137. package/lib/src/runtime/components/EventEmitter.js.map +0 -1
  138. package/lib/src/runtime/components/Graph.d.ts +0 -211
  139. package/lib/src/runtime/components/Graph.d.ts.map +0 -1
  140. package/lib/src/runtime/components/Graph.js +0 -468
  141. package/lib/src/runtime/components/Graph.js.map +0 -1
  142. package/lib/src/runtime/components/HandleResolver.d.ts +0 -36
  143. package/lib/src/runtime/components/HandleResolver.d.ts.map +0 -1
  144. package/lib/src/runtime/components/HandleResolver.js +0 -231
  145. package/lib/src/runtime/components/HandleResolver.js.map +0 -1
  146. package/lib/src/runtime/components/NodeExecutor.d.ts +0 -110
  147. package/lib/src/runtime/components/NodeExecutor.d.ts.map +0 -1
  148. package/lib/src/runtime/components/NodeExecutor.js +0 -659
  149. package/lib/src/runtime/components/NodeExecutor.js.map +0 -1
  150. package/lib/src/runtime/components/RunContextManager.d.ts +0 -86
  151. package/lib/src/runtime/components/RunContextManager.d.ts.map +0 -1
  152. package/lib/src/runtime/components/RunContextManager.js +0 -302
  153. package/lib/src/runtime/components/RunContextManager.js.map +0 -1
  154. package/lib/src/runtime/components/RuntimeValidatorManager.d.ts +0 -31
  155. package/lib/src/runtime/components/RuntimeValidatorManager.d.ts.map +0 -1
  156. package/lib/src/runtime/components/RuntimeValidatorManager.js +0 -55
  157. package/lib/src/runtime/components/RuntimeValidatorManager.js.map +0 -1
  158. package/lib/src/runtime/components/graph-utils.d.ts +0 -33
  159. package/lib/src/runtime/components/graph-utils.d.ts.map +0 -1
  160. package/lib/src/runtime/components/graph-utils.js +0 -292
  161. package/lib/src/runtime/components/graph-utils.js.map +0 -1
  162. package/lib/src/runtime/components/interfaces.d.ts +0 -54
  163. package/lib/src/runtime/components/interfaces.d.ts.map +0 -1
  164. package/lib/src/runtime/components/interfaces.js +0 -2
  165. package/lib/src/runtime/components/interfaces.js.map +0 -1
  166. package/lib/src/runtime/components/types.d.ts +0 -55
  167. package/lib/src/runtime/components/types.d.ts.map +0 -1
  168. package/lib/src/runtime/components/types.js +0 -2
  169. package/lib/src/runtime/components/types.js.map +0 -1
  170. package/lib/src/runtime/utils.d.ts +0 -67
  171. package/lib/src/runtime/utils.d.ts.map +0 -1
  172. package/lib/src/runtime/utils.js +0 -137
  173. package/lib/src/runtime/utils.js.map +0 -1
package/lib/cjs/index.cjs CHANGED
@@ -2154,7 +2154,7 @@ class EdgePropagator {
2154
2154
  // Set input value (respecting skipPropagateValues)
2155
2155
  const shouldSetValue = this.shouldSetInputValue(effectiveRunContexts);
2156
2156
  if (shouldSetValue && valueChanged) {
2157
- this.setTargetInput(edge, dstNode, processedValue);
2157
+ this.setTargetInput(edge, processedValue);
2158
2158
  }
2159
2159
  else if (shouldSetValue && !valueChanged) {
2160
2160
  // Even if value didn't change, update timestamp if we're forcing execution
@@ -2213,7 +2213,7 @@ class EdgePropagator {
2213
2213
  /**
2214
2214
  * Set target input value and emit event
2215
2215
  */
2216
- setTargetInput(edge, dstNode, value) {
2216
+ setTargetInput(edge, value) {
2217
2217
  this.graph.updateNodeInput(edge.target.nodeId, edge.target.handle, value);
2218
2218
  this.handleResolver.scheduleRecomputeHandles(edge.target.nodeId);
2219
2219
  }
@@ -2224,7 +2224,10 @@ class EdgePropagator {
2224
2224
  // Determine if we should propagate
2225
2225
  const shouldPropagate = this.shouldPropagateExecution(effectiveRunContexts);
2226
2226
  if (shouldPropagate && this.graph.allInboundHaveValue(targetNodeId)) {
2227
- this.nodeExecutor.execute(targetNodeId, effectiveRunContexts);
2227
+ this.nodeExecutor.execute(targetNodeId, {
2228
+ runContextIds: effectiveRunContexts,
2229
+ reason: "executeDownstream",
2230
+ });
2228
2231
  }
2229
2232
  }
2230
2233
  /**
@@ -2354,7 +2357,7 @@ class NodeExecutor {
2354
2357
  /**
2355
2358
  * Create an execution context for a node
2356
2359
  */
2357
- createExecutionContext(nodeId, node, inputs, runId, abortSignal, runContextIds, options) {
2360
+ createExecutionContext(nodeId, inputs, runId, abortSignal, runContextIds, options) {
2358
2361
  const emitHandler = options?.emitHandler ??
2359
2362
  ((handle, value) => {
2360
2363
  this.edgePropagator.propagate(nodeId, handle, value, runContextIds);
@@ -2366,8 +2369,9 @@ class NodeExecutor {
2366
2369
  });
2367
2370
  });
2368
2371
  // Create log function that respects node's logLevel using LevelLogger
2369
- const nodeLogLevel = node.logLevel ?? "info";
2370
- const logger = new LevelLogger(nodeLogLevel, `[node:${runId || nodeId}:${node.typeId}]`);
2372
+ const node = this.graph.getNode(nodeId);
2373
+ const nodeLogLevel = node?.logLevel ?? "info";
2374
+ const logger = new LevelLogger(nodeLogLevel, `[node:${runId || nodeId}:${node?.typeId ?? ""}]`);
2371
2375
  const log = (level, message, context) => {
2372
2376
  switch (level) {
2373
2377
  case "debug":
@@ -2386,7 +2390,7 @@ class NodeExecutor {
2386
2390
  };
2387
2391
  return {
2388
2392
  nodeId,
2389
- state: node.state,
2393
+ state: node?.state,
2390
2394
  setState: (next) => this.graph.updateNodeState(nodeId, next),
2391
2395
  emit: emitHandler,
2392
2396
  invalidateDownstream: () => {
@@ -2401,7 +2405,10 @@ class NodeExecutor {
2401
2405
  this.runContextManager.createRunContext(nodeId, opts),
2402
2406
  ]);
2403
2407
  }
2404
- this.execute(nodeId, runContextIdsToUse);
2408
+ this.execute(nodeId, {
2409
+ runContextIds: runContextIdsToUse,
2410
+ reason: opts?.reason ?? "executeFromContext",
2411
+ });
2405
2412
  }
2406
2413
  },
2407
2414
  getInput: (handle) => inputs[handle],
@@ -2413,7 +2420,7 @@ class NodeExecutor {
2413
2420
  this.eventEmitter.emit("stats", {
2414
2421
  kind: "node-custom-data",
2415
2422
  nodeId,
2416
- typeId: node.typeId,
2423
+ typeId: node?.typeId ?? "",
2417
2424
  runId,
2418
2425
  data,
2419
2426
  });
@@ -2424,13 +2431,14 @@ class NodeExecutor {
2424
2431
  /**
2425
2432
  * Internal method for executing inputs changed (also used by GraphRuntime)
2426
2433
  */
2427
- execute(nodeId, runContextIds, canSkipHandleResolution) {
2434
+ execute(nodeId, opts) {
2435
+ let { runContextIds, canSkipHandleResolution, reason = "" } = opts ?? {};
2428
2436
  const node = this.graph.getNode(nodeId);
2429
2437
  if (!node)
2430
2438
  return;
2431
2439
  const runMode = this.runtime.getRunMode();
2432
2440
  if (!runMode) {
2433
- console.trace("NodeExecutor.execute: no runMode, skipping execution");
2441
+ console.trace(`NodeExecutor.execute[${nodeId}:${reason}]: no runMode, skipping execution`);
2434
2442
  return;
2435
2443
  }
2436
2444
  // In manual mode, require runContextIds unless autoRun policy is set
@@ -2442,12 +2450,12 @@ class NodeExecutor {
2442
2450
  ]);
2443
2451
  }
2444
2452
  else {
2445
- console.trace("NodeExecutor.execute: no runContextIds provided in manual mode, skipping execution");
2453
+ console.trace(`NodeExecutor.execute[${nodeId}:${reason}]: no runContextIds provided in manual mode, skipping execution`);
2446
2454
  return;
2447
2455
  }
2448
2456
  }
2449
2457
  if (runMode === "auto" && runContextIds && runContextIds.size > 0) {
2450
- console.trace("NodeExecutor.execute: runContextIds provided in auto mode, ignoring");
2458
+ console.trace(`NodeExecutor.execute[${nodeId}:${reason}]: runContextIds provided in auto mode, ignoring`);
2451
2459
  runContextIds = undefined;
2452
2460
  }
2453
2461
  // Early validation for auto-mode paused state
@@ -2490,7 +2498,11 @@ class NodeExecutor {
2490
2498
  // Re-check node still exists and conditions
2491
2499
  const nodeAfter = this.graph.getNode(nodeId);
2492
2500
  if (nodeAfter) {
2493
- this.execute(nodeId, runContextIds, true);
2501
+ this.execute(nodeId, {
2502
+ runContextIds,
2503
+ canSkipHandleResolution: true,
2504
+ reason: opts?.reason,
2505
+ });
2494
2506
  }
2495
2507
  if (runContextIds && runContextIds.size > 0) {
2496
2508
  for (const id of runContextIds) {
@@ -2502,19 +2514,22 @@ class NodeExecutor {
2502
2514
  }
2503
2515
  // Handle debouncing
2504
2516
  const now = Date.now();
2505
- if (this.shouldDebounce(node, now)) {
2506
- this.handleDebouncedSchedule(node, nodeId, now, runContextIds);
2517
+ if (this.shouldDebounce(nodeId, now)) {
2518
+ this.handleDebouncedSchedule(nodeId, now, runContextIds, reason);
2507
2519
  return;
2508
2520
  }
2509
2521
  // Prepare execution plan
2510
- const executionPlan = this.prepareExecutionPlan(node, nodeId, runContextIds, now);
2522
+ const executionPlan = this.prepareExecutionPlan(nodeId, runContextIds, now, reason);
2511
2523
  // Route to appropriate concurrency handler
2512
- this.routeToConcurrencyHandler(node, nodeId, executionPlan);
2524
+ this.routeToConcurrencyHandler(nodeId, executionPlan);
2513
2525
  }
2514
2526
  /**
2515
2527
  * Check if execution should be debounced
2516
2528
  */
2517
- shouldDebounce(node, now) {
2529
+ shouldDebounce(nodeId, now) {
2530
+ const node = this.graph.getNode(nodeId);
2531
+ if (!node)
2532
+ return false;
2518
2533
  const policy = node.policy ?? {};
2519
2534
  const lastScheduledAt = node.lastScheduledAt;
2520
2535
  return !!(policy.debounceMs &&
@@ -2524,80 +2539,78 @@ class NodeExecutor {
2524
2539
  /**
2525
2540
  * Handle debounced scheduling by replacing the latest queued item
2526
2541
  */
2527
- handleDebouncedSchedule(node, nodeId, now, runContextIds) {
2542
+ handleDebouncedSchedule(nodeId, now, runContextIds, reason) {
2543
+ const node = this.graph.getNode(nodeId);
2544
+ if (!node)
2545
+ return;
2528
2546
  // Decrement pendingQueued for any existing queued items before replacing
2529
2547
  if (node.queue.length > 0) {
2530
2548
  this.decrementQueuedForPlans(node.queue, nodeId);
2531
2549
  }
2532
- const effectiveInputs = this.getEffectiveInputs(nodeId);
2533
- const runSeq = this.graph.incrementNodeRunSeq(nodeId);
2534
- const runId = `${nodeId}:${runSeq}:${now}`;
2535
- const policySnapshot = node.policy ? { ...node.policy } : undefined;
2536
- const plan = {
2537
- runId,
2538
- effectiveInputs,
2539
- runContextIdsForRun: runContextIds,
2540
- timestamp: now,
2541
- policy: policySnapshot,
2542
- };
2543
- this.graph.replaceNodeQueue(nodeId, [plan]);
2550
+ const executionPlan = this.prepareExecutionPlan(nodeId, runContextIds, now, reason);
2551
+ this.graph.replaceNodeQueue(nodeId, [executionPlan]);
2544
2552
  }
2545
2553
  /**
2546
2554
  * Prepare execution plan with all necessary information
2547
2555
  */
2548
- prepareExecutionPlan(node, nodeId, runContextIds, now) {
2556
+ prepareExecutionPlan(nodeId, runContextIds, now, reason) {
2557
+ const node = this.graph.getNode(nodeId);
2549
2558
  this.graph.setNodeLastScheduledAt(nodeId, now);
2550
2559
  const runSeq = this.graph.incrementNodeRunSeq(nodeId);
2551
2560
  const runId = `${nodeId}:${runSeq}:${now}`;
2552
2561
  this.graph.setNodeLatestRunId(nodeId, runId);
2553
2562
  const effectiveInputs = this.getEffectiveInputs(nodeId);
2554
2563
  // Take a shallow snapshot of the current policy for this run
2555
- const policySnapshot = node.policy ? { ...node.policy } : undefined;
2564
+ const policySnapshot = node?.policy ? { ...node.policy } : undefined;
2556
2565
  return {
2557
2566
  runId,
2558
2567
  effectiveInputs,
2559
2568
  runContextIdsForRun: runContextIds,
2560
2569
  timestamp: now,
2561
2570
  policy: policySnapshot,
2571
+ reason,
2562
2572
  };
2563
2573
  }
2564
2574
  /**
2565
2575
  * Route execution to appropriate concurrency handler
2566
2576
  */
2567
- routeToConcurrencyHandler(node, nodeId, plan) {
2577
+ routeToConcurrencyHandler(nodeId, plan) {
2568
2578
  const mode = plan.policy?.asyncConcurrency ?? "switch";
2569
2579
  switch (mode) {
2570
2580
  case "drop":
2571
- this.handleDropMode(node, nodeId, plan);
2581
+ this.handleDropMode(nodeId, plan);
2572
2582
  break;
2573
2583
  case "queue":
2574
- this.handleQueueMode(node, nodeId, plan);
2584
+ this.handleQueueMode(nodeId, plan);
2575
2585
  break;
2576
2586
  case "switch":
2577
2587
  case "merge":
2578
2588
  default:
2579
- this.startRun(node, nodeId, plan);
2589
+ this.startRun(nodeId, plan);
2580
2590
  break;
2581
2591
  }
2582
2592
  }
2583
2593
  /**
2584
2594
  * Handle drop mode - drop execution if node is already running, otherwise start run
2585
2595
  */
2586
- handleDropMode(node, nodeId, plan) {
2596
+ handleDropMode(nodeId, plan) {
2597
+ const node = this.graph.getNode(nodeId);
2598
+ if (!node)
2599
+ return;
2587
2600
  // Drop if node is already running
2588
2601
  if (node.activeControllers.size > 0) {
2589
2602
  return; // Don't increment pendingCount if we're dropping this run
2590
2603
  }
2591
2604
  // Start run if node is not running
2592
- this.startRun(node, nodeId, plan);
2605
+ this.startRun(nodeId, plan);
2593
2606
  }
2594
2607
  /**
2595
2608
  * Handle queue mode - add to queue and process sequentially
2596
2609
  */
2597
- handleQueueMode(node, nodeId, plan) {
2610
+ handleQueueMode(nodeId, plan) {
2598
2611
  const maxQ = plan.policy?.maxQueue ?? 8;
2599
- const currentNode = this.graph.getNode(nodeId);
2600
- if (!currentNode)
2612
+ const node = this.graph.getNode(nodeId);
2613
+ if (!node)
2601
2614
  return;
2602
2615
  // Keep the originating run-context alive while work is queued by
2603
2616
  // incrementing queued counters for the plan's run-contexts.
@@ -2607,18 +2620,18 @@ class NodeExecutor {
2607
2620
  }
2608
2621
  }
2609
2622
  this.graph.addToNodeQueue(nodeId, plan);
2610
- if (currentNode.queue.length > maxQ) {
2623
+ if (node.queue.length > maxQ) {
2611
2624
  const dropped = this.graph.shiftNodeQueue(nodeId);
2612
2625
  if (dropped) {
2613
2626
  this.decrementQueuedForPlans(dropped, nodeId);
2614
2627
  }
2615
2628
  }
2616
- this.processQueue(node, nodeId);
2629
+ this.processQueue(nodeId);
2617
2630
  }
2618
2631
  /**
2619
2632
  * Process queued executions sequentially
2620
2633
  */
2621
- processQueue(node, nodeId) {
2634
+ processQueue(nodeId) {
2622
2635
  const processNext = () => {
2623
2636
  const node = this.graph.getNode(nodeId);
2624
2637
  if (!node)
@@ -2631,7 +2644,7 @@ class NodeExecutor {
2631
2644
  this.graph.setNodeLatestRunId(nodeId, next.runId);
2632
2645
  // Start the run first (which increments pendingNodes), then decrement
2633
2646
  // pendingQueued to ensure the run context stays alive.
2634
- this.startRun(node, nodeId, next, () => {
2647
+ this.startRun(nodeId, next, () => {
2635
2648
  setTimeout(processNext, 0);
2636
2649
  });
2637
2650
  this.decrementQueuedForPlans(next, nodeId);
@@ -2641,19 +2654,19 @@ class NodeExecutor {
2641
2654
  /**
2642
2655
  * Start a node execution run
2643
2656
  */
2644
- startRun(node, nodeId, plan, onDone) {
2657
+ startRun(nodeId, plan, onDone) {
2645
2658
  // Track run-contexts
2646
2659
  this.trackRunContextStart(nodeId, plan.runContextIdsForRun);
2647
2660
  // Setup execution controller
2648
- const controller = this.createExecutionController(nodeId, node, plan.runId);
2661
+ const controller = this.createExecutionController(nodeId, plan.runId);
2649
2662
  // Handle concurrency mode
2650
- this.applyConcurrencyMode(nodeId, node, controller, plan);
2663
+ this.applyConcurrencyMode(nodeId, controller, plan);
2651
2664
  // Setup timeout if needed
2652
- const timeoutId = this.setupTimeout(node, controller, plan);
2665
+ const timeoutId = this.setupTimeout(controller, plan);
2653
2666
  // Create execution context
2654
- const execCtx = this.createExecutionContext(nodeId, node, plan.effectiveInputs, plan.runId, controller.signal, plan.runContextIdsForRun, this.createEmitAndProgressHandlers(node, nodeId, plan));
2667
+ const execCtx = this.createExecutionContext(nodeId, plan.effectiveInputs, plan.runId, controller.signal, plan.runContextIdsForRun, this.createEmitAndProgressHandlers(nodeId, plan));
2655
2668
  // Execute
2656
- this.executeNode(node, nodeId, execCtx, plan, controller, timeoutId, onDone);
2669
+ this.executeNode(nodeId, execCtx, plan, controller, timeoutId, onDone);
2657
2670
  }
2658
2671
  /**
2659
2672
  * Track run-context start for pending nodes
@@ -2682,12 +2695,13 @@ class NodeExecutor {
2682
2695
  /**
2683
2696
  * Create execution controller and update node stats
2684
2697
  */
2685
- createExecutionController(nodeId, node, runId) {
2698
+ createExecutionController(nodeId, runId) {
2686
2699
  const controller = new AbortController();
2687
2700
  const now = Date.now();
2701
+ const node = this.graph.getNode(nodeId);
2688
2702
  this.graph.updateNodeStats(nodeId, {
2689
- runs: node.stats.runs + 1,
2690
- active: node.stats.active + 1,
2703
+ runs: (node?.stats.runs ?? 0) + 1,
2704
+ active: (node?.stats.active ?? 0) + 1,
2691
2705
  lastStartAt: now,
2692
2706
  progress: 0,
2693
2707
  });
@@ -2697,7 +2711,7 @@ class NodeExecutor {
2697
2711
  /**
2698
2712
  * Apply concurrency mode (switch mode aborts other controllers)
2699
2713
  */
2700
- applyConcurrencyMode(nodeId, node, controller, plan) {
2714
+ applyConcurrencyMode(nodeId, controller, plan) {
2701
2715
  const mode = plan.policy?.asyncConcurrency ?? "switch";
2702
2716
  if (mode === "switch") {
2703
2717
  const controllers = this.graph.getNodeControllers(nodeId);
@@ -2710,7 +2724,7 @@ class NodeExecutor {
2710
2724
  /**
2711
2725
  * Setup timeout for execution if configured
2712
2726
  */
2713
- setupTimeout(node, controller, plan) {
2727
+ setupTimeout(controller, plan) {
2714
2728
  const policy = plan.policy ?? {};
2715
2729
  if (policy.timeoutMs && policy.timeoutMs > 0) {
2716
2730
  return setTimeout(() => controller.abort("timeout"), policy.timeoutMs);
@@ -2720,7 +2734,7 @@ class NodeExecutor {
2720
2734
  /**
2721
2735
  * Create emit and progress handlers for execution context
2722
2736
  */
2723
- createEmitAndProgressHandlers(node, nodeId, plan) {
2737
+ createEmitAndProgressHandlers(nodeId, plan) {
2724
2738
  const policy = plan.policy ?? {};
2725
2739
  return {
2726
2740
  emitHandler: (handle, value) => {
@@ -2755,7 +2769,10 @@ class NodeExecutor {
2755
2769
  /**
2756
2770
  * Execute the node with retry logic and cleanup
2757
2771
  */
2758
- executeNode(node, nodeId, ctx, plan, controller, timeoutId, onDone) {
2772
+ executeNode(nodeId, ctx, plan, controller, timeoutId, onDone) {
2773
+ const node = this.graph.getNode(nodeId);
2774
+ if (!node)
2775
+ return;
2759
2776
  // Fire node-start event
2760
2777
  this.eventEmitter.emit("stats", {
2761
2778
  kind: "node-start",
@@ -2763,7 +2780,11 @@ class NodeExecutor {
2763
2780
  typeId: node.typeId,
2764
2781
  runId: plan.runId,
2765
2782
  });
2766
- ctx.log("debug", "node-start");
2783
+ ctx.log("debug", "node-start", {
2784
+ inputs: node.inputs,
2785
+ effectiveInputs: plan.effectiveInputs,
2786
+ reason: plan.reason,
2787
+ });
2767
2788
  const exec = async (attempt) => {
2768
2789
  let hadError = false;
2769
2790
  try {
@@ -2801,7 +2822,7 @@ class NodeExecutor {
2801
2822
  });
2802
2823
  }
2803
2824
  finally {
2804
- this.cleanupExecution(node, nodeId, ctx, plan, controller, timeoutId, hadError, onDone);
2825
+ this.cleanupExecution(nodeId, ctx, plan, controller, timeoutId, hadError, onDone);
2805
2826
  }
2806
2827
  };
2807
2828
  exec(0);
@@ -2809,7 +2830,7 @@ class NodeExecutor {
2809
2830
  /**
2810
2831
  * Cleanup after execution completes
2811
2832
  */
2812
- cleanupExecution(node, nodeId, ctx, plan, controller, timeoutId, hadError, onDone) {
2833
+ cleanupExecution(nodeId, ctx, plan, controller, timeoutId, hadError, onDone) {
2813
2834
  // Decrement pendingNodes count for all relevant run-contexts
2814
2835
  if (plan.runContextIdsForRun && plan.runContextIdsForRun.size > 0) {
2815
2836
  for (const id of plan.runContextIdsForRun) {
@@ -2823,19 +2844,19 @@ class NodeExecutor {
2823
2844
  if (timeoutId)
2824
2845
  clearTimeout(timeoutId);
2825
2846
  this.graph.removeNodeController(nodeId, controller);
2826
- const currentNode = this.graph.getNode(nodeId);
2827
- if (!currentNode)
2847
+ const node = this.graph.getNode(nodeId);
2848
+ if (!node)
2828
2849
  return;
2829
2850
  const controllers = this.graph.getNodeControllers(nodeId);
2830
2851
  const lastEndAt = Date.now();
2831
- const lastDurationMs = currentNode.stats.lastStartAt && lastEndAt
2832
- ? lastEndAt - currentNode.stats.lastStartAt
2852
+ const lastDurationMs = node.stats.lastStartAt && lastEndAt
2853
+ ? lastEndAt - node.stats.lastStartAt
2833
2854
  : undefined;
2834
2855
  this.graph.updateNodeStats(nodeId, {
2835
2856
  active: Math.max(0, controllers.size),
2836
2857
  lastEndAt,
2837
2858
  lastDurationMs,
2838
- lastError: hadError ? currentNode.stats.lastError : undefined,
2859
+ lastError: hadError ? node.stats.lastError : undefined,
2839
2860
  });
2840
2861
  // Track successful completion time (for detecting stale inputs)
2841
2862
  const isCancelled = controller.signal.aborted &&
@@ -2847,26 +2868,22 @@ class NodeExecutor {
2847
2868
  }
2848
2869
  // Only emit node-done if not cancelled (cancellation events emitted separately)
2849
2870
  if (!isCancelled) {
2850
- if (currentNode) {
2871
+ if (node) {
2851
2872
  this.eventEmitter.emit("stats", {
2852
2873
  kind: "node-done",
2853
2874
  nodeId,
2854
- typeId: currentNode.typeId,
2875
+ typeId: node.typeId,
2855
2876
  runId: plan.runId,
2856
- durationMs: currentNode.stats.lastDurationMs,
2877
+ durationMs: node.stats.lastDurationMs,
2857
2878
  });
2858
2879
  }
2859
2880
  }
2860
- if (currentNode) {
2881
+ if (node) {
2861
2882
  ctx.log("debug", "node-done", {
2862
- durationMs: currentNode.stats.lastDurationMs,
2883
+ durationMs: node.stats.lastDurationMs,
2884
+ outputs: node.outputs,
2863
2885
  hadError,
2864
- inputs: currentNode.inputs
2865
- ? structuredClone(currentNode.inputs)
2866
- : undefined,
2867
- outputs: currentNode.outputs
2868
- ? structuredClone(currentNode.outputs)
2869
- : undefined,
2886
+ reason: plan.reason,
2870
2887
  });
2871
2888
  }
2872
2889
  if (onDone)
@@ -2875,14 +2892,13 @@ class NodeExecutor {
2875
2892
  /**
2876
2893
  * Cancel all active runs for a node
2877
2894
  */
2878
- cancelNodeActiveRuns(node, reason) {
2879
- const nodeId = node.nodeId;
2895
+ cancelNodeActiveRuns(nodeId, reason) {
2896
+ const node = this.graph.getNode(nodeId);
2897
+ if (!node)
2898
+ return;
2880
2899
  const controllers = this.graph.getNodeControllers(nodeId);
2881
2900
  for (const controller of controllers) {
2882
- const currentNode = this.graph.getNode(nodeId);
2883
- if (!currentNode)
2884
- continue;
2885
- const runId = currentNode.controllerRunIds.get(controller);
2901
+ const runId = node.controllerRunIds.get(controller);
2886
2902
  if (runId) {
2887
2903
  // Track cancelled runIds for snapshot and user-cancelled operations
2888
2904
  // (to drop emits from cancelled runs)
@@ -2892,8 +2908,8 @@ class NodeExecutor {
2892
2908
  // Emit cancellation event
2893
2909
  this.eventEmitter.emit("stats", {
2894
2910
  kind: "node-done",
2895
- nodeId: currentNode.nodeId,
2896
- typeId: currentNode.typeId,
2911
+ nodeId: nodeId,
2912
+ typeId: node.typeId,
2897
2913
  runId,
2898
2914
  cancelled: true,
2899
2915
  });
@@ -2941,10 +2957,7 @@ class NodeExecutor {
2941
2957
  }
2942
2958
  // Cancel runs for all affected nodes
2943
2959
  for (const nodeId of toCancel) {
2944
- const node = this.graph.getNode(nodeId);
2945
- if (!node)
2946
- continue;
2947
- this.cancelNodeActiveRuns(node, reason);
2960
+ this.cancelNodeActiveRuns(nodeId, reason);
2948
2961
  const runSeq = this.graph.incrementNodeRunSeq(nodeId);
2949
2962
  const now = Date.now();
2950
2963
  const suffix = reason === "snapshot" ? "snapshot" : "cancelled";
@@ -3184,7 +3197,7 @@ class GraphRuntime {
3184
3197
  // However, if autoRun policy is set, nodes run automatically even in manual mode.
3185
3198
  if (anyChanged) {
3186
3199
  this.handleResolver.scheduleRecomputeHandles(nodeId);
3187
- this.executeNodeAutoRun(nodeId);
3200
+ this.executeNodeAutoRun(nodeId, { reason: "setInputs" });
3188
3201
  }
3189
3202
  }
3190
3203
  getOutput(nodeId, output) {
@@ -3195,7 +3208,7 @@ class GraphRuntime {
3195
3208
  this.graph.forEachNode((node) => {
3196
3209
  const effectiveInputs = this.nodeExecutor.getEffectiveInputs(node.nodeId);
3197
3210
  const ctrl = new AbortController();
3198
- const execCtx = this.nodeExecutor.createExecutionContext(node.nodeId, node, effectiveInputs, `${node.nodeId}:init`, ctrl.signal);
3211
+ const execCtx = this.nodeExecutor.createExecutionContext(node.nodeId, effectiveInputs, `${node.nodeId}:init`, ctrl.signal);
3199
3212
  if (node.lifecycle?.prepare) {
3200
3213
  execCtx.log("debug", "prepare-start");
3201
3214
  node.lifecycle.prepare(node.params ?? {}, execCtx);
@@ -3206,7 +3219,7 @@ class GraphRuntime {
3206
3219
  if (this.runMode === "auto" && invalidate) {
3207
3220
  for (const nodeId of this.graph.getNodeIds()) {
3208
3221
  if (this.graph.allInboundHaveValue(nodeId))
3209
- this.execute(nodeId);
3222
+ this.execute(nodeId, { reason: "launch" });
3210
3223
  }
3211
3224
  }
3212
3225
  if (startPaused) {
@@ -3221,7 +3234,7 @@ class GraphRuntime {
3221
3234
  if (this.isInvalidateEvent(event)) {
3222
3235
  // Check if node has all inbound inputs (required for execution)
3223
3236
  if (this.graph.allInboundHaveValue(nodeId)) {
3224
- this.execute(nodeId);
3237
+ this.execute(nodeId, { reason: "triggerExternal" });
3225
3238
  }
3226
3239
  return;
3227
3240
  }
@@ -3345,17 +3358,20 @@ class GraphRuntime {
3345
3358
  setTimeout(check, 10);
3346
3359
  });
3347
3360
  }
3348
- async runFromHereContext(startNodeId, options) {
3361
+ async runFromHereContext(startNodeId, opts) {
3349
3362
  const node = this.graph.getNode(startNodeId);
3350
3363
  if (!node)
3351
3364
  return;
3352
3365
  return new Promise((resolve) => {
3353
3366
  const id = this.runContextManager.createRunContext(startNodeId, {
3354
3367
  resolve,
3355
- ...options,
3368
+ ...opts,
3356
3369
  });
3357
3370
  this.graph.addNodeRunContextId(startNodeId, id);
3358
- this.execute(startNodeId, new Set([id]));
3371
+ this.execute(startNodeId, {
3372
+ runContextIds: new Set([id]),
3373
+ reason: opts?.reason ?? "runFromHereContext",
3374
+ });
3359
3375
  });
3360
3376
  }
3361
3377
  setRunMode(runMode) {
@@ -3402,7 +3418,7 @@ class GraphRuntime {
3402
3418
  }
3403
3419
  }
3404
3420
  }
3405
- executeNodeAutoRun(nodeId) {
3421
+ executeNodeAutoRun(nodeId, opts) {
3406
3422
  const node = this.graph.getNode(nodeId);
3407
3423
  const shouldAutoRun = this.runMode === "auto" || node?.policy?.autoRun === true;
3408
3424
  let runContextIdsToUse = undefined;
@@ -3412,7 +3428,10 @@ class GraphRuntime {
3412
3428
  ]);
3413
3429
  }
3414
3430
  if (shouldAutoRun && this.graph.allInboundHaveValue(nodeId)) {
3415
- this.execute(nodeId, runContextIdsToUse);
3431
+ this.execute(nodeId, {
3432
+ runContextIds: runContextIdsToUse,
3433
+ reason: opts?.reason ?? "executeNodeAutoRun",
3434
+ });
3416
3435
  }
3417
3436
  }
3418
3437
  copyOutputs(fromNodeId, toNodeId, options) {
@@ -3421,7 +3440,7 @@ class GraphRuntime {
3421
3440
  return;
3422
3441
  this.hydrate({ outputs: { [toNodeId]: { ...fromNode.outputs } } }, { invalidate: !options?.dry });
3423
3442
  this.handleResolver.scheduleRecomputeHandles(toNodeId);
3424
- this.executeNodeAutoRun(toNodeId);
3443
+ this.executeNodeAutoRun(toNodeId, { reason: "copyOutputs" });
3425
3444
  }
3426
3445
  hydrate(payload, opts) {
3427
3446
  const releasePause = this.requestPause();
@@ -3479,7 +3498,7 @@ class GraphRuntime {
3479
3498
  const node = this.graph.getNode(nodeId);
3480
3499
  if (!node)
3481
3500
  continue;
3482
- this.nodeExecutor.cancelNodeActiveRuns(node, "node-deleted");
3501
+ this.nodeExecutor.cancelNodeActiveRuns(nodeId, "node-deleted");
3483
3502
  this.runContextManager.cancelNodeInRunContexts(nodeId, true);
3484
3503
  node.runtime.onDeactivated?.();
3485
3504
  node.runtime.dispose?.();
@@ -3540,7 +3559,7 @@ class GraphRuntime {
3540
3559
  this.graph.setNode(n.nodeId, newNode);
3541
3560
  const effectiveInputs = this.nodeExecutor.getEffectiveInputs(newNode.nodeId);
3542
3561
  const ctrl = new AbortController();
3543
- const execCtx = this.nodeExecutor.createExecutionContext(newNode.nodeId, newNode, effectiveInputs, `${newNode.nodeId}:init`, ctrl.signal);
3562
+ const execCtx = this.nodeExecutor.createExecutionContext(newNode.nodeId, effectiveInputs, `${newNode.nodeId}:init`, ctrl.signal);
3544
3563
  if (newNode.lifecycle?.prepare) {
3545
3564
  execCtx.log("debug", "prepare-start");
3546
3565
  newNode.lifecycle.prepare(newNode.params ?? {}, execCtx);
@@ -3651,7 +3670,7 @@ class GraphRuntime {
3651
3670
  this.edgePropagator.clearArrayBuckets(nodeId);
3652
3671
  // Trigger handle resolution when inputs are removed
3653
3672
  this.handleResolver.scheduleRecomputeHandles(nodeId);
3654
- this.executeNodeAutoRun(nodeId);
3673
+ this.executeNodeAutoRun(nodeId, { reason: "graphUpdate" });
3655
3674
  }
3656
3675
  }
3657
3676
  // Propagate changes on edges added
@@ -3721,8 +3740,8 @@ class GraphRuntime {
3721
3740
  });
3722
3741
  this.graph.clear();
3723
3742
  }
3724
- execute(nodeId, runContextIds, canSkipHandleResolution) {
3725
- this.nodeExecutor.execute(nodeId, runContextIds, canSkipHandleResolution);
3743
+ execute(nodeId, opts) {
3744
+ this.nodeExecutor.execute(nodeId, opts);
3726
3745
  }
3727
3746
  propagate(srcNodeId, srcHandle, value, runContextIds) {
3728
3747
  this.edgePropagator.propagate(srcNodeId, srcHandle, value, runContextIds);
@@ -4085,6 +4104,7 @@ class LocalEngine {
4085
4104
  await this.graphRuntime.runFromHereContext(nodeId, {
4086
4105
  skipPropagateValues: options?.skipPropagateValues ?? false,
4087
4106
  propagate: false, // Don't schedule downstream nodes
4107
+ reason: "computeNode",
4088
4108
  });
4089
4109
  }
4090
4110
  /**
@@ -4093,7 +4113,9 @@ class LocalEngine {
4093
4113
  * Uses run-context system for dynamic graph updates.
4094
4114
  */
4095
4115
  async runFromHere(nodeId) {
4096
- await this.graphRuntime.runFromHereContext(nodeId);
4116
+ await this.graphRuntime.runFromHereContext(nodeId, {
4117
+ reason: "runFromHere",
4118
+ });
4097
4119
  }
4098
4120
  setRunMode(runMode) {
4099
4121
  this.graphRuntime.setRunMode(runMode);