@bian-womp/spark-graph 0.2.46 → 0.2.48

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
@@ -566,7 +566,7 @@ class GraphRuntime {
566
566
  if (!this.paused) {
567
567
  // Only schedule if all inbound inputs are present (or there are none)
568
568
  if (anyChanged && this.allInboundHaveValue(nodeId))
569
- this.scheduleInputsChanged(nodeId);
569
+ this.scheduleInputsChangedInternal(nodeId);
570
570
  // Recompute dynamic handles for this node when its direct inputs change
571
571
  if (anyChanged)
572
572
  this.scheduleRecomputeHandles(nodeId);
@@ -639,7 +639,31 @@ class GraphRuntime {
639
639
  },
640
640
  };
641
641
  }
642
- scheduleInputsChanged(nodeId) {
642
+ createExecutionContext(nodeId, node, inputs, runId, abortSignal, options) {
643
+ const emitHandler = options?.emitHandler ??
644
+ ((handle, value) => this.propagate(nodeId, handle, value));
645
+ const reportProgress = options?.reportProgress ??
646
+ ((p) => {
647
+ node.stats.progress = Math.max(0, Math.min(1, Number(p) || 0));
648
+ });
649
+ return {
650
+ state: node.state,
651
+ setState: (next) => Object.assign(node.state, next),
652
+ emit: emitHandler,
653
+ invalidateDownstream: () => this.invalidateDownstreamInternal(nodeId),
654
+ scheduleInputsChanged: () => {
655
+ if (this.allInboundHaveValue(nodeId)) {
656
+ this.scheduleInputsChangedInternal(nodeId);
657
+ }
658
+ },
659
+ getInput: (handle) => inputs[handle],
660
+ environment: this.environment,
661
+ runId,
662
+ abortSignal,
663
+ reportProgress,
664
+ };
665
+ }
666
+ scheduleInputsChangedInternal(nodeId) {
643
667
  const node = this.nodes.get(nodeId);
644
668
  if (!node)
645
669
  return;
@@ -681,24 +705,13 @@ class GraphRuntime {
681
705
  if (policy.timeoutMs && policy.timeoutMs > 0) {
682
706
  timeoutId = setTimeout(() => controller.abort("timeout"), policy.timeoutMs);
683
707
  }
684
- const ctx = {
685
- state: node.state,
686
- environment: this.environment,
687
- runId,
688
- abortSignal: controller.signal,
689
- setState: (next) => Object.assign(node.state, next),
690
- emit: (handle, value) => {
708
+ const ctx = this.createExecutionContext(nodeId, node, capturedInputs, runId, controller.signal, {
709
+ emitHandler: (handle, value) => {
691
710
  const m = policy.asyncConcurrency ?? "switch";
692
711
  if (m !== "merge" && runId !== node.latestRunId)
693
712
  return;
694
713
  this.propagate(nodeId, handle, value);
695
714
  },
696
- invalidateDownstream: () => this.invalidateDownstream(nodeId),
697
- scheduleInputsChanged: () => {
698
- if (this.allInboundHaveValue(nodeId)) {
699
- this.scheduleInputsChanged(nodeId);
700
- }
701
- },
702
715
  reportProgress: (p) => {
703
716
  node.stats.progress = Math.max(0, Math.min(1, Number(p) || 0));
704
717
  this.emit("stats", {
@@ -708,10 +721,11 @@ class GraphRuntime {
708
721
  progress: node.stats.progress,
709
722
  });
710
723
  },
711
- };
724
+ });
712
725
  const exec = async (attempt) => {
713
726
  let hadError = false;
714
727
  try {
728
+ node.lifecycle?.prepare?.(node.params ?? {}, ctx);
715
729
  await node.runtime.onInputsChanged?.(capturedInputs, ctx);
716
730
  }
717
731
  catch (err) {
@@ -853,7 +867,7 @@ class GraphRuntime {
853
867
  }
854
868
  return effective;
855
869
  }
856
- invalidateDownstream(nodeId) {
870
+ invalidateDownstreamInternal(nodeId) {
857
871
  // Notifies dependents; for now we propagate current outputs
858
872
  for (const e of this.edges.filter((e) => e.source.nodeId === nodeId)) {
859
873
  const value = this.getOutput(nodeId, e.source.handle);
@@ -946,7 +960,7 @@ class GraphRuntime {
946
960
  // Recompute dynamic handles for the destination node on input change
947
961
  this.scheduleRecomputeHandles(e.target.nodeId);
948
962
  if (!this.paused && this.allInboundHaveValue(e.target.nodeId))
949
- this.scheduleInputsChanged(e.target.nodeId);
963
+ this.scheduleInputsChangedInternal(e.target.nodeId);
950
964
  }
951
965
  };
952
966
  if (e.convertAsync) {
@@ -1148,22 +1162,22 @@ class GraphRuntime {
1148
1162
  }
1149
1163
  }
1150
1164
  // Invalidate downstream for this node so UI refreshes
1151
- this.invalidateDownstream(nodeId);
1165
+ this.invalidateDownstreamInternal(nodeId);
1152
1166
  }
1153
1167
  launch(invalidate = false) {
1154
1168
  // call onActivated for nodes that implement it
1155
1169
  for (const node of this.nodes.values()) {
1156
- node.lifecycle?.init?.(node.params ?? {}, {
1157
- state: node.state,
1158
- setState: (next) => Object.assign(node.state, next),
1159
- });
1170
+ const effectiveInputs = this.getEffectiveInputs(node.nodeId);
1171
+ const ctrl = new AbortController();
1172
+ const ctx = this.createExecutionContext(node.nodeId, node, effectiveInputs, `${node.nodeId}:init`, ctrl.signal);
1173
+ node.lifecycle?.prepare?.(node.params ?? {}, ctx);
1160
1174
  node.runtime.onActivated?.();
1161
1175
  }
1162
1176
  if (invalidate) {
1163
1177
  // After activation, schedule nodes that have all inbound inputs present
1164
1178
  for (const nodeId of this.nodes.keys()) {
1165
1179
  if (this.allInboundHaveValue(nodeId))
1166
- this.scheduleInputsChanged(nodeId);
1180
+ this.scheduleInputsChangedInternal(nodeId);
1167
1181
  }
1168
1182
  }
1169
1183
  }
@@ -1176,9 +1190,9 @@ class GraphRuntime {
1176
1190
  typeof event === "object" &&
1177
1191
  event.type === "invalidate") {
1178
1192
  if (this.allInboundHaveValue(nodeId))
1179
- this.scheduleInputsChanged(nodeId);
1193
+ this.scheduleInputsChangedInternal(nodeId);
1180
1194
  else
1181
- this.invalidateDownstream(nodeId);
1195
+ this.invalidateDownstreamInternal(nodeId);
1182
1196
  }
1183
1197
  else {
1184
1198
  node.runtime.onExternalEvent?.(event, node.state);
@@ -1281,11 +1295,11 @@ class GraphRuntime {
1281
1295
  resume() {
1282
1296
  this.paused = false;
1283
1297
  }
1284
- __unsafe_invalidateDownstream(nodeId) {
1285
- this.invalidateDownstream(nodeId);
1298
+ invalidateDownstream(nodeId) {
1299
+ this.invalidateDownstreamInternal(nodeId);
1286
1300
  }
1287
- __unsafe_scheduleInputsChanged(nodeId) {
1288
- this.scheduleInputsChanged(nodeId);
1301
+ scheduleInputsChanged(nodeId) {
1302
+ this.scheduleInputsChangedInternal(nodeId);
1289
1303
  }
1290
1304
  // Hydrate inputs/outputs without triggering computation; optionally re-emit outputs downstream
1291
1305
  hydrate(payload, opts) {
@@ -1399,10 +1413,10 @@ class GraphRuntime {
1399
1413
  };
1400
1414
  this.nodes.set(n.nodeId, rn);
1401
1415
  // Activate new node
1402
- rn.lifecycle?.init?.(rn.params ?? {}, {
1403
- state: rn.state,
1404
- setState: (next) => Object.assign(rn.state, next),
1405
- });
1416
+ const effectiveInputs = this.getEffectiveInputs(rn.nodeId);
1417
+ const ctrl = new AbortController();
1418
+ const ctx = this.createExecutionContext(rn.nodeId, rn, effectiveInputs, `${rn.nodeId}:init`, ctrl.signal);
1419
+ rn.lifecycle?.prepare?.(rn.params ?? {}, ctx);
1406
1420
  rn.runtime.onActivated?.();
1407
1421
  }
1408
1422
  else {
@@ -1482,7 +1496,7 @@ class GraphRuntime {
1482
1496
  if (bucketsForNode.size === 0)
1483
1497
  this.arrayInputBuckets.delete(nodeId);
1484
1498
  }
1485
- this.scheduleInputsChanged(nodeId);
1499
+ this.scheduleInputsChangedInternal(nodeId);
1486
1500
  }
1487
1501
  }
1488
1502
  // Re-emit outputs when per-handle target sets change (precise and simple)
@@ -1525,7 +1539,7 @@ class GraphRuntime {
1525
1539
  if (val !== undefined)
1526
1540
  this.propagate(nodeId, handle, val);
1527
1541
  else if (this.allInboundHaveValue(nodeId))
1528
- this.scheduleInputsChanged(nodeId);
1542
+ this.scheduleInputsChangedInternal(nodeId);
1529
1543
  }
1530
1544
  }
1531
1545
  }
@@ -1905,7 +1919,7 @@ class BatchedEngine extends AbstractEngine {
1905
1919
  this.dirtyNodes.clear();
1906
1920
  this.graphRuntime.resume();
1907
1921
  for (const n of nodes)
1908
- this.graphRuntime.__unsafe_scheduleInputsChanged(n);
1922
+ this.graphRuntime.scheduleInputsChanged(n);
1909
1923
  await this.graphRuntime.whenIdle();
1910
1924
  this.graphRuntime.pause();
1911
1925
  }
@@ -1926,7 +1940,7 @@ class PullEngine extends AbstractEngine {
1926
1940
  // Pull API
1927
1941
  async computeNode(nodeId) {
1928
1942
  this.graphRuntime.resume();
1929
- this.graphRuntime.__unsafe_scheduleInputsChanged(nodeId);
1943
+ this.graphRuntime.scheduleInputsChanged(nodeId);
1930
1944
  await this.graphRuntime.whenIdle();
1931
1945
  this.graphRuntime.pause();
1932
1946
  }
@@ -1955,7 +1969,7 @@ class HybridEngine extends AbstractEngine {
1955
1969
  const nodes = Array.from(this.dirtyNodes);
1956
1970
  this.dirtyNodes.clear();
1957
1971
  for (const n of nodes)
1958
- this.graphRuntime.__unsafe_scheduleInputsChanged(n);
1972
+ this.graphRuntime.scheduleInputsChanged(n);
1959
1973
  if (this.flushTimer) {
1960
1974
  clearTimeout(this.flushTimer);
1961
1975
  this.flushTimer = undefined;
@@ -1985,14 +1999,14 @@ class HybridEngine extends AbstractEngine {
1985
1999
  const nodes = Array.from(this.dirtyNodes);
1986
2000
  this.dirtyNodes.clear();
1987
2001
  for (const n of nodes)
1988
- this.graphRuntime.__unsafe_scheduleInputsChanged(n);
2002
+ this.graphRuntime.scheduleInputsChanged(n);
1989
2003
  this.flushTimer = undefined;
1990
2004
  }, windowMs);
1991
2005
  }
1992
2006
  super.setInputs(nodeId, inputs);
1993
2007
  this.dirtyNodes.add(nodeId);
1994
2008
  if (!this.batching)
1995
- this.graphRuntime.__unsafe_scheduleInputsChanged(nodeId);
2009
+ this.graphRuntime.scheduleInputsChanged(nodeId);
1996
2010
  }
1997
2011
  triggerExternal(nodeId, event) {
1998
2012
  super.triggerExternal(nodeId, event);
@@ -2029,12 +2043,41 @@ class StepEngine extends AbstractEngine {
2029
2043
  this.dirtyNodes.clear();
2030
2044
  this.graphRuntime.resume();
2031
2045
  for (const n of nodes)
2032
- this.graphRuntime.__unsafe_scheduleInputsChanged(n);
2046
+ this.graphRuntime.scheduleInputsChanged(n);
2033
2047
  await this.graphRuntime.whenIdle();
2034
2048
  this.graphRuntime.pause();
2035
2049
  }
2036
2050
  }
2037
2051
 
2052
+ /**
2053
+ * Creates an Engine instance for the given GraphRuntime based on engine configuration.
2054
+ * This is the single source of truth for engine creation, used by both local and remote runners.
2055
+ */
2056
+ function createEngine(runtime, config) {
2057
+ const engineKind = config?.engine ?? "push";
2058
+ const batched = config?.batched ?? { flushIntervalMs: 0 };
2059
+ const hybrid = config?.hybrid ?? { windowMs: 250, batchThreshold: 3 };
2060
+ switch (engineKind) {
2061
+ case "push":
2062
+ return new PushEngine(runtime);
2063
+ case "batched":
2064
+ return new BatchedEngine(runtime, {
2065
+ flushIntervalMs: batched.flushIntervalMs,
2066
+ });
2067
+ case "pull":
2068
+ return new PullEngine(runtime);
2069
+ case "hybrid":
2070
+ return new HybridEngine(runtime, {
2071
+ windowMs: hybrid.windowMs,
2072
+ batchThreshold: hybrid.batchThreshold,
2073
+ });
2074
+ case "step":
2075
+ return new StepEngine(runtime);
2076
+ default:
2077
+ throw new Error(`Unknown engine kind: ${engineKind}`);
2078
+ }
2079
+ }
2080
+
2038
2081
  const ComputeCategory = {
2039
2082
  id: "compute",
2040
2083
  displayName: "Compute",
@@ -3446,6 +3489,7 @@ exports.Registry = Registry;
3446
3489
  exports.StepEngine = StepEngine;
3447
3490
  exports.createAsyncGraphDef = createAsyncGraphDef;
3448
3491
  exports.createAsyncGraphRegistry = createAsyncGraphRegistry;
3492
+ exports.createEngine = createEngine;
3449
3493
  exports.createProgressGraphDef = createProgressGraphDef;
3450
3494
  exports.createProgressGraphRegistry = createProgressGraphRegistry;
3451
3495
  exports.createSimpleGraphDef = createSimpleGraphDef;