@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/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;
|
|
@@ -345,6 +412,7 @@ async function createRuntimeAdapter(createRegistry, send, extensions) {
|
|
|
345
412
|
},
|
|
346
413
|
snapshotFull: () => {
|
|
347
414
|
const snap = originalApi.snapshot();
|
|
415
|
+
const extData = originalApi.getExtData();
|
|
348
416
|
const env = graphRuntime?.getEnvironment?.() ?? {};
|
|
349
417
|
const def = graphRuntime?.getGraphDef();
|
|
350
418
|
return {
|
|
@@ -352,6 +420,7 @@ async function createRuntimeAdapter(createRegistry, send, extensions) {
|
|
|
352
420
|
environment: env,
|
|
353
421
|
inputs: snap.inputs,
|
|
354
422
|
outputs: snap.outputs,
|
|
423
|
+
ui: extData?.ui || undefined,
|
|
355
424
|
};
|
|
356
425
|
},
|
|
357
426
|
applySnapshotFull: async (payload) => {
|
|
@@ -416,15 +485,6 @@ async function createRuntimeAdapter(createRegistry, send, extensions) {
|
|
|
416
485
|
}
|
|
417
486
|
graphRuntime.setEnvironment(env);
|
|
418
487
|
},
|
|
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
488
|
setInputs: (nodeId, inputs) => {
|
|
429
489
|
if (engine) {
|
|
430
490
|
engine.setInputs(nodeId, inputs);
|
|
@@ -524,7 +584,6 @@ async function createRuntimeAdapter(createRegistry, send, extensions) {
|
|
|
524
584
|
describeRegistry: wrapMethod("describeRegistry", originalApi.describeRegistry),
|
|
525
585
|
update: wrapMethod("update", originalApi.update),
|
|
526
586
|
setEnvironment: wrapMethod("setEnvironment", originalApi.setEnvironment),
|
|
527
|
-
setInput: wrapMethod("setInput", originalApi.setInput),
|
|
528
587
|
setInputs: wrapMethod("setInputs", originalApi.setInputs),
|
|
529
588
|
triggerExternal: wrapMethod("triggerExternal", originalApi.triggerExternal),
|
|
530
589
|
step: wrapMethod("step", originalApi.step),
|
|
@@ -574,11 +633,6 @@ class RemoteEngine {
|
|
|
574
633
|
},
|
|
575
634
|
});
|
|
576
635
|
}
|
|
577
|
-
setInput(nodeId, handle, value) {
|
|
578
|
-
this.transport.send({
|
|
579
|
-
message: { type: "SetInput", payload: { nodeId, handle, value } },
|
|
580
|
-
});
|
|
581
|
-
}
|
|
582
636
|
// Batch inputs for a single network round-trip
|
|
583
637
|
setInputs(nodeId, inputs) {
|
|
584
638
|
this.transport.send({
|
|
@@ -737,7 +791,13 @@ class RuntimeApiClient {
|
|
|
737
791
|
if (!WebSocketTransport) {
|
|
738
792
|
throw new Error("WebSocketTransport not available");
|
|
739
793
|
}
|
|
740
|
-
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
|
+
});
|
|
741
801
|
}
|
|
742
802
|
else if (kind === "remote-unix") {
|
|
743
803
|
// Dynamic import to avoid bundling in browser builds
|
|
@@ -983,6 +1043,12 @@ class RuntimeApiClient {
|
|
|
983
1043
|
message: { type: "Flush" },
|
|
984
1044
|
});
|
|
985
1045
|
}
|
|
1046
|
+
async setExtData(data) {
|
|
1047
|
+
const transport = await this.ensureConnected();
|
|
1048
|
+
await transport.request({
|
|
1049
|
+
message: { type: "SetExtData", payload: data },
|
|
1050
|
+
});
|
|
1051
|
+
}
|
|
986
1052
|
/**
|
|
987
1053
|
* Dispose the client and close the transport connection.
|
|
988
1054
|
* Idempotent: safe to call multiple times.
|
|
@@ -1094,16 +1160,6 @@ class RuntimeApiServer {
|
|
|
1094
1160
|
ack();
|
|
1095
1161
|
break;
|
|
1096
1162
|
}
|
|
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
1163
|
case "SetInputs": {
|
|
1108
1164
|
this.logCommand("SetInputs", env, {
|
|
1109
1165
|
nodeId: msg.payload.nodeId,
|
|
@@ -1216,15 +1272,14 @@ class RuntimeApiServer {
|
|
|
1216
1272
|
ack();
|
|
1217
1273
|
break;
|
|
1218
1274
|
}
|
|
1219
|
-
case "
|
|
1220
|
-
this.
|
|
1221
|
-
this.runtimeApi.dispose();
|
|
1275
|
+
case "SetExtData": {
|
|
1276
|
+
this.runtimeApi.setExtData(msg.payload);
|
|
1222
1277
|
ack();
|
|
1223
1278
|
break;
|
|
1224
1279
|
}
|
|
1225
|
-
case "
|
|
1226
|
-
|
|
1227
|
-
this.
|
|
1280
|
+
case "Dispose": {
|
|
1281
|
+
this.logCommand("Dispose", env);
|
|
1282
|
+
this.runtimeApi.dispose();
|
|
1228
1283
|
ack();
|
|
1229
1284
|
break;
|
|
1230
1285
|
}
|