@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/dist/actions-sdk.js
CHANGED
|
@@ -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
|
|
41
|
-
const
|
|
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
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
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
|
-
|
|
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
|
-
|
|
79
|
-
baseUrl,
|
|
61
|
+
api,
|
|
80
62
|
token: finalToken,
|
|
81
63
|
authentications,
|
|
82
|
-
debugLog,
|
|
83
64
|
sdk,
|
|
84
65
|
});
|
|
85
66
|
const authsService = createAuthsService({
|
|
86
|
-
|
|
87
|
-
baseUrl,
|
|
67
|
+
api,
|
|
88
68
|
token: finalToken,
|
|
89
69
|
authentications,
|
|
90
|
-
debugLog,
|
|
91
70
|
sdk,
|
|
92
71
|
});
|
|
93
72
|
const fieldsService = createFieldsService({
|
|
94
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 {
|
|
96
|
+
const { api, token } = options;
|
|
131
97
|
return {
|
|
132
98
|
async list(options) {
|
|
133
|
-
|
|
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
|
-
|
|
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 {
|
|
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
|
-
|
|
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 {
|
|
128
|
+
const { api, token } = options;
|
|
336
129
|
return {
|
|
337
130
|
async list(options) {
|
|
338
|
-
|
|
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 {
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
//
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
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
|
-
}
|