@bian-womp/spark-remote 0.2.37 → 0.2.38
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/cjs/UnixSocketTransport-Bghy9EOO.js +103 -0
- package/lib/cjs/UnixSocketTransport-Bghy9EOO.js.map +1 -0
- package/lib/cjs/index-BKs-fgHN.js +1047 -0
- package/lib/cjs/index-BKs-fgHN.js.map +1 -0
- package/lib/cjs/index.cjs +11 -750
- package/lib/cjs/index.cjs.map +1 -1
- package/lib/cjs/src/RuntimeApiClient.d.ts +89 -0
- package/lib/cjs/src/RuntimeApiClient.d.ts.map +1 -0
- package/lib/cjs/src/examples/shared.d.ts +2 -2
- package/lib/cjs/src/examples/shared.d.ts.map +1 -1
- package/lib/cjs/src/index.d.ts +4 -2
- package/lib/cjs/src/index.d.ts.map +1 -1
- package/lib/cjs/src/server/RuntimeApiServer.d.ts +49 -0
- package/lib/cjs/src/server/RuntimeApiServer.d.ts.map +1 -0
- package/lib/esm/UnixSocketTransport-CVUsiFcW.js +101 -0
- package/lib/esm/UnixSocketTransport-CVUsiFcW.js.map +1 -0
- package/lib/esm/index-Dnchg7CQ.js +1037 -0
- package/lib/esm/index-Dnchg7CQ.js.map +1 -0
- package/lib/esm/index.js +3 -746
- package/lib/esm/index.js.map +1 -1
- package/lib/esm/src/RuntimeApiClient.d.ts +89 -0
- package/lib/esm/src/RuntimeApiClient.d.ts.map +1 -0
- package/lib/esm/src/examples/shared.d.ts +2 -2
- package/lib/esm/src/examples/shared.d.ts.map +1 -1
- package/lib/esm/src/index.d.ts +4 -2
- package/lib/esm/src/index.d.ts.map +1 -1
- package/lib/esm/src/server/RuntimeApiServer.d.ts +49 -0
- package/lib/esm/src/server/RuntimeApiServer.d.ts.map +1 -0
- package/package.json +3 -3
- package/lib/cjs/src/RemoteRunner.d.ts +0 -24
- package/lib/cjs/src/RemoteRunner.d.ts.map +0 -1
- package/lib/cjs/src/server/command.d.ts +0 -4
- package/lib/cjs/src/server/command.d.ts.map +0 -1
- package/lib/esm/src/RemoteRunner.d.ts +0 -24
- package/lib/esm/src/RemoteRunner.d.ts.map +0 -1
- package/lib/esm/src/server/command.d.ts +0 -4
- package/lib/esm/src/server/command.d.ts.map +0 -1
package/lib/cjs/index.cjs
CHANGED
|
@@ -1,756 +1,17 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
4
|
-
|
|
3
|
+
var index = require('./index-BKs-fgHN.js');
|
|
4
|
+
require('ws');
|
|
5
|
+
require('@bian-womp/spark-graph');
|
|
5
6
|
|
|
6
|
-
class SeqGenerator {
|
|
7
|
-
constructor() {
|
|
8
|
-
this.lastMs = 0;
|
|
9
|
-
this.seqInMs = 0;
|
|
10
|
-
}
|
|
11
|
-
next() {
|
|
12
|
-
const now = Date.now();
|
|
13
|
-
if (now === this.lastMs) {
|
|
14
|
-
this.seqInMs = (this.seqInMs + 1) % 1000;
|
|
15
|
-
}
|
|
16
|
-
else {
|
|
17
|
-
this.lastMs = now;
|
|
18
|
-
this.seqInMs = 0;
|
|
19
|
-
}
|
|
20
|
-
return now * 1000 + this.seqInMs;
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
7
|
|
|
24
|
-
const OPEN = 1;
|
|
25
|
-
class WebSocketTransport {
|
|
26
|
-
constructor(url) {
|
|
27
|
-
this.listeners = new Set();
|
|
28
|
-
this.seq = new SeqGenerator();
|
|
29
|
-
this.baseUrl = url;
|
|
30
|
-
}
|
|
31
|
-
async connect(options) {
|
|
32
|
-
if (this.ws && this.ws.readyState === OPEN)
|
|
33
|
-
return;
|
|
34
|
-
// Build URL with connection params
|
|
35
|
-
// Handle both ws:///wss:// URLs and http:///https:// URLs (convert to ws)
|
|
36
|
-
let url;
|
|
37
|
-
if (this.baseUrl.startsWith("ws://") || this.baseUrl.startsWith("wss://")) {
|
|
38
|
-
url = new URL(this.baseUrl);
|
|
39
|
-
}
|
|
40
|
-
else {
|
|
41
|
-
// Convert http/https to ws/wss
|
|
42
|
-
const wsBaseUrl = this.baseUrl.replace(/^http/, "ws");
|
|
43
|
-
url = new URL(wsBaseUrl);
|
|
44
|
-
}
|
|
45
|
-
if (options?.params) {
|
|
46
|
-
for (const [key, value] of Object.entries(options.params)) {
|
|
47
|
-
if (value !== undefined && value !== null) {
|
|
48
|
-
url.searchParams.set(key, String(value));
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
const wsUrl = url.toString();
|
|
53
|
-
this.ws = window
|
|
54
|
-
? new window.WebSocket(wsUrl)
|
|
55
|
-
: new ws.WebSocket(wsUrl);
|
|
56
|
-
await new Promise((resolve, reject) => {
|
|
57
|
-
if (!this.ws)
|
|
58
|
-
return reject(new Error("ws init failed"));
|
|
59
|
-
this.ws.onopen = () => resolve();
|
|
60
|
-
this.ws.onerror = (e) => reject(e);
|
|
61
|
-
this.ws.onmessage = (ev) => {
|
|
62
|
-
try {
|
|
63
|
-
const env = JSON.parse(String(ev.data));
|
|
64
|
-
for (const l of Array.from(this.listeners))
|
|
65
|
-
l(env);
|
|
66
|
-
}
|
|
67
|
-
catch { }
|
|
68
|
-
};
|
|
69
|
-
});
|
|
70
|
-
}
|
|
71
|
-
async request(msg) {
|
|
72
|
-
// For now, just send and wait for the next message with matching seq (simple demo)
|
|
73
|
-
const seq = this.seq.next();
|
|
74
|
-
const env = { ...msg, seq };
|
|
75
|
-
const p = new Promise((resolve) => {
|
|
76
|
-
const off = this.subscribe((incoming) => {
|
|
77
|
-
if (incoming.seq === seq) {
|
|
78
|
-
off();
|
|
79
|
-
resolve(incoming);
|
|
80
|
-
}
|
|
81
|
-
});
|
|
82
|
-
});
|
|
83
|
-
this.send(env);
|
|
84
|
-
return p;
|
|
85
|
-
}
|
|
86
|
-
send(msg) {
|
|
87
|
-
if (!this.ws || this.ws.readyState !== OPEN)
|
|
88
|
-
return;
|
|
89
|
-
this.ws.send(JSON.stringify(msg));
|
|
90
|
-
}
|
|
91
|
-
subscribe(cb) {
|
|
92
|
-
this.listeners.add(cb);
|
|
93
|
-
return () => this.listeners.delete(cb);
|
|
94
|
-
}
|
|
95
|
-
async close() {
|
|
96
|
-
if (!this.ws)
|
|
97
|
-
return;
|
|
98
|
-
const ws = this.ws;
|
|
99
|
-
this.ws = undefined;
|
|
100
|
-
await new Promise((resolve) => {
|
|
101
|
-
ws.onclose = () => resolve();
|
|
102
|
-
try {
|
|
103
|
-
ws.close();
|
|
104
|
-
}
|
|
105
|
-
catch {
|
|
106
|
-
resolve();
|
|
107
|
-
}
|
|
108
|
-
});
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
8
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
// Start polling loop
|
|
121
|
-
if (this.polling)
|
|
122
|
-
return;
|
|
123
|
-
const tick = async () => {
|
|
124
|
-
try {
|
|
125
|
-
const url = new URL(this.baseUrl + "/events");
|
|
126
|
-
if (this.cursor)
|
|
127
|
-
url.searchParams.set("cursor", this.cursor);
|
|
128
|
-
// Add connection params to all requests
|
|
129
|
-
if (this.connectParams) {
|
|
130
|
-
for (const [key, value] of Object.entries(this.connectParams)) {
|
|
131
|
-
if (value !== undefined && value !== null) {
|
|
132
|
-
url.searchParams.set(key, String(value));
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
const res = await fetch(url.toString());
|
|
137
|
-
if (!res.ok)
|
|
138
|
-
return;
|
|
139
|
-
const batch = (await res.json());
|
|
140
|
-
for (const env of batch) {
|
|
141
|
-
this.cursor = String(env.seq ?? Date.now());
|
|
142
|
-
for (const l of Array.from(this.listeners))
|
|
143
|
-
l(env);
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
catch { }
|
|
147
|
-
};
|
|
148
|
-
this.polling = setInterval(tick, 200);
|
|
149
|
-
}
|
|
150
|
-
async request(msg) {
|
|
151
|
-
const url = new URL(this.baseUrl + "/command");
|
|
152
|
-
// Add connection params to command requests
|
|
153
|
-
if (this.connectParams) {
|
|
154
|
-
for (const [key, value] of Object.entries(this.connectParams)) {
|
|
155
|
-
if (value !== undefined && value !== null) {
|
|
156
|
-
url.searchParams.set(key, String(value));
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
const res = await fetch(url.toString(), {
|
|
161
|
-
method: "POST",
|
|
162
|
-
headers: { "content-type": "application/json" },
|
|
163
|
-
body: JSON.stringify(msg),
|
|
164
|
-
});
|
|
165
|
-
const data = (await res.json());
|
|
166
|
-
return data;
|
|
167
|
-
}
|
|
168
|
-
send(msg) {
|
|
169
|
-
const url = new URL(this.baseUrl + "/command");
|
|
170
|
-
// Add connection params to send requests
|
|
171
|
-
if (this.connectParams) {
|
|
172
|
-
for (const [key, value] of Object.entries(this.connectParams)) {
|
|
173
|
-
if (value !== undefined && value !== null) {
|
|
174
|
-
url.searchParams.set(key, String(value));
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
fetch(url.toString(), {
|
|
179
|
-
method: "POST",
|
|
180
|
-
headers: { "content-type": "application/json" },
|
|
181
|
-
body: JSON.stringify(msg),
|
|
182
|
-
});
|
|
183
|
-
}
|
|
184
|
-
subscribe(cb) {
|
|
185
|
-
this.listeners.add(cb);
|
|
186
|
-
return () => this.listeners.delete(cb);
|
|
187
|
-
}
|
|
188
|
-
async close() {
|
|
189
|
-
if (this.polling)
|
|
190
|
-
clearInterval(this.polling);
|
|
191
|
-
this.polling = undefined;
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
class RemoteEngine {
|
|
196
|
-
constructor(transport) {
|
|
197
|
-
this.transport = transport;
|
|
198
|
-
this.listeners = new Map();
|
|
199
|
-
this.cache = new Map();
|
|
200
|
-
this.transport.subscribe((env) => this.onEnvelope(env));
|
|
201
|
-
}
|
|
202
|
-
onEnvelope(env) {
|
|
203
|
-
const msg = env.message;
|
|
204
|
-
if (!msg)
|
|
205
|
-
return;
|
|
206
|
-
if (msg.type === "value") {
|
|
207
|
-
const key = `${msg.payload.nodeId}.${msg.payload.handle}`;
|
|
208
|
-
this.cache.set(key, {
|
|
209
|
-
value: msg.payload.value,
|
|
210
|
-
runtimeTypeId: msg.payload.runtimeTypeId,
|
|
211
|
-
});
|
|
212
|
-
this.emit("value", msg.payload);
|
|
213
|
-
}
|
|
214
|
-
else if (msg.type === "error") {
|
|
215
|
-
this.emit("error", msg.payload);
|
|
216
|
-
}
|
|
217
|
-
else if (msg.type === "invalidate") {
|
|
218
|
-
this.emit("invalidate", msg.payload);
|
|
219
|
-
}
|
|
220
|
-
else if (msg.type === "stats") {
|
|
221
|
-
this.emit("stats", msg.payload);
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
launch(invalidate) {
|
|
225
|
-
this.transport.send({
|
|
226
|
-
message: { type: "Launch", payload: { invalidate } },
|
|
227
|
-
});
|
|
228
|
-
}
|
|
229
|
-
setInput(nodeId, handle, value) {
|
|
230
|
-
this.transport.send({
|
|
231
|
-
message: { type: "SetInput", payload: { nodeId, handle, value } },
|
|
232
|
-
});
|
|
233
|
-
}
|
|
234
|
-
// Batch inputs for a single network round-trip
|
|
235
|
-
setInputs(nodeId, inputs) {
|
|
236
|
-
this.transport.send({
|
|
237
|
-
message: { type: "SetInputs", payload: { nodeId, inputs } },
|
|
238
|
-
});
|
|
239
|
-
}
|
|
240
|
-
triggerExternal(nodeId, event) {
|
|
241
|
-
this.transport.send({
|
|
242
|
-
message: { type: "TriggerExternal", payload: { nodeId, event } },
|
|
243
|
-
});
|
|
244
|
-
}
|
|
245
|
-
on(event, handler) {
|
|
246
|
-
if (!this.listeners.has(event))
|
|
247
|
-
this.listeners.set(event, new Set());
|
|
248
|
-
const set = this.listeners.get(event);
|
|
249
|
-
set.add(handler);
|
|
250
|
-
return () => set.delete(handler);
|
|
251
|
-
}
|
|
252
|
-
emit(event, payload) {
|
|
253
|
-
const set = this.listeners.get(event);
|
|
254
|
-
if (set)
|
|
255
|
-
for (const h of Array.from(set))
|
|
256
|
-
h(payload);
|
|
257
|
-
}
|
|
258
|
-
getOutput(nodeId, output) {
|
|
259
|
-
return this.cache.get(`${nodeId}.${output}`)?.value;
|
|
260
|
-
}
|
|
261
|
-
async whenIdle() {
|
|
262
|
-
await this.transport.request({ message: { type: "WhenIdle" } });
|
|
263
|
-
}
|
|
264
|
-
dispose() {
|
|
265
|
-
this.transport.send({ message: { type: "Dispose" } });
|
|
266
|
-
}
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
class RemoteRunner {
|
|
270
|
-
constructor(transport) {
|
|
271
|
-
this.transport = transport;
|
|
272
|
-
this.engine = new RemoteEngine(transport);
|
|
273
|
-
}
|
|
274
|
-
async build(def, opts) {
|
|
275
|
-
await this.transport.request({
|
|
276
|
-
message: {
|
|
277
|
-
type: "Build",
|
|
278
|
-
payload: { def, environment: opts?.environment },
|
|
279
|
-
},
|
|
280
|
-
});
|
|
281
|
-
}
|
|
282
|
-
async update(def) {
|
|
283
|
-
await this.transport.request({
|
|
284
|
-
message: { type: "Update", payload: { def } },
|
|
285
|
-
});
|
|
286
|
-
}
|
|
287
|
-
async describeRegistry() {
|
|
288
|
-
const res = await this.transport.request({
|
|
289
|
-
message: { type: "DescribeRegistry" },
|
|
290
|
-
});
|
|
291
|
-
const payload = res?.message || {};
|
|
292
|
-
return (payload.registry || {
|
|
293
|
-
types: [],
|
|
294
|
-
categories: [],
|
|
295
|
-
nodes: [],
|
|
296
|
-
coercions: [],
|
|
297
|
-
schemaVersion: 4,
|
|
298
|
-
});
|
|
299
|
-
}
|
|
300
|
-
async applyRegistry(deltas) {
|
|
301
|
-
await this.transport.request({
|
|
302
|
-
message: { type: "RegistryApply", payload: { deltas } },
|
|
303
|
-
});
|
|
304
|
-
}
|
|
305
|
-
async snapshot() {
|
|
306
|
-
const res = await this.transport.request({ message: { type: "Snapshot" } });
|
|
307
|
-
const payload = res?.message || {};
|
|
308
|
-
return payload.snapshot || { inputs: {}, outputs: {} };
|
|
309
|
-
}
|
|
310
|
-
async snapshotFull() {
|
|
311
|
-
const res = await this.transport.request({
|
|
312
|
-
message: { type: "SnapshotFull" },
|
|
313
|
-
});
|
|
314
|
-
const payload = res?.message || {};
|
|
315
|
-
return (payload.snapshot || {
|
|
316
|
-
def: undefined,
|
|
317
|
-
environment: {},
|
|
318
|
-
inputs: {},
|
|
319
|
-
outputs: {},
|
|
320
|
-
});
|
|
321
|
-
}
|
|
322
|
-
async applySnapshotFull(payload) {
|
|
323
|
-
await this.transport.request({
|
|
324
|
-
message: { type: "ApplySnapshotFull", payload },
|
|
325
|
-
});
|
|
326
|
-
}
|
|
327
|
-
async setEnvironment(environment, opts) {
|
|
328
|
-
await this.transport.request({
|
|
329
|
-
message: {
|
|
330
|
-
type: "SetEnvironment",
|
|
331
|
-
payload: { environment, merge: opts?.merge },
|
|
332
|
-
},
|
|
333
|
-
});
|
|
334
|
-
}
|
|
335
|
-
async getEnvironment() {
|
|
336
|
-
const res = await this.transport.request({
|
|
337
|
-
message: { type: "GetEnvironment" },
|
|
338
|
-
});
|
|
339
|
-
const payload = res?.message || {};
|
|
340
|
-
return payload.environment || {};
|
|
341
|
-
}
|
|
342
|
-
async coerce(from, to, value) {
|
|
343
|
-
const res = await this.transport.request({
|
|
344
|
-
message: { type: "Coerce", payload: { from, to, value } },
|
|
345
|
-
});
|
|
346
|
-
const payload = res?.message || {};
|
|
347
|
-
return payload.value;
|
|
348
|
-
}
|
|
349
|
-
getEngine() {
|
|
350
|
-
return this.engine;
|
|
351
|
-
}
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
355
|
-
function summarize(value, maxLen = 120) {
|
|
356
|
-
try {
|
|
357
|
-
const s = typeof value === "string" ? value : JSON.stringify(value);
|
|
358
|
-
return s.length > maxLen ? s.slice(0, maxLen) + "…" : s;
|
|
359
|
-
}
|
|
360
|
-
catch {
|
|
361
|
-
return String(value);
|
|
362
|
-
}
|
|
363
|
-
}
|
|
364
|
-
function serializeError(err) {
|
|
365
|
-
try {
|
|
366
|
-
if (!err)
|
|
367
|
-
return { message: String(err) };
|
|
368
|
-
const errAny = err;
|
|
369
|
-
if (err instanceof Error) {
|
|
370
|
-
const base = {
|
|
371
|
-
name: err.name,
|
|
372
|
-
message: err.message,
|
|
373
|
-
stack: err.stack,
|
|
374
|
-
};
|
|
375
|
-
for (const k of Object.keys(errAny))
|
|
376
|
-
base[k] = errAny[k];
|
|
377
|
-
return base;
|
|
378
|
-
}
|
|
379
|
-
if (typeof err === "object") {
|
|
380
|
-
const maybeMsg = errAny?.message;
|
|
381
|
-
const maybeName = errAny?.name;
|
|
382
|
-
const maybeStack = errAny?.stack;
|
|
383
|
-
const out = { ...errAny };
|
|
384
|
-
if (maybeMsg && !out.message)
|
|
385
|
-
out.message = String(maybeMsg);
|
|
386
|
-
if (maybeName && !out.name)
|
|
387
|
-
out.name = String(maybeName);
|
|
388
|
-
if (maybeStack && !out.stack)
|
|
389
|
-
out.stack = String(maybeStack);
|
|
390
|
-
return out;
|
|
391
|
-
}
|
|
392
|
-
return { message: String(err) };
|
|
393
|
-
}
|
|
394
|
-
catch {
|
|
395
|
-
return { message: String(err) };
|
|
396
|
-
}
|
|
397
|
-
}
|
|
398
|
-
|
|
399
|
-
async function handleCommand(label, env, runtimeApi, ack) {
|
|
400
|
-
const msg = env.message;
|
|
401
|
-
switch (msg.type) {
|
|
402
|
-
case "Build": {
|
|
403
|
-
console.debug(`[${label}] rx Build seq=${env.seq} nodes=${msg.payload.def.nodes?.length ?? 0} edges=${msg.payload.def.edges?.length ?? 0} envKeys=${Object.keys(msg.payload.environment ?? {}).join(",")}`);
|
|
404
|
-
await runtimeApi.build(msg.payload.def, msg.payload.environment);
|
|
405
|
-
ack();
|
|
406
|
-
break;
|
|
407
|
-
}
|
|
408
|
-
case "Update": {
|
|
409
|
-
console.debug(`[${label}] rx Update seq=${env.seq} nodes=${msg.payload.def.nodes?.length ?? 0} edges=${msg.payload.def.edges?.length ?? 0}`);
|
|
410
|
-
await runtimeApi.update(msg.payload.def);
|
|
411
|
-
ack();
|
|
412
|
-
break;
|
|
413
|
-
}
|
|
414
|
-
case "SetEnvironment": {
|
|
415
|
-
console.debug(`[${label}] rx SetEnvironment seq=${env.seq}`);
|
|
416
|
-
runtimeApi.setEnvironment(msg.payload.environment, {
|
|
417
|
-
merge: Boolean(msg.payload.merge),
|
|
418
|
-
});
|
|
419
|
-
ack();
|
|
420
|
-
break;
|
|
421
|
-
}
|
|
422
|
-
case "SetInput": {
|
|
423
|
-
console.debug(`[${label}] rx SetInput seq=${env.seq} ${msg.payload.nodeId}.${msg.payload.handle}=${summarize(msg.payload.value)}`);
|
|
424
|
-
runtimeApi.setInput(msg.payload.nodeId, msg.payload.handle, msg.payload.value);
|
|
425
|
-
ack();
|
|
426
|
-
break;
|
|
427
|
-
}
|
|
428
|
-
case "SetInputs": {
|
|
429
|
-
console.debug(`[${label}] rx SetInputs seq=${env.seq} ${msg.payload.nodeId} keys=${Object.keys(msg.payload.inputs || {}).join(",")}`);
|
|
430
|
-
const nodeId = msg.payload.nodeId;
|
|
431
|
-
const inputs = msg.payload.inputs;
|
|
432
|
-
runtimeApi.setInputs(nodeId, inputs);
|
|
433
|
-
ack();
|
|
434
|
-
break;
|
|
435
|
-
}
|
|
436
|
-
case "TriggerExternal": {
|
|
437
|
-
console.debug(`[${label}] rx TriggerExternal seq=${env.seq} ${msg.payload.nodeId} event=${summarize(msg.payload.event)}`);
|
|
438
|
-
runtimeApi.triggerExternal(msg.payload.nodeId, msg.payload.event);
|
|
439
|
-
ack();
|
|
440
|
-
break;
|
|
441
|
-
}
|
|
442
|
-
case "Launch": {
|
|
443
|
-
console.debug(`[${label}] rx Launch seq=${env.seq}`);
|
|
444
|
-
runtimeApi.launch(msg.payload.invalidate);
|
|
445
|
-
ack();
|
|
446
|
-
break;
|
|
447
|
-
}
|
|
448
|
-
case "WhenIdle": {
|
|
449
|
-
console.debug(`[${label}] rx WhenIdle seq=${env.seq}`);
|
|
450
|
-
await runtimeApi.whenIdle();
|
|
451
|
-
ack();
|
|
452
|
-
break;
|
|
453
|
-
}
|
|
454
|
-
case "Snapshot": {
|
|
455
|
-
console.debug(`[${label}] rx Snapshot seq=${env.seq}`);
|
|
456
|
-
const snap = runtimeApi.snapshot();
|
|
457
|
-
ack({ snapshot: snap });
|
|
458
|
-
break;
|
|
459
|
-
}
|
|
460
|
-
case "SnapshotFull": {
|
|
461
|
-
console.debug(`[${label}] rx SnapshotFull seq=${env.seq}`);
|
|
462
|
-
const snap = runtimeApi.snapshotFull();
|
|
463
|
-
ack({ snapshot: snap });
|
|
464
|
-
break;
|
|
465
|
-
}
|
|
466
|
-
case "GetEnvironment": {
|
|
467
|
-
console.debug(`[${label}] rx GetEnvironment seq=${env.seq}`);
|
|
468
|
-
const environment = runtimeApi.getEnvironment();
|
|
469
|
-
ack({ environment });
|
|
470
|
-
break;
|
|
471
|
-
}
|
|
472
|
-
case "ApplySnapshotFull": {
|
|
473
|
-
console.debug(`[${label}] rx ApplySnapshotFull seq=${env.seq}`);
|
|
474
|
-
await runtimeApi.applySnapshotFull(msg.payload);
|
|
475
|
-
ack();
|
|
476
|
-
break;
|
|
477
|
-
}
|
|
478
|
-
case "Coerce": {
|
|
479
|
-
console.debug(`[${label}] rx Coerce seq=${env.seq} ${msg.payload.from} -> ${msg.payload.to}`);
|
|
480
|
-
const value = await runtimeApi.coerce(msg.payload?.from, msg.payload?.to, msg.payload?.value);
|
|
481
|
-
ack({ value });
|
|
482
|
-
break;
|
|
483
|
-
}
|
|
484
|
-
case "DescribeRegistry": {
|
|
485
|
-
console.debug(`[${label}] rx DescribeRegistry seq=${env.seq}`);
|
|
486
|
-
const desc = runtimeApi.describeRegistry();
|
|
487
|
-
ack({ registry: desc });
|
|
488
|
-
break;
|
|
489
|
-
}
|
|
490
|
-
case "RegistryApply": {
|
|
491
|
-
console.debug(`[${label}] rx RegistryApply seq=${env.seq}`);
|
|
492
|
-
await runtimeApi.applyRegistry(msg.payload.deltas || []);
|
|
493
|
-
ack();
|
|
494
|
-
break;
|
|
495
|
-
}
|
|
496
|
-
case "Dispose": {
|
|
497
|
-
console.debug(`[${label}] rx Dispose seq=${env.seq}`);
|
|
498
|
-
runtimeApi.dispose();
|
|
499
|
-
ack();
|
|
500
|
-
break;
|
|
501
|
-
}
|
|
502
|
-
case "Pause":
|
|
503
|
-
case "Resume": {
|
|
504
|
-
console.debug(`[${label}] rx ${msg.type} seq=${env.seq} (not-impl)`);
|
|
505
|
-
ack();
|
|
506
|
-
break;
|
|
507
|
-
}
|
|
508
|
-
default: {
|
|
509
|
-
console.debug(`[${label}] rx Unknown type seq=${env.seq}`);
|
|
510
|
-
ack();
|
|
511
|
-
}
|
|
512
|
-
}
|
|
513
|
-
}
|
|
514
|
-
|
|
515
|
-
async function createRuntimeAdapter(createRegistry, send, extensions) {
|
|
516
|
-
const registry = await createRegistry();
|
|
517
|
-
const builder = new sparkGraph.GraphBuilder(registry);
|
|
518
|
-
let graphRuntime;
|
|
519
|
-
let extData = {};
|
|
520
|
-
// Helper to get current context
|
|
521
|
-
const getContext = () => ({
|
|
522
|
-
registry,
|
|
523
|
-
builder,
|
|
524
|
-
graphRuntime,
|
|
525
|
-
extData,
|
|
526
|
-
});
|
|
527
|
-
// Original implementations - define as separate functions first to allow cross-references
|
|
528
|
-
const originalApi = {
|
|
529
|
-
coerce: async (from, to, value) => {
|
|
530
|
-
const resolved = registry.resolveCoercion(from, to);
|
|
531
|
-
if (!resolved)
|
|
532
|
-
return value;
|
|
533
|
-
if (resolved.kind === "sync")
|
|
534
|
-
return resolved.convert(value);
|
|
535
|
-
const ac = new AbortController();
|
|
536
|
-
return await resolved.convertAsync(value, ac.signal);
|
|
537
|
-
},
|
|
538
|
-
getEnvironment: () => {
|
|
539
|
-
return graphRuntime?.getEnvironment?.() ?? {};
|
|
540
|
-
},
|
|
541
|
-
applyRegistry: async (deltas) => {
|
|
542
|
-
// Pause runtime if exists
|
|
543
|
-
// Apply each delta to the live registry
|
|
544
|
-
for (const d of deltas || []) {
|
|
545
|
-
if (!d || typeof d !== "object")
|
|
546
|
-
continue;
|
|
547
|
-
if (d.kind === "register-enum") {
|
|
548
|
-
registry.registerEnum({
|
|
549
|
-
id: d.id,
|
|
550
|
-
displayName: d.displayName,
|
|
551
|
-
options: d.options || [],
|
|
552
|
-
bakeTarget: d.bakeTarget,
|
|
553
|
-
opts: d.opts,
|
|
554
|
-
});
|
|
555
|
-
}
|
|
556
|
-
else if (d.kind === "register-type") {
|
|
557
|
-
registry.registerType({
|
|
558
|
-
id: d.id,
|
|
559
|
-
displayName: d.displayName,
|
|
560
|
-
bakeTarget: d.bakeTarget,
|
|
561
|
-
validate: (_v) => true,
|
|
562
|
-
});
|
|
563
|
-
}
|
|
564
|
-
else if (d.kind === "register-node") {
|
|
565
|
-
const desc = d.desc || {};
|
|
566
|
-
registry.registerNode({
|
|
567
|
-
id: String(desc.id || ""),
|
|
568
|
-
categoryId: String(desc.categoryId || "compute"),
|
|
569
|
-
displayName: desc.displayName,
|
|
570
|
-
inputs: desc.inputs || {},
|
|
571
|
-
outputs: desc.outputs || {},
|
|
572
|
-
// impl must be empty per frontend registration contract
|
|
573
|
-
impl: () => { },
|
|
574
|
-
});
|
|
575
|
-
}
|
|
576
|
-
}
|
|
577
|
-
// Notify clients (include deltas in invalidate payload)
|
|
578
|
-
send({
|
|
579
|
-
message: {
|
|
580
|
-
type: "invalidate",
|
|
581
|
-
payload: { reason: "registry-changed", deltas },
|
|
582
|
-
},
|
|
583
|
-
});
|
|
584
|
-
},
|
|
585
|
-
build: async (def, opts) => {
|
|
586
|
-
const env = opts || {};
|
|
587
|
-
graphRuntime = builder.build(def, { environment: env });
|
|
588
|
-
graphRuntime.on("value", (p) => send({ message: { type: "value", payload: p } }));
|
|
589
|
-
graphRuntime.on("invalidate", (p) => send({ message: { type: "invalidate", payload: p } }));
|
|
590
|
-
graphRuntime.on("error", (p) => send({ message: { type: "error", payload: p } }));
|
|
591
|
-
graphRuntime.on("stats", (p) => send({ message: { type: "stats", payload: p } }));
|
|
592
|
-
},
|
|
593
|
-
setExtData: (data) => {
|
|
594
|
-
if (!data || typeof data !== "object") {
|
|
595
|
-
extData = {};
|
|
596
|
-
return;
|
|
597
|
-
}
|
|
598
|
-
// Replace to keep semantics deterministic
|
|
599
|
-
extData = { ...data };
|
|
600
|
-
},
|
|
601
|
-
getExtData: () => {
|
|
602
|
-
return extData;
|
|
603
|
-
},
|
|
604
|
-
snapshot: () => {
|
|
605
|
-
const inputs = {};
|
|
606
|
-
const outputs = {};
|
|
607
|
-
if (!graphRuntime)
|
|
608
|
-
return { inputs, outputs };
|
|
609
|
-
const nodes = graphRuntime.getNodeIds();
|
|
610
|
-
for (const nodeId of nodes) {
|
|
611
|
-
const data = graphRuntime.getNodeData(nodeId);
|
|
612
|
-
if (data?.inputs && Object.keys(data.inputs).length > 0) {
|
|
613
|
-
inputs[nodeId] = { ...data.inputs };
|
|
614
|
-
}
|
|
615
|
-
if (data?.outputs && Object.keys(data.outputs).length > 0) {
|
|
616
|
-
outputs[nodeId] = { ...data.outputs };
|
|
617
|
-
}
|
|
618
|
-
}
|
|
619
|
-
return { inputs, outputs };
|
|
620
|
-
},
|
|
621
|
-
snapshotFull: () => {
|
|
622
|
-
const snap = originalApi.snapshot();
|
|
623
|
-
const env = graphRuntime?.getEnvironment?.() ?? {};
|
|
624
|
-
const def = graphRuntime?.getGraphDef();
|
|
625
|
-
return {
|
|
626
|
-
def,
|
|
627
|
-
environment: env,
|
|
628
|
-
inputs: snap.inputs,
|
|
629
|
-
outputs: snap.outputs,
|
|
630
|
-
};
|
|
631
|
-
},
|
|
632
|
-
applySnapshotFull: async (payload) => {
|
|
633
|
-
const def = payload.def;
|
|
634
|
-
if (!def)
|
|
635
|
-
return;
|
|
636
|
-
await originalApi.build(def, payload.environment);
|
|
637
|
-
// Hydrate inputs/outputs exactly, then re-emit outputs without scheduling runs
|
|
638
|
-
graphRuntime?.hydrate({
|
|
639
|
-
inputs: payload.inputs,
|
|
640
|
-
outputs: payload.outputs,
|
|
641
|
-
});
|
|
642
|
-
},
|
|
643
|
-
describeRegistry: () => {
|
|
644
|
-
// types (include enum options when available)
|
|
645
|
-
const types = Array.from(registry.types.entries()).map(([id, d]) => {
|
|
646
|
-
const en = registry.enums.get(id);
|
|
647
|
-
return {
|
|
648
|
-
id,
|
|
649
|
-
displayName: d.displayName,
|
|
650
|
-
bakeTarget: d.bakeTarget,
|
|
651
|
-
...(en ? { options: en.options } : {}),
|
|
652
|
-
};
|
|
653
|
-
});
|
|
654
|
-
// categories: not directly enumerable; derive from node descriptors
|
|
655
|
-
const nodeDescs = Array.from(registry.nodes.values());
|
|
656
|
-
const catIds = new Set(nodeDescs.map((n) => n.categoryId));
|
|
657
|
-
const categories = Array.from(catIds).map((id) => {
|
|
658
|
-
const cat = registry.categories.get?.(id);
|
|
659
|
-
return { id, displayName: cat?.displayName };
|
|
660
|
-
});
|
|
661
|
-
const nodes = nodeDescs.map((n) => ({
|
|
662
|
-
id: n.id,
|
|
663
|
-
categoryId: n.categoryId,
|
|
664
|
-
displayName: n.displayName,
|
|
665
|
-
inputs: n.inputs || {},
|
|
666
|
-
outputs: n.outputs || {},
|
|
667
|
-
inputDefaults: n.inputDefaults || {},
|
|
668
|
-
}));
|
|
669
|
-
const coercions = registry.listCoercions();
|
|
670
|
-
return { types, categories, nodes, coercions, schemaVersion: 4 };
|
|
671
|
-
},
|
|
672
|
-
update: async (def) => {
|
|
673
|
-
if (!graphRuntime)
|
|
674
|
-
return;
|
|
675
|
-
graphRuntime.update(def, registry);
|
|
676
|
-
send({
|
|
677
|
-
message: {
|
|
678
|
-
type: "invalidate",
|
|
679
|
-
payload: { reason: "graph-updated" },
|
|
680
|
-
},
|
|
681
|
-
});
|
|
682
|
-
},
|
|
683
|
-
setEnvironment: (env, opts) => {
|
|
684
|
-
if (!graphRuntime)
|
|
685
|
-
return;
|
|
686
|
-
if (opts?.merge) {
|
|
687
|
-
const current = graphRuntime.getEnvironment();
|
|
688
|
-
const next = { ...(current || {}), ...(env || {}) };
|
|
689
|
-
graphRuntime.setEnvironment(next);
|
|
690
|
-
return;
|
|
691
|
-
}
|
|
692
|
-
graphRuntime.setEnvironment(env);
|
|
693
|
-
},
|
|
694
|
-
setInput: (nodeId, handle, value) => {
|
|
695
|
-
graphRuntime?.setInput(nodeId, handle, value);
|
|
696
|
-
},
|
|
697
|
-
setInputs: (nodeId, inputs) => {
|
|
698
|
-
graphRuntime?.setInputs(nodeId, inputs);
|
|
699
|
-
},
|
|
700
|
-
triggerExternal: (nodeId, event) => {
|
|
701
|
-
graphRuntime?.triggerExternal(nodeId, event);
|
|
702
|
-
},
|
|
703
|
-
launch: (invalidate) => {
|
|
704
|
-
graphRuntime?.launch(invalidate);
|
|
705
|
-
},
|
|
706
|
-
whenIdle: () => {
|
|
707
|
-
return graphRuntime?.whenIdle?.() ?? Promise.resolve();
|
|
708
|
-
},
|
|
709
|
-
dispose: () => {
|
|
710
|
-
graphRuntime?.dispose?.();
|
|
711
|
-
graphRuntime = undefined;
|
|
712
|
-
},
|
|
713
|
-
};
|
|
714
|
-
// Helper to wrap a method with extension support
|
|
715
|
-
const wrapMethod = (key, original) => {
|
|
716
|
-
const extension = extensions?.[key];
|
|
717
|
-
if (!extension) {
|
|
718
|
-
return original;
|
|
719
|
-
}
|
|
720
|
-
return ((...args) => {
|
|
721
|
-
return extension(original, getContext(), ...args);
|
|
722
|
-
});
|
|
723
|
-
};
|
|
724
|
-
// Create API with extensions applied
|
|
725
|
-
const extendedApi = {
|
|
726
|
-
coerce: wrapMethod("coerce", originalApi.coerce),
|
|
727
|
-
getEnvironment: wrapMethod("getEnvironment", originalApi.getEnvironment),
|
|
728
|
-
applyRegistry: wrapMethod("applyRegistry", originalApi.applyRegistry),
|
|
729
|
-
build: wrapMethod("build", originalApi.build),
|
|
730
|
-
setExtData: wrapMethod("setExtData", originalApi.setExtData),
|
|
731
|
-
getExtData: wrapMethod("getExtData", originalApi.getExtData),
|
|
732
|
-
snapshot: wrapMethod("snapshot", originalApi.snapshot),
|
|
733
|
-
snapshotFull: wrapMethod("snapshotFull", originalApi.snapshotFull),
|
|
734
|
-
applySnapshotFull: wrapMethod("applySnapshotFull", originalApi.applySnapshotFull),
|
|
735
|
-
describeRegistry: wrapMethod("describeRegistry", originalApi.describeRegistry),
|
|
736
|
-
update: wrapMethod("update", originalApi.update),
|
|
737
|
-
setEnvironment: wrapMethod("setEnvironment", originalApi.setEnvironment),
|
|
738
|
-
setInput: wrapMethod("setInput", originalApi.setInput),
|
|
739
|
-
setInputs: wrapMethod("setInputs", originalApi.setInputs),
|
|
740
|
-
triggerExternal: wrapMethod("triggerExternal", originalApi.triggerExternal),
|
|
741
|
-
launch: wrapMethod("launch", originalApi.launch),
|
|
742
|
-
whenIdle: wrapMethod("whenIdle", originalApi.whenIdle),
|
|
743
|
-
dispose: wrapMethod("dispose", originalApi.dispose),
|
|
744
|
-
};
|
|
745
|
-
return extendedApi;
|
|
746
|
-
}
|
|
747
|
-
|
|
748
|
-
exports.HttpPollingTransport = HttpPollingTransport;
|
|
749
|
-
exports.RemoteEngine = RemoteEngine;
|
|
750
|
-
exports.RemoteRunner = RemoteRunner;
|
|
751
|
-
exports.WebSocketTransport = WebSocketTransport;
|
|
752
|
-
exports.createRuntimeAdapter = createRuntimeAdapter;
|
|
753
|
-
exports.handleCommand = handleCommand;
|
|
754
|
-
exports.serializeError = serializeError;
|
|
755
|
-
exports.summarize = summarize;
|
|
9
|
+
exports.HttpPollingTransport = index.HttpPollingTransport;
|
|
10
|
+
exports.RemoteEngine = index.RemoteEngine;
|
|
11
|
+
exports.RuntimeApiClient = index.RuntimeApiClient;
|
|
12
|
+
exports.RuntimeApiServer = index.RuntimeApiServer;
|
|
13
|
+
exports.WebSocketTransport = index.WebSocketTransport;
|
|
14
|
+
exports.createRuntimeAdapter = index.createRuntimeAdapter;
|
|
15
|
+
exports.serializeError = index.serializeError;
|
|
16
|
+
exports.summarize = index.summarize;
|
|
756
17
|
//# sourceMappingURL=index.cjs.map
|