@bian-womp/spark-graph 0.3.0 → 0.3.2

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
@@ -1070,10 +1070,13 @@ class ValuePropagator {
1070
1070
  }
1071
1071
  }
1072
1072
  }
1073
- // Schedule downstream execution if propagation is enabled
1074
- if (!this.runtimeCoordinator.isPaused() &&
1073
+ // Schedule downstream execution if propagation is enabled.
1074
+ // In manual mode we still allow scheduling for run-context-aware runs.
1075
+ const paused = this.runtimeCoordinator.isPaused();
1076
+ const canSchedule = (!paused || isRunContextAware) &&
1075
1077
  shouldPropagate &&
1076
- this.executionScheduler.allInboundHaveValue(e.target.nodeId)) {
1078
+ this.executionScheduler.allInboundHaveValue(e.target.nodeId);
1079
+ if (canSchedule) {
1077
1080
  if (isRunContextAware && effectiveRunContexts) {
1078
1081
  this.executionScheduler.scheduleInputsChangedWithRunContexts(e.target.nodeId, effectiveRunContexts);
1079
1082
  }
@@ -1357,7 +1360,8 @@ class ExecutionScheduler {
1357
1360
  const node = this.graphStructure.getNode(nodeId);
1358
1361
  if (!node)
1359
1362
  return;
1360
- if (this.runtimeCoordinator.isPaused())
1363
+ // Block only auto-mode scheduling while paused; allow run-context runs
1364
+ if (!runContextIds && this.runtimeCoordinator.isPaused())
1361
1365
  return;
1362
1366
  // If run-context IDs are provided, attach them to the node
1363
1367
  if (runContextIds) {
@@ -1654,6 +1658,8 @@ class GraphRuntime {
1654
1658
  // State
1655
1659
  this.paused = false;
1656
1660
  this.environment = {};
1661
+ this.runMode = null;
1662
+ this.pauseRefCount = 0;
1657
1663
  // Initialize components
1658
1664
  this.graphStructure = new GraphStructure();
1659
1665
  this.eventEmitter = new EventEmitter();
@@ -1800,13 +1806,16 @@ class GraphRuntime {
1800
1806
  anyChanged = true;
1801
1807
  }
1802
1808
  }
1803
- if (!this.paused) {
1804
- // Only schedule if all inbound inputs are present (or there are none)
1805
- if (anyChanged && this.executionScheduler.allInboundHaveValue(nodeId))
1809
+ // In auto mode, input updates can trigger execution; in manual mode they never should.
1810
+ const isAutoMode = this.runMode === "auto" || this.runMode === null;
1811
+ const canAutoSchedule = !this.paused && isAutoMode;
1812
+ if (canAutoSchedule) {
1813
+ if (anyChanged && this.executionScheduler.allInboundHaveValue(nodeId)) {
1806
1814
  this.executionScheduler.scheduleInputsChangedInternal(nodeId);
1807
- // Recompute dynamic handles for this node when its direct inputs change
1808
- if (anyChanged)
1809
- this.handleResolver.scheduleRecomputeHandles(nodeId);
1815
+ }
1816
+ }
1817
+ if (anyChanged) {
1818
+ this.handleResolver.scheduleRecomputeHandles(nodeId);
1810
1819
  }
1811
1820
  }
1812
1821
  getOutput(nodeId, output) {
@@ -1820,14 +1829,12 @@ class GraphRuntime {
1820
1829
  return;
1821
1830
  const oldResolved = this.graphStructure.getResolvedHandles(nodeId);
1822
1831
  this.graphStructure.setResolvedHandles(nodeId, handles);
1823
- // Clear outputs that are no longer valid handles
1824
1832
  const oldOutputs = oldResolved?.outputs ?? {};
1825
1833
  const newOutputs = handles.outputs ?? {};
1826
1834
  const oldOutputHandles = new Set(Object.keys(oldOutputs));
1827
1835
  const newOutputHandles = new Set(Object.keys(newOutputs));
1828
1836
  for (const handle of oldOutputHandles) {
1829
1837
  if (!newOutputHandles.has(handle)) {
1830
- // Output handle was removed - clear it and emit undefined to invalidate downstream
1831
1838
  delete node.outputs[handle];
1832
1839
  this.eventEmitter.emit("value", {
1833
1840
  nodeId,
@@ -1838,19 +1845,17 @@ class GraphRuntime {
1838
1845
  }
1839
1846
  }
1840
1847
  const edges = this.graphStructure.getEdges();
1841
- // Recompute edge converter/type for edges where this node is source or target
1842
1848
  for (const e of edges) {
1843
1849
  const srcNode = this.graphStructure.getNode(e.source.nodeId);
1844
1850
  const dstNode = this.graphStructure.getNode(e.target.nodeId);
1845
- let srcDeclared = e.effectiveTypeId; // Use effectiveTypeId as fallback
1851
+ let srcDeclared = e.effectiveTypeId;
1846
1852
  let dstDeclared = e.dstDeclared;
1847
- const oldDstDeclared = dstDeclared; // Track old value to detect resolution
1853
+ const oldDstDeclared = dstDeclared;
1848
1854
  if (e.source.nodeId === nodeId) {
1849
1855
  const resolved = this.graphStructure.getResolvedHandles(nodeId);
1850
1856
  srcDeclared = resolved
1851
1857
  ? resolved.outputs[e.source.handle]
1852
1858
  : srcDeclared;
1853
- // Update effectiveTypeId if original wasn't explicit
1854
1859
  if (!e.typeId) {
1855
1860
  e.effectiveTypeId = Array.isArray(srcDeclared)
1856
1861
  ? srcDeclared?.[0] ?? "untyped"
@@ -1867,7 +1872,6 @@ class GraphRuntime {
1867
1872
  const conv = GraphStructure.buildEdgeConverters(srcDeclared, dstDeclared, registry, `updateNodeHandles: ${srcNode?.typeId || ""}.${e.source.nodeId}.${e.source.handle} -> ${dstNode?.typeId || ""}.${e.target.nodeId}.${e.target.handle}`);
1868
1873
  e.convert = conv.convert;
1869
1874
  e.convertAsync = conv.convertAsync;
1870
- // If target handle was just resolved (was undefined, now has a type), re-propagate values
1871
1875
  if (e.target.nodeId === nodeId &&
1872
1876
  oldDstDeclared === undefined &&
1873
1877
  dstDeclared !== undefined) {
@@ -1875,8 +1879,6 @@ class GraphRuntime {
1875
1879
  if (srcNode) {
1876
1880
  const srcValue = srcNode.outputs[e.source.handle];
1877
1881
  if (srcValue !== undefined) {
1878
- // Re-propagate through the now-resolved edge converter
1879
- // Preserve run-contexts if source node has them
1880
1882
  const runContextIds = srcNode.activeRunContexts.size > 0
1881
1883
  ? new Set(srcNode.activeRunContexts)
1882
1884
  : undefined;
@@ -1885,11 +1887,9 @@ class GraphRuntime {
1885
1887
  }
1886
1888
  }
1887
1889
  }
1888
- // Re-emit only valid outputs (after clearing removed ones)
1889
1890
  this.valuePropagator.reemitNodeOutputs(nodeId);
1890
1891
  }
1891
1892
  launch(invalidate = false) {
1892
- // call onActivated for nodes that implement it
1893
1893
  for (const node of this.graphStructure.getNodes().values()) {
1894
1894
  const effectiveInputs = this.executionScheduler.getEffectiveInputs(node.nodeId);
1895
1895
  const ctrl = new AbortController();
@@ -1902,7 +1902,6 @@ class GraphRuntime {
1902
1902
  node.runtime.onActivated?.();
1903
1903
  }
1904
1904
  if (invalidate) {
1905
- // After activation, schedule nodes that have all inbound inputs present
1906
1905
  for (const nodeId of this.graphStructure.getNodes().keys()) {
1907
1906
  if (this.executionScheduler.allInboundHaveValue(nodeId))
1908
1907
  this.executionScheduler.scheduleInputsChangedInternal(nodeId);
@@ -1913,11 +1912,9 @@ class GraphRuntime {
1913
1912
  const node = this.graphStructure.getNode(nodeId);
1914
1913
  if (!node)
1915
1914
  return;
1916
- // Forward event to node's onExternalEvent handler for custom actions
1917
1915
  node.runtime.onExternalEvent?.(event, node.state);
1918
1916
  }
1919
1917
  dispose() {
1920
- // Resolve all pending run-context promises before cleanup
1921
1918
  this.runContextManager.resolveAll();
1922
1919
  for (const node of this.graphStructure.getNodes().values()) {
1923
1920
  node.runtime.onDeactivated?.();
@@ -1932,7 +1929,6 @@ class GraphRuntime {
1932
1929
  getNodeIds() {
1933
1930
  return Array.from(this.graphStructure.getNodes().keys());
1934
1931
  }
1935
- // Unsafe helpers for serializer: read-only accessors and hydration
1936
1932
  getNodeData(nodeId) {
1937
1933
  const node = this.graphStructure.getNode(nodeId);
1938
1934
  if (!node)
@@ -1952,12 +1948,10 @@ class GraphRuntime {
1952
1948
  this.environment = { ...env };
1953
1949
  this.handleResolver.setEnvironment(this.environment);
1954
1950
  this.executionScheduler.setEnvironment(this.environment);
1955
- // Recompute dynamic handles for all nodes when environment changes
1956
1951
  for (const nodeId of this.graphStructure.getNodes().keys()) {
1957
1952
  this.handleResolver.scheduleRecomputeHandles(nodeId);
1958
1953
  }
1959
1954
  }
1960
- // Export a GraphDefinition reflecting the current runtime view
1961
1955
  getGraphDef() {
1962
1956
  const nodes = Array.from(this.graphStructure.getNodes().values()).map((n) => {
1963
1957
  const resolved = this.graphStructure.getResolvedHandles(n.nodeId);
@@ -1974,12 +1968,11 @@ class GraphRuntime {
1974
1968
  id: e.id,
1975
1969
  source: { nodeId: e.source.nodeId, handle: e.source.handle },
1976
1970
  target: { nodeId: e.target.nodeId, handle: e.target.handle },
1977
- typeId: e.typeId, // Only export original typeId (may be undefined)
1971
+ typeId: e.typeId,
1978
1972
  }));
1979
1973
  return { nodes, edges };
1980
1974
  }
1981
1975
  async whenIdle() {
1982
- // If we have active run-contexts, wait for all of them to complete
1983
1976
  const allRunContexts = this.runContextManager.getAllRunContexts();
1984
1977
  if (allRunContexts.size > 0) {
1985
1978
  await new Promise((resolve) => {
@@ -2015,65 +2008,58 @@ class GraphRuntime {
2015
2008
  setTimeout(check, 10);
2016
2009
  });
2017
2010
  }
2018
- /**
2019
- * Run this node and optionally all dynamically reachable downstream nodes as a run-context.
2020
- * Includes nodes added later behind the same path (via re-emits).
2021
- * @param startNodeId - The node to start execution from
2022
- * @param options - Execution options
2023
- * @param options.skipPropagateValues - If true, don't set inputs of linked nodes (default: false)
2024
- * @param options.propagate - If false, don't schedule downstream nodes (default: true)
2025
- */
2026
2011
  async runFromHereContext(startNodeId, options) {
2027
2012
  const node = this.graphStructure.getNode(startNodeId);
2028
- if (!node) {
2029
- // Node doesn't exist - resolve immediately
2013
+ if (!node)
2030
2014
  return;
2031
- }
2032
2015
  const ctx = this.runContextManager.createRunContext(startNodeId, options);
2033
- // Create promise that resolves when context finishes
2034
2016
  const promise = new Promise((resolve) => {
2035
2017
  ctx.resolve = resolve;
2036
2018
  });
2037
- // Temporarily unpause if needed
2038
- const wasPaused = this.paused;
2039
- if (wasPaused) {
2040
- this.paused = false;
2041
- }
2042
- try {
2043
- // Seed the start node with this run-context
2044
- node.activeRunContexts.add(ctx.id);
2045
- this.scheduleInputsChangedWithRunContexts(startNodeId, new Set([ctx.id]));
2046
- await promise;
2047
- }
2048
- finally {
2049
- // Restore pause state if it was paused and no other run-contexts are active
2050
- if (wasPaused && this.runContextManager.getAllRunContexts().size === 0) {
2051
- this.paused = true;
2052
- }
2053
- }
2019
+ node.activeRunContexts.add(ctx.id);
2020
+ this.scheduleInputsChangedWithRunContexts(startNodeId, new Set([ctx.id]));
2021
+ await promise;
2054
2022
  }
2055
- /**
2056
- * Schedule a node with run-context IDs attached
2057
- */
2058
2023
  scheduleInputsChangedWithRunContexts(nodeId, runContextIds) {
2059
2024
  const node = this.graphStructure.getNode(nodeId);
2060
2025
  if (!node)
2061
2026
  return;
2062
- // Attach run-contexts to the node
2063
2027
  for (const id of runContextIds) {
2064
2028
  node.activeRunContexts.add(id);
2065
2029
  }
2066
2030
  this.executionScheduler.scheduleInputsChangedInternal(nodeId, runContextIds);
2067
2031
  }
2068
- pause() {
2069
- this.paused = true;
2032
+ setRunMode(runMode) {
2033
+ this.runMode = runMode;
2034
+ this.updatePausedState();
2035
+ }
2036
+ requestPause() {
2037
+ this.pauseRefCount++;
2038
+ this.updatePausedState();
2039
+ let released = false;
2040
+ return () => {
2041
+ if (released)
2042
+ return;
2043
+ released = true;
2044
+ this.pauseRefCount--;
2045
+ this.updatePausedState();
2046
+ };
2047
+ }
2048
+ updatePausedState() {
2049
+ if (this.pauseRefCount > 0) {
2050
+ this.paused = true;
2051
+ return;
2052
+ }
2053
+ if (this.runMode === "manual") {
2054
+ this.paused = true;
2055
+ }
2056
+ else if (this.runMode === "auto") {
2057
+ this.paused = false;
2058
+ }
2070
2059
  }
2071
2060
  isPaused() {
2072
2061
  return this.paused;
2073
2062
  }
2074
- resume() {
2075
- this.paused = false;
2076
- }
2077
2063
  invalidateDownstream(nodeId) {
2078
2064
  this.valuePropagator.reemitNodeOutputs(nodeId);
2079
2065
  }
@@ -2084,19 +2070,13 @@ class GraphRuntime {
2084
2070
  this.executionScheduler.cancelNodeRuns(nodeIds);
2085
2071
  }
2086
2072
  copyOutputs(fromNodeId, toNodeId, options) {
2087
- // Get outputs from source node
2088
2073
  const fromNode = this.getNodeData(fromNodeId);
2089
2074
  if (!fromNode?.outputs)
2090
2075
  return;
2091
- // Copy outputs to target node using hydrate
2092
- // hydrate already pauses internally, so we don't need to handle dry option here
2093
- // reemit: !options?.dry means don't propagate downstream if dry mode
2094
2076
  this.hydrate({ outputs: { [toNodeId]: { ...fromNode.outputs } } }, { reemit: !options?.dry });
2095
2077
  }
2096
- // Hydrate inputs/outputs without triggering computation; optionally re-emit outputs downstream
2097
2078
  hydrate(payload, opts) {
2098
- const prevPaused = this.paused;
2099
- this.paused = true;
2079
+ const releasePause = this.requestPause();
2100
2080
  try {
2101
2081
  const ins = payload?.inputs || {};
2102
2082
  for (const [nodeId, map] of Object.entries(ins)) {
@@ -2105,7 +2085,6 @@ class GraphRuntime {
2105
2085
  continue;
2106
2086
  for (const [h, v] of Object.entries(map || {})) {
2107
2087
  node.inputs[h] = structuredClone(v);
2108
- // emit input value event
2109
2088
  this.eventEmitter.emit("value", {
2110
2089
  nodeId,
2111
2090
  handle: h,
@@ -2122,7 +2101,6 @@ class GraphRuntime {
2122
2101
  continue;
2123
2102
  for (const [h, v] of Object.entries(map || {})) {
2124
2103
  node.outputs[h] = structuredClone(v);
2125
- // emit output value event
2126
2104
  this.eventEmitter.emit("value", {
2127
2105
  nodeId,
2128
2106
  handle: h,
@@ -2139,31 +2117,23 @@ class GraphRuntime {
2139
2117
  }
2140
2118
  }
2141
2119
  finally {
2142
- this.paused = prevPaused;
2120
+ releasePause();
2143
2121
  }
2144
2122
  }
2145
- // Incrementally update nodes/edges to match new definition without full rebuild
2146
2123
  update(def, registry) {
2147
- // Handle node additions and removals
2148
2124
  const desiredIds = new Set(def.nodes.map((n) => n.nodeId));
2149
2125
  const currentIds = new Set(this.graphStructure.getNodes().keys());
2150
- // Remove nodes not present
2151
2126
  for (const nodeId of Array.from(currentIds)) {
2152
2127
  if (!desiredIds.has(nodeId)) {
2153
2128
  const node = this.graphStructure.getNode(nodeId);
2154
- // Cancel all active runs and emit cancellation events
2155
2129
  this.executionScheduler.cancelNodeActiveRuns(node, "node-deleted");
2156
- // Cancel node in all run-contexts (marks it as cancelled and clears activeRunContexts)
2157
- this.runContextManager.cancelNodeInRunContexts(nodeId,
2158
- /* includeDownstream */ true, this.graphStructure.getEdges(), this.graphStructure.getNodes());
2159
- // Check for run-context completion (they may finish if pending reaches 0)
2130
+ this.runContextManager.cancelNodeInRunContexts(nodeId, true, this.graphStructure.getEdges(), this.graphStructure.getNodes());
2160
2131
  const allRunContexts = this.runContextManager.getAllRunContexts();
2161
2132
  for (const ctx of Array.from(allRunContexts.values())) {
2162
2133
  if (ctx.pending === 0) {
2163
2134
  this.runContextManager.finishRunContext(ctx.id, this.graphStructure.getNodes());
2164
2135
  }
2165
2136
  }
2166
- // Cleanup node resources
2167
2137
  node.runtime.onDeactivated?.();
2168
2138
  node.runtime.dispose?.();
2169
2139
  node.lifecycle?.dispose?.({
@@ -2178,7 +2148,6 @@ class GraphRuntime {
2178
2148
  for (const n of def.nodes) {
2179
2149
  const existing = this.graphStructure.getNode(n.nodeId);
2180
2150
  if (!existing) {
2181
- // create new runtime node
2182
2151
  const desc = registry.nodes.get(n.typeId);
2183
2152
  if (!desc)
2184
2153
  throw new Error(`Unknown node type: ${n.typeId}`);
@@ -2218,7 +2187,6 @@ class GraphRuntime {
2218
2187
  activeRunContexts: new Set(),
2219
2188
  };
2220
2189
  this.graphStructure.setNode(n.nodeId, rn);
2221
- // Activate new node
2222
2190
  const effectiveInputs = this.executionScheduler.getEffectiveInputs(rn.nodeId);
2223
2191
  const ctrl = new AbortController();
2224
2192
  const ctx = this.executionScheduler.createExecutionContext(rn.nodeId, rn, effectiveInputs, `${rn.nodeId}:init`, ctrl.signal);
@@ -2230,7 +2198,6 @@ class GraphRuntime {
2230
2198
  rn.runtime.onActivated?.();
2231
2199
  }
2232
2200
  else {
2233
- // update params/policy
2234
2201
  existing.params = n.params;
2235
2202
  if (!existing.stats) {
2236
2203
  existing.stats = {
@@ -2242,7 +2209,6 @@ class GraphRuntime {
2242
2209
  }
2243
2210
  }
2244
2211
  }
2245
- // Capture previous inbound map before rebuilding edges
2246
2212
  const edges = this.graphStructure.getEdges();
2247
2213
  const prevInbound = new Map();
2248
2214
  for (const e of edges) {
@@ -2250,7 +2216,6 @@ class GraphRuntime {
2250
2216
  set.add(e.target.handle);
2251
2217
  prevInbound.set(e.target.nodeId, set);
2252
2218
  }
2253
- // Capture previous per-handle target sets before rebuilding edges
2254
2219
  const prevOutTargets = new Map();
2255
2220
  for (const e of edges) {
2256
2221
  const tmap = prevOutTargets.get(e.source.nodeId) ?? new Map();
@@ -2259,9 +2224,7 @@ class GraphRuntime {
2259
2224
  tmap.set(e.source.handle, tset);
2260
2225
  prevOutTargets.set(e.source.nodeId, tmap);
2261
2226
  }
2262
- // Precompute per-node resolved handles for updated graph (include dynamic)
2263
2227
  const resolved = GraphStructure.computeResolvedHandleMap(def, registry, this.environment);
2264
- // Check which handles changed and emit events for those
2265
2228
  const changedHandles = {};
2266
2229
  for (const [nodeId, newHandles] of resolved.map) {
2267
2230
  const oldHandles = this.graphStructure.getResolvedHandles(nodeId);
@@ -2270,14 +2233,11 @@ class GraphRuntime {
2270
2233
  changedHandles[nodeId] = newHandles;
2271
2234
  }
2272
2235
  }
2273
- // Update resolved handles
2274
2236
  for (const [nodeId, handles] of resolved.map) {
2275
2237
  this.graphStructure.setResolvedHandles(nodeId, handles);
2276
2238
  }
2277
- // Rebuild edges mapping with coercions
2278
2239
  const newEdges = GraphStructure.buildEdges(def, registry, this.graphStructure.getResolvedHandlesMap());
2279
2240
  this.graphStructure.setEdges(newEdges);
2280
- // Build new inbound map
2281
2241
  const nextInbound = new Map();
2282
2242
  const updatedEdges = this.graphStructure.getEdges();
2283
2243
  for (const e of updatedEdges) {
@@ -2285,7 +2245,6 @@ class GraphRuntime {
2285
2245
  set.add(e.target.handle);
2286
2246
  nextInbound.set(e.target.nodeId, set);
2287
2247
  }
2288
- // For inputs that lost inbound connections, clear and schedule recompute
2289
2248
  for (const [nodeId, prevSet] of prevInbound) {
2290
2249
  const currSet = nextInbound.get(nodeId) ?? new Set();
2291
2250
  const node = this.graphStructure.getNode(nodeId);
@@ -2301,12 +2260,10 @@ class GraphRuntime {
2301
2260
  }
2302
2261
  }
2303
2262
  if (changed) {
2304
- // Clear buckets for handles that lost inbound (handled by ValuePropagator)
2305
2263
  this.valuePropagator.clearArrayBuckets(nodeId);
2306
2264
  this.executionScheduler.scheduleInputsChangedInternal(nodeId);
2307
2265
  }
2308
2266
  }
2309
- // Re-emit outputs when per-handle target sets change (precise and simple)
2310
2267
  const nextOutTargets = new Map();
2311
2268
  for (const e of updatedEdges) {
2312
2269
  const tmap = nextOutTargets.get(e.source.nodeId) ?? new Map();
@@ -2315,7 +2272,7 @@ class GraphRuntime {
2315
2272
  tmap.set(e.source.handle, tset);
2316
2273
  nextOutTargets.set(e.source.nodeId, tmap);
2317
2274
  }
2318
- const setsEqualStr = (a, b) => {
2275
+ const setsEqual = (a, b) => {
2319
2276
  if (!a && !b)
2320
2277
  return true;
2321
2278
  if (!a || !b)
@@ -2341,7 +2298,7 @@ class GraphRuntime {
2341
2298
  for (const handle of handles) {
2342
2299
  const pset = pmap.get(handle) ?? new Set();
2343
2300
  const nset = nmap.get(handle) ?? new Set();
2344
- if (!setsEqualStr(pset, nset)) {
2301
+ if (!setsEqual(pset, nset)) {
2345
2302
  const val = this.getOutput(nodeId, handle);
2346
2303
  if (val !== undefined)
2347
2304
  this.valuePropagator.propagate(nodeId, handle, val);
@@ -2350,11 +2307,6 @@ class GraphRuntime {
2350
2307
  }
2351
2308
  }
2352
2309
  }
2353
- // Prune array bucket contributions for edges that no longer exist
2354
- // This is handled by ValuePropagator - array buckets are managed there
2355
- // The buckets will be cleaned up automatically when edges are removed
2356
- // Schedule async recompute for nodes that indicated Promise-based resolveHandles in this update
2357
- // Emit event for changed handles (if any)
2358
2310
  if (Object.keys(changedHandles).length > 0) {
2359
2311
  this.eventEmitter.emit("invalidate", {
2360
2312
  reason: "graph-updated",
@@ -2585,21 +2537,25 @@ class GraphBuilder {
2585
2537
  }
2586
2538
  }
2587
2539
 
2588
- class AbstractEngine {
2589
- constructor(graphRuntime) {
2540
+ /**
2541
+ * Unified Engine implementation that handles both manual and auto run modes.
2542
+ * - Manual mode: Runtime is paused, nodes execute only when explicitly called via computeNode/runFromHere
2543
+ * - Auto mode: Runtime is resumed, nodes automatically execute when inputs change
2544
+ */
2545
+ class LocalEngine {
2546
+ constructor(graphRuntime, runMode) {
2590
2547
  this.graphRuntime = graphRuntime;
2548
+ this.setRunMode(runMode ?? "manual");
2591
2549
  }
2592
2550
  setInputs(nodeId, inputs, options) {
2593
2551
  if (options?.dry) {
2594
- const wasPaused = this.graphRuntime.isPaused();
2595
- if (!wasPaused)
2596
- this.graphRuntime.pause();
2552
+ // Use requestPause to temporarily pause without affecting base run mode
2553
+ const releasePause = this.graphRuntime.requestPause();
2597
2554
  try {
2598
2555
  this.graphRuntime.setInputs(nodeId, inputs);
2599
2556
  }
2600
2557
  finally {
2601
- if (!wasPaused)
2602
- this.graphRuntime.resume();
2558
+ releasePause();
2603
2559
  }
2604
2560
  }
2605
2561
  else {
@@ -2608,15 +2564,13 @@ class AbstractEngine {
2608
2564
  }
2609
2565
  triggerExternal(nodeId, event, options) {
2610
2566
  if (options?.dry) {
2611
- const wasPaused = this.graphRuntime.isPaused();
2612
- if (!wasPaused)
2613
- this.graphRuntime.pause();
2567
+ // Use requestPause to temporarily pause without affecting base run mode
2568
+ const releasePause = this.graphRuntime.requestPause();
2614
2569
  try {
2615
2570
  this.graphRuntime.triggerExternal(nodeId, event);
2616
2571
  }
2617
2572
  finally {
2618
- if (!wasPaused)
2619
- this.graphRuntime.resume();
2573
+ releasePause();
2620
2574
  }
2621
2575
  }
2622
2576
  else {
@@ -2641,19 +2595,6 @@ class AbstractEngine {
2641
2595
  dispose() {
2642
2596
  // this.graphRuntime.dispose();
2643
2597
  }
2644
- }
2645
-
2646
- /**
2647
- * Unified Engine implementation that handles both manual and auto run modes.
2648
- * - Manual mode: Runtime is paused, nodes execute only when explicitly called via computeNode/runFromHere
2649
- * - Auto mode: Runtime is resumed, nodes automatically execute when inputs change
2650
- */
2651
- class UnifiedEngine extends AbstractEngine {
2652
- constructor(graphRuntime, runMode) {
2653
- super(graphRuntime);
2654
- this.runMode = "manual";
2655
- this.setRunMode(runMode ?? "manual");
2656
- }
2657
2598
  launch(invalidate, runMode) {
2658
2599
  if (runMode)
2659
2600
  this.setRunMode(runMode);
@@ -2680,20 +2621,8 @@ class UnifiedEngine extends AbstractEngine {
2680
2621
  async runFromHere(nodeId) {
2681
2622
  await this.graphRuntime.runFromHereContext(nodeId);
2682
2623
  }
2683
- getRunMode() {
2684
- return this.runMode;
2685
- }
2686
2624
  setRunMode(runMode) {
2687
- if (this.runMode === runMode)
2688
- return;
2689
- this.runMode = runMode;
2690
- // Update runtime pause/resume state based on new mode
2691
- if (runMode === "manual") {
2692
- this.graphRuntime.pause();
2693
- }
2694
- else {
2695
- this.graphRuntime.resume();
2696
- }
2625
+ this.graphRuntime.setRunMode(runMode);
2697
2626
  }
2698
2627
  }
2699
2628
 
@@ -4781,8 +4710,8 @@ exports.CompositeCategory = CompositeCategory;
4781
4710
  exports.ComputeCategory = ComputeCategory;
4782
4711
  exports.GraphBuilder = GraphBuilder;
4783
4712
  exports.GraphRuntime = GraphRuntime;
4713
+ exports.LocalEngine = LocalEngine;
4784
4714
  exports.Registry = Registry;
4785
- exports.UnifiedEngine = UnifiedEngine;
4786
4715
  exports.buildValueConverter = buildValueConverter;
4787
4716
  exports.computeGraphCenter = computeGraphCenter;
4788
4717
  exports.createAsyncGraphDef = createAsyncGraphDef;