@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.mjs
CHANGED
|
@@ -206,11 +206,58 @@ function parseMessage(data) {
|
|
|
206
206
|
function closePeerConnection(pc) {
|
|
207
207
|
pc.close();
|
|
208
208
|
}
|
|
209
|
+
function extractConnectionStats(report) {
|
|
210
|
+
let rtt;
|
|
211
|
+
let availableOutgoingBitrate;
|
|
212
|
+
let localCandidateId;
|
|
213
|
+
let framesPerSecond;
|
|
214
|
+
let jitter;
|
|
215
|
+
let packetLossRatio;
|
|
216
|
+
report.forEach((stat) => {
|
|
217
|
+
if (stat.type === "candidate-pair" && stat.state === "succeeded") {
|
|
218
|
+
if (stat.currentRoundTripTime !== void 0) {
|
|
219
|
+
rtt = stat.currentRoundTripTime * 1e3;
|
|
220
|
+
}
|
|
221
|
+
if (stat.availableOutgoingBitrate !== void 0) {
|
|
222
|
+
availableOutgoingBitrate = stat.availableOutgoingBitrate;
|
|
223
|
+
}
|
|
224
|
+
localCandidateId = stat.localCandidateId;
|
|
225
|
+
}
|
|
226
|
+
if (stat.type === "inbound-rtp" && stat.kind === "video") {
|
|
227
|
+
if (stat.framesPerSecond !== void 0) {
|
|
228
|
+
framesPerSecond = stat.framesPerSecond;
|
|
229
|
+
}
|
|
230
|
+
if (stat.jitter !== void 0) {
|
|
231
|
+
jitter = stat.jitter;
|
|
232
|
+
}
|
|
233
|
+
if (stat.packetsReceived !== void 0 && stat.packetsLost !== void 0 && stat.packetsReceived + stat.packetsLost > 0) {
|
|
234
|
+
packetLossRatio = stat.packetsLost / (stat.packetsReceived + stat.packetsLost);
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
});
|
|
238
|
+
let candidateType;
|
|
239
|
+
if (localCandidateId) {
|
|
240
|
+
const localCandidate = report.get(localCandidateId);
|
|
241
|
+
if (localCandidate == null ? void 0 : localCandidate.candidateType) {
|
|
242
|
+
candidateType = localCandidate.candidateType;
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
return {
|
|
246
|
+
rtt,
|
|
247
|
+
candidateType,
|
|
248
|
+
availableOutgoingBitrate,
|
|
249
|
+
framesPerSecond,
|
|
250
|
+
packetLossRatio,
|
|
251
|
+
jitter,
|
|
252
|
+
timestamp: Date.now()
|
|
253
|
+
};
|
|
254
|
+
}
|
|
209
255
|
|
|
210
256
|
// src/core/CoordinatorClient.ts
|
|
211
257
|
var INITIAL_BACKOFF_MS = 500;
|
|
212
|
-
var MAX_BACKOFF_MS =
|
|
258
|
+
var MAX_BACKOFF_MS = 15e3;
|
|
213
259
|
var BACKOFF_MULTIPLIER = 2;
|
|
260
|
+
var DEFAULT_MAX_ATTEMPTS = 6;
|
|
214
261
|
var CoordinatorClient = class {
|
|
215
262
|
constructor(options) {
|
|
216
263
|
this.baseUrl = options.baseUrl;
|
|
@@ -403,13 +450,14 @@ var CoordinatorClient = class {
|
|
|
403
450
|
});
|
|
404
451
|
}
|
|
405
452
|
/**
|
|
406
|
-
* Polls for the SDP answer with
|
|
453
|
+
* Polls for the SDP answer with exponential backoff.
|
|
407
454
|
* Used for async reconnection when the answer is not immediately available.
|
|
408
455
|
* @param sessionId - The session ID to poll for
|
|
456
|
+
* @param maxAttempts - Optional maximum number of polling attempts before giving up
|
|
409
457
|
* @returns The SDP answer from the server
|
|
410
458
|
*/
|
|
411
|
-
pollSdpAnswer(
|
|
412
|
-
return __async(this,
|
|
459
|
+
pollSdpAnswer(_0) {
|
|
460
|
+
return __async(this, arguments, function* (sessionId, maxAttempts = DEFAULT_MAX_ATTEMPTS) {
|
|
413
461
|
console.debug(
|
|
414
462
|
"[CoordinatorClient] Polling for SDP answer for session:",
|
|
415
463
|
sessionId
|
|
@@ -417,9 +465,14 @@ var CoordinatorClient = class {
|
|
|
417
465
|
let backoffMs = INITIAL_BACKOFF_MS;
|
|
418
466
|
let attempt = 0;
|
|
419
467
|
while (true) {
|
|
468
|
+
if (attempt >= maxAttempts) {
|
|
469
|
+
throw new Error(
|
|
470
|
+
`SDP polling exceeded maximum attempts (${maxAttempts}) for session ${sessionId}`
|
|
471
|
+
);
|
|
472
|
+
}
|
|
420
473
|
attempt++;
|
|
421
474
|
console.debug(
|
|
422
|
-
`[CoordinatorClient] SDP poll attempt ${attempt} for session ${sessionId}`
|
|
475
|
+
`[CoordinatorClient] SDP poll attempt ${attempt}/${maxAttempts} for session ${sessionId}`
|
|
423
476
|
);
|
|
424
477
|
const response = yield fetch(
|
|
425
478
|
`${this.baseUrl}/sessions/${sessionId}/sdp_params`,
|
|
@@ -456,9 +509,10 @@ var CoordinatorClient = class {
|
|
|
456
509
|
* falls back to polling. If no sdpOffer is provided, goes directly to polling.
|
|
457
510
|
* @param sessionId - The session ID to connect to
|
|
458
511
|
* @param sdpOffer - Optional SDP offer from the local WebRTC peer connection
|
|
512
|
+
* @param maxAttempts - Optional maximum number of polling attempts before giving up
|
|
459
513
|
* @returns The SDP answer from the server
|
|
460
514
|
*/
|
|
461
|
-
connect(sessionId, sdpOffer) {
|
|
515
|
+
connect(sessionId, sdpOffer, maxAttempts) {
|
|
462
516
|
return __async(this, null, function* () {
|
|
463
517
|
console.debug("[CoordinatorClient] Connecting to session:", sessionId);
|
|
464
518
|
if (sdpOffer) {
|
|
@@ -467,7 +521,7 @@ var CoordinatorClient = class {
|
|
|
467
521
|
return answer;
|
|
468
522
|
}
|
|
469
523
|
}
|
|
470
|
-
return this.pollSdpAnswer(sessionId);
|
|
524
|
+
return this.pollSdpAnswer(sessionId, maxAttempts);
|
|
471
525
|
});
|
|
472
526
|
}
|
|
473
527
|
/**
|
|
@@ -573,6 +627,7 @@ var LocalCoordinatorClient = class extends CoordinatorClient {
|
|
|
573
627
|
|
|
574
628
|
// src/core/GPUMachineClient.ts
|
|
575
629
|
var PING_INTERVAL_MS = 5e3;
|
|
630
|
+
var STATS_INTERVAL_MS = 2e3;
|
|
576
631
|
var GPUMachineClient = class {
|
|
577
632
|
constructor(config) {
|
|
578
633
|
this.eventListeners = /* @__PURE__ */ new Map();
|
|
@@ -656,6 +711,7 @@ var GPUMachineClient = class {
|
|
|
656
711
|
disconnect() {
|
|
657
712
|
return __async(this, null, function* () {
|
|
658
713
|
this.stopPing();
|
|
714
|
+
this.stopStatsPolling();
|
|
659
715
|
if (this.publishedTrack) {
|
|
660
716
|
yield this.unpublishTrack();
|
|
661
717
|
}
|
|
@@ -812,6 +868,31 @@ var GPUMachineClient = class {
|
|
|
812
868
|
}
|
|
813
869
|
}
|
|
814
870
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
871
|
+
// Stats Polling (RTT)
|
|
872
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
873
|
+
getStats() {
|
|
874
|
+
return this.stats;
|
|
875
|
+
}
|
|
876
|
+
startStatsPolling() {
|
|
877
|
+
this.stopStatsPolling();
|
|
878
|
+
this.statsInterval = setInterval(() => __async(this, null, function* () {
|
|
879
|
+
if (!this.peerConnection) return;
|
|
880
|
+
try {
|
|
881
|
+
const report = yield this.peerConnection.getStats();
|
|
882
|
+
this.stats = extractConnectionStats(report);
|
|
883
|
+
this.emit("statsUpdate", this.stats);
|
|
884
|
+
} catch (e) {
|
|
885
|
+
}
|
|
886
|
+
}), STATS_INTERVAL_MS);
|
|
887
|
+
}
|
|
888
|
+
stopStatsPolling() {
|
|
889
|
+
if (this.statsInterval !== void 0) {
|
|
890
|
+
clearInterval(this.statsInterval);
|
|
891
|
+
this.statsInterval = void 0;
|
|
892
|
+
}
|
|
893
|
+
this.stats = void 0;
|
|
894
|
+
}
|
|
895
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
815
896
|
// Private Helpers
|
|
816
897
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
817
898
|
setStatus(newStatus) {
|
|
@@ -830,6 +911,7 @@ var GPUMachineClient = class {
|
|
|
830
911
|
switch (state) {
|
|
831
912
|
case "connected":
|
|
832
913
|
this.setStatus("connected");
|
|
914
|
+
this.startStatsPolling();
|
|
833
915
|
break;
|
|
834
916
|
case "disconnected":
|
|
835
917
|
case "closed":
|
|
@@ -1011,8 +1093,9 @@ var Reactor = class {
|
|
|
1011
1093
|
}
|
|
1012
1094
|
/**
|
|
1013
1095
|
* Public method for reconnecting to an existing session, that may have been interrupted but can be recovered.
|
|
1096
|
+
* @param options Optional connect options (e.g. maxAttempts for SDP polling)
|
|
1014
1097
|
*/
|
|
1015
|
-
reconnect() {
|
|
1098
|
+
reconnect(options) {
|
|
1016
1099
|
return __async(this, null, function* () {
|
|
1017
1100
|
if (!this.sessionId || !this.coordinatorClient) {
|
|
1018
1101
|
console.warn("[Reactor] No active session to reconnect to.");
|
|
@@ -1032,7 +1115,8 @@ var Reactor = class {
|
|
|
1032
1115
|
try {
|
|
1033
1116
|
const sdpAnswer = yield this.coordinatorClient.connect(
|
|
1034
1117
|
this.sessionId,
|
|
1035
|
-
sdpOffer
|
|
1118
|
+
sdpOffer,
|
|
1119
|
+
options == null ? void 0 : options.maxAttempts
|
|
1036
1120
|
);
|
|
1037
1121
|
yield this.machineClient.connect(sdpAnswer);
|
|
1038
1122
|
this.setStatus("ready");
|
|
@@ -1056,8 +1140,10 @@ var Reactor = class {
|
|
|
1056
1140
|
* Connects to the coordinator and waits for a GPU to be assigned.
|
|
1057
1141
|
* Once a GPU is assigned, the Reactor will connect to the gpu machine via WebRTC.
|
|
1058
1142
|
* If no authentication is provided and not in local mode, an error is thrown.
|
|
1143
|
+
* @param jwtToken Optional JWT token for authentication
|
|
1144
|
+
* @param options Optional connect options (e.g. maxAttempts for SDP polling)
|
|
1059
1145
|
*/
|
|
1060
|
-
connect(jwtToken) {
|
|
1146
|
+
connect(jwtToken, options) {
|
|
1061
1147
|
return __async(this, null, function* () {
|
|
1062
1148
|
console.debug("[Reactor] Connecting, status:", this.status);
|
|
1063
1149
|
if (jwtToken == void 0 && !this.local) {
|
|
@@ -1074,7 +1160,7 @@ var Reactor = class {
|
|
|
1074
1160
|
this.coordinatorClient = this.local ? new LocalCoordinatorClient(this.coordinatorUrl) : new CoordinatorClient({
|
|
1075
1161
|
baseUrl: this.coordinatorUrl,
|
|
1076
1162
|
jwtToken,
|
|
1077
|
-
// Safe: validated
|
|
1163
|
+
// Safe: validated above
|
|
1078
1164
|
model: this.model
|
|
1079
1165
|
});
|
|
1080
1166
|
const iceServers = yield this.coordinatorClient.getIceServers();
|
|
@@ -1083,7 +1169,11 @@ var Reactor = class {
|
|
|
1083
1169
|
const sdpOffer = yield this.machineClient.createOffer();
|
|
1084
1170
|
const sessionId = yield this.coordinatorClient.createSession(sdpOffer);
|
|
1085
1171
|
this.setSessionId(sessionId);
|
|
1086
|
-
const sdpAnswer = yield this.coordinatorClient.connect(
|
|
1172
|
+
const sdpAnswer = yield this.coordinatorClient.connect(
|
|
1173
|
+
sessionId,
|
|
1174
|
+
void 0,
|
|
1175
|
+
options == null ? void 0 : options.maxAttempts
|
|
1176
|
+
);
|
|
1087
1177
|
yield this.machineClient.connect(sdpAnswer);
|
|
1088
1178
|
} catch (error) {
|
|
1089
1179
|
console.error("[Reactor] Connection failed:", error);
|
|
@@ -1093,7 +1183,14 @@ var Reactor = class {
|
|
|
1093
1183
|
"coordinator",
|
|
1094
1184
|
true
|
|
1095
1185
|
);
|
|
1096
|
-
|
|
1186
|
+
try {
|
|
1187
|
+
yield this.disconnect(false);
|
|
1188
|
+
} catch (disconnectError) {
|
|
1189
|
+
console.error(
|
|
1190
|
+
"[Reactor] Failed to clean up after connection failure:",
|
|
1191
|
+
disconnectError
|
|
1192
|
+
);
|
|
1193
|
+
}
|
|
1097
1194
|
throw error;
|
|
1098
1195
|
}
|
|
1099
1196
|
});
|
|
@@ -1104,7 +1201,11 @@ var Reactor = class {
|
|
|
1104
1201
|
setupMachineClientHandlers() {
|
|
1105
1202
|
if (!this.machineClient) return;
|
|
1106
1203
|
this.machineClient.on("message", (message, scope) => {
|
|
1107
|
-
|
|
1204
|
+
if (scope === "application") {
|
|
1205
|
+
this.emit("message", message);
|
|
1206
|
+
} else if (scope === "runtime") {
|
|
1207
|
+
this.emit("runtimeMessage", message);
|
|
1208
|
+
}
|
|
1108
1209
|
});
|
|
1109
1210
|
this.machineClient.on("statusChanged", (status) => {
|
|
1110
1211
|
switch (status) {
|
|
@@ -1131,6 +1232,9 @@ var Reactor = class {
|
|
|
1131
1232
|
this.emit("streamChanged", track, stream);
|
|
1132
1233
|
}
|
|
1133
1234
|
);
|
|
1235
|
+
this.machineClient.on("statsUpdate", (stats) => {
|
|
1236
|
+
this.emit("statsUpdate", stats);
|
|
1237
|
+
});
|
|
1134
1238
|
}
|
|
1135
1239
|
/**
|
|
1136
1240
|
* Disconnects from the coordinator and the gpu machine.
|
|
@@ -1217,6 +1321,10 @@ var Reactor = class {
|
|
|
1217
1321
|
getLastError() {
|
|
1218
1322
|
return this.lastError;
|
|
1219
1323
|
}
|
|
1324
|
+
getStats() {
|
|
1325
|
+
var _a;
|
|
1326
|
+
return (_a = this.machineClient) == null ? void 0 : _a.getStats();
|
|
1327
|
+
}
|
|
1220
1328
|
/**
|
|
1221
1329
|
* Create and store an error
|
|
1222
1330
|
*/
|
|
@@ -1305,10 +1413,10 @@ var createReactorStore = (initProps, publicState = defaultInitState) => {
|
|
|
1305
1413
|
// actions
|
|
1306
1414
|
onMessage: (handler) => {
|
|
1307
1415
|
console.debug("[ReactorStore] Registering message handler");
|
|
1308
|
-
get().internal.reactor.on("
|
|
1416
|
+
get().internal.reactor.on("message", handler);
|
|
1309
1417
|
return () => {
|
|
1310
1418
|
console.debug("[ReactorStore] Cleaning up message handler");
|
|
1311
|
-
get().internal.reactor.off("
|
|
1419
|
+
get().internal.reactor.off("message", handler);
|
|
1312
1420
|
};
|
|
1313
1421
|
},
|
|
1314
1422
|
sendCommand: (command, data, scope) => __async(null, null, function* () {
|
|
@@ -1325,13 +1433,13 @@ var createReactorStore = (initProps, publicState = defaultInitState) => {
|
|
|
1325
1433
|
throw error;
|
|
1326
1434
|
}
|
|
1327
1435
|
}),
|
|
1328
|
-
connect: (jwtToken) => __async(null, null, function* () {
|
|
1436
|
+
connect: (jwtToken, options) => __async(null, null, function* () {
|
|
1329
1437
|
if (jwtToken === void 0) {
|
|
1330
1438
|
jwtToken = get().jwtToken;
|
|
1331
1439
|
}
|
|
1332
1440
|
console.debug("[ReactorStore] Connect called.");
|
|
1333
1441
|
try {
|
|
1334
|
-
yield get().internal.reactor.connect(jwtToken);
|
|
1442
|
+
yield get().internal.reactor.connect(jwtToken, options);
|
|
1335
1443
|
console.debug("[ReactorStore] Connect completed successfully");
|
|
1336
1444
|
} catch (error) {
|
|
1337
1445
|
console.error("[ReactorStore] Connect failed:", error);
|
|
@@ -1376,10 +1484,10 @@ var createReactorStore = (initProps, publicState = defaultInitState) => {
|
|
|
1376
1484
|
throw error;
|
|
1377
1485
|
}
|
|
1378
1486
|
}),
|
|
1379
|
-
reconnect: () => __async(null, null, function* () {
|
|
1487
|
+
reconnect: (options) => __async(null, null, function* () {
|
|
1380
1488
|
console.debug("[ReactorStore] Reconnecting");
|
|
1381
1489
|
try {
|
|
1382
|
-
yield get().internal.reactor.reconnect();
|
|
1490
|
+
yield get().internal.reactor.reconnect(options);
|
|
1383
1491
|
console.debug("[ReactorStore] Reconnect completed successfully");
|
|
1384
1492
|
} catch (error) {
|
|
1385
1493
|
console.error("[ReactorStore] Failed to reconnect:", error);
|
|
@@ -1396,11 +1504,11 @@ import { jsx } from "react/jsx-runtime";
|
|
|
1396
1504
|
function ReactorProvider(_a) {
|
|
1397
1505
|
var _b = _a, {
|
|
1398
1506
|
children,
|
|
1399
|
-
|
|
1507
|
+
connectOptions,
|
|
1400
1508
|
jwtToken
|
|
1401
1509
|
} = _b, props = __objRest(_b, [
|
|
1402
1510
|
"children",
|
|
1403
|
-
"
|
|
1511
|
+
"connectOptions",
|
|
1404
1512
|
"jwtToken"
|
|
1405
1513
|
]);
|
|
1406
1514
|
const storeRef = useRef(void 0);
|
|
@@ -1415,14 +1523,16 @@ function ReactorProvider(_a) {
|
|
|
1415
1523
|
);
|
|
1416
1524
|
console.debug("[ReactorProvider] Reactor store created successfully");
|
|
1417
1525
|
}
|
|
1526
|
+
const _a2 = connectOptions != null ? connectOptions : {}, { autoConnect = false } = _a2, pollingOptions = __objRest(_a2, ["autoConnect"]);
|
|
1418
1527
|
const { coordinatorUrl, modelName, local } = props;
|
|
1528
|
+
const maxAttempts = pollingOptions.maxAttempts;
|
|
1419
1529
|
useEffect(() => {
|
|
1420
1530
|
const handleBeforeUnload = () => {
|
|
1421
|
-
var
|
|
1531
|
+
var _a3;
|
|
1422
1532
|
console.debug(
|
|
1423
1533
|
"[ReactorProvider] Page unloading, performing non-recoverable disconnect"
|
|
1424
1534
|
);
|
|
1425
|
-
(
|
|
1535
|
+
(_a3 = storeRef.current) == null ? void 0 : _a3.getState().internal.reactor.disconnect(false);
|
|
1426
1536
|
};
|
|
1427
1537
|
window.addEventListener("beforeunload", handleBeforeUnload);
|
|
1428
1538
|
return () => {
|
|
@@ -1437,7 +1547,7 @@ function ReactorProvider(_a) {
|
|
|
1437
1547
|
console.debug(
|
|
1438
1548
|
"[ReactorProvider] Starting autoconnect in first render..."
|
|
1439
1549
|
);
|
|
1440
|
-
current2.getState().connect(jwtToken).then(() => {
|
|
1550
|
+
current2.getState().connect(jwtToken, pollingOptions).then(() => {
|
|
1441
1551
|
console.debug(
|
|
1442
1552
|
"[ReactorProvider] Autoconnect successful in first render"
|
|
1443
1553
|
);
|
|
@@ -1480,7 +1590,7 @@ function ReactorProvider(_a) {
|
|
|
1480
1590
|
);
|
|
1481
1591
|
if (autoConnect && current.getState().status === "disconnected" && jwtToken) {
|
|
1482
1592
|
console.debug("[ReactorProvider] Starting autoconnect...");
|
|
1483
|
-
current.getState().connect(jwtToken).then(() => {
|
|
1593
|
+
current.getState().connect(jwtToken, pollingOptions).then(() => {
|
|
1484
1594
|
console.debug("[ReactorProvider] Autoconnect successful");
|
|
1485
1595
|
}).catch((error) => {
|
|
1486
1596
|
console.error("[ReactorProvider] Failed to autoconnect:", error);
|
|
@@ -1496,7 +1606,7 @@ function ReactorProvider(_a) {
|
|
|
1496
1606
|
console.error("[ReactorProvider] Failed to disconnect:", error);
|
|
1497
1607
|
});
|
|
1498
1608
|
};
|
|
1499
|
-
}, [coordinatorUrl, modelName, autoConnect, local, jwtToken]);
|
|
1609
|
+
}, [coordinatorUrl, modelName, autoConnect, local, jwtToken, maxAttempts]);
|
|
1500
1610
|
return /* @__PURE__ */ jsx(ReactorContext.Provider, { value: storeRef.current, children });
|
|
1501
1611
|
}
|
|
1502
1612
|
function useReactorStore(selector) {
|
|
@@ -1509,7 +1619,7 @@ function useReactorStore(selector) {
|
|
|
1509
1619
|
|
|
1510
1620
|
// src/react/hooks.ts
|
|
1511
1621
|
import { useShallow } from "zustand/shallow";
|
|
1512
|
-
import { useEffect as useEffect2, useRef as useRef2 } from "react";
|
|
1622
|
+
import { useEffect as useEffect2, useRef as useRef2, useState as useState2 } from "react";
|
|
1513
1623
|
function useReactor(selector) {
|
|
1514
1624
|
return useReactorStore(useShallow(selector));
|
|
1515
1625
|
}
|
|
@@ -1520,21 +1630,45 @@ function useReactorMessage(handler) {
|
|
|
1520
1630
|
handlerRef.current = handler;
|
|
1521
1631
|
}, [handler]);
|
|
1522
1632
|
useEffect2(() => {
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1633
|
+
const stableHandler = (message) => {
|
|
1634
|
+
handlerRef.current(message);
|
|
1635
|
+
};
|
|
1636
|
+
reactor.on("message", stableHandler);
|
|
1637
|
+
return () => {
|
|
1638
|
+
reactor.off("message", stableHandler);
|
|
1639
|
+
};
|
|
1640
|
+
}, [reactor]);
|
|
1641
|
+
}
|
|
1642
|
+
function useReactorInternalMessage(handler) {
|
|
1643
|
+
const reactor = useReactor((state) => state.internal.reactor);
|
|
1644
|
+
const handlerRef = useRef2(handler);
|
|
1645
|
+
useEffect2(() => {
|
|
1646
|
+
handlerRef.current = handler;
|
|
1647
|
+
}, [handler]);
|
|
1648
|
+
useEffect2(() => {
|
|
1649
|
+
const stableHandler = (message) => {
|
|
1650
|
+
handlerRef.current(message);
|
|
1651
|
+
};
|
|
1652
|
+
reactor.on("runtimeMessage", stableHandler);
|
|
1653
|
+
return () => {
|
|
1654
|
+
reactor.off("runtimeMessage", stableHandler);
|
|
1655
|
+
};
|
|
1656
|
+
}, [reactor]);
|
|
1657
|
+
}
|
|
1658
|
+
function useStats() {
|
|
1659
|
+
const reactor = useReactor((state) => state.internal.reactor);
|
|
1660
|
+
const [stats, setStats] = useState2(void 0);
|
|
1661
|
+
useEffect2(() => {
|
|
1662
|
+
const handler = (newStats) => {
|
|
1663
|
+
setStats(newStats);
|
|
1530
1664
|
};
|
|
1531
|
-
reactor.on("
|
|
1532
|
-
console.debug("[useReactorMessage] Message handler registered");
|
|
1665
|
+
reactor.on("statsUpdate", handler);
|
|
1533
1666
|
return () => {
|
|
1534
|
-
|
|
1535
|
-
|
|
1667
|
+
reactor.off("statsUpdate", handler);
|
|
1668
|
+
setStats(void 0);
|
|
1536
1669
|
};
|
|
1537
1670
|
}, [reactor]);
|
|
1671
|
+
return stats;
|
|
1538
1672
|
}
|
|
1539
1673
|
|
|
1540
1674
|
// src/react/ReactorView.tsx
|
|
@@ -1633,7 +1767,7 @@ function ReactorView({
|
|
|
1633
1767
|
}
|
|
1634
1768
|
|
|
1635
1769
|
// src/react/ReactorController.tsx
|
|
1636
|
-
import React, { useState as
|
|
1770
|
+
import React, { useState as useState3, useCallback } from "react";
|
|
1637
1771
|
import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
1638
1772
|
function ReactorController({
|
|
1639
1773
|
className,
|
|
@@ -1643,9 +1777,9 @@ function ReactorController({
|
|
|
1643
1777
|
sendCommand: state.sendCommand,
|
|
1644
1778
|
status: state.status
|
|
1645
1779
|
}));
|
|
1646
|
-
const [commands, setCommands] =
|
|
1647
|
-
const [formValues, setFormValues] =
|
|
1648
|
-
const [expandedCommands, setExpandedCommands] =
|
|
1780
|
+
const [commands, setCommands] = useState3({});
|
|
1781
|
+
const [formValues, setFormValues] = useState3({});
|
|
1782
|
+
const [expandedCommands, setExpandedCommands] = useState3({});
|
|
1649
1783
|
React.useEffect(() => {
|
|
1650
1784
|
if (status === "disconnected") {
|
|
1651
1785
|
setCommands({});
|
|
@@ -1672,8 +1806,8 @@ function ReactorController({
|
|
|
1672
1806
|
}, 5e3);
|
|
1673
1807
|
return () => clearInterval(interval);
|
|
1674
1808
|
}, [status, commands, requestCapabilities]);
|
|
1675
|
-
|
|
1676
|
-
if (
|
|
1809
|
+
useReactorInternalMessage((message) => {
|
|
1810
|
+
if (message && typeof message === "object" && message.type === "modelCapabilities" && message.data && "commands" in message.data) {
|
|
1677
1811
|
const commandsMessage = message.data;
|
|
1678
1812
|
setCommands(commandsMessage.commands);
|
|
1679
1813
|
const initialValues = {};
|
|
@@ -2086,7 +2220,7 @@ function ReactorController({
|
|
|
2086
2220
|
}
|
|
2087
2221
|
|
|
2088
2222
|
// src/react/WebcamStream.tsx
|
|
2089
|
-
import { useEffect as useEffect4, useRef as useRef4, useState as
|
|
2223
|
+
import { useEffect as useEffect4, useRef as useRef4, useState as useState4 } from "react";
|
|
2090
2224
|
import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
2091
2225
|
function WebcamStream({
|
|
2092
2226
|
className,
|
|
@@ -2098,9 +2232,9 @@ function WebcamStream({
|
|
|
2098
2232
|
showWebcam = true,
|
|
2099
2233
|
videoObjectFit = "contain"
|
|
2100
2234
|
}) {
|
|
2101
|
-
const [stream, setStream] =
|
|
2102
|
-
const [isPublishing, setIsPublishing] =
|
|
2103
|
-
const [permissionDenied, setPermissionDenied] =
|
|
2235
|
+
const [stream, setStream] = useState4(null);
|
|
2236
|
+
const [isPublishing, setIsPublishing] = useState4(false);
|
|
2237
|
+
const [permissionDenied, setPermissionDenied] = useState4(false);
|
|
2104
2238
|
const { status, publishVideoStream, unpublishVideoStream, reactor } = useReactor((state) => ({
|
|
2105
2239
|
status: state.status,
|
|
2106
2240
|
publishVideoStream: state.publishVideoStream,
|
|
@@ -2305,7 +2439,9 @@ export {
|
|
|
2305
2439
|
WebcamStream,
|
|
2306
2440
|
fetchInsecureJwtToken,
|
|
2307
2441
|
useReactor,
|
|
2442
|
+
useReactorInternalMessage,
|
|
2308
2443
|
useReactorMessage,
|
|
2309
|
-
useReactorStore
|
|
2444
|
+
useReactorStore,
|
|
2445
|
+
useStats
|
|
2310
2446
|
};
|
|
2311
2447
|
//# sourceMappingURL=index.mjs.map
|