@bian-womp/spark-workbench 0.3.90 → 0.3.91

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 (181) hide show
  1. package/lib/cjs/index.cjs +19 -6
  2. package/lib/cjs/index.cjs.map +1 -1
  3. package/lib/cjs/src/misc/layout.d.ts.map +1 -1
  4. package/lib/cjs/src/misc/mapping.d.ts.map +1 -1
  5. package/lib/cjs/src/misc/value.d.ts +2 -1
  6. package/lib/cjs/src/misc/value.d.ts.map +1 -1
  7. package/lib/esm/index.js +19 -6
  8. package/lib/esm/index.js.map +1 -1
  9. package/lib/esm/src/misc/layout.d.ts.map +1 -1
  10. package/lib/esm/src/misc/mapping.d.ts.map +1 -1
  11. package/lib/esm/src/misc/value.d.ts +2 -1
  12. package/lib/esm/src/misc/value.d.ts.map +1 -1
  13. package/package.json +4 -4
  14. package/lib/src/adapters/cli/index.d.ts +0 -22
  15. package/lib/src/adapters/cli/index.d.ts.map +0 -1
  16. package/lib/src/adapters/cli/index.js +0 -50
  17. package/lib/src/adapters/cli/index.js.map +0 -1
  18. package/lib/src/core/AbstractWorkbench.d.ts +0 -40
  19. package/lib/src/core/AbstractWorkbench.d.ts.map +0 -1
  20. package/lib/src/core/AbstractWorkbench.js +0 -15
  21. package/lib/src/core/AbstractWorkbench.js.map +0 -1
  22. package/lib/src/core/InMemoryWorkbench.d.ts +0 -304
  23. package/lib/src/core/InMemoryWorkbench.d.ts.map +0 -1
  24. package/lib/src/core/InMemoryWorkbench.js +0 -1016
  25. package/lib/src/core/InMemoryWorkbench.js.map +0 -1
  26. package/lib/src/core/contracts.d.ts +0 -172
  27. package/lib/src/core/contracts.d.ts.map +0 -1
  28. package/lib/src/core/contracts.js +0 -2
  29. package/lib/src/core/contracts.js.map +0 -1
  30. package/lib/src/core/ui-extensions.d.ts +0 -85
  31. package/lib/src/core/ui-extensions.d.ts.map +0 -1
  32. package/lib/src/core/ui-extensions.js +0 -111
  33. package/lib/src/core/ui-extensions.js.map +0 -1
  34. package/lib/src/examples/cli.d.ts +0 -2
  35. package/lib/src/examples/cli.d.ts.map +0 -1
  36. package/lib/src/examples/cli.js +0 -244
  37. package/lib/src/examples/cli.js.map +0 -1
  38. package/lib/src/examples/reactflow/App.d.ts +0 -2
  39. package/lib/src/examples/reactflow/App.d.ts.map +0 -1
  40. package/lib/src/examples/reactflow/App.js +0 -20
  41. package/lib/src/examples/reactflow/App.js.map +0 -1
  42. package/lib/src/index.d.ts +0 -30
  43. package/lib/src/index.d.ts.map +0 -1
  44. package/lib/src/index.js +0 -30
  45. package/lib/src/index.js.map +0 -1
  46. package/lib/src/misc/DebugEvents.d.ts +0 -7
  47. package/lib/src/misc/DebugEvents.d.ts.map +0 -1
  48. package/lib/src/misc/DebugEvents.js +0 -51
  49. package/lib/src/misc/DebugEvents.js.map +0 -1
  50. package/lib/src/misc/DefaultEdge.d.ts +0 -5
  51. package/lib/src/misc/DefaultEdge.d.ts.map +0 -1
  52. package/lib/src/misc/DefaultEdge.js +0 -15
  53. package/lib/src/misc/DefaultEdge.js.map +0 -1
  54. package/lib/src/misc/DefaultNode.d.ts +0 -5
  55. package/lib/src/misc/DefaultNode.d.ts.map +0 -1
  56. package/lib/src/misc/DefaultNode.js +0 -25
  57. package/lib/src/misc/DefaultNode.js.map +0 -1
  58. package/lib/src/misc/DefaultNodeContent.d.ts +0 -4
  59. package/lib/src/misc/DefaultNodeContent.d.ts.map +0 -1
  60. package/lib/src/misc/DefaultNodeContent.js +0 -58
  61. package/lib/src/misc/DefaultNodeContent.js.map +0 -1
  62. package/lib/src/misc/DefaultNodeHeader.d.ts +0 -13
  63. package/lib/src/misc/DefaultNodeHeader.d.ts.map +0 -1
  64. package/lib/src/misc/DefaultNodeHeader.js +0 -78
  65. package/lib/src/misc/DefaultNodeHeader.js.map +0 -1
  66. package/lib/src/misc/Inspector.d.ts +0 -12
  67. package/lib/src/misc/Inspector.d.ts.map +0 -1
  68. package/lib/src/misc/Inspector.js +0 -253
  69. package/lib/src/misc/Inspector.js.map +0 -1
  70. package/lib/src/misc/IssueBadge.d.ts +0 -7
  71. package/lib/src/misc/IssueBadge.d.ts.map +0 -1
  72. package/lib/src/misc/IssueBadge.js +0 -7
  73. package/lib/src/misc/IssueBadge.js.map +0 -1
  74. package/lib/src/misc/KeyboardShortcutToast.d.ts +0 -16
  75. package/lib/src/misc/KeyboardShortcutToast.d.ts.map +0 -1
  76. package/lib/src/misc/KeyboardShortcutToast.js +0 -40
  77. package/lib/src/misc/KeyboardShortcutToast.js.map +0 -1
  78. package/lib/src/misc/NodeHandles.d.ts +0 -18
  79. package/lib/src/misc/NodeHandles.d.ts.map +0 -1
  80. package/lib/src/misc/NodeHandles.js +0 -67
  81. package/lib/src/misc/NodeHandles.js.map +0 -1
  82. package/lib/src/misc/SelectionActiveSync.d.ts +0 -10
  83. package/lib/src/misc/SelectionActiveSync.d.ts.map +0 -1
  84. package/lib/src/misc/SelectionActiveSync.js +0 -21
  85. package/lib/src/misc/SelectionActiveSync.js.map +0 -1
  86. package/lib/src/misc/WorkbenchCanvas.d.ts +0 -23
  87. package/lib/src/misc/WorkbenchCanvas.d.ts.map +0 -1
  88. package/lib/src/misc/WorkbenchCanvas.js +0 -669
  89. package/lib/src/misc/WorkbenchCanvas.js.map +0 -1
  90. package/lib/src/misc/WorkbenchStudio.d.ts +0 -43
  91. package/lib/src/misc/WorkbenchStudio.d.ts.map +0 -1
  92. package/lib/src/misc/WorkbenchStudio.js +0 -463
  93. package/lib/src/misc/WorkbenchStudio.js.map +0 -1
  94. package/lib/src/misc/constants.d.ts +0 -4
  95. package/lib/src/misc/constants.d.ts.map +0 -1
  96. package/lib/src/misc/constants.js +0 -5
  97. package/lib/src/misc/constants.js.map +0 -1
  98. package/lib/src/misc/context/WorkbenchContext.d.ts +0 -133
  99. package/lib/src/misc/context/WorkbenchContext.d.ts.map +0 -1
  100. package/lib/src/misc/context/WorkbenchContext.js +0 -9
  101. package/lib/src/misc/context/WorkbenchContext.js.map +0 -1
  102. package/lib/src/misc/context/WorkbenchContext.provider.d.ts +0 -12
  103. package/lib/src/misc/context/WorkbenchContext.provider.d.ts.map +0 -1
  104. package/lib/src/misc/context/WorkbenchContext.provider.js +0 -995
  105. package/lib/src/misc/context/WorkbenchContext.provider.js.map +0 -1
  106. package/lib/src/misc/context-menu/ContextMenuButton.d.ts +0 -8
  107. package/lib/src/misc/context-menu/ContextMenuButton.d.ts.map +0 -1
  108. package/lib/src/misc/context-menu/ContextMenuButton.js +0 -10
  109. package/lib/src/misc/context-menu/ContextMenuButton.js.map +0 -1
  110. package/lib/src/misc/context-menu/ContextMenuHandlers.d.ts +0 -85
  111. package/lib/src/misc/context-menu/ContextMenuHandlers.d.ts.map +0 -1
  112. package/lib/src/misc/context-menu/ContextMenuHandlers.js +0 -2
  113. package/lib/src/misc/context-menu/ContextMenuHandlers.js.map +0 -1
  114. package/lib/src/misc/context-menu/ContextMenuHelpers.d.ts +0 -45
  115. package/lib/src/misc/context-menu/ContextMenuHelpers.d.ts.map +0 -1
  116. package/lib/src/misc/context-menu/ContextMenuHelpers.js +0 -182
  117. package/lib/src/misc/context-menu/ContextMenuHelpers.js.map +0 -1
  118. package/lib/src/misc/context-menu/DefaultContextMenu.d.ts +0 -3
  119. package/lib/src/misc/context-menu/DefaultContextMenu.d.ts.map +0 -1
  120. package/lib/src/misc/context-menu/DefaultContextMenu.js +0 -111
  121. package/lib/src/misc/context-menu/DefaultContextMenu.js.map +0 -1
  122. package/lib/src/misc/context-menu/NodeContextMenu.d.ts +0 -3
  123. package/lib/src/misc/context-menu/NodeContextMenu.d.ts.map +0 -1
  124. package/lib/src/misc/context-menu/NodeContextMenu.js +0 -51
  125. package/lib/src/misc/context-menu/NodeContextMenu.js.map +0 -1
  126. package/lib/src/misc/context-menu/SelectionContextMenu.d.ts +0 -3
  127. package/lib/src/misc/context-menu/SelectionContextMenu.d.ts.map +0 -1
  128. package/lib/src/misc/context-menu/SelectionContextMenu.js +0 -47
  129. package/lib/src/misc/context-menu/SelectionContextMenu.js.map +0 -1
  130. package/lib/src/misc/hooks.d.ts +0 -18
  131. package/lib/src/misc/hooks.d.ts.map +0 -1
  132. package/lib/src/misc/hooks.js +0 -275
  133. package/lib/src/misc/hooks.js.map +0 -1
  134. package/lib/src/misc/layout.d.ts +0 -117
  135. package/lib/src/misc/layout.d.ts.map +0 -1
  136. package/lib/src/misc/layout.js +0 -205
  137. package/lib/src/misc/layout.js.map +0 -1
  138. package/lib/src/misc/load.d.ts +0 -5
  139. package/lib/src/misc/load.d.ts.map +0 -1
  140. package/lib/src/misc/load.js +0 -106
  141. package/lib/src/misc/load.js.map +0 -1
  142. package/lib/src/misc/mapping.d.ts +0 -128
  143. package/lib/src/misc/mapping.d.ts.map +0 -1
  144. package/lib/src/misc/mapping.js +0 -270
  145. package/lib/src/misc/mapping.js.map +0 -1
  146. package/lib/src/misc/merge-utils.d.ts +0 -12
  147. package/lib/src/misc/merge-utils.d.ts.map +0 -1
  148. package/lib/src/misc/merge-utils.js +0 -51
  149. package/lib/src/misc/merge-utils.js.map +0 -1
  150. package/lib/src/misc/thumbnail-utils.d.ts +0 -53
  151. package/lib/src/misc/thumbnail-utils.d.ts.map +0 -1
  152. package/lib/src/misc/thumbnail-utils.js +0 -629
  153. package/lib/src/misc/thumbnail-utils.js.map +0 -1
  154. package/lib/src/misc/types.d.ts +0 -18
  155. package/lib/src/misc/types.d.ts.map +0 -1
  156. package/lib/src/misc/types.js +0 -2
  157. package/lib/src/misc/types.js.map +0 -1
  158. package/lib/src/misc/value.d.ts +0 -16
  159. package/lib/src/misc/value.d.ts.map +0 -1
  160. package/lib/src/misc/value.js +0 -114
  161. package/lib/src/misc/value.js.map +0 -1
  162. package/lib/src/misc/viewport-utils.d.ts +0 -6
  163. package/lib/src/misc/viewport-utils.d.ts.map +0 -1
  164. package/lib/src/misc/viewport-utils.js +0 -18
  165. package/lib/src/misc/viewport-utils.js.map +0 -1
  166. package/lib/src/runtime/AbstractGraphRunner.d.ts +0 -61
  167. package/lib/src/runtime/AbstractGraphRunner.d.ts.map +0 -1
  168. package/lib/src/runtime/AbstractGraphRunner.js +0 -63
  169. package/lib/src/runtime/AbstractGraphRunner.js.map +0 -1
  170. package/lib/src/runtime/IGraphRunner.d.ts +0 -100
  171. package/lib/src/runtime/IGraphRunner.d.ts.map +0 -1
  172. package/lib/src/runtime/IGraphRunner.js +0 -2
  173. package/lib/src/runtime/IGraphRunner.js.map +0 -1
  174. package/lib/src/runtime/LocalGraphRunner.d.ts +0 -60
  175. package/lib/src/runtime/LocalGraphRunner.d.ts.map +0 -1
  176. package/lib/src/runtime/LocalGraphRunner.js +0 -294
  177. package/lib/src/runtime/LocalGraphRunner.js.map +0 -1
  178. package/lib/src/runtime/RemoteGraphRunner.d.ts +0 -109
  179. package/lib/src/runtime/RemoteGraphRunner.d.ts.map +0 -1
  180. package/lib/src/runtime/RemoteGraphRunner.js +0 -696
  181. package/lib/src/runtime/RemoteGraphRunner.js.map +0 -1
@@ -1,696 +0,0 @@
1
- import { RemoteRuntimeClient, } from "@bian-womp/spark-remote";
2
- import { AbstractGraphRunner } from "./AbstractGraphRunner";
3
- import { isValidViewport } from "../misc/viewport-utils";
4
- // Counter for generating readable runner IDs
5
- let remoteRunnerCounter = 0;
6
- export class RemoteGraphRunner extends AbstractGraphRunner {
7
- /**
8
- * Generate cache key that includes io type to prevent collisions
9
- * between input and output handles with the same name
10
- */
11
- getCacheKey(nodeId, handle, io) {
12
- return `${nodeId}.${io}.${handle}`;
13
- }
14
- applyRegistryDescriptor(desc) {
15
- for (const t of desc.types) {
16
- if (t.options) {
17
- this.registry.registerEnum({
18
- id: t.id,
19
- options: t.options,
20
- bakeTarget: t.bakeTarget,
21
- });
22
- }
23
- else if (!this.registry.types.has(t.id)) {
24
- this.registry.registerType({
25
- id: t.id,
26
- displayName: t.displayName,
27
- validate: (_v) => true,
28
- bakeTarget: t.bakeTarget,
29
- });
30
- }
31
- }
32
- for (const c of desc.categories || []) {
33
- if (!this.registry.categories.has(c.id)) {
34
- const category = {
35
- id: c.id,
36
- displayName: c.displayName,
37
- createRuntime: () => ({
38
- async onInputsChanged() { },
39
- }),
40
- policy: { asyncConcurrency: "switch" },
41
- };
42
- this.registry.categories.register(category);
43
- }
44
- }
45
- for (const c of desc.coercions) {
46
- if (c.async) {
47
- this.registry.registerAsyncCoercion(c.from, c.to, async (v) => v, {
48
- nonTransitive: c.nonTransitive,
49
- });
50
- }
51
- else {
52
- this.registry.registerCoercion(c.from, c.to, (v) => v, {
53
- nonTransitive: c.nonTransitive,
54
- });
55
- }
56
- }
57
- for (const n of desc.nodes) {
58
- if (!this.registry.nodes.has(n.id)) {
59
- this.registry.registerNode({
60
- id: n.id,
61
- categoryId: n.categoryId,
62
- displayName: n.displayName,
63
- inputs: n.inputs || {},
64
- outputs: n.outputs || {},
65
- policy: n.policy || {},
66
- impl: () => { },
67
- });
68
- }
69
- }
70
- this.registryFetched = true;
71
- if (this.registryBootstrapResolve) {
72
- this.registryBootstrapResolve();
73
- this.registryBootstrapResolve = undefined;
74
- this.registryBootstrapReject = undefined;
75
- }
76
- this.emit("registry", this.registry);
77
- }
78
- isRecord(value) {
79
- return typeof value === "object" && value !== null;
80
- }
81
- isRegistryEvent(message) {
82
- if (!this.isRecord(message))
83
- return false;
84
- if (message.type !== "registry")
85
- return false;
86
- if (!this.isRecord(message.payload))
87
- return false;
88
- return "registry" in message.payload;
89
- }
90
- waitForRegistryBootstrap() {
91
- if (this.registryFetched)
92
- return Promise.resolve();
93
- if (this.registryBootstrapPromise)
94
- return this.registryBootstrapPromise;
95
- this.registryBootstrapPromise = new Promise((resolve, reject) => {
96
- this.registryBootstrapResolve = resolve;
97
- this.registryBootstrapReject = reject;
98
- setTimeout(() => {
99
- if (!this.registryFetched && this.registryBootstrapReject) {
100
- this.registryBootstrapReject(new Error(`Registry bootstrap timeout (${this.REGISTRY_BOOTSTRAP_TIMEOUT_MS}ms exceeded)`));
101
- this.registryBootstrapResolve = undefined;
102
- this.registryBootstrapReject = undefined;
103
- }
104
- }, this.REGISTRY_BOOTSTRAP_TIMEOUT_MS);
105
- }).finally(() => {
106
- this.registryBootstrapPromise = undefined;
107
- });
108
- return this.registryBootstrapPromise;
109
- }
110
- /**
111
- * Build RemoteRuntimeClient config from RemoteExecutionBackend config.
112
- */
113
- buildClientConfig(backend) {
114
- if (backend.kind === "remote-http") {
115
- return {
116
- kind: "remote-http",
117
- baseUrl: backend.baseUrl,
118
- connectOptions: backend.connectOptions,
119
- };
120
- }
121
- else {
122
- return {
123
- kind: "remote-ws",
124
- url: backend.url,
125
- connectOptions: backend.connectOptions,
126
- };
127
- }
128
- }
129
- /**
130
- * Setup event subscriptions for the client.
131
- */
132
- setupClientSubscriptions(client) {
133
- // Subscribe to transport status changes
134
- // Convert RemoteRuntimeClient.TransportStatus to IGraphRunner.TransportStatus
135
- // Only emit status if it matches this runner's ID
136
- this.transportStatusUnsubscribe = client.onTransportStatus((status) => {
137
- if (status.runnerId && status.runnerId !== this.runnerId)
138
- return;
139
- // Map remote-unix to undefined since RemoteGraphRunner doesn't support it
140
- const mappedKind = status.kind === "remote-unix" ? undefined : status.kind;
141
- const transportStatus = {
142
- state: status.state,
143
- kind: mappedKind,
144
- runnerId: this.runnerId,
145
- };
146
- // Track current status
147
- this.currentTransportStatus = transportStatus;
148
- this.emit("transport", transportStatus);
149
- });
150
- }
151
- // Ensure remote client
152
- async ensureClient() {
153
- if (this.disposed) {
154
- throw new Error("Cannot ensure client: RemoteGraphRunner has been disposed");
155
- }
156
- if (this.client)
157
- return this.client;
158
- // If already connecting, wait for that connection to complete
159
- if (this.clientPromise)
160
- return this.clientPromise;
161
- const backend = this.backend;
162
- // Create connection promise to prevent concurrent connections
163
- // Create promise wrapper first, then assign immediately before async work starts
164
- let promise;
165
- this.clientPromise = promise = (async () => {
166
- // Build client config from backend config
167
- const clientConfig = this.buildClientConfig(backend);
168
- // Wrap custom event handler to intercept viewport events and emit viewport event
169
- const wrappedOnCustomEvent = (event) => {
170
- const msg = event.message;
171
- if (this.isRecord(msg) && msg.type === "viewport" && this.isRecord(msg.payload)) {
172
- const viewport = msg.payload.viewport;
173
- if (isValidViewport(viewport)) {
174
- this.emit("viewport", { viewport });
175
- }
176
- }
177
- else if (this.isRecord(msg) && msg.type === "flow-opened" && this.isRecord(msg.payload)) {
178
- const sessionId = msg.payload.sessionId;
179
- const resumed = msg.payload.resumed;
180
- if (typeof sessionId === "number") {
181
- console.info(`[RemoteGraphRunner] Flow opened (runner=${this.runnerId}, sessionId=${sessionId}, resumed=${Boolean(resumed)})`);
182
- }
183
- }
184
- else if (this.isRecord(msg) && msg.type === "flow-closed" && this.isRecord(msg.payload)) {
185
- const reason = msg.payload.reason;
186
- console.warn(`[RemoteGraphRunner] Flow closed (runner=${this.runnerId}, reason=${typeof reason === "string" ? reason : "unknown"})`);
187
- }
188
- else if (this.isRegistryEvent(msg)) {
189
- this.applyRegistryDescriptor(msg.payload.registry);
190
- }
191
- // Call original handler if provided
192
- if (backend.onCustomEvent) {
193
- backend.onCustomEvent(event);
194
- }
195
- };
196
- // Create client with wrapped custom event handler
197
- const client = new RemoteRuntimeClient(clientConfig, {
198
- onCustomEvent: wrappedOnCustomEvent,
199
- runnerId: this.runnerId,
200
- });
201
- // Setup event subscriptions
202
- this.setupClientSubscriptions(client);
203
- // Connect the client (this will create and connect transport)
204
- await client.connect();
205
- this.client = client;
206
- this.valueCache.clear();
207
- this.listenersBound = false;
208
- // Wait for registry runtime event pushed by backend on flow-open.
209
- if (!this.registryFetched) {
210
- console.info("[RemoteGraphRunner] Waiting for registry bootstrap event...");
211
- await this.waitForRegistryBootstrap();
212
- console.info("[RemoteGraphRunner] Received registry bootstrap event");
213
- }
214
- // Clear promise on success
215
- this.clientPromise = undefined;
216
- return client;
217
- })();
218
- return promise;
219
- }
220
- constructor(backend) {
221
- super(backend);
222
- this.disposed = false;
223
- this.valueCache = new Map();
224
- this.listenersBound = false;
225
- this.registryFetched = false;
226
- this.REGISTRY_BOOTSTRAP_TIMEOUT_MS = 15000; // 15 seconds
227
- // Generate readable ID for this runner instance (e.g., remote-001, remote-002)
228
- remoteRunnerCounter++;
229
- this.runnerId = `remote-${String(remoteRunnerCounter).padStart(3, "0")}`;
230
- console.info(`[RemoteGraphRunner] Created runner with ID: ${this.runnerId}`);
231
- // Initialize transport status as "connecting" - will be updated when connection completes
232
- this.currentTransportStatus = {
233
- state: "connecting",
234
- kind: backend.kind,
235
- };
236
- // Auto-handle registry-changed invalidations from remote
237
- // We listen on invalidate and if reason matches, we rehydrate registry and emit a registry event
238
- this.ensureClient().then(async (client) => {
239
- const eng = client.engine;
240
- if (!this.listenersBound) {
241
- eng.on("invalidate", async (e) => {
242
- if (e.reason === "registry-changed") {
243
- try {
244
- const deltas = Array.isArray(e.deltas) ? e.deltas : [];
245
- for (const d of deltas) {
246
- if (!d || typeof d !== "object")
247
- continue;
248
- if (d.kind === "register-enum") {
249
- this.registry.registerEnum({
250
- id: d.id,
251
- displayName: d.displayName,
252
- options: d.options,
253
- opts: d.opts,
254
- bakeTarget: d.bakeTarget,
255
- });
256
- }
257
- else if (d.kind === "register-type") {
258
- if (!this.registry.types.has(d.id)) {
259
- this.registry.registerType({
260
- id: d.id,
261
- displayName: d.displayName,
262
- validate: (_v) => true,
263
- bakeTarget: d.bakeTarget,
264
- });
265
- }
266
- }
267
- else if (d.kind === "register-node") {
268
- if (!this.registry.nodes.has(d.desc?.id)) {
269
- this.registry.registerNode({
270
- id: String(d.desc?.id || ""),
271
- categoryId: String(d.desc?.categoryId || "compute"),
272
- displayName: d.desc?.displayName,
273
- inputs: d.desc?.inputs || {},
274
- outputs: d.desc?.outputs || {},
275
- impl: () => { },
276
- });
277
- }
278
- }
279
- }
280
- this.emit("registry", this.registry);
281
- // Trigger update so validation/UI refreshes using last known graph
282
- try {
283
- if (this.lastDef)
284
- await this.update(this.lastDef);
285
- }
286
- catch {
287
- console.error("Failed to update graph definition after registry changed");
288
- }
289
- }
290
- catch {
291
- console.error("Failed to handle registry changed event");
292
- }
293
- }
294
- });
295
- }
296
- });
297
- }
298
- build(def) { }
299
- async update(def, options) {
300
- // Remote: forward update and await completion
301
- const client = await this.ensureClient();
302
- try {
303
- await client.api.update(def, options);
304
- this.emit("invalidate", { reason: "graph-updated" });
305
- this.lastDef = def;
306
- }
307
- catch (err) {
308
- // Log error but don't throw to maintain compatibility
309
- console.error("[RemoteGraphRunner] Error updating graph:", err);
310
- }
311
- }
312
- launch(def, opts) {
313
- if (this.engine) {
314
- this.engine.dispose();
315
- this.engine = undefined;
316
- }
317
- // Remote: build remotely then launch
318
- this.ensureClient().then(async (client) => {
319
- await client.api.build(def);
320
- // Signal UI after remote build as well
321
- this.emit("invalidate", { reason: "graph-built" });
322
- this.lastDef = def;
323
- // Hydrate current remote inputs/outputs (including defaults) into cache
324
- try {
325
- const snap = await client.api.snapshot();
326
- for (const [nodeId, map] of Object.entries(snap.inputs || {})) {
327
- for (const [handle, value] of Object.entries(map || {})) {
328
- this.valueCache.set(this.getCacheKey(nodeId, handle, "input"), {
329
- value,
330
- });
331
- this.emit("value", { nodeId, handle, value, io: "input" });
332
- }
333
- }
334
- for (const [nodeId, map] of Object.entries(snap.outputs || {})) {
335
- for (const [handle, value] of Object.entries(map || {})) {
336
- this.valueCache.set(this.getCacheKey(nodeId, handle, "output"), {
337
- value,
338
- });
339
- this.emit("value", { nodeId, handle, value, io: "output" });
340
- }
341
- }
342
- }
343
- catch {
344
- console.error("Failed to hydrate remote inputs/outputs");
345
- }
346
- await this.createAndLaunchEngine(opts);
347
- });
348
- }
349
- async createAndLaunchEngine(opts) {
350
- const client = await this.ensureClient();
351
- // Configure and launch engine on the backend
352
- await client.api.launch(opts);
353
- // Get the remote engine proxy and wire up event listeners
354
- const eng = client.engine;
355
- if (!this.listenersBound) {
356
- eng.on("value", (e) => {
357
- this.valueCache.set(this.getCacheKey(e.nodeId, e.handle, e.io), {
358
- value: e.value,
359
- runtimeTypeId: e.runtimeTypeId,
360
- });
361
- this.emit("value", e);
362
- });
363
- eng.on("error", (e) => this.emit("error", e));
364
- eng.on("invalidate", (e) => this.emit("invalidate", e));
365
- eng.on("stats", (e) => this.emit("stats", e));
366
- this.listenersBound = true;
367
- }
368
- this.engine = eng;
369
- const runMode = opts?.runMode ?? "manual";
370
- this.emit("status", { running: true, runMode });
371
- // Re-apply staged inputs using client.setInputs for consistency
372
- for (const [nodeId, map] of Object.entries(this.stagedInputs)) {
373
- await eng.setInputs(nodeId, map, undefined).catch(() => {
374
- // Ignore errors during launch - inputs will be set when user calls setInputs
375
- });
376
- }
377
- }
378
- /**
379
- * Launch using an existing backend runtime that has already been built and hydrated.
380
- * This is used when resuming from a snapshot where the backend has already applied
381
- * ApplySnapshotFull (which builds the graph and hydrates inputs/outputs).
382
- * Unlike launch(), this method does NOT call client.build() to avoid destroying
383
- * the runtime state that was just restored.
384
- */
385
- launchExisting(def, opts) {
386
- if (this.engine) {
387
- this.engine.dispose();
388
- this.engine = undefined;
389
- }
390
- // Remote: attach to existing runtime and launch (do NOT rebuild)
391
- this.ensureClient().then(async (client) => {
392
- // NOTE: We do NOT call client.build(def) here because the backend runtime
393
- // has already been built and hydrated via ApplySnapshotFull.
394
- // Calling build() would create a new runtime and lose the restored state.
395
- this.lastDef = def;
396
- await this.createAndLaunchEngine(opts);
397
- });
398
- }
399
- setRunMode(runMode) {
400
- if (this.engine) {
401
- this.engine.setRunMode(runMode);
402
- this.emit("status", { running: true, runMode });
403
- }
404
- }
405
- async computeNode(nodeId) {
406
- const client = await this.ensureClient();
407
- await client.engine.computeNode(nodeId);
408
- }
409
- async runFromHere(nodeId) {
410
- const client = await this.ensureClient();
411
- await client.engine.runFromHere(nodeId);
412
- }
413
- async cancelNodeRuns(nodeIds) {
414
- const client = await this.ensureClient();
415
- await client.engine.cancelNodeRuns(nodeIds);
416
- }
417
- async setInputs(nodeId, inputs, options) {
418
- // Update staged inputs (for getInputs to work correctly)
419
- if (!this.stagedInputs[nodeId])
420
- this.stagedInputs[nodeId] = {};
421
- for (const [handle, value] of Object.entries(inputs)) {
422
- if (value === undefined) {
423
- delete this.stagedInputs[nodeId][handle];
424
- }
425
- else {
426
- this.stagedInputs[nodeId][handle] = value;
427
- }
428
- }
429
- try {
430
- const client = await this.ensureClient();
431
- await client.engine.setInputs(nodeId, inputs, options);
432
- }
433
- catch (err) {
434
- // Emit synthetic events if connection fails
435
- for (const [handle, value] of Object.entries(inputs)) {
436
- this.emit("value", { nodeId, handle, value, io: "input" });
437
- }
438
- throw err;
439
- }
440
- }
441
- async copyOutputs(fromNodeId, toNodeId, options) {
442
- const client = await this.ensureClient();
443
- await client.engine.copyOutputs(fromNodeId, toNodeId, options);
444
- }
445
- async triggerExternal(nodeId, event, options) {
446
- const client = await this.ensureClient();
447
- await client.engine.triggerExternal(nodeId, event, options);
448
- }
449
- async setViewport(viewport) {
450
- const client = await this.ensureClient();
451
- await client.api.setViewport(viewport);
452
- }
453
- async coerce(from, to, value) {
454
- const client = await this.ensureClient();
455
- try {
456
- return await client.api.coerce(from, to, value);
457
- }
458
- catch {
459
- return value;
460
- }
461
- }
462
- async setExtData(data) {
463
- const client = await this.ensureClient();
464
- await client.api.setExtData(data);
465
- }
466
- async updateExtData(updates) {
467
- const client = await this.ensureClient();
468
- return await client.api.updateExtData(updates);
469
- }
470
- async commit(reason) {
471
- const client = await this.ensureClient();
472
- try {
473
- const history = await client.api.commit(reason);
474
- return history;
475
- }
476
- catch (err) {
477
- console.error("[RemoteGraphRunner] Error committing:", err);
478
- throw err;
479
- }
480
- }
481
- async undo() {
482
- const client = await this.ensureClient();
483
- try {
484
- return await client.api.undo();
485
- }
486
- catch {
487
- return false;
488
- }
489
- }
490
- async redo() {
491
- const client = await this.ensureClient();
492
- try {
493
- return await client.api.redo();
494
- }
495
- catch {
496
- return false;
497
- }
498
- }
499
- async snapshotFull() {
500
- const client = await this.ensureClient();
501
- try {
502
- return await client.api.snapshotFull();
503
- }
504
- catch {
505
- return { inputs: {}, outputs: {} };
506
- }
507
- }
508
- async applySnapshotFull(payload, options) {
509
- // Hydrate local cache first so UI can display values immediately
510
- this.hydrateValueCache(payload, { dry: options?.dry });
511
- // Then sync with backend
512
- const client = await this.ensureClient();
513
- await client.api.applySnapshotFull(payload, { skipBuild: options?.skipBuild });
514
- }
515
- async convertSnapshot(converterConfig, options) {
516
- const client = await this.ensureClient();
517
- const converted = await client.api.convertSnapshot(converterConfig, {
518
- dry: options?.dry,
519
- });
520
- // Hydrate local cache with converted values
521
- this.hydrateValueCache(converted, { dry: options?.dry });
522
- return converted;
523
- }
524
- async pause() {
525
- const client = await this.ensureClient();
526
- await client.api.pause();
527
- }
528
- async resume() {
529
- const client = await this.ensureClient();
530
- await client.api.resume();
531
- }
532
- /**
533
- * Hydrates the local valueCache from a snapshot and emits value events.
534
- * This ensures the UI can display inputs/outputs immediately without waiting
535
- * for value events from the remote backend.
536
- */
537
- hydrateValueCache(snapshot, opts) {
538
- // Hydrate inputs
539
- for (const [nodeId, map] of Object.entries(snapshot.inputs || {})) {
540
- for (const [handle, value] of Object.entries(map || {})) {
541
- this.valueCache.set(this.getCacheKey(nodeId, handle, "input"), {
542
- value,
543
- });
544
- this.emit("value", {
545
- nodeId,
546
- handle,
547
- value,
548
- io: "input",
549
- dry: opts?.dry,
550
- });
551
- }
552
- }
553
- // Hydrate outputs
554
- for (const [nodeId, map] of Object.entries(snapshot.outputs || {})) {
555
- for (const [handle, value] of Object.entries(map || {})) {
556
- this.valueCache.set(this.getCacheKey(nodeId, handle, "output"), {
557
- value,
558
- });
559
- this.emit("value", {
560
- nodeId,
561
- handle,
562
- value,
563
- io: "output",
564
- dry: opts?.dry,
565
- });
566
- }
567
- }
568
- }
569
- async setEnvironment(env, opts) {
570
- // Use client if available, otherwise ensure client and then set environment
571
- if (this.client) {
572
- await this.client.api.setEnvironment(env, opts);
573
- }
574
- else {
575
- try {
576
- const client = await this.ensureClient();
577
- await client.api.setEnvironment(env, opts);
578
- }
579
- catch {
580
- // Silently fail if connection not available
581
- }
582
- }
583
- }
584
- getEnvironment() {
585
- // Interface requires sync return, but RemoteRuntimeClient.getEnvironment() is async.
586
- // Returns undefined synchronously; callers needing the actual value should:
587
- // - Use snapshotFull() which includes environment
588
- // - Call client.getEnvironment() directly if they have access to the client
589
- // This is a limitation of the sync interface signature.
590
- return undefined;
591
- }
592
- getOutputs(def) {
593
- const out = {};
594
- const cache = this.valueCache;
595
- if (!cache)
596
- return out;
597
- for (const n of def.nodes) {
598
- const resolved = n.resolvedHandles?.outputs;
599
- const desc = this.registry.nodes.get(n.typeId);
600
- const handles = Object.keys(resolved ?? desc?.outputs ?? {});
601
- for (const h of handles) {
602
- const key = this.getCacheKey(n.nodeId, h, "output");
603
- const rec = cache.get(key);
604
- if (rec) {
605
- if (!out[n.nodeId])
606
- out[n.nodeId] = {};
607
- out[n.nodeId][h] = rec.value;
608
- }
609
- }
610
- }
611
- return out;
612
- }
613
- getInputs(def) {
614
- const out = {};
615
- const cache = this.valueCache;
616
- const inputPrefix = ".input.";
617
- for (const n of def.nodes) {
618
- const staged = this.stagedInputs[n.nodeId] ?? {};
619
- const cur = {};
620
- // Include ALL input values from cache (not just resolved/desc handles).
621
- // Dynamic nodes (e.g. ext.scene.area) have geo handles that may not be in
622
- // resolvedHandles yet; we must return every user-set value for duplication.
623
- const prefix = `${n.nodeId}${inputPrefix}`;
624
- for (const [key, rec] of cache.entries()) {
625
- if (key.startsWith(prefix)) {
626
- const handle = key.slice(prefix.length);
627
- cur[handle] = rec.value;
628
- }
629
- }
630
- // Build inbound handle set for this node to honor wiring precedence
631
- const inbound = new Set(def.edges.filter((e) => e.target.nodeId === n.nodeId).map((e) => e.target.handle));
632
- // Merge staged only for non-inbound handles so UI doesn't override runtime values
633
- const merged = { ...cur };
634
- for (const [h, v] of Object.entries(staged)) {
635
- if (!inbound.has(h))
636
- merged[h] = v;
637
- }
638
- if (Object.keys(merged).length > 0)
639
- out[n.nodeId] = merged;
640
- }
641
- return out;
642
- }
643
- async dispose() {
644
- // Idempotent: allow multiple calls safely
645
- if (this.disposed)
646
- return;
647
- this.disposed = true;
648
- console.info(`[RemoteGraphRunner] Disposing runner with ID: ${this.runnerId}`);
649
- super.dispose();
650
- // Clear client promise if any
651
- this.clientPromise = undefined;
652
- // Unsubscribe from transport status
653
- // Note: Custom events are cleaned up automatically when client is disposed
654
- if (this.transportStatusUnsubscribe) {
655
- this.transportStatusUnsubscribe();
656
- this.transportStatusUnsubscribe = undefined;
657
- }
658
- // Dispose client (which will close transport)
659
- const clientToDispose = this.client;
660
- this.client = undefined;
661
- this.registryFetched = false; // Reset so registry is fetched again on reconnect
662
- if (clientToDispose) {
663
- try {
664
- await clientToDispose.dispose();
665
- }
666
- catch (err) {
667
- console.warn("[RemoteGraphRunner] Error disposing client:", err);
668
- }
669
- }
670
- const disconnectedStatus = {
671
- state: "disconnected",
672
- kind: this.backend.kind,
673
- runnerId: this.runnerId,
674
- };
675
- this.currentTransportStatus = disconnectedStatus;
676
- this.emit("transport", disconnectedStatus);
677
- }
678
- /**
679
- * Override on() to emit current transport status immediately when a new listener subscribes.
680
- * This ensures listeners don't miss the current status when they attach after connection.
681
- */
682
- on(event, handler) {
683
- const unsubscribe = super.on(event, handler);
684
- // If subscribing to transport events and we have a current status, emit it immediately
685
- if (event === "transport" && this.currentTransportStatus) {
686
- // Use setTimeout to ensure this happens after the listener is registered
687
- // This prevents issues if the handler modifies state synchronously
688
- setTimeout(() => {
689
- // Type assertion is safe here because we checked event === "transport"
690
- handler(this.currentTransportStatus);
691
- }, 0);
692
- }
693
- return unsubscribe;
694
- }
695
- }
696
- //# sourceMappingURL=RemoteGraphRunner.js.map