@zapier/zapier-sdk-cli 0.8.4 → 0.9.0

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 (45) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/README.md +35 -51
  3. package/dist/cli.cjs +750 -346
  4. package/dist/cli.mjs +751 -347
  5. package/dist/index.cjs +726 -336
  6. package/dist/index.mjs +727 -337
  7. package/dist/package.json +1 -1
  8. package/dist/src/plugins/add/ast-generator.d.ts +37 -0
  9. package/dist/src/plugins/add/ast-generator.js +403 -0
  10. package/dist/src/plugins/add/index.d.ts +13 -0
  11. package/dist/src/plugins/add/index.js +122 -0
  12. package/dist/src/plugins/add/schemas.d.ts +18 -0
  13. package/dist/src/plugins/add/schemas.js +19 -0
  14. package/dist/src/plugins/getLoginConfigPath/index.d.ts +15 -0
  15. package/dist/src/plugins/getLoginConfigPath/index.js +19 -0
  16. package/dist/src/plugins/getLoginConfigPath/schemas.d.ts +3 -0
  17. package/dist/src/plugins/getLoginConfigPath/schemas.js +5 -0
  18. package/dist/src/plugins/index.d.ts +2 -2
  19. package/dist/src/plugins/index.js +2 -2
  20. package/dist/src/sdk.js +3 -3
  21. package/dist/src/utils/cli-generator.js +15 -0
  22. package/dist/tsconfig.tsbuildinfo +1 -1
  23. package/package.json +4 -4
  24. package/src/plugins/add/ast-generator.ts +777 -0
  25. package/src/plugins/add/index.test.ts +58 -0
  26. package/src/plugins/add/index.ts +187 -0
  27. package/src/plugins/add/schemas.ts +26 -0
  28. package/src/plugins/getLoginConfigPath/index.ts +45 -0
  29. package/src/plugins/getLoginConfigPath/schemas.ts +10 -0
  30. package/src/plugins/index.ts +2 -2
  31. package/src/sdk.ts +4 -4
  32. package/src/utils/cli-generator.ts +22 -0
  33. package/tsup.config.ts +1 -1
  34. package/dist/src/plugins/generateTypes/index.d.ts +0 -21
  35. package/dist/src/plugins/generateTypes/index.js +0 -312
  36. package/dist/src/plugins/generateTypes/schemas.d.ts +0 -18
  37. package/dist/src/plugins/generateTypes/schemas.js +0 -14
  38. package/dist/src/plugins/getConfigPath/index.d.ts +0 -15
  39. package/dist/src/plugins/getConfigPath/index.js +0 -19
  40. package/dist/src/plugins/getConfigPath/schemas.d.ts +0 -3
  41. package/dist/src/plugins/getConfigPath/schemas.js +0 -5
  42. package/src/plugins/generateTypes/index.ts +0 -444
  43. package/src/plugins/generateTypes/schemas.ts +0 -23
  44. package/src/plugins/getConfigPath/index.ts +0 -42
  45. package/src/plugins/getConfigPath/schemas.ts +0 -8
package/dist/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zapier/zapier-sdk-cli",
3
- "version": "0.8.4",
3
+ "version": "0.9.0",
4
4
  "description": "Command line interface for Zapier SDK",
5
5
  "main": "dist/index.cjs",
6
6
  "module": "dist/index.mjs",
@@ -0,0 +1,37 @@
1
+ import type { GetSdkType, ListActionsPluginProvides, ListInputFieldsPluginProvides, ManifestPluginProvides } from "@zapier/zapier-sdk";
2
+ interface GenerateTypesOptions {
3
+ appKey: string;
4
+ authenticationId?: number;
5
+ }
6
+ /**
7
+ * AST-based TypeScript type generator using the TypeScript Compiler API
8
+ */
9
+ export declare class AstTypeGenerator {
10
+ private readonly factory;
11
+ private readonly printer;
12
+ /**
13
+ * Generate TypeScript types using AST for a specific app
14
+ */
15
+ generateTypes(options: GenerateTypesOptions & {
16
+ sdk: GetSdkType<ListActionsPluginProvides & ListInputFieldsPluginProvides & ManifestPluginProvides>;
17
+ }): Promise<string>;
18
+ private parseAppIdentifier;
19
+ private createSourceFile;
20
+ private createImportStatement;
21
+ private createTypeImportStatement;
22
+ private groupActionsByType;
23
+ private createInputInterface;
24
+ private createActionInterface;
25
+ private createAppProxyInterface;
26
+ private createFetchMethodProperty;
27
+ private createAppFactoryInterface;
28
+ private createAppWithFactoryType;
29
+ private createModuleAugmentation;
30
+ private mapFieldTypeToTypeNode;
31
+ private generateEmptyTypesFile;
32
+ private capitalize;
33
+ private sanitizeActionName;
34
+ private sanitizeFieldName;
35
+ private escapeComment;
36
+ }
37
+ export {};
@@ -0,0 +1,403 @@
1
+ import * as ts from "typescript";
2
+ /**
3
+ * AST-based TypeScript type generator using the TypeScript Compiler API
4
+ */
5
+ export class AstTypeGenerator {
6
+ constructor() {
7
+ this.factory = ts.factory;
8
+ this.printer = ts.createPrinter({
9
+ newLine: ts.NewLineKind.LineFeed,
10
+ removeComments: false,
11
+ omitTrailingSemicolon: false,
12
+ });
13
+ }
14
+ /**
15
+ * Generate TypeScript types using AST for a specific app
16
+ */
17
+ async generateTypes(options) {
18
+ const { appKey, authenticationId, sdk } = options;
19
+ // Parse app identifier (support app@version format)
20
+ const { app, version } = this.parseAppIdentifier(appKey);
21
+ // Fetch all actions for the app
22
+ const actionsResult = await sdk.listActions({
23
+ appKey: app,
24
+ });
25
+ const actions = actionsResult.data;
26
+ if (actions.length === 0) {
27
+ return this.generateEmptyTypesFile(app, version);
28
+ }
29
+ // Fetch input fields for each action
30
+ const actionsWithFields = [];
31
+ if (authenticationId) {
32
+ for (const action of actions) {
33
+ try {
34
+ const fieldsResult = await sdk.listInputFields({
35
+ appKey: appKey,
36
+ actionKey: action.key,
37
+ actionType: action.action_type,
38
+ authenticationId: authenticationId,
39
+ });
40
+ const fields = fieldsResult.data.map((field) => {
41
+ const fieldObj = field;
42
+ return {
43
+ ...fieldObj,
44
+ required: fieldObj.is_required || fieldObj.required || false,
45
+ };
46
+ });
47
+ actionsWithFields.push({
48
+ ...action,
49
+ inputFields: fields,
50
+ name: action.title || action.key,
51
+ });
52
+ }
53
+ catch {
54
+ // If we can't get fields for an action, include it without fields
55
+ actionsWithFields.push({
56
+ ...action,
57
+ inputFields: [],
58
+ name: action.title || action.key,
59
+ });
60
+ }
61
+ }
62
+ }
63
+ else {
64
+ // Convert actions to have empty input fields (will generate generic types)
65
+ actions.forEach((action) => {
66
+ actionsWithFields.push({
67
+ ...action,
68
+ inputFields: [],
69
+ name: action.title || action.key,
70
+ app_key: action.app_key || appKey,
71
+ action_type: action.action_type || "write",
72
+ title: action.title || action.key,
73
+ type: "action",
74
+ description: action.description || "",
75
+ });
76
+ });
77
+ }
78
+ // Generate TypeScript AST nodes
79
+ const sourceFile = this.createSourceFile(app, actionsWithFields, version);
80
+ // Print the AST to string
81
+ return this.printer.printFile(sourceFile);
82
+ }
83
+ parseAppIdentifier(identifier) {
84
+ const parts = identifier.split("@");
85
+ return {
86
+ app: parts[0],
87
+ version: parts[1],
88
+ };
89
+ }
90
+ createSourceFile(appKey, actions, version) {
91
+ const appName = this.capitalize(appKey);
92
+ const versionComment = version
93
+ ? ` * Generated for ${appKey}@${version}`
94
+ : ` * Generated for ${appKey}`;
95
+ // Create header comment
96
+ const headerComment = `Auto-generated TypeScript types for Zapier ${appKey} actions
97
+ ${versionComment.slice(3)}
98
+ Generated on: ${new Date().toISOString()}
99
+
100
+ This file automatically augments the base SDK types when present.
101
+ No manual imports or type casting required.
102
+
103
+ Usage:
104
+ import { createZapierSdk } from "@zapier/zapier-sdk";
105
+
106
+ const zapier = createZapierSdk();
107
+ // Types are automatically available:
108
+ await zapier.apps.${appKey}.search.user_by_email({ authenticationId: 123, inputs: { email } })
109
+
110
+ // Factory usage (pinned auth):
111
+ const my${appName} = zapier.apps.${appKey}({ authenticationId: 123 })
112
+ await my${appName}.search.user_by_email({ inputs: { email } })`;
113
+ const statements = [
114
+ // Import the SDK to activate module augmentation
115
+ this.createImportStatement(["@zapier/zapier-sdk"]),
116
+ // Import types we'll use
117
+ this.createTypeImportStatement([
118
+ "ActionExecutionOptions",
119
+ "ActionExecutionResult",
120
+ "ZapierFetchInitOptions",
121
+ ], "@zapier/zapier-sdk"),
122
+ ];
123
+ // Group actions by type
124
+ const actionsByType = this.groupActionsByType(actions);
125
+ // Generate input interfaces for each action
126
+ actions.forEach((action) => {
127
+ if (action.inputFields.length > 0) {
128
+ const inputInterface = this.createInputInterface(appName, action);
129
+ statements.push(inputInterface);
130
+ }
131
+ });
132
+ // Generate action type interfaces
133
+ Object.entries(actionsByType).forEach(([actionType, typeActions]) => {
134
+ const actionInterface = this.createActionInterface(appName, actionType, typeActions);
135
+ statements.push(actionInterface);
136
+ });
137
+ // Generate app proxy interface
138
+ const appProxyInterface = this.createAppProxyInterface(appName, actionsByType);
139
+ statements.push(appProxyInterface);
140
+ // Generate app factory interface
141
+ const appFactoryInterface = this.createAppFactoryInterface(appName);
142
+ statements.push(appFactoryInterface);
143
+ // Generate combined app type
144
+ const appWithFactoryType = this.createAppWithFactoryType(appName);
145
+ statements.push(appWithFactoryType);
146
+ // Generate module augmentation for automatic type integration
147
+ const moduleAugmentation = this.createModuleAugmentation(appKey, appName);
148
+ statements.push(moduleAugmentation);
149
+ // Add empty export to make this a module
150
+ statements.push(this.factory.createExportDeclaration(undefined, false, this.factory.createNamedExports([])));
151
+ // Create source file
152
+ const sourceFile = ts.createSourceFile("generated.d.ts", "", ts.ScriptTarget.Latest, false, ts.ScriptKind.TS);
153
+ // Add the header comment and ESLint disable as leading trivia to the first statement
154
+ if (statements.length > 0) {
155
+ // Add ESLint disable comment
156
+ ts.addSyntheticLeadingComment(statements[0], ts.SyntaxKind.MultiLineCommentTrivia, " eslint-disable @typescript-eslint/naming-convention, @typescript-eslint/no-explicit-any ", true);
157
+ // Add header comment
158
+ ts.addSyntheticLeadingComment(statements[0], ts.SyntaxKind.MultiLineCommentTrivia, headerComment, true);
159
+ }
160
+ return this.factory.updateSourceFile(sourceFile, statements);
161
+ }
162
+ createImportStatement(imports, from) {
163
+ // Handle side-effect only import (no imports, just the module path)
164
+ if (imports.length === 1 && !from && imports[0].startsWith("@")) {
165
+ return this.factory.createImportDeclaration(undefined, undefined, this.factory.createStringLiteral(imports[0]), undefined);
166
+ }
167
+ const fromModule = from || imports[0];
168
+ const importNames = from ? imports : [];
169
+ return this.factory.createImportDeclaration(undefined, importNames.length > 0
170
+ ? this.factory.createImportClause(false, undefined, this.factory.createNamedImports(importNames.map((name) => this.factory.createImportSpecifier(false, undefined, this.factory.createIdentifier(name)))))
171
+ : undefined, this.factory.createStringLiteral(fromModule), undefined);
172
+ }
173
+ createTypeImportStatement(imports, from) {
174
+ return this.factory.createImportDeclaration(undefined, this.factory.createImportClause(true, // typeOnly: true
175
+ undefined, this.factory.createNamedImports(imports.map((name) => this.factory.createImportSpecifier(false, undefined, this.factory.createIdentifier(name))))), this.factory.createStringLiteral(from), undefined);
176
+ }
177
+ groupActionsByType(actions) {
178
+ return actions.reduce((acc, action) => {
179
+ if (!acc[action.action_type]) {
180
+ acc[action.action_type] = [];
181
+ }
182
+ acc[action.action_type].push(action);
183
+ return acc;
184
+ }, {});
185
+ }
186
+ createInputInterface(appName, action) {
187
+ const inputTypeName = `${appName}${this.capitalize(action.action_type)}${this.capitalize(this.sanitizeActionName(action.key))}Inputs`;
188
+ const properties = action.inputFields.map((field) => {
189
+ const fieldType = this.mapFieldTypeToTypeNode(field);
190
+ const isOptional = !field.required;
191
+ let property = this.factory.createPropertySignature(undefined, this.sanitizeFieldName(field.key), isOptional
192
+ ? this.factory.createToken(ts.SyntaxKind.QuestionToken)
193
+ : undefined, fieldType);
194
+ // Add JSDoc comment if helpText exists
195
+ if (field.helpText) {
196
+ property = ts.addSyntheticLeadingComment(property, ts.SyntaxKind.MultiLineCommentTrivia, `* ${this.escapeComment(field.helpText)} `, true);
197
+ }
198
+ return property;
199
+ });
200
+ return this.factory.createInterfaceDeclaration(undefined, inputTypeName, undefined, undefined, properties);
201
+ }
202
+ createActionInterface(appName, actionType, typeActions) {
203
+ const typeName = `${appName}${this.capitalize(actionType)}Actions`;
204
+ const methods = typeActions.map((action) => {
205
+ const actionName = this.sanitizeActionName(action.key);
206
+ let methodSignature;
207
+ if (action.inputFields.length > 0) {
208
+ // Generate type-safe action method signature
209
+ const inputTypeName = `${appName}${this.capitalize(action.action_type)}${this.capitalize(this.sanitizeActionName(action.key))}Inputs`;
210
+ const inputsType = this.factory.createTypeLiteralNode([
211
+ this.factory.createPropertySignature(undefined, "inputs", undefined, this.factory.createTypeReferenceNode(inputTypeName)),
212
+ ]);
213
+ const omitType = this.factory.createTypeReferenceNode("Omit", [
214
+ this.factory.createTypeReferenceNode("ActionExecutionOptions"),
215
+ this.factory.createLiteralTypeNode(this.factory.createStringLiteral("inputs")),
216
+ ]);
217
+ const optionsType = this.factory.createIntersectionTypeNode([
218
+ inputsType,
219
+ omitType,
220
+ ]);
221
+ methodSignature = this.factory.createMethodSignature(undefined, actionName, undefined, undefined, [
222
+ this.factory.createParameterDeclaration(undefined, undefined, "options", undefined, optionsType),
223
+ ], this.factory.createTypeReferenceNode("Promise", [
224
+ this.factory.createTypeReferenceNode("ActionExecutionResult"),
225
+ ]));
226
+ }
227
+ else {
228
+ // No specific input fields available - use generic Record<string, any> for inputs
229
+ const genericInputsType = this.factory.createTypeLiteralNode([
230
+ this.factory.createPropertySignature(undefined, "inputs", this.factory.createToken(ts.SyntaxKind.QuestionToken), this.factory.createTypeReferenceNode("Record", [
231
+ this.factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword),
232
+ this.factory.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword),
233
+ ])),
234
+ ]);
235
+ const intersectionType = this.factory.createIntersectionTypeNode([
236
+ genericInputsType,
237
+ this.factory.createTypeReferenceNode("ActionExecutionOptions"),
238
+ ]);
239
+ methodSignature = this.factory.createMethodSignature(undefined, actionName, undefined, undefined, [
240
+ this.factory.createParameterDeclaration(undefined, undefined, "options", this.factory.createToken(ts.SyntaxKind.QuestionToken), intersectionType),
241
+ ], this.factory.createTypeReferenceNode("Promise", [
242
+ this.factory.createTypeReferenceNode("ActionExecutionResult"),
243
+ ]));
244
+ }
245
+ // Add JSDoc comment if description exists
246
+ if (action.description) {
247
+ methodSignature = ts.addSyntheticLeadingComment(methodSignature, ts.SyntaxKind.MultiLineCommentTrivia, `* ${this.escapeComment(action.description)} `, true);
248
+ }
249
+ return methodSignature;
250
+ });
251
+ return this.factory.createInterfaceDeclaration(undefined, typeName, undefined, undefined, methods);
252
+ }
253
+ createAppProxyInterface(appName, actionsByType) {
254
+ const properties = [
255
+ ...Object.keys(actionsByType).map((actionType) => this.factory.createPropertySignature(undefined, actionType, undefined, this.factory.createTypeReferenceNode(`${appName}${this.capitalize(actionType)}Actions`))),
256
+ // Always include fetch method for authenticated HTTP requests
257
+ this.createFetchMethodProperty(),
258
+ ];
259
+ return this.factory.createInterfaceDeclaration(undefined, `${appName}AppProxy`, undefined, undefined, properties);
260
+ }
261
+ createFetchMethodProperty() {
262
+ let property = this.factory.createPropertySignature(undefined, "fetch", undefined, this.factory.createFunctionTypeNode(undefined, [
263
+ this.factory.createParameterDeclaration(undefined, undefined, "url", undefined, this.factory.createUnionTypeNode([
264
+ this.factory.createTypeReferenceNode("string"),
265
+ this.factory.createTypeReferenceNode("URL"),
266
+ ])),
267
+ this.factory.createParameterDeclaration(undefined, undefined, "init", this.factory.createToken(ts.SyntaxKind.QuestionToken), this.factory.createTypeReferenceNode("ZapierFetchInitOptions")),
268
+ ], this.factory.createTypeReferenceNode("Promise", [
269
+ this.factory.createTypeReferenceNode("Response"),
270
+ ])));
271
+ // Add JSDoc comment
272
+ property = ts.addSyntheticLeadingComment(property, ts.SyntaxKind.MultiLineCommentTrivia, "* Make authenticated HTTP requests through Zapier's Relay service ", true);
273
+ return property;
274
+ }
275
+ createAppFactoryInterface(appName) {
276
+ const callSignature = this.factory.createCallSignature(undefined, [
277
+ this.factory.createParameterDeclaration(undefined, undefined, "options", undefined, this.factory.createTypeLiteralNode([
278
+ this.factory.createPropertySignature(undefined, "authenticationId", undefined, this.factory.createKeywordTypeNode(ts.SyntaxKind.NumberKeyword)),
279
+ ])),
280
+ ], this.factory.createTypeReferenceNode(`${appName}AppProxy`));
281
+ return this.factory.createInterfaceDeclaration(undefined, `${appName}AppFactory`, undefined, undefined, [callSignature]);
282
+ }
283
+ createAppWithFactoryType(appName) {
284
+ return this.factory.createTypeAliasDeclaration(undefined, `${appName}AppWithFactory`, undefined, this.factory.createIntersectionTypeNode([
285
+ this.factory.createTypeReferenceNode(`${appName}AppFactory`),
286
+ this.factory.createTypeReferenceNode(`${appName}AppProxy`),
287
+ ]));
288
+ }
289
+ createModuleAugmentation(appKey, appName) {
290
+ // Create: declare module "@zapier/zapier-sdk" { interface ZapierSdkApps { [appKey]: AppWithFactory } }
291
+ // This creates a new interface that we can merge with ZapierSdk
292
+ return this.factory.createModuleDeclaration([this.factory.createToken(ts.SyntaxKind.DeclareKeyword)], this.factory.createStringLiteral("@zapier/zapier-sdk"), this.factory.createModuleBlock([
293
+ this.factory.createInterfaceDeclaration(undefined, "ZapierSdkApps", undefined, undefined, [
294
+ this.factory.createPropertySignature(undefined, appKey, undefined, this.factory.createTypeReferenceNode(`${appName}AppWithFactory`)),
295
+ ]),
296
+ ]));
297
+ }
298
+ mapFieldTypeToTypeNode(field) {
299
+ // Handle choices (enum-like fields)
300
+ if (field.choices && field.choices.length > 0) {
301
+ const choiceValues = field.choices
302
+ .filter((choice) => choice.value !== undefined &&
303
+ choice.value !== null &&
304
+ choice.value !== "")
305
+ .map((choice) => typeof choice.value === "string"
306
+ ? this.factory.createLiteralTypeNode(this.factory.createStringLiteral(choice.value))
307
+ : this.factory.createLiteralTypeNode(this.factory.createNumericLiteral(String(choice.value))));
308
+ if (choiceValues.length > 0) {
309
+ return this.factory.createUnionTypeNode(choiceValues);
310
+ }
311
+ }
312
+ // Map Zapier field types to TypeScript types
313
+ switch (field.type?.toLowerCase()) {
314
+ case "string":
315
+ case "text":
316
+ case "email":
317
+ case "url":
318
+ case "password":
319
+ case "datetime":
320
+ case "date":
321
+ case "file":
322
+ return this.factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword);
323
+ case "integer":
324
+ case "number":
325
+ return this.factory.createKeywordTypeNode(ts.SyntaxKind.NumberKeyword);
326
+ case "boolean":
327
+ return this.factory.createKeywordTypeNode(ts.SyntaxKind.BooleanKeyword);
328
+ case "array":
329
+ return this.factory.createArrayTypeNode(this.factory.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword));
330
+ case "object":
331
+ return this.factory.createTypeReferenceNode("Record", [
332
+ this.factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword),
333
+ this.factory.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword),
334
+ ]);
335
+ default:
336
+ // Default to string | number | boolean for unknown types
337
+ return this.factory.createUnionTypeNode([
338
+ this.factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword),
339
+ this.factory.createKeywordTypeNode(ts.SyntaxKind.NumberKeyword),
340
+ this.factory.createKeywordTypeNode(ts.SyntaxKind.BooleanKeyword),
341
+ ]);
342
+ }
343
+ }
344
+ generateEmptyTypesFile(appKey, version) {
345
+ const appName = this.capitalize(appKey);
346
+ const versionComment = version
347
+ ? ` * Generated for ${appKey}@${version}`
348
+ : ` * Generated for ${appKey}`;
349
+ return `/* eslint-disable @typescript-eslint/naming-convention, @typescript-eslint/no-explicit-any */
350
+ /**
351
+ * Auto-generated TypeScript types for Zapier ${appKey} actions
352
+ ${versionComment}
353
+ * Generated on: ${new Date().toISOString()}
354
+ *
355
+ * No actions found for this app.
356
+ */
357
+
358
+ import type { ActionExecutionOptions, ActionExecutionResult, ZapierFetchInitOptions } from '@zapier/zapier-sdk'
359
+
360
+ interface ${appName}AppProxy {
361
+ /** Make authenticated HTTP requests through Zapier's Relay service */
362
+ fetch: (url: string | URL, init?: ZapierFetchInitOptions) => Promise<Response>
363
+ }
364
+
365
+ interface ${appName}AppFactory {
366
+ (options: { authenticationId: number }): ${appName}AppProxy
367
+ }
368
+
369
+ type ${appName}AppWithFactory = ${appName}AppFactory & ${appName}AppProxy
370
+
371
+ declare module "@zapier/zapier-sdk" {
372
+ interface ZapierSdkApps {
373
+ ${appKey}: ${appName}AppWithFactory
374
+ }
375
+ }
376
+ `;
377
+ }
378
+ capitalize(str) {
379
+ return str.charAt(0).toUpperCase() + str.slice(1).replace(/[-_]/g, "");
380
+ }
381
+ sanitizeActionName(actionKey) {
382
+ // Ensure the action name is a valid TypeScript identifier
383
+ let sanitized = actionKey.replace(/[^a-zA-Z0-9_$]/g, "_");
384
+ // If it starts with a number, prepend an underscore
385
+ if (/^[0-9]/.test(sanitized)) {
386
+ sanitized = "_" + sanitized;
387
+ }
388
+ return sanitized;
389
+ }
390
+ sanitizeFieldName(fieldKey) {
391
+ // Ensure the field name is a valid TypeScript identifier
392
+ let sanitized = fieldKey.replace(/[^a-zA-Z0-9_$]/g, "_");
393
+ // If it starts with a number, prepend an underscore
394
+ if (/^[0-9]/.test(sanitized)) {
395
+ sanitized = "_" + sanitized;
396
+ }
397
+ return sanitized;
398
+ }
399
+ escapeComment(comment) {
400
+ // Escape comment text to prevent breaking the JSDoc comment
401
+ return comment.replace(/\*\//g, "*\\/").replace(/\r?\n/g, " ");
402
+ }
403
+ }
@@ -0,0 +1,13 @@
1
+ import type { Plugin, GetSdkType, GetContextType, ListActionsPluginProvides, ListAppsPluginProvides, ListInputFieldsPluginProvides, ListAuthenticationsPluginProvides, ManifestPluginProvides } from "@zapier/zapier-sdk";
2
+ import { AddSchema, type AddOptions } from "./schemas";
3
+ export interface AddPluginProvides {
4
+ add: (options: AddOptions) => Promise<void>;
5
+ context: {
6
+ meta: {
7
+ add: {
8
+ inputSchema: typeof AddSchema;
9
+ };
10
+ };
11
+ };
12
+ }
13
+ export declare const addPlugin: Plugin<GetSdkType<ListAppsPluginProvides & ListActionsPluginProvides & ListInputFieldsPluginProvides & ListAuthenticationsPluginProvides & ManifestPluginProvides>, GetContextType<ManifestPluginProvides>, AddPluginProvides>;
@@ -0,0 +1,122 @@
1
+ import { createFunction } from "@zapier/zapier-sdk";
2
+ import { AddSchema } from "./schemas";
3
+ import { AstTypeGenerator } from "./ast-generator";
4
+ import { mkdir, access, writeFile } from "fs/promises";
5
+ import { resolve, join } from "path";
6
+ /**
7
+ * Detect the best default directory for TypeScript types
8
+ * Looks for src or lib directories, falls back to current directory
9
+ */
10
+ async function detectTypesOutputDirectory() {
11
+ // Check for common source directories in priority order
12
+ const candidates = ["src", "lib"];
13
+ for (const candidate of candidates) {
14
+ try {
15
+ await access(candidate);
16
+ return join(candidate, "zapier", "apps");
17
+ }
18
+ catch {
19
+ // Directory doesn't exist, continue to next candidate
20
+ }
21
+ }
22
+ // Fall back to current directory
23
+ return "./zapier/apps/";
24
+ }
25
+ export const addPlugin = ({ sdk, context }) => {
26
+ const add = createFunction(async function add(options) {
27
+ const { appKeys, authenticationIds, configPath, typesOutput = await detectTypesOutputDirectory(), } = options;
28
+ const resolvedTypesOutput = resolve(typesOutput);
29
+ // Ensure types output directory exists
30
+ await mkdir(resolvedTypesOutput, { recursive: true });
31
+ // Get apps using listApps (which respects existing manifest for version locking)
32
+ console.log(`📦 Looking up ${appKeys.length} app(s)...`);
33
+ const appsIterator = sdk.listApps({ appKeys }).items();
34
+ const apps = [];
35
+ for await (const app of appsIterator) {
36
+ apps.push(app);
37
+ }
38
+ if (apps.length === 0) {
39
+ console.warn("⚠️ No apps found");
40
+ return;
41
+ }
42
+ // Fetch authentications if provided
43
+ let authentications = [];
44
+ if (authenticationIds && authenticationIds.length > 0) {
45
+ console.log(`🔐 Looking up ${authenticationIds.length} authentication(s)...`);
46
+ const authsIterator = sdk
47
+ .listAuthentications({ authenticationIds })
48
+ .items();
49
+ for await (const auth of authsIterator) {
50
+ authentications.push(auth);
51
+ }
52
+ console.log(`🔐 Found ${authentications.length} authentication(s)`);
53
+ }
54
+ // Process each app
55
+ for (const app of apps) {
56
+ console.log(`📦 Adding ${app.key}...`);
57
+ try {
58
+ // Extract implementation name and version from current_implementation_id
59
+ const currentImplementationId = app.current_implementation_id;
60
+ const [implementationName, version] = currentImplementationId.split("@");
61
+ if (!implementationName || !version) {
62
+ console.warn(`⚠️ Invalid implementation ID format for '${app.key}': ${currentImplementationId}. Expected format: <implementationName>@<version>. Skipping...`);
63
+ continue;
64
+ }
65
+ // Update manifest using manifest plugin's function
66
+ const [manifestKey] = await context.updateManifestEntry(app.key, {
67
+ implementationName,
68
+ version,
69
+ }, configPath);
70
+ console.log(`📝 Locked ${app.key} to ${implementationName}@${version} using key '${manifestKey}'`);
71
+ // Find matching authentication for this app if authentications were provided
72
+ let authenticationId;
73
+ if (authentications.length > 0) {
74
+ // Match authentication by app_key
75
+ const matchingAuth = authentications.find((auth) => {
76
+ return auth.app_key === app.key;
77
+ });
78
+ if (matchingAuth) {
79
+ authenticationId = matchingAuth.id;
80
+ console.log(`🔐 Using authentication ${authenticationId} (${matchingAuth.title}) for ${app.key}`);
81
+ }
82
+ else {
83
+ console.warn(`⚠️ No matching authentication found for ${app.key}`);
84
+ }
85
+ }
86
+ // Generate types using the manifest key for consistency
87
+ const typesPath = join(resolvedTypesOutput, `${manifestKey}.d.ts`);
88
+ try {
89
+ // Use AST-based generator directly
90
+ const generator = new AstTypeGenerator();
91
+ const typeDefinitions = await generator.generateTypes({
92
+ appKey: manifestKey,
93
+ authenticationId,
94
+ sdk,
95
+ });
96
+ // Write to file
97
+ await writeFile(typesPath, typeDefinitions, "utf8");
98
+ console.log(`🔧 Generated types for ${manifestKey} at ${typesPath}`);
99
+ }
100
+ catch (error) {
101
+ console.warn(`⚠️ Failed to generate types for ${app.key}: ${error}`);
102
+ // Continue even if type generation fails
103
+ }
104
+ }
105
+ catch (error) {
106
+ console.warn(`⚠️ Failed to process ${app.key}: ${error}`);
107
+ }
108
+ }
109
+ console.log(`✅ Added ${apps.length} app(s) to manifest`);
110
+ }, AddSchema);
111
+ return {
112
+ add,
113
+ context: {
114
+ meta: {
115
+ add: {
116
+ categories: ["utility"],
117
+ inputSchema: AddSchema,
118
+ },
119
+ },
120
+ },
121
+ };
122
+ };
@@ -0,0 +1,18 @@
1
+ import { z } from "zod";
2
+ export declare const AddSchema: z.ZodObject<{
3
+ appKeys: z.ZodArray<z.ZodString, "many">;
4
+ authenticationIds: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
5
+ configPath: z.ZodOptional<z.ZodString>;
6
+ typesOutput: z.ZodOptional<z.ZodString>;
7
+ }, "strip", z.ZodTypeAny, {
8
+ appKeys: string[];
9
+ authenticationIds?: string[] | undefined;
10
+ configPath?: string | undefined;
11
+ typesOutput?: string | undefined;
12
+ }, {
13
+ appKeys: string[];
14
+ authenticationIds?: string[] | undefined;
15
+ configPath?: string | undefined;
16
+ typesOutput?: string | undefined;
17
+ }>;
18
+ export type AddOptions = z.infer<typeof AddSchema>;
@@ -0,0 +1,19 @@
1
+ import { z } from "zod";
2
+ import { DEFAULT_CONFIG_PATH } from "@zapier/zapier-sdk";
3
+ export const AddSchema = z.object({
4
+ appKeys: z
5
+ .array(z.string().min(1, "App key cannot be empty"))
6
+ .min(1, "At least one app key is required"),
7
+ authenticationIds: z
8
+ .array(z.string())
9
+ .optional()
10
+ .describe("Authentication IDs to use for type generation"),
11
+ configPath: z
12
+ .string()
13
+ .optional()
14
+ .describe(`Path to Zapier config file (defaults to '${DEFAULT_CONFIG_PATH}')`),
15
+ typesOutput: z
16
+ .string()
17
+ .optional()
18
+ .describe("Directory for TypeScript type files (defaults to (src|lib|.)/zapier/apps/)"),
19
+ });
@@ -0,0 +1,15 @@
1
+ import type { Plugin } from "@zapier/zapier-sdk";
2
+ import { GetLoginConfigPathSchema, type GetLoginConfigPathOptions } from "./schemas";
3
+ export interface GetLoginConfigPathPluginProvides {
4
+ getLoginConfigPath: (options?: GetLoginConfigPathOptions) => Promise<string>;
5
+ context: {
6
+ meta: {
7
+ getLoginConfigPath: {
8
+ inputSchema: typeof GetLoginConfigPathSchema;
9
+ };
10
+ };
11
+ };
12
+ }
13
+ export declare const getLoginConfigPathPlugin: Plugin<{}, // requires no existing SDK methods
14
+ {}, // requires no context
15
+ GetLoginConfigPathPluginProvides>;
@@ -0,0 +1,19 @@
1
+ import { GetLoginConfigPathSchema, } from "./schemas";
2
+ import { createFunction } from "@zapier/zapier-sdk";
3
+ import { getConfigPath } from "@zapier/zapier-sdk-cli-login";
4
+ export const getLoginConfigPathPlugin = () => {
5
+ const getLoginConfigPathWithSdk = createFunction(async function getLoginConfigPathWithSdk(_options) {
6
+ return getConfigPath();
7
+ }, GetLoginConfigPathSchema);
8
+ return {
9
+ getLoginConfigPath: getLoginConfigPathWithSdk,
10
+ context: {
11
+ meta: {
12
+ getLoginConfigPath: {
13
+ categories: ["utility"],
14
+ inputSchema: GetLoginConfigPathSchema,
15
+ },
16
+ },
17
+ },
18
+ };
19
+ };
@@ -0,0 +1,3 @@
1
+ import { z } from "zod";
2
+ export declare const GetLoginConfigPathSchema: z.ZodObject<{}, "strip", z.ZodTypeAny, {}, {}>;
3
+ export type GetLoginConfigPathOptions = z.infer<typeof GetLoginConfigPathSchema>;