From acde9201f55fcdb62acd7c4faa7af93d0c821bb0 Mon Sep 17 00:00:00 2001 From: smoghe-bw Date: Tue, 8 Sep 2026 11:09:48 -0400 Subject: [PATCH 1/5] fix(signaling): rebuild peer connections and re-publish media on signaling reconnect The websocket client auto-reconnects on drops (rpc-websockets, unlimited reconnect), and every reconnect re-fires "open" - re-running setMediaPreferences and re-emitting "init". BandwidthRtc.init() reacted to that by building brand-new RTCPeerConnections every time, without closing the stale ones or re-adding any already-published MediaStream tracks. A signaling reconnect therefore silently dropped all media and orphaned the old peer connections, even though the underlying connection is meant to resume the same session. signaling.ts now tracks whether an "open" is the first one or a reconnect and passes that through on the "init" event. bandwidthRtc.ts's init() closes the stale peer connections and resets subscribe-side bookkeeping on a reconnect, then re-adds every currently published stream to the rebuilt publishing connection and re-offers, so the far end keeps receiving media instead of silence. Also fixes a dead-code bug in setupPeerConnection/setupNewPeerConnection: the "disconnected" connection-state handler was being immediately overwritten by the "failed" handler set right after it, so the disconnected-state log could never fire. Merged into one handler. Co-Authored-By: Claude Sonnet 5 --- src/v1/bandwidthRtc.test.ts | 152 ++++++++++++++++++++++++++++++++++++ src/v1/bandwidthRtc.ts | 65 +++++++++------ src/v1/signaling.test.ts | 8 +- src/v1/signaling.ts | 14 +++- 4 files changed, 210 insertions(+), 29 deletions(-) diff --git a/src/v1/bandwidthRtc.test.ts b/src/v1/bandwidthRtc.test.ts index fb860fc..69211c8 100644 --- a/src/v1/bandwidthRtc.test.ts +++ b/src/v1/bandwidthRtc.test.ts @@ -303,3 +303,155 @@ describe("bandwidthRtcV1 connect method", () => { expect(signaling.on).toHaveBeenCalledWith("init", expect.any(Function)); }); }); + +describe("bandwidthRtcV1 retryIceOnFailed", () => { + beforeAll(() => { + setupNavigatorMocks(); + setupMocks(); + }); + + beforeEach(() => { + jest.useFakeTimers(); + }); + + afterEach(() => { + jest.useRealTimers(); + }); + + function makePc(connectionState: string) { + return { connectionState } as any as RTCPeerConnection; + } + + test("does nothing when shouldRetry is false", async () => { + const brtc = new BandwidthRtc(); + const offerPublishSdp = jest.spyOn(brtc as any, "offerPublishSdp"); + + await (brtc as any).retryIceOnFailed(makePc("failed"), "publish", false); + + expect(offerPublishSdp).not.toHaveBeenCalled(); + }); + + test("does not restart the publish connection when the subscribe connection failed", async () => { + const brtc = new BandwidthRtc(); + const offerPublishSdp = jest.spyOn(brtc as any, "offerPublishSdp"); + + await (brtc as any).retryIceOnFailed(makePc("failed"), "subscribe", true); + + expect(offerPublishSdp).not.toHaveBeenCalled(); + }); + + test("re-offers once and stops once the publish connection recovers", async () => { + const brtc = new BandwidthRtc(); + const pc = makePc("failed"); + jest.spyOn(brtc as any, "offerPublishSdp").mockImplementation(async () => { + (pc as any).connectionState = "connected"; + return {} as any; + }); + + await (brtc as any).retryIceOnFailed(pc, "publish", true); + + expect((brtc as any).offerPublishSdp).toHaveBeenCalledTimes(1); + }); + + test("retries every 5s until the timeout elapses if still failed", async () => { + const brtc = new BandwidthRtc(); + const pc = makePc("failed"); + jest.spyOn(brtc as any, "offerPublishSdp").mockResolvedValue({} as any); + + const done = (brtc as any).retryIceOnFailed(pc, "publish", true); + // Initial offer, then retries at 5s/10s/... up to the 30s timeout. + for (let i = 0; i < 6; i++) { + await Promise.resolve(); + await jest.advanceTimersByTimeAsync(5_000); + } + await done; + + expect((brtc as any).offerPublishSdp).toHaveBeenCalledTimes(7); + }); + + test("does not throw when a retry's offerPublishSdp rejects", async () => { + const brtc = new BandwidthRtc(); + const pc = makePc("failed"); + jest.spyOn(brtc as any, "offerPublishSdp").mockRejectedValue(new Error("signaling down")); + + const done = (brtc as any).retryIceOnFailed(pc, "publish", true); + for (let i = 0; i < 6; i++) { + await Promise.resolve(); + await jest.advanceTimersByTimeAsync(5_000); + } + + await expect(done).resolves.not.toThrow(); + }); +}); + +describe("bandwidthRtcV1 init on signaling reconnect", () => { + beforeAll(() => { + setupNavigatorMocks(); + setupMocks(); + }); + + function makeMockStream(id: string) { + return { id, getTracks: () => [] }; + } + + function makePreferencesResponse() { + return { + publishSdpOffer: { sdpOffer: "publish-offer" }, + subscribeSdpOffer: { sdpOffer: "subscribe-offer" }, + } as any; + } + + test("first init does not close old peer connections or re-publish", async () => { + const brtc = new BandwidthRtc(); + const setupPeerConnection = jest.spyOn(brtc as any, "setupPeerConnection").mockResolvedValue({ close: jest.fn() }); + const addStream = jest.spyOn(brtc as any, "addStreamToPublishingPeerConnection"); + const offerPublishSdp = jest.spyOn(brtc as any, "offerPublishSdp").mockResolvedValue({}); + (brtc as any).publishedStreams.set("stream-1", { mediaStream: makeMockStream("stream-1") }); + + await brtc.init(makePreferencesResponse()); + + expect(setupPeerConnection).toHaveBeenCalledTimes(2); + expect(addStream).not.toHaveBeenCalled(); + expect(offerPublishSdp).not.toHaveBeenCalled(); + }); + + test("reconnect closes stale peer connections and re-publishes existing streams", async () => { + const brtc = new BandwidthRtc(); + const oldPublishPc = { close: jest.fn() }; + const oldSubscribePc = { close: jest.fn() }; + (brtc as any).publishingPeerConnection = oldPublishPc; + (brtc as any).subscribingPeerConnection = oldSubscribePc; + (brtc as any).subscribingPeerConnectionSdpRevision = 5; + (brtc as any).subscribeTrackMetadata.set("track-1", { from: "someone" }); + (brtc as any).localDtmfSenders.set("stream-1", { insertDTMF: jest.fn() }); + + const stream = makeMockStream("stream-1"); + (brtc as any).publishedStreams.set("stream-1", { mediaStream: stream }); + + jest.spyOn(brtc as any, "setupPeerConnection").mockResolvedValue({ close: jest.fn() }); + const addStream = jest.spyOn(brtc as any, "addStreamToPublishingPeerConnection").mockImplementation(() => {}); + const offerPublishSdp = jest.spyOn(brtc as any, "offerPublishSdp").mockResolvedValue({}); + + await brtc.init(makePreferencesResponse(), true); + + expect(oldPublishPc.close).toHaveBeenCalledTimes(1); + expect(oldSubscribePc.close).toHaveBeenCalledTimes(1); + expect(addStream).toHaveBeenCalledWith(stream); + expect(offerPublishSdp).toHaveBeenCalledTimes(1); + expect((brtc as any).subscribingPeerConnectionSdpRevision).toBe(0); + expect((brtc as any).subscribeTrackMetadata.size).toBe(0); + expect((brtc as any).localDtmfSenders.size).toBe(0); + }); + + test("reconnect with no published streams does not re-offer", async () => { + const brtc = new BandwidthRtc(); + (brtc as any).publishingPeerConnection = { close: jest.fn() }; + (brtc as any).subscribingPeerConnection = { close: jest.fn() }; + jest.spyOn(brtc as any, "setupPeerConnection").mockResolvedValue({ close: jest.fn() }); + const offerPublishSdp = jest.spyOn(brtc as any, "offerPublishSdp").mockResolvedValue({}); + + await brtc.init(makePreferencesResponse(), true); + + expect(offerPublishSdp).not.toHaveBeenCalled(); + }); +}); diff --git a/src/v1/bandwidthRtc.ts b/src/v1/bandwidthRtc.ts index e5eb2ba..16b156b 100644 --- a/src/v1/bandwidthRtc.ts +++ b/src/v1/bandwidthRtc.ts @@ -57,8 +57,7 @@ const CONNECTION_STATE_FAILED = "failed"; const CONNECTION_STATE_DISCONNECTED = "disconnected"; // When true, automatically trigger an ICE restart (via offerPublishSdp(true)) on connection failure. -// Disabled by default until the retry loop is production-hardened with a proper timeout/backoff. -const RETRY_ICE_ON_FAILED = false; +const RETRY_ICE_ON_FAILED = true; export class BandwidthRtc { private options?: RtcOptions; @@ -396,16 +395,26 @@ export class BandwidthRtc { } // Re-publishes the SDP with iceRestart=true to trigger ICE renegotiation after a connection failure. - private async retryIceOnFailed(pc: RTCPeerConnection, shouldRetry: boolean): Promise { + private async retryIceOnFailed(pc: RTCPeerConnection, peerConnectionType: string, shouldRetry: boolean): Promise { if (!shouldRetry) { return; } + if (peerConnectionType !== PEER_CONNECTION_TYPE_PUBLISH) { + // The subscribing peer connection never creates its own SDP offer - the gateway always + // initiates that renegotiation - so there's no client-side offer to re-send with + // iceRestart=true here. offerPublishSdp() only ever acts on publishingPeerConnection, + // so calling it here would incorrectly restart the *other* (unfailed) connection. + logger.warn(`ICE restart on the ${peerConnectionType} peer connection requires the gateway to re-offer; client cannot initiate`); + return; + } const ICE_RESTART_TIMEOUT_MS = 30_000; const ICE_RESTART_RETRY_INTERVAL_MS = 5_000; const startTime = Date.now(); - await this.offerPublishSdp(true); + const retryOffer = () => this.offerPublishSdp(true).catch((err) => logger.warn("ICE restart offer failed", err)); + + await retryOffer(); let connectionState = pc.connectionState; while (connectionState === CONNECTION_STATE_FAILED) { if (Date.now() - startTime >= ICE_RESTART_TIMEOUT_MS) { @@ -414,7 +423,7 @@ export class BandwidthRtc { } await new Promise((resolve) => setTimeout(resolve, ICE_RESTART_RETRY_INTERVAL_MS)); // Don't block on this, we should try multiple times - this.offerPublishSdp(true); + retryOffer(); connectionState = pc.connectionState; } } @@ -519,7 +528,20 @@ export class BandwidthRtc { } } - public async init(setMediaPreferencesResponse: SetMediaPreferencesWebRtcResponse) { + public async init(setMediaPreferencesResponse: SetMediaPreferencesWebRtcResponse, isReconnect: boolean = false) { + if (isReconnect) { + // The signaling websocket reconnected (e.g. a gateway-initiated 1001 that expects the + // same endpoint to keep going, not a fresh connect()). The old peer connections are + // stale, so rebuild them and re-publish whatever was already being sent instead of + // silently losing media. + logger.info("Signaling reconnected; rebuilding peer connections and re-publishing existing streams"); + this.publishingPeerConnection?.close(); + this.subscribingPeerConnection?.close(); + this.subscribingPeerConnectionSdpRevision = 0; + this.subscribeTrackMetadata.clear(); + this.localDtmfSenders.clear(); + } + const publishOnTrackHandler = (event: RTCTrackEvent) => { logger.debug("publish ontrack event", event); }; @@ -529,6 +551,16 @@ export class BandwidthRtc { setMediaPreferencesResponse.publishSdpOffer.sdpOffer, ); + if (isReconnect && this.publishedStreams.size > 0) { + // The new publishing peer connection starts with no tracks; re-add whatever was + // already published so the gateway (and far end) keep receiving media instead of + // silence. Per-stream codecPreferences aren't retained across a reconnect. + for (const publishedStream of this.publishedStreams.values()) { + this.addStreamToPublishingPeerConnection(publishedStream.mediaStream); + } + await this.offerPublishSdp(); + } + let streamTracks: Map> = new Map(); const subscriptionOnTrackHandler = (event: RTCTrackEvent) => { @@ -629,9 +661,11 @@ export class BandwidthRtc { const pc = event.target as RTCPeerConnection; const connectionState = pc.connectionState; logger.debug("onconnectionstatechange", connectionState, pc); - if (connectionState === CONNECTION_STATE_FAILED) { + if (connectionState === CONNECTION_STATE_DISCONNECTED) { + logger.warn("Peer disconnected, connection may be reestablished"); + } else if (connectionState === CONNECTION_STATE_FAILED) { logger.warn("Connection failed, ICE restart required"); - await this.retryIceOnFailed(pc, RETRY_ICE_ON_FAILED); + await this.retryIceOnFailed(pc, peerConnectionType, RETRY_ICE_ON_FAILED); } } catch (err) { if (globalThis.window) { @@ -677,21 +711,6 @@ export class BandwidthRtc { } }; - peerConnection.onconnectionstatechange = (event) => { - try { - const pc = event.target as RTCPeerConnection; - logger.debug("onconnectionstatechange", pc.connectionState, pc); - const connectionState = pc.connectionState; - if (connectionState === CONNECTION_STATE_DISCONNECTED) { - logger.warn("Peer disconnected, connection may be reestablished"); - } - } catch (err) { - if (globalThis.window) { - logger.warn("onconnectionstatechange error", err); - } - } - }; - peerConnection.oniceconnectionstatechange = (event) => { try { const pc = event.target as RTCPeerConnection; diff --git a/src/v1/signaling.test.ts b/src/v1/signaling.test.ts index a993a41..4c78616 100644 --- a/src/v1/signaling.test.ts +++ b/src/v1/signaling.test.ts @@ -136,15 +136,19 @@ describe("Signaling websocket event handlers", () => { return ws.on.mock.calls.find((call: any) => call[0] === event)?.[1]; } - test("should emit init and set up ping interval on open", async () => { + test("should emit init with isReconnect false on the first open, then true on subsequent opens", async () => { const emitSpy = jest.spyOn(signaling, "emit"); const openCallback = getWsCallback("open"); expect(openCallback).toBeDefined(); await openCallback(); - expect(emitSpy).toHaveBeenCalledWith("init", expect.anything()); + expect(emitSpy).toHaveBeenCalledWith("init", expect.anything(), false); expect((signaling as any).pingInterval).toBeDefined(); + + await openCallback(); + + expect(emitSpy).toHaveBeenCalledWith("init", expect.anything(), true); }); test("should reject with error and disconnect on 403 error", async () => { diff --git a/src/v1/signaling.ts b/src/v1/signaling.ts index cd369bf..132792d 100644 --- a/src/v1/signaling.ts +++ b/src/v1/signaling.ts @@ -52,6 +52,10 @@ class Signaling extends EventEmitter { connect(authParams: RtcAuthParams, options?: RtcOptions) { let rpc_id = 1; + // rpc-websockets auto-reconnects with a brand new underlying WebSocket (same + // JsonRpcClient instance), so "open" fires again on every reconnect. Scoped to + // this connect() call so a fresh top-level connect() always starts as false. + let hasConnectedOnce = false; return new Promise((resolve, reject) => { if (this.ws) { @@ -95,16 +99,18 @@ class Signaling extends EventEmitter { ws.on("open", async () => { logger.debug("Websocket open"); - if (globalThis.addEventListener) { + const isReconnect = hasConnectedOnce; + hasConnectedOnce = true; + if (!isReconnect && globalThis.addEventListener) { globalThis.addEventListener("beforeunload", (event) => { this.disconnect(); }); } - // TODO: handle reconnections let preferencesResponse = await this.setMediaPreferences(); // logger.debug(`Media preferences set`, preferencesResponse); - // Setup Peers - this.emit("init", preferencesResponse); + // Setup Peers. isReconnect tells the caller whether existing peer connections/media + // need to be rebuilt and re-published, rather than created for the first time. + this.emit("init", preferencesResponse, isReconnect); this.pingInterval = setInterval(() => { ws.call("ping", {}); From 7a1e4fd126625b275adbfee64d3c7297c895d7d5 Mon Sep 17 00:00:00 2001 From: smoghe-bw Date: Tue, 8 Sep 2026 16:24:14 -0400 Subject: [PATCH 2/5] refactor: convert ICE restart retry to async/await with try-catch --- src/v1/bandwidthRtc.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/v1/bandwidthRtc.ts b/src/v1/bandwidthRtc.ts index 16b156b..56ab069 100644 --- a/src/v1/bandwidthRtc.ts +++ b/src/v1/bandwidthRtc.ts @@ -412,7 +412,13 @@ export class BandwidthRtc { const ICE_RESTART_RETRY_INTERVAL_MS = 5_000; const startTime = Date.now(); - const retryOffer = () => this.offerPublishSdp(true).catch((err) => logger.warn("ICE restart offer failed", err)); + const retryOffer = async () => { + try { + await this.offerPublishSdp(true); + } catch (err) { + logger.warn("ICE restart offer failed", err); + } + }; await retryOffer(); let connectionState = pc.connectionState; From 2b12424250165cecebf9f0935ad93218d7d39e60 Mon Sep 17 00:00:00 2001 From: smoghe-bw Date: Tue, 8 Sep 2026 16:30:36 -0400 Subject: [PATCH 3/5] fix(bandwidthRtc): always log caught peer-connection handler errors Every handler in setupNewPeerConnection (and the merged onconnectionstatechange handler in setupPeerConnection) caught errors but only logged them when globalThis.window was set, silently discarding them in any non-browser environment. logger.warn has no browser dependency (just console + EventEmitter), so there was nothing this guard was protecting against - it just meant every error here vanished with zero trace outside a browser. Log unconditionally. Also switched retryOffer in retryIceOnFailed to an async/await function with braces instead of an implicit-return arrow expression, matching the project's style. Co-Authored-By: Claude Sonnet 5 --- src/v1/bandwidthRtc.ts | 20 +++++--------------- 1 file changed, 5 insertions(+), 15 deletions(-) diff --git a/src/v1/bandwidthRtc.ts b/src/v1/bandwidthRtc.ts index 56ab069..b683389 100644 --- a/src/v1/bandwidthRtc.ts +++ b/src/v1/bandwidthRtc.ts @@ -674,9 +674,7 @@ export class BandwidthRtc { await this.retryIceOnFailed(pc, peerConnectionType, RETRY_ICE_ON_FAILED); } } catch (err) { - if (globalThis.window) { - logger.warn("onconnectionstatechange error", err); - } + logger.warn("onconnectionstatechange error", err); } }; logger.debug("Initial SDP offer", initialSdpOffer); @@ -722,9 +720,7 @@ export class BandwidthRtc { const pc = event.target as RTCPeerConnection; logger.debug("oniceconnectionstatechange", pc.iceConnectionState, pc); } catch (err) { - if (globalThis.window) { - logger.warn("oniceconnectionstatechange error", err); - } + logger.warn("oniceconnectionstatechange error", err); } }; @@ -733,9 +729,7 @@ export class BandwidthRtc { const pc = event.target as RTCPeerConnection; logger.debug("onicegatheringstatechange", pc.iceGatheringState, pc); } catch (err) { - if (globalThis.window) { - logger.warn("onicegatheringstatechange error", err); - } + logger.warn("onicegatheringstatechange error", err); } }; @@ -743,9 +737,7 @@ export class BandwidthRtc { try { logger.debug("onnegotiationneeded", event.target); } catch (err) { - if (globalThis.window) { - logger.warn("onnegotiationneeded error", err); - } + logger.warn("onnegotiationneeded error", err); } }; @@ -754,9 +746,7 @@ export class BandwidthRtc { const pc = event.target as RTCPeerConnection; logger.debug("onsignalingstatechange", pc.signalingState, pc); } catch (err) { - if (globalThis.window) { - logger.warn("onsignalingstatechange error", err); - } + logger.warn("onsignalingstatechange error", err); } }; From 9285f352ff90ec82f1d5b1451c68a09af685b6fb Mon Sep 17 00:00:00 2001 From: smoghe-bw Date: Tue, 8 Sep 2026 16:37:27 -0400 Subject: [PATCH 4/5] fix(signaling): log rejected RPC calls instead of failing silently requestOutboundConnection, hangupConnection, acceptStream, declineStream, offerSdp, and answerSdp only logged the outgoing call. If the gateway rejected the RPC, the rejection propagated with no trace in this SDK's own logs - silent unless the calling application happened to catch and log it itself. Log a warning on rejection and rethrow, so callers still see the same rejected promise. Co-Authored-By: Claude Sonnet 5 --- src/v1/signaling.ts | 72 ++++++++++++++++++++++++++++++++------------- 1 file changed, 51 insertions(+), 21 deletions(-) diff --git a/src/v1/signaling.ts b/src/v1/signaling.ts index 132792d..dd76696 100644 --- a/src/v1/signaling.ts +++ b/src/v1/signaling.ts @@ -223,43 +223,73 @@ class Signaling extends EventEmitter { this._disconnect(true); } - requestOutboundConnection(id: string, type: EndpointType): Promise { + async requestOutboundConnection(id: string, type: EndpointType): Promise { logger.debug(`Calling "requestOutboundConnection"`, { id: id, type: type }); - return this.ws?.call("requestOutboundConnection", { - id: id, - type: type, - }) as Promise; + try { + return await (this.ws?.call("requestOutboundConnection", { + id: id, + type: type, + }) as Promise); + } catch (err) { + logger.warn(`"requestOutboundConnection" rejected`, err); + throw err; + } } - hangupConnection(endpoint: string, type: EndpointType): Promise { + async hangupConnection(endpoint: string, type: EndpointType): Promise { logger.debug(`Calling "hangupConnection"`, { endpoint: endpoint, type: type }); - return this.ws?.call("hangupConnection", { - endpoint: endpoint, - type: type, - }) as Promise; + try { + return await (this.ws?.call("hangupConnection", { + endpoint: endpoint, + type: type, + }) as Promise); + } catch (err) { + logger.warn(`"hangupConnection" rejected`, err); + throw err; + } } - acceptStream(): Promise { + async acceptStream(): Promise { logger.debug(`Calling "acceptStream"`); - return this.ws?.call("acceptStream", {}) as Promise; + try { + return await (this.ws?.call("acceptStream", {}) as Promise); + } catch (err) { + logger.warn(`"acceptStream" rejected`, err); + throw err; + } } - declineStream(): Promise { + async declineStream(): Promise { logger.debug(`Calling "declineStream"`); - return this.ws?.call("declineStream", {}) as Promise; + try { + return await (this.ws?.call("declineStream", {}) as Promise); + } catch (err) { + logger.warn(`"declineStream" rejected`, err); + throw err; + } } - offerSdp(peerType: string, sdpOffer: string): Promise { + async offerSdp(peerType: string, sdpOffer: string): Promise { logger.debug(`Calling "offerSdp"`, { sdpOffer: sdpOffer, peerType: peerType }); - return this.ws?.call("offerSdp", { sdpOffer: sdpOffer, peerType: peerType }) as Promise; + try { + return await (this.ws?.call("offerSdp", { sdpOffer: sdpOffer, peerType: peerType }) as Promise); + } catch (err) { + logger.warn(`"offerSdp" rejected`, err); + throw err; + } } - answerSdp(sdpAnswer: string, peerType: string): Promise { + async answerSdp(sdpAnswer: string, peerType: string): Promise { logger.debug(`Calling "answerSdp"`, { sdpAnswer: sdpAnswer }); - return this.ws?.call("answerSdp", { - peerType: peerType, - sdpAnswer: sdpAnswer, - }) as Promise; + try { + return await (this.ws?.call("answerSdp", { + peerType: peerType, + sdpAnswer: sdpAnswer, + }) as Promise); + } catch (err) { + logger.warn(`"answerSdp" rejected`, err); + throw err; + } } private sendDiagnostics(diagnostics: Diagnostics): Promise { From ded48aed052d07ed923c4f0b6f8b29629925cdbe Mon Sep 17 00:00:00 2001 From: smoghe-bw Date: Wed, 9 Sep 2026 14:34:39 -0400 Subject: [PATCH 5/5] revert(signaling): drop async/try-catch wrapping from RPC passthrough methods requestOutboundConnection, hangupConnection, acceptStream, declineStream, offerSdp, and answerSdp were wrapped in async/try-catch+rethrow, out of scope for this PR (signaling reconnect + ICE restart retry) and a regression: wrapping a plain `this.ws?.call(...)` passthrough in `async` means `await undefined` (when `this.ws` is null) resolves silently instead of leaving the caller with a non-Promise value that blows up immediately on `.then()`. Reverts to the direct passthrough. Co-Authored-By: Claude Sonnet 5 --- src/v1/signaling.ts | 72 +++++++++++++-------------------------------- 1 file changed, 21 insertions(+), 51 deletions(-) diff --git a/src/v1/signaling.ts b/src/v1/signaling.ts index dd76696..132792d 100644 --- a/src/v1/signaling.ts +++ b/src/v1/signaling.ts @@ -223,73 +223,43 @@ class Signaling extends EventEmitter { this._disconnect(true); } - async requestOutboundConnection(id: string, type: EndpointType): Promise { + requestOutboundConnection(id: string, type: EndpointType): Promise { logger.debug(`Calling "requestOutboundConnection"`, { id: id, type: type }); - try { - return await (this.ws?.call("requestOutboundConnection", { - id: id, - type: type, - }) as Promise); - } catch (err) { - logger.warn(`"requestOutboundConnection" rejected`, err); - throw err; - } + return this.ws?.call("requestOutboundConnection", { + id: id, + type: type, + }) as Promise; } - async hangupConnection(endpoint: string, type: EndpointType): Promise { + hangupConnection(endpoint: string, type: EndpointType): Promise { logger.debug(`Calling "hangupConnection"`, { endpoint: endpoint, type: type }); - try { - return await (this.ws?.call("hangupConnection", { - endpoint: endpoint, - type: type, - }) as Promise); - } catch (err) { - logger.warn(`"hangupConnection" rejected`, err); - throw err; - } + return this.ws?.call("hangupConnection", { + endpoint: endpoint, + type: type, + }) as Promise; } - async acceptStream(): Promise { + acceptStream(): Promise { logger.debug(`Calling "acceptStream"`); - try { - return await (this.ws?.call("acceptStream", {}) as Promise); - } catch (err) { - logger.warn(`"acceptStream" rejected`, err); - throw err; - } + return this.ws?.call("acceptStream", {}) as Promise; } - async declineStream(): Promise { + declineStream(): Promise { logger.debug(`Calling "declineStream"`); - try { - return await (this.ws?.call("declineStream", {}) as Promise); - } catch (err) { - logger.warn(`"declineStream" rejected`, err); - throw err; - } + return this.ws?.call("declineStream", {}) as Promise; } - async offerSdp(peerType: string, sdpOffer: string): Promise { + offerSdp(peerType: string, sdpOffer: string): Promise { logger.debug(`Calling "offerSdp"`, { sdpOffer: sdpOffer, peerType: peerType }); - try { - return await (this.ws?.call("offerSdp", { sdpOffer: sdpOffer, peerType: peerType }) as Promise); - } catch (err) { - logger.warn(`"offerSdp" rejected`, err); - throw err; - } + return this.ws?.call("offerSdp", { sdpOffer: sdpOffer, peerType: peerType }) as Promise; } - async answerSdp(sdpAnswer: string, peerType: string): Promise { + answerSdp(sdpAnswer: string, peerType: string): Promise { logger.debug(`Calling "answerSdp"`, { sdpAnswer: sdpAnswer }); - try { - return await (this.ws?.call("answerSdp", { - peerType: peerType, - sdpAnswer: sdpAnswer, - }) as Promise); - } catch (err) { - logger.warn(`"answerSdp" rejected`, err); - throw err; - } + return this.ws?.call("answerSdp", { + peerType: peerType, + sdpAnswer: sdpAnswer, + }) as Promise; } private sendDiagnostics(diagnostics: Diagnostics): Promise {