@telnyx/react-voice-commons-sdk 0.4.2 → 0.4.3
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.3] (2026-04-30)
|
|
4
|
+
|
|
5
|
+
### Bug Fixing
|
|
6
|
+
|
|
7
|
+
- **VoIP push always rebuilds the `TelnyxRTC` instance.** `SessionManager.handlePushNotification` now unconditionally disposes any prior client and runs `_connect()` afresh, so the new socket's URL bakes in THIS push's `voice_sdk_id`. Reusing a prior connection would cause the gateway to route this push's INVITE to whichever client announced with the new `voice_sdk_id` (i.e. a different connection), leaving us listening on the wrong socket until CallKit timed out.
|
|
8
|
+
- **Dropped the `loginFromStoredConfig()` fallback inside `TelnyxVoipClient.handlePushNotification`.** When SessionManager's push-driven `_connect()` didn't immediately reach `CONNECTED`, the fallback would create a second TelnyxRTC instance with no `voice_sdk_id` awareness. The gateway then `punt`-ed one of the parallel sessions, dropping the active call. SessionManager already loads stored credentials internally for the cold-launch path; the redundant fallback only created races.
|
|
9
|
+
|
|
10
|
+
### Dependencies
|
|
11
|
+
|
|
12
|
+
- Now requires `@telnyx/react-native-voice-sdk >= 0.4.5`, which removes the auto-reconnect on socket error/close that was triggering the multi-instance race. See the [voice-sdk 0.4.5 changelog](../package/CHANGELOG.md#045-2026-04-30) for details.
|
|
13
|
+
|
|
3
14
|
## [0.4.2] (2026-04-27)
|
|
4
15
|
|
|
5
16
|
### Bug Fixing
|
|
@@ -202,39 +202,22 @@ class SessionManager {
|
|
|
202
202
|
);
|
|
203
203
|
// Store the push notification payload for when the client is created
|
|
204
204
|
this._pendingPushPayload = payload;
|
|
205
|
-
//
|
|
206
|
-
//
|
|
207
|
-
//
|
|
208
|
-
//
|
|
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;
|
|
205
|
+
// Each push always rebuilds the TelnyxRTC client. The voice_sdk_id is
|
|
206
|
+
// baked into the WebSocket URL at socket-open time and can't be mutated
|
|
207
|
+
// mid-flight; reusing a connection from a previous push means the
|
|
208
|
+
// gateway routes THIS push's INVITE to a different (correctly-stamped)
|
|
209
|
+
// client and we sit on the wrong socket forever.
|
|
220
210
|
if (this._telnyxClient) {
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
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
|
-
}
|
|
211
|
+
try {
|
|
212
|
+
await this._telnyxClient.disconnect();
|
|
213
|
+
} catch (err) {
|
|
214
|
+
console.warn('SessionManager: disconnect of prior client threw:', err);
|
|
236
215
|
}
|
|
237
216
|
}
|
|
217
|
+
this._telnyxClient = undefined;
|
|
218
|
+
if (this.currentState !== connection_state_1.TelnyxConnectionState.DISCONNECTED) {
|
|
219
|
+
this._connectionState.next(connection_state_1.TelnyxConnectionState.DISCONNECTED);
|
|
220
|
+
}
|
|
238
221
|
// If we don't have a config yet but we're processing a push notification,
|
|
239
222
|
// attempt to load stored config first (for terminated app startup)
|
|
240
223
|
if (!this._currentConfig && !this._telnyxClient) {
|
|
@@ -333,21 +333,14 @@ class TelnyxVoipClient {
|
|
|
333
333
|
console.log('TelnyxVoipClient: Handling push notification:', payload);
|
|
334
334
|
}
|
|
335
335
|
try {
|
|
336
|
-
//
|
|
337
|
-
//
|
|
336
|
+
// SessionManager owns the entire push lifecycle: it disposes any prior
|
|
337
|
+
// client, loads stored credentials if needed, and rebuilds the
|
|
338
|
+
// TelnyxRTC bound to THIS push's voice_sdk_id. We deliberately do not
|
|
339
|
+
// fall back to a generic loginFromStoredConfig() here — that would
|
|
340
|
+
// create a parallel client with no voice_sdk_id awareness, which the
|
|
341
|
+
// gateway then has to `punt` once the SessionManager-driven session
|
|
342
|
+
// also registers, dropping the active call.
|
|
338
343
|
await this._sessionManager.handlePushNotification(payload);
|
|
339
|
-
// Connect if not already connected
|
|
340
|
-
const currentState = this.currentConnectionState;
|
|
341
|
-
if (currentState !== connection_state_1.TelnyxConnectionState.CONNECTED) {
|
|
342
|
-
// Try to login from stored config - now the push flags should be set
|
|
343
|
-
const loginSuccess = await this.loginFromStoredConfig();
|
|
344
|
-
if (!loginSuccess) {
|
|
345
|
-
console.warn(
|
|
346
|
-
'TelnyxVoipClient: Could not login from stored config for push notification'
|
|
347
|
-
);
|
|
348
|
-
return;
|
|
349
|
-
}
|
|
350
|
-
}
|
|
351
344
|
} catch (error) {
|
|
352
345
|
console.error('TelnyxVoipClient: Error handling push notification:', error);
|
|
353
346
|
throw error;
|
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.3",
|
|
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",
|
|
@@ -86,7 +86,7 @@
|
|
|
86
86
|
},
|
|
87
87
|
"dependencies": {
|
|
88
88
|
"@react-native-community/eslint-config": "^3.2.0",
|
|
89
|
-
"@telnyx/react-native-voice-sdk": ">=0.4.
|
|
89
|
+
"@telnyx/react-native-voice-sdk": ">=0.4.5",
|
|
90
90
|
"eventemitter3": "^5.0.1",
|
|
91
91
|
"expo": "~53.0.22",
|
|
92
92
|
"react-native-url-polyfill": "^3.0.0",
|
|
@@ -178,39 +178,22 @@ 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
|
-
//
|
|
182
|
-
//
|
|
183
|
-
//
|
|
184
|
-
//
|
|
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;
|
|
181
|
+
// Each push always rebuilds the TelnyxRTC client. The voice_sdk_id is
|
|
182
|
+
// baked into the WebSocket URL at socket-open time and can't be mutated
|
|
183
|
+
// mid-flight; reusing a connection from a previous push means the
|
|
184
|
+
// gateway routes THIS push's INVITE to a different (correctly-stamped)
|
|
185
|
+
// client and we sit on the wrong socket forever.
|
|
196
186
|
if (this._telnyxClient) {
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
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
|
-
}
|
|
187
|
+
try {
|
|
188
|
+
await this._telnyxClient.disconnect();
|
|
189
|
+
} catch (err) {
|
|
190
|
+
console.warn('SessionManager: disconnect of prior client threw:', err);
|
|
212
191
|
}
|
|
213
192
|
}
|
|
193
|
+
this._telnyxClient = undefined;
|
|
194
|
+
if (this.currentState !== TelnyxConnectionState.DISCONNECTED) {
|
|
195
|
+
this._connectionState.next(TelnyxConnectionState.DISCONNECTED);
|
|
196
|
+
}
|
|
214
197
|
|
|
215
198
|
// If we don't have a config yet but we're processing a push notification,
|
|
216
199
|
// attempt to load stored config first (for terminated app startup)
|
|
@@ -401,22 +401,14 @@ export class TelnyxVoipClient {
|
|
|
401
401
|
}
|
|
402
402
|
|
|
403
403
|
try {
|
|
404
|
-
//
|
|
405
|
-
//
|
|
404
|
+
// SessionManager owns the entire push lifecycle: it disposes any prior
|
|
405
|
+
// client, loads stored credentials if needed, and rebuilds the
|
|
406
|
+
// TelnyxRTC bound to THIS push's voice_sdk_id. We deliberately do not
|
|
407
|
+
// fall back to a generic loginFromStoredConfig() here — that would
|
|
408
|
+
// create a parallel client with no voice_sdk_id awareness, which the
|
|
409
|
+
// gateway then has to `punt` once the SessionManager-driven session
|
|
410
|
+
// also registers, dropping the active call.
|
|
406
411
|
await this._sessionManager.handlePushNotification(payload);
|
|
407
|
-
|
|
408
|
-
// Connect if not already connected
|
|
409
|
-
const currentState = this.currentConnectionState;
|
|
410
|
-
if (currentState !== TelnyxConnectionState.CONNECTED) {
|
|
411
|
-
// Try to login from stored config - now the push flags should be set
|
|
412
|
-
const loginSuccess = await this.loginFromStoredConfig();
|
|
413
|
-
if (!loginSuccess) {
|
|
414
|
-
console.warn(
|
|
415
|
-
'TelnyxVoipClient: Could not login from stored config for push notification'
|
|
416
|
-
);
|
|
417
|
-
return;
|
|
418
|
-
}
|
|
419
|
-
}
|
|
420
412
|
} catch (error) {
|
|
421
413
|
console.error('TelnyxVoipClient: Error handling push notification:', error);
|
|
422
414
|
throw error;
|