@bian-womp/spark-remote 0.2.50 → 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 +80 -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 +80 -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,7 +582,6 @@ 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),
|
|
530
587
|
step: wrapMethod("step", originalApi.step),
|
|
@@ -574,11 +631,6 @@ class RemoteEngine {
|
|
|
574
631
|
},
|
|
575
632
|
});
|
|
576
633
|
}
|
|
577
|
-
setInput(nodeId, handle, value) {
|
|
578
|
-
this.transport.send({
|
|
579
|
-
message: { type: "SetInput", payload: { nodeId, handle, value } },
|
|
580
|
-
});
|
|
581
|
-
}
|
|
582
634
|
// Batch inputs for a single network round-trip
|
|
583
635
|
setInputs(nodeId, inputs) {
|
|
584
636
|
this.transport.send({
|
|
@@ -737,7 +789,13 @@ class RuntimeApiClient {
|
|
|
737
789
|
if (!WebSocketTransport) {
|
|
738
790
|
throw new Error("WebSocketTransport not available");
|
|
739
791
|
}
|
|
740
|
-
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
|
+
});
|
|
741
799
|
}
|
|
742
800
|
else if (kind === "remote-unix") {
|
|
743
801
|
// Dynamic import to avoid bundling in browser builds
|
|
@@ -1094,16 +1152,6 @@ class RuntimeApiServer {
|
|
|
1094
1152
|
ack();
|
|
1095
1153
|
break;
|
|
1096
1154
|
}
|
|
1097
|
-
case "SetInput": {
|
|
1098
|
-
this.logCommand("SetInput", env, {
|
|
1099
|
-
nodeId: msg.payload.nodeId,
|
|
1100
|
-
handle: msg.payload.handle,
|
|
1101
|
-
value: summarize(msg.payload.value),
|
|
1102
|
-
});
|
|
1103
|
-
this.runtimeApi.setInput(msg.payload.nodeId, msg.payload.handle, msg.payload.value);
|
|
1104
|
-
ack();
|
|
1105
|
-
break;
|
|
1106
|
-
}
|
|
1107
1155
|
case "SetInputs": {
|
|
1108
1156
|
this.logCommand("SetInputs", env, {
|
|
1109
1157
|
nodeId: msg.payload.nodeId,
|
|
@@ -1222,12 +1270,6 @@ class RuntimeApiServer {
|
|
|
1222
1270
|
ack();
|
|
1223
1271
|
break;
|
|
1224
1272
|
}
|
|
1225
|
-
case "Pause":
|
|
1226
|
-
case "Resume": {
|
|
1227
|
-
this.logCommand(`${msg.type} (not-impl)`, env);
|
|
1228
|
-
ack();
|
|
1229
|
-
break;
|
|
1230
|
-
}
|
|
1231
1273
|
default: {
|
|
1232
1274
|
this.logCommand("Unknown type", env);
|
|
1233
1275
|
ack();
|