Sub-task of ENG-2180.
What to change
javascript-sdk/src/modules/StreamingClient.ts, the onIceConnectionStateChange() and onConnectionStateChange() handlers (currently lines ~590-610).
Today the SDK only reacts to:
iceConnectionState === 'connected' | 'completed' → emit CONNECTION_ESTABLISHED
connectionState === 'closed' → emit CONNECTION_CLOSED { WEBRTC_FAILURE }
It has no handler for disconnected or failed. So when a real user's network blips:
- T+5s: Chrome flips
iceConnectionState=disconnected — SDK does nothing, customer's app doesn't know.
- T+15s: Chrome flips
connectionState=failed — SDK does nothing, customer's app still doesn't know.
- T+20s: engine's 15s grace fires, engine closes the PC, Chrome flips
connectionState=closed, SDK finally fires CONNECTION_CLOSED.
For ~15-20s the customer's user sees a frozen avatar with no feedback.
Proposed handlers
private onIceConnectionStateChange() {
switch (this.peerConnection?.iceConnectionState) {
case 'connected':
case 'completed':
this.publicEventEmitter.emit(AnamEvent.CONNECTION_ESTABLISHED);
this.startStatsCollection();
break;
case 'disconnected':
// path went bad; customer can show "Reconnecting…" UI
this.publicEventEmitter.emit(AnamEvent.CONNECTION_UNSTABLE);
// attempt ICE restart proactively (don't wait for the server)
this.attemptIceRestart();
break;
case 'failed':
// path is dead from Chrome's perspective; consent checks stopped
this.publicEventEmitter.emit(AnamEvent.CONNECTION_LOST);
break;
}
}
And a corresponding recovered event when the state returns to connected after a disconnected.
ICE restart on the client side
pc.restartIce() (or setLocalDescription({iceRestart: true}) on the offer) triggers a fresh ICE gathering on the client. Combined with the engine-side restart from ENG-2180's engine subticket, this gives us belt-and-suspenders recovery — whoever notices Disconnected first can drive it.
Note: this also needs the engine to handle a client-initiated ICE restart offer cleanly via the existing websocket offer channel.
New public events
Add to AnamEvent:
CONNECTION_UNSTABLE — path is degraded, attempting recovery
CONNECTION_LOST — path failed; customer should consider this terminal unless CONNECTION_ESTABLISHED fires again
- (existing
CONNECTION_CLOSED stays for final teardown)
Document these in the SDK reference + cookbook — SN and other customers can replace their black-screen experience with a "Reconnecting…" spinner.
Backwards compatibility
New events are additive. Existing CONNECTION_CLOSED behaviour unchanged. SDK consumers that don't subscribe to the new events keep working as today.
Test plan
- Unit tests for each new state transition emitting the right event.
- Manual repro using client-issue-test-bench: kill the user's network for 3s, verify
CONNECTION_UNSTABLE fires then CONNECTION_ESTABLISHED again on recovery (or CONNECTION_LOST if not).
- Verify
pc.restartIce() correctly produces a renegotiation offer that the engine handles.
- Test on a TURN-only session and an srflx-only session.
- Ship behind the customer-facing changelog so SN, DataQueue, etc. know they can wire up
CONNECTION_UNSTABLE to their UI.
Sub-task of ENG-2180.
What to change
javascript-sdk/src/modules/StreamingClient.ts, theonIceConnectionStateChange()andonConnectionStateChange()handlers (currently lines ~590-610).Today the SDK only reacts to:
iceConnectionState === 'connected' | 'completed'→ emitCONNECTION_ESTABLISHEDconnectionState === 'closed'→ emitCONNECTION_CLOSED { WEBRTC_FAILURE }It has no handler for
disconnectedorfailed. So when a real user's network blips:iceConnectionState=disconnected— SDK does nothing, customer's app doesn't know.connectionState=failed— SDK does nothing, customer's app still doesn't know.connectionState=closed, SDK finally firesCONNECTION_CLOSED.For ~15-20s the customer's user sees a frozen avatar with no feedback.
Proposed handlers
And a corresponding
recoveredevent when the state returns toconnectedafter adisconnected.ICE restart on the client side
pc.restartIce()(orsetLocalDescription({iceRestart: true})on the offer) triggers a fresh ICE gathering on the client. Combined with the engine-side restart from ENG-2180's engine subticket, this gives us belt-and-suspenders recovery — whoever notices Disconnected first can drive it.Note: this also needs the engine to handle a client-initiated ICE restart offer cleanly via the existing websocket
offerchannel.New public events
Add to
AnamEvent:CONNECTION_UNSTABLE— path is degraded, attempting recoveryCONNECTION_LOST— path failed; customer should consider this terminal unlessCONNECTION_ESTABLISHEDfires againCONNECTION_CLOSEDstays for final teardown)Document these in the SDK reference + cookbook — SN and other customers can replace their black-screen experience with a "Reconnecting…" spinner.
Backwards compatibility
New events are additive. Existing
CONNECTION_CLOSEDbehaviour unchanged. SDK consumers that don't subscribe to the new events keep working as today.Test plan
CONNECTION_UNSTABLEfires thenCONNECTION_ESTABLISHEDagain on recovery (orCONNECTION_LOSTif not).pc.restartIce()correctly produces a renegotiation offer that the engine handles.CONNECTION_UNSTABLEto their UI.