@signalhousellc/sdk 1.0.67 → 1.0.68

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.
@@ -1,3 +1,9 @@
1
+ /** @typedef {import('../../SignalHouseSDK.js').RequestOptions} RequestOptions */
2
+ /**
3
+ * @template T
4
+ * @typedef {import('../../SignalHouseSDK.js').SdkResponse<T>} SdkResponse
5
+ */
6
+
1
7
  /**
2
8
  * @typedef {Object} SipTrunkHost
3
9
  * @property {string} host - IP address, hostname, FQDN, or CIDR.
@@ -43,8 +49,8 @@ export class SipTrunks {
43
49
  * @async
44
50
  * @roles api, admin, developer, billing, user
45
51
  * @param {Object} [params] - Request parameters.
46
- * @param {import('../../SignalHouseSDK').RequestOptions} [params.options] - Additional options for the request.
47
- * @returns {Promise<Object>} The list of SIP trunks: `{ sipTrunks: [...] }`.
52
+ * @param {RequestOptions} [params.options] - Additional options for the request.
53
+ * @returns {Promise<SdkResponse<any>>} The list of SIP trunks: `{ sipTrunks: [...] }`.
48
54
  */
49
55
  async list({ options = {} } = {}) {
50
56
  return this.client(`/voice/sip-trunks`, { method: "GET", ...options });
@@ -56,9 +62,9 @@ export class SipTrunks {
56
62
  * @roles api, admin, developer, billing, user
57
63
  * @param {Object} params - Request parameters.
58
64
  * @param {string} params.id - The SIP trunk UUID.
59
- * @param {import('../../SignalHouseSDK').RequestOptions} [params.options] - Additional options for the request.
65
+ * @param {RequestOptions} [params.options] - Additional options for the request.
60
66
  * @throws {Error} If id is missing.
61
- * @returns {Promise<Object>} The SIP trunk: `{ sipTrunk: {...} }`.
67
+ * @returns {Promise<SdkResponse<any>>} The SIP trunk: `{ sipTrunk: {...} }`.
62
68
  */
63
69
  async get({ id, options = {} }) {
64
70
  this.client._require({ id });
@@ -72,9 +78,9 @@ export class SipTrunks {
72
78
  * @roles api, admin, developer, billing, user
73
79
  * @param {Object} params - Request parameters.
74
80
  * @param {CreateSipTrunkData} params.trunkData - The SIP trunk to create.
75
- * @param {import('../../SignalHouseSDK').RequestOptions} [params.options] - Additional options for the request.
81
+ * @param {RequestOptions} [params.options] - Additional options for the request.
76
82
  * @throws {Error} If trunkData is missing.
77
- * @returns {Promise<Object>} The created SIP trunk: `{ sipTrunk: {...} }`.
83
+ * @returns {Promise<SdkResponse<any>>} The created SIP trunk: `{ sipTrunk: {...} }`.
78
84
  */
79
85
  async create({ trunkData, options = {} }) {
80
86
  this.client._require({ trunkData });
@@ -88,9 +94,9 @@ export class SipTrunks {
88
94
  * @param {Object} params - Request parameters.
89
95
  * @param {string} params.id - The SIP trunk UUID.
90
96
  * @param {UpdateSipTrunkData} params.updateData - Fields to update.
91
- * @param {import('../../SignalHouseSDK').RequestOptions} [params.options] - Additional options for the request.
97
+ * @param {RequestOptions} [params.options] - Additional options for the request.
92
98
  * @throws {Error} If id or updateData is missing.
93
- * @returns {Promise<Object>} The updated SIP trunk: `{ sipTrunk: {...} }`.
99
+ * @returns {Promise<SdkResponse<any>>} The updated SIP trunk: `{ sipTrunk: {...} }`.
94
100
  */
95
101
  async update({ id, updateData, options = {} }) {
96
102
  this.client._require({ id, updateData });
@@ -104,9 +110,9 @@ export class SipTrunks {
104
110
  * @roles api, admin, developer, billing, user
105
111
  * @param {Object} params - Request parameters.
106
112
  * @param {string} params.id - The SIP trunk UUID.
107
- * @param {import('../../SignalHouseSDK').RequestOptions} [params.options] - Additional options for the request.
113
+ * @param {RequestOptions} [params.options] - Additional options for the request.
108
114
  * @throws {Error} If id is missing.
109
- * @returns {Promise<Object>} `{ success: boolean, message: string }`.
115
+ * @returns {Promise<SdkResponse<any>>} `{ success: boolean, message: string }`.
110
116
  */
111
117
  async delete({ id, options = {} }) {
112
118
  this.client._require({ id });
@@ -120,9 +126,9 @@ export class SipTrunks {
120
126
  * @roles api, admin, developer, billing, user
121
127
  * @param {Object} params - Request parameters.
122
128
  * @param {string} params.id - The SIP trunk UUID.
123
- * @param {import('../../SignalHouseSDK').RequestOptions} [params.options] - Additional options for the request.
129
+ * @param {RequestOptions} [params.options] - Additional options for the request.
124
130
  * @throws {Error} If id is missing.
125
- * @returns {Promise<Object>} The updated SIP trunk: `{ sipTrunk: {...} }`.
131
+ * @returns {Promise<SdkResponse<any>>} The updated SIP trunk: `{ sipTrunk: {...} }`.
126
132
  */
127
133
  async toggleActive({ id, options = {} }) {
128
134
  this.client._require({ id });
@@ -136,9 +142,9 @@ export class SipTrunks {
136
142
  * @roles api, admin, developer, billing, user
137
143
  * @param {Object} params - Request parameters.
138
144
  * @param {string} params.id - The SIP trunk UUID.
139
- * @param {import('../../SignalHouseSDK').RequestOptions} [params.options] - Additional options for the request.
145
+ * @param {RequestOptions} [params.options] - Additional options for the request.
140
146
  * @throws {Error} If id is missing.
141
- * @returns {Promise<Object>} `{ password: string }`.
147
+ * @returns {Promise<SdkResponse<any>>} `{ password: string }`.
142
148
  */
143
149
  async regeneratePassword({ id, options = {} }) {
144
150
  this.client._require({ id });
@@ -151,8 +157,8 @@ export class SipTrunks {
151
157
  * @async
152
158
  * @roles api, admin, developer, billing, user
153
159
  * @param {Object} [params] - Request parameters.
154
- * @param {import('../../SignalHouseSDK').RequestOptions} [params.options] - Additional options for the request.
155
- * @returns {Promise<Object>} `{ sipPops: [...] }`.
160
+ * @param {RequestOptions} [params.options] - Additional options for the request.
161
+ * @returns {Promise<SdkResponse<any>>} `{ sipPops: [...] }`.
156
162
  */
157
163
  async getPops({ options = {} } = {}) {
158
164
  return this.client(`/voice/sip-trunks/pops`, { method: "GET", ...options });
@@ -167,9 +173,9 @@ export class SipTrunks {
167
173
  * @param {Object} params - Request parameters.
168
174
  * @param {string} params.id - The SIP trunk UUID.
169
175
  * @param {string[]} params.phoneNumbers - Phone numbers in E.164 (a leading "+" is optional).
170
- * @param {import('../../SignalHouseSDK').RequestOptions} [params.options] - Additional options for the request.
176
+ * @param {RequestOptions} [params.options] - Additional options for the request.
171
177
  * @throws {Error} If id or phoneNumbers is missing.
172
- * @returns {Promise<Object>} `{ assigned: string[], skipped: { e164, reason }[] }`.
178
+ * @returns {Promise<SdkResponse<any>>} `{ assigned: string[], skipped: { e164, reason }[] }`.
173
179
  */
174
180
  async assignNumbers({ id, phoneNumbers, options = {} }) {
175
181
  this.client._require({ id, phoneNumbers });
@@ -185,9 +191,9 @@ export class SipTrunks {
185
191
  * @param {Object} params - Request parameters.
186
192
  * @param {string} params.id - The SIP trunk UUID.
187
193
  * @param {string[]} params.phoneNumbers - Phone numbers in E.164 (a leading "+" is optional).
188
- * @param {import('../../SignalHouseSDK').RequestOptions} [params.options] - Additional options for the request.
194
+ * @param {RequestOptions} [params.options] - Additional options for the request.
189
195
  * @throws {Error} If id or phoneNumbers is missing.
190
- * @returns {Promise<Object>} `{ unassigned: string[] }`.
196
+ * @returns {Promise<SdkResponse<any>>} `{ unassigned: string[] }`.
191
197
  */
192
198
  async unassignNumbers({ id, phoneNumbers, options = {} }) {
193
199
  this.client._require({ id, phoneNumbers });
@@ -1,3 +1,9 @@
1
+ /** @typedef {import('../../SignalHouseSDK.js').RequestOptions} RequestOptions */
2
+ /**
3
+ * @template T
4
+ * @typedef {import('../../SignalHouseSDK.js').SdkResponse<T>} SdkResponse
5
+ */
6
+
1
7
  /**
2
8
  * @typedef {Object} VoiceGrants
3
9
  * @property {Object} [voice] - Voice grants object.
@@ -55,8 +61,8 @@ export class Tokens {
55
61
  * @roles api, admin, developer, billing, user
56
62
  * @param {Object} [params] - Request parameters.
57
63
  * @param {CreateTokenData} [params.tokenData] - Token mint options (identity, ttl, grants). All fields optional.
58
- * @param {import('../../SignalHouseSDK').RequestOptions} [params.options] - Additional options for the request.
59
- * @returns {Promise<CreateTokenResponse>} The token + embedded SIP credentials.
64
+ * @param {RequestOptions} [params.options] - Additional options for the request.
65
+ * @returns {Promise<SdkResponse<CreateTokenResponse>>} The token + embedded SIP credentials.
60
66
  */
61
67
  async create({ tokenData = {}, options = {} } = {}) {
62
68
  return this.client(`/voice/tokens`, { method: "POST", body: tokenData, ...options });
@@ -77,8 +83,8 @@ export class Tokens {
77
83
  * @param {string} [params.token] - The current voice token (JWT) whose identity to extend.
78
84
  * @param {string} [params.identity] - The wrtc_ identity to extend, if held directly (from a prior mint/refresh response).
79
85
  * @param {number} [params.ttl] - New TTL in seconds. Defaults to the server default, capped at the server maximum.
80
- * @param {import('../../SignalHouseSDK').RequestOptions} [params.options] - Additional options for the request.
81
- * @returns {Promise<CreateTokenResponse>} The refreshed token + embedded SIP credentials (same shape as create()).
86
+ * @param {RequestOptions} [params.options] - Additional options for the request.
87
+ * @returns {Promise<SdkResponse<CreateTokenResponse>>} The refreshed token + embedded SIP credentials (same shape as create()).
82
88
  */
83
89
  async refresh({ token, identity, ttl, options = {} } = {}) {
84
90
  const body = {};
@@ -31,6 +31,8 @@ export class Call {
31
31
  this._session = session;
32
32
  this._listeners = new Map();
33
33
  this._remoteAudio = null;
34
+ /** @type {boolean} Set true before accept/connect to route remote audio yourself instead of the hidden <audio>. */
35
+ this.disableDefaultAudioOutput = false;
34
36
 
35
37
  this.direction = direction;
36
38
  this.id = session.id || session._request?.call_id || null;
@@ -73,6 +73,15 @@ import { Call } from "./Call.js";
73
73
  * "connected" (WSS established), "disconnected", "tokenWillExpire",
74
74
  * "tokenRefreshFailed", "error".
75
75
  */
76
+ /**
77
+ * Narrow a token-or-credentials argument: a VoiceTokenResponse wraps its SIP credentials under `sip_credentials`.
78
+ * @param {VoiceTokenResponse | SipCredentials} value - The argument to inspect.
79
+ * @returns {value is VoiceTokenResponse} - True when the value is a token response.
80
+ */
81
+ function isTokenResponse(value) {
82
+ return !!value && typeof value === "object" && "sip_credentials" in value;
83
+ }
84
+
76
85
  export class Device {
77
86
  /**
78
87
  * @param {VoiceTokenResponse | SipCredentials} tokenOrCreds
@@ -90,8 +99,9 @@ export class Device {
90
99
  */
91
100
  constructor(tokenOrCreds, opts = {}) {
92
101
  this._credentials = this._parseCreds(tokenOrCreds, "Device");
93
- this.identity = tokenOrCreds?.identity || this._credentials.username;
94
- this.expiresAt = tokenOrCreds?.expires_at || null;
102
+ const token = isTokenResponse(tokenOrCreds) ? tokenOrCreds : null;
103
+ this.identity = token?.identity || this._credentials.username;
104
+ this.expiresAt = token?.expires_at || null;
95
105
 
96
106
  this._opts = {
97
107
  registerExpires: opts.registerExpires ?? 600,
@@ -179,12 +189,13 @@ export class Device {
179
189
  */
180
190
  async updateToken(tokenOrCreds) {
181
191
  const creds = this._parseCreds(tokenOrCreds, "Device.updateToken");
182
- const newIdentity = tokenOrCreds?.identity || creds.username;
192
+ const token = isTokenResponse(tokenOrCreds) ? tokenOrCreds : null;
193
+ const newIdentity = token?.identity || creds.username;
183
194
 
184
195
  // Identity-preserving fast path — same credential, just extended expiry.
185
196
  if (this._ua && this._registered && !this._stopped &&
186
197
  newIdentity === this.identity && creds.password === this._credentials.password) {
187
- this.expiresAt = tokenOrCreds?.expires_at || this.expiresAt;
198
+ this.expiresAt = token?.expires_at || this.expiresAt;
188
199
  this._scheduleTokenRefresh();
189
200
  return;
190
201
  }
@@ -205,7 +216,7 @@ export class Device {
205
216
  * @param {ConnectParams} params
206
217
  * @returns {Promise<Call>}
207
218
  */
208
- async connect({ to, from, mediaStream, pcConfig, extraHeaders = [] } = {}) {
219
+ async connect({ to, from, mediaStream, pcConfig, extraHeaders = [] } = /** @type {ConnectParams} */ ({})) {
209
220
  if (!this._ua || !this._registered) {
210
221
  throw new Error("Device.connect: not registered yet — call register() first");
211
222
  }
@@ -235,7 +246,7 @@ export class Device {
235
246
  mediaConstraints: { audio: true, video: false },
236
247
  mediaStream: stream,
237
248
  pcConfig: pcConfig || { iceServers: [] },
238
- rtcOfferConstraints: { offerToReceiveAudio: 1, offerToReceiveVideo: 0 },
249
+ rtcOfferConstraints: { offerToReceiveAudio: true, offerToReceiveVideo: false },
239
250
  extraHeaders: headers,
240
251
  });
241
252
 
@@ -279,7 +290,7 @@ export class Device {
279
290
  * @throws {Error} when a required field is missing.
280
291
  */
281
292
  _parseCreds(tokenOrCreds, ctx) {
282
- const creds = tokenOrCreds?.sip_credentials || tokenOrCreds;
293
+ const creds = /** @type {SipCredentials} */ (isTokenResponse(tokenOrCreds) ? tokenOrCreds.sip_credentials : tokenOrCreds);
283
294
  if (!creds?.username || !creds?.password || !creds?.domain || !creds?.wss_url) {
284
295
  throw new Error(`${ctx}: token/credentials missing required fields (username, password, domain, wss_url)`);
285
296
  }
@@ -320,8 +331,9 @@ export class Device {
320
331
  this._ua = null;
321
332
  this._registered = false;
322
333
  this._credentials = creds;
323
- this.identity = tokenOrCreds?.identity || creds.username;
324
- this.expiresAt = tokenOrCreds?.expires_at || null;
334
+ const token = isTokenResponse(tokenOrCreds) ? tokenOrCreds : null;
335
+ this.identity = token?.identity || creds.username;
336
+ this.expiresAt = token?.expires_at || null;
325
337
 
326
338
  try {
327
339
  await this.register(); // rebuilds the UA + reschedules the refresh timer
@@ -345,11 +357,11 @@ export class Device {
345
357
  * True if the given token has too little life left to safely apply (would
346
358
  * expire within tokenRefreshMs). Tokens with no parseable expiry are treated
347
359
  * as usable.
348
- * @param {VoiceTokenResponse} tokenOrCreds
360
+ * @param {VoiceTokenResponse | SipCredentials} tokenOrCreds
349
361
  * @returns {boolean}
350
362
  */
351
363
  _isTokenStale(tokenOrCreds) {
352
- const exp = Date.parse(tokenOrCreds?.expires_at || "");
364
+ const exp = Date.parse((isTokenResponse(tokenOrCreds) && tokenOrCreds.expires_at) || "");
353
365
  if (!Number.isFinite(exp)) return false;
354
366
  return exp - Date.now() <= this._opts.tokenRefreshMs;
355
367
  }