@zapier/zapier-sdk-cli 0.16.1 → 0.16.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 (60) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/dist/cli.cjs +7 -7
  3. package/dist/cli.mjs +7 -7
  4. package/dist/index.cjs +1 -1
  5. package/dist/index.mjs +1 -1
  6. package/dist/package.json +8 -2
  7. package/dist/src/cli.js +2 -1
  8. package/dist/src/utils/cli-generator.js +8 -7
  9. package/dist/tsconfig.tsbuildinfo +1 -1
  10. package/package.json +11 -5
  11. package/src/cli.test.ts +0 -28
  12. package/src/cli.ts +0 -96
  13. package/src/generators/ast-generator.test.ts +0 -908
  14. package/src/generators/ast-generator.ts +0 -774
  15. package/src/index.ts +0 -12
  16. package/src/plugins/add/index.test.ts +0 -58
  17. package/src/plugins/add/index.ts +0 -177
  18. package/src/plugins/add/schemas.ts +0 -35
  19. package/src/plugins/buildManifest/index.test.ts +0 -679
  20. package/src/plugins/buildManifest/index.ts +0 -131
  21. package/src/plugins/buildManifest/schemas.ts +0 -55
  22. package/src/plugins/bundleCode/index.ts +0 -128
  23. package/src/plugins/bundleCode/schemas.ts +0 -24
  24. package/src/plugins/generateAppTypes/index.test.ts +0 -679
  25. package/src/plugins/generateAppTypes/index.ts +0 -227
  26. package/src/plugins/generateAppTypes/schemas.ts +0 -61
  27. package/src/plugins/getLoginConfigPath/index.ts +0 -45
  28. package/src/plugins/getLoginConfigPath/schemas.ts +0 -10
  29. package/src/plugins/index.ts +0 -8
  30. package/src/plugins/login/index.ts +0 -135
  31. package/src/plugins/login/schemas.ts +0 -13
  32. package/src/plugins/logout/index.ts +0 -37
  33. package/src/plugins/logout/schemas.ts +0 -8
  34. package/src/plugins/mcp/index.ts +0 -43
  35. package/src/plugins/mcp/schemas.ts +0 -13
  36. package/src/sdk.ts +0 -45
  37. package/src/telemetry/builders.ts +0 -113
  38. package/src/telemetry/events.ts +0 -39
  39. package/src/types/sdk.ts +0 -8
  40. package/src/utils/api/client.ts +0 -44
  41. package/src/utils/auth/login.ts +0 -214
  42. package/src/utils/cli-generator-utils.ts +0 -169
  43. package/src/utils/cli-generator.test.ts +0 -347
  44. package/src/utils/cli-generator.ts +0 -807
  45. package/src/utils/constants.ts +0 -9
  46. package/src/utils/directory-detection.ts +0 -23
  47. package/src/utils/errors.ts +0 -26
  48. package/src/utils/getCallablePromise.ts +0 -21
  49. package/src/utils/log.ts +0 -23
  50. package/src/utils/manifest-helpers.ts +0 -25
  51. package/src/utils/package-manager-detector.ts +0 -83
  52. package/src/utils/parameter-resolver.ts +0 -1075
  53. package/src/utils/schema-formatter.ts +0 -153
  54. package/src/utils/serializeAsync.ts +0 -26
  55. package/src/utils/spinner.ts +0 -23
  56. package/src/utils/version-checker.test.ts +0 -239
  57. package/src/utils/version-checker.ts +0 -237
  58. package/tsconfig.build.json +0 -18
  59. package/tsconfig.json +0 -19
  60. package/tsup.config.ts +0 -23
@@ -1,908 +0,0 @@
1
- import { describe, it, expect, vi, beforeEach } from "vitest";
2
- import type { MockInstance } from "vitest";
3
- import { AstTypeGenerator } from "./ast-generator";
4
- import type { ActionItem, InputFieldItem, AppItem } from "@zapier/zapier-sdk";
5
-
6
- interface MockSdk {
7
- listActions: MockInstance;
8
- listInputFields: MockInstance;
9
- getContext: MockInstance;
10
- }
11
-
12
- interface MockContext {
13
- api: {
14
- batch: MockInstance;
15
- };
16
- }
17
-
18
- type SdkType = Parameters<
19
- typeof AstTypeGenerator.prototype.generateTypes
20
- >[0]["sdk"];
21
-
22
- // Helper to create InputFieldItem with all required fields
23
- function createInputFieldItem(
24
- partial: Partial<InputFieldItem> & { key: string; value_type: string },
25
- ): InputFieldItem {
26
- return {
27
- type: "input_field",
28
- key: partial.key,
29
- title: partial.title || "",
30
- value_type: partial.value_type,
31
- is_required: partial.is_required ?? false,
32
- description: partial.description || "",
33
- depends_on: partial.depends_on || [],
34
- placeholder: partial.placeholder || "",
35
- default_value: partial.default_value || "",
36
- invalidates_input_fields: partial.invalidates_input_fields ?? false,
37
- ...partial,
38
- };
39
- }
40
-
41
- describe("AstTypeGenerator", () => {
42
- let generator: AstTypeGenerator;
43
- let mockSdk: MockSdk;
44
- let mockContext: MockContext;
45
- let consoleWarnSpy: MockInstance;
46
-
47
- const mockApp: AppItem = {
48
- key: "test-app",
49
- slug: "test-app",
50
- implementation_id: "test-app@1.0.0",
51
- title: "Test App",
52
- description: "Test app description",
53
- };
54
-
55
- beforeEach(() => {
56
- generator = new AstTypeGenerator();
57
- consoleWarnSpy = vi.spyOn(console, "warn").mockImplementation(() => {});
58
-
59
- mockSdk = {
60
- listActions: vi.fn(),
61
- listInputFields: vi.fn(),
62
- getContext: vi.fn(),
63
- };
64
-
65
- // Mock context with batch API that mimics Promise.allSettled behavior
66
- mockContext = {
67
- api: {
68
- batch: vi.fn(async (tasks) => {
69
- const results = await Promise.allSettled(
70
- tasks.map((task: () => Promise<unknown>) => task()),
71
- );
72
- return results;
73
- }),
74
- },
75
- };
76
- });
77
-
78
- describe("generateTypes", () => {
79
- it("should generate types with all input fields successfully resolved", async () => {
80
- const mockActions: ActionItem[] = [
81
- {
82
- key: "search_user",
83
- title: "Search User",
84
- action_type: "search",
85
- app_key: "test-app",
86
- description: "Search for a user",
87
- type: "action",
88
- },
89
- {
90
- key: "create_task",
91
- title: "Create Task",
92
- action_type: "write",
93
- app_key: "test-app",
94
- description: "Create a new task",
95
- type: "action",
96
- },
97
- ];
98
-
99
- const mockInputFields: Record<string, InputFieldItem[]> = {
100
- search_user: [
101
- {
102
- key: "email",
103
- title: "Email",
104
- value_type: "string",
105
- is_required: true,
106
- description: "User email address",
107
- type: "input_field",
108
- depends_on: [],
109
- placeholder: "",
110
- default_value: "",
111
- invalidates_input_fields: false,
112
- },
113
- {
114
- key: "age",
115
- title: "Age",
116
- value_type: "integer",
117
- is_required: false,
118
- description: "User age",
119
- type: "input_field",
120
- depends_on: [],
121
- placeholder: "",
122
- default_value: "",
123
- invalidates_input_fields: false,
124
- },
125
- ],
126
- create_task: [
127
- {
128
- key: "title",
129
- title: "Title",
130
- value_type: "string",
131
- is_required: true,
132
- description: "Task title",
133
- type: "input_field",
134
- depends_on: [],
135
- placeholder: "",
136
- default_value: "",
137
- invalidates_input_fields: false,
138
- },
139
- {
140
- key: "completed",
141
- title: "Completed",
142
- value_type: "boolean",
143
- is_required: false,
144
- description: "Task completion status",
145
- type: "input_field",
146
- depends_on: [],
147
- placeholder: "",
148
- default_value: "",
149
- invalidates_input_fields: false,
150
- },
151
- ],
152
- };
153
-
154
- mockSdk.listActions.mockResolvedValue({ data: mockActions });
155
- mockSdk.listInputFields.mockImplementation(
156
- ({ actionKey }: { actionKey: string }) =>
157
- Promise.resolve({ data: mockInputFields[actionKey] }),
158
- );
159
-
160
- const result = await generator.generateTypes({
161
- app: mockApp,
162
- authenticationId: 123,
163
- sdk: mockSdk as unknown as SdkType,
164
- context: mockContext as Parameters<
165
- typeof generator.generateTypes
166
- >[0]["context"],
167
- });
168
-
169
- expect(result).toContain("TestAppSearchActions");
170
- expect(result).toContain("TestAppWriteActions");
171
- expect(result).toContain("TestAppSearchSearchuserInputs");
172
- expect(result).toContain("TestAppWriteCreatetaskInputs");
173
- expect(result).toContain("email");
174
- expect(result).toContain("age?");
175
- expect(result).toContain("title");
176
- expect(result).toContain("completed?");
177
- expect(result).toContain("User email address");
178
- expect(result).toContain("Task completion status");
179
- expect(consoleWarnSpy).not.toHaveBeenCalled();
180
- });
181
-
182
- it("should handle failed HTTP requests and log warnings", async () => {
183
- const mockActions: ActionItem[] = [
184
- {
185
- key: "search_user",
186
- title: "Search User",
187
- action_type: "search",
188
- app_key: "test-app",
189
- description: "Search for a user",
190
- type: "action",
191
- },
192
- {
193
- key: "create_task",
194
- title: "Create Task",
195
- action_type: "write",
196
- app_key: "test-app",
197
- description: "Create a new task",
198
- type: "action",
199
- },
200
- {
201
- key: "delete_task",
202
- title: "Delete Task",
203
- action_type: "write",
204
- app_key: "test-app",
205
- description: "Delete a task",
206
- type: "action",
207
- },
208
- ];
209
-
210
- mockSdk.listActions.mockResolvedValue({ data: mockActions });
211
- mockSdk.listInputFields.mockImplementation(
212
- ({ actionKey }: { actionKey: string }) => {
213
- if (actionKey === "search_user") {
214
- return Promise.resolve({
215
- data: [
216
- createInputFieldItem({
217
- key: "email",
218
- title: "Email",
219
- value_type: "string",
220
- is_required: true,
221
- }),
222
- ],
223
- });
224
- } else if (actionKey === "create_task") {
225
- return Promise.reject(new Error("Network timeout"));
226
- } else {
227
- return Promise.reject(new Error("Authentication failed"));
228
- }
229
- },
230
- );
231
-
232
- const result = await generator.generateTypes({
233
- app: mockApp,
234
- authenticationId: 123,
235
- sdk: mockSdk as unknown as SdkType,
236
- context: mockContext as Parameters<
237
- typeof generator.generateTypes
238
- >[0]["context"],
239
- });
240
-
241
- expect(result).toContain("TestAppSearchActions");
242
- expect(result).toContain("TestAppWriteActions");
243
- expect(result).toContain("TestAppSearchSearchuserInputs");
244
- expect(result).toContain("email");
245
-
246
- expect(consoleWarnSpy).toHaveBeenCalledWith(
247
- "Failed to fetch input fields for 2 action(s):",
248
- );
249
- expect(consoleWarnSpy).toHaveBeenCalledWith(
250
- " - create_task (write): Network timeout",
251
- );
252
- expect(consoleWarnSpy).toHaveBeenCalledWith(
253
- " - delete_task (write): Authentication failed",
254
- );
255
- });
256
-
257
- it("should fetch input fields even when authenticationId is not provided", async () => {
258
- const mockActions: ActionItem[] = [
259
- {
260
- key: "search_user",
261
- title: "Search User",
262
- action_type: "search",
263
- app_key: "test-app",
264
- description: "Search for a user",
265
- type: "action",
266
- },
267
- ];
268
-
269
- const mockInputFields: InputFieldItem[] = [
270
- createInputFieldItem({
271
- key: "email",
272
- title: "Email",
273
- value_type: "string",
274
- is_required: true,
275
- description: "User email",
276
- }),
277
- ];
278
-
279
- mockSdk.listActions.mockResolvedValue({ data: mockActions });
280
- mockSdk.listInputFields.mockResolvedValue({ data: mockInputFields });
281
-
282
- const result = await generator.generateTypes({
283
- app: mockApp,
284
- sdk: mockSdk as unknown as SdkType,
285
- context: mockContext as Parameters<
286
- typeof generator.generateTypes
287
- >[0]["context"],
288
- });
289
-
290
- expect(result).toContain("TestAppSearchActions");
291
- expect(result).toContain("search_user");
292
- expect(result).toContain("email: string");
293
- expect(mockSdk.listInputFields).toHaveBeenCalledWith({
294
- appKey: "test-app@1.0.0",
295
- actionKey: "search_user",
296
- actionType: "search",
297
- authenticationId: undefined,
298
- });
299
- expect(consoleWarnSpy).not.toHaveBeenCalled();
300
- });
301
-
302
- it("should include auth-specific input fields only when authenticationId is provided", async () => {
303
- const mockActions: ActionItem[] = [
304
- {
305
- key: "create_task",
306
- title: "Create Task",
307
- action_type: "write",
308
- app_key: "test-app",
309
- description: "Create a task",
310
- type: "action",
311
- },
312
- ];
313
-
314
- mockSdk.listActions.mockResolvedValue({ data: mockActions });
315
-
316
- // Mock listInputFields to return different fields based on authenticationId
317
- mockSdk.listInputFields.mockImplementation(
318
- ({ authenticationId }: { authenticationId?: number }) => {
319
- if (authenticationId === 123) {
320
- // With authentication, return personalized fields
321
- return Promise.resolve({
322
- data: [
323
- createInputFieldItem({
324
- key: "title",
325
- title: "Title",
326
- value_type: "string",
327
- is_required: true,
328
- description: "Task title",
329
- }),
330
- createInputFieldItem({
331
- key: "assigned_user",
332
- title: "Assigned User",
333
- value_type: "string",
334
- is_required: false,
335
- description: "User from your account to assign",
336
- }),
337
- createInputFieldItem({
338
- key: "project_id",
339
- title: "Project",
340
- value_type: "string",
341
- is_required: false,
342
- description: "Project from your workspace",
343
- }),
344
- ],
345
- });
346
- } else {
347
- // Without authentication, return only basic fields
348
- return Promise.resolve({
349
- data: [
350
- createInputFieldItem({
351
- key: "title",
352
- title: "Title",
353
- value_type: "string",
354
- is_required: true,
355
- description: "Task title",
356
- }),
357
- ],
358
- });
359
- }
360
- },
361
- );
362
-
363
- // Generate types without authenticationId
364
- const resultWithoutAuth = await generator.generateTypes({
365
- app: mockApp,
366
- sdk: mockSdk as unknown as SdkType,
367
- context: mockContext as Parameters<
368
- typeof generator.generateTypes
369
- >[0]["context"],
370
- });
371
-
372
- // Should contain basic field
373
- expect(resultWithoutAuth).toContain("title: string");
374
- // Should NOT contain auth-specific fields
375
- expect(resultWithoutAuth).not.toContain("assigned_user");
376
- expect(resultWithoutAuth).not.toContain("project_id");
377
- expect(resultWithoutAuth).not.toContain("User from your account");
378
- expect(resultWithoutAuth).not.toContain("Project from your workspace");
379
-
380
- // Generate types with authenticationId
381
- const resultWithAuth = await generator.generateTypes({
382
- app: mockApp,
383
- authenticationId: 123,
384
- sdk: mockSdk as unknown as SdkType,
385
- context: mockContext as Parameters<
386
- typeof generator.generateTypes
387
- >[0]["context"],
388
- });
389
-
390
- // Should contain basic field
391
- expect(resultWithAuth).toContain("title: string");
392
- // Should contain auth-specific fields
393
- expect(resultWithAuth).toContain("assigned_user?: string");
394
- expect(resultWithAuth).toContain("project_id?: string");
395
- expect(resultWithAuth).toContain("User from your account");
396
- expect(resultWithAuth).toContain("Project from your workspace");
397
-
398
- // Verify the SDK was called with correct parameters
399
- expect(mockSdk.listInputFields).toHaveBeenCalledWith({
400
- appKey: "test-app@1.0.0",
401
- actionKey: "create_task",
402
- actionType: "write",
403
- authenticationId: undefined,
404
- });
405
-
406
- expect(mockSdk.listInputFields).toHaveBeenCalledWith({
407
- appKey: "test-app@1.0.0",
408
- actionKey: "create_task",
409
- actionType: "write",
410
- authenticationId: 123,
411
- });
412
-
413
- expect(consoleWarnSpy).not.toHaveBeenCalled();
414
- });
415
-
416
- it("should handle empty actions list", async () => {
417
- mockSdk.listActions.mockResolvedValue({ data: [] });
418
-
419
- const result = await generator.generateTypes({
420
- app: mockApp,
421
- authenticationId: 123,
422
- sdk: mockSdk as unknown as SdkType,
423
- context: mockContext as Parameters<
424
- typeof generator.generateTypes
425
- >[0]["context"],
426
- });
427
-
428
- expect(result).toContain("import");
429
- expect(result).toContain("@zapier/zapier-sdk");
430
- expect(result).toContain("declare module");
431
- expect(consoleWarnSpy).not.toHaveBeenCalled();
432
- });
433
-
434
- it("should handle all field types correctly", async () => {
435
- const mockActions: ActionItem[] = [
436
- {
437
- key: "test_action",
438
- title: "Test Action",
439
- action_type: "write",
440
- app_key: "test-app",
441
- description: "Test all field types",
442
- type: "action",
443
- },
444
- ];
445
-
446
- const mockInputFields: InputFieldItem[] = [
447
- createInputFieldItem({
448
- key: "string_field",
449
- title: "String Field",
450
- value_type: "string",
451
- is_required: true,
452
- }),
453
- createInputFieldItem({
454
- key: "number_field",
455
- title: "Number Field",
456
- value_type: "number",
457
- is_required: true,
458
- }),
459
- createInputFieldItem({
460
- key: "integer_field",
461
- title: "Integer Field",
462
- value_type: "integer",
463
- is_required: true,
464
- }),
465
- createInputFieldItem({
466
- key: "boolean_field",
467
- title: "Boolean Field",
468
- value_type: "boolean",
469
- is_required: true,
470
- }),
471
- createInputFieldItem({
472
- key: "array_field",
473
- title: "Array Field",
474
- value_type: "array",
475
- is_required: false,
476
- }),
477
- createInputFieldItem({
478
- key: "object_field",
479
- title: "Object Field",
480
- value_type: "object",
481
- is_required: false,
482
- }),
483
- createInputFieldItem({
484
- key: "email_field",
485
- title: "Email Field",
486
- value_type: "email",
487
- is_required: false,
488
- }),
489
- createInputFieldItem({
490
- key: "url_field",
491
- title: "URL Field",
492
- value_type: "url",
493
- is_required: false,
494
- }),
495
- createInputFieldItem({
496
- key: "datetime_field",
497
- title: "DateTime Field",
498
- value_type: "datetime",
499
- is_required: false,
500
- }),
501
- createInputFieldItem({
502
- key: "unknown_field",
503
- title: "Unknown Field",
504
- value_type: "custom_type" as InputFieldItem["value_type"],
505
- is_required: false,
506
- }),
507
- ];
508
-
509
- mockSdk.listActions.mockResolvedValue({ data: mockActions });
510
- mockSdk.listInputFields.mockResolvedValue({ data: mockInputFields });
511
-
512
- const result = await generator.generateTypes({
513
- app: mockApp,
514
- authenticationId: 123,
515
- sdk: mockSdk as unknown as SdkType,
516
- context: mockContext as Parameters<
517
- typeof generator.generateTypes
518
- >[0]["context"],
519
- });
520
-
521
- expect(result).toContain("string_field: string");
522
- expect(result).toContain("number_field: number");
523
- expect(result).toContain("integer_field: number");
524
- expect(result).toContain("boolean_field: boolean");
525
- expect(result).toContain("array_field?: any[]");
526
- expect(result).toContain("object_field?: Record<string, any>");
527
- expect(result).toContain("email_field?: string");
528
- expect(result).toContain("url_field?: string");
529
- expect(result).toContain("datetime_field?: string");
530
- expect(result).toContain("unknown_field?: string | number | boolean");
531
- });
532
-
533
- it("should sanitize invalid field names", async () => {
534
- const mockActions: ActionItem[] = [
535
- {
536
- key: "test-action",
537
- title: "Test Action",
538
- action_type: "write",
539
- app_key: "test-app",
540
- description: "Test field sanitization",
541
- type: "action",
542
- },
543
- ];
544
-
545
- const mockInputFields: InputFieldItem[] = [
546
- createInputFieldItem({
547
- key: "field-with-dashes",
548
- title: "Field with dashes",
549
- value_type: "string",
550
- is_required: true,
551
- }),
552
- createInputFieldItem({
553
- key: "field.with.dots",
554
- title: "Field with dots",
555
- value_type: "string",
556
- is_required: true,
557
- }),
558
- createInputFieldItem({
559
- key: "123-starts-with-number",
560
- title: "Starts with number",
561
- value_type: "string",
562
- is_required: true,
563
- }),
564
- ];
565
-
566
- mockSdk.listActions.mockResolvedValue({ data: mockActions });
567
- mockSdk.listInputFields.mockResolvedValue({ data: mockInputFields });
568
-
569
- const result = await generator.generateTypes({
570
- app: mockApp,
571
- authenticationId: 123,
572
- sdk: mockSdk as unknown as SdkType,
573
- context: mockContext as Parameters<
574
- typeof generator.generateTypes
575
- >[0]["context"],
576
- });
577
-
578
- expect(result).toContain("field_with_dashes: string");
579
- expect(result).toContain("field_with_dots: string");
580
- expect(result).toContain("_123_starts_with_number: string");
581
- });
582
-
583
- it("should group actions by type", async () => {
584
- const mockActions: ActionItem[] = [
585
- {
586
- key: "search_user",
587
- title: "Search User",
588
- action_type: "search",
589
- app_key: "test-app",
590
- description: "Search for a user",
591
- type: "action",
592
- },
593
- {
594
- key: "find_user",
595
- title: "Find User",
596
- action_type: "search",
597
- app_key: "test-app",
598
- description: "Find a user",
599
- type: "action",
600
- },
601
- {
602
- key: "create_task",
603
- title: "Create Task",
604
- action_type: "write",
605
- app_key: "test-app",
606
- description: "Create a task",
607
- type: "action",
608
- },
609
- ];
610
-
611
- mockSdk.listActions.mockResolvedValue({ data: mockActions });
612
- mockSdk.listInputFields.mockResolvedValue({ data: [] });
613
-
614
- const result = await generator.generateTypes({
615
- app: mockApp,
616
- authenticationId: 123,
617
- sdk: mockSdk as unknown as SdkType,
618
- context: mockContext as Parameters<
619
- typeof generator.generateTypes
620
- >[0]["context"],
621
- });
622
-
623
- expect(result).toContain("interface TestAppSearchActions");
624
- expect(result).toContain("interface TestAppWriteActions");
625
- expect(result).toContain("search_user");
626
- expect(result).toContain("find_user");
627
- expect(result).toContain("create_task");
628
- });
629
-
630
- it("should generate module augmentation with all app keys", async () => {
631
- const appWithMultipleKeys: AppItem = {
632
- ...mockApp,
633
- key: "my-app",
634
- slug: "my-app-slug",
635
- };
636
-
637
- mockSdk.listActions.mockResolvedValue({ data: [] });
638
-
639
- const result = await generator.generateTypes({
640
- app: appWithMultipleKeys,
641
- authenticationId: 123,
642
- sdk: mockSdk as unknown as SdkType,
643
- context: mockContext as Parameters<
644
- typeof generator.generateTypes
645
- >[0]["context"],
646
- });
647
-
648
- expect(result).toContain('declare module "@zapier/zapier-sdk"');
649
- expect(result).toContain("interface ZapierSdkApps");
650
- expect(result).toContain("my_app_slug");
651
- expect(result).toContain("MyAppSlugAppWithFactory");
652
- });
653
-
654
- it("should include fetch method in generated types", async () => {
655
- mockSdk.listActions.mockResolvedValue({ data: [] });
656
-
657
- const result = await generator.generateTypes({
658
- app: mockApp,
659
- authenticationId: 123,
660
- sdk: mockSdk as unknown as SdkType,
661
- context: mockContext as Parameters<
662
- typeof generator.generateTypes
663
- >[0]["context"],
664
- });
665
-
666
- expect(result).toContain("fetch");
667
- expect(result).toContain("ZapierFetchInitOptions");
668
- expect(result).toContain(
669
- "Make authenticated HTTP requests through Zapier's Relay service",
670
- );
671
- });
672
-
673
- it("should add JSDoc comments from field descriptions", async () => {
674
- const mockActions: ActionItem[] = [
675
- {
676
- key: "test_action",
677
- title: "Test Action",
678
- action_type: "write",
679
- app_key: "test-app",
680
- description: "This is the action description",
681
- type: "action",
682
- },
683
- ];
684
-
685
- const mockInputFields: InputFieldItem[] = [
686
- createInputFieldItem({
687
- key: "field_with_comment",
688
- title: "Field with comment",
689
- value_type: "string",
690
- is_required: true,
691
- description: "This is a helpful comment for the field",
692
- }),
693
- ];
694
-
695
- mockSdk.listActions.mockResolvedValue({ data: mockActions });
696
- mockSdk.listInputFields.mockResolvedValue({ data: mockInputFields });
697
-
698
- const result = await generator.generateTypes({
699
- app: mockApp,
700
- authenticationId: 123,
701
- sdk: mockSdk as unknown as SdkType,
702
- context: mockContext as Parameters<
703
- typeof generator.generateTypes
704
- >[0]["context"],
705
- });
706
-
707
- expect(result).toContain("This is a helpful comment for the field");
708
- expect(result).toContain("This is the action description");
709
- });
710
-
711
- it("should escape special characters in comments", async () => {
712
- const mockActions: ActionItem[] = [
713
- {
714
- key: "test_action",
715
- title: "Test Action",
716
- action_type: "write",
717
- app_key: "test-app",
718
- description: "Description with */ closing comment",
719
- type: "action",
720
- },
721
- ];
722
-
723
- const mockInputFields: InputFieldItem[] = [
724
- createInputFieldItem({
725
- key: "field",
726
- title: "Field",
727
- value_type: "string",
728
- is_required: true,
729
- description: "Field with */ and\nmultiple\nlines",
730
- }),
731
- ];
732
-
733
- mockSdk.listActions.mockResolvedValue({ data: mockActions });
734
- mockSdk.listInputFields.mockResolvedValue({ data: mockInputFields });
735
-
736
- const result = await generator.generateTypes({
737
- app: mockApp,
738
- authenticationId: 123,
739
- sdk: mockSdk as unknown as SdkType,
740
- context: mockContext as Parameters<
741
- typeof generator.generateTypes
742
- >[0]["context"],
743
- });
744
-
745
- expect(result).toContain("*\\/");
746
- expect(result).not.toContain("\n *\n");
747
- });
748
-
749
- it("should generate proper TypeScript syntax", async () => {
750
- const mockActions: ActionItem[] = [
751
- {
752
- key: "test_action",
753
- title: "Test Action",
754
- action_type: "write",
755
- app_key: "test-app",
756
- description: "Test action",
757
- type: "action",
758
- },
759
- ];
760
-
761
- mockSdk.listActions.mockResolvedValue({ data: mockActions });
762
- mockSdk.listInputFields.mockResolvedValue({ data: [] });
763
-
764
- const result = await generator.generateTypes({
765
- app: mockApp,
766
- authenticationId: 123,
767
- sdk: mockSdk as unknown as SdkType,
768
- context: mockContext as Parameters<
769
- typeof generator.generateTypes
770
- >[0]["context"],
771
- });
772
-
773
- expect(result).toContain('import "@zapier/zapier-sdk"');
774
- expect(result).toContain("import type");
775
- expect(result).toContain("ActionExecutionOptions");
776
- expect(result).toContain("ActionExecutionResult");
777
- expect(result).toContain("Promise<ActionExecutionResult>");
778
- expect(result).toContain("interface");
779
- expect(result).toContain("type TestAppAppWithFactory");
780
- expect(result).toContain("export {};");
781
- });
782
-
783
- it("should handle actions without title", async () => {
784
- const mockActions: ActionItem[] = [
785
- {
786
- key: "action_without_title",
787
- action_type: "write",
788
- app_key: "test-app",
789
- type: "action",
790
- } as ActionItem,
791
- ];
792
-
793
- mockSdk.listActions.mockResolvedValue({ data: mockActions });
794
- mockSdk.listInputFields.mockResolvedValue({ data: [] });
795
-
796
- const result = await generator.generateTypes({
797
- app: mockApp,
798
- authenticationId: 123,
799
- sdk: mockSdk as unknown as SdkType,
800
- context: mockContext as Parameters<
801
- typeof generator.generateTypes
802
- >[0]["context"],
803
- });
804
-
805
- expect(result).toContain("action_without_title");
806
- });
807
-
808
- it("should generate header with timestamp and usage instructions", async () => {
809
- mockSdk.listActions.mockResolvedValue({ data: [] });
810
-
811
- const result = await generator.generateTypes({
812
- app: mockApp,
813
- authenticationId: 123,
814
- sdk: mockSdk as unknown as SdkType,
815
- context: mockContext as Parameters<
816
- typeof generator.generateTypes
817
- >[0]["context"],
818
- });
819
-
820
- expect(result).toContain("Auto-generated TypeScript types");
821
- expect(result).toContain("Generated for test-app@1.0.0");
822
- expect(result).toContain("Generated on:");
823
- expect(result).toContain("Usage:");
824
- expect(result).toContain("createZapierSdk");
825
- expect(result).toContain(
826
- "eslint-disable @typescript-eslint/naming-convention",
827
- );
828
- });
829
-
830
- it("should handle Promise.allSettled with all rejections", async () => {
831
- const mockActions: ActionItem[] = [
832
- {
833
- key: "action1",
834
- title: "Action 1",
835
- action_type: "write",
836
- app_key: "test-app",
837
- description: "Test action",
838
- type: "action",
839
- },
840
- {
841
- key: "action2",
842
- title: "Action 2",
843
- action_type: "write",
844
- app_key: "test-app",
845
- description: "Test action",
846
- type: "action",
847
- },
848
- ];
849
-
850
- mockSdk.listActions.mockResolvedValue({ data: mockActions });
851
- mockSdk.listInputFields.mockRejectedValue(
852
- new Error("All requests failed"),
853
- );
854
-
855
- const result = await generator.generateTypes({
856
- app: mockApp,
857
- authenticationId: 123,
858
- sdk: mockSdk as unknown as SdkType,
859
- context: mockContext as Parameters<
860
- typeof generator.generateTypes
861
- >[0]["context"],
862
- });
863
-
864
- expect(result).toContain("TestAppWriteActions");
865
- expect(consoleWarnSpy).toHaveBeenCalledWith(
866
- "Failed to fetch input fields for 2 action(s):",
867
- );
868
- expect(consoleWarnSpy).toHaveBeenCalledWith(
869
- " - action1 (write): All requests failed",
870
- );
871
- expect(consoleWarnSpy).toHaveBeenCalledWith(
872
- " - action2 (write): All requests failed",
873
- );
874
- });
875
-
876
- it("should handle error without message property", async () => {
877
- const mockActions: ActionItem[] = [
878
- {
879
- key: "test_action",
880
- title: "Test Action",
881
- action_type: "write",
882
- app_key: "test-app",
883
- description: "Test action",
884
- type: "action",
885
- },
886
- ];
887
-
888
- mockSdk.listActions.mockResolvedValue({ data: mockActions });
889
- mockSdk.listInputFields.mockRejectedValue("String error");
890
-
891
- await generator.generateTypes({
892
- app: mockApp,
893
- authenticationId: 123,
894
- sdk: mockSdk as unknown as SdkType,
895
- context: mockContext as Parameters<
896
- typeof generator.generateTypes
897
- >[0]["context"],
898
- });
899
-
900
- expect(consoleWarnSpy).toHaveBeenCalledWith(
901
- "Failed to fetch input fields for 1 action(s):",
902
- );
903
- expect(consoleWarnSpy).toHaveBeenCalledWith(
904
- " - test_action (write): Unknown error",
905
- );
906
- });
907
- });
908
- });