lightspeed-retail-sdk 3.3.4 → 3.3.5

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 CHANGED
@@ -2,9 +2,9 @@
2
2
 
3
3
  A modern JavaScript SDK for interacting with the Lightspeed Retail API. This SDK provides a convenient, secure, and flexible way to access Lightspeed Retail's features—including customer, item, and order management.
4
4
 
5
- **Current Version: 3.3.4** — improve error checking on token refresh to prevent false email warnings.
5
+ **Current Version: 3.3.5** — improve error checking on token refresh to prevent false email warnings.
6
6
 
7
- ## **🆕 Recent Updates (v3.3.4)**
7
+ ## **🆕 Recent Updates (v3.3.5)**
8
8
 
9
9
  - **Add centralized query param builder for API requests**: Add centralized query param builder for API requests. Supports input as object, string, or array, and manages relations/load_relations. Ensures no double-encoding of parameters and handles special cases for 'or' and 'timeStamp'.
10
10
  - **🎯 Enhanced Parameter Support**: All main getter methods now support both legacy and new object-based parameters with full backward compatibility
@@ -66,7 +66,7 @@ const items = await sdk.getItems({
66
66
  ## Table of Contents
67
67
 
68
68
  - [Another Unofficial Lightspeed Retail V3 API SDK](#another-unofficial-lightspeed-retail-v3-api-sdk)
69
- - [**🆕 Recent Updates (v3.3.4)**](#-recent-updates-v334)
69
+ - [**🆕 Recent Updates (v3.3.5)**](#-recent-updates-v335)
70
70
  - [🚀 Key Features](#-key-features)
71
71
  - [🔄 Migrating from 3.1.x](#-migrating-from-31x)
72
72
  - [Backward Compatibility](#backward-compatibility)
@@ -291,6 +291,81 @@ class LightspeedSDKCore {
291
291
  this.refreshInProgress = false; // Release the lock
292
292
  }
293
293
  }
294
+ // Core API request handler
295
+ async executeApiRequest(options, retries = 0) {
296
+ await this.handleRateLimit(options);
297
+ const token = await this.getToken();
298
+ if (!token) throw new Error("Error Fetching Token");
299
+ options.headers = {
300
+ Authorization: `Bearer ${token}`,
301
+ "Content-Type": "application/json",
302
+ ...options.headers
303
+ };
304
+ // Centralized query param handling
305
+ if (options.params) {
306
+ const queryString = buildQueryParams(options.params);
307
+ if (queryString) {
308
+ // Remove any trailing ? or & from url
309
+ options.url = options.url.replace(/[?&]+$/, "");
310
+ options.url += (options.url.includes("?") ? "&" : "?") + queryString;
311
+ }
312
+ delete options.params; // Don't let axios try to re-encode
313
+ }
314
+ try {
315
+ const res = await (0, _axios.default)(options);
316
+ this.lastResponse = res;
317
+ if (options.method === "GET") {
318
+ var _res_data_attributes, _res_data_attributes1;
319
+ // Handle successful response with no data or empty data
320
+ if (!res.data || Object.keys(res.data).length === 0) {
321
+ return {
322
+ data: {},
323
+ next: null,
324
+ previous: null
325
+ };
326
+ }
327
+ // Check if response has the expected structure but with empty arrays
328
+ const dataKeys = Object.keys(res.data).filter((key)=>key !== "@attributes");
329
+ if (dataKeys.length > 0) {
330
+ const firstDataKey = dataKeys[0];
331
+ const firstDataValue = res.data[firstDataKey];
332
+ // No need to log for empty arrays - this is normal
333
+ }
334
+ // Handle successful response with data
335
+ return {
336
+ data: res.data,
337
+ next: (_res_data_attributes = res.data["@attributes"]) === null || _res_data_attributes === void 0 ? void 0 : _res_data_attributes.next,
338
+ previous: (_res_data_attributes1 = res.data["@attributes"]) === null || _res_data_attributes1 === void 0 ? void 0 : _res_data_attributes1.prev
339
+ };
340
+ } else {
341
+ return res.data;
342
+ }
343
+ } catch (err) {
344
+ var _err_response;
345
+ // Handle 401 auth errors with automatic retry
346
+ if (((_err_response = err.response) === null || _err_response === void 0 ? void 0 : _err_response.status) === 401 && !options._authRetryAttempted) {
347
+ console.log("🔄 401 error - forcing token refresh and retrying...");
348
+ options._authRetryAttempted = true;
349
+ this.token = null;
350
+ try {
351
+ await this.refreshTokens();
352
+ return this.executeApiRequest(options, retries);
353
+ } catch (refreshError) {
354
+ console.error("Failed to refresh tokens:", refreshError.message);
355
+ throw refreshError;
356
+ }
357
+ }
358
+ // Handle retryable errors
359
+ if (this.isRetryableError(err) && retries < this.maxRetries) {
360
+ this.handleError(`Network Error Retrying in 2 seconds...`, err.message, false);
361
+ await sleep(2000);
362
+ return this.executeApiRequest(options, retries + 1);
363
+ } else {
364
+ // Simple error handling - let the calling method decide how to handle it
365
+ throw err;
366
+ }
367
+ }
368
+ }
294
369
  // Paginated data fetching
295
370
  async getAllData(options) {
296
371
  var _options_params;
@@ -320,6 +320,97 @@ export class LightspeedSDKCore {
320
320
  }
321
321
  }
322
322
 
323
+ // Core API request handler
324
+ async executeApiRequest(options, retries = 0) {
325
+ await this.handleRateLimit(options);
326
+
327
+ const token = await this.getToken();
328
+ if (!token) throw new Error("Error Fetching Token");
329
+
330
+ options.headers = {
331
+ Authorization: `Bearer ${token}`,
332
+ "Content-Type": "application/json",
333
+ ...options.headers,
334
+ };
335
+
336
+ // Centralized query param handling
337
+ if (options.params) {
338
+ const queryString = buildQueryParams(options.params);
339
+ if (queryString) {
340
+ // Remove any trailing ? or & from url
341
+ options.url = options.url.replace(/[?&]+$/, "");
342
+ options.url += (options.url.includes("?") ? "&" : "?") + queryString;
343
+ }
344
+ delete options.params; // Don't let axios try to re-encode
345
+ }
346
+
347
+ try {
348
+ const res = await axios(options);
349
+ this.lastResponse = res;
350
+
351
+ if (options.method === "GET") {
352
+ // Handle successful response with no data or empty data
353
+ if (!res.data || Object.keys(res.data).length === 0) {
354
+ return {
355
+ data: {},
356
+ next: null,
357
+ previous: null,
358
+ };
359
+ }
360
+
361
+ // Check if response has the expected structure but with empty arrays
362
+ const dataKeys = Object.keys(res.data).filter(
363
+ (key) => key !== "@attributes"
364
+ );
365
+ if (dataKeys.length > 0) {
366
+ const firstDataKey = dataKeys[0];
367
+ const firstDataValue = res.data[firstDataKey];
368
+
369
+ // No need to log for empty arrays - this is normal
370
+ }
371
+
372
+ // Handle successful response with data
373
+ return {
374
+ data: res.data,
375
+ next: res.data["@attributes"]?.next,
376
+ previous: res.data["@attributes"]?.prev,
377
+ };
378
+ } else {
379
+ return res.data;
380
+ }
381
+ } catch (err) {
382
+ // Handle 401 auth errors with automatic retry
383
+ if (err.response?.status === 401 && !options._authRetryAttempted) {
384
+ console.log("🔄 401 error - forcing token refresh and retrying...");
385
+
386
+ options._authRetryAttempted = true;
387
+ this.token = null;
388
+
389
+ try {
390
+ await this.refreshTokens();
391
+ return this.executeApiRequest(options, retries);
392
+ } catch (refreshError) {
393
+ console.error("Failed to refresh tokens:", refreshError.message);
394
+ throw refreshError;
395
+ }
396
+ }
397
+
398
+ // Handle retryable errors
399
+ if (this.isRetryableError(err) && retries < this.maxRetries) {
400
+ this.handleError(
401
+ `Network Error Retrying in 2 seconds...`,
402
+ err.message,
403
+ false
404
+ );
405
+ await sleep(2000);
406
+ return this.executeApiRequest(options, retries + 1);
407
+ } else {
408
+ // Simple error handling - let the calling method decide how to handle it
409
+ throw err;
410
+ }
411
+ }
412
+ }
413
+
323
414
  // Paginated data fetching
324
415
  async getAllData(options) {
325
416
  let allData = [];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lightspeed-retail-sdk",
3
- "version": "3.3.4",
3
+ "version": "3.3.5",
4
4
  "description": "Another unofficial Lightspeed Retail API SDK for Node.js",
5
5
  "type": "module",
6
6
  "main": "dist/index.cjs",