@bian-womp/spark-graph 0.3.46 → 0.3.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.
Files changed (163) hide show
  1. package/lib/cjs/index.cjs +20 -17
  2. package/lib/cjs/index.cjs.map +1 -1
  3. package/lib/cjs/src/core/types.d.ts +2 -1
  4. package/lib/cjs/src/core/types.d.ts.map +1 -1
  5. package/lib/cjs/src/runtime/GraphRuntime.d.ts.map +1 -1
  6. package/lib/cjs/src/runtime/components/NodeExecutor.d.ts.map +1 -1
  7. package/lib/cjs/src/runtime/components/RunContextManager.d.ts +3 -2
  8. package/lib/cjs/src/runtime/components/RunContextManager.d.ts.map +1 -1
  9. package/lib/cjs/src/runtime/utils.d.ts.map +1 -1
  10. package/lib/esm/index.js +20 -17
  11. package/lib/esm/index.js.map +1 -1
  12. package/lib/esm/src/core/types.d.ts +2 -1
  13. package/lib/esm/src/core/types.d.ts.map +1 -1
  14. package/lib/esm/src/runtime/GraphRuntime.d.ts.map +1 -1
  15. package/lib/esm/src/runtime/components/NodeExecutor.d.ts.map +1 -1
  16. package/lib/esm/src/runtime/components/RunContextManager.d.ts +3 -2
  17. package/lib/esm/src/runtime/components/RunContextManager.d.ts.map +1 -1
  18. package/lib/esm/src/runtime/utils.d.ts.map +1 -1
  19. package/lib/src/builder/GraphBuilder.d.ts +43 -0
  20. package/lib/src/builder/GraphBuilder.d.ts.map +1 -0
  21. package/lib/src/builder/GraphBuilder.js +284 -0
  22. package/lib/src/builder/GraphBuilder.js.map +1 -0
  23. package/lib/src/builder/Registry.d.ts +93 -0
  24. package/lib/src/builder/Registry.d.ts.map +1 -0
  25. package/lib/src/builder/Registry.js +393 -0
  26. package/lib/src/builder/Registry.js.map +1 -0
  27. package/lib/src/core/categories.d.ts +22 -0
  28. package/lib/src/core/categories.d.ts.map +1 -0
  29. package/lib/src/core/categories.js +2 -0
  30. package/lib/src/core/categories.js.map +1 -0
  31. package/lib/src/core/order.d.ts +7 -0
  32. package/lib/src/core/order.d.ts.map +1 -0
  33. package/lib/src/core/order.js +66 -0
  34. package/lib/src/core/order.js.map +1 -0
  35. package/lib/src/core/type-utils.d.ts +29 -0
  36. package/lib/src/core/type-utils.d.ts.map +1 -0
  37. package/lib/src/core/type-utils.js +97 -0
  38. package/lib/src/core/type-utils.js.map +1 -0
  39. package/lib/src/core/types.d.ts +92 -0
  40. package/lib/src/core/types.d.ts.map +1 -0
  41. package/lib/src/core/types.js +2 -0
  42. package/lib/src/core/types.js.map +1 -0
  43. package/lib/src/examples/arrays.d.ts +5 -0
  44. package/lib/src/examples/arrays.d.ts.map +1 -0
  45. package/lib/src/examples/arrays.js +49 -0
  46. package/lib/src/examples/arrays.js.map +1 -0
  47. package/lib/src/examples/async.d.ts +5 -0
  48. package/lib/src/examples/async.d.ts.map +1 -0
  49. package/lib/src/examples/async.js +91 -0
  50. package/lib/src/examples/async.js.map +1 -0
  51. package/lib/src/examples/progress.d.ts +5 -0
  52. package/lib/src/examples/progress.d.ts.map +1 -0
  53. package/lib/src/examples/progress.js +51 -0
  54. package/lib/src/examples/progress.js.map +1 -0
  55. package/lib/src/examples/run.d.ts +2 -0
  56. package/lib/src/examples/run.d.ts.map +1 -0
  57. package/lib/src/examples/run.js +32 -0
  58. package/lib/src/examples/run.js.map +1 -0
  59. package/lib/src/examples/runMode.d.ts +2 -0
  60. package/lib/src/examples/runMode.d.ts.map +1 -0
  61. package/lib/src/examples/runMode.js +223 -0
  62. package/lib/src/examples/runMode.js.map +1 -0
  63. package/lib/src/examples/shared.d.ts +5 -0
  64. package/lib/src/examples/shared.d.ts.map +1 -0
  65. package/lib/src/examples/shared.js +49 -0
  66. package/lib/src/examples/shared.js.map +1 -0
  67. package/lib/src/examples/simple.d.ts +5 -0
  68. package/lib/src/examples/simple.d.ts.map +1 -0
  69. package/lib/src/examples/simple.js +79 -0
  70. package/lib/src/examples/simple.js.map +1 -0
  71. package/lib/src/examples/snapshot.d.ts +4 -0
  72. package/lib/src/examples/snapshot.d.ts.map +1 -0
  73. package/lib/src/examples/snapshot.js +58 -0
  74. package/lib/src/examples/snapshot.js.map +1 -0
  75. package/lib/src/examples/validation.d.ts +5 -0
  76. package/lib/src/examples/validation.d.ts.map +1 -0
  77. package/lib/src/examples/validation.js +105 -0
  78. package/lib/src/examples/validation.js.map +1 -0
  79. package/lib/src/index.d.ts +27 -0
  80. package/lib/src/index.d.ts.map +1 -0
  81. package/lib/src/index.js +19 -0
  82. package/lib/src/index.js.map +1 -0
  83. package/lib/src/misc/base.d.ts +51 -0
  84. package/lib/src/misc/base.d.ts.map +1 -0
  85. package/lib/src/misc/base.js +1122 -0
  86. package/lib/src/misc/base.js.map +1 -0
  87. package/lib/src/misc/utils/json.d.ts +22 -0
  88. package/lib/src/misc/utils/json.d.ts.map +1 -0
  89. package/lib/src/misc/utils/json.js +239 -0
  90. package/lib/src/misc/utils/json.js.map +1 -0
  91. package/lib/src/misc/utils/merge.d.ts +51 -0
  92. package/lib/src/misc/utils/merge.d.ts.map +1 -0
  93. package/lib/src/misc/utils/merge.js +600 -0
  94. package/lib/src/misc/utils/merge.js.map +1 -0
  95. package/lib/src/plugins/composite.d.ts +22 -0
  96. package/lib/src/plugins/composite.d.ts.map +1 -0
  97. package/lib/src/plugins/composite.js +59 -0
  98. package/lib/src/plugins/composite.js.map +1 -0
  99. package/lib/src/plugins/compute.d.ts +5 -0
  100. package/lib/src/plugins/compute.d.ts.map +1 -0
  101. package/lib/src/plugins/compute.js +39 -0
  102. package/lib/src/plugins/compute.js.map +1 -0
  103. package/lib/src/runtime/Engine.d.ts +28 -0
  104. package/lib/src/runtime/Engine.d.ts.map +1 -0
  105. package/lib/src/runtime/Engine.js +2 -0
  106. package/lib/src/runtime/Engine.js.map +1 -0
  107. package/lib/src/runtime/GraphLifecycleApi.d.ts +46 -0
  108. package/lib/src/runtime/GraphLifecycleApi.d.ts.map +1 -0
  109. package/lib/src/runtime/GraphLifecycleApi.js +2 -0
  110. package/lib/src/runtime/GraphLifecycleApi.js.map +1 -0
  111. package/lib/src/runtime/GraphRuntime.d.ts +94 -0
  112. package/lib/src/runtime/GraphRuntime.d.ts.map +1 -0
  113. package/lib/src/runtime/GraphRuntime.js +729 -0
  114. package/lib/src/runtime/GraphRuntime.js.map +1 -0
  115. package/lib/src/runtime/LocalEngine.d.ts +45 -0
  116. package/lib/src/runtime/LocalEngine.d.ts.map +1 -0
  117. package/lib/src/runtime/LocalEngine.js +89 -0
  118. package/lib/src/runtime/LocalEngine.js.map +1 -0
  119. package/lib/src/runtime/components/EdgePropagator.d.ts +101 -0
  120. package/lib/src/runtime/components/EdgePropagator.d.ts.map +1 -0
  121. package/lib/src/runtime/components/EdgePropagator.js +372 -0
  122. package/lib/src/runtime/components/EdgePropagator.js.map +1 -0
  123. package/lib/src/runtime/components/EventEmitter.d.ts +12 -0
  124. package/lib/src/runtime/components/EventEmitter.d.ts.map +1 -0
  125. package/lib/src/runtime/components/EventEmitter.js +33 -0
  126. package/lib/src/runtime/components/EventEmitter.js.map +1 -0
  127. package/lib/src/runtime/components/Graph.d.ts +211 -0
  128. package/lib/src/runtime/components/Graph.d.ts.map +1 -0
  129. package/lib/src/runtime/components/Graph.js +468 -0
  130. package/lib/src/runtime/components/Graph.js.map +1 -0
  131. package/lib/src/runtime/components/HandleResolver.d.ts +36 -0
  132. package/lib/src/runtime/components/HandleResolver.d.ts.map +1 -0
  133. package/lib/src/runtime/components/HandleResolver.js +231 -0
  134. package/lib/src/runtime/components/HandleResolver.js.map +1 -0
  135. package/lib/src/runtime/components/NodeExecutor.d.ts +110 -0
  136. package/lib/src/runtime/components/NodeExecutor.d.ts.map +1 -0
  137. package/lib/src/runtime/components/NodeExecutor.js +659 -0
  138. package/lib/src/runtime/components/NodeExecutor.js.map +1 -0
  139. package/lib/src/runtime/components/RunContextManager.d.ts +86 -0
  140. package/lib/src/runtime/components/RunContextManager.d.ts.map +1 -0
  141. package/lib/src/runtime/components/RunContextManager.js +302 -0
  142. package/lib/src/runtime/components/RunContextManager.js.map +1 -0
  143. package/lib/src/runtime/components/RuntimeValidatorManager.d.ts +31 -0
  144. package/lib/src/runtime/components/RuntimeValidatorManager.d.ts.map +1 -0
  145. package/lib/src/runtime/components/RuntimeValidatorManager.js +55 -0
  146. package/lib/src/runtime/components/RuntimeValidatorManager.js.map +1 -0
  147. package/lib/src/runtime/components/graph-utils.d.ts +33 -0
  148. package/lib/src/runtime/components/graph-utils.d.ts.map +1 -0
  149. package/lib/src/runtime/components/graph-utils.js +292 -0
  150. package/lib/src/runtime/components/graph-utils.js.map +1 -0
  151. package/lib/src/runtime/components/interfaces.d.ts +54 -0
  152. package/lib/src/runtime/components/interfaces.d.ts.map +1 -0
  153. package/lib/src/runtime/components/interfaces.js +2 -0
  154. package/lib/src/runtime/components/interfaces.js.map +1 -0
  155. package/lib/src/runtime/components/types.d.ts +55 -0
  156. package/lib/src/runtime/components/types.d.ts.map +1 -0
  157. package/lib/src/runtime/components/types.js +2 -0
  158. package/lib/src/runtime/components/types.js.map +1 -0
  159. package/lib/src/runtime/utils.d.ts +67 -0
  160. package/lib/src/runtime/utils.d.ts.map +1 -0
  161. package/lib/src/runtime/utils.js +137 -0
  162. package/lib/src/runtime/utils.js.map +1 -0
  163. package/package.json +2 -2
@@ -0,0 +1,729 @@
1
+ import { getInputDeclaredTypes } from "../core/type-utils";
2
+ import { Graph } from "./components/Graph";
3
+ import { EventEmitter } from "./components/EventEmitter";
4
+ import { RunContextManager } from "./components/RunContextManager";
5
+ import { HandleResolver } from "./components/HandleResolver";
6
+ import { EdgePropagator } from "./components/EdgePropagator";
7
+ import { NodeExecutor } from "./components/NodeExecutor";
8
+ import { RuntimeValidatorManager } from "./components/RuntimeValidatorManager";
9
+ import { valuesEqual } from "./utils";
10
+ import { buildEdges, tryHandleResolving } from "./components/graph-utils";
11
+ // Types are now imported from components/types.ts (re-exported above)
12
+ export class GraphRuntime {
13
+ constructor() {
14
+ // State
15
+ this.environment = {};
16
+ this.runMode = null;
17
+ this.pauseRefCount = 0;
18
+ this.persistentPauseToken = null;
19
+ // Initialize components
20
+ this.eventEmitter = new EventEmitter();
21
+ this.graph = new Graph(this.eventEmitter);
22
+ this.runContextManager = new RunContextManager(this.graph, "info");
23
+ this.handleResolver = new HandleResolver(this.graph, this.eventEmitter, this.runContextManager, this);
24
+ this.edgePropagator = new EdgePropagator(this.graph, this.eventEmitter, this.runContextManager, this.handleResolver, this);
25
+ // Create NodeExecutor with EdgePropagator and HandleResolver
26
+ this.nodeExecutor = new NodeExecutor(this.graph, this.eventEmitter, this.runContextManager, this.handleResolver, this, this);
27
+ // Create RuntimeValidatorManager
28
+ this.runtimeValidatorManager = new RuntimeValidatorManager(this.graph);
29
+ }
30
+ static create(def, registry, opts) {
31
+ const gr = new GraphRuntime();
32
+ if (opts?.startPaused)
33
+ gr.pause();
34
+ gr.environment = opts?.environment ?? {};
35
+ // Set registry and environment on components
36
+ gr.graph.setRegistry(registry);
37
+ gr.handleResolver.setRegistry(registry);
38
+ gr.handleResolver.setEnvironment(gr.environment);
39
+ gr.nodeExecutor.setEnvironment(gr.environment);
40
+ gr.runtimeValidatorManager.setRegistry(registry);
41
+ // Precompute per-node resolved handles (use def-provided overrides; do not compute dynamically here)
42
+ const initial = gr.isPaused()
43
+ ? {
44
+ resolved: new Map(),
45
+ pending: new Set(),
46
+ }
47
+ : tryHandleResolving(def, registry, gr.environment);
48
+ if (gr.isPaused()) {
49
+ gr.handleResolvingSkippedRef = def;
50
+ }
51
+ for (const [nodeId, handles] of initial.resolved) {
52
+ gr.graph.setResolvedHandles(nodeId, handles);
53
+ }
54
+ // Instantiate nodes
55
+ for (const n of def.nodes) {
56
+ const desc = registry.nodes.get(n.typeId);
57
+ if (!desc)
58
+ throw new Error(`Unknown node type: ${n.typeId}`);
59
+ const cat = registry.categories.get(desc.categoryId);
60
+ if (!cat)
61
+ throw new Error(`Unknown category: ${desc.categoryId}`);
62
+ if (cat.validateImpl)
63
+ cat.validateImpl(desc.impl);
64
+ const runtime = cat.createRuntime({
65
+ nodeId: n.nodeId,
66
+ impl: desc.impl,
67
+ });
68
+ const rn = {
69
+ typeId: n.typeId,
70
+ nodeId: n.nodeId,
71
+ lifecycle: desc.lifecycle,
72
+ inputs: {},
73
+ outputs: {},
74
+ state: {},
75
+ runtime,
76
+ params: n.params,
77
+ policy: {
78
+ ...cat.policy,
79
+ ...desc.policy,
80
+ ...n.params?.policy,
81
+ },
82
+ logLevel: desc.logLevel,
83
+ runSeq: 0,
84
+ activeControllers: new Set(),
85
+ controllerRunIds: new Map(),
86
+ queue: [],
87
+ stats: {
88
+ runs: 0,
89
+ active: 0,
90
+ queued: 0,
91
+ progress: 0,
92
+ },
93
+ activeRunContextIds: new Set(),
94
+ lastInputAt: {},
95
+ lastSuccessAt: undefined,
96
+ };
97
+ gr.graph.setNode(n.nodeId, rn);
98
+ }
99
+ // Instantiate edges
100
+ const resolvedByNode = new Map();
101
+ gr.graph.forEachResolvedHandles((handles, nodeId) => {
102
+ resolvedByNode.set(nodeId, handles);
103
+ });
104
+ const edges = buildEdges(def, registry, resolvedByNode);
105
+ gr.graph.setEdges(edges);
106
+ // Schedule async recompute only for nodes that indicated Promise-based resolveHandles
107
+ for (const nodeId of initial.pending) {
108
+ gr.handleResolver.scheduleRecomputeHandles(nodeId);
109
+ }
110
+ return gr;
111
+ }
112
+ on(event, handler) {
113
+ return this.eventEmitter.on(event, handler);
114
+ }
115
+ setInputs(nodeId, inputs) {
116
+ const node = this.graph.getNode(nodeId);
117
+ if (!node)
118
+ throw new Error(`Node not found: ${nodeId}`);
119
+ let anyChanged = false;
120
+ const registry = this.graph.getRegistry();
121
+ for (const [handle, value] of Object.entries(inputs)) {
122
+ let hasInbound = false;
123
+ this.graph.forEachEdge((e) => {
124
+ if (e.target.nodeId === nodeId && e.target.handle === handle) {
125
+ hasInbound = true;
126
+ }
127
+ });
128
+ if (hasInbound)
129
+ continue;
130
+ // Validate input value against declared type
131
+ if (registry) {
132
+ const desc = registry.nodes.get(node.typeId);
133
+ const resolved = this.graph.getResolvedHandles(nodeId);
134
+ // Get declared types (may be union); prefer resolved handles over registry statics
135
+ const declaredTypes = resolved
136
+ ? getInputDeclaredTypes(resolved.inputs, handle)
137
+ : desc
138
+ ? getInputDeclaredTypes(desc.inputs, handle)
139
+ : undefined;
140
+ const typeIds = Array.isArray(declaredTypes)
141
+ ? declaredTypes
142
+ : declaredTypes
143
+ ? [declaredTypes]
144
+ : [];
145
+ if (typeIds.length > 0) {
146
+ const isValidForAny = typeIds.some((tId) => {
147
+ const typeDesc = registry.types.get(tId);
148
+ // If no validate function, consider it valid
149
+ if (!typeDesc?.validate)
150
+ return true;
151
+ return typeDesc.validate(value);
152
+ });
153
+ if (value !== undefined && !isValidForAny) {
154
+ const typeLabel = typeIds.join("|");
155
+ const errorMessage = `Invalid value for input ${nodeId}.${handle} (type ${typeLabel}): ${JSON.stringify(value)}`;
156
+ this.eventEmitter.emit("error", {
157
+ kind: "input-validation",
158
+ nodeId,
159
+ handle,
160
+ typeId: typeLabel,
161
+ value,
162
+ message: errorMessage,
163
+ });
164
+ // Skip storing invalid value
165
+ continue;
166
+ }
167
+ }
168
+ }
169
+ const prev = node.inputs[handle];
170
+ const same = valuesEqual(prev, value);
171
+ if (!same) {
172
+ this.graph.updateNodeInput(nodeId, handle, value);
173
+ // Emit value event for input updates
174
+ this.eventEmitter.emit("value", { nodeId, handle, value, io: "input" });
175
+ anyChanged = true;
176
+ }
177
+ }
178
+ // In auto mode, input updates can trigger execution; in manual mode they never should.
179
+ // However, if autoRun policy is set, nodes run automatically even in manual mode.
180
+ if (anyChanged) {
181
+ this.handleResolver.scheduleRecomputeHandles(nodeId);
182
+ this.executeNodeAutoRun(nodeId);
183
+ }
184
+ }
185
+ getOutput(nodeId, output) {
186
+ const node = this.graph.getNode(nodeId);
187
+ return node?.outputs[output];
188
+ }
189
+ launch(invalidate = false, startPaused = false) {
190
+ this.graph.forEachNode((node) => {
191
+ const effectiveInputs = this.nodeExecutor.getEffectiveInputs(node.nodeId);
192
+ const ctrl = new AbortController();
193
+ const execCtx = this.nodeExecutor.createExecutionContext(node.nodeId, node, effectiveInputs, `${node.nodeId}:init`, ctrl.signal);
194
+ if (node.lifecycle?.prepare) {
195
+ execCtx.log("debug", "prepare-start");
196
+ node.lifecycle.prepare(node.params ?? {}, execCtx);
197
+ execCtx.log("debug", "prepare-done");
198
+ }
199
+ node.runtime.onActivated?.();
200
+ });
201
+ if (this.runMode === "auto" && invalidate) {
202
+ for (const nodeId of this.graph.getNodeIds()) {
203
+ if (this.graph.allInboundHaveValue(nodeId))
204
+ this.execute(nodeId);
205
+ }
206
+ }
207
+ if (startPaused) {
208
+ this.pause();
209
+ }
210
+ }
211
+ triggerExternal(nodeId, event) {
212
+ const node = this.graph.getNode(nodeId);
213
+ if (!node)
214
+ return;
215
+ // If event is an invalidate event, re-run the node with same inputs
216
+ if (this.isInvalidateEvent(event)) {
217
+ // Check if node has all inbound inputs (required for execution)
218
+ if (this.graph.allInboundHaveValue(nodeId)) {
219
+ this.execute(nodeId);
220
+ }
221
+ return;
222
+ }
223
+ // Forward event to node's onExternalEvent handler for custom actions
224
+ node.runtime.onExternalEvent?.(event, node.state);
225
+ }
226
+ /**
227
+ * Check if an event is an invalidate event that should trigger re-execution
228
+ */
229
+ isInvalidateEvent(event) {
230
+ if (!event || typeof event !== "object")
231
+ return false;
232
+ // Check if event has action === "invalidate"
233
+ const e = event;
234
+ return e.action === "invalidate";
235
+ }
236
+ cancelNodeRuns(nodeIds) {
237
+ this.nodeExecutor.cancelNodeRuns(nodeIds);
238
+ }
239
+ getNodeIds() {
240
+ return this.graph.getNodeIds();
241
+ }
242
+ getNodeData(nodeId) {
243
+ const node = this.graph.getNode(nodeId);
244
+ if (!node)
245
+ return undefined;
246
+ return {
247
+ inputs: { ...node.inputs },
248
+ outputs: { ...node.outputs },
249
+ state: node.state,
250
+ params: node.params,
251
+ stats: { ...node.stats },
252
+ };
253
+ }
254
+ getEnvironment() {
255
+ return { ...this.environment };
256
+ }
257
+ setEnvironment(env) {
258
+ this.environment = { ...env };
259
+ this.handleResolver.setEnvironment(this.environment);
260
+ this.nodeExecutor.setEnvironment(this.environment);
261
+ for (const nodeId of this.graph.getNodeIds()) {
262
+ this.handleResolver.scheduleRecomputeHandles(nodeId);
263
+ }
264
+ }
265
+ /**
266
+ * Register a runtime validator that will be called before node execution.
267
+ * Validators are called in registration order - if any returns true, execution is blocked.
268
+ */
269
+ registerRuntimeValidator(validator) {
270
+ this.runtimeValidatorManager.registerValidator(validator);
271
+ }
272
+ /**
273
+ * Unregister a runtime validator.
274
+ */
275
+ unregisterRuntimeValidator(validator) {
276
+ this.runtimeValidatorManager.unregisterValidator(validator);
277
+ }
278
+ /**
279
+ * Check if any runtime validator blocks execution for this node.
280
+ * Returns RuntimeValidationError if execution should be blocked, null otherwise.
281
+ */
282
+ hasRuntimeValidationBlock(nodeId) {
283
+ return this.runtimeValidatorManager.hasBlock(nodeId);
284
+ }
285
+ getGraphDef() {
286
+ const nodes = [];
287
+ this.graph.forEachNode((n) => {
288
+ const resolved = this.graph.getResolvedHandles(n.nodeId);
289
+ nodes.push({
290
+ nodeId: n.nodeId,
291
+ typeId: n.typeId,
292
+ params: n.params ? { ...n.params } : undefined,
293
+ resolvedHandles: resolved ? { ...resolved } : undefined,
294
+ });
295
+ });
296
+ const edges = [];
297
+ this.graph.forEachEdge((e) => {
298
+ edges.push({
299
+ id: e.id,
300
+ source: { nodeId: e.source.nodeId, handle: e.source.handle },
301
+ target: { nodeId: e.target.nodeId, handle: e.target.handle },
302
+ typeId: e.typeId,
303
+ });
304
+ });
305
+ return { nodes, edges };
306
+ }
307
+ async whenIdle() {
308
+ const allRunContexts = this.runContextManager.getAllRunContexts();
309
+ if (allRunContexts.size > 0) {
310
+ await new Promise((resolve) => {
311
+ const check = () => {
312
+ if (this.runContextManager.getAllRunContexts().size === 0) {
313
+ resolve();
314
+ }
315
+ else {
316
+ setTimeout(check, 10);
317
+ }
318
+ };
319
+ setTimeout(check, 10);
320
+ });
321
+ }
322
+ const isIdle = () => {
323
+ let idle = true;
324
+ this.graph.forEachNode((n) => {
325
+ if (n.activeControllers.size > 0 || n.queue.length > 0) {
326
+ idle = false;
327
+ }
328
+ });
329
+ return idle;
330
+ };
331
+ if (isIdle())
332
+ return;
333
+ await new Promise((resolve) => {
334
+ const check = () => {
335
+ if (isIdle())
336
+ resolve();
337
+ else
338
+ setTimeout(check, 10);
339
+ };
340
+ setTimeout(check, 10);
341
+ });
342
+ }
343
+ async runFromHereContext(startNodeId, options) {
344
+ const node = this.graph.getNode(startNodeId);
345
+ if (!node)
346
+ return;
347
+ return new Promise((resolve) => {
348
+ const id = this.runContextManager.createRunContext(startNodeId, {
349
+ resolve,
350
+ ...options,
351
+ });
352
+ this.graph.addNodeRunContextId(startNodeId, id);
353
+ this.execute(startNodeId, new Set([id]));
354
+ });
355
+ }
356
+ setRunMode(runMode) {
357
+ this.runMode = runMode;
358
+ }
359
+ getRunMode() {
360
+ return this.runMode;
361
+ }
362
+ requestPause() {
363
+ this.pauseRefCount++;
364
+ let released = false;
365
+ return () => {
366
+ if (released)
367
+ return;
368
+ released = true;
369
+ this.pauseRefCount--;
370
+ };
371
+ }
372
+ isPaused() {
373
+ return this.pauseRefCount > 0;
374
+ }
375
+ pause() {
376
+ if (!this.persistentPauseToken) {
377
+ this.persistentPauseToken = this.requestPause();
378
+ }
379
+ }
380
+ resume() {
381
+ if (this.persistentPauseToken) {
382
+ this.persistentPauseToken();
383
+ this.persistentPauseToken = null;
384
+ // If handle resolving was skipped, trigger it now
385
+ if (this.handleResolvingSkippedRef) {
386
+ const registry = this.graph.getRegistry();
387
+ if (registry) {
388
+ const result = tryHandleResolving(this.handleResolvingSkippedRef, registry, this.environment);
389
+ for (const [nodeId, handles] of result.resolved) {
390
+ this.graph.setResolvedHandles(nodeId, handles);
391
+ }
392
+ for (const nodeId of result.pending) {
393
+ this.handleResolver.scheduleRecomputeHandles(nodeId);
394
+ }
395
+ }
396
+ this.handleResolvingSkippedRef = undefined;
397
+ }
398
+ }
399
+ }
400
+ executeNodeAutoRun(nodeId) {
401
+ const node = this.graph.getNode(nodeId);
402
+ const shouldAutoRun = this.runMode === "auto" || node?.policy?.autoRun === true;
403
+ let runContextIdsToUse = undefined;
404
+ if (this.runMode === "manual") {
405
+ runContextIdsToUse = new Set([
406
+ this.runContextManager.createRunContext(nodeId, { propagate: false }),
407
+ ]);
408
+ }
409
+ if (shouldAutoRun && this.graph.allInboundHaveValue(nodeId)) {
410
+ this.execute(nodeId, runContextIdsToUse);
411
+ }
412
+ }
413
+ copyOutputs(fromNodeId, toNodeId, options) {
414
+ const fromNode = this.getNodeData(fromNodeId);
415
+ if (!fromNode?.outputs)
416
+ return;
417
+ this.hydrate({ outputs: { [toNodeId]: { ...fromNode.outputs } } }, { invalidate: !options?.dry });
418
+ this.handleResolver.scheduleRecomputeHandles(toNodeId);
419
+ this.executeNodeAutoRun(toNodeId);
420
+ }
421
+ hydrate(payload, opts) {
422
+ const releasePause = this.requestPause();
423
+ try {
424
+ const ins = payload?.inputs || {};
425
+ const nodesWithChangedInputs = new Set();
426
+ for (const [nodeId, map] of Object.entries(ins)) {
427
+ if (!this.graph.hasNode(nodeId))
428
+ continue;
429
+ let nodeChanged = false;
430
+ for (const [h, v] of Object.entries(map || {})) {
431
+ const node = this.graph.getNode(nodeId);
432
+ const prev = node?.inputs[h];
433
+ const clonedValue = structuredClone(v);
434
+ const same = valuesEqual(prev, clonedValue);
435
+ if (!same) {
436
+ this.graph.updateNodeInput(nodeId, h, clonedValue, false);
437
+ nodeChanged = true;
438
+ }
439
+ }
440
+ if (nodeChanged) {
441
+ nodesWithChangedInputs.add(nodeId);
442
+ }
443
+ }
444
+ const outs = payload?.outputs || {};
445
+ for (const [nodeId, map] of Object.entries(outs)) {
446
+ if (!this.graph.hasNode(nodeId))
447
+ continue;
448
+ for (const [h, v] of Object.entries(map || {})) {
449
+ const clonedValue = structuredClone(v);
450
+ this.graph.updateNodeOutput(nodeId, h, clonedValue);
451
+ }
452
+ }
453
+ // Trigger handle resolution for nodes with changed inputs
454
+ for (const nodeId of nodesWithChangedInputs) {
455
+ this.handleResolver.scheduleRecomputeHandles(nodeId);
456
+ }
457
+ if (opts?.invalidate) {
458
+ for (const nodeId of this.graph.getNodeIds()) {
459
+ this.invalidateDownstream(nodeId);
460
+ }
461
+ }
462
+ }
463
+ finally {
464
+ releasePause();
465
+ }
466
+ }
467
+ update(def, registry) {
468
+ {
469
+ // Delete nodes that are no longer in the definition
470
+ const afterIds = new Set(def.nodes.map((n) => n.nodeId));
471
+ const beforeIds = new Set(this.graph.getNodeIds());
472
+ for (const nodeId of Array.from(beforeIds)) {
473
+ if (!afterIds.has(nodeId)) {
474
+ const node = this.graph.getNode(nodeId);
475
+ if (!node)
476
+ continue;
477
+ this.nodeExecutor.cancelNodeActiveRuns(node, "node-deleted");
478
+ this.runContextManager.cancelNodeInRunContexts(nodeId, true);
479
+ node.runtime.onDeactivated?.();
480
+ node.runtime.dispose?.();
481
+ node.lifecycle?.dispose?.({
482
+ state: node.state,
483
+ setState: (next) => this.graph.updateNodeState(node.nodeId, next),
484
+ });
485
+ this.graph.deleteNode(nodeId);
486
+ this.edgePropagator.clearArrayBuckets(nodeId);
487
+ }
488
+ }
489
+ }
490
+ {
491
+ // Add or update nodes that are in the definition
492
+ for (const n of def.nodes) {
493
+ const existing = this.graph.getNode(n.nodeId);
494
+ if (!existing) {
495
+ const desc = registry.nodes.get(n.typeId);
496
+ if (!desc)
497
+ throw new Error(`Unknown node type: ${n.typeId}`);
498
+ const cat = registry.categories.get(desc.categoryId);
499
+ if (!cat)
500
+ throw new Error(`Unknown category: ${desc.categoryId}`);
501
+ if (cat.validateImpl)
502
+ cat.validateImpl(desc.impl);
503
+ const runtime = cat.createRuntime({
504
+ nodeId: n.nodeId,
505
+ impl: desc.impl,
506
+ });
507
+ const newNode = {
508
+ typeId: n.typeId,
509
+ nodeId: n.nodeId,
510
+ lifecycle: desc.lifecycle,
511
+ inputs: {},
512
+ outputs: {},
513
+ state: {},
514
+ runtime,
515
+ params: n.params,
516
+ policy: {
517
+ ...cat.policy,
518
+ ...desc.policy,
519
+ ...n.params?.policy,
520
+ },
521
+ runSeq: 0,
522
+ activeControllers: new Set(),
523
+ controllerRunIds: new Map(),
524
+ queue: [],
525
+ stats: {
526
+ runs: 0,
527
+ active: 0,
528
+ queued: 0,
529
+ progress: 0,
530
+ },
531
+ activeRunContextIds: new Set(),
532
+ lastInputAt: {},
533
+ lastSuccessAt: undefined,
534
+ };
535
+ this.graph.setNode(n.nodeId, newNode);
536
+ const effectiveInputs = this.nodeExecutor.getEffectiveInputs(newNode.nodeId);
537
+ const ctrl = new AbortController();
538
+ const execCtx = this.nodeExecutor.createExecutionContext(newNode.nodeId, newNode, effectiveInputs, `${newNode.nodeId}:init`, ctrl.signal);
539
+ if (newNode.lifecycle?.prepare) {
540
+ execCtx.log("debug", "prepare-start");
541
+ newNode.lifecycle.prepare(newNode.params ?? {}, execCtx);
542
+ execCtx.log("debug", "prepare-done");
543
+ }
544
+ newNode.runtime.onActivated?.();
545
+ }
546
+ else {
547
+ this.graph.updateNodeParams(n.nodeId, n.params);
548
+ // Re-merge policy when params change (params.policy can override descriptor/category policy)
549
+ const desc = registry.nodes.get(existing.typeId);
550
+ const cat = registry.categories.get(desc?.categoryId ?? "");
551
+ this.graph.updateNodePolicy(n.nodeId, {
552
+ ...cat?.policy,
553
+ ...desc?.policy,
554
+ ...n.params?.policy,
555
+ });
556
+ // Initialize stats if missing
557
+ if (!existing.stats) {
558
+ this.graph.updateNodeStats(n.nodeId, {
559
+ runs: 0,
560
+ active: 0,
561
+ queued: 0,
562
+ progress: 0,
563
+ });
564
+ }
565
+ }
566
+ }
567
+ }
568
+ {
569
+ const beforeInbound = new Map();
570
+ const beforeOutTargets = new Map();
571
+ this.graph.forEachEdge((e) => {
572
+ // Build beforeInbound map
573
+ const set = beforeInbound.get(e.target.nodeId) ?? new Set();
574
+ set.add(e.target.handle);
575
+ beforeInbound.set(e.target.nodeId, set);
576
+ // Build beforeOutTargets map
577
+ const tmap = beforeOutTargets.get(e.source.nodeId) ??
578
+ new Map();
579
+ const tset = tmap.get(e.source.handle) ?? new Set();
580
+ tset.add(`${e.target.nodeId}.${e.target.handle}`);
581
+ tmap.set(e.source.handle, tset);
582
+ beforeOutTargets.set(e.source.nodeId, tmap);
583
+ });
584
+ {
585
+ // Update handles and edges
586
+ const result = this.isPaused()
587
+ ? {
588
+ resolved: new Map(),
589
+ pending: new Set(),
590
+ }
591
+ : tryHandleResolving(def, registry, this.environment);
592
+ if (this.isPaused()) {
593
+ this.handleResolvingSkippedRef = def;
594
+ }
595
+ const changedHandles = {};
596
+ for (const [nodeId, newHandles] of result.resolved) {
597
+ const oldHandles = this.graph.getResolvedHandles(nodeId);
598
+ if (!oldHandles ||
599
+ JSON.stringify(oldHandles) !== JSON.stringify(newHandles)) {
600
+ changedHandles[nodeId] = newHandles;
601
+ }
602
+ }
603
+ for (const [nodeId, handles] of result.resolved) {
604
+ this.graph.setResolvedHandles(nodeId, handles);
605
+ }
606
+ const resolvedByNode = new Map();
607
+ this.graph.forEachResolvedHandles((handles, nodeId) => {
608
+ resolvedByNode.set(nodeId, handles);
609
+ });
610
+ const afterEdges = buildEdges(def, registry, resolvedByNode);
611
+ this.graph.setEdges(afterEdges);
612
+ for (const nodeId of result.pending) {
613
+ this.handleResolver.scheduleRecomputeHandles(nodeId);
614
+ }
615
+ if (Object.keys(changedHandles).length > 0) {
616
+ this.eventEmitter.emit("invalidate", {
617
+ reason: "graph-updated",
618
+ resolvedHandles: changedHandles,
619
+ });
620
+ }
621
+ }
622
+ {
623
+ // Update inputs and propagate changes
624
+ const afterInbound = new Map();
625
+ this.graph.forEachEdge((e) => {
626
+ const set = afterInbound.get(e.target.nodeId) ?? new Set();
627
+ set.add(e.target.handle);
628
+ afterInbound.set(e.target.nodeId, set);
629
+ });
630
+ // Propagate changes on edges removed
631
+ for (const [nodeId, beforeSet] of beforeInbound) {
632
+ const currSet = afterInbound.get(nodeId) ?? new Set();
633
+ if (!this.graph.hasNode(nodeId))
634
+ continue;
635
+ let changed = false;
636
+ for (const handle of Array.from(beforeSet)) {
637
+ if (!currSet.has(handle)) {
638
+ const node = this.graph.getNode(nodeId);
639
+ if (node && handle in node.inputs) {
640
+ this.graph.updateNodeInput(nodeId, handle, undefined);
641
+ changed = true;
642
+ }
643
+ }
644
+ }
645
+ if (changed) {
646
+ this.edgePropagator.clearArrayBuckets(nodeId);
647
+ // Trigger handle resolution when inputs are removed
648
+ this.handleResolver.scheduleRecomputeHandles(nodeId);
649
+ this.executeNodeAutoRun(nodeId);
650
+ }
651
+ }
652
+ // Propagate changes on edges added
653
+ const afterOutTargets = new Map();
654
+ this.graph.forEachEdge((e) => {
655
+ const targetMap = afterOutTargets.get(e.source.nodeId) ??
656
+ new Map();
657
+ const targetSet = targetMap.get(e.source.handle) ?? new Set();
658
+ targetSet.add(`${e.target.nodeId}.${e.target.handle}`);
659
+ targetMap.set(e.source.handle, targetSet);
660
+ afterOutTargets.set(e.source.nodeId, targetMap);
661
+ });
662
+ const setsEqual = (a, b) => {
663
+ if (!a && !b)
664
+ return true;
665
+ if (!a || !b)
666
+ return false;
667
+ if (a.size !== b.size)
668
+ return false;
669
+ for (const v of a)
670
+ if (!b.has(v))
671
+ return false;
672
+ return true;
673
+ };
674
+ const nodesToCheck = new Set([
675
+ ...Array.from(beforeOutTargets.keys()),
676
+ ...Array.from(afterOutTargets.keys()),
677
+ ]);
678
+ for (const nodeId of nodesToCheck) {
679
+ const beforeMap = beforeOutTargets.get(nodeId) ?? new Map();
680
+ const afterMap = afterOutTargets.get(nodeId) ?? new Map();
681
+ const handles = new Set([
682
+ ...Array.from(beforeMap.keys()),
683
+ ...Array.from(afterMap.keys()),
684
+ ]);
685
+ for (const handle of handles) {
686
+ const beforeTargetSet = beforeMap.get(handle) ?? new Set();
687
+ const afterTargetSet = afterMap.get(handle) ?? new Set();
688
+ if (!setsEqual(beforeTargetSet, afterTargetSet)) {
689
+ const val = this.getOutput(nodeId, handle);
690
+ if (val !== undefined) {
691
+ let runContextIdsToUse = undefined;
692
+ if (this.runMode === "manual") {
693
+ runContextIdsToUse = new Set([
694
+ this.runContextManager.createRunContext(nodeId, {
695
+ propagate: false,
696
+ }),
697
+ ]);
698
+ }
699
+ this.propagate(nodeId, handle, val, runContextIdsToUse);
700
+ }
701
+ }
702
+ }
703
+ }
704
+ }
705
+ }
706
+ }
707
+ dispose() {
708
+ this.runContextManager.resolveAll();
709
+ this.graph.forEachNode((node) => {
710
+ node.runtime.onDeactivated?.();
711
+ node.runtime.dispose?.();
712
+ node.lifecycle?.dispose?.({
713
+ state: node.state,
714
+ setState: (next) => this.graph.updateNodeState(node.nodeId, next),
715
+ });
716
+ });
717
+ this.graph.clear();
718
+ }
719
+ execute(nodeId, runContextIds, canSkipHandleResolution) {
720
+ this.nodeExecutor.execute(nodeId, runContextIds, canSkipHandleResolution);
721
+ }
722
+ propagate(srcNodeId, srcHandle, value, runContextIds) {
723
+ this.edgePropagator.propagate(srcNodeId, srcHandle, value, runContextIds);
724
+ }
725
+ invalidateDownstream(nodeId) {
726
+ this.edgePropagator.invalidateDownstream(nodeId);
727
+ }
728
+ }
729
+ //# sourceMappingURL=GraphRuntime.js.map