@unboundcx/sdk 2.6.16 → 2.7.0

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 +87 -54
  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,102 @@ 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
311
319
  if (!response.ok) {
312
320
  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();
321
+ if (response?.body) {
322
+ errorBody = response.body;
323
+ } else if (response?.headers?.['content-type']) {
324
+ const contentType = response.headers['content-type'];
325
+ try {
326
+ if (typeof response?.json === 'function' || typeof response?.text === 'function') {
327
+ if (contentType.includes('application/json')) {
328
+ errorBody = await response.json();
329
+ } else if (contentType.includes('text/')) {
330
+ errorBody = await response.text();
331
+ }
332
+ } else {
333
+ if (contentType.includes('application/json')) {
334
+ errorBody = this._getJsonSafely(response?.body, response?.body || {});
335
+ } else if (contentType.includes('text/')) {
336
+ errorBody = response?.body || '';
337
+ }
338
+ }
339
+ if (!errorBody) {
340
+ errorBody = `HTTP ${response.status} ${response.statusText}`;
341
+ }
342
+ } catch (parseError) {
343
+ errorBody = `HTTP ${response.status} ${response.statusText}`;
320
344
  }
321
- } catch (parseError) {
345
+ } else {
322
346
  errorBody = `HTTP ${response.status} ${response.statusText}`;
323
347
  }
324
348
 
325
349
  // Create a structured error for API/HTTP failures
326
- const httpError = new Error(`API :: Error :: https :: ${options.method} :: ${endpoint} :: ${response.status} :: ${response.statusText}`);
350
+ const httpError = new Error(`API :: Error :: ${transport} :: ${method.toUpperCase()} :: ${endpoint} :: ${response.status} :: ${response.statusText}`);
327
351
  httpError.status = response.status;
328
352
  httpError.statusText = response.statusText;
329
- httpError.method = options.method;
353
+ httpError.method = method;
330
354
  httpError.endpoint = endpoint;
331
355
  httpError.body = errorBody;
356
+ httpError.message = errorBody?.error || errorBody?.message || 'API Error';
332
357
 
358
+ // Debug logging for successful HTTP requests
359
+ if (this.debugMode) {
360
+ console.log(`API :: ERROR :: ${transport} :: ${method.toUpperCase()} :: ${endpoint} :: ${response?.status} :: ${responseRequestId}`, httpError);
361
+ }
362
+
333
363
  throw httpError;
334
364
  }
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();
365
+
366
+ let responseBody;
367
+ if (response?.body) {
368
+ responseBody = response.body;
369
+ } else if (response?.headers?.['content-type']) {
370
+ const contentType = response.headers['content-type'];
371
+ try {
372
+ if (transport === 'https') {
373
+ if (contentType.includes('application/json')) {
374
+ responseBody = await response.json();
375
+ } else if (contentType.includes('text/')) {
376
+ responseBody = await response.text();
377
+ } else {
378
+ responseBody = await response.arrayBuffer();
379
+ }
380
+ } else {
381
+ if (contentType.includes('application/json')) {
382
+ responseBody = this._getJsonSafely(response.body, response.body);
383
+ } else if (contentType.includes('text/')) {
384
+ responseBody = response?.body || '';
385
+ }
386
+ }
387
+ if (!responseBody) {
388
+ responseBody = {};
389
+ }
390
+ } catch (parseError) {
391
+ responseBody = {};
392
+ }
344
393
  } else {
345
- // For binary content (PDFs, images, etc), return as ArrayBuffer
346
- bodyResponse = await response.arrayBuffer();
394
+ responseBody = {};
347
395
  }
348
-
396
+
349
397
  const responseHeaders = response.headers;
350
398
  const responseRequestId =
351
399
  responseHeaders?.get?.('x-request-id') ||
352
400
  responseHeaders?.['x-request-id'];
353
401
 
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
402
 
370
403
  // Debug logging for successful HTTP requests
371
404
  if (this.debugMode) {
372
- console.log(`API :: https :: ${method.toUpperCase()} :: ${endpoint} :: ${response?.status}`);
405
+ console.log(`API :: ${transport} :: ${method.toUpperCase()} :: ${endpoint} :: ${response?.status} :: ${responseRequestId}`);
373
406
  }
374
407
 
375
- return bodyResponse;
408
+ return responseBody;
376
409
  }
377
410
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@unboundcx/sdk",
3
- "version": "2.6.16",
3
+ "version": "2.7.0",
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",