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