@telnyx/react-voice-commons-sdk 0.1.8-beta.1 → 0.1.9

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,35 @@
1
1
  # CHANGELOG.md
2
2
 
3
+ ## [0.1.9](https://github.com/team-telnyx/react-native-voice-commons/releases/tag/0.1.9) (2026-03-19)
4
+
5
+ ### Enhancement
6
+
7
+ - Upgraded low-level SDK dependency from `@telnyx/react-native-voice-sdk@0.3.0` to `@telnyx/react-native-voice-sdk@0.4.0`
8
+ - Commons SDK version now propagates to the low-level SDK User-Agent via `sdkVersion` option
9
+ - Added hidden long-press button on status text to disable push notifications for debugging
10
+ - Disabled debug logging and call reports by default for production readiness
11
+
12
+ ### Bug Fixing
13
+
14
+ - Fixed stale push notification state by clearing pending VoIP push data on call end
15
+ - Fixed User-Agent inconsistency — all WebSocket messages now use `ReactNative-{version}` format
16
+ - Fixed unnecessary JSON-RPC ACK messages (invite, answer, ringing) that were causing issues
17
+ - Fixed `telnyxCallControlId` ReferenceError on inbound calls in the low-level SDK
18
+
19
+ ## [0.1.8](https://github.com/team-telnyx/react-native-voice-commons/releases/tag/0.1.8) (2026-03-09)
20
+
21
+ ### Bug Fixing
22
+
23
+ • Fixed duplicate CXProvider overwrite causing `CXEndCallAction` error 4 (unknownCallUUID) — guard `setupCallKit()` to prevent async `setupAutomatically()` from overwriting the provider created by `setupSynchronously()` during VoIP push handling
24
+ • Fixed intermittent audio loss on push notification calls — defer `CXAnswerCallAction.fulfill()` until `reportCallConnected()` when the WebRTC peer connection is ready
25
+ • Aligned iOS audio session handling with native Telnyx iOS SDK pattern (`RTCAudioSessionConfiguration.webRTC()` with `lockForConfiguration`/`unlockForConfiguration`)
26
+ • Fixed endCall not dismissing CallKit UI — check `endCall()` return value and fallback to `reportCallEnded()` when CXEndCallAction fails
27
+ • Fixed push notification answer crash when voipClient is not yet initialized — defer to `checkForInitialPushNotification()` instead of failing the call
28
+ • Added `voipClient.queueAnswerFromCallKit()` call when handling push notification answer for auto-answer on INVITE arrival
29
+ • Fixed call ENDED state not received by subscribers
30
+ • Added platform guards for iOS-only VoIP bridge methods on Android
31
+ • Fixed push data race condition in Expo apps
32
+
3
33
  ## [0.1.8-beta.1](https://github.com/team-telnyx/react-native-voice-commons/releases/tag/0.1.8-beta.1) (2026-03-04)
4
34
 
5
35
  ### Bug Fixing
@@ -11,6 +41,12 @@
11
41
  • Fixed push notification answer crash when voipClient is not yet initialized — defer to `checkForInitialPushNotification()` instead of failing the call
12
42
  • Added `voipClient.queueAnswerFromCallKit()` call when handling push notification answer for auto-answer on INVITE arrival
13
43
 
44
+ ## [0.1.8-beta.0](https://github.com/team-telnyx/react-native-voice-commons/releases/tag/0.1.8-beta.0) (2026-02-28)
45
+
46
+ ### Bug Fixing
47
+
48
+ • Fixed push data race condition in Expo apps
49
+
14
50
  ## [0.1.7](https://github.com/team-telnyx/react-native-voice-commons/releases/tag/0.1.7) (2026-02-20)
15
51
 
16
52
  ### Enhancement
package/README.md CHANGED
@@ -84,7 +84,7 @@ destroyTelnyxVoipClient(); // Disposes the singleton; next createTelnyxVoipClien
84
84
  **Configuration Options Explained:**
85
85
 
86
86
  - **`enableAppStateManagement: true`** - **Optional (default: true)**: Enables automatic background/foreground app state management. When enabled, the library automatically disconnects when the app goes to background (unless there's an active call) and handles reconnection logic. Set to `false` if you want to handle app lifecycle manually.
87
- - **`debug: true`** - **Optional**: Enables detailed logging for connection states, call transitions, and push notification processing. Useful for development and troubleshooting.
87
+ - **`debug: true`** - **Optional (default: false)**: Enables detailed logging for connection states, call transitions, and push notification processing. Useful for development and troubleshooting.
88
88
 
89
89
  ### 2. TelnyxVoiceApp Wrapper
90
90
 
@@ -6,6 +6,7 @@ const operators_1 = require('rxjs/operators');
6
6
  const call_1 = require('../../models/call');
7
7
  const call_state_1 = require('../../models/call-state');
8
8
  const callkit_coordinator_1 = require('../../callkit/callkit-coordinator');
9
+ const voice_pn_bridge_1 = require('../voice-pn-bridge');
9
10
  /**
10
11
  * Central state machine for call management.
11
12
  *
@@ -343,12 +344,17 @@ class CallStateController {
343
344
  call.callState$.subscribe((state) => {
344
345
  // CallKitCoordinator automatically updates CallKit via setupWebRTCCallListeners
345
346
  console.log('CallStateController: Call state changed to:', state);
346
- // Clean up when call ends
347
+ // Clean up when call ends - delay to next tick so external subscribers
348
+ // receive the ENDED/FAILED state before the call is disposed
347
349
  if (
348
350
  state === call_state_1.TelnyxCallState.ENDED ||
349
351
  state === call_state_1.TelnyxCallState.FAILED
350
352
  ) {
351
- this._removeCall(call.callId);
353
+ // Clear pending push data so the next app launch isn't mistaken for a push launch
354
+ voice_pn_bridge_1.VoicePnBridge.clearPendingVoipPush().catch((e) =>
355
+ console.warn('CallStateController: Failed to clear pending voip push:', e)
356
+ );
357
+ setTimeout(() => this._removeCall(call.callId), 0);
352
358
  }
353
359
  });
354
360
  }
@@ -56,6 +56,7 @@ exports.SessionManager = void 0;
56
56
  const rxjs_1 = require('rxjs');
57
57
  const operators_1 = require('rxjs/operators');
58
58
  const TelnyxSDK = __importStar(require('@telnyx/react-native-voice-sdk'));
59
+ const pkg = __importStar(require('../../../package.json'));
59
60
  const connection_state_1 = require('../../models/connection-state');
60
61
  const config_1 = require('../../models/config');
61
62
  /**
@@ -313,7 +314,13 @@ class SessionManager {
313
314
  login: this._currentConfig.sipUser,
314
315
  password: this._currentConfig.sipPassword,
315
316
  logLevel: this._currentConfig.debug ? 'debug' : 'warn',
317
+ debug: this._currentConfig.debug ?? false,
316
318
  pushNotificationDeviceToken: this._currentConfig.pushNotificationDeviceToken,
319
+ enableCallReports: this._currentConfig.enableCallReports,
320
+ callReportInterval: this._currentConfig.callReportInterval,
321
+ callReportLogLevel: this._currentConfig.callReportLogLevel,
322
+ callReportMaxLogEntries: this._currentConfig.callReportMaxLogEntries,
323
+ sdkVersion: pkg.version,
317
324
  };
318
325
  console.log(
319
326
  '🔧 SessionManager: Creating TelnyxRTC with credential config, logLevel:',
@@ -325,7 +332,13 @@ class SessionManager {
325
332
  clientOptions = {
326
333
  login_token: this._currentConfig.token,
327
334
  logLevel: this._currentConfig.debug ? 'debug' : 'warn',
335
+ debug: this._currentConfig.debug ?? false,
328
336
  pushNotificationDeviceToken: this._currentConfig.pushNotificationDeviceToken,
337
+ enableCallReports: this._currentConfig.enableCallReports,
338
+ callReportInterval: this._currentConfig.callReportInterval,
339
+ callReportLogLevel: this._currentConfig.callReportLogLevel,
340
+ callReportMaxLogEntries: this._currentConfig.callReportMaxLogEntries,
341
+ sdkVersion: pkg.version,
329
342
  };
330
343
  console.log(
331
344
  '🔧 SessionManager: Creating TelnyxRTC with token config, logLevel:',
@@ -92,19 +92,19 @@ export declare class VoicePnBridge {
92
92
  */
93
93
  static getVoipToken(): Promise<string | null>;
94
94
  /**
95
- * Get pending VoIP push from native storage
95
+ * Get pending VoIP push from native storage (iOS only)
96
96
  */
97
97
  static getPendingVoipPush(): Promise<string | null>;
98
98
  /**
99
- * Clear pending VoIP push from native storage
99
+ * Clear pending VoIP push from native storage (iOS only)
100
100
  */
101
101
  static clearPendingVoipPush(): Promise<boolean>;
102
102
  /**
103
- * Get pending VoIP action from native storage
103
+ * Get pending VoIP action from native storage (iOS only)
104
104
  */
105
105
  static getPendingVoipAction(): Promise<string | null>;
106
106
  /**
107
- * Clear pending VoIP action from native storage
107
+ * Clear pending VoIP action from native storage (iOS only)
108
108
  */
109
109
  static clearPendingVoipAction(): Promise<boolean>;
110
110
  /**
@@ -128,6 +128,7 @@ class VoicePnBridge {
128
128
  * Get VoIP token from native storage
129
129
  */
130
130
  static async getVoipToken() {
131
+ if (react_native_1.Platform.OS !== 'ios') return null;
131
132
  try {
132
133
  return await NativeBridge.getVoipToken();
133
134
  } catch (error) {
@@ -136,9 +137,10 @@ class VoicePnBridge {
136
137
  }
137
138
  }
138
139
  /**
139
- * Get pending VoIP push from native storage
140
+ * Get pending VoIP push from native storage (iOS only)
140
141
  */
141
142
  static async getPendingVoipPush() {
143
+ if (react_native_1.Platform.OS !== 'ios') return null;
142
144
  try {
143
145
  return await NativeBridge.getPendingVoipPush();
144
146
  } catch (error) {
@@ -147,9 +149,10 @@ class VoicePnBridge {
147
149
  }
148
150
  }
149
151
  /**
150
- * Clear pending VoIP push from native storage
152
+ * Clear pending VoIP push from native storage (iOS only)
151
153
  */
152
154
  static async clearPendingVoipPush() {
155
+ if (react_native_1.Platform.OS !== 'ios') return true;
153
156
  try {
154
157
  return await NativeBridge.clearPendingVoipPush();
155
158
  } catch (error) {
@@ -158,9 +161,10 @@ class VoicePnBridge {
158
161
  }
159
162
  }
160
163
  /**
161
- * Get pending VoIP action from native storage
164
+ * Get pending VoIP action from native storage (iOS only)
162
165
  */
163
166
  static async getPendingVoipAction() {
167
+ if (react_native_1.Platform.OS !== 'ios') return null;
164
168
  try {
165
169
  return await NativeBridge.getPendingVoipAction();
166
170
  } catch (error) {
@@ -169,9 +173,10 @@ class VoicePnBridge {
169
173
  }
170
174
  }
171
175
  /**
172
- * Clear pending VoIP action from native storage
176
+ * Clear pending VoIP action from native storage (iOS only)
173
177
  */
174
178
  static async clearPendingVoipAction() {
179
+ if (react_native_1.Platform.OS !== 'ios') return true;
175
180
  try {
176
181
  return await NativeBridge.clearPendingVoipAction();
177
182
  } catch (error) {
@@ -7,6 +7,14 @@ export interface CredentialConfig {
7
7
  sipPassword: string;
8
8
  debug?: boolean;
9
9
  pushNotificationDeviceToken?: string;
10
+ /** Enable automatic call quality reporting. Default: true */
11
+ enableCallReports?: boolean;
12
+ /** Stats collection interval in seconds. Default: 5 */
13
+ callReportInterval?: number;
14
+ /** Minimum log level for call reports: 'debug' | 'info' | 'warn' | 'error'. Default: 'debug' */
15
+ callReportLogLevel?: string;
16
+ /** Max log entries per call. Default: 1000 */
17
+ callReportMaxLogEntries?: number;
10
18
  }
11
19
  /**
12
20
  * Configuration for token-based authentication
@@ -16,6 +24,14 @@ export interface TokenConfig {
16
24
  token: string;
17
25
  debug?: boolean;
18
26
  pushNotificationDeviceToken?: string;
27
+ /** Enable automatic call quality reporting. Default: true */
28
+ enableCallReports?: boolean;
29
+ /** Stats collection interval in seconds. Default: 5 */
30
+ callReportInterval?: number;
31
+ /** Minimum log level for call reports: 'debug' | 'info' | 'warn' | 'error'. Default: 'debug' */
32
+ callReportLogLevel?: string;
33
+ /** Max log entries per call. Default: 1000 */
34
+ callReportMaxLogEntries?: number;
19
35
  }
20
36
  /**
21
37
  * Union type for all supported authentication configurations
@@ -612,7 +612,7 @@ const handleBackgroundPush = async (message) => {
612
612
  // TODO: Initialize push notification service in isolate if needed
613
613
  // Use singleton pattern for background client to prevent multiple instances
614
614
  let backgroundClient = (0, telnyx_voip_client_1.createBackgroundTelnyxVoipClient)({
615
- debug: true,
615
+ debug: false,
616
616
  });
617
617
  await backgroundClient.handlePushNotification(message);
618
618
  console.log('[TelnyxVoiceApp] Background push processed successfully');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@telnyx/react-voice-commons-sdk",
3
- "version": "0.1.8-beta.1",
3
+ "version": "0.1.9",
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",
@@ -39,7 +39,7 @@
39
39
  "android": "expo run:android",
40
40
  "ios": "expo run:ios",
41
41
  "dev:local": "npm pkg set dependencies.@telnyx/react-native-voice-sdk=file:../package",
42
- "dev:published": "npm pkg set dependencies.@telnyx/react-native-voice-sdk=^0.3.0",
42
+ "dev:published": "npm pkg set dependencies.@telnyx/react-native-voice-sdk=^0.4.0",
43
43
  "prepublishOnly": "npm run dev:published && npm install --legacy-peer-deps",
44
44
  "postpublish": "npm run dev:local && npm install --legacy-peer-deps"
45
45
  },
@@ -90,7 +90,7 @@
90
90
  },
91
91
  "dependencies": {
92
92
  "@react-native-community/eslint-config": "^3.2.0",
93
- "@telnyx/react-native-voice-sdk": "^0.3.0",
93
+ "@telnyx/react-native-voice-sdk": "^0.4.0",
94
94
  "eventemitter3": "^5.0.1",
95
95
  "expo": "~53.0.22",
96
96
  "react-native-voip-push-notification": "^3.3.3",
@@ -5,6 +5,7 @@ import { Call } from '../../models/call';
5
5
  import { TelnyxCallState } from '../../models/call-state';
6
6
  import { SessionManager } from '../session/session-manager';
7
7
  import { callKitCoordinator } from '../../callkit/callkit-coordinator';
8
+ import { VoicePnBridge } from '../voice-pn-bridge';
8
9
 
9
10
  /**
10
11
  * Central state machine for call management.
@@ -405,9 +406,14 @@ export class CallStateController {
405
406
  // CallKitCoordinator automatically updates CallKit via setupWebRTCCallListeners
406
407
  console.log('CallStateController: Call state changed to:', state);
407
408
 
408
- // Clean up when call ends
409
+ // Clean up when call ends - delay to next tick so external subscribers
410
+ // receive the ENDED/FAILED state before the call is disposed
409
411
  if (state === TelnyxCallState.ENDED || state === TelnyxCallState.FAILED) {
410
- this._removeCall(call.callId);
412
+ // Clear pending push data so the next app launch isn't mistaken for a push launch
413
+ VoicePnBridge.clearPendingVoipPush().catch((e) =>
414
+ console.warn('CallStateController: Failed to clear pending voip push:', e)
415
+ );
416
+ setTimeout(() => this._removeCall(call.callId), 0);
411
417
  }
412
418
  });
413
419
  }
@@ -1,6 +1,7 @@
1
1
  import { BehaviorSubject, Observable } from 'rxjs';
2
2
  import { distinctUntilChanged } from 'rxjs/operators';
3
3
  import * as TelnyxSDK from '@telnyx/react-native-voice-sdk';
4
+ import * as pkg from '../../../package.json';
4
5
  import { TelnyxConnectionState } from '../../models/connection-state';
5
6
  import {
6
7
  Config,
@@ -302,7 +303,13 @@ export class SessionManager {
302
303
  login: this._currentConfig.sipUser,
303
304
  password: this._currentConfig.sipPassword,
304
305
  logLevel: this._currentConfig.debug ? 'debug' : 'warn',
306
+ debug: this._currentConfig.debug ?? false,
305
307
  pushNotificationDeviceToken: this._currentConfig.pushNotificationDeviceToken,
308
+ enableCallReports: this._currentConfig.enableCallReports,
309
+ callReportInterval: this._currentConfig.callReportInterval,
310
+ callReportLogLevel: this._currentConfig.callReportLogLevel,
311
+ callReportMaxLogEntries: this._currentConfig.callReportMaxLogEntries,
312
+ sdkVersion: pkg.version,
306
313
  };
307
314
  console.log(
308
315
  '🔧 SessionManager: Creating TelnyxRTC with credential config, logLevel:',
@@ -314,7 +321,13 @@ export class SessionManager {
314
321
  clientOptions = {
315
322
  login_token: this._currentConfig.token,
316
323
  logLevel: this._currentConfig.debug ? 'debug' : 'warn',
324
+ debug: this._currentConfig.debug ?? false,
317
325
  pushNotificationDeviceToken: this._currentConfig.pushNotificationDeviceToken,
326
+ enableCallReports: this._currentConfig.enableCallReports,
327
+ callReportInterval: this._currentConfig.callReportInterval,
328
+ callReportLogLevel: this._currentConfig.callReportLogLevel,
329
+ callReportMaxLogEntries: this._currentConfig.callReportMaxLogEntries,
330
+ sdkVersion: pkg.version,
318
331
  };
319
332
  console.log(
320
333
  '🔧 SessionManager: Creating TelnyxRTC with token config, logLevel:',
@@ -1,4 +1,4 @@
1
- import { NativeModules, DeviceEventEmitter, EmitterSubscription } from 'react-native';
1
+ import { NativeModules, DeviceEventEmitter, EmitterSubscription, Platform } from 'react-native';
2
2
 
3
3
  export interface CallActionEvent {
4
4
  action: string;
@@ -183,6 +183,7 @@ export class VoicePnBridge {
183
183
  * Get VoIP token from native storage
184
184
  */
185
185
  static async getVoipToken(): Promise<string | null> {
186
+ if (Platform.OS !== 'ios') return null;
186
187
  try {
187
188
  return await NativeBridge.getVoipToken();
188
189
  } catch (error) {
@@ -192,9 +193,10 @@ export class VoicePnBridge {
192
193
  }
193
194
 
194
195
  /**
195
- * Get pending VoIP push from native storage
196
+ * Get pending VoIP push from native storage (iOS only)
196
197
  */
197
198
  static async getPendingVoipPush(): Promise<string | null> {
199
+ if (Platform.OS !== 'ios') return null;
198
200
  try {
199
201
  return await NativeBridge.getPendingVoipPush();
200
202
  } catch (error) {
@@ -204,9 +206,10 @@ export class VoicePnBridge {
204
206
  }
205
207
 
206
208
  /**
207
- * Clear pending VoIP push from native storage
209
+ * Clear pending VoIP push from native storage (iOS only)
208
210
  */
209
211
  static async clearPendingVoipPush(): Promise<boolean> {
212
+ if (Platform.OS !== 'ios') return true;
210
213
  try {
211
214
  return await NativeBridge.clearPendingVoipPush();
212
215
  } catch (error) {
@@ -216,9 +219,10 @@ export class VoicePnBridge {
216
219
  }
217
220
 
218
221
  /**
219
- * Get pending VoIP action from native storage
222
+ * Get pending VoIP action from native storage (iOS only)
220
223
  */
221
224
  static async getPendingVoipAction(): Promise<string | null> {
225
+ if (Platform.OS !== 'ios') return null;
222
226
  try {
223
227
  return await NativeBridge.getPendingVoipAction();
224
228
  } catch (error) {
@@ -228,9 +232,10 @@ export class VoicePnBridge {
228
232
  }
229
233
 
230
234
  /**
231
- * Clear pending VoIP action from native storage
235
+ * Clear pending VoIP action from native storage (iOS only)
232
236
  */
233
237
  static async clearPendingVoipAction(): Promise<boolean> {
238
+ if (Platform.OS !== 'ios') return true;
234
239
  try {
235
240
  return await NativeBridge.clearPendingVoipAction();
236
241
  } catch (error) {
@@ -7,6 +7,14 @@ export interface CredentialConfig {
7
7
  sipPassword: string;
8
8
  debug?: boolean;
9
9
  pushNotificationDeviceToken?: string;
10
+ /** Enable automatic call quality reporting. Default: true */
11
+ enableCallReports?: boolean;
12
+ /** Stats collection interval in seconds. Default: 5 */
13
+ callReportInterval?: number;
14
+ /** Minimum log level for call reports: 'debug' | 'info' | 'warn' | 'error'. Default: 'debug' */
15
+ callReportLogLevel?: string;
16
+ /** Max log entries per call. Default: 1000 */
17
+ callReportMaxLogEntries?: number;
10
18
  }
11
19
 
12
20
  /**
@@ -17,6 +25,14 @@ export interface TokenConfig {
17
25
  token: string;
18
26
  debug?: boolean;
19
27
  pushNotificationDeviceToken?: string;
28
+ /** Enable automatic call quality reporting. Default: true */
29
+ enableCallReports?: boolean;
30
+ /** Stats collection interval in seconds. Default: 5 */
31
+ callReportInterval?: number;
32
+ /** Minimum log level for call reports: 'debug' | 'info' | 'warn' | 'error'. Default: 'debug' */
33
+ callReportLogLevel?: string;
34
+ /** Max log entries per call. Default: 1000 */
35
+ callReportMaxLogEntries?: number;
20
36
  }
21
37
 
22
38
  /**
@@ -437,7 +437,9 @@ const TelnyxVoiceAppComponent: React.FC<TelnyxVoiceAppProps> = ({
437
437
  voipClient.currentConnectionState === TelnyxConnectionState.CONNECTED ||
438
438
  voipClient.currentConnectionState === TelnyxConnectionState.CONNECTING
439
439
  ) {
440
- log(`SKIPPING - Already ${voipClient.currentConnectionState}, preventing duplicate processing`);
440
+ log(
441
+ `SKIPPING - Already ${voipClient.currentConnectionState}, preventing duplicate processing`
442
+ );
441
443
  return;
442
444
  }
443
445
 
@@ -773,7 +775,7 @@ const handleBackgroundPush = async (message: any): Promise<void> => {
773
775
 
774
776
  // Use singleton pattern for background client to prevent multiple instances
775
777
  let backgroundClient = createBackgroundTelnyxVoipClient({
776
- debug: true,
778
+ debug: false,
777
779
  });
778
780
 
779
781
  await backgroundClient.handlePushNotification(message);
@@ -26,6 +26,11 @@ declare module '@telnyx/react-native-voice-sdk' {
26
26
  debug?: boolean;
27
27
  logLevel?: string;
28
28
  pushNotificationDeviceToken?: string;
29
+ enableCallReports?: boolean;
30
+ callReportInterval?: number;
31
+ callReportLogLevel?: string;
32
+ callReportMaxLogEntries?: number;
33
+ sdkVersion?: string;
29
34
  }
30
35
 
31
36
  export enum CallState {