@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/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;
|
|
@@ -418,15 +485,6 @@ async function createRuntimeAdapter(createRegistry, send, extensions) {
|
|
|
418
485
|
}
|
|
419
486
|
graphRuntime.setEnvironment(env);
|
|
420
487
|
},
|
|
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
488
|
setInputs: (nodeId, inputs) => {
|
|
431
489
|
if (engine) {
|
|
432
490
|
engine.setInputs(nodeId, inputs);
|
|
@@ -526,7 +584,6 @@ async function createRuntimeAdapter(createRegistry, send, extensions) {
|
|
|
526
584
|
describeRegistry: wrapMethod("describeRegistry", originalApi.describeRegistry),
|
|
527
585
|
update: wrapMethod("update", originalApi.update),
|
|
528
586
|
setEnvironment: wrapMethod("setEnvironment", originalApi.setEnvironment),
|
|
529
|
-
setInput: wrapMethod("setInput", originalApi.setInput),
|
|
530
587
|
setInputs: wrapMethod("setInputs", originalApi.setInputs),
|
|
531
588
|
triggerExternal: wrapMethod("triggerExternal", originalApi.triggerExternal),
|
|
532
589
|
step: wrapMethod("step", originalApi.step),
|
|
@@ -576,11 +633,6 @@ class RemoteEngine {
|
|
|
576
633
|
},
|
|
577
634
|
});
|
|
578
635
|
}
|
|
579
|
-
setInput(nodeId, handle, value) {
|
|
580
|
-
this.transport.send({
|
|
581
|
-
message: { type: "SetInput", payload: { nodeId, handle, value } },
|
|
582
|
-
});
|
|
583
|
-
}
|
|
584
636
|
// Batch inputs for a single network round-trip
|
|
585
637
|
setInputs(nodeId, inputs) {
|
|
586
638
|
this.transport.send({
|
|
@@ -739,7 +791,13 @@ class RuntimeApiClient {
|
|
|
739
791
|
if (!WebSocketTransport) {
|
|
740
792
|
throw new Error("WebSocketTransport not available");
|
|
741
793
|
}
|
|
742
|
-
return new WebSocketTransport(this.config.url
|
|
794
|
+
return new WebSocketTransport(this.config.url, {
|
|
795
|
+
onConnectionLost: () => {
|
|
796
|
+
this.dispose().catch((err) => {
|
|
797
|
+
console.warn("[RuntimeApiClient] Error disposing on connection loss:", err);
|
|
798
|
+
});
|
|
799
|
+
},
|
|
800
|
+
});
|
|
743
801
|
}
|
|
744
802
|
else if (kind === "remote-unix") {
|
|
745
803
|
// Dynamic import to avoid bundling in browser builds
|
|
@@ -1096,16 +1154,6 @@ class RuntimeApiServer {
|
|
|
1096
1154
|
ack();
|
|
1097
1155
|
break;
|
|
1098
1156
|
}
|
|
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
1157
|
case "SetInputs": {
|
|
1110
1158
|
this.logCommand("SetInputs", env, {
|
|
1111
1159
|
nodeId: msg.payload.nodeId,
|
|
@@ -1224,12 +1272,6 @@ class RuntimeApiServer {
|
|
|
1224
1272
|
ack();
|
|
1225
1273
|
break;
|
|
1226
1274
|
}
|
|
1227
|
-
case "Pause":
|
|
1228
|
-
case "Resume": {
|
|
1229
|
-
this.logCommand(`${msg.type} (not-impl)`, env);
|
|
1230
|
-
ack();
|
|
1231
|
-
break;
|
|
1232
|
-
}
|
|
1233
1275
|
default: {
|
|
1234
1276
|
this.logCommand("Unknown type", env);
|
|
1235
1277
|
ack();
|