@zyratalk1/zyra-twilio-wrapper 1.2.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.
package/dist/index.mjs ADDED
@@ -0,0 +1,2002 @@
1
+ // utils/helper.ts
2
+ function createSuccessResult(message, data) {
3
+ return { success: true, message, data };
4
+ }
5
+ function createErrorResult(error) {
6
+ return {
7
+ success: false,
8
+ error: error instanceof Error ? error : new Error(error)
9
+ };
10
+ }
11
+
12
+ // src/react-native-voip-sdk/bridge/event-emitter.ts
13
+ var BridgeEventEmitter = class {
14
+ constructor() {
15
+ this.listeners = /* @__PURE__ */ new Map();
16
+ }
17
+ on(eventName, listener) {
18
+ const set = this.listeners.get(eventName) ?? /* @__PURE__ */ new Set();
19
+ set.add(listener);
20
+ this.listeners.set(eventName, set);
21
+ return () => this.off(eventName, listener);
22
+ }
23
+ off(eventName, listener) {
24
+ const set = this.listeners.get(eventName);
25
+ if (!set) return;
26
+ set.delete(listener);
27
+ if (set.size === 0) {
28
+ this.listeners.delete(eventName);
29
+ }
30
+ }
31
+ emit(eventName, payload) {
32
+ const set = this.listeners.get(eventName);
33
+ if (!set) return;
34
+ set.forEach((listener) => listener(payload));
35
+ }
36
+ removeAllListeners() {
37
+ this.listeners.clear();
38
+ }
39
+ };
40
+
41
+ // src/react-native-voip-sdk/bridge/twilio-voice-adapter.ts
42
+ import axios from "axios";
43
+ import { NativeModules, Platform } from "react-native";
44
+ import { Voice } from "@twilio/voice-react-native-sdk";
45
+ var TwilioVoiceAdapter = class {
46
+ constructor() {
47
+ this.voice = new Voice();
48
+ this.axiosInstance = axios.create();
49
+ this.sdkConfig = null;
50
+ this.listeners = /* @__PURE__ */ new Map();
51
+ this.callInvites = /* @__PURE__ */ new Map();
52
+ this.calls = /* @__PURE__ */ new Map();
53
+ this.started = false;
54
+ this.defaultConferenceName = "";
55
+ this.runtimeAccessToken = null;
56
+ this.isVoiceRegistered = false;
57
+ }
58
+ async initializeSDK(config) {
59
+ console.log("[VOIP SDK] connect() called");
60
+ this.sdkConfig = config;
61
+ this.runtimeAccessToken = config.accessToken ?? null;
62
+ this.defaultConferenceName = `conf_${config.consumerId}_${Date.now()}`;
63
+ this.axiosInstance.defaults.baseURL = config.serverUrl;
64
+ if (!this.started) {
65
+ this.bindVoiceEvents();
66
+ this.started = true;
67
+ }
68
+ try {
69
+ const token = this.getVoiceAccessToken();
70
+ if (!token) {
71
+ this.emit("onError", {
72
+ scope: "voice_register",
73
+ message: "Twilio access token not provided. SDK initialized for config APIs only."
74
+ });
75
+ return { success: true, data: {} };
76
+ }
77
+ await this.registerVoiceClient(token);
78
+ } catch (error) {
79
+ console.error("[VOIP SDK] connect() failed", error);
80
+ this.emit("onError", {
81
+ scope: "voice_register",
82
+ message: error instanceof Error ? error.message : String(error)
83
+ });
84
+ }
85
+ return { success: true, data: {} };
86
+ }
87
+ async authenticate(payload) {
88
+ console.log("[VOIP SDK] auth started");
89
+ if (!this.sdkConfig) {
90
+ return { success: false, error: "SDK not initialized" };
91
+ }
92
+ try {
93
+ this.captureAccessToken(payload);
94
+ console.log("[VOIP DEBUG] token validation", {
95
+ hasPayloadToken: Boolean(payload?.accessToken),
96
+ hasRuntimeToken: Boolean(this.runtimeAccessToken)
97
+ });
98
+ const res = await this.axiosInstance.post(
99
+ `${this.sdkConfig.serverUrl}/voipSdk.verifySdkToken`,
100
+ payload ?? {},
101
+ {
102
+ headers: {
103
+ Authorization: `Bearer ${this.sdkConfig.sdkToken}`
104
+ }
105
+ }
106
+ );
107
+ const verified = Boolean(res?.data?.result?.data?.verified);
108
+ if (!verified) {
109
+ return { success: false, error: "Invalid SDK token" };
110
+ }
111
+ if (!this.getVoiceAccessToken()) {
112
+ const generatedToken = await this.generateVoiceAccessToken();
113
+ this.runtimeAccessToken = generatedToken;
114
+ }
115
+ const tokenForVoice = this.getVoiceAccessToken();
116
+ if (tokenForVoice) {
117
+ try {
118
+ await this.registerVoiceClient(tokenForVoice);
119
+ } catch (error) {
120
+ this.emit("onError", {
121
+ scope: "voice_register",
122
+ message: error instanceof Error ? error.message : "Voice register failed after auth"
123
+ });
124
+ console.error("[VOIP SDK] voice registration warning after auth", error);
125
+ }
126
+ }
127
+ const auth = {
128
+ consumerId: this.sdkConfig.consumerId,
129
+ accessToken: this.getVoiceAccessToken() ?? this.sdkConfig.sdkToken,
130
+ expiresAt: Date.now() + 55 * 60 * 1e3,
131
+ scopes: ["voice:call", "voice:conference"]
132
+ };
133
+ this.emit("onAuthStateChanged", { state: "AUTHENTICATED" });
134
+ console.log("[VOIP SDK] auth success");
135
+ return { success: true, data: auth };
136
+ } catch (error) {
137
+ console.error("[VOIP SDK] auth failed", error);
138
+ return {
139
+ success: false,
140
+ error: error instanceof Error ? error.message : "Authentication failed"
141
+ };
142
+ }
143
+ }
144
+ async refreshAuth() {
145
+ if (!this.sdkConfig) {
146
+ return { success: false, error: "SDK not initialized" };
147
+ }
148
+ const auth = {
149
+ consumerId: this.sdkConfig.consumerId,
150
+ accessToken: this.getVoiceAccessToken() ?? this.sdkConfig.sdkToken,
151
+ expiresAt: Date.now() + 55 * 60 * 1e3,
152
+ scopes: ["voice:call", "voice:conference"]
153
+ };
154
+ return { success: true, data: auth };
155
+ }
156
+ async startCall(input) {
157
+ console.log("[VOIP SDK] startCall() invoked", { callId: input.callId, number: input.number });
158
+ if (!this.sdkConfig) {
159
+ return { success: false, error: "SDK not initialized" };
160
+ }
161
+ try {
162
+ if (!this.isVoiceNativeModuleAvailable()) {
163
+ return {
164
+ success: false,
165
+ error: "Twilio native module not available (TwilioVoiceReactNative). Rebuild app and verify native linking."
166
+ };
167
+ }
168
+ const token = this.getVoiceAccessToken();
169
+ if (!token) {
170
+ return {
171
+ success: false,
172
+ error: "Twilio access token missing. Pass accessToken in constructor or authenticate({ accessToken })."
173
+ };
174
+ }
175
+ const conferenceName = `conf_${this.sdkConfig.consumerId}_${input.callId}`;
176
+ console.log("[VOIP SDK] voice.connect() params", {
177
+ To: input.number,
178
+ conferenceName
179
+ });
180
+ const call = await this.voice.connect(token, {
181
+ params: {
182
+ To: input.number,
183
+ conferenceName
184
+ }
185
+ });
186
+ const callSid = this.readCallSid(call);
187
+ const tracked = {
188
+ callId: input.callId,
189
+ callSid,
190
+ call,
191
+ direction: "OUTBOUND",
192
+ conferenceName,
193
+ conferenceDetail: null
194
+ };
195
+ this.calls.set(input.callId, tracked);
196
+ this.bindCallEvents(input.callId, call);
197
+ console.log("[VOIP SDK] call request sent", { callId: input.callId });
198
+ const session = {
199
+ callId: input.callId,
200
+ direction: "OUTBOUND",
201
+ status: "DIALING",
202
+ participants: [],
203
+ conferenceRoomId: null
204
+ };
205
+ return { success: true, data: session };
206
+ } catch (error) {
207
+ console.error("[VOIP SDK] startCall() failed", error);
208
+ return {
209
+ success: false,
210
+ error: error instanceof Error ? error.message : "Failed to start call"
211
+ };
212
+ }
213
+ }
214
+ async rejectCall(input) {
215
+ console.log("[VOIP SDK] rejectCall() invoked", { callId: input.callId });
216
+ const invite = this.callInvites.get(input.callId);
217
+ if (!invite) {
218
+ return { success: false, error: "Incoming call invite not found" };
219
+ }
220
+ try {
221
+ if (typeof invite.reject === "function") {
222
+ await invite.reject();
223
+ }
224
+ this.callInvites.delete(input.callId);
225
+ this.emit("onCallEnded", { callId: input.callId, reason: "rejected" });
226
+ return { success: true, data: {} };
227
+ } catch (error) {
228
+ return {
229
+ success: false,
230
+ error: error instanceof Error ? error.message : "Failed to reject call"
231
+ };
232
+ }
233
+ }
234
+ async answerCall(input) {
235
+ console.log("[VOIP DEBUG] call accepted flow start", { callId: input.callId });
236
+ try {
237
+ const invite = this.callInvites.get(input.callId);
238
+ if (!invite) {
239
+ return { success: false, error: "Incoming call invite not found" };
240
+ }
241
+ const call = await invite.accept();
242
+ const tracked = {
243
+ callId: input.callId,
244
+ callSid: this.readCallSid(call) ?? input.callId,
245
+ call,
246
+ direction: "INBOUND",
247
+ conferenceName: this.defaultConferenceName,
248
+ conferenceDetail: null
249
+ };
250
+ this.calls.set(input.callId, tracked);
251
+ this.callInvites.delete(input.callId);
252
+ this.bindCallEvents(input.callId, call);
253
+ console.log("[VOIP DEBUG] call accepted", { callId: input.callId });
254
+ return { success: true, data: {} };
255
+ } catch (error) {
256
+ console.error("[VOIP DEBUG] call accepted failure", error);
257
+ return {
258
+ success: false,
259
+ error: error instanceof Error ? error.message : "Failed to answer call"
260
+ };
261
+ }
262
+ }
263
+ async endCall(input) {
264
+ console.log("[VOIP SDK] endCall() invoked", { callId: input.callId });
265
+ const tracked = this.calls.get(input.callId);
266
+ if (!tracked) return { success: false, error: "Call not found" };
267
+ try {
268
+ await tracked.call.disconnect();
269
+ this.calls.delete(input.callId);
270
+ this.emit("onCallEnded", { callId: input.callId });
271
+ console.log("[VOIP SDK] call ended", { callId: input.callId });
272
+ return { success: true, data: {} };
273
+ } catch (error) {
274
+ console.error("[VOIP SDK] endCall() failed", error);
275
+ return {
276
+ success: false,
277
+ error: error instanceof Error ? error.message : "Failed to end call"
278
+ };
279
+ }
280
+ }
281
+ async toggleMute(input) {
282
+ console.log("[VOIP SDK] toggleMute() invoked", { callId: input.callId, mute: input.mute });
283
+ const tracked = this.calls.get(input.callId);
284
+ if (!tracked) {
285
+ return { success: false, error: "No active call to mute/unmute" };
286
+ }
287
+ try {
288
+ if (typeof tracked.call.mute === "function") {
289
+ await tracked.call.mute(input.mute);
290
+ } else if (typeof tracked.call.isMuted === "function") {
291
+ const current = tracked.call.isMuted();
292
+ if (current !== input.mute) {
293
+ await tracked.call.mute(input.mute);
294
+ }
295
+ }
296
+ console.log("[VOIP SDK] toggleMute() success", { callId: input.callId, mute: input.mute });
297
+ return { success: true, data: {} };
298
+ } catch (error) {
299
+ console.error("[VOIP SDK] toggleMute() failed", error);
300
+ return {
301
+ success: false,
302
+ error: error instanceof Error ? error.message : "Failed to toggle mute"
303
+ };
304
+ }
305
+ }
306
+ async holdCall(input) {
307
+ return this.postCallControl(input.callId, "outgoingCall.hold", {
308
+ waitUrl: this.sdkConfig?.waitUrl ?? "https://api.twilio.com/cowbell.mp3"
309
+ });
310
+ }
311
+ async resumeCall(input) {
312
+ return this.postCallControl(input.callId, "outgoingCall.unhold");
313
+ }
314
+ /**
315
+ * Hold the active call then dial `newParticipantNo` into the same conference.
316
+ *
317
+ * Web SDK parity:
318
+ * OUTBOUND active call → holdAndAddParticipant()
319
+ * step 1: outgoingCall.hold (same as hold())
320
+ * step 2: outgoingCall.addNewCallee { conferenceName, newParticipantNo }
321
+ *
322
+ * INBOUND active call → holdAndAddParticipantIncoming()
323
+ * step 1: outgoingCall.hold (same as hold())
324
+ * step 2: outgoingCall.addNewCalleeIncoming { callSid, newParticipantNo }
325
+ */
326
+ async addNewCallee(input) {
327
+ if (!this.sdkConfig) {
328
+ return { success: false, error: "SDK not initialized" };
329
+ }
330
+ const tracked = this.calls.get(input.callId);
331
+ if (!tracked) {
332
+ return { success: false, error: "Active call not found" };
333
+ }
334
+ if (!input.newParticipantNo) {
335
+ return { success: false, error: "Participant number is required" };
336
+ }
337
+ try {
338
+ const holdResult = await this.postCallControl(
339
+ input.callId,
340
+ "outgoingCall.hold",
341
+ {
342
+ waitUrl: this.sdkConfig.waitUrl ?? "https://api.twilio.com/cowbell.mp3"
343
+ }
344
+ );
345
+ if (!holdResult.success) {
346
+ return { success: false, error: holdResult.error ?? "Failed to hold call before adding participant" };
347
+ }
348
+ const callSid = tracked.callSid ?? input.callId;
349
+ const conferenceName = tracked.conferenceName ?? this.defaultConferenceName;
350
+ let payload;
351
+ let endpoint;
352
+ if (tracked.direction === "INBOUND") {
353
+ endpoint = "outgoingCall.addNewCalleeIncoming";
354
+ payload = { callSid, newParticipantNo: input.newParticipantNo };
355
+ } else {
356
+ endpoint = "outgoingCall.addNewCallee";
357
+ payload = { conferenceName, newParticipantNo: input.newParticipantNo };
358
+ }
359
+ console.log("[VOIP SDK] addNewCallee() start", {
360
+ callId: input.callId,
361
+ direction: tracked.direction,
362
+ endpoint,
363
+ newParticipantNo: input.newParticipantNo
364
+ });
365
+ await this.axiosInstance.post(
366
+ `${this.sdkConfig.serverUrl}/${endpoint}`,
367
+ payload,
368
+ { headers: { Authorization: `Bearer ${this.sdkConfig.sdkToken}` } }
369
+ );
370
+ this.emit("onParticipantJoined", { callId: input.callId, number: input.newParticipantNo });
371
+ console.log("[VOIP SDK] addNewCallee() success", { callId: input.callId, endpoint });
372
+ return { success: true, data: {} };
373
+ } catch (error) {
374
+ console.error("[VOIP SDK] addNewCallee() failed", error);
375
+ return {
376
+ success: false,
377
+ error: error instanceof Error ? error.message : "Failed to add participant"
378
+ };
379
+ }
380
+ }
381
+ async mergeCall(input) {
382
+ const control = await this.postCallControl(input.callId, "outgoingCall.unhold");
383
+ if (!control.success) {
384
+ return {
385
+ success: false,
386
+ error: control.error ?? "Failed to merge call"
387
+ };
388
+ }
389
+ const existing = this.calls.get(input.callId);
390
+ const session = {
391
+ callId: input.callId,
392
+ direction: existing?.direction ?? "OUTBOUND",
393
+ status: "MERGED",
394
+ participants: [],
395
+ conferenceRoomId: null
396
+ };
397
+ return { success: true, data: session };
398
+ }
399
+ async getParticipants(input) {
400
+ if (!this.sdkConfig) {
401
+ return { success: false, error: "SDK not initialized" };
402
+ }
403
+ try {
404
+ const tracked = this.calls.get(input.callId);
405
+ const conferenceSid = tracked?.conferenceName ?? input.callId;
406
+ const res = await this.axiosInstance.post(
407
+ `${this.sdkConfig.serverUrl}/outgoingCall.getParticipants`,
408
+ { conferenceSid },
409
+ {
410
+ headers: {
411
+ Authorization: `Bearer ${this.sdkConfig.sdkToken}`
412
+ }
413
+ }
414
+ );
415
+ const participants = res?.data?.result?.data?.participants ?? [];
416
+ return { success: true, data: participants };
417
+ } catch (error) {
418
+ return {
419
+ success: false,
420
+ error: error instanceof Error ? error.message : "Failed to fetch participants"
421
+ };
422
+ }
423
+ }
424
+ async getConferenceId(input) {
425
+ if (!this.sdkConfig) {
426
+ return { success: false, error: "SDK not initialized" };
427
+ }
428
+ console.log("[VOIP SDK] getConferenceId() invoked", { conferenceName: input.conferenceName });
429
+ try {
430
+ const res = await this.axiosInstance.post(
431
+ `${this.sdkConfig.serverUrl}/outgoingCall.getConferenceId`,
432
+ { conferenceName: input.conferenceName },
433
+ { headers: { Authorization: `Bearer ${this.sdkConfig.sdkToken}` } }
434
+ );
435
+ const conferenceDetail = res?.data?.result?.data ?? null;
436
+ console.log("[VOIP SDK] getConferenceId() success", { conferenceDetail });
437
+ return { success: true, data: conferenceDetail };
438
+ } catch (error) {
439
+ console.error("[VOIP SDK] getConferenceId() failed", error);
440
+ return {
441
+ success: false,
442
+ error: error instanceof Error ? error.message : "Failed to fetch conference ID"
443
+ };
444
+ }
445
+ }
446
+ async getParticipantsByCallSid(input) {
447
+ if (!this.sdkConfig) {
448
+ return { success: false, error: "SDK not initialized" };
449
+ }
450
+ console.log("[VOIP SDK] getParticipantsByCallSid() invoked", { callSid: input.callSid });
451
+ try {
452
+ const res = await this.axiosInstance.post(
453
+ `${this.sdkConfig.serverUrl}/outgoingCall.getConferenceCallsByConferenceId`,
454
+ { callSid: input.callSid },
455
+ { headers: { Authorization: `Bearer ${this.sdkConfig.sdkToken}` } }
456
+ );
457
+ const participants = res?.data?.result?.data ?? [];
458
+ console.log("[VOIP SDK] getParticipantsByCallSid() success", { count: participants.length });
459
+ return { success: true, data: participants };
460
+ } catch (error) {
461
+ console.error("[VOIP SDK] getParticipantsByCallSid() failed", error);
462
+ return {
463
+ success: false,
464
+ error: error instanceof Error ? error.message : "Failed to fetch participants"
465
+ };
466
+ }
467
+ }
468
+ async removeParticipant(input) {
469
+ if (!this.sdkConfig) {
470
+ return { success: false, error: "SDK not initialized" };
471
+ }
472
+ console.log("[VOIP SDK] removeParticipant() invoked", {
473
+ conferenceSid: input.conferenceSid,
474
+ callSid: input.callSid
475
+ });
476
+ try {
477
+ await this.axiosInstance.post(
478
+ `${this.sdkConfig.serverUrl}/outgoingCall.removeParticipant`,
479
+ {
480
+ conferenceSid: input.conferenceSid,
481
+ callSid: input.callSid
482
+ },
483
+ { headers: { Authorization: `Bearer ${this.sdkConfig.sdkToken}` } }
484
+ );
485
+ this.emit("onParticipantLeft", { participantId: input.callSid });
486
+ console.log("[VOIP SDK] removeParticipant() success");
487
+ return { success: true, data: {} };
488
+ } catch (error) {
489
+ console.error("[VOIP SDK] removeParticipant() failed", error);
490
+ return {
491
+ success: false,
492
+ error: error instanceof Error ? error.message : "Failed to remove participant"
493
+ };
494
+ }
495
+ }
496
+ async disconnectParticipant(input) {
497
+ if (!this.sdkConfig) {
498
+ return { success: false, error: "SDK not initialized" };
499
+ }
500
+ try {
501
+ await this.axiosInstance.post(
502
+ `${this.sdkConfig.serverUrl}/outgoingCall.removeParticipant`,
503
+ {
504
+ conferenceSid: this.defaultConferenceName,
505
+ callSid: input.participantId
506
+ },
507
+ {
508
+ headers: {
509
+ Authorization: `Bearer ${this.sdkConfig.sdkToken}`
510
+ }
511
+ }
512
+ );
513
+ this.emit("onParticipantLeft", { participantId: input.participantId });
514
+ return { success: true, data: {} };
515
+ } catch (error) {
516
+ return {
517
+ success: false,
518
+ error: error instanceof Error ? error.message : "Failed to disconnect participant"
519
+ };
520
+ }
521
+ }
522
+ addListener(eventName, callback) {
523
+ const set = this.listeners.get(eventName) ?? /* @__PURE__ */ new Set();
524
+ set.add(callback);
525
+ this.listeners.set(eventName, set);
526
+ return {
527
+ remove: () => {
528
+ const active = this.listeners.get(eventName);
529
+ if (!active) return;
530
+ active.delete(callback);
531
+ if (active.size === 0) {
532
+ this.listeners.delete(eventName);
533
+ }
534
+ }
535
+ };
536
+ }
537
+ emit(eventName, payload) {
538
+ const set = this.listeners.get(eventName);
539
+ if (!set) return;
540
+ set.forEach((listener) => listener(payload));
541
+ }
542
+ bindVoiceEvents() {
543
+ this.voice.on(Voice.Event.CallInvite, (invite) => {
544
+ const callId = this.readInviteSid(invite) ?? `incoming_${Date.now()}`;
545
+ const from = (typeof invite.getFrom === "function" ? invite.getFrom() : null) ?? "Unknown";
546
+ const to = (typeof invite.getTo === "function" ? invite.getTo() : null) ?? "";
547
+ console.log("[VOIP SDK] received call event", { callId, from, to });
548
+ this.callInvites.set(callId, invite);
549
+ this.emit("onCallRinging", { callId, direction: "INBOUND", from, to });
550
+ invite.on("cancelled", () => {
551
+ this.callInvites.delete(callId);
552
+ this.emit("onMissedCall", { callId });
553
+ this.emit("onCallEnded", { callId, reason: "cancelled" });
554
+ });
555
+ });
556
+ if (typeof this.voice.on === "function") {
557
+ this.voice.on(Voice.Event.Registered, () => {
558
+ console.log("[VOIP SDK] Device registered (onReady)");
559
+ this.emit("onReady", {});
560
+ });
561
+ }
562
+ this.voice.on(Voice.Event.Error, (error) => {
563
+ console.error("[VOIP DEBUG] websocket state", {
564
+ state: "ERROR",
565
+ message: error?.message ?? String(error)
566
+ });
567
+ this.emit("onError", {
568
+ scope: "voice",
569
+ message: error?.message ?? String(error)
570
+ });
571
+ });
572
+ }
573
+ bindCallEvents(callId, call) {
574
+ call.on("ringing", () => {
575
+ console.log("[VOIP DEBUG] call ringing", { callId });
576
+ this.emit("onCallStateChanged", { callId, status: "RINGING" });
577
+ });
578
+ call.on("connected", () => {
579
+ console.log("[VOIP DEBUG] media stream started", { callId });
580
+ const tracked = this.calls.get(callId);
581
+ let liveSid = null;
582
+ if (tracked) {
583
+ liveSid = this.readCallSid(call);
584
+ if (liveSid) {
585
+ tracked.callSid = liveSid;
586
+ this.calls.set(callId, tracked);
587
+ console.log("[VOIP DEBUG] callSid captured on connect", { callId, callSid: liveSid });
588
+ }
589
+ this.fetchConferenceId(callId, tracked.conferenceName ?? this.defaultConferenceName).catch(
590
+ (err) => console.warn("[VOIP SDK] getConferenceId failed after connect", err)
591
+ );
592
+ }
593
+ this.emit("onCallConnected", { callId, callSid: liveSid ?? callId, status: "CONNECTED" });
594
+ this.emit("onCallStateChanged", { callId, callSid: liveSid ?? callId, status: "CONNECTED" });
595
+ });
596
+ call.on("reconnecting", () => {
597
+ console.log("[VOIP DEBUG] reconnect attempt", { callId });
598
+ this.emit("onCallStateChanged", { callId, status: "RECONNECTING" });
599
+ });
600
+ call.on("disconnected", (error) => {
601
+ this.calls.delete(callId);
602
+ this.emit("onCallEnded", {
603
+ callId,
604
+ reason: error?.message ?? null
605
+ });
606
+ console.log("[VOIP DEBUG] call ended", {
607
+ callId,
608
+ reason: error?.message ?? null
609
+ });
610
+ });
611
+ call.on("connectFailure", (error) => {
612
+ const reason = error?.message ?? String(error);
613
+ console.error("[VOIP DEBUG] call connect failure", { callId, message: reason });
614
+ this.calls.delete(callId);
615
+ this.emit("onCallEnded", { callId, reason });
616
+ this.emit("onError", {
617
+ scope: "call_connect",
618
+ callId,
619
+ message: reason
620
+ });
621
+ });
622
+ }
623
+ async fetchConferenceId(callId, conferenceName) {
624
+ if (!this.sdkConfig) return;
625
+ console.log("[VOIP SDK] fetchConferenceId() start", { callId, conferenceName });
626
+ try {
627
+ const res = await this.axiosInstance.post(
628
+ `${this.sdkConfig.serverUrl}/outgoingCall.getConferenceId`,
629
+ { conferenceName },
630
+ { headers: { Authorization: `Bearer ${this.sdkConfig.sdkToken}` } }
631
+ );
632
+ const conferenceDetail = res?.data?.result?.data ?? null;
633
+ const tracked = this.calls.get(callId);
634
+ if (tracked && conferenceDetail) {
635
+ tracked.conferenceDetail = conferenceDetail;
636
+ this.calls.set(callId, tracked);
637
+ console.log("[VOIP SDK] conference detail stored", { callId, conferenceDetail });
638
+ }
639
+ this.emit("onCallStateChanged", {
640
+ callId,
641
+ status: "CONNECTED",
642
+ conferenceDetail
643
+ });
644
+ } catch (err) {
645
+ console.warn("[VOIP SDK] fetchConferenceId() failed", err);
646
+ }
647
+ }
648
+ readCallSid(call) {
649
+ if (!call) return null;
650
+ if (typeof call.getSid === "function") {
651
+ const sid = call.getSid();
652
+ return typeof sid === "string" ? sid : null;
653
+ }
654
+ return null;
655
+ }
656
+ readInviteSid(invite) {
657
+ if (!invite) return null;
658
+ if (typeof invite.getCallSid === "function") {
659
+ const sid = invite.getCallSid();
660
+ return typeof sid === "string" ? sid : null;
661
+ }
662
+ return null;
663
+ }
664
+ async postCallControl(callId, endpoint, extraPayload) {
665
+ console.log("[VOIP SDK] postCallControl() start", { callId, endpoint });
666
+ if (!this.sdkConfig) {
667
+ return { success: false, error: "SDK not initialized" };
668
+ }
669
+ const tracked = this.calls.get(callId);
670
+ const callSid = tracked?.callSid ?? callId;
671
+ const conferenceName = tracked?.conferenceName ?? this.defaultConferenceName;
672
+ try {
673
+ await this.axiosInstance.post(
674
+ `${this.sdkConfig.serverUrl}/${endpoint}`,
675
+ {
676
+ callSid,
677
+ conferenceName,
678
+ ...extraPayload ?? {}
679
+ },
680
+ {
681
+ headers: {
682
+ Authorization: `Bearer ${this.sdkConfig.sdkToken}`
683
+ }
684
+ }
685
+ );
686
+ console.log("[VOIP SDK] postCallControl() success", { callId, endpoint });
687
+ return { success: true, data: {} };
688
+ } catch (error) {
689
+ console.error("[VOIP SDK] postCallControl() failed", error);
690
+ return {
691
+ success: false,
692
+ error: error instanceof Error ? error.message : "Call control failed"
693
+ };
694
+ }
695
+ }
696
+ captureAccessToken(payload) {
697
+ if (!payload) return;
698
+ console.log("[VOIP DEBUG] captureAccessToken() invoked");
699
+ if (typeof payload.accessToken === "string" && payload.accessToken.trim()) {
700
+ this.runtimeAccessToken = payload.accessToken.trim();
701
+ return;
702
+ }
703
+ const tokenFromMetadata = payload.metadata?.accessToken;
704
+ if (typeof tokenFromMetadata === "string" && tokenFromMetadata.trim().length > 0) {
705
+ this.runtimeAccessToken = tokenFromMetadata.trim();
706
+ }
707
+ }
708
+ async generateVoiceAccessToken() {
709
+ if (!this.sdkConfig) {
710
+ throw new Error("SDK not initialized");
711
+ }
712
+ const payload = {
713
+ identity: this.sdkConfig.identity,
714
+ ttl: 3600,
715
+ incomingAllow: true
716
+ };
717
+ console.log("[VOIP DEBUG] token generation start");
718
+ const res = await this.axiosInstance.post(
719
+ `${this.sdkConfig.serverUrl}/voipSdk.generateToken`,
720
+ payload,
721
+ {
722
+ headers: {
723
+ Authorization: `Bearer ${this.sdkConfig.sdkToken}`
724
+ }
725
+ }
726
+ );
727
+ const token = res?.data?.result?.data?.token ?? res?.data?.result?.token ?? res?.data?.token;
728
+ if (typeof token !== "string" || token.trim().length === 0) {
729
+ throw new Error("Token generation failed: empty access token");
730
+ }
731
+ console.log("[VOIP DEBUG] token generation success");
732
+ return token.trim();
733
+ }
734
+ async registerVoiceClient(token) {
735
+ if (!this.isVoiceNativeModuleAvailable()) {
736
+ throw new Error(
737
+ "Twilio native module not available (TwilioVoiceReactNative). Rebuild app after installing @twilio/voice-react-native-sdk."
738
+ );
739
+ }
740
+ try {
741
+ const shouldRegisterOnAndroid = Platform.OS !== "android" || Boolean(
742
+ this.sdkConfig?.enableAndroidVoiceRegister
743
+ );
744
+ if (!shouldRegisterOnAndroid) {
745
+ console.warn(
746
+ "[VOIP SDK] Android voice.register() skipped to prevent crash. Enable `enableAndroidVoiceRegister` only after Firebase + Twilio incoming-call push setup."
747
+ );
748
+ this.isVoiceRegistered = false;
749
+ return;
750
+ }
751
+ if (Platform.OS === "ios") {
752
+ await this.voice.initializePushRegistry();
753
+ }
754
+ await this.voice.register(token);
755
+ this.isVoiceRegistered = true;
756
+ console.log("[VOIP SDK] websocket connected");
757
+ } catch (error) {
758
+ this.isVoiceRegistered = false;
759
+ throw error;
760
+ }
761
+ }
762
+ isVoiceNativeModuleAvailable() {
763
+ return Boolean(NativeModules?.TwilioVoiceReactNative);
764
+ }
765
+ getVoiceAccessToken() {
766
+ if (this.runtimeAccessToken && this.runtimeAccessToken.trim()) {
767
+ return this.runtimeAccessToken.trim();
768
+ }
769
+ if (this.sdkConfig?.accessToken && this.sdkConfig.accessToken.trim()) {
770
+ return this.sdkConfig.accessToken.trim();
771
+ }
772
+ return null;
773
+ }
774
+ };
775
+
776
+ // src/react-native-voip-sdk/bridge/native-module.ts
777
+ var defaultNativeModule = new TwilioVoiceAdapter();
778
+ function resolveNativeModule(custom) {
779
+ if (custom) return custom;
780
+ return defaultNativeModule;
781
+ }
782
+
783
+ // src/react-native-voip-sdk/bridge/index.ts
784
+ var BridgeClient = class {
785
+ constructor(nativeModule) {
786
+ this.events = new BridgeEventEmitter();
787
+ this.nativeSubscriptions = [];
788
+ this.nativeModule = resolveNativeModule(nativeModule);
789
+ console.log("[VOIP SDK] BridgeClient initialized");
790
+ this.subscribeToNativeEvents();
791
+ }
792
+ on(eventName, listener) {
793
+ return this.events.on(eventName, listener);
794
+ }
795
+ emit(eventName, payload) {
796
+ console.log("[VOIP EVENT] bridge emit", { eventName, payload });
797
+ this.events.emit(eventName, payload);
798
+ }
799
+ destroy() {
800
+ this.nativeSubscriptions.forEach((sub) => sub.remove());
801
+ this.nativeSubscriptions = [];
802
+ this.events.removeAllListeners();
803
+ }
804
+ subscribeToNativeEvents() {
805
+ if (!this.nativeModule.addListener) return;
806
+ const sdkEvents = [
807
+ "onReady",
808
+ "onCallRinging",
809
+ "onCallConnected",
810
+ "onParticipantJoined",
811
+ "onParticipantLeft",
812
+ "onCallEnded",
813
+ "onCallStateChanged",
814
+ "onAuthStateChanged",
815
+ "onMissedCall",
816
+ "onError"
817
+ ];
818
+ sdkEvents.forEach((eventName) => {
819
+ const sub = this.nativeModule.addListener?.(eventName, (payload) => {
820
+ console.log("[VOIP EVENT] native event", { eventName, payload });
821
+ this.events.emit(eventName, payload);
822
+ });
823
+ if (sub) {
824
+ this.nativeSubscriptions.push(sub);
825
+ }
826
+ });
827
+ }
828
+ };
829
+
830
+ // src/react-native-voip-sdk/core/auth/authManager.ts
831
+ var AuthManager = class {
832
+ constructor(transport, retry, emit) {
833
+ this.transport = transport;
834
+ this.retry = retry;
835
+ this.emit = emit;
836
+ this.authState = "UNAUTHENTICATED";
837
+ this.auth = null;
838
+ this.refreshTimer = null;
839
+ }
840
+ getState() {
841
+ return this.authState;
842
+ }
843
+ getAuth() {
844
+ return this.auth;
845
+ }
846
+ async authenticate(payload) {
847
+ console.log("[VOIP SDK] auth started");
848
+ console.log("[VOIP DEBUG] token validation", {
849
+ hasAccessToken: Boolean(payload?.accessToken)
850
+ });
851
+ this.setState("AUTHENTICATING");
852
+ const result = await this.retry.execute(
853
+ async () => this.transport.authenticate(payload),
854
+ { operation: "token_exchange" }
855
+ );
856
+ if (!result.success || !result.data) {
857
+ this.setState("UNAUTHENTICATED");
858
+ console.error("[VOIP SDK] auth failed", {
859
+ message: result.error ?? "Authentication failed"
860
+ });
861
+ throw new Error(result.error ?? "Authentication failed");
862
+ }
863
+ this.auth = result.data;
864
+ this.setState("AUTHENTICATED");
865
+ this.scheduleRefresh();
866
+ console.log("[VOIP SDK] auth success", {
867
+ consumerId: result.data.consumerId,
868
+ expiresAt: result.data.expiresAt
869
+ });
870
+ return result.data;
871
+ }
872
+ async refreshIfNeeded() {
873
+ console.log("[VOIP DEBUG] refreshIfNeeded() start");
874
+ if (!this.auth) {
875
+ this.setState("EXPIRED");
876
+ throw new Error("No auth session to refresh");
877
+ }
878
+ if (!this.transport.refreshAuth) return;
879
+ this.setState("REFRESHING");
880
+ const result = await this.retry.execute(
881
+ async () => this.transport.refreshAuth?.() ?? { success: false, error: "refresh not supported" },
882
+ { operation: "token_refresh" }
883
+ );
884
+ const data = result.success ? result.data : void 0;
885
+ const error = !result.success ? result.error : void 0;
886
+ if (!result.success || !data) {
887
+ this.auth = null;
888
+ this.setState("EXPIRED");
889
+ console.error("[VOIP DEBUG] token refresh failed", {
890
+ message: error ?? "Token refresh failed"
891
+ });
892
+ throw new Error(error ?? "Token refresh failed");
893
+ }
894
+ this.auth = data;
895
+ this.setState("AUTHENTICATED");
896
+ this.scheduleRefresh();
897
+ console.log("[VOIP DEBUG] token refresh success");
898
+ }
899
+ destroy() {
900
+ if (this.refreshTimer) {
901
+ clearTimeout(this.refreshTimer);
902
+ this.refreshTimer = null;
903
+ }
904
+ this.auth = null;
905
+ this.setState("UNAUTHENTICATED");
906
+ }
907
+ setState(nextState) {
908
+ this.authState = nextState;
909
+ this.emit("onAuthStateChanged", { state: nextState });
910
+ }
911
+ scheduleRefresh() {
912
+ if (!this.auth?.expiresAt) return;
913
+ if (this.refreshTimer) clearTimeout(this.refreshTimer);
914
+ const now = Date.now();
915
+ const refreshAt = Math.max(now, this.auth.expiresAt - 2 * 60 * 1e3);
916
+ const delay = Math.max(1e3, refreshAt - now);
917
+ this.refreshTimer = setTimeout(() => {
918
+ void this.refreshIfNeeded().catch((error) => {
919
+ this.emit("onError", {
920
+ scope: "auth",
921
+ message: error instanceof Error ? error.message : String(error)
922
+ });
923
+ });
924
+ }, delay);
925
+ }
926
+ };
927
+
928
+ // src/react-native-voip-sdk/core/call-engine/callEngine.ts
929
+ var CallEngine = class {
930
+ constructor(nativeModule, retry, emit) {
931
+ this.nativeModule = nativeModule;
932
+ this.retry = retry;
933
+ this.emit = emit;
934
+ this.sessions = /* @__PURE__ */ new Map();
935
+ }
936
+ getSession(callId) {
937
+ return this.sessions.get(callId);
938
+ }
939
+ async startCall(number) {
940
+ console.log("[VOIP SDK] startCall() invoked", { number });
941
+ const callId = `call_${Date.now()}_${Math.floor(Math.random() * 1e3)}`;
942
+ const result = await this.retry.execute(
943
+ async () => this.nativeModule.startCall({ number, callId }),
944
+ { operation: "call_setup" }
945
+ );
946
+ if (!result.success || !result.data) {
947
+ console.error("[VOIP SDK] call request failed", {
948
+ message: result.error ?? "Failed to start call"
949
+ });
950
+ throw new Error(result.error ?? "Failed to start call");
951
+ }
952
+ console.log("[VOIP DEBUG] dial request success", {
953
+ callId: result.data.callId
954
+ });
955
+ this.sessions.set(result.data.callId, result.data);
956
+ this.emit("onCallStateChanged", {
957
+ callId: result.data.callId,
958
+ status: result.data.status,
959
+ direction: result.data.direction
960
+ });
961
+ return result.data;
962
+ }
963
+ async answerCall(callId) {
964
+ await this.execCallCommand("answerCall", callId, "ANSWERED");
965
+ }
966
+ async endCall(callId) {
967
+ console.log("[VOIP SDK] endCall() invoked", { callId });
968
+ await this.execCallCommand("endCall", callId, "ENDED");
969
+ console.log("[VOIP DEBUG] call ended", { callId });
970
+ this.emit("onCallEnded", { callId });
971
+ }
972
+ async holdCall(callId) {
973
+ await this.execCallCommand("holdCall", callId, "ON_HOLD");
974
+ }
975
+ async resumeCall(callId) {
976
+ await this.execCallCommand("resumeCall", callId, "RESUMED");
977
+ }
978
+ async toggleMute(callId, mute) {
979
+ console.log("[VOIP SDK] toggleMute() invoked", { callId, mute });
980
+ if (!this.nativeModule.toggleMute) {
981
+ throw new Error("toggleMute not supported by native module");
982
+ }
983
+ const result = await this.retry.execute(
984
+ async () => this.nativeModule.toggleMute({ callId, mute }),
985
+ { operation: "call_control_toggleMute" }
986
+ );
987
+ if (!result.success) {
988
+ throw new Error(result.error ?? "Failed to toggle mute");
989
+ }
990
+ this.emit("onCallStateChanged", { callId, muted: mute });
991
+ console.log("[VOIP SDK] toggleMute() success", { callId, mute });
992
+ }
993
+ async mergeCall(callId) {
994
+ const result = await this.retry.execute(
995
+ async () => this.nativeModule.mergeCall({ callId }),
996
+ { operation: "merge_call" }
997
+ );
998
+ if (!result.success || !result.data) {
999
+ throw new Error(result.error ?? "Failed to merge call");
1000
+ }
1001
+ this.sessions.set(result.data.callId, result.data);
1002
+ this.emit("onCallStateChanged", {
1003
+ callId: result.data.callId,
1004
+ status: result.data.status,
1005
+ direction: result.data.direction
1006
+ });
1007
+ return result.data;
1008
+ }
1009
+ async execCallCommand(methodName, callId, status) {
1010
+ console.log("[VOIP SDK] call control start", { methodName, callId, status });
1011
+ const result = await this.retry.execute(
1012
+ async () => this.nativeModule[methodName]({ callId }),
1013
+ { operation: `call_control_${methodName}` }
1014
+ );
1015
+ if (!result.success) {
1016
+ console.error("[VOIP SDK] call control failure", {
1017
+ methodName,
1018
+ callId,
1019
+ message: result.error ?? `Failed ${methodName}`
1020
+ });
1021
+ throw new Error(result.error ?? `Failed ${methodName}`);
1022
+ }
1023
+ const existing = this.sessions.get(callId);
1024
+ if (existing) {
1025
+ existing.status = status;
1026
+ this.sessions.set(callId, existing);
1027
+ }
1028
+ this.emit("onCallStateChanged", { callId, status });
1029
+ console.log("[VOIP SDK] call control success", { methodName, callId, status });
1030
+ }
1031
+ };
1032
+
1033
+ // src/react-native-voip-sdk/core/config/configService.ts
1034
+ import axios2 from "axios";
1035
+
1036
+ // src/parseRules.ts
1037
+ async function createParseRule(deps, input) {
1038
+ deps.ensureAuthenticated();
1039
+ try {
1040
+ const res = await deps.axiosInstance.post(
1041
+ `${deps.serverUrl}/voipSdk.createParseRule`,
1042
+ input,
1043
+ {
1044
+ headers: {
1045
+ Authorization: `Bearer ${deps.sdkToken}`
1046
+ }
1047
+ }
1048
+ );
1049
+ const data = res?.data?.result ?? res?.data ?? null;
1050
+ if (!data) {
1051
+ throw new Error("Invalid response from server");
1052
+ }
1053
+ return createSuccessResult(
1054
+ "Parse rule configuration created successfully",
1055
+ data
1056
+ );
1057
+ } catch (error) {
1058
+ return createErrorResult(
1059
+ error instanceof Error ? error.message : "Failed to create parse rule configuration"
1060
+ );
1061
+ }
1062
+ }
1063
+ async function listParseRules(deps, input) {
1064
+ deps.ensureAuthenticated();
1065
+ try {
1066
+ const res = await deps.axiosInstance.post(
1067
+ `${deps.serverUrl}/voipSdk.listParseRules`,
1068
+ input ?? {},
1069
+ {
1070
+ headers: {
1071
+ Authorization: `Bearer ${deps.sdkToken}`
1072
+ }
1073
+ }
1074
+ );
1075
+ const data = res?.data?.result ?? res?.data ?? null;
1076
+ if (!data) {
1077
+ throw new Error("Invalid response from server");
1078
+ }
1079
+ return createSuccessResult(
1080
+ "Parse rules fetched successfully",
1081
+ data
1082
+ );
1083
+ } catch (error) {
1084
+ return createErrorResult(
1085
+ error instanceof Error ? error.message : "Failed to list parse rules"
1086
+ );
1087
+ }
1088
+ }
1089
+
1090
+ // src/routingConfig.ts
1091
+ async function getRoutingConfig(deps) {
1092
+ deps.ensureAuthenticated();
1093
+ try {
1094
+ const res = await deps.axiosInstance.get(
1095
+ `${deps.serverUrl}/voipSdk.getRoutingConfig`,
1096
+ {
1097
+ headers: {
1098
+ Authorization: `Bearer ${deps.sdkToken}`
1099
+ }
1100
+ }
1101
+ );
1102
+ const data = res?.data?.result ?? res?.data ?? null;
1103
+ if (!data) {
1104
+ throw new Error("Invalid response from server");
1105
+ }
1106
+ return createSuccessResult(
1107
+ "Routing configuration fetched successfully",
1108
+ data
1109
+ );
1110
+ } catch (error) {
1111
+ return createErrorResult(
1112
+ error instanceof Error ? error.message : "Failed to fetch routing configuration"
1113
+ );
1114
+ }
1115
+ }
1116
+ async function setRoutingConfig(deps, input) {
1117
+ deps.ensureAuthenticated();
1118
+ try {
1119
+ const res = await deps.axiosInstance.post(
1120
+ `${deps.serverUrl}/voipSdk.setRoutingConfig`,
1121
+ input,
1122
+ {
1123
+ headers: {
1124
+ Authorization: `Bearer ${deps.sdkToken}`
1125
+ }
1126
+ }
1127
+ );
1128
+ const data = res?.data?.result ?? res?.data ?? null;
1129
+ if (!data) {
1130
+ throw new Error("Invalid response from server");
1131
+ }
1132
+ return createSuccessResult(
1133
+ data.message || "Routing configuration updated successfully",
1134
+ data
1135
+ );
1136
+ } catch (error) {
1137
+ return createErrorResult(
1138
+ error instanceof Error ? error.message : "Failed to update routing configuration"
1139
+ );
1140
+ }
1141
+ }
1142
+
1143
+ // src/webhook.ts
1144
+ function validateWebhookUrl(webhookUrl) {
1145
+ const trimmed = (webhookUrl ?? "").trim();
1146
+ if (!trimmed) throw new Error("Webhook URL is required");
1147
+ let parsed;
1148
+ try {
1149
+ parsed = new URL(trimmed);
1150
+ } catch {
1151
+ throw new Error("Invalid webhook URL");
1152
+ }
1153
+ if (parsed.protocol !== "https:" && parsed.protocol !== "http:") {
1154
+ throw new Error("Webhook URL must start with http:// or https://");
1155
+ }
1156
+ return parsed.toString();
1157
+ }
1158
+ async function registerWebhook(deps, webhookUrl) {
1159
+ deps.ensureAuthenticated();
1160
+ try {
1161
+ const normalizedUrl = validateWebhookUrl(webhookUrl);
1162
+ const payload = { webhookUrl: normalizedUrl };
1163
+ await deps.axiosInstance.post(
1164
+ `${deps.serverUrl}/voipSdk.registerWebhook`,
1165
+ payload,
1166
+ { headers: { Authorization: `Bearer ${deps.sdkToken}` } }
1167
+ );
1168
+ return createSuccessResult("Webhook registered", { webhookUrl: normalizedUrl });
1169
+ } catch (error) {
1170
+ return createErrorResult(
1171
+ error instanceof Error ? error : "Webhook registration failed"
1172
+ );
1173
+ }
1174
+ }
1175
+ async function deregisterWebhook(deps) {
1176
+ deps.ensureAuthenticated();
1177
+ try {
1178
+ const payload = {};
1179
+ await deps.axiosInstance.post(
1180
+ `${deps.serverUrl}/voipSdk.deRegisterWebHook`,
1181
+ payload,
1182
+ { headers: { Authorization: `Bearer ${deps.sdkToken}` } }
1183
+ );
1184
+ return createSuccessResult("Webhook deregistered", null);
1185
+ } catch (error) {
1186
+ return createErrorResult(
1187
+ error instanceof Error ? error : "Webhook deregistration failed"
1188
+ );
1189
+ }
1190
+ }
1191
+ async function isWebhookConfigured(deps) {
1192
+ deps.ensureAuthenticated();
1193
+ try {
1194
+ const res = await deps.axiosInstance.get(
1195
+ `${deps.serverUrl}/voipSdk.isConfiguredWebhookUrl`,
1196
+ { headers: { Authorization: `Bearer ${deps.sdkToken}` } }
1197
+ );
1198
+ const data = res?.data?.result?.data ?? res?.data?.data ?? null;
1199
+ const rawUrl = data?.webhookUrl ?? data?.url ?? (typeof data === "string" ? data : void 0);
1200
+ const url = rawUrl ? String(rawUrl) : null;
1201
+ const configured = Boolean(url);
1202
+ const result = { configured, webhookUrl: url };
1203
+ return createSuccessResult("Webhook configuration fetched", result);
1204
+ } catch (error) {
1205
+ return createErrorResult(
1206
+ error instanceof Error ? error : "Webhook configuration check failed"
1207
+ );
1208
+ }
1209
+ }
1210
+
1211
+ // src/welcomeMessageConfig.ts
1212
+ function isUrl(value) {
1213
+ try {
1214
+ const url = new URL(value);
1215
+ return url.protocol === "http:" || url.protocol === "https:";
1216
+ } catch {
1217
+ return false;
1218
+ }
1219
+ }
1220
+ function isAudioLikeInput(value) {
1221
+ return value.startsWith("http://") || value.startsWith("https://") || value.startsWith("data:audio/");
1222
+ }
1223
+ function validateWelcomeConfigInput(input) {
1224
+ const welcomeMessage = input.welcomeMessage?.trim();
1225
+ if (!welcomeMessage) {
1226
+ throw new Error("Payload is missing or empty. welcomeMessage is required.");
1227
+ }
1228
+ if (input.welcomeType === "tts") {
1229
+ if (isAudioLikeInput(welcomeMessage)) {
1230
+ throw new Error(
1231
+ "welcomeType is tts but input appears to be audio. Only text is allowed for TTS."
1232
+ );
1233
+ }
1234
+ }
1235
+ if (input.welcomeType === "audio") {
1236
+ if (!isUrl(welcomeMessage)) {
1237
+ throw new Error(
1238
+ "welcomeType is audio but welcomeMessage must be a valid URL (e.g. S3 URL)."
1239
+ );
1240
+ }
1241
+ }
1242
+ return {
1243
+ welcomeType: input.welcomeType,
1244
+ welcomeMessage
1245
+ };
1246
+ }
1247
+ async function welcomeMessageConfig(deps, input) {
1248
+ deps.ensureAuthenticated();
1249
+ try {
1250
+ const payload = validateWelcomeConfigInput(input);
1251
+ const res = await deps.axiosInstance.post(
1252
+ `${deps.serverUrl}/voipSdk.welcomeMessageConfig`,
1253
+ payload,
1254
+ {
1255
+ headers: {
1256
+ Authorization: `Bearer ${deps.sdkToken}`
1257
+ }
1258
+ }
1259
+ );
1260
+ const data = res?.data?.result ?? res?.data ?? null;
1261
+ if (!data) {
1262
+ throw new Error("Invalid response from server");
1263
+ }
1264
+ return createSuccessResult(
1265
+ data.message || "Welcome configuration updated successfully",
1266
+ {
1267
+ success: data.success,
1268
+ message: data.message,
1269
+ welcomeType: data.welcomeType,
1270
+ routingConfigId: data.routingConfigId
1271
+ }
1272
+ );
1273
+ } catch (error) {
1274
+ return createErrorResult(
1275
+ error instanceof Error ? error : "Failed to upsert welcome configuration"
1276
+ );
1277
+ }
1278
+ }
1279
+
1280
+ // src/react-native-voip-sdk/core/config/configService.ts
1281
+ var ConfigService = class {
1282
+ constructor(deps) {
1283
+ this.deps = deps;
1284
+ this.axiosInstance = axios2.create();
1285
+ this.axiosInstance.defaults.baseURL = deps.serverUrl;
1286
+ }
1287
+ setAccessToken(token) {
1288
+ this.axiosInstance.defaults.headers.common.Authorization = `Bearer ${token}`;
1289
+ }
1290
+ async getRoutingConfig() {
1291
+ return getRoutingConfig(this.withDeps());
1292
+ }
1293
+ async setRoutingConfig(input) {
1294
+ return setRoutingConfig(this.withDeps(), input);
1295
+ }
1296
+ async createParseRule(input) {
1297
+ return createParseRule(this.withDeps(), input);
1298
+ }
1299
+ async listParseRules(input) {
1300
+ return listParseRules(this.withDeps(), input);
1301
+ }
1302
+ async registerWebhook(webhookUrl) {
1303
+ return registerWebhook(this.withDeps(), webhookUrl);
1304
+ }
1305
+ async deregisterWebhook() {
1306
+ return deregisterWebhook(this.withDeps());
1307
+ }
1308
+ async isWebhookConfigured() {
1309
+ return isWebhookConfigured(this.withDeps());
1310
+ }
1311
+ async welcomeMessageConfig(welcomeType, welcomeMessage) {
1312
+ return welcomeMessageConfig(this.withDeps(), { welcomeType, welcomeMessage });
1313
+ }
1314
+ withDeps() {
1315
+ return {
1316
+ axiosInstance: this.axiosInstance,
1317
+ serverUrl: this.deps.serverUrl,
1318
+ sdkToken: this.deps.sdkToken,
1319
+ ensureAuthenticated: this.deps.ensureAuthenticated
1320
+ };
1321
+ }
1322
+ };
1323
+
1324
+ // src/react-native-voip-sdk/core/conference/conferenceManager.ts
1325
+ var ConferenceManager = class {
1326
+ constructor(nativeModule, retry, emit) {
1327
+ this.nativeModule = nativeModule;
1328
+ this.retry = retry;
1329
+ this.emit = emit;
1330
+ }
1331
+ async getParticipants(callId) {
1332
+ const result = await this.retry.execute(
1333
+ async () => this.nativeModule.getParticipants({ callId }),
1334
+ { operation: "participant_fetch" }
1335
+ );
1336
+ if (!result.success || !result.data) {
1337
+ throw new Error(result.error ?? "Failed to fetch participants");
1338
+ }
1339
+ return result.data;
1340
+ }
1341
+ async disconnectParticipant(participantId) {
1342
+ const result = await this.retry.execute(
1343
+ async () => this.nativeModule.disconnectParticipant({ participantId }),
1344
+ { operation: "participant_disconnect" }
1345
+ );
1346
+ if (!result.success) {
1347
+ throw new Error(result.error ?? "Failed to disconnect participant");
1348
+ }
1349
+ this.emit("onParticipantLeft", { participantId });
1350
+ }
1351
+ };
1352
+
1353
+ // src/react-native-voip-sdk/core/retry/retryPolicy.ts
1354
+ var DEFAULT_RETRY_POLICY = {
1355
+ maxRetries: 4,
1356
+ baseDelayMs: 400,
1357
+ maxDelayMs: 8e3,
1358
+ jitterRatio: 0.25,
1359
+ circuitBreakerThreshold: 5,
1360
+ circuitBreakerCooldownMs: 15e3
1361
+ };
1362
+ function wait(ms) {
1363
+ return new Promise((resolve) => setTimeout(resolve, ms));
1364
+ }
1365
+ var CircuitBreaker = class {
1366
+ constructor(threshold, cooldownMs) {
1367
+ this.threshold = threshold;
1368
+ this.cooldownMs = cooldownMs;
1369
+ this.failureCount = 0;
1370
+ this.openedAt = 0;
1371
+ }
1372
+ canExecute() {
1373
+ if (this.failureCount < this.threshold) return true;
1374
+ if (Date.now() - this.openedAt > this.cooldownMs) {
1375
+ this.failureCount = 0;
1376
+ this.openedAt = 0;
1377
+ return true;
1378
+ }
1379
+ return false;
1380
+ }
1381
+ recordSuccess() {
1382
+ this.failureCount = 0;
1383
+ this.openedAt = 0;
1384
+ }
1385
+ recordFailure() {
1386
+ this.failureCount += 1;
1387
+ if (this.failureCount >= this.threshold && this.openedAt === 0) {
1388
+ this.openedAt = Date.now();
1389
+ }
1390
+ }
1391
+ };
1392
+ var RetryPolicy = class {
1393
+ constructor(config) {
1394
+ this.config = { ...DEFAULT_RETRY_POLICY, ...config };
1395
+ this.breaker = new CircuitBreaker(
1396
+ this.config.circuitBreakerThreshold,
1397
+ this.config.circuitBreakerCooldownMs
1398
+ );
1399
+ }
1400
+ async execute(task, context) {
1401
+ console.log("[VOIP DEBUG] retry.execute() start", {
1402
+ operation: context.operation,
1403
+ maxRetries: this.config.maxRetries
1404
+ });
1405
+ if (!this.breaker.canExecute()) {
1406
+ console.error("[VOIP DEBUG] circuit breaker open", {
1407
+ operation: context.operation
1408
+ });
1409
+ throw new Error(`Circuit open for operation: ${context.operation}`);
1410
+ }
1411
+ let attempt = 0;
1412
+ let lastError;
1413
+ while (attempt <= this.config.maxRetries) {
1414
+ try {
1415
+ if (attempt > 0) {
1416
+ console.log("[VOIP DEBUG] reconnect attempt", {
1417
+ operation: context.operation,
1418
+ retryCount: attempt
1419
+ });
1420
+ }
1421
+ const result = await task();
1422
+ this.breaker.recordSuccess();
1423
+ console.log("[VOIP DEBUG] retry.execute() success", {
1424
+ operation: context.operation,
1425
+ attempt
1426
+ });
1427
+ return result;
1428
+ } catch (error) {
1429
+ lastError = error;
1430
+ this.breaker.recordFailure();
1431
+ console.error("[VOIP DEBUG] retry.execute() failure", {
1432
+ operation: context.operation,
1433
+ attempt,
1434
+ message: error instanceof Error ? error.message : String(error)
1435
+ });
1436
+ if (attempt === this.config.maxRetries) break;
1437
+ const baseDelay = Math.min(
1438
+ this.config.baseDelayMs * 2 ** attempt,
1439
+ this.config.maxDelayMs
1440
+ );
1441
+ const jitter = baseDelay * this.config.jitterRatio * (Math.random() * 2 - 1);
1442
+ const delay = Math.max(0, Math.floor(baseDelay + jitter));
1443
+ console.log("[VOIP DEBUG] websocket state", {
1444
+ operation: context.operation,
1445
+ state: "RETRY_WAIT",
1446
+ delayMs: delay
1447
+ });
1448
+ await wait(delay);
1449
+ attempt += 1;
1450
+ }
1451
+ }
1452
+ console.error("[VOIP DEBUG] retry.execute() exhausted", {
1453
+ operation: context.operation
1454
+ });
1455
+ throw lastError instanceof Error ? lastError : new Error(`Retry failed for operation: ${context.operation}`);
1456
+ }
1457
+ };
1458
+
1459
+ // src/react-native-voip-sdk/js/public-api.ts
1460
+ function logVoipDebug(message, payload) {
1461
+ if (payload) {
1462
+ console.log(`[VOIP SDK] ${message}`, payload);
1463
+ return;
1464
+ }
1465
+ console.log(`[VOIP SDK] ${message}`);
1466
+ }
1467
+ var ReactNativeVoipSdk = class {
1468
+ constructor(config, nativeModule) {
1469
+ this.initialized = false;
1470
+ this.activeCallId = null;
1471
+ /** Tracks every in-flight call leg (parallel or single); kept in sync with native `onCallEnded`. */
1472
+ this.activeCallIds = /* @__PURE__ */ new Set();
1473
+ this.unsubscribeOnCallEnded = null;
1474
+ logVoipDebug("constructor() invoked", {
1475
+ hasServerUrl: Boolean(config.serverUrl),
1476
+ hasIdentity: Boolean(config.identity),
1477
+ hasSdkToken: Boolean(config.sdkToken),
1478
+ hasAccessToken: Boolean(config.accessToken)
1479
+ });
1480
+ this.internalConferenceName = `conf_${config.identity}_${Date.now()}`;
1481
+ this.sdkConfig = {
1482
+ serverUrl: config.serverUrl,
1483
+ consumerId: config.identity,
1484
+ identity: config.identity,
1485
+ sdkToken: config.sdkToken,
1486
+ accessToken: config.accessToken,
1487
+ waitUrl: config.waitUrl
1488
+ };
1489
+ this.bridge = new BridgeClient(nativeModule);
1490
+ this.retry = new RetryPolicy(config.retry);
1491
+ this.authManager = new AuthManager(
1492
+ {
1493
+ authenticate: async (payload) => this.bridge.nativeModule.authenticate(payload),
1494
+ refreshAuth: async () => this.bridge.nativeModule.refreshAuth?.() ?? {
1495
+ success: false,
1496
+ error: "Native refreshAuth not implemented"
1497
+ }
1498
+ },
1499
+ this.retry,
1500
+ (eventName, payload) => this.bridge.emit(eventName, payload)
1501
+ );
1502
+ this.callEngine = new CallEngine(
1503
+ this.bridge.nativeModule,
1504
+ this.retry,
1505
+ (eventName, payload) => this.bridge.emit(eventName, payload)
1506
+ );
1507
+ this.conferenceManager = new ConferenceManager(
1508
+ this.bridge.nativeModule,
1509
+ this.retry,
1510
+ (eventName, payload) => this.bridge.emit(eventName, payload)
1511
+ );
1512
+ this.configService = new ConfigService({
1513
+ serverUrl: this.sdkConfig.serverUrl,
1514
+ sdkToken: this.sdkConfig.sdkToken,
1515
+ ensureAuthenticated: this.ensureAuthenticated.bind(this)
1516
+ });
1517
+ this.unsubscribeOnCallEnded = this.bridge.on("onCallEnded", (payload) => {
1518
+ const callId = typeof payload.callId === "string" ? payload.callId : null;
1519
+ if (!callId) return;
1520
+ this.activeCallIds.delete(callId);
1521
+ if (this.activeCallId === callId) {
1522
+ this.activeCallId = this.pickNextActiveCallId();
1523
+ }
1524
+ });
1525
+ }
1526
+ on(eventName, listener) {
1527
+ return this.bridge.on(eventName, listener);
1528
+ }
1529
+ async initializeSDK(config) {
1530
+ logVoipDebug("initializeSDK() called");
1531
+ if (this.initialized) return;
1532
+ const mergedConfig = { ...this.sdkConfig, ...config };
1533
+ const result = await this.bridge.nativeModule.initializeSDK(mergedConfig);
1534
+ if (!result.success) {
1535
+ logVoipDebug("initializeSDK() failed", {
1536
+ error: result.error ?? "Failed to initialize native VOIP SDK"
1537
+ });
1538
+ throw new Error(result.error ?? "Failed to initialize native VOIP SDK");
1539
+ }
1540
+ this.initialized = true;
1541
+ logVoipDebug("initializeSDK() success");
1542
+ }
1543
+ async authenticate(payload) {
1544
+ logVoipDebug("authenticate() start", {
1545
+ hasAccessToken: Boolean(payload?.accessToken)
1546
+ });
1547
+ const auth = await this.authManager.authenticate(payload);
1548
+ this.configService.setAccessToken(auth.accessToken);
1549
+ logVoipDebug("authenticate() success", {
1550
+ consumerId: auth.consumerId,
1551
+ expiresAt: auth.expiresAt
1552
+ });
1553
+ return auth;
1554
+ }
1555
+ async startCall(number) {
1556
+ logVoipDebug("startCall() invoked", { number });
1557
+ this.ensureAuthenticated();
1558
+ const session = await this.callEngine.startCall(number);
1559
+ this.activeCallIds.add(session.callId);
1560
+ this.activeCallId = session.callId;
1561
+ logVoipDebug("startCall() success", { callId: session.callId });
1562
+ return session;
1563
+ }
1564
+ async answerCall(callId) {
1565
+ this.ensureAuthenticated();
1566
+ await this.callEngine.answerCall(callId);
1567
+ }
1568
+ async endCall(callId) {
1569
+ logVoipDebug("endCall() invoked", { callId });
1570
+ this.ensureAuthenticated();
1571
+ await this.callEngine.endCall(callId);
1572
+ this.activeCallIds.delete(callId);
1573
+ if (this.activeCallId === callId) {
1574
+ this.activeCallId = this.pickNextActiveCallId();
1575
+ }
1576
+ logVoipDebug("endCall() success", { callId });
1577
+ }
1578
+ async holdCall(callId) {
1579
+ this.ensureAuthenticated();
1580
+ await this.callEngine.holdCall(callId);
1581
+ }
1582
+ async resumeCall(callId) {
1583
+ this.ensureAuthenticated();
1584
+ await this.callEngine.resumeCall(callId);
1585
+ }
1586
+ async mergeCall(callId) {
1587
+ this.ensureAuthenticated();
1588
+ return this.callEngine.mergeCall(callId);
1589
+ }
1590
+ async getParticipants(callId) {
1591
+ this.ensureAuthenticated();
1592
+ return this.conferenceManager.getParticipants(callId);
1593
+ }
1594
+ async disconnectParticipant(participantId) {
1595
+ this.ensureAuthenticated();
1596
+ await this.conferenceManager.disconnectParticipant(participantId);
1597
+ }
1598
+ // Backward-compatible aliases
1599
+ async init() {
1600
+ return this.initializeSDK();
1601
+ }
1602
+ async verifySDKToken() {
1603
+ try {
1604
+ await this.authenticate();
1605
+ return true;
1606
+ } catch {
1607
+ return false;
1608
+ }
1609
+ }
1610
+ /**
1611
+ * Start a call or add to a conference.
1612
+ * Use `makeCall(to, { parallel: true })` (or `addCall(to)`) while a call is active to open a second
1613
+ * independent leg; failures on that leg are isolated to its `callId` in `onError` / `onCallEnded`.
1614
+ */
1615
+ async makeCall(to, options) {
1616
+ logVoipDebug("makeCall() start", {
1617
+ to,
1618
+ hasActiveCall: Boolean(this.activeCallId),
1619
+ parallel: Boolean(options?.parallel)
1620
+ });
1621
+ try {
1622
+ const destination = to.trim();
1623
+ if (!destination) {
1624
+ return createErrorResult("Destination required");
1625
+ }
1626
+ if (!this.activeCallId) {
1627
+ const data = await this.startCall(destination);
1628
+ logVoipDebug("makeCall() new call started", { callId: data.callId });
1629
+ return createSuccessResult("Call started", data);
1630
+ }
1631
+ if (options?.parallel) {
1632
+ const data = await this.startCall(destination);
1633
+ logVoipDebug("makeCall() parallel call started", { callId: data.callId });
1634
+ return createSuccessResult("Parallel call started", data);
1635
+ }
1636
+ if (!this.bridge.nativeModule.addNewCallee) {
1637
+ return createErrorResult("addNewCallee not supported by native module");
1638
+ }
1639
+ const result = await this.bridge.nativeModule.addNewCallee({
1640
+ callId: this.activeCallId,
1641
+ newParticipantNo: destination
1642
+ });
1643
+ if (!result.success) {
1644
+ logVoipDebug("makeCall() addNewCallee failed", { error: result.error });
1645
+ return createErrorResult(result.error ?? "Failed to add participant");
1646
+ }
1647
+ const existingSession = this.callEngine.getSession(this.activeCallId);
1648
+ const session = existingSession ?? {
1649
+ callId: this.activeCallId,
1650
+ direction: "OUTBOUND",
1651
+ status: "MERGED",
1652
+ participants: [],
1653
+ conferenceRoomId: null
1654
+ };
1655
+ logVoipDebug("makeCall() participant added", {
1656
+ callId: this.activeCallId,
1657
+ newParticipantNo: destination
1658
+ });
1659
+ return createSuccessResult("Participant added", session);
1660
+ } catch (error) {
1661
+ logVoipDebug("makeCall() failure", {
1662
+ message: error instanceof Error ? error.message : String(error)
1663
+ });
1664
+ return createErrorResult(error instanceof Error ? error : "Failed to start call");
1665
+ }
1666
+ }
1667
+ /** Start a parallel outbound call while another call is active (alias for `makeCall(to, { parallel: true })`). */
1668
+ async addCall(to) {
1669
+ return this.makeCall(to, { parallel: true });
1670
+ }
1671
+ /** All active call legs (parallel-aware). */
1672
+ getActiveCallIds() {
1673
+ return [...this.activeCallIds];
1674
+ }
1675
+ async acceptCall(callId) {
1676
+ try {
1677
+ const id = callId ?? this.activeCallId;
1678
+ if (!id) return createErrorResult("No active call to answer");
1679
+ await this.answerCall(id);
1680
+ return createSuccessResult("Call accepted", null);
1681
+ } catch (error) {
1682
+ return createErrorResult(error instanceof Error ? error : "Failed to accept call");
1683
+ }
1684
+ }
1685
+ async rejectCall(callId) {
1686
+ try {
1687
+ if (!this.bridge.nativeModule.rejectCall) {
1688
+ return createErrorResult("rejectCall not supported by native module");
1689
+ }
1690
+ const result = await this.bridge.nativeModule.rejectCall({ callId });
1691
+ if (!result.success) {
1692
+ return createErrorResult(result.error ?? "Failed to reject call");
1693
+ }
1694
+ return createSuccessResult("Call rejected", null);
1695
+ } catch (error) {
1696
+ return createErrorResult(error instanceof Error ? error : "Failed to reject call");
1697
+ }
1698
+ }
1699
+ async hangup(callId) {
1700
+ try {
1701
+ const id = callId ?? this.activeCallId;
1702
+ if (!id) return createErrorResult("No active call to end");
1703
+ await this.endCall(id);
1704
+ return createSuccessResult("Call ended", null);
1705
+ } catch (error) {
1706
+ return createErrorResult(error instanceof Error ? error : "Failed to end call");
1707
+ }
1708
+ }
1709
+ async hold(callId) {
1710
+ try {
1711
+ await this.holdCall(callId);
1712
+ return createSuccessResult("Call held successfully", null);
1713
+ } catch (error) {
1714
+ return createErrorResult(error instanceof Error ? error : "Failed to hold call");
1715
+ }
1716
+ }
1717
+ async resume(callId) {
1718
+ try {
1719
+ await this.resumeCall(callId);
1720
+ return createSuccessResult("Call resumed successfully", null);
1721
+ } catch (error) {
1722
+ return createErrorResult(error instanceof Error ? error : "Failed to resume call");
1723
+ }
1724
+ }
1725
+ async merge(callId) {
1726
+ try {
1727
+ const data = await this.mergeCall(callId);
1728
+ return createSuccessResult("Call merged successfully", data);
1729
+ } catch (error) {
1730
+ return createErrorResult(error instanceof Error ? error : "Failed to merge call");
1731
+ }
1732
+ }
1733
+ async toggleMute(mute, callId) {
1734
+ const id = callId ?? this.activeCallId;
1735
+ logVoipDebug("toggleMute() invoked", { mute, callId: id, hasActiveCall: Boolean(this.activeCallId) });
1736
+ try {
1737
+ if (!id) return createErrorResult("No active call");
1738
+ await this.callEngine.toggleMute(id, mute);
1739
+ return createSuccessResult(`Mute ${mute}`, null);
1740
+ } catch (error) {
1741
+ return createErrorResult(error instanceof Error ? error : "Failed to toggle mute");
1742
+ }
1743
+ }
1744
+ async makeCallIncoming(to, callSid) {
1745
+ logVoipDebug("makeCallIncoming() start", { to, callSid, hasActiveCall: Boolean(this.activeCallId) });
1746
+ try {
1747
+ const destination = to.trim();
1748
+ if (!destination) return createErrorResult("Destination required");
1749
+ if (!this.activeCallId) {
1750
+ const data = await this.startCall(destination);
1751
+ logVoipDebug("makeCallIncoming() new call started", { callId: data.callId });
1752
+ return createSuccessResult("Call started", data);
1753
+ }
1754
+ await this.holdAndAddParticipantIncoming(callSid, destination);
1755
+ const existingSession = this.callEngine.getSession(this.activeCallId);
1756
+ const session = existingSession ?? {
1757
+ callId: this.activeCallId,
1758
+ direction: "INBOUND",
1759
+ status: "MERGED",
1760
+ participants: [],
1761
+ conferenceRoomId: null
1762
+ };
1763
+ logVoipDebug("makeCallIncoming() participant added", { callId: this.activeCallId });
1764
+ return createSuccessResult("Participant added", session);
1765
+ } catch (error) {
1766
+ logVoipDebug("makeCallIncoming() failure", {
1767
+ message: error instanceof Error ? error.message : String(error)
1768
+ });
1769
+ return createErrorResult(error instanceof Error ? error : "Failed to start incoming call");
1770
+ }
1771
+ }
1772
+ async holdAndAddParticipant(callSid, number) {
1773
+ logVoipDebug("holdAndAddParticipant() invoked", { callSid, number });
1774
+ this.ensureAuthenticated();
1775
+ try {
1776
+ if (!callSid) throw new Error("callSid is required.");
1777
+ if (!number) throw new Error("Participant number is required.");
1778
+ if (!this.internalConferenceName) throw new Error("conferenceName is not set.");
1779
+ if (this.bridge.nativeModule.addNewCallee) {
1780
+ const id = this.activeCallId ?? callSid;
1781
+ const result = await this.bridge.nativeModule.addNewCallee({
1782
+ callId: id,
1783
+ newParticipantNo: number
1784
+ });
1785
+ if (!result.success) {
1786
+ return createErrorResult(result.error ?? "Failed to add participant");
1787
+ }
1788
+ return createSuccessResult("Participant added successfully", result.data ?? {});
1789
+ }
1790
+ const holdId = this.activeCallId ?? callSid;
1791
+ await this.holdCall(holdId);
1792
+ const configService = this.configService;
1793
+ const axiosInst = configService.axiosInstance ?? configService.deps?.axiosInstance;
1794
+ if (!axiosInst) throw new Error("HTTP client not available");
1795
+ const res = await axiosInst.post(
1796
+ `${this.sdkConfig.serverUrl}/outgoingCall.addNewCallee`,
1797
+ { conferenceName: this.internalConferenceName, newParticipantNo: number },
1798
+ { headers: { Authorization: `Bearer ${this.sdkConfig.sdkToken}` } }
1799
+ );
1800
+ return createSuccessResult("Participant added successfully", res?.data?.result ?? res?.data);
1801
+ } catch (error) {
1802
+ return createErrorResult(error instanceof Error ? error : "Failed to add participant");
1803
+ }
1804
+ }
1805
+ async holdAndAddParticipantIncoming(callSid, number) {
1806
+ logVoipDebug("holdAndAddParticipantIncoming() invoked", { callSid, number });
1807
+ this.ensureAuthenticated();
1808
+ try {
1809
+ if (!callSid) throw new Error("callSid is required.");
1810
+ if (!number) throw new Error("Participant number is required.");
1811
+ if (this.bridge.nativeModule.addNewCallee) {
1812
+ const id = this.activeCallId ?? callSid;
1813
+ const result = await this.bridge.nativeModule.addNewCallee({
1814
+ callId: id,
1815
+ newParticipantNo: number
1816
+ });
1817
+ if (!result.success) {
1818
+ return createErrorResult(result.error ?? "Failed to add participant");
1819
+ }
1820
+ return createSuccessResult("Participant added successfully", result.data ?? {});
1821
+ }
1822
+ const holdId = this.activeCallId ?? callSid;
1823
+ await this.holdCall(holdId);
1824
+ const configService = this.configService;
1825
+ const axiosInst = configService.axiosInstance ?? configService.deps?.axiosInstance;
1826
+ if (!axiosInst) throw new Error("HTTP client not available");
1827
+ const res = await axiosInst.post(
1828
+ `${this.sdkConfig.serverUrl}/outgoingCall.addNewCalleeIncoming`,
1829
+ { callSid, newParticipantNo: number },
1830
+ { headers: { Authorization: `Bearer ${this.sdkConfig.sdkToken}` } }
1831
+ );
1832
+ return createSuccessResult("Participant added successfully", res?.data?.result ?? res?.data);
1833
+ } catch (error) {
1834
+ return createErrorResult(error instanceof Error ? error : "Failed to add participant");
1835
+ }
1836
+ }
1837
+ async getConferenceId() {
1838
+ logVoipDebug("getConferenceId() invoked");
1839
+ this.ensureAuthenticated();
1840
+ try {
1841
+ if (!this.internalConferenceName) {
1842
+ throw new Error("conferenceName is required to fetch conference details.");
1843
+ }
1844
+ if (this.bridge.nativeModule.getConferenceId) {
1845
+ const result = await this.bridge.nativeModule.getConferenceId({
1846
+ conferenceName: this.internalConferenceName
1847
+ });
1848
+ if (!result.success) {
1849
+ return createErrorResult(result.error ?? "Failed to fetch conference details");
1850
+ }
1851
+ return createSuccessResult("Conference fetched successfully", result.data ?? null);
1852
+ }
1853
+ return createErrorResult("getConferenceId not supported");
1854
+ } catch (error) {
1855
+ return createErrorResult(error instanceof Error ? error : "Failed to fetch conference details");
1856
+ }
1857
+ }
1858
+ async getParticipantsIncoming(callSid) {
1859
+ logVoipDebug("getParticipantsIncoming() invoked", { callSid });
1860
+ this.ensureAuthenticated();
1861
+ try {
1862
+ if (this.bridge.nativeModule.getParticipantsByCallSid) {
1863
+ const result = await this.bridge.nativeModule.getParticipantsByCallSid({ callSid });
1864
+ if (!result.success) {
1865
+ return createErrorResult(result.error ?? "Failed to fetch participants");
1866
+ }
1867
+ return createSuccessResult("Participants fetched successfully", result.data ?? []);
1868
+ }
1869
+ return createErrorResult("getParticipantsByCallSid not supported");
1870
+ } catch (error) {
1871
+ return createErrorResult(error instanceof Error ? error : "Failed to fetch participants");
1872
+ }
1873
+ }
1874
+ async getParticipantsOutgoing(callSid) {
1875
+ logVoipDebug("getParticipantsOutgoing() invoked", { callSid });
1876
+ this.ensureAuthenticated();
1877
+ try {
1878
+ if (this.bridge.nativeModule.getParticipantsByCallSid) {
1879
+ const result = await this.bridge.nativeModule.getParticipantsByCallSid({ callSid });
1880
+ if (!result.success) {
1881
+ return createErrorResult(result.error ?? "Failed to fetch participants");
1882
+ }
1883
+ return createSuccessResult("Participants fetched successfully", result.data ?? []);
1884
+ }
1885
+ return createErrorResult("getParticipantsByCallSid not supported");
1886
+ } catch (error) {
1887
+ return createErrorResult(error instanceof Error ? error : "Failed to fetch participants");
1888
+ }
1889
+ }
1890
+ async removeParticipant(conferenceSid, callSid) {
1891
+ logVoipDebug("removeParticipant() invoked", { conferenceSid, callSid });
1892
+ this.ensureAuthenticated();
1893
+ try {
1894
+ if (!conferenceSid) throw new Error("conferenceSid is required.");
1895
+ if (!callSid) throw new Error("callSid is required.");
1896
+ if (this.bridge.nativeModule.removeParticipant) {
1897
+ const result = await this.bridge.nativeModule.removeParticipant({ conferenceSid, callSid });
1898
+ if (!result.success) {
1899
+ return createErrorResult(result.error ?? "Failed to remove participant");
1900
+ }
1901
+ return createSuccessResult("Participant removed successfully", result.data ?? {});
1902
+ }
1903
+ await this.disconnectParticipant(callSid);
1904
+ return createSuccessResult("Participant removed successfully", null);
1905
+ } catch (error) {
1906
+ return createErrorResult(error instanceof Error ? error : "Failed to remove participant");
1907
+ }
1908
+ }
1909
+ async getRoutingConfig() {
1910
+ return this.configService.getRoutingConfig();
1911
+ }
1912
+ async setRoutingConfig(input) {
1913
+ return this.configService.setRoutingConfig(input);
1914
+ }
1915
+ async createParseRule(input) {
1916
+ return this.configService.createParseRule(input);
1917
+ }
1918
+ async listParseRules(input) {
1919
+ return this.configService.listParseRules(input);
1920
+ }
1921
+ async registerWebhook(webhookUrl) {
1922
+ return this.configService.registerWebhook(webhookUrl);
1923
+ }
1924
+ async deregisterWebhook() {
1925
+ return this.configService.deregisterWebhook();
1926
+ }
1927
+ async isWebhookConfigured() {
1928
+ return this.configService.isWebhookConfigured();
1929
+ }
1930
+ async welcomeMessageConfig(welcomeType, welcomeMessage) {
1931
+ return this.configService.welcomeMessageConfig(welcomeType, welcomeMessage);
1932
+ }
1933
+ destroy() {
1934
+ logVoipDebug("destroy() invoked");
1935
+ this.unsubscribeOnCallEnded?.();
1936
+ this.unsubscribeOnCallEnded = null;
1937
+ this.authManager.destroy();
1938
+ this.bridge.destroy();
1939
+ this.initialized = false;
1940
+ this.activeCallId = null;
1941
+ this.activeCallIds.clear();
1942
+ logVoipDebug("destroy() completed");
1943
+ }
1944
+ pickNextActiveCallId() {
1945
+ if (this.activeCallIds.size === 0) return null;
1946
+ return this.activeCallIds.values().next().value ?? null;
1947
+ }
1948
+ ensureAuthenticated() {
1949
+ if (this.authManager.getState() !== "AUTHENTICATED") {
1950
+ throw new Error("SDK not authenticated.");
1951
+ }
1952
+ }
1953
+ };
1954
+
1955
+ // src/index.ts
1956
+ var ZyraTwilioWrapper = class extends ReactNativeVoipSdk {
1957
+ constructor(config, nativeModule) {
1958
+ super(config, nativeModule);
1959
+ this.activeCallSid = null;
1960
+ this.conferenceDetail = null;
1961
+ this.participants = [];
1962
+ this.waitUrl = "https://api.twilio.com/cowbell.mp3";
1963
+ this.conferenceName = this.internalConferenceName;
1964
+ this.waitUrl = config.waitUrl ?? this.waitUrl;
1965
+ this.on("onReady", () => this.onReady?.());
1966
+ this.on("onCallRinging", (payload) => this.onIncoming?.(payload));
1967
+ this.on("onCallConnected", (payload) => {
1968
+ if (typeof payload.callSid === "string") {
1969
+ this.activeCallSid = payload.callSid;
1970
+ }
1971
+ if (payload.conferenceDetail) {
1972
+ this.conferenceDetail = payload.conferenceDetail;
1973
+ }
1974
+ this.onConnect?.(payload);
1975
+ });
1976
+ this.on("onCallEnded", () => {
1977
+ if (this.getActiveCallIds().length === 0) {
1978
+ this.activeCallSid = null;
1979
+ this.onDisconnect?.();
1980
+ }
1981
+ });
1982
+ this.on("onMissedCall", () => {
1983
+ this.activeCallSid = null;
1984
+ this.onMissedCall?.();
1985
+ });
1986
+ this.on("onError", (payload) => {
1987
+ const message = typeof payload.message === "string" ? payload.message : "Unknown SDK error";
1988
+ this.onError?.(new Error(message));
1989
+ });
1990
+ }
1991
+ destroy() {
1992
+ this.activeCallSid = null;
1993
+ this.conferenceDetail = null;
1994
+ this.participants = [];
1995
+ super.destroy();
1996
+ }
1997
+ };
1998
+ export {
1999
+ ReactNativeVoipSdk,
2000
+ ZyraTwilioWrapper as default
2001
+ };
2002
+ //# sourceMappingURL=index.mjs.map