@reactor-team/js-sdk 2.3.1 → 2.4.0
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/dist/index.d.mts +63 -14
- package/dist/index.d.ts +63 -14
- package/dist/index.js +180 -42
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +186 -50
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -88,8 +88,10 @@ __export(index_exports, {
|
|
|
88
88
|
WebcamStream: () => WebcamStream,
|
|
89
89
|
fetchInsecureJwtToken: () => fetchInsecureJwtToken,
|
|
90
90
|
useReactor: () => useReactor,
|
|
91
|
+
useReactorInternalMessage: () => useReactorInternalMessage,
|
|
91
92
|
useReactorMessage: () => useReactorMessage,
|
|
92
|
-
useReactorStore: () => useReactorStore
|
|
93
|
+
useReactorStore: () => useReactorStore,
|
|
94
|
+
useStats: () => useStats
|
|
93
95
|
});
|
|
94
96
|
module.exports = __toCommonJS(index_exports);
|
|
95
97
|
|
|
@@ -249,11 +251,58 @@ function parseMessage(data) {
|
|
|
249
251
|
function closePeerConnection(pc) {
|
|
250
252
|
pc.close();
|
|
251
253
|
}
|
|
254
|
+
function extractConnectionStats(report) {
|
|
255
|
+
let rtt;
|
|
256
|
+
let availableOutgoingBitrate;
|
|
257
|
+
let localCandidateId;
|
|
258
|
+
let framesPerSecond;
|
|
259
|
+
let jitter;
|
|
260
|
+
let packetLossRatio;
|
|
261
|
+
report.forEach((stat) => {
|
|
262
|
+
if (stat.type === "candidate-pair" && stat.state === "succeeded") {
|
|
263
|
+
if (stat.currentRoundTripTime !== void 0) {
|
|
264
|
+
rtt = stat.currentRoundTripTime * 1e3;
|
|
265
|
+
}
|
|
266
|
+
if (stat.availableOutgoingBitrate !== void 0) {
|
|
267
|
+
availableOutgoingBitrate = stat.availableOutgoingBitrate;
|
|
268
|
+
}
|
|
269
|
+
localCandidateId = stat.localCandidateId;
|
|
270
|
+
}
|
|
271
|
+
if (stat.type === "inbound-rtp" && stat.kind === "video") {
|
|
272
|
+
if (stat.framesPerSecond !== void 0) {
|
|
273
|
+
framesPerSecond = stat.framesPerSecond;
|
|
274
|
+
}
|
|
275
|
+
if (stat.jitter !== void 0) {
|
|
276
|
+
jitter = stat.jitter;
|
|
277
|
+
}
|
|
278
|
+
if (stat.packetsReceived !== void 0 && stat.packetsLost !== void 0 && stat.packetsReceived + stat.packetsLost > 0) {
|
|
279
|
+
packetLossRatio = stat.packetsLost / (stat.packetsReceived + stat.packetsLost);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
});
|
|
283
|
+
let candidateType;
|
|
284
|
+
if (localCandidateId) {
|
|
285
|
+
const localCandidate = report.get(localCandidateId);
|
|
286
|
+
if (localCandidate == null ? void 0 : localCandidate.candidateType) {
|
|
287
|
+
candidateType = localCandidate.candidateType;
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
return {
|
|
291
|
+
rtt,
|
|
292
|
+
candidateType,
|
|
293
|
+
availableOutgoingBitrate,
|
|
294
|
+
framesPerSecond,
|
|
295
|
+
packetLossRatio,
|
|
296
|
+
jitter,
|
|
297
|
+
timestamp: Date.now()
|
|
298
|
+
};
|
|
299
|
+
}
|
|
252
300
|
|
|
253
301
|
// src/core/CoordinatorClient.ts
|
|
254
302
|
var INITIAL_BACKOFF_MS = 500;
|
|
255
|
-
var MAX_BACKOFF_MS =
|
|
303
|
+
var MAX_BACKOFF_MS = 15e3;
|
|
256
304
|
var BACKOFF_MULTIPLIER = 2;
|
|
305
|
+
var DEFAULT_MAX_ATTEMPTS = 6;
|
|
257
306
|
var CoordinatorClient = class {
|
|
258
307
|
constructor(options) {
|
|
259
308
|
this.baseUrl = options.baseUrl;
|
|
@@ -446,13 +495,14 @@ var CoordinatorClient = class {
|
|
|
446
495
|
});
|
|
447
496
|
}
|
|
448
497
|
/**
|
|
449
|
-
* Polls for the SDP answer with
|
|
498
|
+
* Polls for the SDP answer with exponential backoff.
|
|
450
499
|
* Used for async reconnection when the answer is not immediately available.
|
|
451
500
|
* @param sessionId - The session ID to poll for
|
|
501
|
+
* @param maxAttempts - Optional maximum number of polling attempts before giving up
|
|
452
502
|
* @returns The SDP answer from the server
|
|
453
503
|
*/
|
|
454
|
-
pollSdpAnswer(
|
|
455
|
-
return __async(this,
|
|
504
|
+
pollSdpAnswer(_0) {
|
|
505
|
+
return __async(this, arguments, function* (sessionId, maxAttempts = DEFAULT_MAX_ATTEMPTS) {
|
|
456
506
|
console.debug(
|
|
457
507
|
"[CoordinatorClient] Polling for SDP answer for session:",
|
|
458
508
|
sessionId
|
|
@@ -460,9 +510,14 @@ var CoordinatorClient = class {
|
|
|
460
510
|
let backoffMs = INITIAL_BACKOFF_MS;
|
|
461
511
|
let attempt = 0;
|
|
462
512
|
while (true) {
|
|
513
|
+
if (attempt >= maxAttempts) {
|
|
514
|
+
throw new Error(
|
|
515
|
+
`SDP polling exceeded maximum attempts (${maxAttempts}) for session ${sessionId}`
|
|
516
|
+
);
|
|
517
|
+
}
|
|
463
518
|
attempt++;
|
|
464
519
|
console.debug(
|
|
465
|
-
`[CoordinatorClient] SDP poll attempt ${attempt} for session ${sessionId}`
|
|
520
|
+
`[CoordinatorClient] SDP poll attempt ${attempt}/${maxAttempts} for session ${sessionId}`
|
|
466
521
|
);
|
|
467
522
|
const response = yield fetch(
|
|
468
523
|
`${this.baseUrl}/sessions/${sessionId}/sdp_params`,
|
|
@@ -499,9 +554,10 @@ var CoordinatorClient = class {
|
|
|
499
554
|
* falls back to polling. If no sdpOffer is provided, goes directly to polling.
|
|
500
555
|
* @param sessionId - The session ID to connect to
|
|
501
556
|
* @param sdpOffer - Optional SDP offer from the local WebRTC peer connection
|
|
557
|
+
* @param maxAttempts - Optional maximum number of polling attempts before giving up
|
|
502
558
|
* @returns The SDP answer from the server
|
|
503
559
|
*/
|
|
504
|
-
connect(sessionId, sdpOffer) {
|
|
560
|
+
connect(sessionId, sdpOffer, maxAttempts) {
|
|
505
561
|
return __async(this, null, function* () {
|
|
506
562
|
console.debug("[CoordinatorClient] Connecting to session:", sessionId);
|
|
507
563
|
if (sdpOffer) {
|
|
@@ -510,7 +566,7 @@ var CoordinatorClient = class {
|
|
|
510
566
|
return answer;
|
|
511
567
|
}
|
|
512
568
|
}
|
|
513
|
-
return this.pollSdpAnswer(sessionId);
|
|
569
|
+
return this.pollSdpAnswer(sessionId, maxAttempts);
|
|
514
570
|
});
|
|
515
571
|
}
|
|
516
572
|
/**
|
|
@@ -616,6 +672,7 @@ var LocalCoordinatorClient = class extends CoordinatorClient {
|
|
|
616
672
|
|
|
617
673
|
// src/core/GPUMachineClient.ts
|
|
618
674
|
var PING_INTERVAL_MS = 5e3;
|
|
675
|
+
var STATS_INTERVAL_MS = 2e3;
|
|
619
676
|
var GPUMachineClient = class {
|
|
620
677
|
constructor(config) {
|
|
621
678
|
this.eventListeners = /* @__PURE__ */ new Map();
|
|
@@ -699,6 +756,7 @@ var GPUMachineClient = class {
|
|
|
699
756
|
disconnect() {
|
|
700
757
|
return __async(this, null, function* () {
|
|
701
758
|
this.stopPing();
|
|
759
|
+
this.stopStatsPolling();
|
|
702
760
|
if (this.publishedTrack) {
|
|
703
761
|
yield this.unpublishTrack();
|
|
704
762
|
}
|
|
@@ -855,6 +913,31 @@ var GPUMachineClient = class {
|
|
|
855
913
|
}
|
|
856
914
|
}
|
|
857
915
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
916
|
+
// Stats Polling (RTT)
|
|
917
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
918
|
+
getStats() {
|
|
919
|
+
return this.stats;
|
|
920
|
+
}
|
|
921
|
+
startStatsPolling() {
|
|
922
|
+
this.stopStatsPolling();
|
|
923
|
+
this.statsInterval = setInterval(() => __async(this, null, function* () {
|
|
924
|
+
if (!this.peerConnection) return;
|
|
925
|
+
try {
|
|
926
|
+
const report = yield this.peerConnection.getStats();
|
|
927
|
+
this.stats = extractConnectionStats(report);
|
|
928
|
+
this.emit("statsUpdate", this.stats);
|
|
929
|
+
} catch (e) {
|
|
930
|
+
}
|
|
931
|
+
}), STATS_INTERVAL_MS);
|
|
932
|
+
}
|
|
933
|
+
stopStatsPolling() {
|
|
934
|
+
if (this.statsInterval !== void 0) {
|
|
935
|
+
clearInterval(this.statsInterval);
|
|
936
|
+
this.statsInterval = void 0;
|
|
937
|
+
}
|
|
938
|
+
this.stats = void 0;
|
|
939
|
+
}
|
|
940
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
858
941
|
// Private Helpers
|
|
859
942
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
860
943
|
setStatus(newStatus) {
|
|
@@ -873,6 +956,7 @@ var GPUMachineClient = class {
|
|
|
873
956
|
switch (state) {
|
|
874
957
|
case "connected":
|
|
875
958
|
this.setStatus("connected");
|
|
959
|
+
this.startStatsPolling();
|
|
876
960
|
break;
|
|
877
961
|
case "disconnected":
|
|
878
962
|
case "closed":
|
|
@@ -1054,8 +1138,9 @@ var Reactor = class {
|
|
|
1054
1138
|
}
|
|
1055
1139
|
/**
|
|
1056
1140
|
* Public method for reconnecting to an existing session, that may have been interrupted but can be recovered.
|
|
1141
|
+
* @param options Optional connect options (e.g. maxAttempts for SDP polling)
|
|
1057
1142
|
*/
|
|
1058
|
-
reconnect() {
|
|
1143
|
+
reconnect(options) {
|
|
1059
1144
|
return __async(this, null, function* () {
|
|
1060
1145
|
if (!this.sessionId || !this.coordinatorClient) {
|
|
1061
1146
|
console.warn("[Reactor] No active session to reconnect to.");
|
|
@@ -1075,7 +1160,8 @@ var Reactor = class {
|
|
|
1075
1160
|
try {
|
|
1076
1161
|
const sdpAnswer = yield this.coordinatorClient.connect(
|
|
1077
1162
|
this.sessionId,
|
|
1078
|
-
sdpOffer
|
|
1163
|
+
sdpOffer,
|
|
1164
|
+
options == null ? void 0 : options.maxAttempts
|
|
1079
1165
|
);
|
|
1080
1166
|
yield this.machineClient.connect(sdpAnswer);
|
|
1081
1167
|
this.setStatus("ready");
|
|
@@ -1099,8 +1185,10 @@ var Reactor = class {
|
|
|
1099
1185
|
* Connects to the coordinator and waits for a GPU to be assigned.
|
|
1100
1186
|
* Once a GPU is assigned, the Reactor will connect to the gpu machine via WebRTC.
|
|
1101
1187
|
* If no authentication is provided and not in local mode, an error is thrown.
|
|
1188
|
+
* @param jwtToken Optional JWT token for authentication
|
|
1189
|
+
* @param options Optional connect options (e.g. maxAttempts for SDP polling)
|
|
1102
1190
|
*/
|
|
1103
|
-
connect(jwtToken) {
|
|
1191
|
+
connect(jwtToken, options) {
|
|
1104
1192
|
return __async(this, null, function* () {
|
|
1105
1193
|
console.debug("[Reactor] Connecting, status:", this.status);
|
|
1106
1194
|
if (jwtToken == void 0 && !this.local) {
|
|
@@ -1117,7 +1205,7 @@ var Reactor = class {
|
|
|
1117
1205
|
this.coordinatorClient = this.local ? new LocalCoordinatorClient(this.coordinatorUrl) : new CoordinatorClient({
|
|
1118
1206
|
baseUrl: this.coordinatorUrl,
|
|
1119
1207
|
jwtToken,
|
|
1120
|
-
// Safe: validated
|
|
1208
|
+
// Safe: validated above
|
|
1121
1209
|
model: this.model
|
|
1122
1210
|
});
|
|
1123
1211
|
const iceServers = yield this.coordinatorClient.getIceServers();
|
|
@@ -1126,7 +1214,11 @@ var Reactor = class {
|
|
|
1126
1214
|
const sdpOffer = yield this.machineClient.createOffer();
|
|
1127
1215
|
const sessionId = yield this.coordinatorClient.createSession(sdpOffer);
|
|
1128
1216
|
this.setSessionId(sessionId);
|
|
1129
|
-
const sdpAnswer = yield this.coordinatorClient.connect(
|
|
1217
|
+
const sdpAnswer = yield this.coordinatorClient.connect(
|
|
1218
|
+
sessionId,
|
|
1219
|
+
void 0,
|
|
1220
|
+
options == null ? void 0 : options.maxAttempts
|
|
1221
|
+
);
|
|
1130
1222
|
yield this.machineClient.connect(sdpAnswer);
|
|
1131
1223
|
} catch (error) {
|
|
1132
1224
|
console.error("[Reactor] Connection failed:", error);
|
|
@@ -1136,7 +1228,14 @@ var Reactor = class {
|
|
|
1136
1228
|
"coordinator",
|
|
1137
1229
|
true
|
|
1138
1230
|
);
|
|
1139
|
-
|
|
1231
|
+
try {
|
|
1232
|
+
yield this.disconnect(false);
|
|
1233
|
+
} catch (disconnectError) {
|
|
1234
|
+
console.error(
|
|
1235
|
+
"[Reactor] Failed to clean up after connection failure:",
|
|
1236
|
+
disconnectError
|
|
1237
|
+
);
|
|
1238
|
+
}
|
|
1140
1239
|
throw error;
|
|
1141
1240
|
}
|
|
1142
1241
|
});
|
|
@@ -1147,7 +1246,11 @@ var Reactor = class {
|
|
|
1147
1246
|
setupMachineClientHandlers() {
|
|
1148
1247
|
if (!this.machineClient) return;
|
|
1149
1248
|
this.machineClient.on("message", (message, scope) => {
|
|
1150
|
-
|
|
1249
|
+
if (scope === "application") {
|
|
1250
|
+
this.emit("message", message);
|
|
1251
|
+
} else if (scope === "runtime") {
|
|
1252
|
+
this.emit("runtimeMessage", message);
|
|
1253
|
+
}
|
|
1151
1254
|
});
|
|
1152
1255
|
this.machineClient.on("statusChanged", (status) => {
|
|
1153
1256
|
switch (status) {
|
|
@@ -1174,6 +1277,9 @@ var Reactor = class {
|
|
|
1174
1277
|
this.emit("streamChanged", track, stream);
|
|
1175
1278
|
}
|
|
1176
1279
|
);
|
|
1280
|
+
this.machineClient.on("statsUpdate", (stats) => {
|
|
1281
|
+
this.emit("statsUpdate", stats);
|
|
1282
|
+
});
|
|
1177
1283
|
}
|
|
1178
1284
|
/**
|
|
1179
1285
|
* Disconnects from the coordinator and the gpu machine.
|
|
@@ -1260,6 +1366,10 @@ var Reactor = class {
|
|
|
1260
1366
|
getLastError() {
|
|
1261
1367
|
return this.lastError;
|
|
1262
1368
|
}
|
|
1369
|
+
getStats() {
|
|
1370
|
+
var _a;
|
|
1371
|
+
return (_a = this.machineClient) == null ? void 0 : _a.getStats();
|
|
1372
|
+
}
|
|
1263
1373
|
/**
|
|
1264
1374
|
* Create and store an error
|
|
1265
1375
|
*/
|
|
@@ -1348,10 +1458,10 @@ var createReactorStore = (initProps, publicState = defaultInitState) => {
|
|
|
1348
1458
|
// actions
|
|
1349
1459
|
onMessage: (handler) => {
|
|
1350
1460
|
console.debug("[ReactorStore] Registering message handler");
|
|
1351
|
-
get().internal.reactor.on("
|
|
1461
|
+
get().internal.reactor.on("message", handler);
|
|
1352
1462
|
return () => {
|
|
1353
1463
|
console.debug("[ReactorStore] Cleaning up message handler");
|
|
1354
|
-
get().internal.reactor.off("
|
|
1464
|
+
get().internal.reactor.off("message", handler);
|
|
1355
1465
|
};
|
|
1356
1466
|
},
|
|
1357
1467
|
sendCommand: (command, data, scope) => __async(null, null, function* () {
|
|
@@ -1368,13 +1478,13 @@ var createReactorStore = (initProps, publicState = defaultInitState) => {
|
|
|
1368
1478
|
throw error;
|
|
1369
1479
|
}
|
|
1370
1480
|
}),
|
|
1371
|
-
connect: (jwtToken) => __async(null, null, function* () {
|
|
1481
|
+
connect: (jwtToken, options) => __async(null, null, function* () {
|
|
1372
1482
|
if (jwtToken === void 0) {
|
|
1373
1483
|
jwtToken = get().jwtToken;
|
|
1374
1484
|
}
|
|
1375
1485
|
console.debug("[ReactorStore] Connect called.");
|
|
1376
1486
|
try {
|
|
1377
|
-
yield get().internal.reactor.connect(jwtToken);
|
|
1487
|
+
yield get().internal.reactor.connect(jwtToken, options);
|
|
1378
1488
|
console.debug("[ReactorStore] Connect completed successfully");
|
|
1379
1489
|
} catch (error) {
|
|
1380
1490
|
console.error("[ReactorStore] Connect failed:", error);
|
|
@@ -1419,10 +1529,10 @@ var createReactorStore = (initProps, publicState = defaultInitState) => {
|
|
|
1419
1529
|
throw error;
|
|
1420
1530
|
}
|
|
1421
1531
|
}),
|
|
1422
|
-
reconnect: () => __async(null, null, function* () {
|
|
1532
|
+
reconnect: (options) => __async(null, null, function* () {
|
|
1423
1533
|
console.debug("[ReactorStore] Reconnecting");
|
|
1424
1534
|
try {
|
|
1425
|
-
yield get().internal.reactor.reconnect();
|
|
1535
|
+
yield get().internal.reactor.reconnect(options);
|
|
1426
1536
|
console.debug("[ReactorStore] Reconnect completed successfully");
|
|
1427
1537
|
} catch (error) {
|
|
1428
1538
|
console.error("[ReactorStore] Failed to reconnect:", error);
|
|
@@ -1439,11 +1549,11 @@ var import_jsx_runtime = require("react/jsx-runtime");
|
|
|
1439
1549
|
function ReactorProvider(_a) {
|
|
1440
1550
|
var _b = _a, {
|
|
1441
1551
|
children,
|
|
1442
|
-
|
|
1552
|
+
connectOptions,
|
|
1443
1553
|
jwtToken
|
|
1444
1554
|
} = _b, props = __objRest(_b, [
|
|
1445
1555
|
"children",
|
|
1446
|
-
"
|
|
1556
|
+
"connectOptions",
|
|
1447
1557
|
"jwtToken"
|
|
1448
1558
|
]);
|
|
1449
1559
|
const storeRef = (0, import_react3.useRef)(void 0);
|
|
@@ -1458,14 +1568,16 @@ function ReactorProvider(_a) {
|
|
|
1458
1568
|
);
|
|
1459
1569
|
console.debug("[ReactorProvider] Reactor store created successfully");
|
|
1460
1570
|
}
|
|
1571
|
+
const _a2 = connectOptions != null ? connectOptions : {}, { autoConnect = false } = _a2, pollingOptions = __objRest(_a2, ["autoConnect"]);
|
|
1461
1572
|
const { coordinatorUrl, modelName, local } = props;
|
|
1573
|
+
const maxAttempts = pollingOptions.maxAttempts;
|
|
1462
1574
|
(0, import_react3.useEffect)(() => {
|
|
1463
1575
|
const handleBeforeUnload = () => {
|
|
1464
|
-
var
|
|
1576
|
+
var _a3;
|
|
1465
1577
|
console.debug(
|
|
1466
1578
|
"[ReactorProvider] Page unloading, performing non-recoverable disconnect"
|
|
1467
1579
|
);
|
|
1468
|
-
(
|
|
1580
|
+
(_a3 = storeRef.current) == null ? void 0 : _a3.getState().internal.reactor.disconnect(false);
|
|
1469
1581
|
};
|
|
1470
1582
|
window.addEventListener("beforeunload", handleBeforeUnload);
|
|
1471
1583
|
return () => {
|
|
@@ -1480,7 +1592,7 @@ function ReactorProvider(_a) {
|
|
|
1480
1592
|
console.debug(
|
|
1481
1593
|
"[ReactorProvider] Starting autoconnect in first render..."
|
|
1482
1594
|
);
|
|
1483
|
-
current2.getState().connect(jwtToken).then(() => {
|
|
1595
|
+
current2.getState().connect(jwtToken, pollingOptions).then(() => {
|
|
1484
1596
|
console.debug(
|
|
1485
1597
|
"[ReactorProvider] Autoconnect successful in first render"
|
|
1486
1598
|
);
|
|
@@ -1523,7 +1635,7 @@ function ReactorProvider(_a) {
|
|
|
1523
1635
|
);
|
|
1524
1636
|
if (autoConnect && current.getState().status === "disconnected" && jwtToken) {
|
|
1525
1637
|
console.debug("[ReactorProvider] Starting autoconnect...");
|
|
1526
|
-
current.getState().connect(jwtToken).then(() => {
|
|
1638
|
+
current.getState().connect(jwtToken, pollingOptions).then(() => {
|
|
1527
1639
|
console.debug("[ReactorProvider] Autoconnect successful");
|
|
1528
1640
|
}).catch((error) => {
|
|
1529
1641
|
console.error("[ReactorProvider] Failed to autoconnect:", error);
|
|
@@ -1539,7 +1651,7 @@ function ReactorProvider(_a) {
|
|
|
1539
1651
|
console.error("[ReactorProvider] Failed to disconnect:", error);
|
|
1540
1652
|
});
|
|
1541
1653
|
};
|
|
1542
|
-
}, [coordinatorUrl, modelName, autoConnect, local, jwtToken]);
|
|
1654
|
+
}, [coordinatorUrl, modelName, autoConnect, local, jwtToken, maxAttempts]);
|
|
1543
1655
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ReactorContext.Provider, { value: storeRef.current, children });
|
|
1544
1656
|
}
|
|
1545
1657
|
function useReactorStore(selector) {
|
|
@@ -1563,21 +1675,45 @@ function useReactorMessage(handler) {
|
|
|
1563
1675
|
handlerRef.current = handler;
|
|
1564
1676
|
}, [handler]);
|
|
1565
1677
|
(0, import_react4.useEffect)(() => {
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1678
|
+
const stableHandler = (message) => {
|
|
1679
|
+
handlerRef.current(message);
|
|
1680
|
+
};
|
|
1681
|
+
reactor.on("message", stableHandler);
|
|
1682
|
+
return () => {
|
|
1683
|
+
reactor.off("message", stableHandler);
|
|
1684
|
+
};
|
|
1685
|
+
}, [reactor]);
|
|
1686
|
+
}
|
|
1687
|
+
function useReactorInternalMessage(handler) {
|
|
1688
|
+
const reactor = useReactor((state) => state.internal.reactor);
|
|
1689
|
+
const handlerRef = (0, import_react4.useRef)(handler);
|
|
1690
|
+
(0, import_react4.useEffect)(() => {
|
|
1691
|
+
handlerRef.current = handler;
|
|
1692
|
+
}, [handler]);
|
|
1693
|
+
(0, import_react4.useEffect)(() => {
|
|
1694
|
+
const stableHandler = (message) => {
|
|
1695
|
+
handlerRef.current(message);
|
|
1696
|
+
};
|
|
1697
|
+
reactor.on("runtimeMessage", stableHandler);
|
|
1698
|
+
return () => {
|
|
1699
|
+
reactor.off("runtimeMessage", stableHandler);
|
|
1700
|
+
};
|
|
1701
|
+
}, [reactor]);
|
|
1702
|
+
}
|
|
1703
|
+
function useStats() {
|
|
1704
|
+
const reactor = useReactor((state) => state.internal.reactor);
|
|
1705
|
+
const [stats, setStats] = (0, import_react4.useState)(void 0);
|
|
1706
|
+
(0, import_react4.useEffect)(() => {
|
|
1707
|
+
const handler = (newStats) => {
|
|
1708
|
+
setStats(newStats);
|
|
1573
1709
|
};
|
|
1574
|
-
reactor.on("
|
|
1575
|
-
console.debug("[useReactorMessage] Message handler registered");
|
|
1710
|
+
reactor.on("statsUpdate", handler);
|
|
1576
1711
|
return () => {
|
|
1577
|
-
|
|
1578
|
-
|
|
1712
|
+
reactor.off("statsUpdate", handler);
|
|
1713
|
+
setStats(void 0);
|
|
1579
1714
|
};
|
|
1580
1715
|
}, [reactor]);
|
|
1716
|
+
return stats;
|
|
1581
1717
|
}
|
|
1582
1718
|
|
|
1583
1719
|
// src/react/ReactorView.tsx
|
|
@@ -1715,8 +1851,8 @@ function ReactorController({
|
|
|
1715
1851
|
}, 5e3);
|
|
1716
1852
|
return () => clearInterval(interval);
|
|
1717
1853
|
}, [status, commands, requestCapabilities]);
|
|
1718
|
-
|
|
1719
|
-
if (
|
|
1854
|
+
useReactorInternalMessage((message) => {
|
|
1855
|
+
if (message && typeof message === "object" && message.type === "modelCapabilities" && message.data && "commands" in message.data) {
|
|
1720
1856
|
const commandsMessage = message.data;
|
|
1721
1857
|
setCommands(commandsMessage.commands);
|
|
1722
1858
|
const initialValues = {};
|
|
@@ -2349,7 +2485,9 @@ function fetchInsecureJwtToken(_0) {
|
|
|
2349
2485
|
WebcamStream,
|
|
2350
2486
|
fetchInsecureJwtToken,
|
|
2351
2487
|
useReactor,
|
|
2488
|
+
useReactorInternalMessage,
|
|
2352
2489
|
useReactorMessage,
|
|
2353
|
-
useReactorStore
|
|
2490
|
+
useReactorStore,
|
|
2491
|
+
useStats
|
|
2354
2492
|
});
|
|
2355
2493
|
//# sourceMappingURL=index.js.map
|