@zapier/zapier-sdk 0.13.8 → 0.13.9
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/CHANGELOG.md +7 -0
- package/dist/api/polling.d.ts.map +1 -1
- package/dist/api/polling.js +1 -11
- package/dist/index.cjs +106 -13
- package/dist/index.d.mts +73 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.mjs +106 -14
- package/dist/utils/batch-utils.d.ts +72 -0
- package/dist/utils/batch-utils.d.ts.map +1 -0
- package/dist/utils/batch-utils.js +162 -0
- package/dist/utils/batch-utils.test.d.ts +2 -0
- package/dist/utils/batch-utils.test.d.ts.map +1 -0
- package/dist/utils/batch-utils.test.js +476 -0
- package/dist/utils/retry-utils.d.ts +45 -0
- package/dist/utils/retry-utils.d.ts.map +1 -0
- package/dist/utils/retry-utils.js +51 -0
- package/dist/utils/retry-utils.test.d.ts +2 -0
- package/dist/utils/retry-utils.test.d.ts.map +1 -0
- package/dist/utils/retry-utils.test.js +90 -0
- package/package.json +1 -1
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Retry and Backoff Utilities
|
|
3
|
+
*
|
|
4
|
+
* Shared utilities for implementing resilient retry logic with exponential backoff
|
|
5
|
+
* and jitter. Used by both polling and batch operations.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Maximum number of consecutive errors before giving up
|
|
9
|
+
* Prevents infinite retry loops when the API is consistently failing
|
|
10
|
+
*/
|
|
11
|
+
export declare const MAX_CONSECUTIVE_ERRORS = 3;
|
|
12
|
+
/**
|
|
13
|
+
* Base delay for error backoff in milliseconds
|
|
14
|
+
* Each error adds this amount (with scaling) to the wait time
|
|
15
|
+
*/
|
|
16
|
+
export declare const BASE_ERROR_BACKOFF_MS = 1000;
|
|
17
|
+
/**
|
|
18
|
+
* Jitter factor (0.0 to 1.0) for randomizing wait times
|
|
19
|
+
* Prevents thundering herd problem when many clients retry simultaneously
|
|
20
|
+
* A factor of 0.5 means we add 0-50% random variance to the base interval
|
|
21
|
+
*/
|
|
22
|
+
export declare const JITTER_FACTOR = 0.5;
|
|
23
|
+
/**
|
|
24
|
+
* Calculate wait time with jitter and error backoff
|
|
25
|
+
*
|
|
26
|
+
* This implements two key reliability patterns:
|
|
27
|
+
* 1. Jitter - Adds randomness to prevent synchronized retries across clients
|
|
28
|
+
* 2. Error backoff - Increases wait time when errors occur, giving the API time to recover
|
|
29
|
+
*
|
|
30
|
+
* @param baseInterval - The base wait time in milliseconds
|
|
31
|
+
* @param errorCount - Number of consecutive errors (0 if no errors)
|
|
32
|
+
* @returns Wait time in milliseconds with jitter and error backoff applied
|
|
33
|
+
*
|
|
34
|
+
* @example
|
|
35
|
+
* // No errors: returns 1000-1500ms (1000 + 0-500ms jitter)
|
|
36
|
+
* calculateWaitTime(1000, 0);
|
|
37
|
+
*
|
|
38
|
+
* // 2 errors: returns 1000-1500ms base + up to 1000ms error backoff = 1000-2500ms
|
|
39
|
+
* calculateWaitTime(1000, 2);
|
|
40
|
+
*
|
|
41
|
+
* // 6 errors: returns 1000-1500ms base + 2000ms capped backoff = 3000-3500ms
|
|
42
|
+
* calculateWaitTime(1000, 6);
|
|
43
|
+
*/
|
|
44
|
+
export declare function calculateWaitTime(baseInterval: number, errorCount: number): number;
|
|
45
|
+
//# sourceMappingURL=retry-utils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"retry-utils.d.ts","sourceRoot":"","sources":["../../src/utils/retry-utils.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH;;;GAGG;AACH,eAAO,MAAM,sBAAsB,IAAI,CAAC;AAExC;;;GAGG;AACH,eAAO,MAAM,qBAAqB,OAAQ,CAAC;AAE3C;;;;GAIG;AACH,eAAO,MAAM,aAAa,MAAM,CAAC;AAEjC;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,iBAAiB,CAC/B,YAAY,EAAE,MAAM,EACpB,UAAU,EAAE,MAAM,GACjB,MAAM,CAYR"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Retry and Backoff Utilities
|
|
3
|
+
*
|
|
4
|
+
* Shared utilities for implementing resilient retry logic with exponential backoff
|
|
5
|
+
* and jitter. Used by both polling and batch operations.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Maximum number of consecutive errors before giving up
|
|
9
|
+
* Prevents infinite retry loops when the API is consistently failing
|
|
10
|
+
*/
|
|
11
|
+
export const MAX_CONSECUTIVE_ERRORS = 3;
|
|
12
|
+
/**
|
|
13
|
+
* Base delay for error backoff in milliseconds
|
|
14
|
+
* Each error adds this amount (with scaling) to the wait time
|
|
15
|
+
*/
|
|
16
|
+
export const BASE_ERROR_BACKOFF_MS = 1000;
|
|
17
|
+
/**
|
|
18
|
+
* Jitter factor (0.0 to 1.0) for randomizing wait times
|
|
19
|
+
* Prevents thundering herd problem when many clients retry simultaneously
|
|
20
|
+
* A factor of 0.5 means we add 0-50% random variance to the base interval
|
|
21
|
+
*/
|
|
22
|
+
export const JITTER_FACTOR = 0.5;
|
|
23
|
+
/**
|
|
24
|
+
* Calculate wait time with jitter and error backoff
|
|
25
|
+
*
|
|
26
|
+
* This implements two key reliability patterns:
|
|
27
|
+
* 1. Jitter - Adds randomness to prevent synchronized retries across clients
|
|
28
|
+
* 2. Error backoff - Increases wait time when errors occur, giving the API time to recover
|
|
29
|
+
*
|
|
30
|
+
* @param baseInterval - The base wait time in milliseconds
|
|
31
|
+
* @param errorCount - Number of consecutive errors (0 if no errors)
|
|
32
|
+
* @returns Wait time in milliseconds with jitter and error backoff applied
|
|
33
|
+
*
|
|
34
|
+
* @example
|
|
35
|
+
* // No errors: returns 1000-1500ms (1000 + 0-500ms jitter)
|
|
36
|
+
* calculateWaitTime(1000, 0);
|
|
37
|
+
*
|
|
38
|
+
* // 2 errors: returns 1000-1500ms base + up to 1000ms error backoff = 1000-2500ms
|
|
39
|
+
* calculateWaitTime(1000, 2);
|
|
40
|
+
*
|
|
41
|
+
* // 6 errors: returns 1000-1500ms base + 2000ms capped backoff = 3000-3500ms
|
|
42
|
+
* calculateWaitTime(1000, 6);
|
|
43
|
+
*/
|
|
44
|
+
export function calculateWaitTime(baseInterval, errorCount) {
|
|
45
|
+
// Jitter to avoid thundering herd (multiple clients retrying at exact same time)
|
|
46
|
+
const jitter = Math.random() * JITTER_FACTOR * baseInterval;
|
|
47
|
+
// Exponential backoff scaled by error count, but capped at 2x base interval
|
|
48
|
+
// This prevents wait times from growing unboundedly
|
|
49
|
+
const errorBackoff = Math.min(BASE_ERROR_BACKOFF_MS * (errorCount / 2), baseInterval * 2);
|
|
50
|
+
return Math.floor(baseInterval + jitter + errorBackoff);
|
|
51
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"retry-utils.test.d.ts","sourceRoot":"","sources":["../../src/utils/retry-utils.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
|
|
2
|
+
import { calculateWaitTime, MAX_CONSECUTIVE_ERRORS, BASE_ERROR_BACKOFF_MS, JITTER_FACTOR, } from "./retry-utils";
|
|
3
|
+
describe("retry-utils", () => {
|
|
4
|
+
describe("calculateWaitTime", () => {
|
|
5
|
+
beforeEach(() => {
|
|
6
|
+
// Seed random for consistent testing
|
|
7
|
+
vi.spyOn(Math, "random");
|
|
8
|
+
});
|
|
9
|
+
afterEach(() => {
|
|
10
|
+
vi.restoreAllMocks();
|
|
11
|
+
});
|
|
12
|
+
it("should return base interval with jitter when no errors", () => {
|
|
13
|
+
vi.spyOn(Math, "random").mockReturnValue(0.5); // Middle of jitter range
|
|
14
|
+
const baseInterval = 1000;
|
|
15
|
+
const waitTime = calculateWaitTime(baseInterval, 0);
|
|
16
|
+
// With 0.5 random and JITTER_FACTOR 0.5:
|
|
17
|
+
// jitter = 0.5 * 0.5 * 1000 = 250ms
|
|
18
|
+
// errorBackoff = 0 (no errors)
|
|
19
|
+
// total = 1000 + 250 + 0 = 1250ms
|
|
20
|
+
expect(waitTime).toBe(1250);
|
|
21
|
+
});
|
|
22
|
+
it("should add error backoff for consecutive errors", () => {
|
|
23
|
+
vi.spyOn(Math, "random").mockReturnValue(0); // No jitter for simpler math
|
|
24
|
+
const baseInterval = 1000;
|
|
25
|
+
const errorCount = 2;
|
|
26
|
+
const waitTime = calculateWaitTime(baseInterval, errorCount);
|
|
27
|
+
// jitter = 0 (random is 0)
|
|
28
|
+
// errorBackoff = BASE_ERROR_BACKOFF_MS * (errorCount / 2) = 1000 * (2/2) = 1000ms
|
|
29
|
+
// total = 1000 + 0 + 1000 = 2000ms
|
|
30
|
+
expect(waitTime).toBe(2000);
|
|
31
|
+
});
|
|
32
|
+
it("should cap error backoff at 2x base interval", () => {
|
|
33
|
+
vi.spyOn(Math, "random").mockReturnValue(0); // No jitter
|
|
34
|
+
const baseInterval = 1000;
|
|
35
|
+
const errorCount = 10; // Large error count
|
|
36
|
+
const waitTime = calculateWaitTime(baseInterval, errorCount);
|
|
37
|
+
// errorBackoff would be 1000 * (10/2) = 5000ms
|
|
38
|
+
// but it's capped at 2x base = 2000ms
|
|
39
|
+
// total = 1000 + 0 + 2000 = 3000ms
|
|
40
|
+
expect(waitTime).toBe(3000);
|
|
41
|
+
});
|
|
42
|
+
it("should add random jitter to prevent thundering herd", () => {
|
|
43
|
+
const baseInterval = 1000;
|
|
44
|
+
const results = new Set();
|
|
45
|
+
// Run multiple times with real random to ensure jitter varies
|
|
46
|
+
vi.spyOn(Math, "random").mockRestore(); // Use real random
|
|
47
|
+
for (let i = 0; i < 10; i++) {
|
|
48
|
+
const waitTime = calculateWaitTime(baseInterval, 0);
|
|
49
|
+
results.add(waitTime);
|
|
50
|
+
}
|
|
51
|
+
// Should have multiple different values due to jitter
|
|
52
|
+
expect(results.size).toBeGreaterThan(1);
|
|
53
|
+
// All values should be in expected range
|
|
54
|
+
// baseInterval (1000) + jitter (0 to JITTER_FACTOR * baseInterval)
|
|
55
|
+
// = 1000 to 1500ms
|
|
56
|
+
for (const result of results) {
|
|
57
|
+
expect(result).toBeGreaterThanOrEqual(1000);
|
|
58
|
+
expect(result).toBeLessThanOrEqual(1500);
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
it("should combine jitter and error backoff", () => {
|
|
62
|
+
vi.spyOn(Math, "random").mockReturnValue(0.5);
|
|
63
|
+
const baseInterval = 1000;
|
|
64
|
+
const errorCount = 2;
|
|
65
|
+
const waitTime = calculateWaitTime(baseInterval, errorCount);
|
|
66
|
+
// jitter = 0.5 * 0.5 * 1000 = 250ms
|
|
67
|
+
// errorBackoff = 1000 * (2/2) = 1000ms
|
|
68
|
+
// total = 1000 + 250 + 1000 = 2250ms
|
|
69
|
+
expect(waitTime).toBe(2250);
|
|
70
|
+
});
|
|
71
|
+
it("should return integer wait time", () => {
|
|
72
|
+
vi.spyOn(Math, "random").mockReturnValue(0.333);
|
|
73
|
+
const baseInterval = 1000;
|
|
74
|
+
const waitTime = calculateWaitTime(baseInterval, 1);
|
|
75
|
+
// Result should be floored integer
|
|
76
|
+
expect(Number.isInteger(waitTime)).toBe(true);
|
|
77
|
+
});
|
|
78
|
+
});
|
|
79
|
+
describe("constants", () => {
|
|
80
|
+
it("should export MAX_CONSECUTIVE_ERRORS", () => {
|
|
81
|
+
expect(MAX_CONSECUTIVE_ERRORS).toBe(3);
|
|
82
|
+
});
|
|
83
|
+
it("should export BASE_ERROR_BACKOFF_MS", () => {
|
|
84
|
+
expect(BASE_ERROR_BACKOFF_MS).toBe(1000);
|
|
85
|
+
});
|
|
86
|
+
it("should export JITTER_FACTOR", () => {
|
|
87
|
+
expect(JITTER_FACTOR).toBe(0.5);
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
});
|