@dora-cell/sdk 1.0.0 → 1.0.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/README.md +18 -14
- package/dist/index.d.mts +26 -5
- package/dist/index.d.ts +26 -5
- package/dist/index.js +217 -84
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +217 -84
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -22999,43 +22999,41 @@ var ConnectionError = class extends DoraCellError {
|
|
|
22999
22999
|
|
|
23000
23000
|
// src/core/AuthProvider.ts
|
|
23001
23001
|
var ApiTokenAuthProvider = class {
|
|
23002
|
-
constructor(
|
|
23002
|
+
constructor(publicKey, secretKey) {
|
|
23003
23003
|
this.credentials = null;
|
|
23004
|
-
this.
|
|
23005
|
-
|
|
23006
|
-
|
|
23007
|
-
defaultBaseUrl = process.env.NEXT_PUBLIC_BASE_API_URL || "";
|
|
23008
|
-
}
|
|
23009
|
-
this.apiBaseUrl = apiBaseUrl || defaultBaseUrl;
|
|
23004
|
+
this.sessionToken = null;
|
|
23005
|
+
this.publicKey = publicKey;
|
|
23006
|
+
this.secretKey = secretKey;
|
|
23010
23007
|
}
|
|
23011
|
-
async authenticate(config) {
|
|
23008
|
+
async authenticate(config, apiBaseUrl) {
|
|
23012
23009
|
if (config.type !== "api-token") {
|
|
23013
23010
|
throw new AuthenticationError("Invalid auth config type for ApiTokenAuthProvider");
|
|
23014
23011
|
}
|
|
23012
|
+
const baseUrl = apiBaseUrl?.replace(/\/$/, "") || "https://api.cell.usedora.com/api";
|
|
23015
23013
|
try {
|
|
23016
|
-
|
|
23017
|
-
|
|
23014
|
+
console.log("SDK: Verifying keys at:", `${baseUrl}/sdk/v1/auth/session`);
|
|
23015
|
+
const authResponse = await fetch(`${baseUrl}/sdk/v1/auth/session`, {
|
|
23016
|
+
method: "POST",
|
|
23018
23017
|
headers: {
|
|
23019
|
-
"Authorization": `Bearer ${this.apiToken}`,
|
|
23020
23018
|
"Content-Type": "application/json",
|
|
23021
23019
|
"Accept": "application/json"
|
|
23022
23020
|
},
|
|
23023
|
-
|
|
23021
|
+
body: JSON.stringify({
|
|
23022
|
+
secret_key: this.secretKey
|
|
23023
|
+
})
|
|
23024
23024
|
});
|
|
23025
|
-
if (!
|
|
23026
|
-
if (response.status === 401 || response.status === 403) {
|
|
23027
|
-
throw new AuthenticationError(
|
|
23028
|
-
"Invalid API token or insufficient permissions",
|
|
23029
|
-
{ status: response.status }
|
|
23030
|
-
);
|
|
23031
|
-
}
|
|
23025
|
+
if (!authResponse.ok) {
|
|
23032
23026
|
throw new AuthenticationError(
|
|
23033
|
-
`
|
|
23034
|
-
{ status:
|
|
23027
|
+
`Key verification failed: ${authResponse.status}`,
|
|
23028
|
+
{ status: authResponse.status }
|
|
23035
23029
|
);
|
|
23036
23030
|
}
|
|
23037
|
-
const
|
|
23038
|
-
this.
|
|
23031
|
+
const authData = await authResponse.json();
|
|
23032
|
+
this.sessionToken = authData.session_token || authData.token || authData.data?.session_token;
|
|
23033
|
+
if (!this.sessionToken) {
|
|
23034
|
+
throw new AuthenticationError("No session token returned after key verification");
|
|
23035
|
+
}
|
|
23036
|
+
this.credentials = this.parseCredentials(authData);
|
|
23039
23037
|
return this.credentials;
|
|
23040
23038
|
} catch (error) {
|
|
23041
23039
|
if (error instanceof AuthenticationError) {
|
|
@@ -23047,28 +23045,38 @@ var ApiTokenAuthProvider = class {
|
|
|
23047
23045
|
);
|
|
23048
23046
|
}
|
|
23049
23047
|
}
|
|
23050
|
-
async refreshCredentials() {
|
|
23051
|
-
return this.authenticate({
|
|
23048
|
+
async refreshCredentials(apiBaseUrl) {
|
|
23049
|
+
return this.authenticate({
|
|
23050
|
+
type: "api-token",
|
|
23051
|
+
publicKey: this.publicKey,
|
|
23052
|
+
secretKey: this.secretKey
|
|
23053
|
+
}, apiBaseUrl);
|
|
23052
23054
|
}
|
|
23053
23055
|
isAuthenticated() {
|
|
23054
|
-
return this.credentials !== null;
|
|
23056
|
+
return this.credentials !== null && this.sessionToken !== null;
|
|
23057
|
+
}
|
|
23058
|
+
getSessionToken() {
|
|
23059
|
+
return this.sessionToken;
|
|
23055
23060
|
}
|
|
23056
23061
|
parseCredentials(data) {
|
|
23057
|
-
const
|
|
23058
|
-
const
|
|
23059
|
-
const
|
|
23060
|
-
const
|
|
23061
|
-
const
|
|
23062
|
+
const DEFAULT_WSS_URL = "wss://cell.usedora.com:8089/ws";
|
|
23063
|
+
const DEFAULT_SIP_IP = "64.227.10.164";
|
|
23064
|
+
const DEFAULT_PASSWORD = "1234";
|
|
23065
|
+
const wsUrl = data.ws_url || data.wsUrl || DEFAULT_WSS_URL;
|
|
23066
|
+
const sipDomain = data.sip_domain || data.sipDomain || DEFAULT_SIP_IP;
|
|
23067
|
+
const password = data.password || DEFAULT_PASSWORD;
|
|
23068
|
+
const extensions = data.extensions || [];
|
|
23062
23069
|
const expiresIn = data.expires_in || data.expiresIn;
|
|
23063
|
-
|
|
23064
|
-
|
|
23065
|
-
|
|
23066
|
-
|
|
23070
|
+
let sipUri = data.sip_uri || data.sipUri;
|
|
23071
|
+
if (!sipUri && extensions.length > 0) {
|
|
23072
|
+
const ext = extensions[0].extension;
|
|
23073
|
+
sipUri = `sip:${ext}@${sipDomain}`;
|
|
23074
|
+
console.log(`SDK: Constructed SIP URI from extension: ${sipUri}`);
|
|
23067
23075
|
}
|
|
23068
23076
|
return {
|
|
23069
23077
|
wsUrl,
|
|
23070
|
-
sipUri,
|
|
23071
|
-
password
|
|
23078
|
+
sipUri: sipUri || "",
|
|
23079
|
+
password,
|
|
23072
23080
|
sipDomain,
|
|
23073
23081
|
extensions,
|
|
23074
23082
|
expiresIn
|
|
@@ -23076,64 +23084,42 @@ var ApiTokenAuthProvider = class {
|
|
|
23076
23084
|
}
|
|
23077
23085
|
};
|
|
23078
23086
|
var DirectCredentialsAuthProvider = class {
|
|
23079
|
-
constructor() {
|
|
23080
|
-
this.credentials =
|
|
23087
|
+
constructor(sipUri, password, wsUrl) {
|
|
23088
|
+
this.credentials = { sipUri, password, wsUrl, extensions: [] };
|
|
23081
23089
|
}
|
|
23082
|
-
async authenticate(
|
|
23083
|
-
if (config.type !== "direct") {
|
|
23084
|
-
throw new AuthenticationError("Invalid auth config type for DirectCredentialsAuthProvider");
|
|
23085
|
-
}
|
|
23086
|
-
this.credentials = {
|
|
23087
|
-
wsUrl: config.wsUrl,
|
|
23088
|
-
sipUri: config.sipUri,
|
|
23089
|
-
password: config.password
|
|
23090
|
-
};
|
|
23090
|
+
async authenticate() {
|
|
23091
23091
|
return this.credentials;
|
|
23092
23092
|
}
|
|
23093
23093
|
isAuthenticated() {
|
|
23094
|
-
return
|
|
23094
|
+
return true;
|
|
23095
23095
|
}
|
|
23096
23096
|
};
|
|
23097
|
-
var DEFAULT_WSS_URL = "wss://cell.usedora.com:8089/ws";
|
|
23098
|
-
var DEFAULT_SIP_IP = "64.227.10.164";
|
|
23099
|
-
var DEFAULT_PASSWORD = "1234";
|
|
23100
23097
|
var ExtensionAuthProvider = class {
|
|
23101
|
-
constructor() {
|
|
23102
|
-
this.credentials = null;
|
|
23103
|
-
}
|
|
23104
|
-
async authenticate(config) {
|
|
23105
|
-
if (config.type !== "extension") {
|
|
23106
|
-
throw new AuthenticationError("Invalid auth config type for ExtensionAuthProvider");
|
|
23107
|
-
}
|
|
23108
|
-
const extension = config.extension;
|
|
23109
|
-
const password = config.password || DEFAULT_PASSWORD;
|
|
23110
|
-
const wsUrl = DEFAULT_WSS_URL;
|
|
23111
|
-
const sipDomain = config.sipDomain || DEFAULT_SIP_IP;
|
|
23112
|
-
const sipUri = `sip:${extension}@${sipDomain}`;
|
|
23098
|
+
constructor(extension, password = "1234", sipDomain = "64.227.10.164") {
|
|
23113
23099
|
this.credentials = {
|
|
23114
|
-
|
|
23115
|
-
sipUri,
|
|
23100
|
+
sipUri: `sip:${extension}@${sipDomain}`,
|
|
23116
23101
|
password,
|
|
23117
|
-
|
|
23118
|
-
extensions: [{ extension,
|
|
23102
|
+
wsUrl: "wss://cell.usedora.com:8089/ws",
|
|
23103
|
+
extensions: [{ extension, isPrimary: true }],
|
|
23104
|
+
sipDomain
|
|
23119
23105
|
};
|
|
23106
|
+
}
|
|
23107
|
+
async authenticate() {
|
|
23120
23108
|
return this.credentials;
|
|
23121
23109
|
}
|
|
23122
23110
|
isAuthenticated() {
|
|
23123
|
-
return
|
|
23111
|
+
return true;
|
|
23124
23112
|
}
|
|
23125
23113
|
};
|
|
23126
23114
|
function createAuthProvider(config) {
|
|
23127
|
-
|
|
23128
|
-
|
|
23129
|
-
|
|
23130
|
-
|
|
23131
|
-
|
|
23132
|
-
|
|
23133
|
-
return new ExtensionAuthProvider();
|
|
23134
|
-
default:
|
|
23135
|
-
throw new AuthenticationError("Unknown authentication type");
|
|
23115
|
+
if (config.type === "api-token") {
|
|
23116
|
+
return new ApiTokenAuthProvider(config.publicKey, config.secretKey);
|
|
23117
|
+
} else if (config.type === "direct") {
|
|
23118
|
+
return new DirectCredentialsAuthProvider(config.sipUri, config.password, config.wsUrl);
|
|
23119
|
+
} else if (config.type === "extension") {
|
|
23120
|
+
return new ExtensionAuthProvider(config.extension, config.password, config.sipDomain);
|
|
23136
23121
|
}
|
|
23122
|
+
throw new AuthenticationError("Unknown authentication type");
|
|
23137
23123
|
}
|
|
23138
23124
|
|
|
23139
23125
|
// src/utils/phoneFormatter.ts
|
|
@@ -23407,6 +23393,64 @@ var CallManager = class {
|
|
|
23407
23393
|
}
|
|
23408
23394
|
};
|
|
23409
23395
|
|
|
23396
|
+
// src/core/ApiClient.ts
|
|
23397
|
+
var ApiClient = class {
|
|
23398
|
+
constructor(baseUrl) {
|
|
23399
|
+
this.sessionToken = null;
|
|
23400
|
+
this.baseUrl = baseUrl.endsWith("/") ? baseUrl.slice(0, -1) : baseUrl;
|
|
23401
|
+
}
|
|
23402
|
+
setSessionToken(token) {
|
|
23403
|
+
this.sessionToken = token;
|
|
23404
|
+
}
|
|
23405
|
+
getSessionToken() {
|
|
23406
|
+
return this.sessionToken;
|
|
23407
|
+
}
|
|
23408
|
+
async post(path, data, headers = {}) {
|
|
23409
|
+
return this.request(path, {
|
|
23410
|
+
method: "POST",
|
|
23411
|
+
headers: {
|
|
23412
|
+
"Content-Type": "application/json",
|
|
23413
|
+
...headers
|
|
23414
|
+
},
|
|
23415
|
+
body: JSON.stringify(data)
|
|
23416
|
+
});
|
|
23417
|
+
}
|
|
23418
|
+
async get(path, headers = {}) {
|
|
23419
|
+
return this.request(path, {
|
|
23420
|
+
method: "GET",
|
|
23421
|
+
headers: {
|
|
23422
|
+
...headers
|
|
23423
|
+
}
|
|
23424
|
+
});
|
|
23425
|
+
}
|
|
23426
|
+
async request(path, options) {
|
|
23427
|
+
const url = path.startsWith("http") ? path : `${this.baseUrl}${path.startsWith("/") ? "" : "/"}${path}`;
|
|
23428
|
+
const headers = {
|
|
23429
|
+
...options.headers
|
|
23430
|
+
};
|
|
23431
|
+
if (this.sessionToken && !headers["Authorization"]) {
|
|
23432
|
+
headers["Authorization"] = `Bearer ${this.sessionToken}`;
|
|
23433
|
+
}
|
|
23434
|
+
const response = await fetch(url, {
|
|
23435
|
+
...options,
|
|
23436
|
+
headers
|
|
23437
|
+
});
|
|
23438
|
+
if (!response.ok) {
|
|
23439
|
+
let errorData;
|
|
23440
|
+
try {
|
|
23441
|
+
errorData = await response.json();
|
|
23442
|
+
} catch (e) {
|
|
23443
|
+
errorData = { message: response.statusText };
|
|
23444
|
+
}
|
|
23445
|
+
const error = new Error(errorData.message || `API Request failed: ${response.status}`);
|
|
23446
|
+
error.status = response.status;
|
|
23447
|
+
error.data = errorData;
|
|
23448
|
+
throw error;
|
|
23449
|
+
}
|
|
23450
|
+
return response.json();
|
|
23451
|
+
}
|
|
23452
|
+
};
|
|
23453
|
+
|
|
23410
23454
|
// src/core/DoraCell.ts
|
|
23411
23455
|
var DoraCell = class {
|
|
23412
23456
|
constructor(config) {
|
|
@@ -23424,6 +23468,8 @@ var DoraCell = class {
|
|
|
23424
23468
|
};
|
|
23425
23469
|
this.events = new eventemitter3_default();
|
|
23426
23470
|
this.authProvider = createAuthProvider(config.auth);
|
|
23471
|
+
this.apiBaseUrl = this.resolveApiBaseUrl(config);
|
|
23472
|
+
this.apiClient = new ApiClient(this.apiBaseUrl);
|
|
23427
23473
|
if (this.config.debug) {
|
|
23428
23474
|
import_jssip.default.debug.enable("JsSIP:*");
|
|
23429
23475
|
}
|
|
@@ -23433,7 +23479,20 @@ var DoraCell = class {
|
|
|
23433
23479
|
*/
|
|
23434
23480
|
async initialize() {
|
|
23435
23481
|
try {
|
|
23436
|
-
this.credentials = await this.authProvider.authenticate(
|
|
23482
|
+
this.credentials = await this.authProvider.authenticate(
|
|
23483
|
+
this.config.auth,
|
|
23484
|
+
this.apiBaseUrl
|
|
23485
|
+
);
|
|
23486
|
+
if (this.authProvider instanceof ApiTokenAuthProvider) {
|
|
23487
|
+
const token = this.authProvider.getSessionToken();
|
|
23488
|
+
if (token) this.apiClient.setSessionToken(token);
|
|
23489
|
+
}
|
|
23490
|
+
if (this.config.autoSelectExtension && this.credentials?.extensions && this.credentials.extensions.length > 0) {
|
|
23491
|
+
const primary = this.credentials.extensions.find((e) => e.isPrimary) || this.credentials.extensions[0];
|
|
23492
|
+
const domain = this.credentials.sipDomain || "cell.usedora.com";
|
|
23493
|
+
this.credentials.sipUri = `sip:${primary.extension}@${domain}`;
|
|
23494
|
+
console.log(`SDK: Auto-selected extension ${primary.extension}`);
|
|
23495
|
+
}
|
|
23437
23496
|
await this.initializeUserAgent();
|
|
23438
23497
|
this.initializeCallManager();
|
|
23439
23498
|
await this.waitForRegistration();
|
|
@@ -23586,6 +23645,35 @@ var DoraCell = class {
|
|
|
23586
23645
|
getCurrentCall() {
|
|
23587
23646
|
return this.callManager?.getCurrentCall() || null;
|
|
23588
23647
|
}
|
|
23648
|
+
/**
|
|
23649
|
+
* Get user's wallet/credit balance
|
|
23650
|
+
*/
|
|
23651
|
+
async getWallet() {
|
|
23652
|
+
const token = this.apiClient.getSessionToken();
|
|
23653
|
+
if (!token) {
|
|
23654
|
+
console.warn("SDK: Cannot fetch wallet, no session token available.");
|
|
23655
|
+
return { balance: 0, currency: "NGN" };
|
|
23656
|
+
}
|
|
23657
|
+
try {
|
|
23658
|
+
console.log("SDK: Fetching wallet balance...");
|
|
23659
|
+
const response = await this.apiClient.get("/wallets");
|
|
23660
|
+
const wallets = Array.isArray(response) ? response : response.data || [];
|
|
23661
|
+
if (wallets.length === 0) {
|
|
23662
|
+
console.log("SDK: No wallets found for user.");
|
|
23663
|
+
return { balance: 0, currency: "NGN" };
|
|
23664
|
+
}
|
|
23665
|
+
const primary = wallets[0];
|
|
23666
|
+
const result = {
|
|
23667
|
+
balance: parseFloat(primary.balance || "0"),
|
|
23668
|
+
currency: primary.currency || "NGN"
|
|
23669
|
+
};
|
|
23670
|
+
console.log("SDK: Wallet balance fetched:", result);
|
|
23671
|
+
return result;
|
|
23672
|
+
} catch (error) {
|
|
23673
|
+
console.error("SDK: Failed to fetch wallet:", error);
|
|
23674
|
+
throw error;
|
|
23675
|
+
}
|
|
23676
|
+
}
|
|
23589
23677
|
/**
|
|
23590
23678
|
* Get connection status
|
|
23591
23679
|
*/
|
|
@@ -23593,13 +23681,41 @@ var DoraCell = class {
|
|
|
23593
23681
|
return this.connectionStatus;
|
|
23594
23682
|
}
|
|
23595
23683
|
/**
|
|
23596
|
-
*
|
|
23684
|
+
* Fetch available extensions (DID numbers) from the API
|
|
23685
|
+
*/
|
|
23686
|
+
async fetchExtensions() {
|
|
23687
|
+
if (!this.authProvider.isAuthenticated()) {
|
|
23688
|
+
throw new Error("SDK not authenticated. Call initialize() first.");
|
|
23689
|
+
}
|
|
23690
|
+
try {
|
|
23691
|
+
const response = await this.apiClient.get("/extensions");
|
|
23692
|
+
const extensions = response.data || response.extensions || response;
|
|
23693
|
+
if (this.credentials && Array.isArray(extensions)) {
|
|
23694
|
+
this.credentials.extensions = extensions;
|
|
23695
|
+
}
|
|
23696
|
+
return Array.isArray(extensions) ? extensions : [];
|
|
23697
|
+
} catch (error) {
|
|
23698
|
+
console.error("SDK: Failed to fetch extensions:", error);
|
|
23699
|
+
return this.credentials?.extensions || [];
|
|
23700
|
+
}
|
|
23701
|
+
}
|
|
23702
|
+
/**
|
|
23703
|
+
* Get available extensions (DID numbers)
|
|
23597
23704
|
*/
|
|
23598
23705
|
getExtensions() {
|
|
23599
|
-
|
|
23600
|
-
|
|
23706
|
+
return this.credentials?.extensions || [];
|
|
23707
|
+
}
|
|
23708
|
+
/**
|
|
23709
|
+
* Update active extension and re-initialize SIP connection
|
|
23710
|
+
*/
|
|
23711
|
+
async setExtension(extension) {
|
|
23712
|
+
console.log(`SDK: Switching to extension ${extension}...`);
|
|
23713
|
+
if (this.credentials) {
|
|
23714
|
+
const domain = this.credentials.sipDomain || "cell.usedora.com";
|
|
23715
|
+
this.credentials.sipUri = `sip:${extension}@${domain}`;
|
|
23716
|
+
await this.initializeUserAgent();
|
|
23717
|
+
this.emitConnectionStatus(this.connectionStatus);
|
|
23601
23718
|
}
|
|
23602
|
-
return this.credentials.extensions.map((ext) => ext.extension);
|
|
23603
23719
|
}
|
|
23604
23720
|
/**
|
|
23605
23721
|
* Event listener management
|
|
@@ -23631,9 +23747,10 @@ var DoraCell = class {
|
|
|
23631
23747
|
}
|
|
23632
23748
|
// Helper methods
|
|
23633
23749
|
emitConnectionStatus(error) {
|
|
23750
|
+
const extension = this.credentials?.extensions?.[0]?.extension || (this.credentials?.sipUri ? extractNumberFromSipUri(this.credentials.sipUri) : void 0);
|
|
23634
23751
|
const state = {
|
|
23635
23752
|
status: this.connectionStatus,
|
|
23636
|
-
extension
|
|
23753
|
+
extension,
|
|
23637
23754
|
error
|
|
23638
23755
|
};
|
|
23639
23756
|
this.events.emit("connection:status", state);
|
|
@@ -23674,6 +23791,22 @@ var DoraCell = class {
|
|
|
23674
23791
|
return v.toString(16);
|
|
23675
23792
|
});
|
|
23676
23793
|
}
|
|
23794
|
+
resolveApiBaseUrl(config) {
|
|
23795
|
+
if (config.apiBaseUrl) return config.apiBaseUrl;
|
|
23796
|
+
if (config.environment) {
|
|
23797
|
+
switch (config.environment) {
|
|
23798
|
+
case "staging":
|
|
23799
|
+
case "dev":
|
|
23800
|
+
return "https://dev.api.cell.usedora.com/api";
|
|
23801
|
+
case "production":
|
|
23802
|
+
return "https://api.cell.usedora.com/api";
|
|
23803
|
+
}
|
|
23804
|
+
}
|
|
23805
|
+
if (typeof process !== "undefined" && process.env?.NEXT_PUBLIC_BASE_API_URL) {
|
|
23806
|
+
return process.env.NEXT_PUBLIC_BASE_API_URL;
|
|
23807
|
+
}
|
|
23808
|
+
return "https://api.cell.usedora.com/api";
|
|
23809
|
+
}
|
|
23677
23810
|
};
|
|
23678
23811
|
|
|
23679
23812
|
export { AuthenticationError, CallError, CallSession, ConnectionError, DoraCell, DoraCellError, DoraCell as default, extractNumberFromSipUri, formatPhoneToSIP, isValidPhoneNumber, normalizePhoneNumber };
|