@unboundcx/sdk 2.6.16 → 2.7.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.
Files changed (2) hide show
  1. package/base.js +93 -57
  2. package/package.json +1 -1
package/base.js CHANGED
@@ -101,6 +101,14 @@ export class BaseSDK {
101
101
  removeTransport(name) {
102
102
  this.transports.delete(name);
103
103
  }
104
+
105
+ _getJsonSafely(str, defaultValue) {
106
+ try {
107
+ return JSON.parse(str);
108
+ } catch (e) {
109
+ return defaultValue;
110
+ }
111
+ }
104
112
 
105
113
  async _getAvailableTransport(forceFetch = false) {
106
114
  if (forceFetch) {
@@ -174,9 +182,10 @@ export class BaseSDK {
174
182
 
175
183
  // Try transport plugins first
176
184
  const transport = await this._getAvailableTransport(forceFetch);
185
+ let response;
177
186
  if (transport) {
178
187
  try {
179
- const result = await transport.request(endpoint, method, params, {
188
+ response = await transport.request(endpoint, method, params, {
180
189
  namespace: this.namespace,
181
190
  token: this.token,
182
191
  callId: this.callId,
@@ -184,13 +193,7 @@ export class BaseSDK {
184
193
  baseURL: this.baseURL || this.fullUrl,
185
194
  });
186
195
 
187
- // Debug logging for transport plugins
188
- if (this.debugMode) {
189
- const status = result?.status || 200;
190
- console.log(`API :: ${transport.name} :: ${method.toUpperCase()} :: ${endpoint} :: ${status}`);
191
- }
192
196
 
193
- return result;
194
197
  } catch (err) {
195
198
  // IMPORTANT: This catch block should ONLY handle transport-level failures
196
199
  // (e.g., WebSocket disconnected, plugin unavailable, network errors)
@@ -198,22 +201,22 @@ export class BaseSDK {
198
201
  // Transport plugins should:
199
202
  // - RETURN API error responses normally (400, 500, etc.) as response objects
200
203
  // - ONLY THROW for transport mechanism failures
201
- //
202
- // This ensures API errors are passed through unchanged, just like built-in fetch
203
204
 
204
- if (this.debugMode) {
205
- console.log(`API :: Transport ${transport.name} failure :: ${method.toUpperCase()} :: ${endpoint} :: ${err.message}`);
206
- }
207
205
  console.warn(
208
206
  `Transport ${transport.name} mechanism failed, falling back to HTTP:`,
209
207
  err.message,
210
208
  );
211
- // Fall through to built-in HTTP fetch
209
+
210
+ // Built-in HTTP transport (fallback)
211
+ return this._httpRequest(endpoint, method, params);
212
212
  }
213
+ } else {
214
+ // No transport available, fallback to HTTP
215
+ return this._httpRequest(endpoint, method, params);
213
216
  }
214
217
 
215
- // Built-in HTTP transport (fallback)
216
- return this._httpRequest(endpoint, method, params);
218
+ return this._processResponse(response, transport.name, method, endpoint);
219
+
217
220
  }
218
221
 
219
222
  _isMultipartBody(body) {
@@ -306,72 +309,105 @@ export class BaseSDK {
306
309
 
307
310
  const response = await fetch(url, options);
308
311
 
312
+ return this._processResponse(response, 'https', method, endpoint);
313
+
314
+ }
315
+
316
+ async _processResponse(response, transport, method, endpoint) {
309
317
  // Check if the response indicates an HTTP error
310
318
  // These are API/configuration errors, not transport failures
319
+
320
+ const responseHeaders = response.headers;
321
+ const responseRequestId =
322
+ responseHeaders?.get?.('x-request-id') ||
323
+ responseHeaders?.['x-request-id'] || '';
324
+
311
325
  if (!response.ok) {
312
326
  let errorBody;
313
- const contentType = response.headers.get('content-type') || '';
314
-
315
- try {
316
- if (contentType.includes('application/json')) {
317
- errorBody = await response.json();
318
- } else {
319
- errorBody = await response.text();
327
+ if (response?.body) {
328
+ errorBody = response.body;
329
+ } else if (response?.headers?.['content-type']) {
330
+ const contentType = response.headers['content-type'];
331
+ try {
332
+ if (typeof response?.json === 'function' || typeof response?.text === 'function') {
333
+ if (contentType.includes('application/json')) {
334
+ errorBody = await response.json();
335
+ } else if (contentType.includes('text/')) {
336
+ errorBody = await response.text();
337
+ }
338
+ } else {
339
+ if (contentType.includes('application/json')) {
340
+ errorBody = this._getJsonSafely(response?.body, response?.body || {});
341
+ } else if (contentType.includes('text/')) {
342
+ errorBody = response?.body || '';
343
+ }
344
+ }
345
+ if (!errorBody) {
346
+ errorBody = `HTTP ${response.status} ${response.statusText}`;
347
+ }
348
+ } catch (parseError) {
349
+ errorBody = `HTTP ${response.status} ${response.statusText}`;
320
350
  }
321
- } catch (parseError) {
351
+ } else {
322
352
  errorBody = `HTTP ${response.status} ${response.statusText}`;
323
353
  }
324
354
 
325
355
  // Create a structured error for API/HTTP failures
326
- const httpError = new Error(`API :: Error :: https :: ${options.method} :: ${endpoint} :: ${response.status} :: ${response.statusText}`);
356
+ const httpError = new Error(`API :: Error :: ${transport} :: ${method.toUpperCase()} :: ${endpoint} :: ${response.status} :: ${response.statusText}`);
327
357
  httpError.status = response.status;
328
358
  httpError.statusText = response.statusText;
329
- httpError.method = options.method;
359
+ httpError.method = method;
330
360
  httpError.endpoint = endpoint;
331
361
  httpError.body = errorBody;
362
+ httpError.message = errorBody?.error || errorBody?.message || 'API Error';
332
363
 
364
+ // Debug logging for successful HTTP requests
365
+ if (this.debugMode) {
366
+ console.log(`API :: ERROR :: ${transport} :: ${method.toUpperCase()} :: ${endpoint} :: ${response?.status} :: ${responseRequestId}`, httpError);
367
+ }
368
+
333
369
  throw httpError;
334
370
  }
335
-
336
- // Check content type to determine how to parse successful response
337
- const contentType = response.headers.get('content-type') || '';
338
- let bodyResponse;
339
-
340
- if (contentType.includes('application/json')) {
341
- bodyResponse = await response.json();
342
- } else if (contentType.includes('text/')) {
343
- bodyResponse = await response.text();
371
+
372
+ let responseBody;
373
+ if (response?.body) {
374
+ responseBody = response.body;
375
+ } else if (response?.headers?.['content-type']) {
376
+ const contentType = response.headers['content-type'];
377
+ try {
378
+ if (transport === 'https') {
379
+ if (contentType.includes('application/json')) {
380
+ responseBody = await response.json();
381
+ } else if (contentType.includes('text/')) {
382
+ responseBody = await response.text();
383
+ } else {
384
+ responseBody = await response.arrayBuffer();
385
+ }
386
+ } else {
387
+ if (contentType.includes('application/json')) {
388
+ responseBody = this._getJsonSafely(response.body, response.body);
389
+ } else if (contentType.includes('text/')) {
390
+ responseBody = response?.body || '';
391
+ }
392
+ }
393
+ if (!responseBody) {
394
+ responseBody = {};
395
+ }
396
+ } catch (parseError) {
397
+ responseBody = {};
398
+ }
344
399
  } else {
345
- // For binary content (PDFs, images, etc), return as ArrayBuffer
346
- bodyResponse = await response.arrayBuffer();
400
+ responseBody = {};
347
401
  }
402
+
348
403
 
349
- const responseHeaders = response.headers;
350
- const responseRequestId =
351
- responseHeaders?.get?.('x-request-id') ||
352
- responseHeaders?.['x-request-id'];
353
404
 
354
- if (!response.ok) {
355
- // Debug logging for HTTP errors
356
- if (this.debugMode) {
357
- console.log(`API :: https :: ${method.toUpperCase()} :: ${endpoint} :: ${response?.status}`);
358
- }
359
-
360
- throw {
361
- name: `API :: Error :: https :: ${method} :: ${endpoint} :: ${responseRequestId} :: ${response?.status} :: ${response?.statusText}`,
362
- message: bodyResponse?.message || `API Error occured.`,
363
- method,
364
- endpoint,
365
- status: response?.status,
366
- statusText: response?.statusText,
367
- };
368
- }
369
405
 
370
406
  // Debug logging for successful HTTP requests
371
407
  if (this.debugMode) {
372
- console.log(`API :: https :: ${method.toUpperCase()} :: ${endpoint} :: ${response?.status}`);
408
+ console.log(`API :: ${transport} :: ${method.toUpperCase()} :: ${endpoint} :: ${response?.status} :: ${responseRequestId}`);
373
409
  }
374
410
 
375
- return bodyResponse;
411
+ return responseBody;
376
412
  }
377
413
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@unboundcx/sdk",
3
- "version": "2.6.16",
3
+ "version": "2.7.1",
4
4
  "description": "Official JavaScript SDK for the Unbound API - A comprehensive toolkit for integrating with Unbound's communication, AI, and data management services",
5
5
  "main": "index.js",
6
6
  "type": "module",