@bian-womp/spark-graph 0.3.43 → 0.3.45

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
@@ -495,7 +495,8 @@ Registry.idCounter = 0;
495
495
  * This is the ONLY place where nodes, edges, and resolvedByNode are directly updated.
496
496
  */
497
497
  class Graph {
498
- constructor(registry) {
498
+ constructor(eventEmitter, registry) {
499
+ this.eventEmitter = eventEmitter;
499
500
  this.registry = registry;
500
501
  this.nodes = new Map();
501
502
  this.edges = [];
@@ -552,7 +553,7 @@ class Graph {
552
553
  /**
553
554
  * Update node inputs
554
555
  */
555
- updateNodeInput(nodeId, handle, value) {
556
+ updateNodeInput(nodeId, handle, value, calculated = true) {
556
557
  const node = this.getNodeMutable(nodeId);
557
558
  if (!node)
558
559
  return;
@@ -562,15 +563,16 @@ class Graph {
562
563
  else {
563
564
  node.inputs[handle] = value;
564
565
  }
565
- }
566
- /**
567
- * Delete a node input handle
568
- */
569
- deleteNodeInput(nodeId, handle) {
570
- const node = this.nodes.get(nodeId);
571
- if (!node)
572
- return;
573
- delete node.inputs[handle];
566
+ if (calculated) {
567
+ this.updateNodeLastInputAt(nodeId, handle, Date.now());
568
+ }
569
+ this.eventEmitter.emit("value", {
570
+ nodeId,
571
+ handle,
572
+ value,
573
+ io: "input",
574
+ runtimeTypeId: unwrapTypeId(value),
575
+ });
574
576
  }
575
577
  /**
576
578
  * Update node outputs
@@ -579,7 +581,19 @@ class Graph {
579
581
  const node = this.nodes.get(nodeId);
580
582
  if (!node)
581
583
  return;
582
- node.outputs[handle] = value;
584
+ if (value === undefined) {
585
+ delete node.outputs[handle];
586
+ }
587
+ else {
588
+ node.outputs[handle] = value;
589
+ }
590
+ this.eventEmitter.emit("value", {
591
+ nodeId,
592
+ handle,
593
+ value,
594
+ io: "output",
595
+ runtimeTypeId: unwrapTypeId(value),
596
+ });
583
597
  }
584
598
  /**
585
599
  * Update node state
@@ -652,9 +666,8 @@ class Graph {
652
666
  const node = this.nodes.get(nodeId);
653
667
  if (!node)
654
668
  return;
655
- if (!node.lastInputAt) {
669
+ if (!node.lastInputAt)
656
670
  node.lastInputAt = {};
657
- }
658
671
  node.lastInputAt[handle] = timestamp;
659
672
  }
660
673
  /**
@@ -1977,13 +1990,6 @@ class EdgePropagator {
1977
1990
  return false;
1978
1991
  }
1979
1992
  this.graph.updateNodeOutput(srcNodeId, srcHandle, value);
1980
- this.eventEmitter.emit("value", {
1981
- nodeId: srcNodeId,
1982
- handle: srcHandle,
1983
- value,
1984
- io: "output",
1985
- runtimeTypeId: unwrapTypeId(value),
1986
- });
1987
1993
  return true;
1988
1994
  }
1989
1995
  /**
@@ -2205,16 +2211,7 @@ class EdgePropagator {
2205
2211
  * Set target input value and emit event
2206
2212
  */
2207
2213
  setTargetInput(edge, dstNode, value) {
2208
- const now = Date.now();
2209
2214
  this.graph.updateNodeInput(edge.target.nodeId, edge.target.handle, value);
2210
- this.graph.updateNodeLastInputAt(edge.target.nodeId, edge.target.handle, now);
2211
- this.eventEmitter.emit("value", {
2212
- nodeId: edge.target.nodeId,
2213
- handle: edge.target.handle,
2214
- value,
2215
- io: "input",
2216
- runtimeTypeId: unwrapTypeId(value),
2217
- });
2218
2215
  this.handleResolver.scheduleRecomputeHandles(edge.target.nodeId);
2219
2216
  }
2220
2217
  /**
@@ -2430,7 +2427,7 @@ class NodeExecutor {
2430
2427
  return;
2431
2428
  const runMode = this.runtime.getRunMode();
2432
2429
  if (!runMode) {
2433
- console.warn("NodeExecutor.execute: no runMode, skipping execution");
2430
+ console.trace("NodeExecutor.execute: no runMode, skipping execution");
2434
2431
  return;
2435
2432
  }
2436
2433
  // In manual mode, require runContextIds unless autoRun policy is set
@@ -2444,12 +2441,12 @@ class NodeExecutor {
2444
2441
  ]);
2445
2442
  }
2446
2443
  else {
2447
- console.warn("NodeExecutor.execute: no runContextIds provided in manual mode, skipping execution");
2444
+ console.trace("NodeExecutor.execute: no runContextIds provided in manual mode, skipping execution");
2448
2445
  return;
2449
2446
  }
2450
2447
  }
2451
2448
  if (runMode === "auto" && runContextIds && runContextIds.size > 0) {
2452
- console.warn("NodeExecutor.execute: runContextIds provided in auto mode, ignoring");
2449
+ console.trace("NodeExecutor.execute: runContextIds provided in auto mode, ignoring");
2453
2450
  runContextIds = undefined;
2454
2451
  }
2455
2452
  // Early validation for auto-mode paused state
@@ -3024,8 +3021,8 @@ class GraphRuntime {
3024
3021
  this.pauseRefCount = 0;
3025
3022
  this.persistentPauseToken = null;
3026
3023
  // Initialize components
3027
- this.graph = new Graph();
3028
3024
  this.eventEmitter = new EventEmitter();
3025
+ this.graph = new Graph(this.eventEmitter);
3029
3026
  this.runContextManager = new RunContextManager(this.graph, "debug");
3030
3027
  this.handleResolver = new HandleResolver(this.graph, this.eventEmitter, this.runContextManager, this);
3031
3028
  this.edgePropagator = new EdgePropagator(this.graph, this.eventEmitter, this.runContextManager, this.handleResolver, this);
@@ -3135,7 +3132,7 @@ class GraphRuntime {
3135
3132
  if (hasInbound)
3136
3133
  continue;
3137
3134
  // Validate input value against declared type
3138
- if (value !== undefined && registry) {
3135
+ if (registry) {
3139
3136
  const desc = registry.nodes.get(node.typeId);
3140
3137
  const resolved = this.graph.getResolvedHandles(nodeId);
3141
3138
  // Get declared types (may be union); prefer resolved handles over registry statics
@@ -3157,7 +3154,7 @@ class GraphRuntime {
3157
3154
  return true;
3158
3155
  return typeDesc.validate(value);
3159
3156
  });
3160
- if (!isValidForAny) {
3157
+ if (value !== undefined && !isValidForAny) {
3161
3158
  const typeLabel = typeIds.join("|");
3162
3159
  const errorMessage = `Invalid value for input ${nodeId}.${handle} (type ${typeLabel}): ${JSON.stringify(value)}`;
3163
3160
  this.eventEmitter.emit("error", {
@@ -3186,11 +3183,7 @@ class GraphRuntime {
3186
3183
  // However, if autoRun policy is set, nodes run automatically even in manual mode.
3187
3184
  if (anyChanged) {
3188
3185
  this.handleResolver.scheduleRecomputeHandles(nodeId);
3189
- const node = this.graph.getNode(nodeId);
3190
- const shouldAutoRun = this.runMode === "auto" || node?.policy?.autoRun === true;
3191
- if (shouldAutoRun && this.graph.allInboundHaveValue(nodeId)) {
3192
- this.execute(nodeId);
3193
- }
3186
+ this.executeNodeAutoRun(nodeId);
3194
3187
  }
3195
3188
  }
3196
3189
  getOutput(nodeId, output) {
@@ -3405,17 +3398,28 @@ class GraphRuntime {
3405
3398
  }
3406
3399
  }
3407
3400
  }
3401
+ executeNodeAutoRun(nodeId) {
3402
+ const node = this.graph.getNode(nodeId);
3403
+ const shouldAutoRun = this.runMode === "auto" || node?.policy?.autoRun === true;
3404
+ let runContextIdsToUse = undefined;
3405
+ if (this.runMode === "manual") {
3406
+ runContextIdsToUse = new Set([
3407
+ this.runContextManager.createRunContext(nodeId, undefined, {
3408
+ propagate: false,
3409
+ }),
3410
+ ]);
3411
+ }
3412
+ if (shouldAutoRun && this.graph.allInboundHaveValue(nodeId)) {
3413
+ this.execute(nodeId, runContextIdsToUse);
3414
+ }
3415
+ }
3408
3416
  copyOutputs(fromNodeId, toNodeId, options) {
3409
3417
  const fromNode = this.getNodeData(fromNodeId);
3410
3418
  if (!fromNode?.outputs)
3411
3419
  return;
3412
3420
  this.hydrate({ outputs: { [toNodeId]: { ...fromNode.outputs } } }, { invalidate: !options?.dry });
3413
3421
  this.handleResolver.scheduleRecomputeHandles(toNodeId);
3414
- const node = this.graph.getNode(toNodeId);
3415
- const shouldAutoRun = this.runMode === "auto" || node?.policy?.autoRun === true;
3416
- if (shouldAutoRun && this.graph.allInboundHaveValue(toNodeId)) {
3417
- this.execute(toNodeId);
3418
- }
3422
+ this.executeNodeAutoRun(toNodeId);
3419
3423
  }
3420
3424
  hydrate(payload, opts) {
3421
3425
  const releasePause = this.requestPause();
@@ -3432,14 +3436,7 @@ class GraphRuntime {
3432
3436
  const clonedValue = structuredClone(v);
3433
3437
  const same = valuesEqual(prev, clonedValue);
3434
3438
  if (!same) {
3435
- this.graph.updateNodeInput(nodeId, h, clonedValue);
3436
- this.eventEmitter.emit("value", {
3437
- nodeId,
3438
- handle: h,
3439
- value: clonedValue,
3440
- io: "input",
3441
- runtimeTypeId: unwrapTypeId(clonedValue),
3442
- });
3439
+ this.graph.updateNodeInput(nodeId, h, clonedValue, false);
3443
3440
  nodeChanged = true;
3444
3441
  }
3445
3442
  }
@@ -3454,13 +3451,6 @@ class GraphRuntime {
3454
3451
  for (const [h, v] of Object.entries(map || {})) {
3455
3452
  const clonedValue = structuredClone(v);
3456
3453
  this.graph.updateNodeOutput(nodeId, h, clonedValue);
3457
- this.eventEmitter.emit("value", {
3458
- nodeId,
3459
- handle: h,
3460
- value: clonedValue,
3461
- io: "output",
3462
- runtimeTypeId: unwrapTypeId(clonedValue),
3463
- });
3464
3454
  }
3465
3455
  }
3466
3456
  // Trigger handle resolution for nodes with changed inputs
@@ -3650,7 +3640,7 @@ class GraphRuntime {
3650
3640
  if (!currSet.has(handle)) {
3651
3641
  const node = this.graph.getNode(nodeId);
3652
3642
  if (node && handle in node.inputs) {
3653
- this.graph.deleteNodeInput(nodeId, handle);
3643
+ this.graph.updateNodeInput(nodeId, handle, undefined);
3654
3644
  changed = true;
3655
3645
  }
3656
3646
  }
@@ -3659,11 +3649,7 @@ class GraphRuntime {
3659
3649
  this.edgePropagator.clearArrayBuckets(nodeId);
3660
3650
  // Trigger handle resolution when inputs are removed
3661
3651
  this.handleResolver.scheduleRecomputeHandles(nodeId);
3662
- const node = this.graph.getNode(nodeId);
3663
- const shouldAutoRun = this.runMode === "auto" || node?.policy?.autoRun === true;
3664
- if (shouldAutoRun && this.graph.allInboundHaveValue(nodeId)) {
3665
- this.execute(nodeId);
3666
- }
3652
+ this.executeNodeAutoRun(nodeId);
3667
3653
  }
3668
3654
  }
3669
3655
  // Propagate changes on edges added