repzo 1.0.228 → 1.0.229

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
@@ -26,7 +26,21 @@ npm install repzo
26
26
 
27
27
  ```typescript
28
28
  import Repzo from "repzo";
29
+
30
+ // Basic initialization
29
31
  const repzo = new Repzo("my-repzo-api-key");
32
+
33
+ // With options (staging environment, custom timeout, retry logic)
34
+ const repzoWithOptions = new Repzo("my-repzo-api-key", {
35
+ env: "staging", // 'production', 'staging', or 'local'
36
+ timeout: 60000, // Request timeout in milliseconds
37
+ retryAttempts: 3, // Number of retry attempts (default: 3)
38
+ headers: {
39
+ "Custom-Header": "value",
40
+ },
41
+ });
42
+
43
+ // Find clients
30
44
  let clients = repzo.client.find({ search: "Mecca" });
31
45
 
32
46
  // Example usage with type safety
@@ -39,6 +53,26 @@ const params: Service.Client.Find.Params = {
39
53
  // Your API implementation here
40
54
  ```
41
55
 
56
+ ### Retry Logic
57
+
58
+ The SDK includes automatic retry logic for failed requests:
59
+
60
+ - **Default Behavior**: Automatically retries failed requests up to 3 times
61
+ - **Exponential Backoff**: Uses exponential backoff strategy (2^attempt seconds)
62
+ - **401 Protection**: Never retries on 401 (Unauthorized) errors
63
+ - **Configurable**: Set custom retry attempts via `retryAttempts` option
64
+
65
+ ```typescript
66
+ // Default: 3 retry attempts
67
+ const repzo = new Repzo("api-key");
68
+
69
+ // Custom: 5 retry attempts
70
+ const repzoCustom = new Repzo("api-key", { retryAttempts: 5 });
71
+
72
+ // Disable retries: 1 attempt only
73
+ const repzoNoRetry = new Repzo("api-key", { retryAttempts: 1 });
74
+ ```
75
+
42
76
  ## 📖 Documentation
43
77
 
44
78
  This SDK provides **three levels of documentation**:
package/changelog.md CHANGED
@@ -4,6 +4,9 @@
4
4
 
5
5
  ### Added
6
6
 
7
+ - add: Automatic retry logic for HTTP requests with exponential backoff (default: 3 attempts, configurable via `retryAttempts` option)
8
+ - add: `retryAttempts` option to SDK constructor for configuring retry behavior @mkhamis
9
+ - add: 401 error protection - authentication errors are never retried @mkhamis
7
10
  - add: asset-part-type, asset-part, asset-part-unit, asset-part-transfer, asset-part-receival, return-asset-part-unit & store-asset-part-unit @maramalshen
8
11
  - update adjustInventory @maramalshen
9
12
  - update FullInvoice with ubl keys @maramalshen
@@ -14,6 +17,8 @@
14
17
 
15
18
  ### Changed
16
19
 
20
+ - All HTTP methods (\_fetch, \_create, \_update, \_patch, \_delete) now support automatic retry with exponential backoff @mkhamis
21
+
17
22
  ### Fixed
18
23
 
19
24
  ### Removed
package/lib/index.d.ts CHANGED
@@ -123,6 +123,7 @@ export default class Repzo {
123
123
  private svAPIEndpoint;
124
124
  headers: Headers;
125
125
  private timeout;
126
+ private retryAttempts;
126
127
  constructor(apiKey: string, options?: Options);
127
128
  private static _end_points;
128
129
  static get END_POINTS(): {
@@ -242,6 +243,7 @@ export default class Repzo {
242
243
  readonly PROMOTIONS: "promotions";
243
244
  readonly COMPARE_INVOICE_TO_WAREHOUSE: "compare-invoice-to-warehouse";
244
245
  };
246
+ private _retryRequest;
245
247
  private _fetch;
246
248
  private _create;
247
249
  private _update;
package/lib/index.js CHANGED
@@ -2338,52 +2338,83 @@ class Repzo {
2338
2338
  else {
2339
2339
  this.timeout = 180000;
2340
2340
  }
2341
+ this.retryAttempts = options?.retryAttempts ?? 3;
2341
2342
  }
2342
2343
  static get END_POINTS() {
2343
2344
  return Repzo._end_points;
2344
2345
  }
2345
- async _fetch(baseUrl, path, params) {
2346
- if (params) {
2347
- params = normalizeParams(params);
2346
+ async _retryRequest(requestFn, attempt = 1) {
2347
+ try {
2348
+ return await requestFn();
2349
+ }
2350
+ catch (error) {
2351
+ // Don't retry on 401 (Unauthorized) errors
2352
+ if (error?.response?.status === 401) {
2353
+ throw error;
2354
+ }
2355
+ // Retry if we haven't exceeded the retry attempts
2356
+ if (attempt < this.retryAttempts) {
2357
+ // Exponential backoff: wait 2^attempt seconds before retrying
2358
+ const delay = Math.pow(2, attempt) * 1000;
2359
+ await new Promise((resolve) => setTimeout(resolve, delay));
2360
+ return this._retryRequest(requestFn, attempt + 1);
2361
+ }
2362
+ // If all retries failed, throw the error
2363
+ throw error;
2348
2364
  }
2349
- let res = await axios.get(`${baseUrl}/${path}`, {
2350
- params,
2351
- headers: this.headers,
2352
- timeout: this.timeout,
2365
+ }
2366
+ async _fetch(baseUrl, path, params) {
2367
+ return this._retryRequest(async () => {
2368
+ if (params) {
2369
+ params = normalizeParams(params);
2370
+ }
2371
+ let res = await axios.get(`${baseUrl}/${path}`, {
2372
+ params,
2373
+ headers: this.headers,
2374
+ timeout: this.timeout,
2375
+ });
2376
+ return res.data;
2353
2377
  });
2354
- return res.data;
2355
2378
  }
2356
2379
  async _create(baseUrl, path, body, params) {
2357
- let res = await axios.post(`${baseUrl}/${path}`, body, {
2358
- params,
2359
- headers: this.headers,
2360
- timeout: this.timeout,
2380
+ return this._retryRequest(async () => {
2381
+ let res = await axios.post(`${baseUrl}/${path}`, body, {
2382
+ params,
2383
+ headers: this.headers,
2384
+ timeout: this.timeout,
2385
+ });
2386
+ return res.data;
2361
2387
  });
2362
- return res.data;
2363
2388
  }
2364
2389
  async _update(baseUrl, path, body, params) {
2365
- let res = await axios.put(`${baseUrl}/${path}`, body, {
2366
- params,
2367
- headers: this.headers,
2368
- timeout: this.timeout,
2390
+ return this._retryRequest(async () => {
2391
+ let res = await axios.put(`${baseUrl}/${path}`, body, {
2392
+ params,
2393
+ headers: this.headers,
2394
+ timeout: this.timeout,
2395
+ });
2396
+ return res.data;
2369
2397
  });
2370
- return res.data;
2371
2398
  }
2372
2399
  async _patch(baseUrl, path, body, params) {
2373
- let res = await axios.put(`${baseUrl}/${path}`, body, {
2374
- params,
2375
- headers: this.headers,
2376
- timeout: this.timeout,
2400
+ return this._retryRequest(async () => {
2401
+ let res = await axios.put(`${baseUrl}/${path}`, body, {
2402
+ params,
2403
+ headers: this.headers,
2404
+ timeout: this.timeout,
2405
+ });
2406
+ return res.data;
2377
2407
  });
2378
- return res.data;
2379
2408
  }
2380
2409
  async _delete(baseUrl, path, params) {
2381
- let res = await axios.delete(`${baseUrl}/${path}`, {
2382
- params,
2383
- headers: this.headers,
2384
- timeout: this.timeout,
2410
+ return this._retryRequest(async () => {
2411
+ let res = await axios.delete(`${baseUrl}/${path}`, {
2412
+ params,
2413
+ headers: this.headers,
2414
+ timeout: this.timeout,
2415
+ });
2416
+ return res.data;
2385
2417
  });
2386
- return res.data;
2387
2418
  }
2388
2419
  }
2389
2420
  Repzo._end_points = end_points;
@@ -12,6 +12,7 @@ export interface Options {
12
12
  [key: string]: string;
13
13
  };
14
14
  timeout?: number | undefined;
15
+ retryAttempts?: number;
15
16
  }
16
17
  export interface Headers {
17
18
  "api-key": string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "repzo",
3
- "version": "1.0.228",
3
+ "version": "1.0.229",
4
4
  "description": "Repzo TypeScript SDK",
5
5
  "main": "./lib/index.js",
6
6
  "types": "./lib/index.d.ts",
package/src/index.ts CHANGED
@@ -230,6 +230,7 @@ export default class Repzo {
230
230
  private svAPIEndpoint: string;
231
231
  headers: Headers;
232
232
  private timeout: number;
233
+ private retryAttempts: number;
233
234
  constructor(apiKey: string, options?: Options) {
234
235
  this.svAPIEndpoint =
235
236
  !options?.env || options?.env == "production"
@@ -251,22 +252,51 @@ export default class Repzo {
251
252
  } else {
252
253
  this.timeout = 180000;
253
254
  }
255
+ this.retryAttempts = options?.retryAttempts ?? 3;
254
256
  }
255
257
 
256
258
  private static _end_points = end_points;
257
259
  public static get END_POINTS() {
258
260
  return Repzo._end_points;
259
261
  }
260
- private async _fetch(baseUrl: string, path: string, params?: Params) {
261
- if (params) {
262
- params = normalizeParams(params);
262
+
263
+ private async _retryRequest<T>(
264
+ requestFn: () => Promise<T>,
265
+ attempt: number = 1,
266
+ ): Promise<T> {
267
+ try {
268
+ return await requestFn();
269
+ } catch (error: any) {
270
+ // Don't retry on 401 (Unauthorized) errors
271
+ if (error?.response?.status === 401) {
272
+ throw error;
273
+ }
274
+
275
+ // Retry if we haven't exceeded the retry attempts
276
+ if (attempt < this.retryAttempts) {
277
+ // Exponential backoff: wait 2^attempt seconds before retrying
278
+ const delay = Math.pow(2, attempt) * 1000;
279
+ await new Promise((resolve) => setTimeout(resolve, delay));
280
+ return this._retryRequest(requestFn, attempt + 1);
281
+ }
282
+
283
+ // If all retries failed, throw the error
284
+ throw error;
263
285
  }
264
- let res: any = await axios.get(`${baseUrl}/${path}`, {
265
- params,
266
- headers: this.headers,
267
- timeout: this.timeout,
286
+ }
287
+
288
+ private async _fetch(baseUrl: string, path: string, params?: Params) {
289
+ return this._retryRequest(async () => {
290
+ if (params) {
291
+ params = normalizeParams(params);
292
+ }
293
+ let res: any = await axios.get(`${baseUrl}/${path}`, {
294
+ params,
295
+ headers: this.headers,
296
+ timeout: this.timeout,
297
+ });
298
+ return res.data;
268
299
  });
269
- return res.data;
270
300
  }
271
301
 
272
302
  private async _create(
@@ -275,12 +305,14 @@ export default class Repzo {
275
305
  body: Data,
276
306
  params?: Params,
277
307
  ) {
278
- let res: any = await axios.post(`${baseUrl}/${path}`, body, {
279
- params,
280
- headers: this.headers,
281
- timeout: this.timeout,
308
+ return this._retryRequest(async () => {
309
+ let res: any = await axios.post(`${baseUrl}/${path}`, body, {
310
+ params,
311
+ headers: this.headers,
312
+ timeout: this.timeout,
313
+ });
314
+ return res.data;
282
315
  });
283
- return res.data;
284
316
  }
285
317
 
286
318
  private async _update(
@@ -289,12 +321,14 @@ export default class Repzo {
289
321
  body: Data,
290
322
  params?: Params,
291
323
  ) {
292
- let res: any = await axios.put(`${baseUrl}/${path}`, body, {
293
- params,
294
- headers: this.headers,
295
- timeout: this.timeout,
324
+ return this._retryRequest(async () => {
325
+ let res: any = await axios.put(`${baseUrl}/${path}`, body, {
326
+ params,
327
+ headers: this.headers,
328
+ timeout: this.timeout,
329
+ });
330
+ return res.data;
296
331
  });
297
- return res.data;
298
332
  }
299
333
 
300
334
  private async _patch(
@@ -303,21 +337,25 @@ export default class Repzo {
303
337
  body: Data,
304
338
  params?: Params,
305
339
  ) {
306
- let res: any = await axios.put(`${baseUrl}/${path}`, body, {
307
- params,
308
- headers: this.headers,
309
- timeout: this.timeout,
340
+ return this._retryRequest(async () => {
341
+ let res: any = await axios.put(`${baseUrl}/${path}`, body, {
342
+ params,
343
+ headers: this.headers,
344
+ timeout: this.timeout,
345
+ });
346
+ return res.data;
310
347
  });
311
- return res.data;
312
348
  }
313
349
 
314
350
  private async _delete(baseUrl: string, path: string, params?: Params) {
315
- let res: any = await axios.delete(`${baseUrl}/${path}`, {
316
- params,
317
- headers: this.headers,
318
- timeout: this.timeout,
351
+ return this._retryRequest(async () => {
352
+ let res: any = await axios.delete(`${baseUrl}/${path}`, {
353
+ params,
354
+ headers: this.headers,
355
+ timeout: this.timeout,
356
+ });
357
+ return res.data;
319
358
  });
320
- return res.data;
321
359
  }
322
360
 
323
361
  available_services = availableService;
@@ -11,6 +11,7 @@ export interface Options {
11
11
  env?: "staging" | "local" | "production";
12
12
  headers?: { [key: string]: string };
13
13
  timeout?: number | undefined;
14
+ retryAttempts?: number;
14
15
  }
15
16
  export interface Headers {
16
17
  "api-key": string;