@zyratalk1/zyra-twilio-wrapper 1.2.0 → 1.2.2

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/.eslint.json ADDED
@@ -0,0 +1,15 @@
1
+ {
2
+ "env": {
3
+ "node": true,
4
+ "es2021": true
5
+ },
6
+ "extends": "eslint:recommended",
7
+ "parserOptions": {
8
+ "ecmaVersion": "latest",
9
+ "sourceType": "commonjs"
10
+ },
11
+ "rules": {
12
+ "semi": ["error", "always"],
13
+ "quotes": ["error", "double"]
14
+ }
15
+ }
package/dist/index.d.mts CHANGED
@@ -4,6 +4,7 @@ interface Config {
4
4
  waitUrl?: string;
5
5
  sdkToken: string;
6
6
  accessToken?: string;
7
+ enableAndroidVoiceRegister?: boolean;
7
8
  requestTimeoutMs?: number;
8
9
  retry?: {
9
10
  maxRetries?: number;
@@ -287,14 +288,6 @@ interface NativeModule {
287
288
  };
288
289
  }
289
290
  type SDKEventName = "onReady" | "onCallRinging" | "onCallConnected" | "onParticipantJoined" | "onParticipantLeft" | "onCallEnded" | "onCallStateChanged" | "onAuthStateChanged" | "onMissedCall" | "onError";
290
- /**
291
- * Options for {@link ReactNativeVoipSdk.makeCall}.
292
- * - `parallel`: start a second Twilio `voice.connect` leg while another call is active (independent lifecycle).
293
- * - Omitted / false: if a call is already active, add a conference participant via `addNewCallee` (existing behavior).
294
- */
295
- interface MakeCallOptions {
296
- parallel?: boolean;
297
- }
298
291
 
299
292
  type EventPayload = Record<string, unknown>;
300
293
  type Listener = (payload: EventPayload) => void;
@@ -327,9 +320,6 @@ declare class ReactNativeVoipSdk {
327
320
  private readonly configService;
328
321
  private initialized;
329
322
  protected activeCallId: string | null;
330
- /** Tracks every in-flight call leg (parallel or single); kept in sync with native `onCallEnded`. */
331
- protected activeCallIds: Set<string>;
332
- private unsubscribeOnCallEnded;
333
323
  protected internalConferenceName: string;
334
324
  constructor(config: Config, nativeModule?: NativeModule);
335
325
  on(eventName: SDKEventName | string, listener: (payload: Record<string, unknown>) => void): () => void;
@@ -345,23 +335,14 @@ declare class ReactNativeVoipSdk {
345
335
  disconnectParticipant(participantId: string): Promise<void>;
346
336
  init(): Promise<void>;
347
337
  verifySDKToken(): Promise<boolean>;
348
- /**
349
- * Start a call or add to a conference.
350
- * Use `makeCall(to, { parallel: true })` (or `addCall(to)`) while a call is active to open a second
351
- * independent leg; failures on that leg are isolated to its `callId` in `onError` / `onCallEnded`.
352
- */
353
- makeCall(to: string, options?: MakeCallOptions): Promise<CallResponse<CallSession>>;
354
- /** Start a parallel outbound call while another call is active (alias for `makeCall(to, { parallel: true })`). */
355
- addCall(to: string): Promise<CallResponse<CallSession>>;
356
- /** All active call legs (parallel-aware). */
357
- getActiveCallIds(): string[];
338
+ makeCall(to: string): Promise<CallResponse<CallSession>>;
358
339
  acceptCall(callId?: string): Promise<CallResponse<null>>;
359
340
  rejectCall(callId: string): Promise<CallResponse<null>>;
360
341
  hangup(callId?: string): Promise<CallResponse<null>>;
361
342
  hold(callId: string): Promise<CallResponse<null>>;
362
343
  resume(callId: string): Promise<CallResponse<null>>;
363
344
  merge(callId: string): Promise<CallResponse<CallSession>>;
364
- toggleMute(mute: boolean, callId?: string): Promise<CallResponse<null>>;
345
+ toggleMute(mute: boolean): Promise<CallResponse<null>>;
365
346
  makeCallIncoming(to: string, callSid: string): Promise<CallResponse<CallSession>>;
366
347
  holdAndAddParticipant(callSid: string, number: string): Promise<CallResponse<unknown>>;
367
348
  holdAndAddParticipantIncoming(callSid: string, number: string): Promise<CallResponse<unknown>>;
@@ -380,7 +361,6 @@ declare class ReactNativeVoipSdk {
380
361
  isWebhookConfigured(): Promise<CallResponse<WebhookConfigResult>>;
381
362
  welcomeMessageConfig(welcomeType: "audio" | "tts", welcomeMessage: string): Promise<CallResponse<WelcomeConfigResult>>;
382
363
  destroy(): void;
383
- private pickNextActiveCallId;
384
364
  private ensureAuthenticated;
385
365
  }
386
366
 
@@ -404,4 +384,4 @@ declare class ZyraTwilioWrapper extends ReactNativeVoipSdk {
404
384
  destroy(): void;
405
385
  }
406
386
 
407
- export { type AuthPayload, type CallSession, type ConsumerAuth, type MakeCallOptions, type NativeModule, type Participant, ReactNativeVoipSdk, type SDKConfig, type SDKEventName, ZyraTwilioWrapper as default };
387
+ export { type AuthPayload, type CallSession, type ConsumerAuth, type NativeModule, type Participant, ReactNativeVoipSdk, type SDKConfig, type SDKEventName, ZyraTwilioWrapper as default };
package/dist/index.d.ts CHANGED
@@ -4,6 +4,7 @@ interface Config {
4
4
  waitUrl?: string;
5
5
  sdkToken: string;
6
6
  accessToken?: string;
7
+ enableAndroidVoiceRegister?: boolean;
7
8
  requestTimeoutMs?: number;
8
9
  retry?: {
9
10
  maxRetries?: number;
@@ -287,14 +288,6 @@ interface NativeModule {
287
288
  };
288
289
  }
289
290
  type SDKEventName = "onReady" | "onCallRinging" | "onCallConnected" | "onParticipantJoined" | "onParticipantLeft" | "onCallEnded" | "onCallStateChanged" | "onAuthStateChanged" | "onMissedCall" | "onError";
290
- /**
291
- * Options for {@link ReactNativeVoipSdk.makeCall}.
292
- * - `parallel`: start a second Twilio `voice.connect` leg while another call is active (independent lifecycle).
293
- * - Omitted / false: if a call is already active, add a conference participant via `addNewCallee` (existing behavior).
294
- */
295
- interface MakeCallOptions {
296
- parallel?: boolean;
297
- }
298
291
 
299
292
  type EventPayload = Record<string, unknown>;
300
293
  type Listener = (payload: EventPayload) => void;
@@ -327,9 +320,6 @@ declare class ReactNativeVoipSdk {
327
320
  private readonly configService;
328
321
  private initialized;
329
322
  protected activeCallId: string | null;
330
- /** Tracks every in-flight call leg (parallel or single); kept in sync with native `onCallEnded`. */
331
- protected activeCallIds: Set<string>;
332
- private unsubscribeOnCallEnded;
333
323
  protected internalConferenceName: string;
334
324
  constructor(config: Config, nativeModule?: NativeModule);
335
325
  on(eventName: SDKEventName | string, listener: (payload: Record<string, unknown>) => void): () => void;
@@ -345,23 +335,14 @@ declare class ReactNativeVoipSdk {
345
335
  disconnectParticipant(participantId: string): Promise<void>;
346
336
  init(): Promise<void>;
347
337
  verifySDKToken(): Promise<boolean>;
348
- /**
349
- * Start a call or add to a conference.
350
- * Use `makeCall(to, { parallel: true })` (or `addCall(to)`) while a call is active to open a second
351
- * independent leg; failures on that leg are isolated to its `callId` in `onError` / `onCallEnded`.
352
- */
353
- makeCall(to: string, options?: MakeCallOptions): Promise<CallResponse<CallSession>>;
354
- /** Start a parallel outbound call while another call is active (alias for `makeCall(to, { parallel: true })`). */
355
- addCall(to: string): Promise<CallResponse<CallSession>>;
356
- /** All active call legs (parallel-aware). */
357
- getActiveCallIds(): string[];
338
+ makeCall(to: string): Promise<CallResponse<CallSession>>;
358
339
  acceptCall(callId?: string): Promise<CallResponse<null>>;
359
340
  rejectCall(callId: string): Promise<CallResponse<null>>;
360
341
  hangup(callId?: string): Promise<CallResponse<null>>;
361
342
  hold(callId: string): Promise<CallResponse<null>>;
362
343
  resume(callId: string): Promise<CallResponse<null>>;
363
344
  merge(callId: string): Promise<CallResponse<CallSession>>;
364
- toggleMute(mute: boolean, callId?: string): Promise<CallResponse<null>>;
345
+ toggleMute(mute: boolean): Promise<CallResponse<null>>;
365
346
  makeCallIncoming(to: string, callSid: string): Promise<CallResponse<CallSession>>;
366
347
  holdAndAddParticipant(callSid: string, number: string): Promise<CallResponse<unknown>>;
367
348
  holdAndAddParticipantIncoming(callSid: string, number: string): Promise<CallResponse<unknown>>;
@@ -380,7 +361,6 @@ declare class ReactNativeVoipSdk {
380
361
  isWebhookConfigured(): Promise<CallResponse<WebhookConfigResult>>;
381
362
  welcomeMessageConfig(welcomeType: "audio" | "tts", welcomeMessage: string): Promise<CallResponse<WelcomeConfigResult>>;
382
363
  destroy(): void;
383
- private pickNextActiveCallId;
384
364
  private ensureAuthenticated;
385
365
  }
386
366
 
@@ -404,4 +384,4 @@ declare class ZyraTwilioWrapper extends ReactNativeVoipSdk {
404
384
  destroy(): void;
405
385
  }
406
386
 
407
- export { type AuthPayload, type CallSession, type ConsumerAuth, type MakeCallOptions, type NativeModule, type Participant, ReactNativeVoipSdk, type SDKConfig, type SDKEventName, ZyraTwilioWrapper as default };
387
+ export { type AuthPayload, type CallSession, type ConsumerAuth, type NativeModule, type Participant, ReactNativeVoipSdk, type SDKConfig, type SDKEventName, ZyraTwilioWrapper as default };
package/dist/index.js CHANGED
@@ -209,15 +209,14 @@ var TwilioVoiceAdapter = class {
209
209
  error: "Twilio access token missing. Pass accessToken in constructor or authenticate({ accessToken })."
210
210
  };
211
211
  }
212
- const conferenceName = `conf_${this.sdkConfig.consumerId}_${input.callId}`;
213
212
  console.log("[VOIP SDK] voice.connect() params", {
214
213
  To: input.number,
215
- conferenceName
214
+ conferenceName: this.defaultConferenceName
216
215
  });
217
216
  const call = await this.voice.connect(token, {
218
217
  params: {
219
218
  To: input.number,
220
- conferenceName
219
+ conferenceName: this.defaultConferenceName
221
220
  }
222
221
  });
223
222
  const callSid = this.readCallSid(call);
@@ -226,7 +225,7 @@ var TwilioVoiceAdapter = class {
226
225
  callSid,
227
226
  call,
228
227
  direction: "OUTBOUND",
229
- conferenceName,
228
+ conferenceName: this.defaultConferenceName,
230
229
  conferenceDetail: null
231
230
  };
232
231
  this.calls.set(input.callId, tracked);
@@ -268,10 +267,26 @@ var TwilioVoiceAdapter = class {
268
267
  };
269
268
  }
270
269
  }
270
+ async waitForInvite(callId, timeoutMs = 8e3) {
271
+ const existing = this.callInvites.get(callId);
272
+ if (existing) return existing;
273
+ console.log("[VOIP DEBUG] waitForInvite polling\u2026", { callId, timeoutMs });
274
+ const start = Date.now();
275
+ while (Date.now() - start < timeoutMs) {
276
+ await new Promise((r) => setTimeout(r, 300));
277
+ const inv = this.callInvites.get(callId);
278
+ if (inv) {
279
+ console.log("[VOIP DEBUG] waitForInvite found invite", { callId, elapsed: Date.now() - start });
280
+ return inv;
281
+ }
282
+ }
283
+ console.warn("[VOIP DEBUG] waitForInvite timed out", { callId });
284
+ return null;
285
+ }
271
286
  async answerCall(input) {
272
287
  console.log("[VOIP DEBUG] call accepted flow start", { callId: input.callId });
273
288
  try {
274
- const invite = this.callInvites.get(input.callId);
289
+ const invite = await this.waitForInvite(input.callId);
275
290
  if (!invite) {
276
291
  return { success: false, error: "Incoming call invite not found" };
277
292
  }
@@ -1505,9 +1520,6 @@ var ReactNativeVoipSdk = class {
1505
1520
  constructor(config, nativeModule) {
1506
1521
  this.initialized = false;
1507
1522
  this.activeCallId = null;
1508
- /** Tracks every in-flight call leg (parallel or single); kept in sync with native `onCallEnded`. */
1509
- this.activeCallIds = /* @__PURE__ */ new Set();
1510
- this.unsubscribeOnCallEnded = null;
1511
1523
  logVoipDebug("constructor() invoked", {
1512
1524
  hasServerUrl: Boolean(config.serverUrl),
1513
1525
  hasIdentity: Boolean(config.identity),
@@ -1521,7 +1533,8 @@ var ReactNativeVoipSdk = class {
1521
1533
  identity: config.identity,
1522
1534
  sdkToken: config.sdkToken,
1523
1535
  accessToken: config.accessToken,
1524
- waitUrl: config.waitUrl
1536
+ waitUrl: config.waitUrl,
1537
+ enableAndroidVoiceRegister: config.enableAndroidVoiceRegister
1525
1538
  };
1526
1539
  this.bridge = new BridgeClient(nativeModule);
1527
1540
  this.retry = new RetryPolicy(config.retry);
@@ -1551,14 +1564,6 @@ var ReactNativeVoipSdk = class {
1551
1564
  sdkToken: this.sdkConfig.sdkToken,
1552
1565
  ensureAuthenticated: this.ensureAuthenticated.bind(this)
1553
1566
  });
1554
- this.unsubscribeOnCallEnded = this.bridge.on("onCallEnded", (payload) => {
1555
- const callId = typeof payload.callId === "string" ? payload.callId : null;
1556
- if (!callId) return;
1557
- this.activeCallIds.delete(callId);
1558
- if (this.activeCallId === callId) {
1559
- this.activeCallId = this.pickNextActiveCallId();
1560
- }
1561
- });
1562
1567
  }
1563
1568
  on(eventName, listener) {
1564
1569
  return this.bridge.on(eventName, listener);
@@ -1593,7 +1598,6 @@ var ReactNativeVoipSdk = class {
1593
1598
  logVoipDebug("startCall() invoked", { number });
1594
1599
  this.ensureAuthenticated();
1595
1600
  const session = await this.callEngine.startCall(number);
1596
- this.activeCallIds.add(session.callId);
1597
1601
  this.activeCallId = session.callId;
1598
1602
  logVoipDebug("startCall() success", { callId: session.callId });
1599
1603
  return session;
@@ -1606,9 +1610,8 @@ var ReactNativeVoipSdk = class {
1606
1610
  logVoipDebug("endCall() invoked", { callId });
1607
1611
  this.ensureAuthenticated();
1608
1612
  await this.callEngine.endCall(callId);
1609
- this.activeCallIds.delete(callId);
1610
1613
  if (this.activeCallId === callId) {
1611
- this.activeCallId = this.pickNextActiveCallId();
1614
+ this.activeCallId = null;
1612
1615
  }
1613
1616
  logVoipDebug("endCall() success", { callId });
1614
1617
  }
@@ -1644,17 +1647,8 @@ var ReactNativeVoipSdk = class {
1644
1647
  return false;
1645
1648
  }
1646
1649
  }
1647
- /**
1648
- * Start a call or add to a conference.
1649
- * Use `makeCall(to, { parallel: true })` (or `addCall(to)`) while a call is active to open a second
1650
- * independent leg; failures on that leg are isolated to its `callId` in `onError` / `onCallEnded`.
1651
- */
1652
- async makeCall(to, options) {
1653
- logVoipDebug("makeCall() start", {
1654
- to,
1655
- hasActiveCall: Boolean(this.activeCallId),
1656
- parallel: Boolean(options?.parallel)
1657
- });
1650
+ async makeCall(to) {
1651
+ logVoipDebug("makeCall() start", { to, hasActiveCall: Boolean(this.activeCallId) });
1658
1652
  try {
1659
1653
  const destination = to.trim();
1660
1654
  if (!destination) {
@@ -1665,11 +1659,6 @@ var ReactNativeVoipSdk = class {
1665
1659
  logVoipDebug("makeCall() new call started", { callId: data.callId });
1666
1660
  return createSuccessResult("Call started", data);
1667
1661
  }
1668
- if (options?.parallel) {
1669
- const data = await this.startCall(destination);
1670
- logVoipDebug("makeCall() parallel call started", { callId: data.callId });
1671
- return createSuccessResult("Parallel call started", data);
1672
- }
1673
1662
  if (!this.bridge.nativeModule.addNewCallee) {
1674
1663
  return createErrorResult("addNewCallee not supported by native module");
1675
1664
  }
@@ -1701,14 +1690,6 @@ var ReactNativeVoipSdk = class {
1701
1690
  return createErrorResult(error instanceof Error ? error : "Failed to start call");
1702
1691
  }
1703
1692
  }
1704
- /** Start a parallel outbound call while another call is active (alias for `makeCall(to, { parallel: true })`). */
1705
- async addCall(to) {
1706
- return this.makeCall(to, { parallel: true });
1707
- }
1708
- /** All active call legs (parallel-aware). */
1709
- getActiveCallIds() {
1710
- return [...this.activeCallIds];
1711
- }
1712
1693
  async acceptCall(callId) {
1713
1694
  try {
1714
1695
  const id = callId ?? this.activeCallId;
@@ -1767,10 +1748,10 @@ var ReactNativeVoipSdk = class {
1767
1748
  return createErrorResult(error instanceof Error ? error : "Failed to merge call");
1768
1749
  }
1769
1750
  }
1770
- async toggleMute(mute, callId) {
1771
- const id = callId ?? this.activeCallId;
1772
- logVoipDebug("toggleMute() invoked", { mute, callId: id, hasActiveCall: Boolean(this.activeCallId) });
1751
+ async toggleMute(mute) {
1752
+ logVoipDebug("toggleMute() invoked", { mute, hasActiveCall: Boolean(this.activeCallId) });
1773
1753
  try {
1754
+ const id = this.activeCallId;
1774
1755
  if (!id) return createErrorResult("No active call");
1775
1756
  await this.callEngine.toggleMute(id, mute);
1776
1757
  return createSuccessResult(`Mute ${mute}`, null);
@@ -1969,19 +1950,12 @@ var ReactNativeVoipSdk = class {
1969
1950
  }
1970
1951
  destroy() {
1971
1952
  logVoipDebug("destroy() invoked");
1972
- this.unsubscribeOnCallEnded?.();
1973
- this.unsubscribeOnCallEnded = null;
1974
1953
  this.authManager.destroy();
1975
1954
  this.bridge.destroy();
1976
1955
  this.initialized = false;
1977
1956
  this.activeCallId = null;
1978
- this.activeCallIds.clear();
1979
1957
  logVoipDebug("destroy() completed");
1980
1958
  }
1981
- pickNextActiveCallId() {
1982
- if (this.activeCallIds.size === 0) return null;
1983
- return this.activeCallIds.values().next().value ?? null;
1984
- }
1985
1959
  ensureAuthenticated() {
1986
1960
  if (this.authManager.getState() !== "AUTHENTICATED") {
1987
1961
  throw new Error("SDK not authenticated.");
@@ -2011,10 +1985,8 @@ var ZyraTwilioWrapper = class extends ReactNativeVoipSdk {
2011
1985
  this.onConnect?.(payload);
2012
1986
  });
2013
1987
  this.on("onCallEnded", () => {
2014
- if (this.getActiveCallIds().length === 0) {
2015
- this.activeCallSid = null;
2016
- this.onDisconnect?.();
2017
- }
1988
+ this.activeCallSid = null;
1989
+ this.onDisconnect?.();
2018
1990
  });
2019
1991
  this.on("onMissedCall", () => {
2020
1992
  this.activeCallSid = null;