@bian-womp/spark-remote 0.2.50 → 0.2.52
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/cjs/index.cjs +93 -38
- package/lib/cjs/index.cjs.map +1 -1
- package/lib/cjs/src/RemoteEngine.d.ts +0 -1
- package/lib/cjs/src/RemoteEngine.d.ts.map +1 -1
- package/lib/cjs/src/RuntimeApiClient.d.ts +1 -0
- package/lib/cjs/src/RuntimeApiClient.d.ts.map +1 -1
- package/lib/cjs/src/examples/shared.d.ts.map +1 -1
- package/lib/cjs/src/server/RuntimeApiServer.d.ts.map +1 -1
- package/lib/cjs/src/server/runtime.d.ts +0 -1
- package/lib/cjs/src/server/runtime.d.ts.map +1 -1
- package/lib/cjs/src/transport/WebSocketTransport.d.ts +12 -1
- package/lib/cjs/src/transport/WebSocketTransport.d.ts.map +1 -1
- package/lib/esm/index.js +93 -38
- package/lib/esm/index.js.map +1 -1
- package/lib/esm/src/RemoteEngine.d.ts +0 -1
- package/lib/esm/src/RemoteEngine.d.ts.map +1 -1
- package/lib/esm/src/RuntimeApiClient.d.ts +1 -0
- package/lib/esm/src/RuntimeApiClient.d.ts.map +1 -1
- package/lib/esm/src/examples/shared.d.ts.map +1 -1
- package/lib/esm/src/server/RuntimeApiServer.d.ts.map +1 -1
- package/lib/esm/src/server/runtime.d.ts +0 -1
- package/lib/esm/src/server/runtime.d.ts.map +1 -1
- package/lib/esm/src/transport/WebSocketTransport.d.ts +12 -1
- package/lib/esm/src/transport/WebSocketTransport.d.ts.map +1 -1
- package/package.json +3 -3
package/lib/cjs/index.cjs
CHANGED
|
@@ -23,22 +23,23 @@ class SeqGenerator {
|
|
|
23
23
|
|
|
24
24
|
const OPEN = 1;
|
|
25
25
|
class WebSocketTransport {
|
|
26
|
-
constructor(url) {
|
|
26
|
+
constructor(url, options) {
|
|
27
27
|
this.listeners = new Set();
|
|
28
28
|
this.seq = new SeqGenerator();
|
|
29
|
+
this.isAlive = false;
|
|
30
|
+
this.PING_INTERVAL_MS = 30000;
|
|
31
|
+
this.PONG_TIMEOUT_MS = 10000;
|
|
29
32
|
this.baseUrl = url;
|
|
33
|
+
this.onConnectionLost = options?.onConnectionLost;
|
|
30
34
|
}
|
|
31
35
|
async connect(options) {
|
|
32
36
|
if (this.ws && this.ws.readyState === OPEN)
|
|
33
37
|
return;
|
|
34
|
-
// Build URL with connection params
|
|
35
|
-
// Handle both ws:///wss:// URLs and http:///https:// URLs (convert to ws)
|
|
36
38
|
let url;
|
|
37
39
|
if (this.baseUrl.startsWith("ws://") || this.baseUrl.startsWith("wss://")) {
|
|
38
40
|
url = new URL(this.baseUrl);
|
|
39
41
|
}
|
|
40
42
|
else {
|
|
41
|
-
// Convert http/https to ws/wss
|
|
42
43
|
const wsBaseUrl = this.baseUrl.replace(/^http/, "ws");
|
|
43
44
|
url = new URL(wsBaseUrl);
|
|
44
45
|
}
|
|
@@ -56,7 +57,10 @@ class WebSocketTransport {
|
|
|
56
57
|
await new Promise((resolve, reject) => {
|
|
57
58
|
if (!this.ws)
|
|
58
59
|
return reject(new Error("ws init failed"));
|
|
59
|
-
this.ws.onopen = () =>
|
|
60
|
+
this.ws.onopen = () => {
|
|
61
|
+
this.startHeartbeat();
|
|
62
|
+
resolve();
|
|
63
|
+
};
|
|
60
64
|
this.ws.onerror = (e) => reject(e);
|
|
61
65
|
this.ws.onmessage = (ev) => {
|
|
62
66
|
try {
|
|
@@ -66,10 +70,72 @@ class WebSocketTransport {
|
|
|
66
70
|
}
|
|
67
71
|
catch { }
|
|
68
72
|
};
|
|
73
|
+
this.ws.onclose = () => {
|
|
74
|
+
this.stopHeartbeat();
|
|
75
|
+
};
|
|
69
76
|
});
|
|
70
77
|
}
|
|
78
|
+
startHeartbeat() {
|
|
79
|
+
this.stopHeartbeat();
|
|
80
|
+
this.isAlive = true;
|
|
81
|
+
if (typeof window !== "undefined" || !this.ws)
|
|
82
|
+
return;
|
|
83
|
+
this.pongHandler = () => {
|
|
84
|
+
this.isAlive = true;
|
|
85
|
+
if (this.pongTimeout) {
|
|
86
|
+
clearTimeout(this.pongTimeout);
|
|
87
|
+
this.pongTimeout = undefined;
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
this.ws.on("pong", this.pongHandler);
|
|
91
|
+
this.pingInterval = setInterval(() => {
|
|
92
|
+
if (!this.ws || this.ws.readyState !== OPEN) {
|
|
93
|
+
this.stopHeartbeat();
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
if (!this.isAlive) {
|
|
97
|
+
this.onConnectionLost?.();
|
|
98
|
+
this.stopHeartbeat();
|
|
99
|
+
this.ws.terminate();
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
this.isAlive = false;
|
|
103
|
+
try {
|
|
104
|
+
this.ws.ping();
|
|
105
|
+
}
|
|
106
|
+
catch (err) {
|
|
107
|
+
console.warn("[WebSocketTransport] Failed to send ping:", err);
|
|
108
|
+
this.onConnectionLost?.();
|
|
109
|
+
this.stopHeartbeat();
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
this.pongTimeout = setTimeout(() => {
|
|
113
|
+
if (!this.isAlive) {
|
|
114
|
+
this.onConnectionLost?.();
|
|
115
|
+
this.stopHeartbeat();
|
|
116
|
+
if (this.ws) {
|
|
117
|
+
this.ws.terminate();
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}, this.PONG_TIMEOUT_MS);
|
|
121
|
+
}, this.PING_INTERVAL_MS);
|
|
122
|
+
}
|
|
123
|
+
stopHeartbeat() {
|
|
124
|
+
if (this.pingInterval) {
|
|
125
|
+
clearInterval(this.pingInterval);
|
|
126
|
+
this.pingInterval = undefined;
|
|
127
|
+
}
|
|
128
|
+
if (this.pongTimeout) {
|
|
129
|
+
clearTimeout(this.pongTimeout);
|
|
130
|
+
this.pongTimeout = undefined;
|
|
131
|
+
}
|
|
132
|
+
if (this.ws && this.pongHandler) {
|
|
133
|
+
this.ws.off("pong", this.pongHandler);
|
|
134
|
+
this.pongHandler = undefined;
|
|
135
|
+
}
|
|
136
|
+
this.isAlive = false;
|
|
137
|
+
}
|
|
71
138
|
async request(msg) {
|
|
72
|
-
// For now, just send and wait for the next message with matching seq (simple demo)
|
|
73
139
|
const seq = this.seq.next();
|
|
74
140
|
const env = { ...msg, seq };
|
|
75
141
|
const p = new Promise((resolve) => {
|
|
@@ -93,6 +159,7 @@ class WebSocketTransport {
|
|
|
93
159
|
return () => this.listeners.delete(cb);
|
|
94
160
|
}
|
|
95
161
|
async close() {
|
|
162
|
+
this.stopHeartbeat();
|
|
96
163
|
if (!this.ws)
|
|
97
164
|
return;
|
|
98
165
|
const ws = this.ws;
|
|
@@ -347,6 +414,7 @@ async function createRuntimeAdapter(createRegistry, send, extensions) {
|
|
|
347
414
|
},
|
|
348
415
|
snapshotFull: () => {
|
|
349
416
|
const snap = originalApi.snapshot();
|
|
417
|
+
const extData = originalApi.getExtData();
|
|
350
418
|
const env = graphRuntime?.getEnvironment?.() ?? {};
|
|
351
419
|
const def = graphRuntime?.getGraphDef();
|
|
352
420
|
return {
|
|
@@ -354,6 +422,7 @@ async function createRuntimeAdapter(createRegistry, send, extensions) {
|
|
|
354
422
|
environment: env,
|
|
355
423
|
inputs: snap.inputs,
|
|
356
424
|
outputs: snap.outputs,
|
|
425
|
+
ui: extData?.ui || undefined,
|
|
357
426
|
};
|
|
358
427
|
},
|
|
359
428
|
applySnapshotFull: async (payload) => {
|
|
@@ -418,15 +487,6 @@ async function createRuntimeAdapter(createRegistry, send, extensions) {
|
|
|
418
487
|
}
|
|
419
488
|
graphRuntime.setEnvironment(env);
|
|
420
489
|
},
|
|
421
|
-
setInput: (nodeId, handle, value) => {
|
|
422
|
-
// If engine exists, use it; otherwise fall back to direct runtime access
|
|
423
|
-
if (engine) {
|
|
424
|
-
engine.setInput(nodeId, handle, value);
|
|
425
|
-
}
|
|
426
|
-
else {
|
|
427
|
-
graphRuntime?.setInput(nodeId, handle, value);
|
|
428
|
-
}
|
|
429
|
-
},
|
|
430
490
|
setInputs: (nodeId, inputs) => {
|
|
431
491
|
if (engine) {
|
|
432
492
|
engine.setInputs(nodeId, inputs);
|
|
@@ -526,7 +586,6 @@ async function createRuntimeAdapter(createRegistry, send, extensions) {
|
|
|
526
586
|
describeRegistry: wrapMethod("describeRegistry", originalApi.describeRegistry),
|
|
527
587
|
update: wrapMethod("update", originalApi.update),
|
|
528
588
|
setEnvironment: wrapMethod("setEnvironment", originalApi.setEnvironment),
|
|
529
|
-
setInput: wrapMethod("setInput", originalApi.setInput),
|
|
530
589
|
setInputs: wrapMethod("setInputs", originalApi.setInputs),
|
|
531
590
|
triggerExternal: wrapMethod("triggerExternal", originalApi.triggerExternal),
|
|
532
591
|
step: wrapMethod("step", originalApi.step),
|
|
@@ -576,11 +635,6 @@ class RemoteEngine {
|
|
|
576
635
|
},
|
|
577
636
|
});
|
|
578
637
|
}
|
|
579
|
-
setInput(nodeId, handle, value) {
|
|
580
|
-
this.transport.send({
|
|
581
|
-
message: { type: "SetInput", payload: { nodeId, handle, value } },
|
|
582
|
-
});
|
|
583
|
-
}
|
|
584
638
|
// Batch inputs for a single network round-trip
|
|
585
639
|
setInputs(nodeId, inputs) {
|
|
586
640
|
this.transport.send({
|
|
@@ -739,7 +793,13 @@ class RuntimeApiClient {
|
|
|
739
793
|
if (!WebSocketTransport) {
|
|
740
794
|
throw new Error("WebSocketTransport not available");
|
|
741
795
|
}
|
|
742
|
-
return new WebSocketTransport(this.config.url
|
|
796
|
+
return new WebSocketTransport(this.config.url, {
|
|
797
|
+
onConnectionLost: () => {
|
|
798
|
+
this.dispose().catch((err) => {
|
|
799
|
+
console.warn("[RuntimeApiClient] Error disposing on connection loss:", err);
|
|
800
|
+
});
|
|
801
|
+
},
|
|
802
|
+
});
|
|
743
803
|
}
|
|
744
804
|
else if (kind === "remote-unix") {
|
|
745
805
|
// Dynamic import to avoid bundling in browser builds
|
|
@@ -985,6 +1045,12 @@ class RuntimeApiClient {
|
|
|
985
1045
|
message: { type: "Flush" },
|
|
986
1046
|
});
|
|
987
1047
|
}
|
|
1048
|
+
async setExtData(data) {
|
|
1049
|
+
const transport = await this.ensureConnected();
|
|
1050
|
+
await transport.request({
|
|
1051
|
+
message: { type: "SetExtData", payload: data },
|
|
1052
|
+
});
|
|
1053
|
+
}
|
|
988
1054
|
/**
|
|
989
1055
|
* Dispose the client and close the transport connection.
|
|
990
1056
|
* Idempotent: safe to call multiple times.
|
|
@@ -1096,16 +1162,6 @@ class RuntimeApiServer {
|
|
|
1096
1162
|
ack();
|
|
1097
1163
|
break;
|
|
1098
1164
|
}
|
|
1099
|
-
case "SetInput": {
|
|
1100
|
-
this.logCommand("SetInput", env, {
|
|
1101
|
-
nodeId: msg.payload.nodeId,
|
|
1102
|
-
handle: msg.payload.handle,
|
|
1103
|
-
value: summarize(msg.payload.value),
|
|
1104
|
-
});
|
|
1105
|
-
this.runtimeApi.setInput(msg.payload.nodeId, msg.payload.handle, msg.payload.value);
|
|
1106
|
-
ack();
|
|
1107
|
-
break;
|
|
1108
|
-
}
|
|
1109
1165
|
case "SetInputs": {
|
|
1110
1166
|
this.logCommand("SetInputs", env, {
|
|
1111
1167
|
nodeId: msg.payload.nodeId,
|
|
@@ -1218,15 +1274,14 @@ class RuntimeApiServer {
|
|
|
1218
1274
|
ack();
|
|
1219
1275
|
break;
|
|
1220
1276
|
}
|
|
1221
|
-
case "
|
|
1222
|
-
this.
|
|
1223
|
-
this.runtimeApi.dispose();
|
|
1277
|
+
case "SetExtData": {
|
|
1278
|
+
this.runtimeApi.setExtData(msg.payload);
|
|
1224
1279
|
ack();
|
|
1225
1280
|
break;
|
|
1226
1281
|
}
|
|
1227
|
-
case "
|
|
1228
|
-
|
|
1229
|
-
this.
|
|
1282
|
+
case "Dispose": {
|
|
1283
|
+
this.logCommand("Dispose", env);
|
|
1284
|
+
this.runtimeApi.dispose();
|
|
1230
1285
|
ack();
|
|
1231
1286
|
break;
|
|
1232
1287
|
}
|