@telnyx/react-voice-commons-sdk 0.4.3 → 1.1.0

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.
Files changed (47) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/README.md +28 -7
  3. package/android/src/main/java/com/telnyx/react_voice_commons/CallForegroundService.kt +16 -2
  4. package/android/src/main/java/com/telnyx/react_voice_commons/TelnyxFirebaseMessagingService.kt +16 -2
  5. package/android/src/main/java/com/telnyx/react_voice_commons/TelnyxNotificationHelper.kt +345 -24
  6. package/android/src/main/java/com/telnyx/react_voice_commons/VoicePnBridgeModule.kt +74 -1
  7. package/android/src/main/java/com/telnyx/react_voice_commons/VoicePnManager.kt +36 -1
  8. package/ios/CallKitBridge.m +20 -1
  9. package/ios/CallKitBridge.swift +935 -60
  10. package/ios/README.md +2 -2
  11. package/ios/VoicePnBridge.m +21 -1
  12. package/ios/VoicePnBridge.swift +62 -1
  13. package/lib/callkit/callkit-coordinator.d.ts +39 -3
  14. package/lib/callkit/callkit-coordinator.js +561 -148
  15. package/lib/callkit/callkit.d.ts +7 -0
  16. package/lib/callkit/callkit.js +83 -11
  17. package/lib/hooks/useAppStateHandler.js +6 -2
  18. package/lib/index.d.ts +1 -0
  19. package/lib/index.js +9 -0
  20. package/lib/internal/CallKitHandler.d.ts +3 -0
  21. package/lib/internal/CallKitHandler.js +52 -46
  22. package/lib/internal/calls/call-state-controller.d.ts +39 -1
  23. package/lib/internal/calls/call-state-controller.js +152 -64
  24. package/lib/internal/session/session-manager.d.ts +16 -1
  25. package/lib/internal/session/session-manager.js +229 -58
  26. package/lib/internal/voice-pn-bridge.d.ts +44 -0
  27. package/lib/internal/voice-pn-bridge.js +94 -0
  28. package/lib/models/call.js +35 -0
  29. package/lib/models/config.d.ts +18 -0
  30. package/lib/models/config.js +2 -0
  31. package/lib/telnyx-voice-app.js +85 -43
  32. package/lib/telnyx-voip-client.d.ts +48 -5
  33. package/lib/telnyx-voip-client.js +276 -22
  34. package/package.json +7 -5
  35. package/src/callkit/callkit-coordinator.ts +642 -160
  36. package/src/callkit/callkit.ts +110 -12
  37. package/src/hooks/useAppStateHandler.ts +7 -2
  38. package/src/index.ts +3 -0
  39. package/src/internal/CallKitHandler.tsx +60 -55
  40. package/src/internal/calls/call-state-controller.ts +173 -74
  41. package/src/internal/session/session-manager.ts +277 -61
  42. package/src/internal/voice-pn-bridge.ts +111 -0
  43. package/src/models/call.ts +34 -0
  44. package/src/models/config.ts +18 -0
  45. package/src/telnyx-voice-app.tsx +96 -45
  46. package/src/telnyx-voip-client.ts +260 -24
  47. package/src/types/telnyx-sdk.d.ts +12 -1
@@ -1,15 +1,72 @@
1
1
  'use strict';
2
+ var __createBinding =
3
+ (this && this.__createBinding) ||
4
+ (Object.create
5
+ ? function (o, m, k, k2) {
6
+ if (k2 === undefined) k2 = k;
7
+ var desc = Object.getOwnPropertyDescriptor(m, k);
8
+ if (!desc || ('get' in desc ? !m.__esModule : desc.writable || desc.configurable)) {
9
+ desc = {
10
+ enumerable: true,
11
+ get: function () {
12
+ return m[k];
13
+ },
14
+ };
15
+ }
16
+ Object.defineProperty(o, k2, desc);
17
+ }
18
+ : function (o, m, k, k2) {
19
+ if (k2 === undefined) k2 = k;
20
+ o[k2] = m[k];
21
+ });
22
+ var __setModuleDefault =
23
+ (this && this.__setModuleDefault) ||
24
+ (Object.create
25
+ ? function (o, v) {
26
+ Object.defineProperty(o, 'default', { enumerable: true, value: v });
27
+ }
28
+ : function (o, v) {
29
+ o['default'] = v;
30
+ });
31
+ var __importStar =
32
+ (this && this.__importStar) ||
33
+ (function () {
34
+ var ownKeys = function (o) {
35
+ ownKeys =
36
+ Object.getOwnPropertyNames ||
37
+ function (o) {
38
+ var ar = [];
39
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
40
+ return ar;
41
+ };
42
+ return ownKeys(o);
43
+ };
44
+ return function (mod) {
45
+ if (mod && mod.__esModule) return mod;
46
+ var result = {};
47
+ if (mod != null)
48
+ for (var k = ownKeys(mod), i = 0; i < k.length; i++)
49
+ if (k[i] !== 'default') __createBinding(result, mod, k[i]);
50
+ __setModuleDefault(result, mod);
51
+ return result;
52
+ };
53
+ })();
2
54
  Object.defineProperty(exports, '__esModule', { value: true });
3
55
  exports.TelnyxVoipClient = void 0;
4
56
  exports.createTelnyxVoipClient = createTelnyxVoipClient;
5
57
  exports.destroyTelnyxVoipClient = destroyTelnyxVoipClient;
6
58
  exports.createBackgroundTelnyxVoipClient = createBackgroundTelnyxVoipClient;
59
+ const react_native_1 = require('react-native');
7
60
  const connection_state_1 = require('./models/connection-state');
8
61
  const call_state_1 = require('./models/call-state');
9
62
  const config_1 = require('./models/config');
10
63
  const session_manager_1 = require('./internal/session/session-manager');
11
64
  const call_state_controller_1 = require('./internal/calls/call-state-controller');
12
65
  const voice_pn_bridge_1 = require('./internal/voice-pn-bridge');
66
+ const USE_TRICKLE_ICE_STORAGE_KEY = '@use_trickle_ice';
67
+ const PUSH_WHEN_ACTIVE_STORAGE_KEY = '@push_when_active';
68
+ const MISSED_CALL_NOTIFICATIONS_STORAGE_KEY = '@enable_missed_call_notifications';
69
+ const LEGACY_CALLKIT_ANSWER_KEY = '__legacy_callkit_answer__';
13
70
  /**
14
71
  * The main public interface for the react-voice-commons module.
15
72
  *
@@ -48,10 +105,12 @@ class TelnyxVoipClient {
48
105
  * @param options Configuration options for the client
49
106
  */
50
107
  constructor(options = {}) {
108
+ this._pendingCallKitAnswers = new Map();
51
109
  this._disposed = false;
52
110
  this._options = {
53
111
  enableAppStateManagement: true,
54
112
  debug: false,
113
+ useTrickleIce: false,
55
114
  ...options,
56
115
  };
57
116
  // Initialize core components
@@ -64,6 +123,7 @@ class TelnyxVoipClient {
64
123
  console.log(
65
124
  '🔧 TelnyxVoipClient: Client ready, initializing call state controller listeners'
66
125
  );
126
+ this._flushPendingCallKitAnswers();
67
127
  this._callStateController.initializeClientListeners();
68
128
  });
69
129
  // Clear any tracked calls when the session disconnects, so ghosts
@@ -154,6 +214,66 @@ class TelnyxVoipClient {
154
214
  getCall(callId) {
155
215
  return this._callStateController.getCall(callId);
156
216
  }
217
+ /**
218
+ * Explicitly set the active call for multi-call scenarios.
219
+ * @param callId The ID of the call to mark as active
220
+ */
221
+ setActiveCall(callId) {
222
+ this._throwIfDisposed();
223
+ this._callStateController.setActiveCall(callId);
224
+ }
225
+ /**
226
+ * Clear the explicitly selected active call and return to default selection.
227
+ */
228
+ clearActiveCall() {
229
+ this._throwIfDisposed();
230
+ this._callStateController.clearActiveCall();
231
+ }
232
+ /**
233
+ * Swap the current active call with a held call.
234
+ * On iOS this is coordinated through CallKit so native and SDK state stay aligned.
235
+ *
236
+ * @param targetCallId ID of the held call to make active
237
+ */
238
+ async swapCalls(targetCallId) {
239
+ this._throwIfDisposed();
240
+ const activeCall = this.currentActiveCall;
241
+ const heldCall = this.getCall(targetCallId);
242
+ if (!activeCall || !heldCall || activeCall.callId === heldCall.callId) {
243
+ throw new Error('An active call and a different held call are required to swap calls');
244
+ }
245
+ if (activeCall.currentState !== call_state_1.TelnyxCallState.ACTIVE) {
246
+ throw new Error(`Cannot swap active call in state: ${activeCall.currentState}`);
247
+ }
248
+ if (heldCall.currentState !== call_state_1.TelnyxCallState.HELD) {
249
+ throw new Error(`Cannot swap target call in state: ${heldCall.currentState}`);
250
+ }
251
+ if (react_native_1.Platform.OS === 'ios') {
252
+ const { callKitCoordinator } = await Promise.resolve().then(() =>
253
+ __importStar(require('./callkit/callkit-coordinator'))
254
+ );
255
+ if (callKitCoordinator.isAvailable()) {
256
+ const success = await callKitCoordinator.swapCallsFromUI(
257
+ activeCall.telnyxCall,
258
+ heldCall.telnyxCall
259
+ );
260
+ if (!success) {
261
+ throw new Error('CallKit failed to swap calls');
262
+ }
263
+ return;
264
+ }
265
+ }
266
+ await activeCall.hold();
267
+ try {
268
+ await heldCall.resume();
269
+ this.setActiveCall(heldCall.callId);
270
+ } catch (error) {
271
+ await activeCall.resume().catch((resumeError) => {
272
+ console.error('Failed to restore active call after swap failure', resumeError);
273
+ });
274
+ throw error;
275
+ }
276
+ }
157
277
  /**
158
278
  * Current session ID (UUID) for this connection.
159
279
  */
@@ -185,9 +305,14 @@ class TelnyxVoipClient {
185
305
  if (this._options.debug) {
186
306
  console.log('TelnyxVoipClient: Logging in with credentials for user:', config.sipUser);
187
307
  }
308
+ const loginConfig = await this._withNativeVoipPushToken({
309
+ ...config,
310
+ useTrickleIce: config.useTrickleIce ?? this._options.useTrickleIce,
311
+ });
312
+ await voice_pn_bridge_1.VoicePnBridge.setIncomingCallRingtone(loginConfig.incomingCallRingtone);
188
313
  // Store credentials for future reconnection
189
- await this._storeCredentials(config);
190
- await this._sessionManager.connectWithCredential(config);
314
+ await this._storeCredentials(loginConfig);
315
+ await this._sessionManager.connectWithCredential(loginConfig);
191
316
  }
192
317
  /**
193
318
  * Connects to the Telnyx platform using token authentication.
@@ -207,9 +332,14 @@ class TelnyxVoipClient {
207
332
  if (this._options.debug) {
208
333
  console.log('TelnyxVoipClient: Logging in with token');
209
334
  }
335
+ const loginConfig = await this._withNativeVoipPushToken({
336
+ ...config,
337
+ useTrickleIce: config.useTrickleIce ?? this._options.useTrickleIce,
338
+ });
339
+ await voice_pn_bridge_1.VoicePnBridge.setIncomingCallRingtone(loginConfig.incomingCallRingtone);
210
340
  // Store token for future reconnection
211
- await this._storeToken(config);
212
- await this._sessionManager.connectWithToken(config);
341
+ await this._storeToken(loginConfig);
342
+ await this._sessionManager.connectWithToken(loginConfig);
213
343
  }
214
344
  /**
215
345
  * Disconnects from the Telnyx platform.
@@ -246,12 +376,32 @@ class TelnyxVoipClient {
246
376
  const storedPassword = await AsyncStorage.getItem('@telnyx_password');
247
377
  const storedCredentialToken = await AsyncStorage.getItem('@credential_token');
248
378
  const storedPushToken = await AsyncStorage.getItem('@push_token');
379
+ const storedUseTrickleIce = await AsyncStorage.getItem(USE_TRICKLE_ICE_STORAGE_KEY);
380
+ const storedPushWhenActive = await AsyncStorage.getItem(PUSH_WHEN_ACTIVE_STORAGE_KEY);
381
+ const storedMissedCallNotifications = await AsyncStorage.getItem(
382
+ MISSED_CALL_NOTIFICATIONS_STORAGE_KEY
383
+ );
384
+ const useTrickleIce =
385
+ storedUseTrickleIce === null ? this._options.useTrickleIce : storedUseTrickleIce === 'true';
386
+ const pushWhenActive = storedPushWhenActive === 'true';
387
+ const enableMissedCallNotifications = storedMissedCallNotifications === 'true';
388
+ const nativePushToken =
389
+ react_native_1.Platform.OS === 'ios'
390
+ ? (await voice_pn_bridge_1.VoicePnBridge.getVoipToken())?.trim()
391
+ : null;
392
+ const pushNotificationDeviceToken = nativePushToken || storedPushToken;
393
+ if (nativePushToken && nativePushToken !== storedPushToken) {
394
+ await AsyncStorage.setItem('@push_token', nativePushToken);
395
+ }
249
396
  // Check if we have credential-based authentication data
250
397
  if (storedUsername && storedPassword) {
251
398
  // Create credential config from stored data
252
399
  const { createCredentialConfig } = require('./models/config');
253
400
  const config = createCredentialConfig(storedUsername, storedPassword, {
254
- pushNotificationDeviceToken: storedPushToken,
401
+ pushNotificationDeviceToken,
402
+ useTrickleIce,
403
+ pushWhenActive,
404
+ enableMissedCallNotifications,
255
405
  });
256
406
  if (this._options.debug) {
257
407
  console.log(
@@ -267,7 +417,10 @@ class TelnyxVoipClient {
267
417
  // Create token config from stored data
268
418
  const { createTokenConfig } = require('./models/config');
269
419
  const config = createTokenConfig(storedCredentialToken, {
270
- pushNotificationDeviceToken: storedPushToken,
420
+ pushNotificationDeviceToken,
421
+ useTrickleIce,
422
+ pushWhenActive,
423
+ enableMissedCallNotifications,
271
424
  });
272
425
  if (this._options.debug) {
273
426
  console.log('TelnyxVoipClient: Reconnecting with stored token');
@@ -381,38 +534,60 @@ class TelnyxVoipClient {
381
534
  * This should be called when the user answers from CallKit before the socket connection is established
382
535
  * @param customHeaders Optional custom headers to include with the answer
383
536
  */
384
- queueAnswerFromCallKit(customHeaders = {}) {
537
+ queueAnswerFromCallKit(callKitUUIDOrHeaders, customHeaders = {}) {
385
538
  this._throwIfDisposed();
386
539
  if (this._options.debug) {
387
- console.log('TelnyxVoipClient: Queuing answer action from CallKit', customHeaders);
540
+ console.log('TelnyxVoipClient: Queuing answer action from CallKit', {
541
+ callKitUUIDOrHeaders,
542
+ customHeaders,
543
+ });
388
544
  }
389
545
  const telnyxClient = this._sessionManager.telnyxClient;
390
546
  if (telnyxClient && typeof telnyxClient.queueAnswerFromCallKit === 'function') {
391
- telnyxClient.queueAnswerFromCallKit(customHeaders);
547
+ telnyxClient.queueAnswerFromCallKit(callKitUUIDOrHeaders, customHeaders);
392
548
  } else {
393
- console.warn(
394
- 'TelnyxVoipClient: TelnyxRTC client not available or method not found for queueAnswerFromCallKit'
395
- );
549
+ const normalizedUUID =
550
+ typeof callKitUUIDOrHeaders === 'string' ? callKitUUIDOrHeaders.toLowerCase() : undefined;
551
+ const actionKey = normalizedUUID ?? LEGACY_CALLKIT_ANSWER_KEY;
552
+ let retainedUUIDOrHeaders = normalizedUUID;
553
+ if (callKitUUIDOrHeaders && typeof callKitUUIDOrHeaders !== 'string') {
554
+ retainedUUIDOrHeaders = { ...callKitUUIDOrHeaders };
555
+ }
556
+ this._pendingCallKitAnswers.set(actionKey, {
557
+ callKitUUIDOrHeaders: retainedUUIDOrHeaders,
558
+ customHeaders: { ...customHeaders },
559
+ });
396
560
  }
397
561
  }
398
562
  /**
399
563
  * Queue an end action for when the call invite arrives (for CallKit integration)
400
564
  * This should be called when the user ends from CallKit before the socket connection is established
401
565
  */
402
- queueEndFromCallKit() {
566
+ queueEndFromCallKit(callKitUUID) {
403
567
  this._throwIfDisposed();
404
568
  if (this._options.debug) {
405
569
  console.log('TelnyxVoipClient: Queuing end action from CallKit');
406
570
  }
407
571
  const telnyxClient = this._sessionManager.telnyxClient;
408
572
  if (telnyxClient && typeof telnyxClient.queueEndFromCallKit === 'function') {
409
- telnyxClient.queueEndFromCallKit();
573
+ telnyxClient.queueEndFromCallKit(callKitUUID);
410
574
  } else {
411
575
  console.warn(
412
576
  'TelnyxVoipClient: TelnyxRTC client not available or method not found for queueEndFromCallKit'
413
577
  );
414
578
  }
415
579
  }
580
+ /**
581
+ * Associate the next push-delivered INVITE with its app-facing CallKit UUID.
582
+ * The underlying signaling call ID remains unchanged.
583
+ * @internal
584
+ */
585
+ setPushNotificationCallKitUUID(callKitUUID) {
586
+ const telnyxClient = this._sessionManager.telnyxClient;
587
+ if (telnyxClient && typeof telnyxClient.setPushNotificationCallKitUUID === 'function') {
588
+ telnyxClient.setPushNotificationCallKitUUID(callKitUUID);
589
+ }
590
+ }
416
591
  // ========== Lifecycle Methods ==========
417
592
  /**
418
593
  * Dispose of the client and clean up all resources.
@@ -421,18 +596,72 @@ class TelnyxVoipClient {
421
596
  * This is particularly important for background clients that should be
422
597
  * disposed after handling push notifications.
423
598
  */
424
- dispose() {
425
- if (this._disposed) {
426
- return;
599
+ async dispose() {
600
+ if (this._disposePromise) {
601
+ return this._disposePromise;
427
602
  }
428
603
  if (this._options.debug) {
429
604
  console.log('TelnyxVoipClient: Disposing client');
430
605
  }
431
606
  this._disposed = true;
432
- this._callStateController.dispose();
433
- this._sessionManager.dispose();
607
+ this._disposePromise = (async () => {
608
+ try {
609
+ await this._sessionManager.dispose();
610
+ } finally {
611
+ this._pendingCallKitAnswers.clear();
612
+ this._callStateController.dispose();
613
+ }
614
+ })();
615
+ return this._disposePromise;
434
616
  }
435
617
  // ========== Private Methods ==========
618
+ /**
619
+ * Forward answers captured during cold start as soon as SessionManager has
620
+ * created the TelnyxRTC instance. SessionManager invokes its ready callback
621
+ * before connect(), so the UUID-keyed action is present when the INVITE
622
+ * arrives.
623
+ */
624
+ _flushPendingCallKitAnswers() {
625
+ const telnyxClient = this._sessionManager.telnyxClient;
626
+ if (!telnyxClient || typeof telnyxClient.queueAnswerFromCallKit !== 'function') {
627
+ return;
628
+ }
629
+ for (const [actionKey, pendingAnswer] of this._pendingCallKitAnswers) {
630
+ try {
631
+ telnyxClient.queueAnswerFromCallKit(
632
+ pendingAnswer.callKitUUIDOrHeaders,
633
+ pendingAnswer.customHeaders
634
+ );
635
+ this._pendingCallKitAnswers.delete(actionKey);
636
+ } catch (error) {
637
+ console.error('TelnyxVoipClient: Failed to restore pending CallKit answer', {
638
+ actionKey,
639
+ error,
640
+ });
641
+ }
642
+ }
643
+ }
644
+ /**
645
+ * Prefer an explicitly supplied token, otherwise hydrate it from PushKit's
646
+ * native storage. PushKit registration starts in AppDelegate before React
647
+ * mounts, so this removes the race between the JS token event and login.
648
+ */
649
+ async _withNativeVoipPushToken(config) {
650
+ if (react_native_1.Platform.OS !== 'ios' || config.pushNotificationDeviceToken?.trim()) {
651
+ return config;
652
+ }
653
+ const nativePushToken = (await voice_pn_bridge_1.VoicePnBridge.getVoipToken())?.trim();
654
+ if (!nativePushToken) {
655
+ return config;
656
+ }
657
+ if (this._options.debug) {
658
+ console.log('TelnyxVoipClient: Using PushKit token from native storage');
659
+ }
660
+ return {
661
+ ...config,
662
+ pushNotificationDeviceToken: nativePushToken,
663
+ };
664
+ }
436
665
  /**
437
666
  * Store credential configuration for automatic reconnection
438
667
  */
@@ -441,6 +670,18 @@ class TelnyxVoipClient {
441
670
  const AsyncStorage = require('@react-native-async-storage/async-storage').default;
442
671
  await AsyncStorage.setItem('@telnyx_username', config.sipUser);
443
672
  await AsyncStorage.setItem('@telnyx_password', config.sipPassword);
673
+ await AsyncStorage.setItem(
674
+ USE_TRICKLE_ICE_STORAGE_KEY,
675
+ String(config.useTrickleIce ?? false)
676
+ );
677
+ await AsyncStorage.setItem(
678
+ PUSH_WHEN_ACTIVE_STORAGE_KEY,
679
+ String(config.pushWhenActive ?? false)
680
+ );
681
+ await AsyncStorage.setItem(
682
+ MISSED_CALL_NOTIFICATIONS_STORAGE_KEY,
683
+ String(config.enableMissedCallNotifications ?? false)
684
+ );
444
685
  if (config.pushNotificationDeviceToken) {
445
686
  await AsyncStorage.setItem('@push_token', config.pushNotificationDeviceToken);
446
687
  }
@@ -463,6 +704,18 @@ class TelnyxVoipClient {
463
704
  try {
464
705
  const AsyncStorage = require('@react-native-async-storage/async-storage').default;
465
706
  await AsyncStorage.setItem('@credential_token', config.token);
707
+ await AsyncStorage.setItem(
708
+ USE_TRICKLE_ICE_STORAGE_KEY,
709
+ String(config.useTrickleIce ?? false)
710
+ );
711
+ await AsyncStorage.setItem(
712
+ PUSH_WHEN_ACTIVE_STORAGE_KEY,
713
+ String(config.pushWhenActive ?? false)
714
+ );
715
+ await AsyncStorage.setItem(
716
+ MISSED_CALL_NOTIFICATIONS_STORAGE_KEY,
717
+ String(config.enableMissedCallNotifications ?? false)
718
+ );
466
719
  if (config.pushNotificationDeviceToken) {
467
720
  await AsyncStorage.setItem('@push_token', config.pushNotificationDeviceToken);
468
721
  }
@@ -511,10 +764,11 @@ function createTelnyxVoipClient(options) {
511
764
  * Disposes the current singleton so that a subsequent call to
512
765
  * `createTelnyxVoipClient()` will create a fresh instance.
513
766
  */
514
- function destroyTelnyxVoipClient() {
515
- if (_sharedInstance) {
516
- _sharedInstance.dispose();
767
+ async function destroyTelnyxVoipClient() {
768
+ const sharedInstance = _sharedInstance;
769
+ if (sharedInstance) {
517
770
  _sharedInstance = null;
771
+ await sharedInstance.dispose();
518
772
  }
519
773
  }
520
774
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@telnyx/react-voice-commons-sdk",
3
- "version": "0.4.3",
3
+ "version": "1.1.0",
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",
@@ -30,7 +30,7 @@
30
30
  "dev": "tsc --watch",
31
31
  "test": "jest",
32
32
  "test:watch": "jest --watch",
33
- "lint": "eslint src --ext .ts,.tsx",
33
+ "lint": "ESLINT_USE_FLAT_CONFIG=false eslint src --ext .ts,.tsx",
34
34
  "clean": "rm -rf lib",
35
35
  "docs": "typedoc",
36
36
  "docs:watch": "typedoc --watch",
@@ -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.5",
89
+ "@telnyx/react-native-voice-sdk": ">=1.0.0",
90
90
  "eventemitter3": "^5.0.1",
91
91
  "expo": "~53.0.22",
92
92
  "react-native-url-polyfill": "^3.0.0",
@@ -98,11 +98,13 @@
98
98
  "@types/jest": "^29.5.0",
99
99
  "@types/react": "~19.0.14",
100
100
  "@types/react-native": "^0.72.8",
101
- "@typescript-eslint/eslint-plugin": "^6.0.0",
102
- "@typescript-eslint/parser": "^6.0.0",
101
+ "@typescript-eslint/eslint-plugin": "^8.0.0",
102
+ "@typescript-eslint/parser": "^8.0.0",
103
103
  "eslint": "^8.0.0",
104
+ "expo-router": "^5.1.0",
104
105
  "jest": "^29.5.0",
105
106
  "prettier": "^3.0.0",
107
+ "react": "^19.0.0",
106
108
  "ts-jest": "^29.1.0",
107
109
  "typedoc": "^0.28.14",
108
110
  "typedoc-plugin-markdown": "^4.9.0",