@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/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(apiToken, apiBaseUrl) {
23002
+ constructor(publicKey, secretKey) {
23003
23003
  this.credentials = null;
23004
- this.apiToken = apiToken;
23005
- let defaultBaseUrl = "";
23006
- if (typeof process !== "undefined" && process.env) {
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
- const response = await fetch(`${this.apiBaseUrl}/api/sip-credentials`, {
23017
- method: "GET",
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
- credentials: "include"
23021
+ body: JSON.stringify({
23022
+ secret_key: this.secretKey
23023
+ })
23024
23024
  });
23025
- if (!response.ok) {
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
- `Failed to fetch SIP credentials: ${response.status}`,
23034
- { status: response.status }
23027
+ `Key verification failed: ${authResponse.status}`,
23028
+ { status: authResponse.status }
23035
23029
  );
23036
23030
  }
23037
- const data = await response.json();
23038
- this.credentials = this.parseCredentials(data);
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({ type: "api-token", apiToken: this.apiToken });
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 wsUrl = data.ws_url || data.wsUrl;
23058
- const sipUri = data.sip_uri || data.sipUri;
23059
- const password = data.password;
23060
- const sipDomain = data.sip_domain || data.sipDomain;
23061
- const extensions = data.extensions;
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
- if (!wsUrl || !sipUri) {
23064
- throw new AuthenticationError(
23065
- "Invalid credentials response: missing ws_url or sip_uri"
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: 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 = null;
23087
+ constructor(sipUri, password, wsUrl) {
23088
+ this.credentials = { sipUri, password, wsUrl, extensions: [] };
23081
23089
  }
23082
- async authenticate(config) {
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 this.credentials !== null;
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
- wsUrl,
23115
- sipUri,
23100
+ sipUri: `sip:${extension}@${sipDomain}`,
23116
23101
  password,
23117
- sipDomain,
23118
- extensions: [{ extension, displayName: `Ext ${extension}`, isPrimary: true }]
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 this.credentials !== null;
23111
+ return true;
23124
23112
  }
23125
23113
  };
23126
23114
  function createAuthProvider(config) {
23127
- switch (config.type) {
23128
- case "api-token":
23129
- return new ApiTokenAuthProvider(config.apiToken, config.apiBaseUrl);
23130
- case "direct":
23131
- return new DirectCredentialsAuthProvider();
23132
- case "extension":
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(this.config.auth);
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
- * Get available extensions
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
- if (!this.credentials?.extensions) {
23600
- return [];
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: this.credentials?.extensions?.[0]?.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 };