@zapier/zapier-sdk-cli 0.8.4 → 0.10.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.
- package/CHANGELOG.md +32 -0
- package/README.md +35 -51
- package/dist/cli.cjs +950 -433
- package/dist/cli.mjs +951 -434
- package/dist/index.cjs +729 -336
- package/dist/index.mjs +730 -337
- package/dist/package.json +1 -1
- package/dist/src/plugins/add/ast-generator.d.ts +37 -0
- package/dist/src/plugins/add/ast-generator.js +403 -0
- package/dist/src/plugins/add/index.d.ts +13 -0
- package/dist/src/plugins/add/index.js +120 -0
- package/dist/src/plugins/add/schemas.d.ts +18 -0
- package/dist/src/plugins/add/schemas.js +19 -0
- package/dist/src/plugins/getLoginConfigPath/index.d.ts +15 -0
- package/dist/src/plugins/getLoginConfigPath/index.js +19 -0
- package/dist/src/plugins/getLoginConfigPath/schemas.d.ts +3 -0
- package/dist/src/plugins/getLoginConfigPath/schemas.js +5 -0
- package/dist/src/plugins/index.d.ts +2 -2
- package/dist/src/plugins/index.js +2 -2
- package/dist/src/sdk.js +3 -3
- package/dist/src/utils/cli-generator-utils.d.ts +2 -1
- package/dist/src/utils/cli-generator-utils.js +11 -5
- package/dist/src/utils/cli-generator.js +65 -65
- package/dist/src/utils/parameter-resolver.d.ts +4 -1
- package/dist/src/utils/parameter-resolver.js +92 -15
- package/dist/src/utils/schema-formatter.d.ts +5 -1
- package/dist/src/utils/schema-formatter.js +48 -18
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +4 -4
- package/src/plugins/add/ast-generator.ts +777 -0
- package/src/plugins/add/index.test.ts +58 -0
- package/src/plugins/add/index.ts +187 -0
- package/src/plugins/add/schemas.ts +26 -0
- package/src/plugins/getLoginConfigPath/index.ts +45 -0
- package/src/plugins/getLoginConfigPath/schemas.ts +10 -0
- package/src/plugins/index.ts +2 -2
- package/src/sdk.ts +4 -4
- package/src/utils/cli-generator-utils.ts +17 -5
- package/src/utils/cli-generator.ts +90 -79
- package/src/utils/parameter-resolver.ts +155 -21
- package/src/utils/schema-formatter.ts +68 -33
- package/tsup.config.ts +1 -1
- package/dist/src/plugins/generateTypes/index.d.ts +0 -21
- package/dist/src/plugins/generateTypes/index.js +0 -312
- package/dist/src/plugins/generateTypes/schemas.d.ts +0 -18
- package/dist/src/plugins/generateTypes/schemas.js +0 -14
- package/dist/src/plugins/getConfigPath/index.d.ts +0 -15
- package/dist/src/plugins/getConfigPath/index.js +0 -19
- package/dist/src/plugins/getConfigPath/schemas.d.ts +0 -3
- package/dist/src/plugins/getConfigPath/schemas.js +0 -5
- package/src/plugins/generateTypes/index.ts +0 -444
- package/src/plugins/generateTypes/schemas.ts +0 -23
- package/src/plugins/getConfigPath/index.ts +0 -42
- package/src/plugins/getConfigPath/schemas.ts +0 -8
|
@@ -2,45 +2,16 @@ import type { Command } from "commander";
|
|
|
2
2
|
import { z } from "zod";
|
|
3
3
|
import type { ZapierSdk } from "@zapier/zapier-sdk";
|
|
4
4
|
import {
|
|
5
|
-
hasResolver,
|
|
6
5
|
isPositional,
|
|
7
6
|
formatErrorMessage,
|
|
8
7
|
ZapierError,
|
|
8
|
+
type FunctionRegistryEntry,
|
|
9
9
|
} from "@zapier/zapier-sdk";
|
|
10
10
|
import { SchemaParameterResolver } from "./parameter-resolver";
|
|
11
|
-
import { formatItemsFromSchema } from "./schema-formatter";
|
|
11
|
+
import { formatItemsFromSchema, formatJsonOutput } from "./schema-formatter";
|
|
12
12
|
import chalk from "chalk";
|
|
13
|
-
import util from "util";
|
|
14
13
|
import inquirer from "inquirer";
|
|
15
14
|
|
|
16
|
-
// ============================================================================
|
|
17
|
-
// JSON Formatting
|
|
18
|
-
// ============================================================================
|
|
19
|
-
|
|
20
|
-
function formatJsonOutput(data: unknown): void {
|
|
21
|
-
// Don't print anything for undefined results (commands that just perform actions)
|
|
22
|
-
if (data === undefined) {
|
|
23
|
-
return;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
// Show success message for action results
|
|
27
|
-
if (
|
|
28
|
-
data &&
|
|
29
|
-
typeof data === "object" &&
|
|
30
|
-
!Array.isArray(data) &&
|
|
31
|
-
((data as Record<string, unknown>).success !== undefined ||
|
|
32
|
-
(data as Record<string, unknown>).id ||
|
|
33
|
-
(data as Record<string, unknown>).status)
|
|
34
|
-
) {
|
|
35
|
-
console.log(chalk.green("✅ Action completed successfully!\n"));
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
// Use util.inspect for colored output
|
|
39
|
-
console.log(
|
|
40
|
-
util.inspect(data, { colors: true, depth: null, breakLength: 80 }),
|
|
41
|
-
);
|
|
42
|
-
}
|
|
43
|
-
|
|
44
15
|
// ============================================================================
|
|
45
16
|
// Types
|
|
46
17
|
// ============================================================================
|
|
@@ -66,7 +37,10 @@ interface CliParameter {
|
|
|
66
37
|
// Schema Analysis
|
|
67
38
|
// ============================================================================
|
|
68
39
|
|
|
69
|
-
function analyzeZodSchema(
|
|
40
|
+
function analyzeZodSchema(
|
|
41
|
+
schema: z.ZodSchema,
|
|
42
|
+
functionInfo?: FunctionRegistryEntry,
|
|
43
|
+
): CliParameter[] {
|
|
70
44
|
const parameters: CliParameter[] = [];
|
|
71
45
|
|
|
72
46
|
// Handle ZodEffects (schemas with .refine(), .transform(), etc.)
|
|
@@ -78,14 +52,18 @@ function analyzeZodSchema(schema: z.ZodSchema): CliParameter[] {
|
|
|
78
52
|
// Get the underlying schema
|
|
79
53
|
const innerSchema = (schema as unknown as { _def: { schema: z.ZodSchema } })
|
|
80
54
|
._def.schema;
|
|
81
|
-
return analyzeZodSchema(innerSchema);
|
|
55
|
+
return analyzeZodSchema(innerSchema, functionInfo);
|
|
82
56
|
}
|
|
83
57
|
|
|
84
58
|
if (schema instanceof z.ZodObject) {
|
|
85
59
|
const shape = schema.shape;
|
|
86
60
|
|
|
87
61
|
for (const [key, fieldSchema] of Object.entries(shape)) {
|
|
88
|
-
const param = analyzeZodField(
|
|
62
|
+
const param = analyzeZodField(
|
|
63
|
+
key,
|
|
64
|
+
fieldSchema as z.ZodSchema,
|
|
65
|
+
functionInfo,
|
|
66
|
+
);
|
|
89
67
|
if (param) {
|
|
90
68
|
parameters.push(param);
|
|
91
69
|
}
|
|
@@ -98,6 +76,7 @@ function analyzeZodSchema(schema: z.ZodSchema): CliParameter[] {
|
|
|
98
76
|
function analyzeZodField(
|
|
99
77
|
name: string,
|
|
100
78
|
schema: z.ZodSchema,
|
|
79
|
+
functionInfo?: FunctionRegistryEntry,
|
|
101
80
|
): CliParameter | null {
|
|
102
81
|
let baseSchema = schema;
|
|
103
82
|
let required = true;
|
|
@@ -150,6 +129,14 @@ function analyzeZodField(
|
|
|
150
129
|
paramType = "string";
|
|
151
130
|
}
|
|
152
131
|
|
|
132
|
+
// Check if this parameter has a resolver
|
|
133
|
+
let paramHasResolver = false;
|
|
134
|
+
|
|
135
|
+
// Check function-specific resolvers first
|
|
136
|
+
if (functionInfo?.resolvers?.[name]) {
|
|
137
|
+
paramHasResolver = true;
|
|
138
|
+
}
|
|
139
|
+
|
|
153
140
|
// Extract resolver metadata
|
|
154
141
|
return {
|
|
155
142
|
name,
|
|
@@ -158,7 +145,7 @@ function analyzeZodField(
|
|
|
158
145
|
description: schema.description,
|
|
159
146
|
default: defaultValue,
|
|
160
147
|
choices,
|
|
161
|
-
hasResolver:
|
|
148
|
+
hasResolver: paramHasResolver,
|
|
162
149
|
isPositional: isPositional(schema),
|
|
163
150
|
};
|
|
164
151
|
}
|
|
@@ -206,12 +193,7 @@ export function generateCliCommands(program: Command, sdk: ZapierSdk): void {
|
|
|
206
193
|
// Convert methodName to kebab-case CLI command
|
|
207
194
|
const cliCommandName = methodNameToCliCommand(fnInfo.name);
|
|
208
195
|
|
|
209
|
-
const config = createCommandConfig(
|
|
210
|
-
cliCommandName,
|
|
211
|
-
fnInfo.name,
|
|
212
|
-
fnInfo.inputSchema as z.ZodSchema,
|
|
213
|
-
sdk,
|
|
214
|
-
);
|
|
196
|
+
const config = createCommandConfig(cliCommandName, fnInfo, sdk);
|
|
215
197
|
|
|
216
198
|
addCommand(program, cliCommandName, config);
|
|
217
199
|
});
|
|
@@ -288,11 +270,11 @@ export function generateCliCommands(program: Command, sdk: ZapierSdk): void {
|
|
|
288
270
|
|
|
289
271
|
function createCommandConfig(
|
|
290
272
|
cliCommandName: string,
|
|
291
|
-
|
|
292
|
-
schema: z.ZodSchema,
|
|
273
|
+
functionInfo: FunctionRegistryEntry,
|
|
293
274
|
sdk: ZapierSdk,
|
|
294
275
|
): CliCommandConfig {
|
|
295
|
-
const
|
|
276
|
+
const schema = functionInfo.inputSchema as z.ZodSchema;
|
|
277
|
+
const parameters = analyzeZodSchema(schema, functionInfo);
|
|
296
278
|
const description = schema.description || `${cliCommandName} command`;
|
|
297
279
|
|
|
298
280
|
const handler = async (...args: unknown[]) => {
|
|
@@ -304,7 +286,7 @@ function createCommandConfig(
|
|
|
304
286
|
const options = commandObj.opts();
|
|
305
287
|
|
|
306
288
|
// Check if this is a list command for pagination
|
|
307
|
-
const isListCommand =
|
|
289
|
+
const isListCommand = functionInfo.type === "list";
|
|
308
290
|
const hasPaginationParams = parameters.some(
|
|
309
291
|
(p) => p.name === "maxItems" || p.name === "pageSize",
|
|
310
292
|
);
|
|
@@ -325,6 +307,7 @@ function createCommandConfig(
|
|
|
325
307
|
schema,
|
|
326
308
|
rawParams,
|
|
327
309
|
sdk,
|
|
310
|
+
functionInfo.name,
|
|
328
311
|
);
|
|
329
312
|
|
|
330
313
|
// Handle paginated list commands with async iteration
|
|
@@ -339,11 +322,11 @@ function createCommandConfig(
|
|
|
339
322
|
string,
|
|
340
323
|
(params: unknown) => Promise<unknown> & AsyncIterable<unknown>
|
|
341
324
|
>;
|
|
342
|
-
const sdkIterator = sdkObj[
|
|
325
|
+
const sdkIterator = sdkObj[functionInfo.name](resolvedParams);
|
|
343
326
|
await handlePaginatedListWithAsyncIteration(
|
|
344
|
-
|
|
327
|
+
functionInfo.name,
|
|
345
328
|
sdkIterator,
|
|
346
|
-
|
|
329
|
+
functionInfo,
|
|
347
330
|
);
|
|
348
331
|
} else {
|
|
349
332
|
// Special handling for commands that write to files
|
|
@@ -354,7 +337,7 @@ function createCommandConfig(
|
|
|
354
337
|
string,
|
|
355
338
|
(params: unknown) => Promise<unknown>
|
|
356
339
|
>;
|
|
357
|
-
await sdkObj[
|
|
340
|
+
await sdkObj[functionInfo.name](resolvedParams);
|
|
358
341
|
console.log(
|
|
359
342
|
chalk.green(`✅ ${cliCommandName} completed successfully!`),
|
|
360
343
|
);
|
|
@@ -367,7 +350,7 @@ function createCommandConfig(
|
|
|
367
350
|
string,
|
|
368
351
|
(params: unknown) => Promise<unknown>
|
|
369
352
|
>;
|
|
370
|
-
const result: unknown = await sdkObj[
|
|
353
|
+
const result: unknown = await sdkObj[functionInfo.name](resolvedParams);
|
|
371
354
|
const items = (result as { data?: unknown })?.data
|
|
372
355
|
? (result as { data: unknown }).data
|
|
373
356
|
: result;
|
|
@@ -380,8 +363,7 @@ function createCommandConfig(
|
|
|
380
363
|
(resolvedParams as { maxItems?: number }).maxItems,
|
|
381
364
|
hasUserSpecifiedMaxItems,
|
|
382
365
|
shouldUseJson as boolean,
|
|
383
|
-
|
|
384
|
-
sdkMethodName,
|
|
366
|
+
functionInfo,
|
|
385
367
|
);
|
|
386
368
|
} else {
|
|
387
369
|
formatJsonOutput(items);
|
|
@@ -436,6 +418,9 @@ function addCommand(
|
|
|
436
418
|
): void {
|
|
437
419
|
const command = program.command(commandName).description(config.description);
|
|
438
420
|
|
|
421
|
+
// Track whether we've already used a positional array parameter
|
|
422
|
+
let hasPositionalArray = false;
|
|
423
|
+
|
|
439
424
|
// Add parameters to command
|
|
440
425
|
config.parameters.forEach((param) => {
|
|
441
426
|
// Convert camelCase to kebab-case for CLI display
|
|
@@ -447,6 +432,25 @@ function addCommand(
|
|
|
447
432
|
`[${kebabName}]`,
|
|
448
433
|
param.description || `${kebabName} parameter`,
|
|
449
434
|
);
|
|
435
|
+
} else if (
|
|
436
|
+
param.required &&
|
|
437
|
+
param.type === "array" &&
|
|
438
|
+
!hasPositionalArray
|
|
439
|
+
) {
|
|
440
|
+
// First required array parameter becomes a variadic positional argument
|
|
441
|
+
hasPositionalArray = true;
|
|
442
|
+
command.argument(
|
|
443
|
+
`<${kebabName}...>`,
|
|
444
|
+
param.description || `${kebabName} parameter`,
|
|
445
|
+
);
|
|
446
|
+
} else if (param.required && param.type === "array") {
|
|
447
|
+
// Subsequent required array parameters become required flags
|
|
448
|
+
const flags = [`--${kebabName}`];
|
|
449
|
+
const flagSignature = flags.join(", ") + ` <values...>`;
|
|
450
|
+
command.requiredOption(
|
|
451
|
+
flagSignature,
|
|
452
|
+
param.description || `${kebabName} parameter (required)`,
|
|
453
|
+
);
|
|
450
454
|
} else if (param.required) {
|
|
451
455
|
// Required parameters without resolvers become required positional arguments
|
|
452
456
|
command.argument(
|
|
@@ -532,6 +536,11 @@ function convertCliArgsToSdkParams(
|
|
|
532
536
|
}
|
|
533
537
|
|
|
534
538
|
function convertValue(value: unknown, type: CliParameter["type"]): unknown {
|
|
539
|
+
// Don't convert undefined values - let the resolver system handle them
|
|
540
|
+
if (value === undefined) {
|
|
541
|
+
return undefined;
|
|
542
|
+
}
|
|
543
|
+
|
|
535
544
|
switch (type) {
|
|
536
545
|
case "number":
|
|
537
546
|
return Number(value);
|
|
@@ -563,13 +572,15 @@ function convertValue(value: unknown, type: CliParameter["type"]): unknown {
|
|
|
563
572
|
async function handlePaginatedListWithAsyncIteration(
|
|
564
573
|
sdkMethodName: string,
|
|
565
574
|
sdkResult: unknown,
|
|
566
|
-
|
|
575
|
+
functionInfo: FunctionRegistryEntry,
|
|
567
576
|
): Promise<void> {
|
|
568
|
-
const itemName = getItemNameFromMethod(
|
|
577
|
+
const itemName = getItemNameFromMethod(functionInfo);
|
|
569
578
|
let totalShown = 0;
|
|
570
579
|
let pageCount = 0;
|
|
571
580
|
|
|
572
|
-
console.log(
|
|
581
|
+
console.log(
|
|
582
|
+
chalk.blue(`📋 ${getListTitleFromMethod(sdkMethodName, functionInfo)}\n`),
|
|
583
|
+
);
|
|
573
584
|
|
|
574
585
|
try {
|
|
575
586
|
// Use async iteration to go through pages
|
|
@@ -598,13 +609,15 @@ async function handlePaginatedListWithAsyncIteration(
|
|
|
598
609
|
if (pageCount > 1) {
|
|
599
610
|
console.clear();
|
|
600
611
|
console.log(
|
|
601
|
-
chalk.blue(
|
|
612
|
+
chalk.blue(
|
|
613
|
+
`📋 ${getListTitleFromMethod(sdkMethodName, functionInfo)}\n`,
|
|
614
|
+
),
|
|
602
615
|
);
|
|
603
616
|
}
|
|
604
617
|
|
|
605
|
-
// Format and display items using
|
|
606
|
-
if (
|
|
607
|
-
formatItemsFromSchema(
|
|
618
|
+
// Format and display items using function info
|
|
619
|
+
if (functionInfo) {
|
|
620
|
+
formatItemsFromSchema(functionInfo, items, totalShown);
|
|
608
621
|
} else {
|
|
609
622
|
formatItemsGeneric(items, totalShown);
|
|
610
623
|
}
|
|
@@ -646,8 +659,8 @@ async function handlePaginatedListWithAsyncIteration(
|
|
|
646
659
|
return;
|
|
647
660
|
}
|
|
648
661
|
|
|
649
|
-
if (
|
|
650
|
-
formatItemsFromSchema(
|
|
662
|
+
if (functionInfo) {
|
|
663
|
+
formatItemsFromSchema(functionInfo, items, 0);
|
|
651
664
|
} else {
|
|
652
665
|
formatItemsGeneric(items, 0);
|
|
653
666
|
}
|
|
@@ -664,8 +677,7 @@ function formatNonPaginatedResults(
|
|
|
664
677
|
requestedMaxItems?: number,
|
|
665
678
|
userSpecifiedMaxItems?: boolean,
|
|
666
679
|
useRawJson?: boolean,
|
|
667
|
-
|
|
668
|
-
methodName?: string,
|
|
680
|
+
functionInfo?: FunctionRegistryEntry,
|
|
669
681
|
): void {
|
|
670
682
|
if (!Array.isArray(result)) {
|
|
671
683
|
if (useRawJson) {
|
|
@@ -681,7 +693,7 @@ function formatNonPaginatedResults(
|
|
|
681
693
|
return;
|
|
682
694
|
}
|
|
683
695
|
|
|
684
|
-
const itemName =
|
|
696
|
+
const itemName = functionInfo ? getItemNameFromMethod(functionInfo) : "items";
|
|
685
697
|
|
|
686
698
|
if (result.length === 0) {
|
|
687
699
|
console.log(chalk.yellow(`No ${itemName} found.`));
|
|
@@ -690,9 +702,9 @@ function formatNonPaginatedResults(
|
|
|
690
702
|
|
|
691
703
|
console.log(chalk.green(`\n✅ Found ${result.length} ${itemName}:\n`));
|
|
692
704
|
|
|
693
|
-
// Use
|
|
694
|
-
if (
|
|
695
|
-
formatItemsFromSchema(
|
|
705
|
+
// Use function info for formatting
|
|
706
|
+
if (functionInfo) {
|
|
707
|
+
formatItemsFromSchema(functionInfo, result);
|
|
696
708
|
} else {
|
|
697
709
|
// Fallback to generic formatting
|
|
698
710
|
formatItemsGeneric(result);
|
|
@@ -729,22 +741,21 @@ function formatItemsGeneric(
|
|
|
729
741
|
}
|
|
730
742
|
|
|
731
743
|
// Generic helper functions that infer from schema description or method name
|
|
732
|
-
function getItemNameFromMethod(
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
if (listMatch) {
|
|
736
|
-
return listMatch[1].toLowerCase();
|
|
744
|
+
function getItemNameFromMethod(functionInfo: FunctionRegistryEntry): string {
|
|
745
|
+
if (functionInfo.itemType) {
|
|
746
|
+
return `${functionInfo.itemType} items`;
|
|
737
747
|
}
|
|
738
748
|
|
|
739
|
-
// Fallback to generic
|
|
740
749
|
return "items";
|
|
741
750
|
}
|
|
742
751
|
|
|
743
|
-
function getListTitleFromMethod(
|
|
744
|
-
|
|
745
|
-
|
|
752
|
+
function getListTitleFromMethod(
|
|
753
|
+
methodName: string,
|
|
754
|
+
functionInfo: FunctionRegistryEntry,
|
|
755
|
+
): string {
|
|
756
|
+
if (functionInfo.itemType) {
|
|
757
|
+
return `Available ${functionInfo.itemType} items`;
|
|
758
|
+
}
|
|
746
759
|
|
|
747
|
-
|
|
748
|
-
const capitalized = itemName.charAt(0).toUpperCase() + itemName.slice(1);
|
|
749
|
-
return `Available ${capitalized}`;
|
|
760
|
+
return `${methodName} items`;
|
|
750
761
|
}
|
|
@@ -2,11 +2,6 @@ import inquirer from "inquirer";
|
|
|
2
2
|
import chalk from "chalk";
|
|
3
3
|
import { z } from "zod";
|
|
4
4
|
import type { ZapierSdk } from "@zapier/zapier-sdk";
|
|
5
|
-
import {
|
|
6
|
-
getResolver,
|
|
7
|
-
hasResolver,
|
|
8
|
-
getResolutionOrderForParams,
|
|
9
|
-
} from "@zapier/zapier-sdk";
|
|
10
5
|
|
|
11
6
|
// ============================================================================
|
|
12
7
|
// Types
|
|
@@ -24,6 +19,66 @@ interface ResolverContext {
|
|
|
24
19
|
sdk: ZapierSdk;
|
|
25
20
|
currentParams: Record<string, unknown>;
|
|
26
21
|
resolvedParams: Record<string, unknown>;
|
|
22
|
+
functionName?: string;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// ============================================================================
|
|
26
|
+
// Local Resolution Helper Functions
|
|
27
|
+
// ============================================================================
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Resolve dependency chain for a parameter using local resolvers
|
|
31
|
+
* Returns parameters in the order they need to be resolved
|
|
32
|
+
*/
|
|
33
|
+
function getLocalResolutionOrder(
|
|
34
|
+
paramName: string,
|
|
35
|
+
resolvers: Record<string, any>,
|
|
36
|
+
resolved: Set<string> = new Set(),
|
|
37
|
+
): string[] {
|
|
38
|
+
const resolver = resolvers[paramName];
|
|
39
|
+
if (!resolver || resolver.type === "static") {
|
|
40
|
+
return [paramName];
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const order: string[] = [];
|
|
44
|
+
|
|
45
|
+
if ("depends" in resolver && resolver.depends) {
|
|
46
|
+
for (const dependency of resolver.depends) {
|
|
47
|
+
if (!resolved.has(dependency)) {
|
|
48
|
+
order.push(...getLocalResolutionOrder(dependency, resolvers, resolved));
|
|
49
|
+
resolved.add(dependency);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
if (!resolved.has(paramName)) {
|
|
55
|
+
order.push(paramName);
|
|
56
|
+
resolved.add(paramName);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return order;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Get resolution order for multiple parameters using local resolvers
|
|
64
|
+
*/
|
|
65
|
+
function getLocalResolutionOrderForParams(
|
|
66
|
+
paramNames: string[],
|
|
67
|
+
resolvers: Record<string, any>,
|
|
68
|
+
): string[] {
|
|
69
|
+
const resolved = new Set<string>();
|
|
70
|
+
const order: string[] = [];
|
|
71
|
+
|
|
72
|
+
for (const paramName of paramNames) {
|
|
73
|
+
const paramOrder = getLocalResolutionOrder(paramName, resolvers, resolved);
|
|
74
|
+
for (const param of paramOrder) {
|
|
75
|
+
if (!order.includes(param)) {
|
|
76
|
+
order.push(param);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return order;
|
|
27
82
|
}
|
|
28
83
|
|
|
29
84
|
// ============================================================================
|
|
@@ -35,6 +90,7 @@ export class SchemaParameterResolver {
|
|
|
35
90
|
schema: z.ZodSchema,
|
|
36
91
|
providedParams: unknown,
|
|
37
92
|
sdk: ZapierSdk,
|
|
93
|
+
functionName?: string,
|
|
38
94
|
): Promise<unknown> {
|
|
39
95
|
// 1. Try to parse with current parameters
|
|
40
96
|
const parseResult = schema.safeParse(providedParams);
|
|
@@ -42,7 +98,7 @@ export class SchemaParameterResolver {
|
|
|
42
98
|
// Get all schema parameters to check which ones have resolvers
|
|
43
99
|
const allParams = this.extractParametersFromSchema(schema);
|
|
44
100
|
const resolvableParams = allParams.filter((param) =>
|
|
45
|
-
hasResolver(param.name),
|
|
101
|
+
this.hasResolver(param.name, sdk, functionName),
|
|
46
102
|
);
|
|
47
103
|
|
|
48
104
|
// Get all missing parameters that have resolvers
|
|
@@ -108,12 +164,18 @@ export class SchemaParameterResolver {
|
|
|
108
164
|
sdk,
|
|
109
165
|
currentParams: providedParams as Record<string, unknown>,
|
|
110
166
|
resolvedParams,
|
|
167
|
+
functionName,
|
|
111
168
|
};
|
|
112
169
|
|
|
170
|
+
// Get local resolvers for this function
|
|
171
|
+
const localResolvers = this.getLocalResolvers(sdk, functionName);
|
|
172
|
+
|
|
113
173
|
if (functionallyRequired.length > 0) {
|
|
114
174
|
const requiredParamNames = functionallyRequired.map((p) => p.name);
|
|
115
|
-
const requiredResolutionOrder =
|
|
116
|
-
|
|
175
|
+
const requiredResolutionOrder = getLocalResolutionOrderForParams(
|
|
176
|
+
requiredParamNames,
|
|
177
|
+
localResolvers,
|
|
178
|
+
);
|
|
117
179
|
|
|
118
180
|
// Find all parameters that need to be resolved (including dependencies)
|
|
119
181
|
// from the available resolvable parameters
|
|
@@ -135,7 +197,11 @@ export class SchemaParameterResolver {
|
|
|
135
197
|
|
|
136
198
|
for (const param of orderedRequiredParams) {
|
|
137
199
|
try {
|
|
138
|
-
const value = await this.resolveParameter(
|
|
200
|
+
const value = await this.resolveParameter(
|
|
201
|
+
param,
|
|
202
|
+
context,
|
|
203
|
+
functionName,
|
|
204
|
+
);
|
|
139
205
|
this.setNestedValue(resolvedParams, param.path, value);
|
|
140
206
|
|
|
141
207
|
// Update context with newly resolved value
|
|
@@ -168,8 +234,10 @@ export class SchemaParameterResolver {
|
|
|
168
234
|
// 3. Resolve parameters that should always be prompted for (but can be skipped)
|
|
169
235
|
if (alwaysPrompt.length > 0) {
|
|
170
236
|
const alwaysPromptNames = alwaysPrompt.map((p) => p.name);
|
|
171
|
-
const alwaysPromptResolutionOrder =
|
|
172
|
-
|
|
237
|
+
const alwaysPromptResolutionOrder = getLocalResolutionOrderForParams(
|
|
238
|
+
alwaysPromptNames,
|
|
239
|
+
localResolvers,
|
|
240
|
+
);
|
|
173
241
|
|
|
174
242
|
const orderedAlwaysPromptParams = alwaysPromptResolutionOrder
|
|
175
243
|
.map((paramName) => alwaysPrompt.find((p) => p.name === paramName))
|
|
@@ -177,7 +245,11 @@ export class SchemaParameterResolver {
|
|
|
177
245
|
|
|
178
246
|
for (const param of orderedAlwaysPromptParams) {
|
|
179
247
|
try {
|
|
180
|
-
const value = await this.resolveParameter(
|
|
248
|
+
const value = await this.resolveParameter(
|
|
249
|
+
param,
|
|
250
|
+
context,
|
|
251
|
+
functionName,
|
|
252
|
+
);
|
|
181
253
|
this.setNestedValue(resolvedParams, param.path, value);
|
|
182
254
|
|
|
183
255
|
// Update context with newly resolved value
|
|
@@ -207,8 +279,10 @@ export class SchemaParameterResolver {
|
|
|
207
279
|
if (shouldResolveOptional.resolveOptional) {
|
|
208
280
|
// Resolve optional parameters using their resolvers
|
|
209
281
|
const optionalParamNames = trulyOptional.map((p) => p.name);
|
|
210
|
-
const optionalResolutionOrder =
|
|
211
|
-
|
|
282
|
+
const optionalResolutionOrder = getLocalResolutionOrderForParams(
|
|
283
|
+
optionalParamNames,
|
|
284
|
+
localResolvers,
|
|
285
|
+
);
|
|
212
286
|
|
|
213
287
|
const orderedOptionalParams = optionalResolutionOrder
|
|
214
288
|
.map((paramName) => trulyOptional.find((p) => p.name === paramName))
|
|
@@ -216,7 +290,11 @@ export class SchemaParameterResolver {
|
|
|
216
290
|
|
|
217
291
|
for (const param of orderedOptionalParams) {
|
|
218
292
|
try {
|
|
219
|
-
const value = await this.resolveParameter(
|
|
293
|
+
const value = await this.resolveParameter(
|
|
294
|
+
param,
|
|
295
|
+
context,
|
|
296
|
+
functionName,
|
|
297
|
+
);
|
|
220
298
|
this.setNestedValue(resolvedParams, param.path, value);
|
|
221
299
|
|
|
222
300
|
// Update context with newly resolved value
|
|
@@ -306,8 +384,9 @@ export class SchemaParameterResolver {
|
|
|
306
384
|
private async resolveParameter(
|
|
307
385
|
param: ResolvableParameter,
|
|
308
386
|
context: ResolverContext,
|
|
387
|
+
functionName?: string,
|
|
309
388
|
): Promise<unknown> {
|
|
310
|
-
const resolver = getResolver(param.name);
|
|
389
|
+
const resolver = this.getResolver(param.name, context.sdk, functionName);
|
|
311
390
|
if (!resolver) {
|
|
312
391
|
throw new Error(`No resolver found for parameter: ${param.name}`);
|
|
313
392
|
}
|
|
@@ -430,10 +509,10 @@ export class SchemaParameterResolver {
|
|
|
430
509
|
|
|
431
510
|
// Separate new required and optional fields
|
|
432
511
|
const newRequiredFields = newFields.filter(
|
|
433
|
-
(field: unknown) => (field as {
|
|
512
|
+
(field: unknown) => (field as { is_required: boolean }).is_required,
|
|
434
513
|
);
|
|
435
514
|
const newOptionalFields = newFields.filter(
|
|
436
|
-
(field: unknown) => !(field as {
|
|
515
|
+
(field: unknown) => !(field as { is_required: boolean }).is_required,
|
|
437
516
|
);
|
|
438
517
|
|
|
439
518
|
// Prompt for new required fields
|
|
@@ -536,7 +615,7 @@ export class SchemaParameterResolver {
|
|
|
536
615
|
type?: string;
|
|
537
616
|
key: string;
|
|
538
617
|
label?: string;
|
|
539
|
-
|
|
618
|
+
is_required?: boolean;
|
|
540
619
|
helpText?: string;
|
|
541
620
|
default?: unknown;
|
|
542
621
|
choices?: Array<{ label?: string; value: unknown }>;
|
|
@@ -545,7 +624,7 @@ export class SchemaParameterResolver {
|
|
|
545
624
|
const fieldPrompt: Record<string, unknown> = {
|
|
546
625
|
type: fieldObj.type === "boolean" ? "confirm" : "input",
|
|
547
626
|
name: fieldObj.key,
|
|
548
|
-
message: `${fieldObj.label || fieldObj.key}${fieldObj.
|
|
627
|
+
message: `${fieldObj.label || fieldObj.key}${fieldObj.is_required ? " (required)" : " (optional)"}:`,
|
|
549
628
|
};
|
|
550
629
|
|
|
551
630
|
if (fieldObj.helpText) {
|
|
@@ -574,7 +653,7 @@ export class SchemaParameterResolver {
|
|
|
574
653
|
|
|
575
654
|
if (answer[fieldObj.key] !== undefined && answer[fieldObj.key] !== "") {
|
|
576
655
|
inputs[fieldObj.key] = answer[fieldObj.key];
|
|
577
|
-
} else if (fieldObj.
|
|
656
|
+
} else if (fieldObj.is_required) {
|
|
578
657
|
throw new Error(`Required field ${fieldObj.key} cannot be empty`);
|
|
579
658
|
}
|
|
580
659
|
} catch (error) {
|
|
@@ -598,4 +677,59 @@ export class SchemaParameterResolver {
|
|
|
598
677
|
errorObj?.isTTYError === true
|
|
599
678
|
);
|
|
600
679
|
}
|
|
680
|
+
|
|
681
|
+
private hasResolver(
|
|
682
|
+
paramName: string,
|
|
683
|
+
sdk: ZapierSdk,
|
|
684
|
+
functionName?: string,
|
|
685
|
+
): boolean {
|
|
686
|
+
// Check plugin-specific resolvers first
|
|
687
|
+
if (functionName && typeof sdk.getRegistry === "function") {
|
|
688
|
+
const registry = sdk.getRegistry();
|
|
689
|
+
const functionInfo = registry.functions.find(
|
|
690
|
+
(f) => f.name === functionName,
|
|
691
|
+
);
|
|
692
|
+
if (functionInfo && functionInfo.resolvers?.[paramName]) {
|
|
693
|
+
return true;
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
// No global registry fallback
|
|
698
|
+
return false;
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
private getResolver(
|
|
702
|
+
paramName: string,
|
|
703
|
+
sdk: ZapierSdk,
|
|
704
|
+
functionName?: string,
|
|
705
|
+
): any {
|
|
706
|
+
// Check plugin-specific resolvers first
|
|
707
|
+
if (functionName && typeof sdk.getRegistry === "function") {
|
|
708
|
+
const registry = sdk.getRegistry();
|
|
709
|
+
const functionInfo = registry.functions.find(
|
|
710
|
+
(f) => f.name === functionName,
|
|
711
|
+
);
|
|
712
|
+
if (functionInfo && functionInfo.resolvers?.[paramName]) {
|
|
713
|
+
return functionInfo.resolvers[paramName];
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
// No global registry fallback
|
|
718
|
+
return null;
|
|
719
|
+
}
|
|
720
|
+
|
|
721
|
+
private getLocalResolvers(
|
|
722
|
+
sdk: ZapierSdk,
|
|
723
|
+
functionName?: string,
|
|
724
|
+
): Record<string, any> {
|
|
725
|
+
if (!functionName || typeof sdk.getRegistry !== "function") {
|
|
726
|
+
return {};
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
const registry = sdk.getRegistry();
|
|
730
|
+
const functionInfo = registry.functions.find(
|
|
731
|
+
(f) => f.name === functionName,
|
|
732
|
+
);
|
|
733
|
+
return functionInfo?.resolvers || {};
|
|
734
|
+
}
|
|
601
735
|
}
|