@bian-womp/spark-graph 0.3.49 → 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 -108
  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 -108
  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,27 +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
- formatJson: true,
2863
- durationMs: currentNode.stats.lastDurationMs,
2883
+ durationMs: node.stats.lastDurationMs,
2884
+ outputs: node.outputs,
2864
2885
  hadError,
2865
- inputs: currentNode.inputs
2866
- ? structuredClone(currentNode.inputs)
2867
- : undefined,
2868
- outputs: currentNode.outputs
2869
- ? structuredClone(currentNode.outputs)
2870
- : undefined,
2886
+ reason: plan.reason,
2871
2887
  });
2872
2888
  }
2873
2889
  if (onDone)
@@ -2876,14 +2892,13 @@ class NodeExecutor {
2876
2892
  /**
2877
2893
  * Cancel all active runs for a node
2878
2894
  */
2879
- cancelNodeActiveRuns(node, reason) {
2880
- const nodeId = node.nodeId;
2895
+ cancelNodeActiveRuns(nodeId, reason) {
2896
+ const node = this.graph.getNode(nodeId);
2897
+ if (!node)
2898
+ return;
2881
2899
  const controllers = this.graph.getNodeControllers(nodeId);
2882
2900
  for (const controller of controllers) {
2883
- const currentNode = this.graph.getNode(nodeId);
2884
- if (!currentNode)
2885
- continue;
2886
- const runId = currentNode.controllerRunIds.get(controller);
2901
+ const runId = node.controllerRunIds.get(controller);
2887
2902
  if (runId) {
2888
2903
  // Track cancelled runIds for snapshot and user-cancelled operations
2889
2904
  // (to drop emits from cancelled runs)
@@ -2893,8 +2908,8 @@ class NodeExecutor {
2893
2908
  // Emit cancellation event
2894
2909
  this.eventEmitter.emit("stats", {
2895
2910
  kind: "node-done",
2896
- nodeId: currentNode.nodeId,
2897
- typeId: currentNode.typeId,
2911
+ nodeId: nodeId,
2912
+ typeId: node.typeId,
2898
2913
  runId,
2899
2914
  cancelled: true,
2900
2915
  });
@@ -2942,10 +2957,7 @@ class NodeExecutor {
2942
2957
  }
2943
2958
  // Cancel runs for all affected nodes
2944
2959
  for (const nodeId of toCancel) {
2945
- const node = this.graph.getNode(nodeId);
2946
- if (!node)
2947
- continue;
2948
- this.cancelNodeActiveRuns(node, reason);
2960
+ this.cancelNodeActiveRuns(nodeId, reason);
2949
2961
  const runSeq = this.graph.incrementNodeRunSeq(nodeId);
2950
2962
  const now = Date.now();
2951
2963
  const suffix = reason === "snapshot" ? "snapshot" : "cancelled";
@@ -3185,7 +3197,7 @@ class GraphRuntime {
3185
3197
  // However, if autoRun policy is set, nodes run automatically even in manual mode.
3186
3198
  if (anyChanged) {
3187
3199
  this.handleResolver.scheduleRecomputeHandles(nodeId);
3188
- this.executeNodeAutoRun(nodeId);
3200
+ this.executeNodeAutoRun(nodeId, { reason: "setInputs" });
3189
3201
  }
3190
3202
  }
3191
3203
  getOutput(nodeId, output) {
@@ -3196,7 +3208,7 @@ class GraphRuntime {
3196
3208
  this.graph.forEachNode((node) => {
3197
3209
  const effectiveInputs = this.nodeExecutor.getEffectiveInputs(node.nodeId);
3198
3210
  const ctrl = new AbortController();
3199
- 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);
3200
3212
  if (node.lifecycle?.prepare) {
3201
3213
  execCtx.log("debug", "prepare-start");
3202
3214
  node.lifecycle.prepare(node.params ?? {}, execCtx);
@@ -3207,7 +3219,7 @@ class GraphRuntime {
3207
3219
  if (this.runMode === "auto" && invalidate) {
3208
3220
  for (const nodeId of this.graph.getNodeIds()) {
3209
3221
  if (this.graph.allInboundHaveValue(nodeId))
3210
- this.execute(nodeId);
3222
+ this.execute(nodeId, { reason: "launch" });
3211
3223
  }
3212
3224
  }
3213
3225
  if (startPaused) {
@@ -3222,7 +3234,7 @@ class GraphRuntime {
3222
3234
  if (this.isInvalidateEvent(event)) {
3223
3235
  // Check if node has all inbound inputs (required for execution)
3224
3236
  if (this.graph.allInboundHaveValue(nodeId)) {
3225
- this.execute(nodeId);
3237
+ this.execute(nodeId, { reason: "triggerExternal" });
3226
3238
  }
3227
3239
  return;
3228
3240
  }
@@ -3346,17 +3358,20 @@ class GraphRuntime {
3346
3358
  setTimeout(check, 10);
3347
3359
  });
3348
3360
  }
3349
- async runFromHereContext(startNodeId, options) {
3361
+ async runFromHereContext(startNodeId, opts) {
3350
3362
  const node = this.graph.getNode(startNodeId);
3351
3363
  if (!node)
3352
3364
  return;
3353
3365
  return new Promise((resolve) => {
3354
3366
  const id = this.runContextManager.createRunContext(startNodeId, {
3355
3367
  resolve,
3356
- ...options,
3368
+ ...opts,
3357
3369
  });
3358
3370
  this.graph.addNodeRunContextId(startNodeId, id);
3359
- this.execute(startNodeId, new Set([id]));
3371
+ this.execute(startNodeId, {
3372
+ runContextIds: new Set([id]),
3373
+ reason: opts?.reason ?? "runFromHereContext",
3374
+ });
3360
3375
  });
3361
3376
  }
3362
3377
  setRunMode(runMode) {
@@ -3403,7 +3418,7 @@ class GraphRuntime {
3403
3418
  }
3404
3419
  }
3405
3420
  }
3406
- executeNodeAutoRun(nodeId) {
3421
+ executeNodeAutoRun(nodeId, opts) {
3407
3422
  const node = this.graph.getNode(nodeId);
3408
3423
  const shouldAutoRun = this.runMode === "auto" || node?.policy?.autoRun === true;
3409
3424
  let runContextIdsToUse = undefined;
@@ -3413,7 +3428,10 @@ class GraphRuntime {
3413
3428
  ]);
3414
3429
  }
3415
3430
  if (shouldAutoRun && this.graph.allInboundHaveValue(nodeId)) {
3416
- this.execute(nodeId, runContextIdsToUse);
3431
+ this.execute(nodeId, {
3432
+ runContextIds: runContextIdsToUse,
3433
+ reason: opts?.reason ?? "executeNodeAutoRun",
3434
+ });
3417
3435
  }
3418
3436
  }
3419
3437
  copyOutputs(fromNodeId, toNodeId, options) {
@@ -3422,7 +3440,7 @@ class GraphRuntime {
3422
3440
  return;
3423
3441
  this.hydrate({ outputs: { [toNodeId]: { ...fromNode.outputs } } }, { invalidate: !options?.dry });
3424
3442
  this.handleResolver.scheduleRecomputeHandles(toNodeId);
3425
- this.executeNodeAutoRun(toNodeId);
3443
+ this.executeNodeAutoRun(toNodeId, { reason: "copyOutputs" });
3426
3444
  }
3427
3445
  hydrate(payload, opts) {
3428
3446
  const releasePause = this.requestPause();
@@ -3480,7 +3498,7 @@ class GraphRuntime {
3480
3498
  const node = this.graph.getNode(nodeId);
3481
3499
  if (!node)
3482
3500
  continue;
3483
- this.nodeExecutor.cancelNodeActiveRuns(node, "node-deleted");
3501
+ this.nodeExecutor.cancelNodeActiveRuns(nodeId, "node-deleted");
3484
3502
  this.runContextManager.cancelNodeInRunContexts(nodeId, true);
3485
3503
  node.runtime.onDeactivated?.();
3486
3504
  node.runtime.dispose?.();
@@ -3541,7 +3559,7 @@ class GraphRuntime {
3541
3559
  this.graph.setNode(n.nodeId, newNode);
3542
3560
  const effectiveInputs = this.nodeExecutor.getEffectiveInputs(newNode.nodeId);
3543
3561
  const ctrl = new AbortController();
3544
- 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);
3545
3563
  if (newNode.lifecycle?.prepare) {
3546
3564
  execCtx.log("debug", "prepare-start");
3547
3565
  newNode.lifecycle.prepare(newNode.params ?? {}, execCtx);
@@ -3652,7 +3670,7 @@ class GraphRuntime {
3652
3670
  this.edgePropagator.clearArrayBuckets(nodeId);
3653
3671
  // Trigger handle resolution when inputs are removed
3654
3672
  this.handleResolver.scheduleRecomputeHandles(nodeId);
3655
- this.executeNodeAutoRun(nodeId);
3673
+ this.executeNodeAutoRun(nodeId, { reason: "graphUpdate" });
3656
3674
  }
3657
3675
  }
3658
3676
  // Propagate changes on edges added
@@ -3722,8 +3740,8 @@ class GraphRuntime {
3722
3740
  });
3723
3741
  this.graph.clear();
3724
3742
  }
3725
- execute(nodeId, runContextIds, canSkipHandleResolution) {
3726
- this.nodeExecutor.execute(nodeId, runContextIds, canSkipHandleResolution);
3743
+ execute(nodeId, opts) {
3744
+ this.nodeExecutor.execute(nodeId, opts);
3727
3745
  }
3728
3746
  propagate(srcNodeId, srcHandle, value, runContextIds) {
3729
3747
  this.edgePropagator.propagate(srcNodeId, srcHandle, value, runContextIds);
@@ -4086,6 +4104,7 @@ class LocalEngine {
4086
4104
  await this.graphRuntime.runFromHereContext(nodeId, {
4087
4105
  skipPropagateValues: options?.skipPropagateValues ?? false,
4088
4106
  propagate: false, // Don't schedule downstream nodes
4107
+ reason: "computeNode",
4089
4108
  });
4090
4109
  }
4091
4110
  /**
@@ -4094,7 +4113,9 @@ class LocalEngine {
4094
4113
  * Uses run-context system for dynamic graph updates.
4095
4114
  */
4096
4115
  async runFromHere(nodeId) {
4097
- await this.graphRuntime.runFromHereContext(nodeId);
4116
+ await this.graphRuntime.runFromHereContext(nodeId, {
4117
+ reason: "runFromHere",
4118
+ });
4098
4119
  }
4099
4120
  setRunMode(runMode) {
4100
4121
  this.graphRuntime.setRunMode(runMode);