@dora-cell/sdk 0.1.1-beta.10

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.js ADDED
@@ -0,0 +1,704 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
5
+ var JsSIP = require('jssip');
6
+ var EventEmitter = require('eventemitter3');
7
+
8
+ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
9
+
10
+ var JsSIP__default = /*#__PURE__*/_interopDefault(JsSIP);
11
+ var EventEmitter__default = /*#__PURE__*/_interopDefault(EventEmitter);
12
+
13
+ // src/core/DoraCell.ts
14
+
15
+ // src/types/index.ts
16
+ var DoraCellError = class extends Error {
17
+ constructor(message, code, details) {
18
+ super(message);
19
+ this.code = code;
20
+ this.details = details;
21
+ this.name = "DoraCellError";
22
+ }
23
+ };
24
+ var AuthenticationError = class extends DoraCellError {
25
+ constructor(message, details) {
26
+ super(message, "AUTH_ERROR", details);
27
+ this.name = "AuthenticationError";
28
+ }
29
+ };
30
+ var CallError = class extends DoraCellError {
31
+ constructor(message, details) {
32
+ super(message, "CALL_ERROR", details);
33
+ this.name = "CallError";
34
+ }
35
+ };
36
+ var ConnectionError = class extends DoraCellError {
37
+ constructor(message, details) {
38
+ super(message, "CONNECTION_ERROR", details);
39
+ this.name = "ConnectionError";
40
+ }
41
+ };
42
+
43
+ // src/core/AuthProvider.ts
44
+ var ApiTokenAuthProvider = class {
45
+ constructor(apiToken, apiBaseUrl) {
46
+ this.credentials = null;
47
+ this.apiToken = apiToken;
48
+ let defaultBaseUrl = "";
49
+ if (typeof process !== "undefined" && process.env) {
50
+ defaultBaseUrl = process.env.NEXT_PUBLIC_BASE_API_URL || "";
51
+ }
52
+ this.apiBaseUrl = apiBaseUrl || defaultBaseUrl;
53
+ }
54
+ async authenticate(config) {
55
+ if (config.type !== "api-token") {
56
+ throw new AuthenticationError("Invalid auth config type for ApiTokenAuthProvider");
57
+ }
58
+ try {
59
+ const response = await fetch(`${this.apiBaseUrl}/api/sip-credentials`, {
60
+ method: "GET",
61
+ headers: {
62
+ "Authorization": `Bearer ${this.apiToken}`,
63
+ "Content-Type": "application/json",
64
+ "Accept": "application/json"
65
+ },
66
+ credentials: "include"
67
+ });
68
+ if (!response.ok) {
69
+ if (response.status === 401 || response.status === 403) {
70
+ throw new AuthenticationError(
71
+ "Invalid API token or insufficient permissions",
72
+ { status: response.status }
73
+ );
74
+ }
75
+ throw new AuthenticationError(
76
+ `Failed to fetch SIP credentials: ${response.status}`,
77
+ { status: response.status }
78
+ );
79
+ }
80
+ const data = await response.json();
81
+ this.credentials = this.parseCredentials(data);
82
+ return this.credentials;
83
+ } catch (error) {
84
+ if (error instanceof AuthenticationError) {
85
+ throw error;
86
+ }
87
+ throw new AuthenticationError(
88
+ `Authentication failed: ${error instanceof Error ? error.message : "Unknown error"}`,
89
+ { originalError: error }
90
+ );
91
+ }
92
+ }
93
+ async refreshCredentials() {
94
+ return this.authenticate({ type: "api-token", apiToken: this.apiToken });
95
+ }
96
+ isAuthenticated() {
97
+ return this.credentials !== null;
98
+ }
99
+ parseCredentials(data) {
100
+ const wsUrl = data.ws_url || data.wsUrl;
101
+ const sipUri = data.sip_uri || data.sipUri;
102
+ const password = data.password;
103
+ const sipDomain = data.sip_domain || data.sipDomain;
104
+ const extensions = data.extensions;
105
+ const expiresIn = data.expires_in || data.expiresIn;
106
+ if (!wsUrl || !sipUri) {
107
+ throw new AuthenticationError(
108
+ "Invalid credentials response: missing ws_url or sip_uri"
109
+ );
110
+ }
111
+ return {
112
+ wsUrl,
113
+ sipUri,
114
+ password: password || "",
115
+ sipDomain,
116
+ extensions,
117
+ expiresIn
118
+ };
119
+ }
120
+ };
121
+ var DirectCredentialsAuthProvider = class {
122
+ constructor() {
123
+ this.credentials = null;
124
+ }
125
+ async authenticate(config) {
126
+ if (config.type !== "direct") {
127
+ throw new AuthenticationError("Invalid auth config type for DirectCredentialsAuthProvider");
128
+ }
129
+ this.credentials = {
130
+ wsUrl: config.wsUrl,
131
+ sipUri: config.sipUri,
132
+ password: config.password
133
+ };
134
+ return this.credentials;
135
+ }
136
+ isAuthenticated() {
137
+ return this.credentials !== null;
138
+ }
139
+ };
140
+ var DEFAULT_WSS_URL = "wss://cell.usedora.com:8089";
141
+ var DEFAULT_SIP_IP = "64.227.10.164";
142
+ var DEFAULT_PASSWORD = "1234";
143
+ var ExtensionAuthProvider = class {
144
+ constructor() {
145
+ this.credentials = null;
146
+ }
147
+ async authenticate(config) {
148
+ if (config.type !== "extension") {
149
+ throw new AuthenticationError("Invalid auth config type for ExtensionAuthProvider");
150
+ }
151
+ const extension = config.extension;
152
+ const password = config.password || DEFAULT_PASSWORD;
153
+ const wsUrl = DEFAULT_WSS_URL;
154
+ const sipDomain = config.sipDomain || DEFAULT_SIP_IP;
155
+ const sipUri = `sip:${extension}@${sipDomain}`;
156
+ this.credentials = {
157
+ wsUrl,
158
+ sipUri,
159
+ password,
160
+ sipDomain,
161
+ extensions: [{ extension, displayName: `Ext ${extension}`, isPrimary: true }]
162
+ };
163
+ return this.credentials;
164
+ }
165
+ isAuthenticated() {
166
+ return this.credentials !== null;
167
+ }
168
+ };
169
+ function createAuthProvider(config) {
170
+ switch (config.type) {
171
+ case "api-token":
172
+ return new ApiTokenAuthProvider(config.apiToken, config.apiBaseUrl);
173
+ case "direct":
174
+ return new DirectCredentialsAuthProvider();
175
+ case "extension":
176
+ return new ExtensionAuthProvider();
177
+ default:
178
+ throw new AuthenticationError("Unknown authentication type");
179
+ }
180
+ }
181
+
182
+ // src/utils/phoneFormatter.ts
183
+ function formatPhoneToSIP(phone, sipDomain) {
184
+ let cleaned = phone.replace(/\D/g, "");
185
+ if (cleaned.length <= 4) {
186
+ return `sip:${cleaned}@${sipDomain}`;
187
+ }
188
+ if (cleaned.startsWith("0")) {
189
+ cleaned = "234" + cleaned.substring(1);
190
+ return `sip:${cleaned}@${sipDomain}`;
191
+ }
192
+ if (cleaned.startsWith("234")) {
193
+ return `sip:${cleaned}@${sipDomain}`;
194
+ }
195
+ cleaned = "234" + cleaned;
196
+ return `sip:${cleaned}@${sipDomain}`;
197
+ }
198
+ function normalizePhoneNumber(phone, countryCode = "234") {
199
+ let cleaned = phone.replace(/\D/g, "");
200
+ if (cleaned.length <= 4) {
201
+ return cleaned;
202
+ }
203
+ if (cleaned.startsWith("0")) {
204
+ return countryCode + cleaned.substring(1);
205
+ }
206
+ if (cleaned.startsWith(countryCode)) {
207
+ return cleaned;
208
+ }
209
+ return countryCode + cleaned;
210
+ }
211
+ function extractNumberFromSipUri(sipUri) {
212
+ const match = sipUri.match(/sip:([^@]+)@/);
213
+ return match ? match[1] : sipUri;
214
+ }
215
+ function isValidPhoneNumber(phone, minLength = 3) {
216
+ const cleaned = phone.replace(/\D/g, "");
217
+ return cleaned.length >= minLength;
218
+ }
219
+
220
+ // src/core/CallManager.ts
221
+ var CallSession = class {
222
+ constructor(session, direction, remoteNumber, localExtension, events) {
223
+ this.status = "idle";
224
+ this.duration = 0;
225
+ // JsSIP RTCSession
226
+ this._isMuted = false;
227
+ this.remoteStreamValue = null;
228
+ this.id = this.generateCallId();
229
+ this.session = session;
230
+ this.direction = direction;
231
+ this.remoteNumber = remoteNumber;
232
+ this.localExtension = localExtension;
233
+ this.events = events;
234
+ this.setupSessionHandlers();
235
+ }
236
+ generateCallId() {
237
+ return `call_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
238
+ }
239
+ setupSessionHandlers() {
240
+ this.session.on("progress", (evt) => {
241
+ const code = evt.response?.status_code;
242
+ if (code === 180 || code === 183) {
243
+ this.status = "ringing";
244
+ this.events.emit("call:ringing", this);
245
+ }
246
+ });
247
+ this.session.on("confirmed", () => {
248
+ this.status = "ongoing";
249
+ this.startTime = Date.now();
250
+ this.startDurationTimer();
251
+ this.events.emit("call:connected", this);
252
+ });
253
+ this.session.on("peerconnection", (evt) => {
254
+ evt.peerconnection.ontrack = (event) => {
255
+ if (event.streams && event.streams[0]) {
256
+ this.remoteStreamValue = event.streams[0];
257
+ }
258
+ };
259
+ });
260
+ this.session.on("ended", (evt) => {
261
+ this.handleCallEnd(evt?.cause);
262
+ });
263
+ this.session.on("failed", (evt) => {
264
+ this.handleCallEnd(evt?.cause || "Call failed");
265
+ });
266
+ this.session.on("rejected", (evt) => {
267
+ this.handleCallEnd(evt?.cause || "Call rejected");
268
+ });
269
+ }
270
+ handleCallEnd(reason) {
271
+ this.status = "ended";
272
+ this.endTime = Date.now();
273
+ this.stopDurationTimer();
274
+ this.remoteStreamValue = null;
275
+ this._isMuted = false;
276
+ this.events.emit("call:ended", this, reason);
277
+ }
278
+ startDurationTimer() {
279
+ this.durationInterval = window.setInterval(() => {
280
+ if (this.startTime) {
281
+ this.duration = Math.floor((Date.now() - this.startTime) / 1e3);
282
+ }
283
+ }, 1e3);
284
+ }
285
+ stopDurationTimer() {
286
+ if (this.durationInterval) {
287
+ clearInterval(this.durationInterval);
288
+ this.durationInterval = void 0;
289
+ }
290
+ }
291
+ // Public methods
292
+ mute() {
293
+ if (this.session && !this._isMuted) {
294
+ this.session.mute({ audio: true });
295
+ this._isMuted = true;
296
+ }
297
+ }
298
+ unmute() {
299
+ if (this.session && this._isMuted) {
300
+ this.session.unmute({ audio: true });
301
+ this._isMuted = false;
302
+ }
303
+ }
304
+ hangup() {
305
+ if (this.session) {
306
+ this.session.terminate();
307
+ }
308
+ }
309
+ isMuted() {
310
+ return this._isMuted;
311
+ }
312
+ getRemoteStream() {
313
+ return this.remoteStreamValue;
314
+ }
315
+ getFormattedDuration() {
316
+ const mm = String(Math.floor(this.duration / 60)).padStart(2, "0");
317
+ const ss = String(this.duration % 60).padStart(2, "0");
318
+ return `${mm}:${ss}`;
319
+ }
320
+ destroy() {
321
+ this.stopDurationTimer();
322
+ this.session = null;
323
+ this.remoteStreamValue = null;
324
+ }
325
+ };
326
+ var CallManager = class {
327
+ constructor(credentials, events, callConfig) {
328
+ this.ua = null;
329
+ // JsSIP User Agent
330
+ this.currentCall = null;
331
+ this.credentials = credentials;
332
+ this.events = events;
333
+ this.callConfig = callConfig;
334
+ }
335
+ setUserAgent(ua) {
336
+ this.ua = ua;
337
+ }
338
+ /**
339
+ * Initiate an outbound call
340
+ */
341
+ async initiateCall(targetNumber, options) {
342
+ if (!this.ua) {
343
+ throw new CallError("User Agent not initialized");
344
+ }
345
+ if (this.currentCall && this.currentCall.status !== "ended") {
346
+ throw new CallError("A call is already in progress");
347
+ }
348
+ try {
349
+ await navigator.mediaDevices.getUserMedia({ audio: true });
350
+ const extension = options?.extension || this.getDefaultExtension();
351
+ const sipDomain = this.extractDomain(this.credentials.sipUri);
352
+ const sipTarget = formatPhoneToSIP(targetNumber, sipDomain);
353
+ const session = this.ua.call(sipTarget, {
354
+ mediaConstraints: options?.mediaConstraints || { audio: true },
355
+ pcConfig: this.callConfig.pcConfig
356
+ });
357
+ const call = new CallSession(
358
+ session,
359
+ "outbound",
360
+ targetNumber,
361
+ extension,
362
+ this.events
363
+ );
364
+ call.status = "connecting";
365
+ this.currentCall = call;
366
+ this.events.emit("call:outgoing", call);
367
+ return call;
368
+ } catch (error) {
369
+ throw new CallError(
370
+ `Failed to initiate call: ${error instanceof Error ? error.message : "Unknown error"}`,
371
+ { originalError: error }
372
+ );
373
+ }
374
+ }
375
+ /**
376
+ * Answer an incoming call
377
+ */
378
+ answerCall(session) {
379
+ if (!session) {
380
+ throw new CallError("No session provided");
381
+ }
382
+ try {
383
+ const remoteUri = session.remote_identity?.uri?.toString() || "Unknown";
384
+ const extension = this.getDefaultExtension();
385
+ session.answer({
386
+ mediaConstraints: { audio: true },
387
+ pcConfig: this.callConfig.pcConfig
388
+ });
389
+ const call = new CallSession(
390
+ session,
391
+ "inbound",
392
+ remoteUri,
393
+ extension,
394
+ this.events
395
+ );
396
+ this.currentCall = call;
397
+ return call;
398
+ } catch (error) {
399
+ throw new CallError(
400
+ `Failed to answer call: ${error instanceof Error ? error.message : "Unknown error"}`,
401
+ { originalError: error }
402
+ );
403
+ }
404
+ }
405
+ /**
406
+ * Handle incoming call from JsSIP
407
+ */
408
+ handleIncomingCall(session) {
409
+ const remoteUri = session.remote_identity?.uri?.toString() || "Unknown";
410
+ const extension = this.getDefaultExtension();
411
+ const call = new CallSession(
412
+ session,
413
+ "inbound",
414
+ remoteUri,
415
+ extension,
416
+ this.events
417
+ );
418
+ call.status = "ringing";
419
+ this.currentCall = call;
420
+ this.events.emit("call:incoming", call);
421
+ return call;
422
+ }
423
+ getCurrentCall() {
424
+ return this.currentCall;
425
+ }
426
+ clearCurrentCall() {
427
+ if (this.currentCall) {
428
+ this.currentCall.destroy();
429
+ this.currentCall = null;
430
+ }
431
+ }
432
+ getDefaultExtension() {
433
+ if (this.credentials.extensions && this.credentials.extensions.length > 0) {
434
+ return this.credentials.extensions[0].extension;
435
+ }
436
+ return this.extractExtension(this.credentials.sipUri);
437
+ }
438
+ extractExtension(sipUri) {
439
+ const match = sipUri.match(/sip:([^@]+)@/);
440
+ return match ? match[1] : "unknown";
441
+ }
442
+ extractDomain(sipUri) {
443
+ const match = sipUri.match(/@(.+)$/);
444
+ return match ? match[1] : "";
445
+ }
446
+ destroy() {
447
+ this.clearCurrentCall();
448
+ this.ua = null;
449
+ }
450
+ };
451
+
452
+ // src/core/DoraCell.ts
453
+ var DoraCell = class {
454
+ constructor(config) {
455
+ this.credentials = null;
456
+ this.ua = null;
457
+ // JsSIP User Agent
458
+ this.callManager = null;
459
+ this.connectionStatus = "disconnected";
460
+ this.retryCount = 0;
461
+ this.maxRetries = 3;
462
+ this.config = {
463
+ autoSelectExtension: true,
464
+ debug: false,
465
+ ...config
466
+ };
467
+ this.events = new EventEmitter__default.default();
468
+ this.authProvider = createAuthProvider(config.auth);
469
+ if (this.config.debug) {
470
+ JsSIP__default.default.debug.enable("JsSIP:*");
471
+ }
472
+ }
473
+ /**
474
+ * Initialize the SDK - authenticate and connect to SIP server
475
+ */
476
+ async initialize() {
477
+ try {
478
+ this.credentials = await this.authProvider.authenticate(this.config.auth);
479
+ await this.initializeUserAgent();
480
+ this.initializeCallManager();
481
+ } catch (error) {
482
+ const errorMessage = error instanceof Error ? error.message : "Unknown error";
483
+ this.emitError(new ConnectionError(`Initialization failed: ${errorMessage}`));
484
+ throw error;
485
+ }
486
+ }
487
+ async initializeUserAgent() {
488
+ if (!this.credentials) {
489
+ throw new ConnectionError("No credentials available");
490
+ }
491
+ try {
492
+ const socket = new JsSIP__default.default.WebSocketInterface(this.credentials.wsUrl);
493
+ const pcConfig = {
494
+ iceServers: this.config.turnServers || this.getDefaultTurnServers()
495
+ };
496
+ const uaConfig = {
497
+ uri: this.credentials.sipUri,
498
+ password: this.credentials.password,
499
+ sockets: [socket],
500
+ register: true,
501
+ display_name: this.getDisplayName(),
502
+ session_timers: false,
503
+ use_preloaded_route: false,
504
+ pcConfig,
505
+ instance_id: this.generateInstanceId()
506
+ };
507
+ this.ua = new JsSIP__default.default.UA(uaConfig);
508
+ this.setupUserAgentHandlers();
509
+ this.ua.start();
510
+ } catch (error) {
511
+ throw new ConnectionError(
512
+ `Failed to initialize User Agent: ${error instanceof Error ? error.message : "Unknown error"}`
513
+ );
514
+ }
515
+ }
516
+ setupUserAgentHandlers() {
517
+ if (!this.ua) return;
518
+ this.ua.on("connected", () => {
519
+ this.connectionStatus = "connected";
520
+ this.emitConnectionStatus();
521
+ });
522
+ this.ua.on("disconnected", () => {
523
+ this.connectionStatus = "disconnected";
524
+ this.emitConnectionStatus();
525
+ });
526
+ this.ua.on("registered", () => {
527
+ this.connectionStatus = "registered";
528
+ this.retryCount = 0;
529
+ this.emitConnectionStatus();
530
+ });
531
+ this.ua.on("registrationFailed", (e) => {
532
+ this.connectionStatus = "registrationFailed";
533
+ this.emitConnectionStatus(e.cause);
534
+ if (this.retryCount < this.maxRetries) {
535
+ this.retryCount++;
536
+ setTimeout(() => {
537
+ this.ua?.register();
538
+ }, 3e3);
539
+ }
540
+ });
541
+ this.ua.on("newRTCSession", (e) => {
542
+ const session = e.session;
543
+ if (session.direction === "incoming") {
544
+ this.callManager?.handleIncomingCall(session);
545
+ }
546
+ });
547
+ }
548
+ initializeCallManager() {
549
+ if (!this.credentials) {
550
+ throw new ConnectionError("No credentials available for call manager");
551
+ }
552
+ const pcConfig = {
553
+ iceServers: this.config.turnServers || this.getDefaultTurnServers()
554
+ };
555
+ this.callManager = new CallManager(
556
+ this.credentials,
557
+ this.events,
558
+ { pcConfig }
559
+ );
560
+ this.callManager.setUserAgent(this.ua);
561
+ }
562
+ /**
563
+ * Make an outbound call
564
+ */
565
+ async call(phoneNumber, options) {
566
+ if (!this.callManager) {
567
+ throw new CallError("SDK not initialized. Call initialize() first.");
568
+ }
569
+ if (this.connectionStatus !== "registered") {
570
+ throw new CallError("Not registered to SIP server");
571
+ }
572
+ return this.callManager.initiateCall(phoneNumber, options);
573
+ }
574
+ /**
575
+ * Answer an incoming call
576
+ */
577
+ answerCall() {
578
+ const currentCall = this.callManager?.getCurrentCall();
579
+ if (!currentCall) {
580
+ throw new CallError("No incoming call to answer");
581
+ }
582
+ if (currentCall.direction !== "inbound") {
583
+ throw new CallError("Current call is not an incoming call");
584
+ }
585
+ }
586
+ /**
587
+ * Hangup the current call
588
+ */
589
+ hangup() {
590
+ const currentCall = this.callManager?.getCurrentCall();
591
+ if (!currentCall) {
592
+ throw new CallError("No active call to hang up");
593
+ }
594
+ currentCall.hangup();
595
+ }
596
+ /**
597
+ * Get current call
598
+ */
599
+ getCurrentCall() {
600
+ return this.callManager?.getCurrentCall() || null;
601
+ }
602
+ /**
603
+ * Get connection status
604
+ */
605
+ getStatus() {
606
+ return this.connectionStatus;
607
+ }
608
+ /**
609
+ * Get available extensions
610
+ */
611
+ getExtensions() {
612
+ if (!this.credentials?.extensions) {
613
+ return [];
614
+ }
615
+ return this.credentials.extensions.map((ext) => ext.extension);
616
+ }
617
+ /**
618
+ * Event listener management
619
+ */
620
+ on(event, handler) {
621
+ this.events.on(event, handler);
622
+ }
623
+ off(event, handler) {
624
+ this.events.off(event, handler);
625
+ }
626
+ once(event, handler) {
627
+ this.events.once(event, handler);
628
+ }
629
+ /**
630
+ * Cleanup and destroy the SDK instance
631
+ */
632
+ destroy() {
633
+ if (this.ua) {
634
+ this.ua.stop();
635
+ this.ua = null;
636
+ }
637
+ if (this.callManager) {
638
+ this.callManager.destroy();
639
+ this.callManager = null;
640
+ }
641
+ this.events.removeAllListeners();
642
+ this.connectionStatus = "disconnected";
643
+ this.credentials = null;
644
+ }
645
+ // Helper methods
646
+ emitConnectionStatus(error) {
647
+ const state = {
648
+ status: this.connectionStatus,
649
+ extension: this.credentials?.extensions?.[0]?.extension,
650
+ error
651
+ };
652
+ this.events.emit("connection:status", state);
653
+ }
654
+ emitError(error) {
655
+ this.events.emit("error", error);
656
+ }
657
+ getDefaultTurnServers() {
658
+ let turnUri = "turn:64.227.10.164:3478";
659
+ let turnUser = "02018890089";
660
+ let turnPass = "dora12345";
661
+ if (typeof process !== "undefined" && process.env) {
662
+ turnUri = process.env.NEXT_PUBLIC_TURN_URI || turnUri;
663
+ turnUser = process.env.NEXT_PUBLIC_TURN_USER || turnUser;
664
+ turnPass = process.env.NEXT_PUBLIC_TURN_PASS || turnPass;
665
+ }
666
+ return [
667
+ {
668
+ urls: turnUri,
669
+ username: turnUser,
670
+ credential: turnPass
671
+ }
672
+ ];
673
+ }
674
+ getDisplayName() {
675
+ if (this.credentials?.extensions?.[0]?.displayName) {
676
+ return this.credentials.extensions[0].displayName;
677
+ }
678
+ if (this.credentials?.extensions?.[0]?.extension) {
679
+ return `Ext ${this.credentials.extensions[0].extension}`;
680
+ }
681
+ return "Dora Cell User";
682
+ }
683
+ generateInstanceId() {
684
+ return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
685
+ const r = Math.random() * 16 | 0;
686
+ const v = c === "x" ? r : r & 3 | 8;
687
+ return v.toString(16);
688
+ });
689
+ }
690
+ };
691
+
692
+ exports.AuthenticationError = AuthenticationError;
693
+ exports.CallError = CallError;
694
+ exports.CallSession = CallSession;
695
+ exports.ConnectionError = ConnectionError;
696
+ exports.DoraCell = DoraCell;
697
+ exports.DoraCellError = DoraCellError;
698
+ exports.default = DoraCell;
699
+ exports.extractNumberFromSipUri = extractNumberFromSipUri;
700
+ exports.formatPhoneToSIP = formatPhoneToSIP;
701
+ exports.isValidPhoneNumber = isValidPhoneNumber;
702
+ exports.normalizePhoneNumber = normalizePhoneNumber;
703
+ //# sourceMappingURL=index.js.map
704
+ //# sourceMappingURL=index.js.map