@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.
Files changed (41) hide show
  1. package/dist/actions-sdk.d.ts +0 -4
  2. package/dist/actions-sdk.js +43 -1029
  3. package/dist/api.d.ts +62 -0
  4. package/dist/api.js +227 -0
  5. package/dist/functions/bundleCode.d.ts +18 -0
  6. package/dist/functions/bundleCode.js +91 -0
  7. package/dist/functions/generateTypes.d.ts +16 -0
  8. package/dist/functions/generateTypes.js +271 -0
  9. package/dist/functions/getAction.d.ts +16 -0
  10. package/dist/functions/getAction.js +25 -0
  11. package/dist/functions/getApp.d.ts +14 -0
  12. package/dist/functions/getApp.js +41 -0
  13. package/dist/functions/listActions.d.ts +15 -0
  14. package/dist/functions/listActions.js +127 -0
  15. package/dist/functions/listApps.d.ts +16 -0
  16. package/dist/functions/listApps.js +50 -0
  17. package/dist/functions/listAuths.d.ts +18 -0
  18. package/dist/functions/listAuths.js +118 -0
  19. package/dist/functions/listFields.d.ts +18 -0
  20. package/dist/functions/listFields.js +67 -0
  21. package/dist/functions/runAction.d.ts +18 -0
  22. package/dist/functions/runAction.js +156 -0
  23. package/dist/index.d.ts +9 -0
  24. package/dist/index.js +20 -1
  25. package/dist/output-schemas.d.ts +4 -4
  26. package/dist/schemas.d.ts +24 -24
  27. package/dist/types.d.ts +12 -0
  28. package/package.json +1 -1
  29. package/src/actions-sdk.ts +47 -1376
  30. package/src/api.ts +361 -0
  31. package/src/functions/bundleCode.ts +85 -0
  32. package/src/functions/generateTypes.ts +309 -0
  33. package/src/functions/getAction.ts +34 -0
  34. package/src/functions/getApp.ts +47 -0
  35. package/src/functions/listActions.ts +151 -0
  36. package/src/functions/listApps.ts +65 -0
  37. package/src/functions/listAuths.ts +161 -0
  38. package/src/functions/listFields.ts +95 -0
  39. package/src/functions/runAction.ts +256 -0
  40. package/src/index.ts +11 -0
  41. package/src/types.ts +13 -0
@@ -10,35 +10,21 @@ var __createBinding = (this && this.__createBinding) || (Object.create ? (functi
10
10
  if (k2 === undefined) k2 = k;
11
11
  o[k2] = m[k];
12
12
  }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
13
  var __exportStar = (this && this.__exportStar) || function(m, exports) {
19
14
  for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
20
15
  };
21
- var __importStar = (this && this.__importStar) || (function () {
22
- var ownKeys = function(o) {
23
- ownKeys = Object.getOwnPropertyNames || function (o) {
24
- var ar = [];
25
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
26
- return ar;
27
- };
28
- return ownKeys(o);
29
- };
30
- return function (mod) {
31
- if (mod && mod.__esModule) return mod;
32
- var result = {};
33
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
34
- __setModuleDefault(result, mod);
35
- return result;
36
- };
37
- })();
38
16
  Object.defineProperty(exports, "__esModule", { value: true });
39
17
  exports.createActionsSdk = createActionsSdk;
40
- const types_1 = require("./types");
41
- const schemas_1 = require("./schemas");
18
+ const api_1 = require("./api");
19
+ const listAuths_1 = require("./functions/listAuths");
20
+ const listApps_1 = require("./functions/listApps");
21
+ const getApp_1 = require("./functions/getApp");
22
+ const listActions_1 = require("./functions/listActions");
23
+ const getAction_1 = require("./functions/getAction");
24
+ const runAction_1 = require("./functions/runAction");
25
+ const listFields_1 = require("./functions/listFields");
26
+ const generateTypes_1 = require("./functions/generateTypes");
27
+ const bundleCode_1 = require("./functions/bundleCode");
42
28
  __exportStar(require("./schemas"), exports);
43
29
  __exportStar(require("./output-schemas"), exports);
44
30
  function createActionsSdk(options = {}) {
@@ -56,58 +42,45 @@ function createActionsSdk(options = {}) {
56
42
  const { fetch: customFetch = globalThis.fetch, baseUrl = "https://zapier.com", token, authentications = {}, debug = false, } = options;
57
43
  // If no token provided, try to get it from environment variable
58
44
  const finalToken = token || process.env.ZAPIER_TOKEN;
59
- const debugLog = createDebugLogger(debug);
60
- // Wrap fetch with debug logging if debug is enabled
61
- const wrappedFetch = debug
62
- ? createDebugFetch({
63
- originalFetch: customFetch,
64
- debugLog,
65
- })
66
- : customFetch;
45
+ // Create the API client
46
+ const api = (0, api_1.createZapierApi)({
47
+ baseUrl,
48
+ token: finalToken,
49
+ debug,
50
+ fetch: customFetch,
51
+ });
67
52
  // Create SDK object - we'll populate services after creation to avoid circular dependency
68
53
  const sdk = {};
69
54
  const appsService = createAppsService({
70
- fetch: wrappedFetch,
71
- baseUrl,
55
+ api,
72
56
  token: finalToken,
73
57
  authentications,
74
- debugLog: debug ? debugLog : undefined,
75
58
  sdk,
76
59
  });
77
60
  const actionsService = createActionsService({
78
- fetch: wrappedFetch,
79
- baseUrl,
61
+ api,
80
62
  token: finalToken,
81
63
  authentications,
82
- debugLog,
83
64
  sdk,
84
65
  });
85
66
  const authsService = createAuthsService({
86
- fetch: wrappedFetch,
87
- baseUrl,
67
+ api,
88
68
  token: finalToken,
89
69
  authentications,
90
- debugLog,
91
70
  sdk,
92
71
  });
93
72
  const fieldsService = createFieldsService({
94
- fetch: wrappedFetch,
95
- baseUrl,
73
+ api,
96
74
  token: finalToken,
97
75
  authentications,
98
- debugLog,
99
76
  sdk,
100
77
  });
101
78
  // Create root namespace tools
102
79
  const generateFunction = async (options) => {
103
- // Validate options using schema
104
- const validatedOptions = schemas_1.SdkSchemas.generate.parse(options);
105
- return await generateTypes(validatedOptions, sdk);
80
+ return (0, generateTypes_1.generateTypes)({ ...options, api, token: finalToken });
106
81
  };
107
82
  const bundleFunction = async (options) => {
108
- // Validate options using schema
109
- const validatedOptions = schemas_1.SdkSchemas.bundle.parse(options);
110
- return await bundleCode(validatedOptions);
83
+ return (0, bundleCode_1.bundleCode)(options);
111
84
  };
112
85
  // Populate the SDK object
113
86
  sdk.apps = appsService;
@@ -116,205 +89,25 @@ function createActionsSdk(options = {}) {
116
89
  sdk.fields = fieldsService;
117
90
  sdk.generate = generateFunction;
118
91
  sdk.bundle = bundleFunction;
119
- if (debug) {
120
- debugLog("🚀 Zapier SDK initialized", {
121
- baseUrl,
122
- hasAuth: !!finalToken,
123
- tokenType: finalToken ? (isJwt(finalToken) ? "JWT" : "Bearer") : "none",
124
- appAuthCount: authentications ? Object.keys(authentications).length : 0,
125
- });
126
- }
92
+ // Note: Debug logging for SDK initialization is now handled by the API client
127
93
  return sdk;
128
94
  }
129
95
  function createAppsService(options) {
130
- const { fetch, baseUrl, debugLog } = options;
96
+ const { api, token } = options;
131
97
  return {
132
98
  async list(options) {
133
- const url = new URL("/api/v4/apps/", baseUrl);
134
- // Add query parameters if provided
135
- if (options?.category) {
136
- url.searchParams.set("category", options.category);
137
- }
138
- if (options?.limit) {
139
- url.searchParams.set("limit", options.limit.toString());
140
- }
141
- if (options?.offset) {
142
- url.searchParams.set("offset", options.offset.toString());
143
- }
144
- const response = await fetch(url.toString());
145
- if (!response.ok) {
146
- throw new Error(`Failed to fetch apps: ${response.status} ${response.statusText}`);
147
- }
148
- const data = await response.json();
149
- // Debug: Log pagination info if debug is enabled
150
- if (debugLog) {
151
- debugLog("API Response Pagination Info", {
152
- resultsCount: data.results?.length || 0,
153
- hasNext: !!data.next,
154
- hasPrevious: !!data.previous,
155
- count: data.count,
156
- nextUrl: data.next,
157
- previousUrl: data.previous,
158
- });
159
- }
160
- // Transform API response to our Integration interface
161
- const apps = data.results?.map((app) => ({
162
- key: app.slug,
163
- name: app.name,
164
- description: app.description,
165
- version: "1.0.0", // API doesn't provide version
166
- category: app.category?.name,
167
- actions: [], // Will be populated separately
168
- triggers: [],
169
- current_implementation_id: app.current_implementation_id,
170
- })) || [];
171
- // Add pagination metadata to the response
172
- if (apps.length > 0) {
173
- apps.__pagination = {
174
- count: data.count,
175
- hasNext: !!data.next,
176
- hasPrevious: !!data.previous,
177
- nextUrl: data.next,
178
- previousUrl: data.previous,
179
- };
180
- }
181
- return apps;
99
+ return (0, listApps_1.listApps)({ ...options, api, token });
182
100
  },
183
101
  async get(options) {
184
- const { key } = options;
185
- const url = new URL(`/api/v4/apps/${key}/`, baseUrl);
186
- const response = await fetch(url.toString());
187
- if (!response.ok) {
188
- if (response.status === 404) {
189
- throw new types_1.AppNotFoundError(key);
190
- }
191
- throw new Error(`Failed to fetch app: ${response.status} ${response.statusText}`);
192
- }
193
- const app = await response.json();
194
- return {
195
- key: app.slug,
196
- name: app.name,
197
- description: app.description,
198
- version: "1.0.0",
199
- category: app.category?.name,
200
- actions: [],
201
- triggers: [],
202
- current_implementation_id: app.current_implementation_id,
203
- };
102
+ return (0, getApp_1.getApp)({ ...options, api, token });
204
103
  },
205
104
  };
206
105
  }
207
106
  function createAuthsService(options) {
208
- const { fetch, baseUrl, token, authentications, debugLog, sdk } = options;
209
- // Local function to handle the actual API fetching
210
- const listAuths = async (options) => {
211
- const url = new URL("/api/v4/authentications/", baseUrl);
212
- // Handle appKey filtering by getting the selected_api first
213
- if (options?.appKey) {
214
- try {
215
- // Use the SDK's getApp function instead of duplicating the API call
216
- const appData = await sdk.apps.get({ key: options.appKey });
217
- const selectedApi = appData.current_implementation_id;
218
- if (selectedApi) {
219
- // Use versionless_selected_api to find auths across all app versions
220
- // Extract the base name without the version (e.g., "SlackCLIAPI" from "SlackCLIAPI@1.21.1")
221
- const versionlessApi = selectedApi.split("@")[0];
222
- url.searchParams.set("versionless_selected_api", versionlessApi);
223
- }
224
- }
225
- catch (error) {
226
- // If it's an AppNotFoundError, re-throw it
227
- if (error instanceof types_1.AppNotFoundError) {
228
- throw error;
229
- }
230
- // For other errors, continue without app filtering
231
- console.warn(`Warning: Could not filter by app ${options.appKey}:`, error instanceof Error ? error.message : "Unknown error");
232
- }
233
- }
234
- // Add other query parameters if provided
235
- if (options?.account_id) {
236
- url.searchParams.set("account_id", options.account_id);
237
- }
238
- if (options?.owner) {
239
- url.searchParams.set("owner", options.owner);
240
- }
241
- if (options?.limit) {
242
- url.searchParams.set("limit", options.limit.toString());
243
- }
244
- if (options?.offset) {
245
- url.searchParams.set("offset", options.offset.toString());
246
- }
247
- // Build headers with auth if available
248
- const headers = {};
249
- if (token) {
250
- headers["Authorization"] = getAuthorizationHeader(token);
251
- }
252
- else {
253
- throw new Error("Authentication token is required to list authentications. Please provide token when creating the SDK.");
254
- }
255
- const response = await fetch(url.toString(), { headers });
256
- if (!response.ok) {
257
- if (response.status === 401) {
258
- throw new Error(`Authentication failed. Your token may not have permission to access authentications or may be expired. (HTTP ${response.status})`);
259
- }
260
- if (response.status === 403) {
261
- throw new Error(`Access forbidden. Your token may not have the required scopes to list authentications. (HTTP ${response.status})`);
262
- }
263
- throw new Error(`Failed to fetch authentications: ${response.status} ${response.statusText}`);
264
- }
265
- const data = await response.json();
266
- // Debug: Log pagination info if debug is enabled
267
- if (debugLog) {
268
- debugLog("API Response Pagination Info", {
269
- resultsCount: data.results?.length || 0,
270
- hasNext: !!data.next,
271
- hasPrevious: !!data.previous,
272
- count: data.count,
273
- nextUrl: data.next,
274
- previousUrl: data.previous,
275
- });
276
- }
277
- // Transform API response
278
- const auths = data.results || [];
279
- // Add pagination metadata to the response
280
- if (auths.length > 0) {
281
- auths.__pagination = {
282
- count: data.count,
283
- hasNext: !!data.next,
284
- hasPrevious: !!data.previous,
285
- nextUrl: data.next,
286
- previousUrl: data.previous,
287
- };
288
- }
289
- return auths;
290
- };
107
+ const { api, token, authentications } = options;
291
108
  return {
292
109
  async list(options) {
293
- // If a limit is provided and no specific owner filter, prioritize owned auths
294
- if (options?.limit && options?.owner === undefined) {
295
- // First get owned auths
296
- const ownedAuths = await listAuths({
297
- ...options,
298
- owner: "me",
299
- });
300
- // If we have enough owned auths, just slice and return
301
- if (ownedAuths.length >= options.limit) {
302
- return ownedAuths.slice(0, options.limit);
303
- }
304
- // Get all auths up to the original limit to fill remaining slots
305
- const allAuths = await listAuths({
306
- ...options,
307
- owner: undefined,
308
- });
309
- // Filter out auths the user already owns to avoid duplicates
310
- const ownedAuthIds = new Set(ownedAuths.map((auth) => auth.id));
311
- const additionalAuths = allAuths.filter((auth) => !ownedAuthIds.has(auth.id));
312
- // Combine and slice to the requested limit
313
- const combined = [...ownedAuths, ...additionalAuths];
314
- return combined.slice(0, options.limit);
315
- }
316
- // Standard implementation for non-prioritized requests
317
- return listAuths(options);
110
+ return (0, listAuths_1.listAuths)({ ...options, api, token });
318
111
  },
319
112
  find(options) {
320
113
  if (!authentications) {
@@ -332,225 +125,28 @@ function createAuthsService(options) {
332
125
  };
333
126
  }
334
127
  function createFieldsService(options) {
335
- const { fetch, baseUrl, token, sdk } = options;
128
+ const { api, token } = options;
336
129
  return {
337
130
  async list(options) {
338
- // Map consistent parameter names to internal API names
339
- const { app, action, type, authId, params } = options;
340
- const appKey = app;
341
- const actionKey = action;
342
- const actionType = type;
343
- // Build headers with auth if available
344
- const headers = {};
345
- if (token) {
346
- headers["Authorization"] = getAuthorizationHeader(token);
347
- }
348
- // Use the SDK's getApp function instead of duplicating the API call
349
- const appData = await sdk.apps.get({ key: appKey });
350
- const selectedApi = appData.current_implementation_id;
351
- if (!selectedApi) {
352
- throw new Error("No current_implementation_id found for app");
353
- }
354
- // Build needs request
355
- const needsRequest = {
356
- selected_api: selectedApi,
357
- action: actionKey,
358
- type_of: actionType,
359
- authentication_id: authId,
360
- params: params || {},
361
- };
362
- const needsUrl = new URL("/api/v4/implementations/needs/", baseUrl);
363
- // Add Content-Type to headers
364
- const needsHeaders = { ...headers, "Content-Type": "application/json" };
365
- const needsResponse = await fetch(needsUrl.toString(), {
366
- method: "POST",
367
- headers: needsHeaders,
368
- body: JSON.stringify(needsRequest),
369
- });
370
- if (!needsResponse.ok) {
371
- // Add more detailed error info for debugging
372
- let errorMessage = `Failed to fetch action fields: ${needsResponse.status} ${needsResponse.statusText}`;
373
- try {
374
- const errorBody = await needsResponse.text();
375
- errorMessage += ` - ${errorBody}`;
376
- }
377
- catch {
378
- // Ignore error reading response body
379
- }
380
- throw new Error(errorMessage);
381
- }
382
- const needsData = await needsResponse.json();
383
- if (!needsData.success) {
384
- throw new Error(`Failed to get action fields: ${needsData.errors?.join(", ") || "Unknown error"}`);
385
- }
386
- // Transform API response to our ActionField interface
387
- return (needsData.needs || []).map((need) => ({
388
- key: need.key,
389
- label: need.label,
390
- required: need.required || false,
391
- type: need.type,
392
- helpText: need.help_text,
393
- helpTextHtml: need.help_text_html,
394
- choices: need.choices?.map((choice) => ({
395
- value: choice.value,
396
- label: choice.label,
397
- })),
398
- default: need.default,
399
- placeholder: need.placeholder,
400
- computed: need.computed,
401
- customField: need.custom_field,
402
- dependsOn: need.depends_on,
403
- format: need.format,
404
- inputFormat: need.input_format,
405
- }));
131
+ return (0, listFields_1.listFields)({ ...options, api, token });
406
132
  },
407
133
  };
408
134
  }
409
135
  function createActionsService(options) {
410
- const { fetch, baseUrl, token, authentications, debugLog, sdk } = options;
136
+ const { api, token, authentications } = options;
411
137
  // Create the base service with named methods
412
138
  const baseService = {
413
139
  async list(options) {
414
- // If filtering by appKey, use optimized approach with selected_apis parameter
415
- if (options?.appKey) {
416
- try {
417
- // Use the SDK's getApp function instead of duplicating the API call
418
- const appData = await sdk.apps.get({ key: options.appKey });
419
- // Extract implementation name from current_implementation_id (e.g., "SlackCLIAPI@1.20.0" -> "SlackCLIAPI")
420
- const implementationId = appData.current_implementation_id?.split("@")[0];
421
- if (!implementationId) {
422
- throw new Error("No current_implementation_id found for app");
423
- }
424
- const url = new URL("/api/v4/implementations/", baseUrl);
425
- url.searchParams.set("global", "true");
426
- url.searchParams.set("public_only", "true");
427
- url.searchParams.set("selected_apis", implementationId);
428
- const response = await fetch(url.toString());
429
- if (!response.ok) {
430
- throw new Error(`Failed to fetch actions: ${response.status} ${response.statusText}`);
431
- }
432
- const data = await response.json();
433
- const actions = [];
434
- // Transform implementations to actions
435
- for (const implementation of data.results || []) {
436
- if (implementation.actions) {
437
- for (const action of implementation.actions) {
438
- const transformedAction = {
439
- key: action.key,
440
- name: action.name || action.label,
441
- description: action.description || "",
442
- appKey: implementation.slug || "",
443
- type: action.type || action.type_of || "read",
444
- inputFields: [], // Would need additional API call for detailed fields
445
- outputFields: [],
446
- };
447
- // Apply type filter if provided
448
- if (options?.type && transformedAction.type !== options.type) {
449
- continue;
450
- }
451
- actions.push(transformedAction);
452
- }
453
- }
454
- }
455
- // Add pagination metadata for app-specific queries
456
- if (actions.length > 0) {
457
- actions.__pagination = {
458
- count: data.count,
459
- hasNext: !!data.next,
460
- hasPrevious: !!data.previous,
461
- nextUrl: data.next,
462
- previousUrl: data.previous,
463
- };
464
- }
465
- return actions;
466
- }
467
- catch (error) {
468
- // If it's an AppNotFoundError, don't try fallback - just re-throw
469
- if (error instanceof types_1.AppNotFoundError) {
470
- throw error;
471
- }
472
- // Fallback to original approach if optimized approach fails
473
- console.warn("Optimized app lookup failed, falling back to full scan:", error);
474
- }
475
- }
476
- // Original approach for general queries or when optimization fails
477
- const url = new URL("/api/v4/implementations/", baseUrl);
478
- // Add query parameters
479
- url.searchParams.set("global", "true");
480
- url.searchParams.set("public_only", "true");
481
- const response = await fetch(url.toString());
482
- if (!response.ok) {
483
- throw new Error(`Failed to fetch actions: ${response.status} ${response.statusText}`);
484
- }
485
- const data = await response.json();
486
- const actions = [];
487
- // Transform implementations to actions
488
- for (const implementation of data.results || []) {
489
- if (implementation.actions) {
490
- for (const action of implementation.actions) {
491
- const transformedAction = {
492
- key: action.key,
493
- name: action.name || action.label,
494
- description: action.description || "",
495
- appKey: implementation.slug || "",
496
- type: action.type || action.type_of || "read",
497
- inputFields: [], // Would need additional API call for detailed fields
498
- outputFields: [],
499
- };
500
- // Apply filters
501
- if (options?.appKey &&
502
- transformedAction.appKey !== options.appKey) {
503
- continue;
504
- }
505
- if (options?.type && transformedAction.type !== options.type) {
506
- continue;
507
- }
508
- actions.push(transformedAction);
509
- }
510
- }
511
- }
512
- // Add pagination metadata for general queries
513
- if (actions.length > 0) {
514
- actions.__pagination = {
515
- count: data.count,
516
- hasNext: !!data.next,
517
- hasPrevious: !!data.previous,
518
- nextUrl: data.next,
519
- previousUrl: data.previous,
520
- };
521
- }
522
- return actions;
140
+ return (0, listActions_1.listActions)({ ...options, api, token });
523
141
  },
524
142
  async get(options) {
525
- const { app, action: actionKey, type } = options;
526
- const actions = await baseService.list({ appKey: app });
527
- const action = actions.find((a) => a.key === actionKey && a.type === type);
528
- if (!action) {
529
- throw new Error(`Action not found: ${actionKey} with type ${type}`);
530
- }
531
- return action;
532
- },
533
- async fields(options) {
534
- return await sdk.fields.list(options);
535
- },
536
- async getFields(options) {
537
- return await sdk.fields.list(options);
143
+ return (0, getAction_1.getAction)({ ...options, api, token });
538
144
  },
539
145
  // run will be replaced with function/proxy pattern below
540
146
  };
541
147
  // Create the run function with proxy capabilities
542
148
  const runFunction = async (params) => {
543
149
  const { app, type, action, inputs, authId: providedAuthId } = params;
544
- // Validate that the action exists
545
- const actionData = await baseService.get({
546
- app: app,
547
- action: action,
548
- type: type,
549
- });
550
- // Validate action type matches
551
- if (actionData.type !== type) {
552
- throw new Error(`Action type mismatch: expected ${type}, got ${actionData.type}`);
553
- }
554
150
  // Resolve auth ID for this specific app
555
151
  let authId = providedAuthId;
556
152
  if (!authId && authentications) {
@@ -563,28 +159,16 @@ function createActionsService(options) {
563
159
  }
564
160
  authId = auths[0].id;
565
161
  }
566
- // Execute the action using the appropriate API based on action type
567
- const startTime = Date.now();
568
- const result = await executeActionWithStrategy({
569
- fetch,
570
- baseUrl,
571
- appSlug: app,
572
- actionKey: action,
573
- actionType: actionData.type,
574
- executionOptions: { inputs: inputs || {} },
575
- auth: token ? { token: token, authentication_id: authId } : undefined,
576
- debugLog,
577
- sdk,
162
+ // Delegate to standalone runAction function
163
+ return (0, runAction_1.runAction)({
164
+ app,
165
+ type,
166
+ action,
167
+ inputs,
168
+ authId,
169
+ api,
170
+ token,
578
171
  });
579
- const executionTime = Date.now() - startTime;
580
- return {
581
- success: true,
582
- data: result,
583
- metadata: {
584
- executionTime,
585
- requestId: generateRequestId(),
586
- },
587
- };
588
172
  };
589
173
  // Create proxy wrapper for the run function
590
174
  const runWithProxy = new Proxy(runFunction, {
@@ -622,573 +206,3 @@ function createActionsService(options) {
622
206
  // Return the service with the new run function/proxy
623
207
  return baseService;
624
208
  }
625
- async function executeActionWithStrategy(options) {
626
- const { fetch, baseUrl, appSlug, actionKey, actionType, executionOptions, auth, debugLog, sdk, } = options;
627
- debugLog?.("🎯 Choosing execution strategy", { actionType });
628
- // Actions API supports: read, read_bulk, write
629
- // Invoke API supports: search, read, write, read_bulk, and more
630
- const actionsApiTypes = ["read", "read_bulk", "write"];
631
- const useActionsApi = actionsApiTypes.includes(actionType);
632
- if (useActionsApi) {
633
- debugLog?.("🔧 Using Actions API", {
634
- actionType,
635
- reason: "supported type",
636
- });
637
- return executeActionViaActionsApi({
638
- fetch,
639
- baseUrl,
640
- appSlug,
641
- actionKey,
642
- actionType,
643
- executionOptions,
644
- auth,
645
- debugLog,
646
- sdk,
647
- });
648
- }
649
- else {
650
- debugLog?.("🔧 Using Invoke API", {
651
- actionType,
652
- reason: "unsupported by Actions API",
653
- });
654
- return executeActionViaInvokeApi({
655
- fetch,
656
- baseUrl,
657
- appSlug,
658
- actionKey,
659
- actionType,
660
- executionOptions,
661
- auth,
662
- debugLog,
663
- sdk,
664
- });
665
- }
666
- }
667
- async function executeActionViaActionsApi(options) {
668
- const { fetch, baseUrl, appSlug, actionKey, actionType, executionOptions, auth, sdk, } = options;
669
- // Note: Action validation is handled by the caller before reaching this function
670
- // Use the SDK's getApp function instead of duplicating the API call
671
- const appData = await sdk.apps.get({ key: appSlug });
672
- const selectedApi = appData.current_implementation_id;
673
- if (!selectedApi) {
674
- throw new Error("No current_implementation_id found for app");
675
- }
676
- if (!auth?.token) {
677
- throw new Error("Authentication token is required. Please provide token when creating the SDK.");
678
- }
679
- // Determine if token is JWT and use appropriate prefix
680
- const authHeader = getAuthorizationHeader(auth.token);
681
- // Step 1: POST to /actions/v1/runs to start execution
682
- const runRequest = {
683
- data: {
684
- authentication_id: auth.authentication_id || 1,
685
- selected_api: selectedApi,
686
- action_key: actionKey,
687
- action_type: actionType,
688
- inputs: executionOptions.inputs || {},
689
- },
690
- };
691
- const runUrl = `${baseUrl}/api/actions/v1/runs`;
692
- const headers = {
693
- "Content-Type": "application/json",
694
- Authorization: authHeader,
695
- };
696
- const runResponse = await fetch(runUrl, {
697
- method: "POST",
698
- headers,
699
- body: JSON.stringify(runRequest),
700
- });
701
- if (!runResponse.ok) {
702
- throw new Error(`Failed to start action execution: POST ${runUrl} - ${runResponse.status} ${runResponse.statusText}`);
703
- }
704
- const runData = await runResponse.json();
705
- const runId = runData.data.id;
706
- // Step 2: Poll GET /actions/v1/runs/{run_id} for results
707
- return await pollForResults({ fetch, baseUrl, runId, authHeader });
708
- }
709
- async function pollForResults(options) {
710
- const { fetch, baseUrl, runId, authHeader } = options;
711
- let delay = 50; // Start with 50ms
712
- const maxDelay = 1000; // Cap at 1 second
713
- const maxAttempts = 30; // Maximum number of polling attempts
714
- for (let attempt = 0; attempt < maxAttempts; attempt++) {
715
- const pollUrl = `${baseUrl}/api/actions/v1/runs/${runId}`;
716
- const response = await fetch(pollUrl, {
717
- headers: {
718
- Authorization: authHeader,
719
- },
720
- });
721
- if (response.status === 200) {
722
- // Action completed
723
- const result = await response.json();
724
- return result.data;
725
- }
726
- else if (response.status === 202) {
727
- // Still processing, wait and try again
728
- await new Promise((resolve) => setTimeout(resolve, delay));
729
- delay = Math.min(delay * 2, maxDelay); // Double delay up to max
730
- continue;
731
- }
732
- else {
733
- // Error occurred
734
- throw new Error(`Failed to fetch action results: GET ${pollUrl} - ${response.status} ${response.statusText}`);
735
- }
736
- }
737
- throw new Error(`Action execution timed out after ${maxAttempts} attempts`);
738
- }
739
- async function executeActionViaInvokeApi(options) {
740
- const { fetch, baseUrl, appSlug, actionKey, actionType, executionOptions, auth, sdk, } = options;
741
- // Note: Action and app validation is handled by the caller before reaching this function
742
- // Use the SDK's getApp function instead of duplicating the API call
743
- const appData = await sdk.apps.get({ key: appSlug });
744
- const selectedApi = appData.current_implementation_id;
745
- if (!selectedApi) {
746
- throw new Error("No current_implementation_id found for app");
747
- }
748
- if (!auth?.token) {
749
- throw new Error("Authentication token is required. Please provide token when creating the SDK.");
750
- }
751
- // Determine if token is JWT and use appropriate prefix
752
- const authHeader = getAuthorizationHeader(auth.token);
753
- // Step 1: POST to /invoke/v1/invoke to start execution
754
- const invokeRequest = {
755
- selected_api: selectedApi,
756
- action: actionKey,
757
- type_of: actionType,
758
- authentication_id: auth.authentication_id || 1,
759
- params: executionOptions.inputs || {},
760
- };
761
- const invokeUrl = `${baseUrl}/api/invoke/v1/invoke`;
762
- const headers = {
763
- "Content-Type": "application/json",
764
- Authorization: authHeader,
765
- };
766
- const invokeResponse = await fetch(invokeUrl, {
767
- method: "POST",
768
- headers,
769
- body: JSON.stringify(invokeRequest),
770
- });
771
- if (!invokeResponse.ok) {
772
- throw new Error(`Failed to start action execution: POST ${invokeUrl} - ${invokeResponse.status} ${invokeResponse.statusText}`);
773
- }
774
- const invokeData = await invokeResponse.json();
775
- const invocationId = invokeData.invocation_id;
776
- // Step 2: Poll GET /invoke/v1/invoke/{invocation_id} for results
777
- return await pollForInvokeResults({
778
- fetch,
779
- baseUrl,
780
- invocationId,
781
- authHeader,
782
- });
783
- }
784
- async function pollForInvokeResults(options) {
785
- const { fetch, baseUrl, invocationId, authHeader } = options;
786
- let delay = 50; // Start with 50ms
787
- const maxDelay = 1000; // Cap at 1 second
788
- const maxAttempts = 30; // Maximum number of polling attempts
789
- for (let attempt = 0; attempt < maxAttempts; attempt++) {
790
- const pollUrl = `${baseUrl}/api/invoke/v1/invoke/${invocationId}`;
791
- const response = await fetch(pollUrl, {
792
- headers: {
793
- Authorization: authHeader,
794
- },
795
- });
796
- if (response.status === 200) {
797
- // Action completed
798
- const result = await response.json();
799
- return result.results || result;
800
- }
801
- else if (response.status === 202) {
802
- // Still processing, wait and try again
803
- await new Promise((resolve) => setTimeout(resolve, delay));
804
- delay = Math.min(delay * 2, maxDelay); // Double delay up to max
805
- continue;
806
- }
807
- else {
808
- // Error occurred
809
- throw new Error(`Failed to fetch action results: GET ${pollUrl} - ${response.status} ${response.statusText}`);
810
- }
811
- }
812
- throw new Error(`Action execution timed out after ${maxAttempts} attempts`);
813
- }
814
- function generateRequestId() {
815
- return Math.random().toString(36).substring(2) + Date.now().toString(36);
816
- }
817
- function getAuthorizationHeader(token) {
818
- // Check if token is a JWT (has 3 parts separated by dots)
819
- if (isJwt(token)) {
820
- return `JWT ${token}`;
821
- }
822
- // Default to Bearer for other token types
823
- return `Bearer ${token}`;
824
- }
825
- function isJwt(token) {
826
- // JWT tokens have exactly 3 parts separated by dots
827
- const parts = token.split(".");
828
- if (parts.length !== 3) {
829
- return false;
830
- }
831
- // Each part should be base64url encoded (no padding)
832
- // Basic validation - each part should be non-empty and contain valid base64url characters
833
- const base64urlPattern = /^[A-Za-z0-9_-]+$/;
834
- return parts.every((part) => part.length > 0 && base64urlPattern.test(part));
835
- }
836
- function createDebugLogger(enabled) {
837
- if (!enabled) {
838
- return () => { }; // No-op function when debug is disabled
839
- }
840
- return (message, data) => {
841
- const timestamp = new Date().toISOString();
842
- if (data) {
843
- console.log(`[${timestamp}] Zapier SDK: ${message}`, data);
844
- }
845
- else {
846
- console.log(`[${timestamp}] Zapier SDK: ${message}`);
847
- }
848
- };
849
- }
850
- function createDebugFetch(options) {
851
- const { originalFetch, debugLog } = options;
852
- return async (input, options) => {
853
- const startTime = Date.now();
854
- // Convert input to URL string for logging
855
- const urlString = input instanceof URL
856
- ? input.toString()
857
- : typeof input === "string"
858
- ? input
859
- : input.url;
860
- // Log request
861
- const requestInfo = {
862
- method: options?.method || "GET",
863
- url: urlString,
864
- headers: options?.headers
865
- ? maskSensitiveHeaders(options.headers)
866
- : undefined,
867
- hasBody: !!options?.body,
868
- };
869
- debugLog("📤 HTTP Request", requestInfo);
870
- try {
871
- const response = await originalFetch(input, options);
872
- const duration = Date.now() - startTime;
873
- // Log response
874
- debugLog("📥 HTTP Response", {
875
- status: response.status,
876
- statusText: response.statusText,
877
- ok: response.ok,
878
- duration: `${duration}ms`,
879
- url: urlString,
880
- });
881
- return response;
882
- }
883
- catch (error) {
884
- const duration = Date.now() - startTime;
885
- debugLog("❌ HTTP Error", {
886
- error: error instanceof Error ? error.message : "Unknown error",
887
- duration: `${duration}ms`,
888
- url: urlString,
889
- });
890
- throw error;
891
- }
892
- };
893
- }
894
- function maskSensitiveHeaders(headers) {
895
- const maskedHeaders = {};
896
- if (headers instanceof Headers) {
897
- headers.forEach((value, key) => {
898
- maskedHeaders[key] =
899
- key.toLowerCase() === "authorization" ? maskToken(value) : value;
900
- });
901
- }
902
- else if (Array.isArray(headers)) {
903
- headers.forEach(([key, value]) => {
904
- maskedHeaders[key] =
905
- key.toLowerCase() === "authorization" ? maskToken(value) : value;
906
- });
907
- }
908
- else {
909
- Object.entries(headers).forEach(([key, value]) => {
910
- maskedHeaders[key] =
911
- key.toLowerCase() === "authorization" ? maskToken(value) : value;
912
- });
913
- }
914
- return maskedHeaders;
915
- }
916
- function maskToken(token) {
917
- if (token.length <= 8) {
918
- return "***";
919
- }
920
- // Show first 4 and last 4 characters, mask the middle
921
- const start = token.substring(0, 4);
922
- const end = token.substring(token.length - 4);
923
- const middle = "*".repeat(Math.min(token.length - 8, 20)); // Cap mask length
924
- return `${start}${middle}${end}`;
925
- }
926
- async function generateTypes(options, sdk) {
927
- const { appKey, authId, output = `./types/${appKey}.d.ts` } = options;
928
- // Parse app identifier (support app@version format)
929
- const { app, version } = parseAppIdentifier(appKey);
930
- // Fetch all actions for the app
931
- const actions = await sdk.actions.list({ appKey: app });
932
- if (actions.length === 0) {
933
- const typeDefinitions = generateEmptyTypesFile(app, version);
934
- if (output) {
935
- const fs = await Promise.resolve().then(() => __importStar(require("fs")));
936
- const path = await Promise.resolve().then(() => __importStar(require("path")));
937
- fs.mkdirSync(path.dirname(output), { recursive: true });
938
- fs.writeFileSync(output, typeDefinitions, "utf8");
939
- }
940
- return typeDefinitions;
941
- }
942
- // Fetch input fields for each action
943
- const actionsWithFields = [];
944
- if (authId) {
945
- for (const action of actions) {
946
- try {
947
- const fields = await sdk.fields.list({
948
- app: action.appKey,
949
- action: action.key,
950
- type: action.type,
951
- authId: authId,
952
- });
953
- actionsWithFields.push({ ...action, inputFields: fields });
954
- }
955
- catch {
956
- // If we can't get fields for an action, include it without fields
957
- actionsWithFields.push({ ...action, inputFields: [] });
958
- }
959
- }
960
- }
961
- else {
962
- // Convert actions to have empty input fields (will generate generic types)
963
- actions.forEach((action) => {
964
- actionsWithFields.push({ ...action, inputFields: [] });
965
- });
966
- }
967
- // Generate TypeScript types
968
- const typeDefinitions = generateTypeDefinitions(app, actionsWithFields, version);
969
- // Write to file if output path specified
970
- if (output) {
971
- const fs = await Promise.resolve().then(() => __importStar(require("fs")));
972
- const path = await Promise.resolve().then(() => __importStar(require("path")));
973
- fs.mkdirSync(path.dirname(output), { recursive: true });
974
- fs.writeFileSync(output, typeDefinitions, "utf8");
975
- }
976
- return typeDefinitions;
977
- }
978
- function parseAppIdentifier(identifier) {
979
- const parts = identifier.split("@");
980
- return {
981
- app: parts[0],
982
- version: parts[1],
983
- };
984
- }
985
- function generateTypeDefinitions(appKey, actions, version) {
986
- // Handle empty actions
987
- if (actions.length === 0) {
988
- return generateEmptyTypesFile(appKey, version);
989
- }
990
- // Group actions by type
991
- const actionsByType = actions.reduce((acc, action) => {
992
- if (!acc[action.type]) {
993
- acc[action.type] = [];
994
- }
995
- acc[action.type].push(action);
996
- return acc;
997
- }, {});
998
- const appName = capitalize(appKey);
999
- const versionComment = version
1000
- ? ` * Generated for ${appKey}@${version}`
1001
- : ` * Generated for ${appKey}`;
1002
- let output = `/* eslint-disable @typescript-eslint/naming-convention */
1003
- /**
1004
- * Auto-generated TypeScript types for Zapier ${appKey} actions
1005
- ${versionComment}
1006
- * Generated on: ${new Date().toISOString()}
1007
- *
1008
- * Usage:
1009
- * import type { ${appName}Actions } from './path/to/this/file'
1010
- * const sdk: ActionsSdk & { actions: ${appName}Actions } = createActionsSdk(...)
1011
- */
1012
-
1013
- import type { ActionExecutionOptions, ActionExecutionResult } from '@zapier/zapier-sdk'
1014
-
1015
- `;
1016
- // Generate input types for each action
1017
- actions.forEach((action) => {
1018
- if (action.inputFields.length > 0) {
1019
- const inputTypeName = `${appName}${capitalize(action.type)}${capitalize(sanitizeActionName(action.key))}Inputs`;
1020
- output += `interface ${inputTypeName} {\n`;
1021
- action.inputFields.forEach((field) => {
1022
- const isOptional = !field.required;
1023
- const fieldType = mapFieldTypeToTypeScript(field);
1024
- const description = field.helpText
1025
- ? ` /** ${escapeComment(field.helpText)} */\n`
1026
- : "";
1027
- output += `${description} ${sanitizeFieldName(field.key)}${isOptional ? "?" : ""}: ${fieldType}\n`;
1028
- });
1029
- output += `}\n\n`;
1030
- }
1031
- });
1032
- // Generate action type interfaces for each action type
1033
- Object.entries(actionsByType).forEach(([actionType, typeActions]) => {
1034
- const typeName = `${appName}${capitalize(actionType)}Actions`;
1035
- output += `interface ${typeName} {\n`;
1036
- typeActions.forEach((action) => {
1037
- const actionName = sanitizeActionName(action.key);
1038
- const description = action.description
1039
- ? ` /** ${escapeComment(action.description)} */\n`
1040
- : "";
1041
- // Generate type-safe action method signature
1042
- if (action.inputFields.length > 0) {
1043
- const inputTypeName = `${appName}${capitalize(action.type)}${capitalize(sanitizeActionName(action.key))}Inputs`;
1044
- output += `${description} ${actionName}: (options: { inputs: ${inputTypeName} } & Omit<ActionExecutionOptions, 'inputs'>) => Promise<ActionExecutionResult>\n`;
1045
- }
1046
- else {
1047
- // No specific input fields available - use generic Record<string, any> for inputs
1048
- output += `${description} ${actionName}: (options?: { inputs?: Record<string, any> } & ActionExecutionOptions) => Promise<ActionExecutionResult>\n`;
1049
- }
1050
- });
1051
- output += `}\n\n`;
1052
- });
1053
- // Generate the main app actions interface
1054
- output += `export interface ${appName}Actions {\n`;
1055
- output += ` run: {\n`;
1056
- output += ` ${appKey}: {\n`;
1057
- Object.keys(actionsByType).forEach((actionType) => {
1058
- const typeName = `${appName}${capitalize(actionType)}Actions`;
1059
- output += ` ${actionType}: ${typeName}\n`;
1060
- });
1061
- output += ` }\n`;
1062
- output += ` }\n`;
1063
- output += `}\n`;
1064
- return output;
1065
- }
1066
- function generateEmptyTypesFile(appKey, version) {
1067
- const appName = capitalize(appKey);
1068
- const versionComment = version
1069
- ? ` * Generated for ${appKey}@${version}`
1070
- : ` * Generated for ${appKey}`;
1071
- return `/* eslint-disable @typescript-eslint/naming-convention */
1072
- /**
1073
- * Auto-generated TypeScript types for Zapier ${appKey} actions
1074
- ${versionComment}
1075
- * Generated on: ${new Date().toISOString()}
1076
- *
1077
- * No actions found for this app.
1078
- */
1079
-
1080
- import type { ActionExecutionOptions, ActionExecutionResult } from '@zapier/zapier-sdk'
1081
-
1082
- export interface ${appName}Actions {
1083
- run: {
1084
- ${appKey}: {
1085
- // No actions available
1086
- }
1087
- }
1088
- }
1089
- `;
1090
- }
1091
- function capitalize(str) {
1092
- return str.charAt(0).toUpperCase() + str.slice(1).replace(/[-_]/g, "");
1093
- }
1094
- function sanitizeActionName(actionKey) {
1095
- // Ensure the action name is a valid TypeScript identifier
1096
- return actionKey.replace(/[^a-zA-Z0-9_$]/g, "_");
1097
- }
1098
- function sanitizeFieldName(fieldKey) {
1099
- // Ensure the field name is a valid TypeScript identifier
1100
- return fieldKey.replace(/[^a-zA-Z0-9_$]/g, "_");
1101
- }
1102
- function escapeComment(comment) {
1103
- // Escape comment text to prevent breaking the JSDoc comment
1104
- return comment.replace(/\*\//g, "*\\/").replace(/\r?\n/g, " ");
1105
- }
1106
- function mapFieldTypeToTypeScript(field) {
1107
- // Handle choices (enum-like fields)
1108
- if (field.choices && field.choices.length > 0) {
1109
- const choiceValues = field.choices
1110
- .filter((choice) => choice.value !== undefined &&
1111
- choice.value !== null &&
1112
- choice.value !== "")
1113
- .map((choice) => typeof choice.value === "string" ? `"${choice.value}"` : choice.value);
1114
- if (choiceValues.length > 0) {
1115
- return choiceValues.join(" | ");
1116
- }
1117
- // If all choices were filtered out, fall through to default type handling
1118
- }
1119
- // Map Zapier field types to TypeScript types
1120
- switch (field.type?.toLowerCase()) {
1121
- case "string":
1122
- case "text":
1123
- case "email":
1124
- case "url":
1125
- case "password":
1126
- return "string";
1127
- case "integer":
1128
- case "number":
1129
- return "number";
1130
- case "boolean":
1131
- return "boolean";
1132
- case "datetime":
1133
- case "date":
1134
- return "string"; // ISO date strings
1135
- case "file":
1136
- return "string"; // File URL or content
1137
- case "array":
1138
- return "any[]";
1139
- case "object":
1140
- return "Record<string, any>";
1141
- default:
1142
- // Default to string for unknown types, with union for common cases
1143
- return "string | number | boolean";
1144
- }
1145
- }
1146
- // ============================================================================
1147
- // Bundle Implementation
1148
- // ============================================================================
1149
- async function bundleCode(options) {
1150
- const { input, output, target = "es2020", cjs = false, minify = false, string: returnString = false, } = options;
1151
- // Dynamically import esbuild
1152
- const { buildSync } = await Promise.resolve().then(() => __importStar(require("esbuild")));
1153
- const fs = await Promise.resolve().then(() => __importStar(require("fs")));
1154
- const path = await Promise.resolve().then(() => __importStar(require("path")));
1155
- // Resolve input path
1156
- const resolvedInput = path.resolve(process.cwd(), input);
1157
- try {
1158
- // Bundle with esbuild
1159
- const result = buildSync({
1160
- entryPoints: [resolvedInput],
1161
- bundle: true,
1162
- platform: "node",
1163
- target: target,
1164
- format: cjs ? "cjs" : "esm",
1165
- minify: minify,
1166
- write: false,
1167
- external: [], // Bundle everything
1168
- banner: {
1169
- js: "#!/usr/bin/env node",
1170
- },
1171
- });
1172
- if (result.errors.length > 0) {
1173
- throw new Error(`Bundle failed: ${result.errors.map((e) => e.text).join(", ")}`);
1174
- }
1175
- const bundledCode = result.outputFiles?.[0]?.text;
1176
- if (!bundledCode) {
1177
- throw new Error("No output generated");
1178
- }
1179
- let finalOutput = bundledCode;
1180
- if (returnString) {
1181
- // Output as quoted string for node -e using JSON.stringify
1182
- finalOutput = JSON.stringify(bundledCode);
1183
- }
1184
- // Write to file if output path specified
1185
- if (output) {
1186
- fs.mkdirSync(path.dirname(output), { recursive: true });
1187
- fs.writeFileSync(output, finalOutput, "utf8");
1188
- }
1189
- return finalOutput;
1190
- }
1191
- catch (error) {
1192
- throw new Error(`Bundle failed: ${error instanceof Error ? error.message : "Unknown error"}`);
1193
- }
1194
- }