@zapier/zapier-sdk-cli 0.9.0 → 0.11.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/dist/cli.cjs +316 -144
- package/dist/cli.mjs +317 -145
- package/dist/index.cjs +15 -12
- package/dist/index.mjs +15 -12
- package/dist/package.json +1 -1
- package/dist/src/plugins/add/index.js +11 -13
- 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 +50 -65
- package/dist/src/utils/parameter-resolver.d.ts +9 -1
- package/dist/src/utils/parameter-resolver.js +192 -56
- 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 +3 -3
- package/src/plugins/add/index.ts +15 -15
- package/src/utils/cli-generator-utils.ts +17 -5
- package/src/utils/cli-generator.ts +68 -79
- package/src/utils/parameter-resolver.ts +310 -80
- package/src/utils/schema-formatter.ts +68 -33
|
@@ -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
|
}
|
|
@@ -380,7 +459,7 @@ export class SchemaParameterResolver {
|
|
|
380
459
|
const inputs: Record<string, unknown> = {};
|
|
381
460
|
let processedFieldKeys = new Set<string>();
|
|
382
461
|
let iteration = 0;
|
|
383
|
-
const maxIterations =
|
|
462
|
+
const maxIterations = 10; // Prevent infinite loops
|
|
384
463
|
|
|
385
464
|
while (iteration < maxIterations) {
|
|
386
465
|
iteration++;
|
|
@@ -400,12 +479,12 @@ export class SchemaParameterResolver {
|
|
|
400
479
|
),
|
|
401
480
|
);
|
|
402
481
|
|
|
403
|
-
const
|
|
482
|
+
const rootFieldItems = await typedResolver.fetch(
|
|
404
483
|
updatedContext.sdk,
|
|
405
484
|
updatedContext.resolvedParams,
|
|
406
485
|
);
|
|
407
486
|
|
|
408
|
-
if (!
|
|
487
|
+
if (!rootFieldItems || rootFieldItems.length === 0) {
|
|
409
488
|
if (iteration === 1) {
|
|
410
489
|
console.log(
|
|
411
490
|
chalk.yellow(`No input fields required for this action.`),
|
|
@@ -414,59 +493,180 @@ export class SchemaParameterResolver {
|
|
|
414
493
|
break;
|
|
415
494
|
}
|
|
416
495
|
|
|
417
|
-
//
|
|
418
|
-
const
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
496
|
+
// Process fields recursively, maintaining fieldset structure
|
|
497
|
+
const fieldStats = await this.processFieldItems(
|
|
498
|
+
rootFieldItems,
|
|
499
|
+
inputs,
|
|
500
|
+
processedFieldKeys,
|
|
501
|
+
[],
|
|
502
|
+
iteration,
|
|
424
503
|
);
|
|
425
504
|
|
|
426
|
-
|
|
427
|
-
|
|
505
|
+
// If no new fields were processed, we're done
|
|
506
|
+
if (fieldStats.newRequired === 0 && fieldStats.newOptional === 0) {
|
|
428
507
|
break;
|
|
429
508
|
}
|
|
430
509
|
|
|
431
|
-
//
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
510
|
+
// If we only processed optional fields and skipped them, no need to re-fetch
|
|
511
|
+
if (fieldStats.newRequired === 0 && fieldStats.optionalSkipped) {
|
|
512
|
+
break;
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
if (iteration >= maxIterations) {
|
|
517
|
+
console.log(
|
|
518
|
+
chalk.yellow(
|
|
519
|
+
`\n⚠️ Maximum field resolution iterations reached. Some dynamic fields may not have been discovered.`,
|
|
520
|
+
),
|
|
437
521
|
);
|
|
522
|
+
}
|
|
438
523
|
|
|
439
|
-
|
|
440
|
-
|
|
524
|
+
return inputs;
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
/**
|
|
528
|
+
* Recursively processes fieldsets and their fields, maintaining natural structure
|
|
529
|
+
* and creating nested inputs as needed (e.g., fieldset "foo" becomes inputs.foo = [{}])
|
|
530
|
+
*/
|
|
531
|
+
private async processFieldItems(
|
|
532
|
+
items: unknown[],
|
|
533
|
+
targetInputs: Record<string, unknown>,
|
|
534
|
+
processedFieldKeys: Set<string>,
|
|
535
|
+
fieldsetPath: string[] = [],
|
|
536
|
+
iteration: number = 1,
|
|
537
|
+
): Promise<{
|
|
538
|
+
newRequired: number;
|
|
539
|
+
newOptional: number;
|
|
540
|
+
optionalSkipped: boolean;
|
|
541
|
+
}> {
|
|
542
|
+
let newRequiredCount = 0;
|
|
543
|
+
let newOptionalCount = 0;
|
|
544
|
+
let optionalSkipped = false;
|
|
545
|
+
|
|
546
|
+
for (const item of items) {
|
|
547
|
+
const typedItem = item as {
|
|
548
|
+
type?: string;
|
|
549
|
+
key?: string;
|
|
550
|
+
title?: string;
|
|
551
|
+
fields?: unknown[];
|
|
552
|
+
is_required?: boolean;
|
|
553
|
+
};
|
|
554
|
+
|
|
555
|
+
if (typedItem.type === "fieldset" && typedItem.fields && typedItem.key) {
|
|
556
|
+
// Show fieldset context to user
|
|
557
|
+
const fieldsetTitle = typedItem.title || typedItem.key;
|
|
558
|
+
const pathDisplay =
|
|
559
|
+
fieldsetPath.length > 0 ? ` (in ${fieldsetPath.join(" > ")})` : "";
|
|
441
560
|
console.log(
|
|
442
|
-
chalk.
|
|
443
|
-
`\n
|
|
561
|
+
chalk.cyan(
|
|
562
|
+
`\n📁 Processing fieldset: ${fieldsetTitle}${pathDisplay}`,
|
|
444
563
|
),
|
|
445
564
|
);
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
565
|
+
|
|
566
|
+
// Create fieldset array in target inputs if it doesn't exist
|
|
567
|
+
if (!targetInputs[typedItem.key]) {
|
|
568
|
+
targetInputs[typedItem.key] = [{}];
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
// Process fields within this fieldset recursively
|
|
572
|
+
const fieldsetTarget = (
|
|
573
|
+
targetInputs[typedItem.key] as Record<string, unknown>[]
|
|
574
|
+
)[0];
|
|
575
|
+
const nestedPath = [...fieldsetPath, fieldsetTitle];
|
|
576
|
+
|
|
577
|
+
const nestedStats = await this.processFieldItems(
|
|
578
|
+
typedItem.fields,
|
|
579
|
+
fieldsetTarget,
|
|
580
|
+
processedFieldKeys,
|
|
581
|
+
nestedPath,
|
|
582
|
+
iteration,
|
|
583
|
+
);
|
|
584
|
+
|
|
585
|
+
newRequiredCount += nestedStats.newRequired;
|
|
586
|
+
newOptionalCount += nestedStats.newOptional;
|
|
587
|
+
if (nestedStats.optionalSkipped) {
|
|
588
|
+
optionalSkipped = true;
|
|
589
|
+
}
|
|
590
|
+
} else if (typedItem.type === "input_field" && typedItem.key) {
|
|
591
|
+
// Skip if already processed
|
|
592
|
+
if (processedFieldKeys.has(typedItem.key)) {
|
|
593
|
+
continue;
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
const isRequired = typedItem.is_required || false;
|
|
597
|
+
|
|
598
|
+
if (isRequired) {
|
|
599
|
+
// Process required field immediately
|
|
600
|
+
newRequiredCount++;
|
|
601
|
+
if (newRequiredCount === 1 && fieldsetPath.length === 0) {
|
|
602
|
+
// Only show this message once at root level
|
|
603
|
+
console.log(
|
|
604
|
+
chalk.blue(
|
|
605
|
+
`\n📝 Please provide values for the following ${iteration === 1 ? "" : "additional "}input fields:`,
|
|
606
|
+
),
|
|
607
|
+
);
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
await this.promptForField(typedItem, targetInputs);
|
|
611
|
+
processedFieldKeys.add(typedItem.key);
|
|
612
|
+
} else {
|
|
613
|
+
// Collect optional fields for batch processing
|
|
614
|
+
newOptionalCount++;
|
|
449
615
|
}
|
|
450
616
|
}
|
|
617
|
+
// Skip info fields - they're for display only
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
// Handle optional fields after processing all required fields
|
|
621
|
+
if (newOptionalCount > 0) {
|
|
622
|
+
const optionalFields = items.filter((item: unknown) => {
|
|
623
|
+
const typedItem = item as {
|
|
624
|
+
type?: string;
|
|
625
|
+
key?: string;
|
|
626
|
+
is_required?: boolean;
|
|
627
|
+
};
|
|
628
|
+
return (
|
|
629
|
+
typedItem.type === "input_field" &&
|
|
630
|
+
typedItem.key &&
|
|
631
|
+
!typedItem.is_required &&
|
|
632
|
+
!processedFieldKeys.has(typedItem.key)
|
|
633
|
+
);
|
|
634
|
+
});
|
|
451
635
|
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
636
|
+
if (optionalFields.length > 0) {
|
|
637
|
+
const pathContext =
|
|
638
|
+
fieldsetPath.length > 0 ? ` in ${fieldsetPath.join(" > ")}` : "";
|
|
455
639
|
console.log(
|
|
456
640
|
chalk.gray(
|
|
457
|
-
`\nThere are ${
|
|
641
|
+
`\nThere are ${optionalFields.length} ${iteration === 1 ? "" : "additional "}optional field(s) available${pathContext}.`,
|
|
458
642
|
),
|
|
459
643
|
);
|
|
460
644
|
|
|
461
645
|
try {
|
|
462
|
-
shouldConfigureOptional = await inquirer.prompt([
|
|
646
|
+
const shouldConfigureOptional = await inquirer.prompt([
|
|
463
647
|
{
|
|
464
648
|
type: "confirm",
|
|
465
649
|
name: "configure",
|
|
466
|
-
message: `Would you like to configure ${iteration === 1 ? "" : "these additional "}optional fields?`,
|
|
650
|
+
message: `Would you like to configure ${iteration === 1 ? "" : "these additional "}optional fields${pathContext}?`,
|
|
467
651
|
default: false,
|
|
468
652
|
},
|
|
469
653
|
]);
|
|
654
|
+
|
|
655
|
+
if (shouldConfigureOptional.configure) {
|
|
656
|
+
console.log(chalk.cyan(`\nOptional fields${pathContext}:`));
|
|
657
|
+
for (const field of optionalFields) {
|
|
658
|
+
await this.promptForField(field, targetInputs);
|
|
659
|
+
const typedField = field as { key: string };
|
|
660
|
+
processedFieldKeys.add(typedField.key);
|
|
661
|
+
}
|
|
662
|
+
} else {
|
|
663
|
+
optionalSkipped = true;
|
|
664
|
+
// Mark these fields as processed even if skipped to avoid re-asking
|
|
665
|
+
optionalFields.forEach((field: unknown) => {
|
|
666
|
+
const typedField = field as { key: string };
|
|
667
|
+
processedFieldKeys.add(typedField.key);
|
|
668
|
+
});
|
|
669
|
+
}
|
|
470
670
|
} catch (error) {
|
|
471
671
|
if (this.isUserCancellation(error)) {
|
|
472
672
|
console.log(chalk.yellow("\n\nOperation cancelled by user"));
|
|
@@ -474,39 +674,14 @@ export class SchemaParameterResolver {
|
|
|
474
674
|
}
|
|
475
675
|
throw error;
|
|
476
676
|
}
|
|
477
|
-
|
|
478
|
-
if (shouldConfigureOptional.configure) {
|
|
479
|
-
console.log(chalk.cyan(`\nOptional fields:`));
|
|
480
|
-
for (const field of newOptionalFields) {
|
|
481
|
-
await this.promptForField(field, inputs);
|
|
482
|
-
processedFieldKeys.add((field as { key: string }).key);
|
|
483
|
-
}
|
|
484
|
-
} else {
|
|
485
|
-
// Mark these fields as processed even if skipped to avoid re-asking
|
|
486
|
-
newOptionalFields.forEach((field: unknown) =>
|
|
487
|
-
processedFieldKeys.add((field as { key: string }).key),
|
|
488
|
-
);
|
|
489
|
-
}
|
|
490
|
-
}
|
|
491
|
-
|
|
492
|
-
// If we only processed optional fields and skipped them, no need to re-fetch
|
|
493
|
-
if (
|
|
494
|
-
newRequiredFields.length === 0 &&
|
|
495
|
-
(!newOptionalFields.length || !shouldConfigureOptional.configure)
|
|
496
|
-
) {
|
|
497
|
-
break;
|
|
498
677
|
}
|
|
499
678
|
}
|
|
500
679
|
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
);
|
|
507
|
-
}
|
|
508
|
-
|
|
509
|
-
return inputs;
|
|
680
|
+
return {
|
|
681
|
+
newRequired: newRequiredCount,
|
|
682
|
+
newOptional: newOptionalCount,
|
|
683
|
+
optionalSkipped,
|
|
684
|
+
};
|
|
510
685
|
}
|
|
511
686
|
|
|
512
687
|
private getNestedValue(obj: unknown, path: string[]): unknown {
|
|
@@ -536,7 +711,7 @@ export class SchemaParameterResolver {
|
|
|
536
711
|
type?: string;
|
|
537
712
|
key: string;
|
|
538
713
|
label?: string;
|
|
539
|
-
|
|
714
|
+
is_required?: boolean;
|
|
540
715
|
helpText?: string;
|
|
541
716
|
default?: unknown;
|
|
542
717
|
choices?: Array<{ label?: string; value: unknown }>;
|
|
@@ -545,7 +720,7 @@ export class SchemaParameterResolver {
|
|
|
545
720
|
const fieldPrompt: Record<string, unknown> = {
|
|
546
721
|
type: fieldObj.type === "boolean" ? "confirm" : "input",
|
|
547
722
|
name: fieldObj.key,
|
|
548
|
-
message: `${fieldObj.label || fieldObj.key}${fieldObj.
|
|
723
|
+
message: `${fieldObj.label || fieldObj.key}${fieldObj.is_required ? " (required)" : " (optional)"}:`,
|
|
549
724
|
};
|
|
550
725
|
|
|
551
726
|
if (fieldObj.helpText) {
|
|
@@ -574,7 +749,7 @@ export class SchemaParameterResolver {
|
|
|
574
749
|
|
|
575
750
|
if (answer[fieldObj.key] !== undefined && answer[fieldObj.key] !== "") {
|
|
576
751
|
inputs[fieldObj.key] = answer[fieldObj.key];
|
|
577
|
-
} else if (fieldObj.
|
|
752
|
+
} else if (fieldObj.is_required) {
|
|
578
753
|
throw new Error(`Required field ${fieldObj.key} cannot be empty`);
|
|
579
754
|
}
|
|
580
755
|
} catch (error) {
|
|
@@ -598,4 +773,59 @@ export class SchemaParameterResolver {
|
|
|
598
773
|
errorObj?.isTTYError === true
|
|
599
774
|
);
|
|
600
775
|
}
|
|
776
|
+
|
|
777
|
+
private hasResolver(
|
|
778
|
+
paramName: string,
|
|
779
|
+
sdk: ZapierSdk,
|
|
780
|
+
functionName?: string,
|
|
781
|
+
): boolean {
|
|
782
|
+
// Check plugin-specific resolvers first
|
|
783
|
+
if (functionName && typeof sdk.getRegistry === "function") {
|
|
784
|
+
const registry = sdk.getRegistry();
|
|
785
|
+
const functionInfo = registry.functions.find(
|
|
786
|
+
(f) => f.name === functionName,
|
|
787
|
+
);
|
|
788
|
+
if (functionInfo && functionInfo.resolvers?.[paramName]) {
|
|
789
|
+
return true;
|
|
790
|
+
}
|
|
791
|
+
}
|
|
792
|
+
|
|
793
|
+
// No global registry fallback
|
|
794
|
+
return false;
|
|
795
|
+
}
|
|
796
|
+
|
|
797
|
+
private getResolver(
|
|
798
|
+
paramName: string,
|
|
799
|
+
sdk: ZapierSdk,
|
|
800
|
+
functionName?: string,
|
|
801
|
+
): any {
|
|
802
|
+
// Check plugin-specific resolvers first
|
|
803
|
+
if (functionName && typeof sdk.getRegistry === "function") {
|
|
804
|
+
const registry = sdk.getRegistry();
|
|
805
|
+
const functionInfo = registry.functions.find(
|
|
806
|
+
(f) => f.name === functionName,
|
|
807
|
+
);
|
|
808
|
+
if (functionInfo && functionInfo.resolvers?.[paramName]) {
|
|
809
|
+
return functionInfo.resolvers[paramName];
|
|
810
|
+
}
|
|
811
|
+
}
|
|
812
|
+
|
|
813
|
+
// No global registry fallback
|
|
814
|
+
return null;
|
|
815
|
+
}
|
|
816
|
+
|
|
817
|
+
private getLocalResolvers(
|
|
818
|
+
sdk: ZapierSdk,
|
|
819
|
+
functionName?: string,
|
|
820
|
+
): Record<string, any> {
|
|
821
|
+
if (!functionName || typeof sdk.getRegistry !== "function") {
|
|
822
|
+
return {};
|
|
823
|
+
}
|
|
824
|
+
|
|
825
|
+
const registry = sdk.getRegistry();
|
|
826
|
+
const functionInfo = registry.functions.find(
|
|
827
|
+
(f) => f.name === functionName,
|
|
828
|
+
);
|
|
829
|
+
return functionInfo?.resolvers || {};
|
|
830
|
+
}
|
|
601
831
|
}
|