@bian-womp/spark-remote 0.2.49 → 0.2.51
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 +83 -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.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 +83 -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.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/esm/index.js
CHANGED
|
@@ -21,22 +21,23 @@ class SeqGenerator {
|
|
|
21
21
|
|
|
22
22
|
const OPEN = 1;
|
|
23
23
|
class WebSocketTransport {
|
|
24
|
-
constructor(url) {
|
|
24
|
+
constructor(url, options) {
|
|
25
25
|
this.listeners = new Set();
|
|
26
26
|
this.seq = new SeqGenerator();
|
|
27
|
+
this.isAlive = false;
|
|
28
|
+
this.PING_INTERVAL_MS = 30000;
|
|
29
|
+
this.PONG_TIMEOUT_MS = 10000;
|
|
27
30
|
this.baseUrl = url;
|
|
31
|
+
this.onConnectionLost = options?.onConnectionLost;
|
|
28
32
|
}
|
|
29
33
|
async connect(options) {
|
|
30
34
|
if (this.ws && this.ws.readyState === OPEN)
|
|
31
35
|
return;
|
|
32
|
-
// Build URL with connection params
|
|
33
|
-
// Handle both ws:///wss:// URLs and http:///https:// URLs (convert to ws)
|
|
34
36
|
let url;
|
|
35
37
|
if (this.baseUrl.startsWith("ws://") || this.baseUrl.startsWith("wss://")) {
|
|
36
38
|
url = new URL(this.baseUrl);
|
|
37
39
|
}
|
|
38
40
|
else {
|
|
39
|
-
// Convert http/https to ws/wss
|
|
40
41
|
const wsBaseUrl = this.baseUrl.replace(/^http/, "ws");
|
|
41
42
|
url = new URL(wsBaseUrl);
|
|
42
43
|
}
|
|
@@ -54,7 +55,10 @@ class WebSocketTransport {
|
|
|
54
55
|
await new Promise((resolve, reject) => {
|
|
55
56
|
if (!this.ws)
|
|
56
57
|
return reject(new Error("ws init failed"));
|
|
57
|
-
this.ws.onopen = () =>
|
|
58
|
+
this.ws.onopen = () => {
|
|
59
|
+
this.startHeartbeat();
|
|
60
|
+
resolve();
|
|
61
|
+
};
|
|
58
62
|
this.ws.onerror = (e) => reject(e);
|
|
59
63
|
this.ws.onmessage = (ev) => {
|
|
60
64
|
try {
|
|
@@ -64,10 +68,72 @@ class WebSocketTransport {
|
|
|
64
68
|
}
|
|
65
69
|
catch { }
|
|
66
70
|
};
|
|
71
|
+
this.ws.onclose = () => {
|
|
72
|
+
this.stopHeartbeat();
|
|
73
|
+
};
|
|
67
74
|
});
|
|
68
75
|
}
|
|
76
|
+
startHeartbeat() {
|
|
77
|
+
this.stopHeartbeat();
|
|
78
|
+
this.isAlive = true;
|
|
79
|
+
if (typeof window !== "undefined" || !this.ws)
|
|
80
|
+
return;
|
|
81
|
+
this.pongHandler = () => {
|
|
82
|
+
this.isAlive = true;
|
|
83
|
+
if (this.pongTimeout) {
|
|
84
|
+
clearTimeout(this.pongTimeout);
|
|
85
|
+
this.pongTimeout = undefined;
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
this.ws.on("pong", this.pongHandler);
|
|
89
|
+
this.pingInterval = setInterval(() => {
|
|
90
|
+
if (!this.ws || this.ws.readyState !== OPEN) {
|
|
91
|
+
this.stopHeartbeat();
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
if (!this.isAlive) {
|
|
95
|
+
this.onConnectionLost?.();
|
|
96
|
+
this.stopHeartbeat();
|
|
97
|
+
this.ws.terminate();
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
this.isAlive = false;
|
|
101
|
+
try {
|
|
102
|
+
this.ws.ping();
|
|
103
|
+
}
|
|
104
|
+
catch (err) {
|
|
105
|
+
console.warn("[WebSocketTransport] Failed to send ping:", err);
|
|
106
|
+
this.onConnectionLost?.();
|
|
107
|
+
this.stopHeartbeat();
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
this.pongTimeout = setTimeout(() => {
|
|
111
|
+
if (!this.isAlive) {
|
|
112
|
+
this.onConnectionLost?.();
|
|
113
|
+
this.stopHeartbeat();
|
|
114
|
+
if (this.ws) {
|
|
115
|
+
this.ws.terminate();
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}, this.PONG_TIMEOUT_MS);
|
|
119
|
+
}, this.PING_INTERVAL_MS);
|
|
120
|
+
}
|
|
121
|
+
stopHeartbeat() {
|
|
122
|
+
if (this.pingInterval) {
|
|
123
|
+
clearInterval(this.pingInterval);
|
|
124
|
+
this.pingInterval = undefined;
|
|
125
|
+
}
|
|
126
|
+
if (this.pongTimeout) {
|
|
127
|
+
clearTimeout(this.pongTimeout);
|
|
128
|
+
this.pongTimeout = undefined;
|
|
129
|
+
}
|
|
130
|
+
if (this.ws && this.pongHandler) {
|
|
131
|
+
this.ws.off("pong", this.pongHandler);
|
|
132
|
+
this.pongHandler = undefined;
|
|
133
|
+
}
|
|
134
|
+
this.isAlive = false;
|
|
135
|
+
}
|
|
69
136
|
async request(msg) {
|
|
70
|
-
// For now, just send and wait for the next message with matching seq (simple demo)
|
|
71
137
|
const seq = this.seq.next();
|
|
72
138
|
const env = { ...msg, seq };
|
|
73
139
|
const p = new Promise((resolve) => {
|
|
@@ -91,6 +157,7 @@ class WebSocketTransport {
|
|
|
91
157
|
return () => this.listeners.delete(cb);
|
|
92
158
|
}
|
|
93
159
|
async close() {
|
|
160
|
+
this.stopHeartbeat();
|
|
94
161
|
if (!this.ws)
|
|
95
162
|
return;
|
|
96
163
|
const ws = this.ws;
|
|
@@ -416,15 +483,6 @@ async function createRuntimeAdapter(createRegistry, send, extensions) {
|
|
|
416
483
|
}
|
|
417
484
|
graphRuntime.setEnvironment(env);
|
|
418
485
|
},
|
|
419
|
-
setInput: (nodeId, handle, value) => {
|
|
420
|
-
// If engine exists, use it; otherwise fall back to direct runtime access
|
|
421
|
-
if (engine) {
|
|
422
|
-
engine.setInput(nodeId, handle, value);
|
|
423
|
-
}
|
|
424
|
-
else {
|
|
425
|
-
graphRuntime?.setInput(nodeId, handle, value);
|
|
426
|
-
}
|
|
427
|
-
},
|
|
428
486
|
setInputs: (nodeId, inputs) => {
|
|
429
487
|
if (engine) {
|
|
430
488
|
engine.setInputs(nodeId, inputs);
|
|
@@ -524,9 +582,11 @@ async function createRuntimeAdapter(createRegistry, send, extensions) {
|
|
|
524
582
|
describeRegistry: wrapMethod("describeRegistry", originalApi.describeRegistry),
|
|
525
583
|
update: wrapMethod("update", originalApi.update),
|
|
526
584
|
setEnvironment: wrapMethod("setEnvironment", originalApi.setEnvironment),
|
|
527
|
-
setInput: wrapMethod("setInput", originalApi.setInput),
|
|
528
585
|
setInputs: wrapMethod("setInputs", originalApi.setInputs),
|
|
529
586
|
triggerExternal: wrapMethod("triggerExternal", originalApi.triggerExternal),
|
|
587
|
+
step: wrapMethod("step", originalApi.step),
|
|
588
|
+
computeNode: wrapMethod("computeNode", originalApi.computeNode),
|
|
589
|
+
flush: wrapMethod("flush", originalApi.flush),
|
|
530
590
|
launch: wrapMethod("launch", originalApi.launch),
|
|
531
591
|
whenIdle: wrapMethod("whenIdle", originalApi.whenIdle),
|
|
532
592
|
dispose: wrapMethod("dispose", originalApi.dispose),
|
|
@@ -571,11 +631,6 @@ class RemoteEngine {
|
|
|
571
631
|
},
|
|
572
632
|
});
|
|
573
633
|
}
|
|
574
|
-
setInput(nodeId, handle, value) {
|
|
575
|
-
this.transport.send({
|
|
576
|
-
message: { type: "SetInput", payload: { nodeId, handle, value } },
|
|
577
|
-
});
|
|
578
|
-
}
|
|
579
634
|
// Batch inputs for a single network round-trip
|
|
580
635
|
setInputs(nodeId, inputs) {
|
|
581
636
|
this.transport.send({
|
|
@@ -734,7 +789,13 @@ class RuntimeApiClient {
|
|
|
734
789
|
if (!WebSocketTransport) {
|
|
735
790
|
throw new Error("WebSocketTransport not available");
|
|
736
791
|
}
|
|
737
|
-
return new WebSocketTransport(this.config.url
|
|
792
|
+
return new WebSocketTransport(this.config.url, {
|
|
793
|
+
onConnectionLost: () => {
|
|
794
|
+
this.dispose().catch((err) => {
|
|
795
|
+
console.warn("[RuntimeApiClient] Error disposing on connection loss:", err);
|
|
796
|
+
});
|
|
797
|
+
},
|
|
798
|
+
});
|
|
738
799
|
}
|
|
739
800
|
else if (kind === "remote-unix") {
|
|
740
801
|
// Dynamic import to avoid bundling in browser builds
|
|
@@ -1091,16 +1152,6 @@ class RuntimeApiServer {
|
|
|
1091
1152
|
ack();
|
|
1092
1153
|
break;
|
|
1093
1154
|
}
|
|
1094
|
-
case "SetInput": {
|
|
1095
|
-
this.logCommand("SetInput", env, {
|
|
1096
|
-
nodeId: msg.payload.nodeId,
|
|
1097
|
-
handle: msg.payload.handle,
|
|
1098
|
-
value: summarize(msg.payload.value),
|
|
1099
|
-
});
|
|
1100
|
-
this.runtimeApi.setInput(msg.payload.nodeId, msg.payload.handle, msg.payload.value);
|
|
1101
|
-
ack();
|
|
1102
|
-
break;
|
|
1103
|
-
}
|
|
1104
1155
|
case "SetInputs": {
|
|
1105
1156
|
this.logCommand("SetInputs", env, {
|
|
1106
1157
|
nodeId: msg.payload.nodeId,
|
|
@@ -1219,12 +1270,6 @@ class RuntimeApiServer {
|
|
|
1219
1270
|
ack();
|
|
1220
1271
|
break;
|
|
1221
1272
|
}
|
|
1222
|
-
case "Pause":
|
|
1223
|
-
case "Resume": {
|
|
1224
|
-
this.logCommand(`${msg.type} (not-impl)`, env);
|
|
1225
|
-
ack();
|
|
1226
|
-
break;
|
|
1227
|
-
}
|
|
1228
1273
|
default: {
|
|
1229
1274
|
this.logCommand("Unknown type", env);
|
|
1230
1275
|
ack();
|