@zapier/zapier-sdk 0.13.6 → 0.13.7

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 (114) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/dist/index.cjs +1 -1
  3. package/dist/index.mjs +1 -1
  4. package/package.json +7 -2
  5. package/src/api/auth.ts +0 -28
  6. package/src/api/client.ts +0 -491
  7. package/src/api/debug.test.ts +0 -76
  8. package/src/api/debug.ts +0 -154
  9. package/src/api/index.ts +0 -90
  10. package/src/api/polling.test.ts +0 -405
  11. package/src/api/polling.ts +0 -253
  12. package/src/api/schemas.ts +0 -465
  13. package/src/api/types.ts +0 -152
  14. package/src/auth.ts +0 -72
  15. package/src/constants.ts +0 -16
  16. package/src/index.ts +0 -111
  17. package/src/plugins/api/index.ts +0 -43
  18. package/src/plugins/apps/index.ts +0 -203
  19. package/src/plugins/apps/schemas.ts +0 -64
  20. package/src/plugins/eventEmission/builders.ts +0 -115
  21. package/src/plugins/eventEmission/index.test.ts +0 -169
  22. package/src/plugins/eventEmission/index.ts +0 -294
  23. package/src/plugins/eventEmission/transport.test.ts +0 -214
  24. package/src/plugins/eventEmission/transport.ts +0 -135
  25. package/src/plugins/eventEmission/types.ts +0 -58
  26. package/src/plugins/eventEmission/utils.ts +0 -121
  27. package/src/plugins/fetch/index.ts +0 -83
  28. package/src/plugins/fetch/schemas.ts +0 -37
  29. package/src/plugins/findFirstAuthentication/index.test.ts +0 -209
  30. package/src/plugins/findFirstAuthentication/index.ts +0 -68
  31. package/src/plugins/findFirstAuthentication/schemas.ts +0 -47
  32. package/src/plugins/findUniqueAuthentication/index.test.ts +0 -197
  33. package/src/plugins/findUniqueAuthentication/index.ts +0 -77
  34. package/src/plugins/findUniqueAuthentication/schemas.ts +0 -49
  35. package/src/plugins/getAction/index.test.ts +0 -239
  36. package/src/plugins/getAction/index.ts +0 -75
  37. package/src/plugins/getAction/schemas.ts +0 -41
  38. package/src/plugins/getApp/index.test.ts +0 -181
  39. package/src/plugins/getApp/index.ts +0 -60
  40. package/src/plugins/getApp/schemas.ts +0 -33
  41. package/src/plugins/getAuthentication/index.test.ts +0 -294
  42. package/src/plugins/getAuthentication/index.ts +0 -95
  43. package/src/plugins/getAuthentication/schemas.ts +0 -38
  44. package/src/plugins/getProfile/index.ts +0 -60
  45. package/src/plugins/getProfile/schemas.ts +0 -24
  46. package/src/plugins/listActions/index.test.ts +0 -526
  47. package/src/plugins/listActions/index.ts +0 -132
  48. package/src/plugins/listActions/schemas.ts +0 -55
  49. package/src/plugins/listApps/index.test.ts +0 -378
  50. package/src/plugins/listApps/index.ts +0 -159
  51. package/src/plugins/listApps/schemas.ts +0 -41
  52. package/src/plugins/listAuthentications/index.test.ts +0 -739
  53. package/src/plugins/listAuthentications/index.ts +0 -152
  54. package/src/plugins/listAuthentications/schemas.ts +0 -77
  55. package/src/plugins/listInputFieldChoices/index.test.ts +0 -653
  56. package/src/plugins/listInputFieldChoices/index.ts +0 -173
  57. package/src/plugins/listInputFieldChoices/schemas.ts +0 -125
  58. package/src/plugins/listInputFields/index.test.ts +0 -439
  59. package/src/plugins/listInputFields/index.ts +0 -294
  60. package/src/plugins/listInputFields/schemas.ts +0 -68
  61. package/src/plugins/manifest/index.test.ts +0 -776
  62. package/src/plugins/manifest/index.ts +0 -461
  63. package/src/plugins/manifest/schemas.ts +0 -60
  64. package/src/plugins/registry/index.ts +0 -160
  65. package/src/plugins/request/index.test.ts +0 -333
  66. package/src/plugins/request/index.ts +0 -105
  67. package/src/plugins/request/schemas.ts +0 -69
  68. package/src/plugins/runAction/index.test.ts +0 -388
  69. package/src/plugins/runAction/index.ts +0 -215
  70. package/src/plugins/runAction/schemas.ts +0 -60
  71. package/src/resolvers/actionKey.ts +0 -37
  72. package/src/resolvers/actionType.ts +0 -34
  73. package/src/resolvers/appKey.ts +0 -7
  74. package/src/resolvers/authenticationId.ts +0 -54
  75. package/src/resolvers/index.ts +0 -11
  76. package/src/resolvers/inputFieldKey.ts +0 -70
  77. package/src/resolvers/inputs.ts +0 -69
  78. package/src/schemas/Action.ts +0 -52
  79. package/src/schemas/App.ts +0 -45
  80. package/src/schemas/Auth.ts +0 -59
  81. package/src/schemas/Field.ts +0 -169
  82. package/src/schemas/Run.ts +0 -40
  83. package/src/schemas/UserProfile.ts +0 -60
  84. package/src/sdk.test.ts +0 -212
  85. package/src/sdk.ts +0 -178
  86. package/src/types/domain.test.ts +0 -50
  87. package/src/types/domain.ts +0 -66
  88. package/src/types/errors.ts +0 -278
  89. package/src/types/events.ts +0 -43
  90. package/src/types/functions.ts +0 -28
  91. package/src/types/optional-zapier-sdk-cli-login.d.ts +0 -37
  92. package/src/types/plugin.ts +0 -125
  93. package/src/types/properties.ts +0 -80
  94. package/src/types/sdk.ts +0 -111
  95. package/src/types/telemetry-events.ts +0 -85
  96. package/src/utils/array-utils.test.ts +0 -131
  97. package/src/utils/array-utils.ts +0 -41
  98. package/src/utils/domain-utils.test.ts +0 -433
  99. package/src/utils/domain-utils.ts +0 -267
  100. package/src/utils/file-utils.test.ts +0 -73
  101. package/src/utils/file-utils.ts +0 -94
  102. package/src/utils/function-utils.test.ts +0 -141
  103. package/src/utils/function-utils.ts +0 -245
  104. package/src/utils/pagination-utils.test.ts +0 -620
  105. package/src/utils/pagination-utils.ts +0 -242
  106. package/src/utils/schema-utils.ts +0 -207
  107. package/src/utils/string-utils.test.ts +0 -45
  108. package/src/utils/string-utils.ts +0 -54
  109. package/src/utils/validation.test.ts +0 -51
  110. package/src/utils/validation.ts +0 -44
  111. package/tsconfig.build.json +0 -18
  112. package/tsconfig.json +0 -20
  113. package/tsconfig.tsbuildinfo +0 -1
  114. package/tsup.config.ts +0 -23
package/src/api/debug.ts DELETED
@@ -1,154 +0,0 @@
1
- /**
2
- * Debug Logging Utilities
3
- *
4
- * This module provides debug logging capabilities for API requests and responses,
5
- * including request timing and detailed logging of HTTP interactions.
6
- */
7
-
8
- import type { DebugLogger } from "./types";
9
-
10
- import type { InspectOptions } from "util";
11
-
12
- type UtilModule = {
13
- inspect: (object: unknown, options?: InspectOptions) => string;
14
- };
15
-
16
- // We don't want to import util since it might not work in a browser. So we'll lazily load it and cache the result so
17
- // we can use it synchronously after that. The types above are just to make our linter happy.
18
- let utilModule: UtilModule | null = null;
19
- let utilPromise: Promise<UtilModule | null> | null = null;
20
-
21
- export function createDebugLogger(enabled: boolean): DebugLogger {
22
- if (!enabled) {
23
- return () => {}; // No-op function when debug is disabled
24
- }
25
-
26
- // Try to load util module on first use.
27
- if (!utilPromise) {
28
- utilPromise = import("util")
29
- .then((util) => {
30
- utilModule = util;
31
- return util;
32
- })
33
- .catch(() => {
34
- utilModule = null;
35
- return null;
36
- });
37
- }
38
-
39
- return (message: string, data?: unknown) => {
40
- if (data === undefined || data === "") {
41
- console.log(`[Zapier SDK] ${message}`);
42
- return;
43
- }
44
-
45
- // Use cached module if available for sync logging. Yes, this means if the util module isn't yet loaded, we'll fall
46
- // back to a plain old console.log, which might mean we don't see proper formatting/nesting. But that's unlikely
47
- // since this logging happens during async fetching. Alternatively, we could switch to async logging, but this
48
- // seems fine since we have to have a fallback anyway.
49
- if (utilModule) {
50
- const formatted = utilModule.inspect(data, {
51
- colors: true,
52
- depth: null,
53
- breakLength: 80,
54
- });
55
- console.log(`[Zapier SDK] ${message}`, formatted);
56
- } else {
57
- // Fallback - browser will handle formatting automatically
58
- console.log(`[Zapier SDK] ${message}`, data);
59
- }
60
- };
61
- }
62
-
63
- export function censorHeaders(
64
- headers?: HeadersInit,
65
- ): Record<string, string> | undefined {
66
- if (!headers) return headers;
67
-
68
- const headersObj = new Headers(headers);
69
- const authKeys = ["authorization", "x-api-key"];
70
-
71
- for (const [key, value] of headersObj.entries()) {
72
- if (authKeys.some((authKey) => key.toLowerCase() === authKey)) {
73
- // Find space after prefix (like "Bearer ")
74
- const spaceIndex = value.indexOf(" ");
75
- if (spaceIndex > 0 && spaceIndex < value.length - 1) {
76
- const prefix = value.substring(0, spaceIndex + 1); // Include the space
77
- const token = value.substring(spaceIndex + 1);
78
-
79
- if (token.length > 12) {
80
- // Show first 4 and last 4 chars, truncate middle
81
- const start = token.substring(0, 4);
82
- const end = token.substring(token.length - 4);
83
- headersObj.set(key, `${prefix}${start}...${end}`);
84
- } else {
85
- // Short token, use first character + three dots
86
- const firstChar = token.charAt(0);
87
- headersObj.set(key, `${prefix}${firstChar}...`);
88
- }
89
- } else {
90
- // No prefix found, truncate the whole value
91
- if (value.length > 12) {
92
- const start = value.substring(0, 4);
93
- const end = value.substring(value.length - 4);
94
- headersObj.set(key, `${start}...${end}`);
95
- } else {
96
- // Short token, use first character + three dots
97
- const firstChar = value.charAt(0);
98
- headersObj.set(key, `${firstChar}...`);
99
- }
100
- }
101
- }
102
- }
103
-
104
- return Object.fromEntries(headersObj);
105
- }
106
-
107
- export function createDebugFetch(options: {
108
- originalFetch: typeof globalThis.fetch;
109
- debugLog: DebugLogger;
110
- }) {
111
- const { originalFetch, debugLog } = options;
112
- return async (input: RequestInfo | URL, options?: RequestInit) => {
113
- const startTime = Date.now();
114
-
115
- // Convert input to URL string for logging
116
- const url = typeof input === "string" ? input : input.toString();
117
- const method = options?.method || "GET";
118
-
119
- debugLog(`→ ${method} ${url}`, {
120
- headers: censorHeaders(options?.headers),
121
- body:
122
- options?.body && typeof options.body === "string"
123
- ? (() => {
124
- try {
125
- return JSON.parse(options.body);
126
- } catch {
127
- return options.body;
128
- }
129
- })()
130
- : options?.body,
131
- });
132
-
133
- try {
134
- const response = await originalFetch(input, options);
135
- const duration = Date.now() - startTime;
136
-
137
- debugLog(`← ${response.status} ${response.statusText} (${duration}ms)`, {
138
- url,
139
- method,
140
- status: response.status,
141
- });
142
-
143
- return response;
144
- } catch (error) {
145
- const duration = Date.now() - startTime;
146
- debugLog(`✖ Request failed (${duration}ms)`, {
147
- url,
148
- method,
149
- error: error instanceof Error ? error.message : error,
150
- });
151
- throw error;
152
- }
153
- };
154
- }
package/src/api/index.ts DELETED
@@ -1,90 +0,0 @@
1
- /**
2
- * Zapier API Client Module
3
- *
4
- * This module provides a centralized API layer for all HTTP interactions
5
- * with Zapier's various APIs. It handles authentication, error handling,
6
- * polling, and provides consistent patterns across all services.
7
- */
8
-
9
- // Re-export all types
10
- export type {
11
- // API Client Infrastructure Types
12
- ApiClient,
13
- ApiClientOptions,
14
- RequestOptions,
15
- PollOptions,
16
- DebugLogger,
17
- // API Response Model Types
18
- Action,
19
- Field,
20
- Choice,
21
- ActionExecutionResult,
22
- ActionField,
23
- ActionFieldChoice,
24
- NeedsRequest,
25
- NeedsResponse,
26
- Authentication,
27
- AuthenticationsResponse,
28
- Implementation,
29
- ImplementationsResponse,
30
- } from "./types";
31
-
32
- // Import for local use
33
- import type { ApiClient } from "./types";
34
-
35
- // Re-export authentication utilities
36
- export { isJwt, getAuthorizationHeader } from "./auth";
37
-
38
- // Re-export debug utilities
39
- export { createDebugLogger, createDebugFetch } from "./debug";
40
-
41
- // Re-export polling utilities
42
- export { pollUntilComplete } from "./polling";
43
-
44
- // Re-export the main client factory
45
- export { createZapierApi } from "./client";
46
-
47
- // Import for local use
48
- import { createZapierApi } from "./client";
49
-
50
- // Utility Functions
51
- export function generateRequestId(): string {
52
- return Math.random().toString(36).substring(2) + Date.now().toString(36);
53
- }
54
-
55
- /**
56
- * Utility function to get or create an API client for standalone functions
57
- *
58
- * @param config - Configuration that may include an existing API client
59
- * @returns ApiClient instance
60
- */
61
- export function getOrCreateApiClient(config: {
62
- baseUrl?: string;
63
- token?: string;
64
- getToken?: () => Promise<string | undefined>;
65
- api?: ApiClient;
66
- debug?: boolean;
67
- fetch?: typeof globalThis.fetch;
68
- }): ApiClient {
69
- const {
70
- baseUrl = "https://zapier.com",
71
- token,
72
- getToken,
73
- api: providedApi,
74
- debug = false,
75
- fetch: customFetch,
76
- } = config;
77
-
78
- // Use provided API client or create a new one
79
- if (providedApi) {
80
- return providedApi;
81
- }
82
-
83
- return createZapierApi({
84
- baseUrl,
85
- token,
86
- getToken,
87
- debug,
88
- fetch: customFetch,
89
- });
90
- }
@@ -1,405 +0,0 @@
1
- import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
2
- import { pollUntilComplete } from "./polling";
3
- import {
4
- ZapierTimeoutError,
5
- ZapierApiError,
6
- ZapierValidationError,
7
- } from "../types/errors";
8
-
9
- // Mock the timers/promises module
10
- vi.mock("timers/promises", () => ({
11
- setTimeout: vi.fn(() => Promise.resolve()),
12
- }));
13
-
14
- describe("pollUntilComplete", () => {
15
- beforeEach(() => {
16
- vi.clearAllMocks();
17
- });
18
-
19
- afterEach(() => {
20
- vi.clearAllMocks();
21
- });
22
-
23
- describe("successful polling", () => {
24
- it("should return immediately on first successful response", async () => {
25
- const mockResponse = {
26
- ok: true,
27
- status: 200,
28
- json: vi.fn().mockResolvedValue({ data: "success" }),
29
- } as unknown as Response;
30
-
31
- const fetchPoll = vi.fn().mockResolvedValue(mockResponse);
32
-
33
- const result = await pollUntilComplete({ fetchPoll });
34
- expect(result).toEqual({ data: "success" });
35
- expect(fetchPoll).toHaveBeenCalledTimes(1);
36
- });
37
-
38
- it("should poll until success status is received", async () => {
39
- const pendingResponse = {
40
- ok: true,
41
- status: 202,
42
- } as Response;
43
-
44
- const successResponse = {
45
- ok: true,
46
- status: 200,
47
- json: vi.fn().mockResolvedValue({ data: "complete" }),
48
- } as unknown as Response;
49
-
50
- const fetchPoll = vi
51
- .fn()
52
- .mockResolvedValueOnce(pendingResponse)
53
- .mockResolvedValueOnce(pendingResponse)
54
- .mockResolvedValueOnce(successResponse);
55
-
56
- const result = await pollUntilComplete({ fetchPoll });
57
- expect(result).toEqual({ data: "complete" });
58
- expect(fetchPoll).toHaveBeenCalledTimes(3);
59
- });
60
-
61
- it("should use custom success and pending status codes", async () => {
62
- const pendingResponse = {
63
- ok: true,
64
- status: 301,
65
- } as Response;
66
-
67
- const successResponse = {
68
- ok: true,
69
- status: 204,
70
- json: vi.fn().mockResolvedValue({ done: true }),
71
- } as unknown as Response;
72
-
73
- const fetchPoll = vi
74
- .fn()
75
- .mockResolvedValueOnce(pendingResponse)
76
- .mockResolvedValueOnce(successResponse);
77
-
78
- const result = await pollUntilComplete({
79
- fetchPoll,
80
- successStatus: 204,
81
- pendingStatus: 301,
82
- });
83
- expect(result).toEqual({ done: true });
84
- expect(fetchPoll).toHaveBeenCalledTimes(2);
85
- });
86
-
87
- it("should apply result extractor function", async () => {
88
- const mockResponse = {
89
- ok: true,
90
- status: 200,
91
- json: vi.fn().mockResolvedValue({
92
- nested: { data: { value: "extracted" } },
93
- }),
94
- } as unknown as Response;
95
-
96
- const fetchPoll = vi.fn().mockResolvedValue(mockResponse);
97
- const resultExtractor = (response: any) => response.nested.data.value;
98
-
99
- const result = await pollUntilComplete({
100
- fetchPoll,
101
- resultExtractor,
102
- });
103
- expect(result).toBe("extracted");
104
- });
105
- });
106
-
107
- describe("initial delay", () => {
108
- it("should wait for initial delay before first poll", async () => {
109
- const mockResponse = {
110
- ok: true,
111
- status: 200,
112
- json: vi.fn().mockResolvedValue({ data: "success" }),
113
- } as unknown as Response;
114
-
115
- const fetchPoll = vi.fn().mockResolvedValue(mockResponse);
116
- const { setTimeout: mockSetTimeout } = await import("timers/promises");
117
-
118
- const result = await pollUntilComplete({
119
- fetchPoll,
120
- initialDelay: 500,
121
- });
122
-
123
- expect(result).toEqual({ data: "success" });
124
- expect(fetchPoll).toHaveBeenCalledTimes(1);
125
- // Verify setTimeout was called with the initial delay
126
- expect(mockSetTimeout).toHaveBeenCalledWith(500);
127
- });
128
- });
129
-
130
- describe("error handling", () => {
131
- it("should retry on transient network errors", async () => {
132
- const successResponse = {
133
- ok: true,
134
- status: 200,
135
- json: vi.fn().mockResolvedValue({ data: "recovered" }),
136
- } as unknown as Response;
137
-
138
- const fetchPoll = vi
139
- .fn()
140
- .mockRejectedValueOnce(new Error("Network error"))
141
- .mockRejectedValueOnce(new Error("Network error"))
142
- .mockResolvedValueOnce(successResponse);
143
-
144
- const result = await pollUntilComplete({ fetchPoll });
145
- expect(result).toEqual({ data: "recovered" });
146
- expect(fetchPoll).toHaveBeenCalledTimes(3);
147
- });
148
-
149
- it("should fail after MAX_CONSECUTIVE_ERRORS network failures", async () => {
150
- const fetchPoll = vi
151
- .fn()
152
- .mockRejectedValue(new Error("Persistent network error"));
153
-
154
- await expect(pollUntilComplete({ fetchPoll })).rejects.toThrow(
155
- ZapierApiError,
156
- );
157
- expect(fetchPoll).toHaveBeenCalledTimes(3);
158
- });
159
-
160
- it("should retry on HTTP error responses", async () => {
161
- const errorResponse = {
162
- ok: false,
163
- status: 500,
164
- statusText: "Internal Server Error",
165
- } as Response;
166
-
167
- const successResponse = {
168
- ok: true,
169
- status: 200,
170
- json: vi.fn().mockResolvedValue({ data: "recovered" }),
171
- } as unknown as Response;
172
-
173
- const fetchPoll = vi
174
- .fn()
175
- .mockResolvedValueOnce(errorResponse)
176
- .mockResolvedValueOnce(errorResponse)
177
- .mockResolvedValueOnce(successResponse);
178
-
179
- const result = await pollUntilComplete({ fetchPoll });
180
- expect(result).toEqual({ data: "recovered" });
181
- expect(fetchPoll).toHaveBeenCalledTimes(3);
182
- });
183
-
184
- it("should fail after MAX_CONSECUTIVE_ERRORS HTTP failures", async () => {
185
- const errorResponse = {
186
- ok: false,
187
- status: 503,
188
- statusText: "Service Unavailable",
189
- } as Response;
190
-
191
- const fetchPoll = vi.fn().mockResolvedValue(errorResponse);
192
-
193
- await expect(pollUntilComplete({ fetchPoll })).rejects.toThrow(
194
- ZapierApiError,
195
- );
196
- expect(fetchPoll).toHaveBeenCalledTimes(3);
197
- });
198
-
199
- it("should throw error for unexpected response status", async () => {
200
- const unexpectedResponse = {
201
- ok: true,
202
- status: 301, // Not success (200) or pending (202)
203
- } as Response;
204
-
205
- const fetchPoll = vi.fn().mockResolvedValue(unexpectedResponse);
206
-
207
- await expect(pollUntilComplete({ fetchPoll })).rejects.toThrow(
208
- "Unexpected response status",
209
- );
210
- // Will retry 3 times before failing
211
- expect(fetchPoll).toHaveBeenCalledTimes(3);
212
- });
213
-
214
- it("should throw error if successful response is not valid JSON", async () => {
215
- const mockResponse = {
216
- ok: true,
217
- status: 200,
218
- json: vi.fn().mockRejectedValue(new Error("Invalid JSON")),
219
- } as unknown as Response;
220
-
221
- const fetchPoll = vi.fn().mockResolvedValue(mockResponse);
222
-
223
- await expect(pollUntilComplete({ fetchPoll })).rejects.toThrow(
224
- "Failed to poll after 3 consecutive errors",
225
- );
226
- // Will retry 3 times before failing
227
- expect(fetchPoll).toHaveBeenCalledTimes(3);
228
- });
229
- });
230
-
231
- describe("timeout behavior", () => {
232
- it("should timeout after specified duration", async () => {
233
- // Mock Date.now to simulate time passing
234
- const originalDateNow = Date.now;
235
- let currentTime = 1000;
236
- Date.now = vi.fn(() => currentTime);
237
-
238
- const pendingResponse = {
239
- ok: true,
240
- status: 202,
241
- } as Response;
242
-
243
- const fetchPoll = vi.fn().mockImplementation(() => {
244
- // Simulate time passing with each poll
245
- currentTime += 500;
246
- return Promise.resolve(pendingResponse);
247
- });
248
-
249
- try {
250
- await expect(
251
- pollUntilComplete({
252
- fetchPoll,
253
- timeoutMs: 1000,
254
- initialDelay: 0,
255
- }),
256
- ).rejects.toThrow(ZapierTimeoutError);
257
- } finally {
258
- Date.now = originalDateNow;
259
- }
260
- });
261
-
262
- it("should use default timeout when not specified", async () => {
263
- // Mock Date.now to simulate time passing beyond default timeout
264
- const originalDateNow = Date.now;
265
- let currentTime = 1000;
266
- Date.now = vi.fn(() => currentTime);
267
-
268
- const pendingResponse = {
269
- ok: true,
270
- status: 202,
271
- } as Response;
272
-
273
- const fetchPoll = vi.fn().mockImplementation(() => {
274
- // Simulate time passing beyond default timeout (180 seconds + buffer)
275
- currentTime += 50000;
276
- return Promise.resolve(pendingResponse);
277
- });
278
-
279
- try {
280
- await expect(
281
- pollUntilComplete({
282
- fetchPoll,
283
- initialDelay: 0,
284
- }),
285
- ).rejects.toThrow(ZapierTimeoutError);
286
- } finally {
287
- Date.now = originalDateNow;
288
- }
289
- });
290
- });
291
-
292
- describe("input validation", () => {
293
- it("should throw error for invalid timeout", async () => {
294
- const fetchPoll = vi.fn();
295
-
296
- await expect(
297
- pollUntilComplete({
298
- fetchPoll,
299
- timeoutMs: 0,
300
- }),
301
- ).rejects.toThrow(ZapierValidationError);
302
-
303
- await expect(
304
- pollUntilComplete({
305
- fetchPoll,
306
- timeoutMs: -1000,
307
- }),
308
- ).rejects.toThrow(/Timeout must be greater than 0/);
309
- });
310
-
311
- it("should throw error for negative initial delay", async () => {
312
- const fetchPoll = vi.fn();
313
-
314
- await expect(
315
- pollUntilComplete({
316
- fetchPoll,
317
- initialDelay: -100,
318
- }),
319
- ).rejects.toThrow(ZapierValidationError);
320
-
321
- await expect(
322
- pollUntilComplete({
323
- fetchPoll,
324
- initialDelay: -1,
325
- }),
326
- ).rejects.toThrow(/Initial delay must be non-negative/);
327
- });
328
- });
329
-
330
- describe("edge cases", () => {
331
- it("should handle rapid status changes", async () => {
332
- const responses = [
333
- { ok: true, status: 202 } as Response,
334
- { ok: false, status: 500 } as Response,
335
- { ok: true, status: 202 } as Response,
336
- {
337
- ok: true,
338
- status: 200,
339
- json: vi.fn().mockResolvedValue({ data: "success" }),
340
- } as unknown as Response,
341
- ];
342
-
343
- let index = 0;
344
- const fetchPoll = vi.fn().mockImplementation(() => {
345
- const response = responses[index];
346
- index++;
347
- return Promise.resolve(response);
348
- });
349
-
350
- const result = await pollUntilComplete({ fetchPoll });
351
- expect(result).toEqual({ data: "success" });
352
- expect(fetchPoll).toHaveBeenCalledTimes(4);
353
- });
354
-
355
- it("should handle immediate success with no initial delay", async () => {
356
- const mockResponse = {
357
- ok: true,
358
- status: 200,
359
- json: vi.fn().mockResolvedValue({ instant: true }),
360
- } as unknown as Response;
361
-
362
- const fetchPoll = vi.fn().mockResolvedValue(mockResponse);
363
-
364
- const result = await pollUntilComplete({
365
- fetchPoll,
366
- initialDelay: 0,
367
- });
368
-
369
- expect(result).toEqual({ instant: true });
370
- expect(fetchPoll).toHaveBeenCalledTimes(1);
371
- });
372
-
373
- it("should handle very short timeout", async () => {
374
- // Mock Date.now to simulate time passing
375
- const originalDateNow = Date.now;
376
- let currentTime = 1000;
377
- Date.now = vi.fn(() => currentTime);
378
-
379
- const pendingResponse = {
380
- ok: true,
381
- status: 202,
382
- } as Response;
383
-
384
- const fetchPoll = vi.fn().mockImplementation(() => {
385
- // Simulate time passing beyond the short timeout
386
- currentTime += 200;
387
- return Promise.resolve(pendingResponse);
388
- });
389
-
390
- try {
391
- await expect(
392
- pollUntilComplete({
393
- fetchPoll,
394
- timeoutMs: 100,
395
- initialDelay: 0,
396
- }),
397
- ).rejects.toThrow(ZapierTimeoutError);
398
- // Should have made at least 1 attempt before timing out
399
- expect(fetchPoll.mock.calls.length).toBeGreaterThanOrEqual(1);
400
- } finally {
401
- Date.now = originalDateNow;
402
- }
403
- });
404
- });
405
- });