@telnyx/react-voice-commons-sdk 0.4.0 → 0.4.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/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
# CHANGELOG.md
|
|
2
2
|
|
|
3
|
+
## [0.4.1] (2026-04-27)
|
|
4
|
+
|
|
5
|
+
### Bug Fixing
|
|
6
|
+
|
|
7
|
+
- **Recover from stale TelnyxRTC client on incoming VoIP push.** `SessionManager.handlePushNotification` now calls `client.isFresh(30_000)` before reusing an existing client. If the socket has been silent past the threshold (typical after an iOS app freeze), the client is disposed, state is flipped to `DISCONNECTED`, and the push falls through to the full `_connect()` path so login runs with the new push's `voice_sdk_id`. This resolves the case where an answered call would hang on "connecting" until CallKit timed out, because `processVoIPNotification()` was being called on a socket that had silently died.
|
|
8
|
+
- Widened the reconnect-trigger state check in `handlePushNotification` to include `ERROR`, not only `DISCONNECTED`, so a push that arrives after a failed session re-establishes the connection instead of being dropped.
|
|
9
|
+
|
|
10
|
+
### Dependencies
|
|
11
|
+
|
|
12
|
+
- Now requires `@telnyx/react-native-voice-sdk >= 0.4.3`, which adds the `isFresh()` / `connectionIdleMs` API and auto-reconnects on unexpected socket errors. See the [voice-sdk 0.4.3 changelog](../package/CHANGELOG.md#043-2026-04-27) for details.
|
|
13
|
+
|
|
3
14
|
## [0.4.0] (2026-04-19)
|
|
4
15
|
|
|
5
16
|
### Enhancement
|
|
@@ -202,6 +202,39 @@ class SessionManager {
|
|
|
202
202
|
);
|
|
203
203
|
// Store the push notification payload for when the client is created
|
|
204
204
|
this._pendingPushPayload = payload;
|
|
205
|
+
// If we have a TelnyxRTC instance but its socket isn't fresh — either not
|
|
206
|
+
// connected at all, or connected but with no traffic for longer than the
|
|
207
|
+
// threshold — dispose it so we go through the full _connect() path below
|
|
208
|
+
// instead of calling processVoIPNotification on a stale client.
|
|
209
|
+
//
|
|
210
|
+
// This handles two cases iOS gives us with the same code path:
|
|
211
|
+
// 1. Terminated-then-cold-launched: a client exists (constructed at
|
|
212
|
+
// app boot) but its connection was never opened (idleMs=Infinity).
|
|
213
|
+
// 2. Suspended-then-thawed: a client exists with `connected=true`,
|
|
214
|
+
// but the kernel killed the TLS session during freeze without
|
|
215
|
+
// notifying the JS WebSocket wrapper, so no error event fired.
|
|
216
|
+
//
|
|
217
|
+
// 30s threshold matches the server's keep-alive ping cadence (~20s),
|
|
218
|
+
// so a live session always remains fresh.
|
|
219
|
+
const STALE_THRESHOLD_MS = 30000;
|
|
220
|
+
if (this._telnyxClient) {
|
|
221
|
+
const client = this._telnyxClient;
|
|
222
|
+
const isFresh =
|
|
223
|
+
typeof client.isFresh === 'function'
|
|
224
|
+
? client.isFresh(STALE_THRESHOLD_MS)
|
|
225
|
+
: !!client.connected;
|
|
226
|
+
if (!isFresh) {
|
|
227
|
+
try {
|
|
228
|
+
await this._telnyxClient.disconnect();
|
|
229
|
+
} catch (err) {
|
|
230
|
+
console.warn('SessionManager: disconnect of stale client threw:', err);
|
|
231
|
+
}
|
|
232
|
+
this._telnyxClient = undefined;
|
|
233
|
+
if (this.currentState !== connection_state_1.TelnyxConnectionState.DISCONNECTED) {
|
|
234
|
+
this._connectionState.next(connection_state_1.TelnyxConnectionState.DISCONNECTED);
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
}
|
|
205
238
|
// If we don't have a config yet but we're processing a push notification,
|
|
206
239
|
// attempt to load stored config first (for terminated app startup)
|
|
207
240
|
if (!this._currentConfig && !this._telnyxClient) {
|
|
@@ -269,11 +302,16 @@ class SessionManager {
|
|
|
269
302
|
console.log(
|
|
270
303
|
'SessionManager: RELEASE DEBUG - No client available, checking if we can trigger immediate connection'
|
|
271
304
|
);
|
|
272
|
-
// If we have config (either existing or newly loaded from storage) and
|
|
273
|
-
//
|
|
305
|
+
// If we have config (either existing or newly loaded from storage) and
|
|
306
|
+
// are not currently connected/connecting, trigger immediate connection.
|
|
307
|
+
// We accept DISCONNECTED and ERROR (a socket failure bumps state to
|
|
308
|
+
// ERROR) so a push after a failed session still re-establishes the
|
|
309
|
+
// connection. The _connect() method will process the pending push
|
|
310
|
+
// payload BEFORE calling connect().
|
|
274
311
|
if (
|
|
275
312
|
this._currentConfig &&
|
|
276
|
-
this.currentState === connection_state_1.TelnyxConnectionState.DISCONNECTED
|
|
313
|
+
(this.currentState === connection_state_1.TelnyxConnectionState.DISCONNECTED ||
|
|
314
|
+
this.currentState === connection_state_1.TelnyxConnectionState.ERROR)
|
|
277
315
|
) {
|
|
278
316
|
console.log(
|
|
279
317
|
'SessionManager: RELEASE DEBUG - Triggering immediate connection for push notification with config type:',
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@telnyx/react-voice-commons-sdk",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.1",
|
|
4
4
|
"description": "A high-level, state-agnostic, drop-in module for the Telnyx React Native SDK that simplifies WebRTC voice calling integration",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"module": "lib/index.js",
|
|
@@ -178,6 +178,40 @@ export class SessionManager {
|
|
|
178
178
|
// Store the push notification payload for when the client is created
|
|
179
179
|
(this as any)._pendingPushPayload = payload;
|
|
180
180
|
|
|
181
|
+
// If we have a TelnyxRTC instance but its socket isn't fresh — either not
|
|
182
|
+
// connected at all, or connected but with no traffic for longer than the
|
|
183
|
+
// threshold — dispose it so we go through the full _connect() path below
|
|
184
|
+
// instead of calling processVoIPNotification on a stale client.
|
|
185
|
+
//
|
|
186
|
+
// This handles two cases iOS gives us with the same code path:
|
|
187
|
+
// 1. Terminated-then-cold-launched: a client exists (constructed at
|
|
188
|
+
// app boot) but its connection was never opened (idleMs=Infinity).
|
|
189
|
+
// 2. Suspended-then-thawed: a client exists with `connected=true`,
|
|
190
|
+
// but the kernel killed the TLS session during freeze without
|
|
191
|
+
// notifying the JS WebSocket wrapper, so no error event fired.
|
|
192
|
+
//
|
|
193
|
+
// 30s threshold matches the server's keep-alive ping cadence (~20s),
|
|
194
|
+
// so a live session always remains fresh.
|
|
195
|
+
const STALE_THRESHOLD_MS = 30000;
|
|
196
|
+
if (this._telnyxClient) {
|
|
197
|
+
const client = this._telnyxClient as any;
|
|
198
|
+
const isFresh =
|
|
199
|
+
typeof client.isFresh === 'function'
|
|
200
|
+
? client.isFresh(STALE_THRESHOLD_MS)
|
|
201
|
+
: !!client.connected;
|
|
202
|
+
if (!isFresh) {
|
|
203
|
+
try {
|
|
204
|
+
await this._telnyxClient.disconnect();
|
|
205
|
+
} catch (err) {
|
|
206
|
+
console.warn('SessionManager: disconnect of stale client threw:', err);
|
|
207
|
+
}
|
|
208
|
+
this._telnyxClient = undefined;
|
|
209
|
+
if (this.currentState !== TelnyxConnectionState.DISCONNECTED) {
|
|
210
|
+
this._connectionState.next(TelnyxConnectionState.DISCONNECTED);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
181
215
|
// If we don't have a config yet but we're processing a push notification,
|
|
182
216
|
// attempt to load stored config first (for terminated app startup)
|
|
183
217
|
if (!this._currentConfig && !this._telnyxClient) {
|
|
@@ -255,9 +289,17 @@ export class SessionManager {
|
|
|
255
289
|
'SessionManager: RELEASE DEBUG - No client available, checking if we can trigger immediate connection'
|
|
256
290
|
);
|
|
257
291
|
|
|
258
|
-
// If we have config (either existing or newly loaded from storage) and
|
|
259
|
-
//
|
|
260
|
-
|
|
292
|
+
// If we have config (either existing or newly loaded from storage) and
|
|
293
|
+
// are not currently connected/connecting, trigger immediate connection.
|
|
294
|
+
// We accept DISCONNECTED and ERROR (a socket failure bumps state to
|
|
295
|
+
// ERROR) so a push after a failed session still re-establishes the
|
|
296
|
+
// connection. The _connect() method will process the pending push
|
|
297
|
+
// payload BEFORE calling connect().
|
|
298
|
+
if (
|
|
299
|
+
this._currentConfig &&
|
|
300
|
+
(this.currentState === TelnyxConnectionState.DISCONNECTED ||
|
|
301
|
+
this.currentState === TelnyxConnectionState.ERROR)
|
|
302
|
+
) {
|
|
261
303
|
console.log(
|
|
262
304
|
'SessionManager: RELEASE DEBUG - Triggering immediate connection for push notification with config type:',
|
|
263
305
|
(this._currentConfig as any).type || 'credential'
|