@reactor-team/js-sdk 2.5.0 → 2.5.1
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 +11 -1
- package/dist/index.d.ts +11 -1
- package/dist/index.js +172 -34
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +170 -34
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.d.mts
CHANGED
|
@@ -95,6 +95,11 @@ interface ReactorError {
|
|
|
95
95
|
declare class ConflictError extends Error {
|
|
96
96
|
constructor(message: string);
|
|
97
97
|
}
|
|
98
|
+
declare class AbortError extends Error {
|
|
99
|
+
constructor(message: string);
|
|
100
|
+
}
|
|
101
|
+
/** Matches both our custom AbortError and the native DOMException thrown by fetch(). */
|
|
102
|
+
declare function isAbortError(error: unknown): boolean;
|
|
98
103
|
interface ReactorState$1 {
|
|
99
104
|
status: ReactorStatus;
|
|
100
105
|
lastError?: ReactorError;
|
|
@@ -200,6 +205,11 @@ declare class Reactor {
|
|
|
200
205
|
connect(jwtToken?: string, options?: ConnectOptions): Promise<void>;
|
|
201
206
|
/**
|
|
202
207
|
* Sets up event handlers for the machine client.
|
|
208
|
+
*
|
|
209
|
+
* Each handler captures the client reference at registration time and
|
|
210
|
+
* ignores events if this.machineClient has since changed (e.g. after
|
|
211
|
+
* disconnect + reconnect), preventing stale WebRTC teardown events from
|
|
212
|
+
* interfering with a new connection.
|
|
203
213
|
*/
|
|
204
214
|
private setupMachineClientHandlers;
|
|
205
215
|
/**
|
|
@@ -367,4 +377,4 @@ declare function useStats(): ConnectionStats | undefined;
|
|
|
367
377
|
*/
|
|
368
378
|
declare function fetchInsecureJwtToken(apiKey: string, coordinatorUrl?: string): Promise<string>;
|
|
369
379
|
|
|
370
|
-
export { type AudioTrackOptions, ConflictError, type ConnectOptions, type ConnectionStats, type MessageScope, type Options, PROD_COORDINATOR_URL, Reactor, type ReactorConnectOptions, ReactorController, type ReactorControllerProps, type ReactorError, type ReactorEvent, ReactorProvider, type ReactorState$1 as ReactorState, type ReactorStatus, ReactorView, type ReactorViewProps, type TrackConfig, type VideoTrackOptions, WebcamStream, type WebcamStreamProps, audio, fetchInsecureJwtToken, useReactor, useReactorInternalMessage, useReactorMessage, useReactorStore, useStats, video };
|
|
380
|
+
export { AbortError, type AudioTrackOptions, ConflictError, type ConnectOptions, type ConnectionStats, type MessageScope, type Options, PROD_COORDINATOR_URL, Reactor, type ReactorConnectOptions, ReactorController, type ReactorControllerProps, type ReactorError, type ReactorEvent, ReactorProvider, type ReactorState$1 as ReactorState, type ReactorStatus, ReactorView, type ReactorViewProps, type TrackConfig, type VideoTrackOptions, WebcamStream, type WebcamStreamProps, audio, fetchInsecureJwtToken, isAbortError, useReactor, useReactorInternalMessage, useReactorMessage, useReactorStore, useStats, video };
|
package/dist/index.d.ts
CHANGED
|
@@ -95,6 +95,11 @@ interface ReactorError {
|
|
|
95
95
|
declare class ConflictError extends Error {
|
|
96
96
|
constructor(message: string);
|
|
97
97
|
}
|
|
98
|
+
declare class AbortError extends Error {
|
|
99
|
+
constructor(message: string);
|
|
100
|
+
}
|
|
101
|
+
/** Matches both our custom AbortError and the native DOMException thrown by fetch(). */
|
|
102
|
+
declare function isAbortError(error: unknown): boolean;
|
|
98
103
|
interface ReactorState$1 {
|
|
99
104
|
status: ReactorStatus;
|
|
100
105
|
lastError?: ReactorError;
|
|
@@ -200,6 +205,11 @@ declare class Reactor {
|
|
|
200
205
|
connect(jwtToken?: string, options?: ConnectOptions): Promise<void>;
|
|
201
206
|
/**
|
|
202
207
|
* Sets up event handlers for the machine client.
|
|
208
|
+
*
|
|
209
|
+
* Each handler captures the client reference at registration time and
|
|
210
|
+
* ignores events if this.machineClient has since changed (e.g. after
|
|
211
|
+
* disconnect + reconnect), preventing stale WebRTC teardown events from
|
|
212
|
+
* interfering with a new connection.
|
|
203
213
|
*/
|
|
204
214
|
private setupMachineClientHandlers;
|
|
205
215
|
/**
|
|
@@ -367,4 +377,4 @@ declare function useStats(): ConnectionStats | undefined;
|
|
|
367
377
|
*/
|
|
368
378
|
declare function fetchInsecureJwtToken(apiKey: string, coordinatorUrl?: string): Promise<string>;
|
|
369
379
|
|
|
370
|
-
export { type AudioTrackOptions, ConflictError, type ConnectOptions, type ConnectionStats, type MessageScope, type Options, PROD_COORDINATOR_URL, Reactor, type ReactorConnectOptions, ReactorController, type ReactorControllerProps, type ReactorError, type ReactorEvent, ReactorProvider, type ReactorState$1 as ReactorState, type ReactorStatus, ReactorView, type ReactorViewProps, type TrackConfig, type VideoTrackOptions, WebcamStream, type WebcamStreamProps, audio, fetchInsecureJwtToken, useReactor, useReactorInternalMessage, useReactorMessage, useReactorStore, useStats, video };
|
|
380
|
+
export { AbortError, type AudioTrackOptions, ConflictError, type ConnectOptions, type ConnectionStats, type MessageScope, type Options, PROD_COORDINATOR_URL, Reactor, type ReactorConnectOptions, ReactorController, type ReactorControllerProps, type ReactorError, type ReactorEvent, ReactorProvider, type ReactorState$1 as ReactorState, type ReactorStatus, ReactorView, type ReactorViewProps, type TrackConfig, type VideoTrackOptions, WebcamStream, type WebcamStreamProps, audio, fetchInsecureJwtToken, isAbortError, useReactor, useReactorInternalMessage, useReactorMessage, useReactorStore, useStats, video };
|
package/dist/index.js
CHANGED
|
@@ -79,6 +79,7 @@ var __async = (__this, __arguments, generator) => {
|
|
|
79
79
|
// src/index.ts
|
|
80
80
|
var index_exports = {};
|
|
81
81
|
__export(index_exports, {
|
|
82
|
+
AbortError: () => AbortError,
|
|
82
83
|
ConflictError: () => ConflictError,
|
|
83
84
|
PROD_COORDINATOR_URL: () => PROD_COORDINATOR_URL,
|
|
84
85
|
Reactor: () => Reactor,
|
|
@@ -88,6 +89,7 @@ __export(index_exports, {
|
|
|
88
89
|
WebcamStream: () => WebcamStream,
|
|
89
90
|
audio: () => audio,
|
|
90
91
|
fetchInsecureJwtToken: () => fetchInsecureJwtToken,
|
|
92
|
+
isAbortError: () => isAbortError,
|
|
91
93
|
useReactor: () => useReactor,
|
|
92
94
|
useReactorInternalMessage: () => useReactorInternalMessage,
|
|
93
95
|
useReactorMessage: () => useReactorMessage,
|
|
@@ -109,6 +111,14 @@ var ConflictError = class extends Error {
|
|
|
109
111
|
super(message);
|
|
110
112
|
}
|
|
111
113
|
};
|
|
114
|
+
var AbortError = class extends Error {
|
|
115
|
+
constructor(message) {
|
|
116
|
+
super(message);
|
|
117
|
+
}
|
|
118
|
+
};
|
|
119
|
+
function isAbortError(error) {
|
|
120
|
+
return error instanceof AbortError || error instanceof Error && error.name === "AbortError";
|
|
121
|
+
}
|
|
112
122
|
|
|
113
123
|
// src/core/types.ts
|
|
114
124
|
var import_zod = require("zod");
|
|
@@ -214,13 +224,17 @@ function rewriteMids(sdp, trackNames) {
|
|
|
214
224
|
function createOffer(pc, trackNames) {
|
|
215
225
|
return __async(this, null, function* () {
|
|
216
226
|
const offer = yield pc.createOffer();
|
|
227
|
+
let needsAnswerRestore = false;
|
|
217
228
|
if (trackNames && trackNames.length > 0 && offer.sdp) {
|
|
218
229
|
const munged = rewriteMids(offer.sdp, trackNames);
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
230
|
+
try {
|
|
231
|
+
yield pc.setLocalDescription(
|
|
232
|
+
new RTCSessionDescription({ type: "offer", sdp: munged })
|
|
233
|
+
);
|
|
234
|
+
} catch (e) {
|
|
235
|
+
yield pc.setLocalDescription(offer);
|
|
236
|
+
needsAnswerRestore = true;
|
|
237
|
+
}
|
|
224
238
|
} else {
|
|
225
239
|
yield pc.setLocalDescription(offer);
|
|
226
240
|
}
|
|
@@ -229,9 +243,49 @@ function createOffer(pc, trackNames) {
|
|
|
229
243
|
if (!localDescription) {
|
|
230
244
|
throw new Error("Failed to create local description");
|
|
231
245
|
}
|
|
232
|
-
|
|
246
|
+
let sdp = localDescription.sdp;
|
|
247
|
+
if (needsAnswerRestore && trackNames && trackNames.length > 0) {
|
|
248
|
+
sdp = rewriteMids(sdp, trackNames);
|
|
249
|
+
}
|
|
250
|
+
return { sdp, needsAnswerRestore };
|
|
233
251
|
});
|
|
234
252
|
}
|
|
253
|
+
function buildMidMapping(transceivers) {
|
|
254
|
+
var _a;
|
|
255
|
+
const localToRemote = /* @__PURE__ */ new Map();
|
|
256
|
+
const remoteToLocal = /* @__PURE__ */ new Map();
|
|
257
|
+
for (const entry of transceivers) {
|
|
258
|
+
const mid = (_a = entry.transceiver) == null ? void 0 : _a.mid;
|
|
259
|
+
if (mid) {
|
|
260
|
+
localToRemote.set(mid, entry.name);
|
|
261
|
+
remoteToLocal.set(entry.name, mid);
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
return { localToRemote, remoteToLocal };
|
|
265
|
+
}
|
|
266
|
+
function restoreAnswerMids(sdp, remoteToLocal) {
|
|
267
|
+
const lines = sdp.split("\r\n");
|
|
268
|
+
for (let i = 0; i < lines.length; i++) {
|
|
269
|
+
if (lines[i].startsWith("a=mid:")) {
|
|
270
|
+
const remoteMid = lines[i].substring("a=mid:".length);
|
|
271
|
+
const localMid = remoteToLocal.get(remoteMid);
|
|
272
|
+
if (localMid !== void 0) {
|
|
273
|
+
lines[i] = `a=mid:${localMid}`;
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
if (lines[i].startsWith("a=group:BUNDLE ")) {
|
|
277
|
+
const parts = lines[i].split(" ");
|
|
278
|
+
for (let j = 1; j < parts.length; j++) {
|
|
279
|
+
const localMid = remoteToLocal.get(parts[j]);
|
|
280
|
+
if (localMid !== void 0) {
|
|
281
|
+
parts[j] = localMid;
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
lines[i] = parts.join(" ");
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
return lines.join("\r\n");
|
|
288
|
+
}
|
|
235
289
|
function setRemoteDescription(pc, sdp) {
|
|
236
290
|
return __async(this, null, function* () {
|
|
237
291
|
const sessionDescription = new RTCSessionDescription({
|
|
@@ -359,6 +413,22 @@ var CoordinatorClient = class {
|
|
|
359
413
|
this.baseUrl = options.baseUrl;
|
|
360
414
|
this.jwtToken = options.jwtToken;
|
|
361
415
|
this.model = options.model;
|
|
416
|
+
this.abortController = new AbortController();
|
|
417
|
+
}
|
|
418
|
+
/**
|
|
419
|
+
* Aborts any in-flight HTTP requests and polling loops.
|
|
420
|
+
* A fresh AbortController is created so the client remains reusable.
|
|
421
|
+
*/
|
|
422
|
+
abort() {
|
|
423
|
+
this.abortController.abort();
|
|
424
|
+
this.abortController = new AbortController();
|
|
425
|
+
}
|
|
426
|
+
/**
|
|
427
|
+
* The current abort signal, passed to every fetch() and sleep() call.
|
|
428
|
+
* Protected so subclasses can forward it to their own fetch calls.
|
|
429
|
+
*/
|
|
430
|
+
get signal() {
|
|
431
|
+
return this.abortController.signal;
|
|
362
432
|
}
|
|
363
433
|
/**
|
|
364
434
|
* Returns the authorization header with JWT Bearer token
|
|
@@ -379,7 +449,8 @@ var CoordinatorClient = class {
|
|
|
379
449
|
`${this.baseUrl}/ice_servers?model=${this.model}`,
|
|
380
450
|
{
|
|
381
451
|
method: "GET",
|
|
382
|
-
headers: this.getAuthHeaders()
|
|
452
|
+
headers: this.getAuthHeaders(),
|
|
453
|
+
signal: this.signal
|
|
383
454
|
}
|
|
384
455
|
);
|
|
385
456
|
if (!response.ok) {
|
|
@@ -413,7 +484,8 @@ var CoordinatorClient = class {
|
|
|
413
484
|
headers: __spreadProps(__spreadValues({}, this.getAuthHeaders()), {
|
|
414
485
|
"Content-Type": "application/json"
|
|
415
486
|
}),
|
|
416
|
-
body: JSON.stringify(requestBody)
|
|
487
|
+
body: JSON.stringify(requestBody),
|
|
488
|
+
signal: this.signal
|
|
417
489
|
});
|
|
418
490
|
if (!response.ok) {
|
|
419
491
|
const errorText = yield response.text();
|
|
@@ -447,7 +519,8 @@ var CoordinatorClient = class {
|
|
|
447
519
|
`${this.baseUrl}/sessions/${this.currentSessionId}`,
|
|
448
520
|
{
|
|
449
521
|
method: "GET",
|
|
450
|
-
headers: this.getAuthHeaders()
|
|
522
|
+
headers: this.getAuthHeaders(),
|
|
523
|
+
signal: this.signal
|
|
451
524
|
}
|
|
452
525
|
);
|
|
453
526
|
if (!response.ok) {
|
|
@@ -460,12 +533,13 @@ var CoordinatorClient = class {
|
|
|
460
533
|
}
|
|
461
534
|
/**
|
|
462
535
|
* Terminates the current session by sending a DELETE request to the coordinator.
|
|
463
|
-
*
|
|
536
|
+
* No-op if no session has been created yet.
|
|
537
|
+
* @throws Error if the request fails (except for 404, which clears local state)
|
|
464
538
|
*/
|
|
465
539
|
terminateSession() {
|
|
466
540
|
return __async(this, null, function* () {
|
|
467
541
|
if (!this.currentSessionId) {
|
|
468
|
-
|
|
542
|
+
return;
|
|
469
543
|
}
|
|
470
544
|
console.debug(
|
|
471
545
|
"[CoordinatorClient] Terminating session:",
|
|
@@ -475,7 +549,8 @@ var CoordinatorClient = class {
|
|
|
475
549
|
`${this.baseUrl}/sessions/${this.currentSessionId}`,
|
|
476
550
|
{
|
|
477
551
|
method: "DELETE",
|
|
478
|
-
headers: this.getAuthHeaders()
|
|
552
|
+
headers: this.getAuthHeaders(),
|
|
553
|
+
signal: this.signal
|
|
479
554
|
}
|
|
480
555
|
);
|
|
481
556
|
if (response.ok) {
|
|
@@ -525,7 +600,8 @@ var CoordinatorClient = class {
|
|
|
525
600
|
headers: __spreadProps(__spreadValues({}, this.getAuthHeaders()), {
|
|
526
601
|
"Content-Type": "application/json"
|
|
527
602
|
}),
|
|
528
|
-
body: JSON.stringify(requestBody)
|
|
603
|
+
body: JSON.stringify(requestBody),
|
|
604
|
+
signal: this.signal
|
|
529
605
|
}
|
|
530
606
|
);
|
|
531
607
|
if (response.status === 200) {
|
|
@@ -561,6 +637,9 @@ var CoordinatorClient = class {
|
|
|
561
637
|
let backoffMs = INITIAL_BACKOFF_MS;
|
|
562
638
|
let attempt = 0;
|
|
563
639
|
while (true) {
|
|
640
|
+
if (this.signal.aborted) {
|
|
641
|
+
throw new AbortError("SDP polling aborted");
|
|
642
|
+
}
|
|
564
643
|
if (attempt >= maxAttempts) {
|
|
565
644
|
throw new Error(
|
|
566
645
|
`SDP polling exceeded maximum attempts (${maxAttempts}) for session ${sessionId}`
|
|
@@ -576,7 +655,8 @@ var CoordinatorClient = class {
|
|
|
576
655
|
method: "GET",
|
|
577
656
|
headers: __spreadProps(__spreadValues({}, this.getAuthHeaders()), {
|
|
578
657
|
"Content-Type": "application/json"
|
|
579
|
-
})
|
|
658
|
+
}),
|
|
659
|
+
signal: this.signal
|
|
580
660
|
}
|
|
581
661
|
);
|
|
582
662
|
if (response.status === 200) {
|
|
@@ -621,10 +701,26 @@ var CoordinatorClient = class {
|
|
|
621
701
|
});
|
|
622
702
|
}
|
|
623
703
|
/**
|
|
624
|
-
*
|
|
704
|
+
* Abort-aware sleep. Resolves after `ms` milliseconds unless the
|
|
705
|
+
* abort signal fires first, in which case it rejects with AbortError.
|
|
625
706
|
*/
|
|
626
707
|
sleep(ms) {
|
|
627
|
-
return new Promise((resolve) =>
|
|
708
|
+
return new Promise((resolve, reject) => {
|
|
709
|
+
const { signal } = this;
|
|
710
|
+
if (signal.aborted) {
|
|
711
|
+
reject(new AbortError("Sleep aborted"));
|
|
712
|
+
return;
|
|
713
|
+
}
|
|
714
|
+
const timer = setTimeout(() => {
|
|
715
|
+
signal.removeEventListener("abort", onAbort);
|
|
716
|
+
resolve();
|
|
717
|
+
}, ms);
|
|
718
|
+
const onAbort = () => {
|
|
719
|
+
clearTimeout(timer);
|
|
720
|
+
reject(new AbortError("Sleep aborted"));
|
|
721
|
+
};
|
|
722
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
723
|
+
});
|
|
628
724
|
}
|
|
629
725
|
};
|
|
630
726
|
|
|
@@ -646,7 +742,8 @@ var LocalCoordinatorClient = class extends CoordinatorClient {
|
|
|
646
742
|
return __async(this, null, function* () {
|
|
647
743
|
console.debug("[LocalCoordinatorClient] Fetching ICE servers...");
|
|
648
744
|
const response = yield fetch(`${this.localBaseUrl}/ice_servers`, {
|
|
649
|
-
method: "GET"
|
|
745
|
+
method: "GET",
|
|
746
|
+
signal: this.signal
|
|
650
747
|
});
|
|
651
748
|
if (!response.ok) {
|
|
652
749
|
throw new Error("Failed to get ICE servers from local coordinator.");
|
|
@@ -670,7 +767,8 @@ var LocalCoordinatorClient = class extends CoordinatorClient {
|
|
|
670
767
|
console.debug("[LocalCoordinatorClient] Creating local session...");
|
|
671
768
|
this.sdpOffer = sdpOffer;
|
|
672
769
|
const response = yield fetch(`${this.localBaseUrl}/start_session`, {
|
|
673
|
-
method: "POST"
|
|
770
|
+
method: "POST",
|
|
771
|
+
signal: this.signal
|
|
674
772
|
});
|
|
675
773
|
if (!response.ok) {
|
|
676
774
|
throw new Error("Failed to send local start session command.");
|
|
@@ -698,7 +796,8 @@ var LocalCoordinatorClient = class extends CoordinatorClient {
|
|
|
698
796
|
headers: {
|
|
699
797
|
"Content-Type": "application/json"
|
|
700
798
|
},
|
|
701
|
-
body: JSON.stringify(sdpBody)
|
|
799
|
+
body: JSON.stringify(sdpBody),
|
|
800
|
+
signal: this.signal
|
|
702
801
|
});
|
|
703
802
|
if (!response.ok) {
|
|
704
803
|
if (response.status === 409) {
|
|
@@ -715,7 +814,8 @@ var LocalCoordinatorClient = class extends CoordinatorClient {
|
|
|
715
814
|
return __async(this, null, function* () {
|
|
716
815
|
console.debug("[LocalCoordinatorClient] Stopping local session...");
|
|
717
816
|
yield fetch(`${this.localBaseUrl}/stop_session`, {
|
|
718
|
-
method: "POST"
|
|
817
|
+
method: "POST",
|
|
818
|
+
signal: this.signal
|
|
719
819
|
});
|
|
720
820
|
});
|
|
721
821
|
}
|
|
@@ -790,12 +890,21 @@ var GPUMachineClient = class {
|
|
|
790
890
|
);
|
|
791
891
|
}
|
|
792
892
|
const trackNames = entries.map((e) => e.name);
|
|
793
|
-
const
|
|
893
|
+
const { sdp, needsAnswerRestore } = yield createOffer(
|
|
894
|
+
this.peerConnection,
|
|
895
|
+
trackNames
|
|
896
|
+
);
|
|
897
|
+
if (needsAnswerRestore) {
|
|
898
|
+
this.midMapping = buildMidMapping(entries);
|
|
899
|
+
} else {
|
|
900
|
+
this.midMapping = void 0;
|
|
901
|
+
}
|
|
794
902
|
console.debug(
|
|
795
903
|
"[GPUMachineClient] Created SDP offer with MIDs:",
|
|
796
|
-
trackNames
|
|
904
|
+
trackNames,
|
|
905
|
+
needsAnswerRestore ? "(needs answer restore)" : "(native munging)"
|
|
797
906
|
);
|
|
798
|
-
return
|
|
907
|
+
return sdp;
|
|
799
908
|
});
|
|
800
909
|
}
|
|
801
910
|
/**
|
|
@@ -844,7 +953,14 @@ var GPUMachineClient = class {
|
|
|
844
953
|
}
|
|
845
954
|
this.setStatus("connecting");
|
|
846
955
|
try {
|
|
847
|
-
|
|
956
|
+
let answer = sdpAnswer;
|
|
957
|
+
if (this.midMapping) {
|
|
958
|
+
answer = restoreAnswerMids(
|
|
959
|
+
answer,
|
|
960
|
+
this.midMapping.remoteToLocal
|
|
961
|
+
);
|
|
962
|
+
}
|
|
963
|
+
yield setRemoteDescription(this.peerConnection, answer);
|
|
848
964
|
console.debug("[GPUMachineClient] Remote description set");
|
|
849
965
|
} catch (error) {
|
|
850
966
|
console.error("[GPUMachineClient] Failed to connect:", error);
|
|
@@ -872,6 +988,7 @@ var GPUMachineClient = class {
|
|
|
872
988
|
this.peerConnection = void 0;
|
|
873
989
|
}
|
|
874
990
|
this.transceiverMap.clear();
|
|
991
|
+
this.midMapping = void 0;
|
|
875
992
|
this.peerConnected = false;
|
|
876
993
|
this.dataChannelOpen = false;
|
|
877
994
|
this.setStatus("disconnected");
|
|
@@ -1096,13 +1213,19 @@ var GPUMachineClient = class {
|
|
|
1096
1213
|
}
|
|
1097
1214
|
};
|
|
1098
1215
|
this.peerConnection.ontrack = (event) => {
|
|
1099
|
-
var _a;
|
|
1100
|
-
|
|
1101
|
-
const
|
|
1216
|
+
var _a, _b;
|
|
1217
|
+
let trackName;
|
|
1218
|
+
for (const [name, entry] of this.transceiverMap) {
|
|
1219
|
+
if (entry.transceiver === event.transceiver) {
|
|
1220
|
+
trackName = name;
|
|
1221
|
+
break;
|
|
1222
|
+
}
|
|
1223
|
+
}
|
|
1224
|
+
trackName != null ? trackName : trackName = (_a = event.transceiver.mid) != null ? _a : `unknown-${event.track.id}`;
|
|
1102
1225
|
console.debug(
|
|
1103
|
-
`[GPUMachineClient] Track received: "${trackName}" (${event.track.kind}, mid=${mid})`
|
|
1226
|
+
`[GPUMachineClient] Track received: "${trackName}" (${event.track.kind}, mid=${event.transceiver.mid})`
|
|
1104
1227
|
);
|
|
1105
|
-
const stream = (
|
|
1228
|
+
const stream = (_b = event.streams[0]) != null ? _b : new MediaStream([event.track]);
|
|
1106
1229
|
this.emit("trackReceived", trackName, event.track, stream);
|
|
1107
1230
|
};
|
|
1108
1231
|
this.peerConnection.onicecandidate = (event) => {
|
|
@@ -1325,8 +1448,8 @@ var Reactor = class {
|
|
|
1325
1448
|
options == null ? void 0 : options.maxAttempts
|
|
1326
1449
|
);
|
|
1327
1450
|
yield this.machineClient.connect(sdpAnswer);
|
|
1328
|
-
this.setStatus("ready");
|
|
1329
1451
|
} catch (error) {
|
|
1452
|
+
if (isAbortError(error)) return;
|
|
1330
1453
|
let recoverable = false;
|
|
1331
1454
|
if (error instanceof ConflictError) {
|
|
1332
1455
|
recoverable = true;
|
|
@@ -1385,6 +1508,7 @@ var Reactor = class {
|
|
|
1385
1508
|
);
|
|
1386
1509
|
yield this.machineClient.connect(sdpAnswer);
|
|
1387
1510
|
} catch (error) {
|
|
1511
|
+
if (isAbortError(error)) return;
|
|
1388
1512
|
console.error("[Reactor] Connection failed:", error);
|
|
1389
1513
|
this.createError(
|
|
1390
1514
|
"CONNECTION_FAILED",
|
|
@@ -1406,17 +1530,25 @@ var Reactor = class {
|
|
|
1406
1530
|
}
|
|
1407
1531
|
/**
|
|
1408
1532
|
* Sets up event handlers for the machine client.
|
|
1533
|
+
*
|
|
1534
|
+
* Each handler captures the client reference at registration time and
|
|
1535
|
+
* ignores events if this.machineClient has since changed (e.g. after
|
|
1536
|
+
* disconnect + reconnect), preventing stale WebRTC teardown events from
|
|
1537
|
+
* interfering with a new connection.
|
|
1409
1538
|
*/
|
|
1410
1539
|
setupMachineClientHandlers() {
|
|
1411
1540
|
if (!this.machineClient) return;
|
|
1412
|
-
this.machineClient
|
|
1541
|
+
const client = this.machineClient;
|
|
1542
|
+
client.on("message", (message, scope) => {
|
|
1543
|
+
if (this.machineClient !== client) return;
|
|
1413
1544
|
if (scope === "application") {
|
|
1414
1545
|
this.emit("message", message);
|
|
1415
1546
|
} else if (scope === "runtime") {
|
|
1416
1547
|
this.emit("runtimeMessage", message);
|
|
1417
1548
|
}
|
|
1418
1549
|
});
|
|
1419
|
-
|
|
1550
|
+
client.on("statusChanged", (status) => {
|
|
1551
|
+
if (this.machineClient !== client) return;
|
|
1420
1552
|
switch (status) {
|
|
1421
1553
|
case "connected":
|
|
1422
1554
|
this.setStatus("ready");
|
|
@@ -1435,13 +1567,15 @@ var Reactor = class {
|
|
|
1435
1567
|
break;
|
|
1436
1568
|
}
|
|
1437
1569
|
});
|
|
1438
|
-
|
|
1570
|
+
client.on(
|
|
1439
1571
|
"trackReceived",
|
|
1440
1572
|
(name, track, stream) => {
|
|
1573
|
+
if (this.machineClient !== client) return;
|
|
1441
1574
|
this.emit("trackReceived", name, track, stream);
|
|
1442
1575
|
}
|
|
1443
1576
|
);
|
|
1444
|
-
|
|
1577
|
+
client.on("statsUpdate", (stats) => {
|
|
1578
|
+
if (this.machineClient !== client) return;
|
|
1445
1579
|
this.emit("statsUpdate", stats);
|
|
1446
1580
|
});
|
|
1447
1581
|
}
|
|
@@ -1451,10 +1585,12 @@ var Reactor = class {
|
|
|
1451
1585
|
*/
|
|
1452
1586
|
disconnect(recoverable = false) {
|
|
1453
1587
|
return __async(this, null, function* () {
|
|
1588
|
+
var _a;
|
|
1454
1589
|
if (this.status === "disconnected" && !this.sessionId) {
|
|
1455
1590
|
console.warn("[Reactor] Already disconnected");
|
|
1456
1591
|
return;
|
|
1457
1592
|
}
|
|
1593
|
+
(_a = this.coordinatorClient) == null ? void 0 : _a.abort();
|
|
1458
1594
|
if (this.coordinatorClient && !recoverable) {
|
|
1459
1595
|
try {
|
|
1460
1596
|
yield this.coordinatorClient.terminateSession();
|
|
@@ -2679,6 +2815,7 @@ function fetchInsecureJwtToken(_0) {
|
|
|
2679
2815
|
}
|
|
2680
2816
|
// Annotate the CommonJS export names for ESM import in node:
|
|
2681
2817
|
0 && (module.exports = {
|
|
2818
|
+
AbortError,
|
|
2682
2819
|
ConflictError,
|
|
2683
2820
|
PROD_COORDINATOR_URL,
|
|
2684
2821
|
Reactor,
|
|
@@ -2688,6 +2825,7 @@ function fetchInsecureJwtToken(_0) {
|
|
|
2688
2825
|
WebcamStream,
|
|
2689
2826
|
audio,
|
|
2690
2827
|
fetchInsecureJwtToken,
|
|
2828
|
+
isAbortError,
|
|
2691
2829
|
useReactor,
|
|
2692
2830
|
useReactorInternalMessage,
|
|
2693
2831
|
useReactorMessage,
|