@dora-cell/sdk 0.1.1-beta.9 → 1.0.1

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/README.md CHANGED
@@ -8,124 +8,101 @@ VoIP calling SDK for Dora Cell - Make calls from any JavaScript application with
8
8
  ✅ **TypeScript support** - Full type definitions included
9
9
  ✅ **WebRTC-based** - High-quality voice calls using JsSIP
10
10
  ✅ **Event-driven API** - Listen to call state changes
11
- ✅ **API token authentication** - Secure, no user login required
12
- ✅ **Auto-extension selection** - Automatically selects first available extension
13
- ✅ **React bindings** - Optional React hooks for easy integration
11
+ ✅ **API Token Auth** - Securely authenticate with Public & Secret keys
12
+ ✅ **Dynamic Extensions** - Fetch and switch between DID numbers
13
+ ✅ **Wallet Integration** - Check credit balance directly from the SDK
14
+ ✅ **React bindings** - Premium UI components and hooks included
14
15
 
15
16
  ## Installation
16
17
 
17
18
  ```bash
18
- npm install @dora-cell/sdk jssip
19
+ npm install @dora-cell/sdk
19
20
  # or
20
- yarn add @dora-cell/sdk jssip
21
+ yarn add @dora-cell/sdk
21
22
  # or
22
- pnpm add @dora-cell/sdk jssip
23
+ pnpm add @dora-cell/sdk
23
24
  ```
24
25
 
25
- For React projects:
26
- ```bash
27
- npm install @dora-cell/sdk @dora-cell/sdk-react jssip
28
- ```
26
+ > [!NOTE]
27
+ > `jssip` is a dependency of this SDK and will be installed automatically. You only need to install it manually if you want to use a specific version.
29
28
 
30
29
  ## Quick Start
31
30
 
32
- ### Vanilla JavaScript
31
+ ### 1. Initialize the SDK
32
+
33
+ Choose your preferred authentication method:
34
+
35
+ #### Option A: Extension Authentication (Recommended for Testing)
36
+
37
+ Best for quick testing or internal apps where default credentials are used.
33
38
 
34
39
  ```javascript
35
- import { DoraCell } from '@dora-cell/sdk';
40
+ import { DoraCell } from "@dora-cell/sdk";
36
41
 
37
- // Initialize SDK with API token
38
42
  const sdk = new DoraCell({
39
43
  auth: {
40
- type: 'api-token',
41
- apiToken: 'your-api-token-here',
42
- apiBaseUrl: 'https://api.usedora.com'
44
+ type: "extension",
45
+ extension: "1001",
43
46
  },
44
- debug: true // Enable console logging
45
47
  });
48
+ ```
46
49
 
47
- // Initialize connection
48
- await sdk.initialize();
50
+ #### Option B: API Token Authentication (Production)
49
51
 
50
- // Listen for connection status
51
- sdk.on('connection:status', (state) => {
52
- console.log('Connection status:', state.status);
53
- });
52
+ Best for production apps. The SDK will verify keys and fetch SIP credentials automatically.
54
53
 
55
- // Listen for call events
56
- sdk.on('call:connected', (call) => {
57
- console.log('Call connected:', call.remoteNumber);
54
+ ```javascript
55
+ const sdk = new DoraCell({
56
+ auth: {
57
+ type: "api-token",
58
+ publicKey: "pk_live_...",
59
+ secretKey: "sk_live_...",
60
+ },
58
61
  });
62
+ ```
63
+
64
+ #### Option C: Direct Credentials (Advanced)
59
65
 
60
- sdk.on('call:ended', (call, reason) => {
61
- console.log('Call ended:', reason);
66
+ If you already have SIP credentials.
67
+
68
+ ```javascript
69
+ const sdk = new DoraCell({
70
+ auth: {
71
+ type: "direct",
72
+ sipUri: "sip:1001@voice.example.com",
73
+ password: "your-password",
74
+ wsUrl: "wss://voice.example.com:8089/ws",
75
+ },
62
76
  });
77
+ ```
63
78
 
64
- // Make a call
65
- const call = await sdk.call('+2348012345678');
79
+ ### 2. Connect and Register
66
80
 
67
- // Mute/unmute
68
- call.mute();
69
- call.unmute();
81
+ ```javascript
82
+ // Listen for registration
83
+ sdk.on("connection:status", (state) => {
84
+ if (state.status === "registered") {
85
+ console.log("SIP Ready to make calls!");
86
+ }
87
+ });
70
88
 
71
- // Hang up
72
- call.hangup();
89
+ // Initialize connection
90
+ await sdk.initialize();
73
91
  ```
74
92
 
75
- ### React
76
-
77
- ```tsx
78
- import { DoraCellProvider, useCall, useConnectionStatus } from '@dora-cell/sdk-react';
79
-
80
- function App() {
81
- return (
82
- <DoraCellProvider
83
- config={{
84
- auth: {
85
- type: 'api-token',
86
- apiToken: 'your-api-token-here',
87
- apiBaseUrl: 'https://api.usedora.com'
88
- }
89
- }}
90
- >
91
- <CallInterface />
92
- </DoraCellProvider>
93
- );
94
- }
93
+ ### 3. Make a Call
94
+
95
+ ```javascript
96
+ try {
97
+ const call = await sdk.call("+2348012345678");
98
+
99
+ // Mute/unmute
100
+ call.mute();
95
101
 
96
- function CallInterface() {
97
- const { call, hangup, toggleMute, callStatus, callDuration, isMuted } = useCall();
98
- const { isConnected } = useConnectionStatus();
99
- const [number, setNumber] = useState('');
100
-
101
- const handleCall = async () => {
102
- try {
103
- await call(number);
104
- } catch (error) {
105
- console.error('Call failed:', error);
106
- }
107
- };
108
-
109
- return (
110
- <div>
111
- <p>Status: {isConnected ? 'Connected' : 'Disconnected'}</p>
112
- <input
113
- value={number}
114
- onChange={(e) => setNumber(e.target.value)}
115
- placeholder="Enter phone number"
116
- />
117
- <button onClick={handleCall} disabled={!isConnected}>
118
- Call
119
- </button>
120
- {callStatus === 'ongoing' && (
121
- <>
122
- <p>Duration: {callDuration}</p>
123
- <button onClick={toggleMute}>{isMuted ? 'Unmute' : 'Mute'}</button>
124
- <button onClick={hangup}>Hang Up</button>
125
- </>
126
- )}
127
- </div>
128
- );
102
+ // Hang up
103
+ call.hangup();
104
+ } catch (error) {
105
+ console.error("Call failed:", error.message);
129
106
  }
130
107
  ```
131
108
 
@@ -133,143 +110,64 @@ function CallInterface() {
133
110
 
134
111
  ### DoraCell Class
135
112
 
136
- #### Constructor
113
+ #### `new DoraCell(config)`
137
114
 
138
- ```typescript
139
- new DoraCell(config: DoraCellConfig)
140
- ```
141
-
142
- **Config options:**
143
- - `auth` - Authentication configuration (required)
144
- - `type: 'api-token'` - Use API token authentication
145
- - `apiToken: string` - Your Dora Cell API token
146
- - `apiBaseUrl?: string` - API base URL (optional)
147
- - `turnServers?: RTCIceServer[]` - Custom TURN/STUN servers
148
- - `debug?: boolean` - Enable debug logging
149
- - `autoSelectExtension?: boolean` - Auto-select first extension (default: true)
115
+ - `config.auth` (Required)
116
+ - `type: 'api-token' | 'extension' | 'direct'`
117
+ - `publicKey/secretKey` (for api-token)
118
+ - `extension` (for extension type)
119
+ - `config.environment?: 'dev' | 'staging' | 'production'` - Shortcut for API URLs (default: 'production')
120
+ - `config.autoSelectExtension?: boolean` - Register first available DID (default: true)
121
+ - `config.debug?: boolean` - Enable JsSIP debug logs (default: false)
150
122
 
151
123
  #### Methods
152
124
 
153
- **`initialize(): Promise<void>`**
154
- Initialize the SDK and connect to SIP server.
155
-
156
- **`call(phoneNumber: string, options?: CallOptions): Promise<Call>`**
157
- Make an outbound call.
158
-
159
- Options:
160
- - `extension?: string` - Specific extension to use for this call
161
-
162
- **`hangup(): void`**
163
- Hang up the current call.
164
-
165
- **`answerCall(): void`**
166
- Answer an incoming call.
167
-
168
- **`getCurrentCall(): Call | null`**
169
- Get the current active call.
170
-
171
- **`getStatus(): ConnectionStatus`**
172
- Get current connection status.
173
-
174
- **`getExtensions(): string[]`**
175
- Get list of available extensions.
176
-
177
- **`on(event, handler): void`**
178
- Register event listener.
179
-
180
- **`off(event, handler): void`**
181
- Remove event listener.
182
-
183
- **`destroy(): void`**
184
- Cleanup and destroy SDK instance.
185
-
186
- ### Events
187
-
188
- ```typescript
189
- sdk.on('connection:status', (state: ConnectionState) => {});
190
- sdk.on('call:incoming', (call: Call) => {});
191
- sdk.on('call:outgoing', (call: Call) => {});
192
- sdk.on('call:ringing', (call: Call) => {});
193
- sdk.on('call:connected', (call: Call) => {});
194
- sdk.on('call:ended', (call: Call, reason?: string) => {});
195
- sdk.on('call:failed', (call: Call, error: string) => {});
196
- sdk.on('error', (error: Error) => {});
197
- ```
125
+ - `initialize(): Promise<void>` - Authenticate and register with the SIP server.
126
+ - `call(number, extension?): Promise<Call>` - Initiate an outbound call.
127
+ - `getWallet(): Promise<{ balance: number, currency: string }>` - Get current credit balance.
128
+ - `fetchExtensions(): Promise<Extension[]>` - Fetch available DID numbers/extensions.
129
+ - `setExtension(ext): Promise<void>` - Switch active extension and re-register SIP.
130
+ - `answerCall(): void` - Answer a pending incoming call.
131
+ - `hangup(): void` - End the current active call.
132
+ - `on(event, handler): void` - Listen for events (see below).
133
+ - `destroy(): void` - Cleanup and disconnect.
198
134
 
199
135
  ### Call Object
200
136
 
201
- ```typescript
202
- interface Call {
203
- id: string;
204
- status: CallStatus;
205
- direction: 'inbound' | 'outbound';
206
- remoteNumber: string;
207
- localExtension: string;
208
- duration: number;
209
-
210
- mute(): void;
211
- unmute(): void;
212
- hangup(): void;
213
- isMuted(): boolean;
214
- }
215
- ```
216
-
217
- ## Authentication
218
-
219
- ### Getting an API Token
220
-
221
- 1. Log in to your Dora Cell dashboard
222
- 2. Navigate to Settings → API Tokens
223
- 3. Generate a new API token
224
- 4. Copy the token and use it in your SDK configuration
225
-
226
- ### Backend API Endpoint
227
-
228
- The SDK expects your backend to provide a `/api/sip-credentials` endpoint that:
229
- - Accepts `Authorization: Bearer <token>` header
230
- - Returns SIP credentials in this format:
231
-
232
- ```json
233
- {
234
- "ws_url": "wss://cell.usedora.com:8089/ws",
235
- "sip_uri": "sip:101@64.227.10.164",
236
- "password": "your-password",
237
- "extensions": [
238
- { "extension": "101", "displayName": "Main Line" }
239
- ]
240
- }
241
- ```
242
-
243
- ## Phone Number Formatting
137
+ Returned by `call()` and emitted in events.
244
138
 
245
- The SDK automatically handles Nigerian phone number formatting:
139
+ - `id`: Unique call ID.
140
+ - `status`: `'idle' | 'connecting' | 'ringing' | 'ongoing' | 'ended'`.
141
+ - `direction`: `'inbound' | 'outbound'`.
142
+ - `remoteNumber`: The other party's number.
143
+ - `duration`: Call duration in seconds.
144
+ - `mute()` / `unmute()`: Control microphone.
145
+ - `hangup()`: End this specific call.
146
+ - `getRemoteStream()`: Returns the WebRTC `MediaStream` for audio playback.
246
147
 
247
- - `08012345678` → `sip:2348012345678@domain`
248
- - `2348012345678` → `sip:2348012345678@domain`
249
- - `101` (extension) → `sip:101@domain`
148
+ ## Events
250
149
 
251
- ## Error Handling
150
+ | Event | Description | Data |
151
+ | ------------------- | ------------------------------ | ---------------------------------- |
152
+ | `connection:status` | SIP registration status change | `{ status: string, extension?: string, error?: string }` |
153
+ | `call:incoming` | New incoming call detected | `Call` object |
154
+ | `call:outgoing` | New outbound call started | `Call` object |
155
+ | `call:ringing` | Remote party is ringing | `Call` object |
156
+ | `call:connected` | Call answered and connected | `Call` object |
157
+ | `call:ended` | Call terminated | `Call` object, `reason: string` |
158
+ | `call:failed` | Call failed to connect | `Call` object, `error: string` |
159
+ | `error` | General SDK error | `Error` object |
252
160
 
253
- ```javascript
254
- import { AuthenticationError, CallError, ConnectionError } from '@dora-cell/sdk';
161
+ ## Browser Requirements
255
162
 
256
- try {
257
- await sdk.initialize();
258
- } catch (error) {
259
- if (error instanceof AuthenticationError) {
260
- console.error('Authentication failed:', error.message);
261
- } else if (error instanceof ConnectionError) {
262
- console.error('Connection failed:', error.message);
263
- }
264
- }
265
- ```
163
+ - **HTTPS**: WebRTC and microphone access require a secure context (HTTPS or localhost).
164
+ - **Environment**: Modern browsers with WebRTC support (Chrome, Firefox, Safari, Edge).
266
165
 
267
166
  ## Examples
268
167
 
269
- See the `/examples` directory for complete working examples:
270
- - `vanilla-js/` - Pure JavaScript implementation
271
- - `react-app/` - React application
272
- - `nextjs-app/` - Next.js application
168
+ Check the [`examples/`](../../examples) directory in the main repository:
169
+
170
+ - `next-js-demo/` - Complete Next.js implementation with React components
273
171
 
274
172
  ## License
275
173
 
package/dist/index.d.mts CHANGED
@@ -5,8 +5,8 @@ import EventEmitter from 'eventemitter3';
5
5
  */
6
6
  interface ApiTokenAuth {
7
7
  type: 'api-token';
8
- apiToken: string;
9
- apiBaseUrl?: string;
8
+ publicKey: string;
9
+ secretKey: string;
10
10
  }
11
11
  interface DirectCredentialsAuth {
12
12
  type: 'direct';
@@ -21,8 +21,11 @@ interface ExtensionAuth {
21
21
  sipDomain?: string;
22
22
  }
23
23
  type AuthConfig = ApiTokenAuth | DirectCredentialsAuth | ExtensionAuth;
24
+ type DoraCellEnv = 'dev' | 'staging' | 'production' | 'local';
24
25
  interface DoraCellConfig {
25
26
  auth: AuthConfig;
27
+ apiBaseUrl?: string;
28
+ environment?: DoraCellEnv;
26
29
  sipServer?: string;
27
30
  turnServers?: RTCIceServer[];
28
31
  debug?: boolean;
@@ -60,6 +63,7 @@ interface Call {
60
63
  unmute(): void;
61
64
  hangup(): void;
62
65
  isMuted(): boolean;
66
+ getRemoteStream(): MediaStream | null;
63
67
  }
64
68
  type ConnectionStatus = 'disconnected' | 'connecting' | 'connected' | 'registered' | 'registrationFailed';
65
69
  interface ConnectionState {
@@ -105,6 +109,8 @@ declare class DoraCell {
105
109
  private credentials;
106
110
  private ua;
107
111
  private callManager;
112
+ private apiClient;
113
+ private apiBaseUrl;
108
114
  private connectionStatus;
109
115
  private retryCount;
110
116
  private maxRetries;
@@ -113,6 +119,7 @@ declare class DoraCell {
113
119
  * Initialize the SDK - authenticate and connect to SIP server
114
120
  */
115
121
  initialize(): Promise<void>;
122
+ private waitForRegistration;
116
123
  private initializeUserAgent;
117
124
  private setupUserAgentHandlers;
118
125
  private initializeCallManager;
@@ -132,14 +139,29 @@ declare class DoraCell {
132
139
  * Get current call
133
140
  */
134
141
  getCurrentCall(): Call | null;
142
+ /**
143
+ * Get user's wallet/credit balance
144
+ */
145
+ getWallet(): Promise<{
146
+ balance: number;
147
+ currency: string;
148
+ }>;
135
149
  /**
136
150
  * Get connection status
137
151
  */
138
152
  getStatus(): ConnectionStatus;
139
153
  /**
140
- * Get available extensions
154
+ * Fetch available extensions (DID numbers) from the API
155
+ */
156
+ fetchExtensions(): Promise<any[]>;
157
+ /**
158
+ * Get available extensions (DID numbers)
159
+ */
160
+ getExtensions(): any[];
161
+ /**
162
+ * Update active extension and re-initialize SIP connection
141
163
  */
142
- getExtensions(): string[];
164
+ setExtension(extension: string): Promise<void>;
143
165
  /**
144
166
  * Event listener management
145
167
  */
@@ -155,6 +177,7 @@ declare class DoraCell {
155
177
  private getDefaultTurnServers;
156
178
  private getDisplayName;
157
179
  private generateInstanceId;
180
+ private resolveApiBaseUrl;
158
181
  }
159
182
 
160
183
  /**
@@ -213,4 +236,4 @@ declare function extractNumberFromSipUri(sipUri: string): string;
213
236
  */
214
237
  declare function isValidPhoneNumber(phone: string, minLength?: number): boolean;
215
238
 
216
- export { type ApiTokenAuth, type AuthConfig, AuthenticationError, type Call, type CallDirection, CallError, type CallOptions, CallSession, type CallStatus, ConnectionError, type ConnectionState, type ConnectionStatus, type DirectCredentialsAuth, DoraCell, type DoraCellConfig, DoraCellError, type DoraCellEvent, type DoraCellEventMap, type Extension, type SipCredentials, DoraCell as default, extractNumberFromSipUri, formatPhoneToSIP, isValidPhoneNumber, normalizePhoneNumber };
239
+ export { type ApiTokenAuth, type AuthConfig, AuthenticationError, type Call, type CallDirection, CallError, type CallOptions, CallSession, type CallStatus, ConnectionError, type ConnectionState, type ConnectionStatus, type DirectCredentialsAuth, DoraCell, type DoraCellConfig, DoraCellError, type DoraCellEvent, type DoraCellEventMap, type Extension, type ExtensionAuth, type SipCredentials, DoraCell as default, extractNumberFromSipUri, formatPhoneToSIP, isValidPhoneNumber, normalizePhoneNumber };
package/dist/index.d.ts CHANGED
@@ -5,8 +5,8 @@ import EventEmitter from 'eventemitter3';
5
5
  */
6
6
  interface ApiTokenAuth {
7
7
  type: 'api-token';
8
- apiToken: string;
9
- apiBaseUrl?: string;
8
+ publicKey: string;
9
+ secretKey: string;
10
10
  }
11
11
  interface DirectCredentialsAuth {
12
12
  type: 'direct';
@@ -21,8 +21,11 @@ interface ExtensionAuth {
21
21
  sipDomain?: string;
22
22
  }
23
23
  type AuthConfig = ApiTokenAuth | DirectCredentialsAuth | ExtensionAuth;
24
+ type DoraCellEnv = 'dev' | 'staging' | 'production' | 'local';
24
25
  interface DoraCellConfig {
25
26
  auth: AuthConfig;
27
+ apiBaseUrl?: string;
28
+ environment?: DoraCellEnv;
26
29
  sipServer?: string;
27
30
  turnServers?: RTCIceServer[];
28
31
  debug?: boolean;
@@ -60,6 +63,7 @@ interface Call {
60
63
  unmute(): void;
61
64
  hangup(): void;
62
65
  isMuted(): boolean;
66
+ getRemoteStream(): MediaStream | null;
63
67
  }
64
68
  type ConnectionStatus = 'disconnected' | 'connecting' | 'connected' | 'registered' | 'registrationFailed';
65
69
  interface ConnectionState {
@@ -105,6 +109,8 @@ declare class DoraCell {
105
109
  private credentials;
106
110
  private ua;
107
111
  private callManager;
112
+ private apiClient;
113
+ private apiBaseUrl;
108
114
  private connectionStatus;
109
115
  private retryCount;
110
116
  private maxRetries;
@@ -113,6 +119,7 @@ declare class DoraCell {
113
119
  * Initialize the SDK - authenticate and connect to SIP server
114
120
  */
115
121
  initialize(): Promise<void>;
122
+ private waitForRegistration;
116
123
  private initializeUserAgent;
117
124
  private setupUserAgentHandlers;
118
125
  private initializeCallManager;
@@ -132,14 +139,29 @@ declare class DoraCell {
132
139
  * Get current call
133
140
  */
134
141
  getCurrentCall(): Call | null;
142
+ /**
143
+ * Get user's wallet/credit balance
144
+ */
145
+ getWallet(): Promise<{
146
+ balance: number;
147
+ currency: string;
148
+ }>;
135
149
  /**
136
150
  * Get connection status
137
151
  */
138
152
  getStatus(): ConnectionStatus;
139
153
  /**
140
- * Get available extensions
154
+ * Fetch available extensions (DID numbers) from the API
155
+ */
156
+ fetchExtensions(): Promise<any[]>;
157
+ /**
158
+ * Get available extensions (DID numbers)
159
+ */
160
+ getExtensions(): any[];
161
+ /**
162
+ * Update active extension and re-initialize SIP connection
141
163
  */
142
- getExtensions(): string[];
164
+ setExtension(extension: string): Promise<void>;
143
165
  /**
144
166
  * Event listener management
145
167
  */
@@ -155,6 +177,7 @@ declare class DoraCell {
155
177
  private getDefaultTurnServers;
156
178
  private getDisplayName;
157
179
  private generateInstanceId;
180
+ private resolveApiBaseUrl;
158
181
  }
159
182
 
160
183
  /**
@@ -213,4 +236,4 @@ declare function extractNumberFromSipUri(sipUri: string): string;
213
236
  */
214
237
  declare function isValidPhoneNumber(phone: string, minLength?: number): boolean;
215
238
 
216
- export { type ApiTokenAuth, type AuthConfig, AuthenticationError, type Call, type CallDirection, CallError, type CallOptions, CallSession, type CallStatus, ConnectionError, type ConnectionState, type ConnectionStatus, type DirectCredentialsAuth, DoraCell, type DoraCellConfig, DoraCellError, type DoraCellEvent, type DoraCellEventMap, type Extension, type SipCredentials, DoraCell as default, extractNumberFromSipUri, formatPhoneToSIP, isValidPhoneNumber, normalizePhoneNumber };
239
+ export { type ApiTokenAuth, type AuthConfig, AuthenticationError, type Call, type CallDirection, CallError, type CallOptions, CallSession, type CallStatus, ConnectionError, type ConnectionState, type ConnectionStatus, type DirectCredentialsAuth, DoraCell, type DoraCellConfig, DoraCellError, type DoraCellEvent, type DoraCellEventMap, type Extension, type ExtensionAuth, type SipCredentials, DoraCell as default, extractNumberFromSipUri, formatPhoneToSIP, isValidPhoneNumber, normalizePhoneNumber };