@zapier/zapier-sdk 0.0.2 → 0.0.3
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/dist/actions-sdk.d.ts +0 -4
- package/dist/actions-sdk.js +43 -1029
- package/dist/api.d.ts +62 -0
- package/dist/api.js +227 -0
- package/dist/functions/bundleCode.d.ts +18 -0
- package/dist/functions/bundleCode.js +91 -0
- package/dist/functions/generateTypes.d.ts +16 -0
- package/dist/functions/generateTypes.js +271 -0
- package/dist/functions/getAction.d.ts +16 -0
- package/dist/functions/getAction.js +25 -0
- package/dist/functions/getApp.d.ts +14 -0
- package/dist/functions/getApp.js +41 -0
- package/dist/functions/listActions.d.ts +15 -0
- package/dist/functions/listActions.js +127 -0
- package/dist/functions/listApps.d.ts +16 -0
- package/dist/functions/listApps.js +50 -0
- package/dist/functions/listAuths.d.ts +18 -0
- package/dist/functions/listAuths.js +118 -0
- package/dist/functions/listFields.d.ts +18 -0
- package/dist/functions/listFields.js +67 -0
- package/dist/functions/runAction.d.ts +18 -0
- package/dist/functions/runAction.js +156 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.js +20 -1
- package/dist/output-schemas.d.ts +4 -4
- package/dist/schemas.d.ts +24 -24
- package/dist/types.d.ts +12 -0
- package/package.json +1 -1
- package/src/actions-sdk.ts +47 -1376
- package/src/api.ts +361 -0
- package/src/functions/bundleCode.ts +85 -0
- package/src/functions/generateTypes.ts +309 -0
- package/src/functions/getAction.ts +34 -0
- package/src/functions/getApp.ts +47 -0
- package/src/functions/listActions.ts +151 -0
- package/src/functions/listApps.ts +65 -0
- package/src/functions/listAuths.ts +161 -0
- package/src/functions/listFields.ts +95 -0
- package/src/functions/runAction.ts +256 -0
- package/src/index.ts +11 -0
- package/src/types.ts +13 -0
package/src/api.ts
ADDED
|
@@ -0,0 +1,361 @@
|
|
|
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
|
+
// ============================================================================
|
|
10
|
+
// Types
|
|
11
|
+
// ============================================================================
|
|
12
|
+
|
|
13
|
+
export interface ApiClientOptions {
|
|
14
|
+
baseUrl: string;
|
|
15
|
+
token?: string;
|
|
16
|
+
debug?: boolean;
|
|
17
|
+
fetch?: typeof globalThis.fetch;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface ApiClient {
|
|
21
|
+
get: (path: string, options?: RequestOptions) => Promise<any>;
|
|
22
|
+
post: (path: string, data?: any, options?: RequestOptions) => Promise<any>;
|
|
23
|
+
put: (path: string, data?: any, options?: RequestOptions) => Promise<any>;
|
|
24
|
+
delete: (path: string, options?: RequestOptions) => Promise<any>;
|
|
25
|
+
poll: (path: string, options?: PollOptions) => Promise<any>;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface RequestOptions {
|
|
29
|
+
headers?: Record<string, string>;
|
|
30
|
+
searchParams?: Record<string, string>;
|
|
31
|
+
authRequired?: boolean;
|
|
32
|
+
customErrorHandler?: (response: Response) => Error | undefined;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export interface PollOptions extends RequestOptions {
|
|
36
|
+
maxAttempts?: number;
|
|
37
|
+
initialDelay?: number;
|
|
38
|
+
maxDelay?: number;
|
|
39
|
+
successStatus?: number;
|
|
40
|
+
pendingStatus?: number;
|
|
41
|
+
resultExtractor?: (response: any) => any;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
interface DebugLogger {
|
|
45
|
+
(message: string, data?: any): void;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// ============================================================================
|
|
49
|
+
// Authentication Utilities
|
|
50
|
+
// ============================================================================
|
|
51
|
+
|
|
52
|
+
export function isJwt(token: string): boolean {
|
|
53
|
+
// JWT tokens have exactly 3 parts separated by dots
|
|
54
|
+
const parts = token.split(".");
|
|
55
|
+
if (parts.length !== 3) {
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Each part should be base64url encoded (no padding)
|
|
60
|
+
// Basic validation - each part should be non-empty and contain valid base64url characters
|
|
61
|
+
const base64UrlPattern = /^[A-Za-z0-9_-]+$/;
|
|
62
|
+
return parts.every((part) => part.length > 0 && base64UrlPattern.test(part));
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export function getAuthorizationHeader(token: string): string {
|
|
66
|
+
// Check if token is a JWT (has 3 parts separated by dots)
|
|
67
|
+
if (isJwt(token)) {
|
|
68
|
+
return `JWT ${token}`;
|
|
69
|
+
}
|
|
70
|
+
// Default to Bearer for other token types
|
|
71
|
+
return `Bearer ${token}`;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// ============================================================================
|
|
75
|
+
// Debug Logging
|
|
76
|
+
// ============================================================================
|
|
77
|
+
|
|
78
|
+
function createDebugLogger(enabled: boolean): DebugLogger {
|
|
79
|
+
if (!enabled) {
|
|
80
|
+
return () => {}; // No-op function when debug is disabled
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return (message: string, data?: any) => {
|
|
84
|
+
console.log(`[Zapier SDK] ${message}`, data || "");
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function createDebugFetch(options: {
|
|
89
|
+
originalFetch: typeof globalThis.fetch;
|
|
90
|
+
debugLog: DebugLogger;
|
|
91
|
+
}) {
|
|
92
|
+
const { originalFetch, debugLog } = options;
|
|
93
|
+
return async (input: RequestInfo | URL, options?: RequestInit) => {
|
|
94
|
+
const startTime = Date.now();
|
|
95
|
+
|
|
96
|
+
// Convert input to URL string for logging
|
|
97
|
+
const url = typeof input === "string" ? input : input.toString();
|
|
98
|
+
const method = options?.method || "GET";
|
|
99
|
+
|
|
100
|
+
debugLog(`→ ${method} ${url}`, {
|
|
101
|
+
headers: options?.headers,
|
|
102
|
+
body: options?.body ? JSON.parse(options.body as string) : undefined,
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
try {
|
|
106
|
+
const response = await originalFetch(input, options);
|
|
107
|
+
const duration = Date.now() - startTime;
|
|
108
|
+
|
|
109
|
+
debugLog(`← ${response.status} ${response.statusText} (${duration}ms)`, {
|
|
110
|
+
url,
|
|
111
|
+
method,
|
|
112
|
+
status: response.status,
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
return response;
|
|
116
|
+
} catch (error) {
|
|
117
|
+
const duration = Date.now() - startTime;
|
|
118
|
+
debugLog(`✖ Request failed (${duration}ms)`, {
|
|
119
|
+
url,
|
|
120
|
+
method,
|
|
121
|
+
error: error instanceof Error ? error.message : error,
|
|
122
|
+
});
|
|
123
|
+
throw error;
|
|
124
|
+
}
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// ============================================================================
|
|
129
|
+
// Polling Utilities
|
|
130
|
+
// ============================================================================
|
|
131
|
+
|
|
132
|
+
export async function pollUntilComplete(options: {
|
|
133
|
+
fetch: typeof globalThis.fetch;
|
|
134
|
+
url: string;
|
|
135
|
+
headers?: Record<string, string>;
|
|
136
|
+
maxAttempts?: number;
|
|
137
|
+
initialDelay?: number;
|
|
138
|
+
maxDelay?: number;
|
|
139
|
+
successStatus?: number;
|
|
140
|
+
pendingStatus?: number;
|
|
141
|
+
resultExtractor?: (response: any) => any;
|
|
142
|
+
}): Promise<any> {
|
|
143
|
+
const {
|
|
144
|
+
fetch,
|
|
145
|
+
url,
|
|
146
|
+
headers = {},
|
|
147
|
+
maxAttempts = 30,
|
|
148
|
+
initialDelay = 50,
|
|
149
|
+
maxDelay = 1000,
|
|
150
|
+
successStatus = 200,
|
|
151
|
+
pendingStatus = 202,
|
|
152
|
+
resultExtractor = (response) => response,
|
|
153
|
+
} = options;
|
|
154
|
+
|
|
155
|
+
let delay = initialDelay;
|
|
156
|
+
|
|
157
|
+
for (let attempt = 0; attempt < maxAttempts; attempt++) {
|
|
158
|
+
const response = await fetch(url, { headers });
|
|
159
|
+
|
|
160
|
+
if (response.status === successStatus) {
|
|
161
|
+
// Success - extract and return results
|
|
162
|
+
const result = await response.json();
|
|
163
|
+
return resultExtractor(result);
|
|
164
|
+
} else if (response.status === pendingStatus) {
|
|
165
|
+
// Still processing - wait and retry
|
|
166
|
+
if (attempt < maxAttempts - 1) {
|
|
167
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
168
|
+
delay = Math.min(delay * 2, maxDelay); // Exponential backoff
|
|
169
|
+
continue;
|
|
170
|
+
}
|
|
171
|
+
} else {
|
|
172
|
+
// Error occurred
|
|
173
|
+
throw new Error(
|
|
174
|
+
`Request failed: GET ${url} - ${response.status} ${response.statusText}`,
|
|
175
|
+
);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
throw new Error(`Operation timed out after ${maxAttempts} attempts`);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// ============================================================================
|
|
183
|
+
// API Client Factory
|
|
184
|
+
// ============================================================================
|
|
185
|
+
|
|
186
|
+
export function createZapierApi(options: ApiClientOptions): ApiClient {
|
|
187
|
+
const {
|
|
188
|
+
baseUrl,
|
|
189
|
+
token,
|
|
190
|
+
debug = false,
|
|
191
|
+
fetch: originalFetch = globalThis.fetch,
|
|
192
|
+
} = options;
|
|
193
|
+
|
|
194
|
+
const debugLog = createDebugLogger(debug);
|
|
195
|
+
const fetch = createDebugFetch({ originalFetch, debugLog });
|
|
196
|
+
|
|
197
|
+
// Helper to build full URLs
|
|
198
|
+
function buildUrl(
|
|
199
|
+
path: string,
|
|
200
|
+
searchParams?: Record<string, string>,
|
|
201
|
+
): string {
|
|
202
|
+
const url = new URL(path, baseUrl);
|
|
203
|
+
if (searchParams) {
|
|
204
|
+
Object.entries(searchParams).forEach(([key, value]) => {
|
|
205
|
+
url.searchParams.set(key, value);
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
return url.toString();
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// Helper to build headers
|
|
212
|
+
function buildHeaders(options: RequestOptions = {}): Record<string, string> {
|
|
213
|
+
const headers: Record<string, string> = {
|
|
214
|
+
...options.headers,
|
|
215
|
+
};
|
|
216
|
+
|
|
217
|
+
// Add auth header if token provided and not explicitly disabled
|
|
218
|
+
if (token && options.authRequired !== false) {
|
|
219
|
+
headers.Authorization = getAuthorizationHeader(token);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
return headers;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// Helper to handle responses
|
|
226
|
+
async function handleResponse(
|
|
227
|
+
response: Response,
|
|
228
|
+
customErrorHandler?: (response: Response) => Error | undefined,
|
|
229
|
+
): Promise<any> {
|
|
230
|
+
if (!response.ok) {
|
|
231
|
+
// Check for custom error handling first
|
|
232
|
+
if (customErrorHandler) {
|
|
233
|
+
const customError = customErrorHandler(response);
|
|
234
|
+
if (customError) {
|
|
235
|
+
throw customError;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// Try to parse JSON, fall back to text if that fails
|
|
243
|
+
try {
|
|
244
|
+
return await response.json();
|
|
245
|
+
} catch {
|
|
246
|
+
return await response.text();
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// Helper to perform HTTP requests with JSON handling
|
|
251
|
+
async function fetchJson(
|
|
252
|
+
method: string,
|
|
253
|
+
path: string,
|
|
254
|
+
data?: any,
|
|
255
|
+
options: RequestOptions = {},
|
|
256
|
+
): Promise<any> {
|
|
257
|
+
const url = buildUrl(path, options.searchParams);
|
|
258
|
+
const headers = buildHeaders(options);
|
|
259
|
+
|
|
260
|
+
// Add Content-Type for JSON requests with body data
|
|
261
|
+
if (data && typeof data === "object") {
|
|
262
|
+
headers["Content-Type"] = "application/json";
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
const response = await fetch(url, {
|
|
266
|
+
method,
|
|
267
|
+
headers,
|
|
268
|
+
body: data ? JSON.stringify(data) : undefined,
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
return handleResponse(response, options.customErrorHandler);
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
return {
|
|
275
|
+
async get(path: string, options: RequestOptions = {}): Promise<any> {
|
|
276
|
+
return fetchJson("GET", path, undefined, options);
|
|
277
|
+
},
|
|
278
|
+
|
|
279
|
+
async post(
|
|
280
|
+
path: string,
|
|
281
|
+
data?: any,
|
|
282
|
+
options: RequestOptions = {},
|
|
283
|
+
): Promise<any> {
|
|
284
|
+
return fetchJson("POST", path, data, options);
|
|
285
|
+
},
|
|
286
|
+
|
|
287
|
+
async put(
|
|
288
|
+
path: string,
|
|
289
|
+
data?: any,
|
|
290
|
+
options: RequestOptions = {},
|
|
291
|
+
): Promise<any> {
|
|
292
|
+
return fetchJson("PUT", path, data, options);
|
|
293
|
+
},
|
|
294
|
+
|
|
295
|
+
async delete(path: string, options: RequestOptions = {}): Promise<any> {
|
|
296
|
+
return fetchJson("DELETE", path, undefined, options);
|
|
297
|
+
},
|
|
298
|
+
|
|
299
|
+
async poll(path: string, options: PollOptions = {}): Promise<any> {
|
|
300
|
+
const url = buildUrl(path, options.searchParams);
|
|
301
|
+
const headers = buildHeaders(options);
|
|
302
|
+
|
|
303
|
+
return pollUntilComplete({
|
|
304
|
+
fetch,
|
|
305
|
+
url,
|
|
306
|
+
headers,
|
|
307
|
+
maxAttempts: options.maxAttempts,
|
|
308
|
+
initialDelay: options.initialDelay,
|
|
309
|
+
maxDelay: options.maxDelay,
|
|
310
|
+
successStatus: options.successStatus,
|
|
311
|
+
pendingStatus: options.pendingStatus,
|
|
312
|
+
resultExtractor: options.resultExtractor,
|
|
313
|
+
});
|
|
314
|
+
},
|
|
315
|
+
};
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
// ============================================================================
|
|
319
|
+
// Utility Functions
|
|
320
|
+
// ============================================================================
|
|
321
|
+
|
|
322
|
+
export function generateRequestId(): string {
|
|
323
|
+
return Math.random().toString(36).substring(2) + Date.now().toString(36);
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
// ============================================================================
|
|
327
|
+
// Utility Functions for Standalone Functions
|
|
328
|
+
// ============================================================================
|
|
329
|
+
|
|
330
|
+
/**
|
|
331
|
+
* Utility function to get or create an API client for standalone functions
|
|
332
|
+
*
|
|
333
|
+
* @param config - Configuration that may include an existing API client
|
|
334
|
+
* @returns ApiClient instance
|
|
335
|
+
*/
|
|
336
|
+
export function getOrCreateApiClient(config: {
|
|
337
|
+
baseUrl?: string;
|
|
338
|
+
token?: string;
|
|
339
|
+
api?: ApiClient;
|
|
340
|
+
debug?: boolean;
|
|
341
|
+
fetch?: typeof globalThis.fetch;
|
|
342
|
+
}): ApiClient {
|
|
343
|
+
const {
|
|
344
|
+
baseUrl = "https://zapier.com",
|
|
345
|
+
token = process.env.ZAPIER_TOKEN,
|
|
346
|
+
api: providedApi,
|
|
347
|
+
debug = false,
|
|
348
|
+
fetch: customFetch,
|
|
349
|
+
} = config;
|
|
350
|
+
|
|
351
|
+
// Use provided API client or create a new one
|
|
352
|
+
return (
|
|
353
|
+
providedApi ||
|
|
354
|
+
createZapierApi({
|
|
355
|
+
baseUrl,
|
|
356
|
+
token,
|
|
357
|
+
debug,
|
|
358
|
+
fetch: customFetch,
|
|
359
|
+
})
|
|
360
|
+
);
|
|
361
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
export interface BundleCodeOptions {
|
|
2
|
+
input: string;
|
|
3
|
+
output?: string;
|
|
4
|
+
target?: string;
|
|
5
|
+
cjs?: boolean;
|
|
6
|
+
minify?: boolean;
|
|
7
|
+
string?: boolean;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Bundle TypeScript code into executable JavaScript
|
|
12
|
+
*
|
|
13
|
+
* This function can be used standalone without instantiating a full SDK,
|
|
14
|
+
* which enables better tree-shaking in applications that only need this functionality.
|
|
15
|
+
*
|
|
16
|
+
* @param options - Bundling configuration options
|
|
17
|
+
* @returns Promise<string> - Bundled JavaScript code
|
|
18
|
+
*/
|
|
19
|
+
export async function bundleCode(options: BundleCodeOptions): Promise<string> {
|
|
20
|
+
const {
|
|
21
|
+
input,
|
|
22
|
+
output,
|
|
23
|
+
target = "es2020",
|
|
24
|
+
cjs = false,
|
|
25
|
+
minify = false,
|
|
26
|
+
string: returnString = false,
|
|
27
|
+
} = options;
|
|
28
|
+
|
|
29
|
+
// Dynamically import esbuild
|
|
30
|
+
const { buildSync } = await import("esbuild");
|
|
31
|
+
const fs = await import("fs");
|
|
32
|
+
const path = await import("path");
|
|
33
|
+
|
|
34
|
+
// Resolve input path
|
|
35
|
+
const resolvedInput = path.resolve(process.cwd(), input);
|
|
36
|
+
|
|
37
|
+
try {
|
|
38
|
+
// Bundle with esbuild
|
|
39
|
+
const result = buildSync({
|
|
40
|
+
entryPoints: [resolvedInput],
|
|
41
|
+
bundle: true,
|
|
42
|
+
platform: "node",
|
|
43
|
+
target: target,
|
|
44
|
+
format: cjs ? "cjs" : "esm",
|
|
45
|
+
minify: minify,
|
|
46
|
+
write: false,
|
|
47
|
+
external: [], // Bundle everything
|
|
48
|
+
banner: {
|
|
49
|
+
js: "#!/usr/bin/env node",
|
|
50
|
+
},
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
if (result.errors.length > 0) {
|
|
54
|
+
throw new Error(
|
|
55
|
+
`Bundle failed: ${result.errors.map((e) => e.text).join(", ")}`,
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const bundledCode = result.outputFiles?.[0]?.text;
|
|
60
|
+
if (!bundledCode) {
|
|
61
|
+
throw new Error("No output generated");
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
let finalOutput = bundledCode;
|
|
65
|
+
|
|
66
|
+
if (returnString) {
|
|
67
|
+
// Output as quoted string for node -e using JSON.stringify
|
|
68
|
+
finalOutput = JSON.stringify(bundledCode);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Write to file if output path specified
|
|
72
|
+
if (output) {
|
|
73
|
+
fs.mkdirSync(path.dirname(output), { recursive: true });
|
|
74
|
+
fs.writeFileSync(output, finalOutput, "utf8");
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return finalOutput;
|
|
78
|
+
} catch (error) {
|
|
79
|
+
throw new Error(
|
|
80
|
+
`Bundle failed: ${
|
|
81
|
+
error instanceof Error ? error.message : "Unknown error"
|
|
82
|
+
}`,
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
}
|